diff --git a/CMakeLists.txt b/CMakeLists.txt
index 437d3c0a154..055f374b4c8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1514,7 +1514,6 @@ if(QOPENGL)
src/shaders/vinylqualityshader.cpp
src/util/opengltexture2d.cpp
src/waveform/renderers/allshader/digitsrenderer.cpp
- src/waveform/renderers/allshader/matrixforwidgetgeometry.cpp
src/waveform/renderers/allshader/waveformrenderbackground.cpp
src/waveform/renderers/allshader/waveformrenderbeat.cpp
src/waveform/renderers/allshader/waveformrenderer.cpp
@@ -3810,6 +3809,12 @@ 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)
+target_compile_definitions(mixxx-lib PRIVATE rendergraph=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 8011fe39f94..c21ec6a8008 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 00000000000..dfcecc593a4
--- /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 00000000000..644c2ce7647
--- /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 00000000000..0f6712e87d5
--- /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 00000000000..78de913751a
--- /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 00000000000..101cdb324e4
--- /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 00000000000..da4626bbf7c
--- /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 00000000000..c528eb94747
--- /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 00000000000..951c85c3669
--- /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 00000000000..5aa3d1556b1
--- /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 00000000000..376c71668ba
--- /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 00000000000..07b3d7f1f3b
--- /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 00000000000..a3d58014be3
--- /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 00000000000..0a808489f5b
--- /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 00000000000..b8a61f8682f
--- /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 00000000000..6568d01f187
--- /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 00000000000..53e86e4501c
--- /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 00000000000..5cf90a770ea
--- /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 00000000000..a831457b968
--- /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 00000000000..d5ce8b2d9db
--- /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 00000000000..df2bcf93236
--- /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 00000000000..bbe37bccd69
--- /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 00000000000..b2d03f1352c
--- /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 00000000000..07b3d7f1f3b
--- /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 00000000000..a3d58014be3
--- /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/unicolor.frag b/res/shaders/rendergraph/unicolor.frag
new file mode 100644
index 00000000000..7099bb8f3dd
--- /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 00000000000..bfef503120b
--- /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 00000000000..9e268c18fba
--- /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 00000000000..d126f3dab58
--- /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 00000000000..059bbdaa050
--- /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 00000000000..8b1720e22c8
--- /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/rendergraph/assert.h b/src/rendergraph/common/rendergraph/assert.h
new file mode 100644
index 00000000000..6b47ad6a7f4
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/assert.h
@@ -0,0 +1,3 @@
+// rendergraph is a module but we want to include util/assert.h from Mixxx,
+// without adding the entire src/ dir to the include paths.
+#include "../../../util/assert.h"
diff --git a/src/rendergraph/common/rendergraph/attributeinit.h b/src/rendergraph/common/rendergraph/attributeinit.h
new file mode 100644
index 00000000000..ccbbbf16b9b
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/attributeinit.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "rendergraph/types.h"
+
+namespace rendergraph {
+struct AttributeInit;
+}
+
+// helper to create an AttributeSet using an initializer_list
+struct rendergraph::AttributeInit {
+ int m_tupleSize;
+ PrimitiveType m_primitiveType;
+
+ template
+ static AttributeInit create() {
+ return AttributeInit{tupleSizeOf(), primitiveTypeOf()};
+ }
+};
diff --git a/src/rendergraph/common/rendergraph/attributeset.h b/src/rendergraph/common/rendergraph/attributeset.h
new file mode 100644
index 00000000000..717f47a3084
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/attributeset.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "backend/baseattributeset.h"
+#include "rendergraph/attributeinit.h"
+
+namespace rendergraph {
+
+class AttributeSet : public BaseAttributeSet {
+ public:
+ AttributeSet(std::initializer_list list, const std::vector& names);
+};
+
+template
+AttributeSet makeAttributeSet(const QString (&names)[N]) {
+ static_assert(sizeof...(Ts) == N);
+ return AttributeSet({(AttributeInit::create())...},
+ std::vector(std::cbegin(names), std::cend(names)));
+}
+
+} // namespace rendergraph
diff --git a/src/rendergraph/common/rendergraph/geometry.h b/src/rendergraph/common/rendergraph/geometry.h
new file mode 100644
index 00000000000..47108777493
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/geometry.h
@@ -0,0 +1,67 @@
+#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:
+ using DrawingMode = rendergraph::DrawingMode;
+
+ struct Point2D {
+ QVector2D position2D;
+ };
+
+ struct TexturedPoint2D {
+ QVector2D position2D;
+ QVector2D texcoord2D;
+ };
+
+ struct RGBColoredPoint2D {
+ QVector2D position2D;
+ QVector3D color3D;
+ };
+
+ struct RGBAColoredPoint2D {
+ QVector2D position2D;
+ QVector4D color4D;
+ };
+
+ Geometry(const rendergraph::AttributeSet& attributeSet, int vertexCount);
+
+ const Attribute* attributes() const {
+ return BaseGeometry::attributes();
+ }
+
+ void setAttributeValues(int attributePosition, const float* data, int numTuples);
+
+ int attributeCount() const {
+ return BaseGeometry::attributeCount();
+ }
+
+ void allocate(int vertexCount) {
+ BaseGeometry::allocate(vertexCount);
+ }
+
+ int sizeOfVertex() const {
+ return BaseGeometry::sizeOfVertex();
+ }
+ int vertexCount() const {
+ return BaseGeometry::vertexCount();
+ }
+
+ 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 00000000000..37db115ce40
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/geometrynode.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "backend/basegeometrynode.h"
+#include "rendergraph/geometry.h"
+#include "rendergraph/material.h"
+#include "rendergraph/nodeinterface.h"
+
+namespace rendergraph {
+class GeometryNode;
+} // namespace rendergraph
+
+class rendergraph::GeometryNode : public rendergraph::NodeInterface {
+ public:
+ GeometryNode();
+ virtual ~GeometryNode() = default;
+
+ template
+ void initForRectangles(int numRectangles) {
+ const int verticesPerRectangle = 6; // 2 triangles
+ setGeometry(std::make_unique(T_Material::attributes(),
+ numRectangles * verticesPerRectangle));
+ setMaterial(std::make_unique());
+ geometry().setDrawingMode(DrawingMode::Triangles);
+ }
+
+ void setUsePreprocess(bool value);
+ void setMaterial(std::unique_ptr material);
+ void setGeometry(std::unique_ptr geometry);
+
+ Geometry& geometry() const;
+ Material& material() const;
+
+ void markDirtyGeometry();
+ void markDirtyMaterial();
+
+ 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 00000000000..ab18e89166a
--- /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 < 0 ? -1 : 1;
+ }
+ // 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 00000000000..ee35b465934
--- /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 00000000000..94c80693799
--- /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 00000000000..864f4a60adf
--- /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 00000000000..661246abd05
--- /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 00000000000..e81382bf9ce
--- /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 00000000000..0921ebb4fae
--- /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 00000000000..455555d1e97
--- /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 00000000000..cfe1fde7782
--- /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 00000000000..b3f61767626
--- /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 00000000000..2fa5a2310cc
--- /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 00000000000..d67fd16320c
--- /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 00000000000..26225f4d3c6
--- /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 00000000000..43c811ebebd
--- /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 00000000000..67da71e91f3
--- /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 00000000000..3060648e846
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/node.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "backend/basenode.h"
+#include "rendergraph/nodeinterface.h"
+
+namespace rendergraph {
+class Node;
+} // namespace rendergraph
+
+class rendergraph::Node : public rendergraph::NodeInterface {
+};
diff --git a/src/rendergraph/common/rendergraph/opacitynode.h b/src/rendergraph/common/rendergraph/opacitynode.h
new file mode 100644
index 00000000000..d69f0b1c42a
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/opacitynode.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "backend/baseopacitynode.h"
+#include "rendergraph/nodeinterface.h"
+
+namespace rendergraph {
+class OpacityNode;
+} // namespace rendergraph
+
+class rendergraph::OpacityNode : public rendergraph::NodeInterface {
+};
diff --git a/src/rendergraph/common/rendergraph/texture.h b/src/rendergraph/common/rendergraph/texture.h
new file mode 100644
index 00000000000..aa6bbe95ccf
--- /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* pContext, 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/types.h b/src/rendergraph/common/rendergraph/types.h
new file mode 100644
index 00000000000..f92e8de6cda
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/types.h
@@ -0,0 +1,36 @@
+#pragma once
+
+namespace rendergraph {
+
+enum class DrawingMode {
+ Triangles,
+ TriangleStrip
+};
+
+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 00000000000..19a6a9c74a3
--- /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 00000000000..069469b0310
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/uniformscache.cpp
@@ -0,0 +1,26 @@
+#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 00000000000..045887f0c37
--- /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 00000000000..68e077613bd
--- /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 00000000000..34af87e5c94
--- /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 00000000000..afa7e466d1c
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h
@@ -0,0 +1,174 @@
+#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());
+ }
+ int index() const {
+ return static_cast(m_pWrite - m_pData);
+ }
+ void addTriangle(QVector2D p1, QVector2D p2, QVector2D p3, QVector4D rgba) {
+ addTriangle(p1.x(),
+ p1.y(),
+ p2.x(),
+ p2.y(),
+ p3.x(),
+ p3.y(),
+ rgba.x(),
+ rgba.y(),
+ rgba.z(),
+ rgba.w());
+ }
+ void addTriangle(QVector2D p1,
+ QVector2D p2,
+ QVector2D p3,
+ QVector4D rgba1,
+ QVector4D rgba2,
+ QVector4D rgba3) {
+ addTriangle(p1.x(),
+ p1.y(),
+ p2.x(),
+ p2.y(),
+ p3.x(),
+ p3.y(),
+ rgba1.x(),
+ rgba1.y(),
+ rgba1.z(),
+ rgba1.w(),
+ rgba2.x(),
+ rgba2.y(),
+ rgba2.z(),
+ rgba2.w(),
+ rgba3.x(),
+ rgba3.y(),
+ rgba3.z(),
+ rgba3.w());
+ }
+
+ private:
+ 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}};
+ }
+ 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 00000000000..c7191e902ec
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h
@@ -0,0 +1,152 @@
+#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 addTriangle(QVector2D p1, QVector2D p2, QVector2D p3, QVector3D rgb) {
+ addTriangle(p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y(), rgb.x(), rgb.y(), rgb.z());
+ }
+ void addTriangle(QVector2D p1,
+ QVector2D p2,
+ QVector2D p3,
+ QVector3D rgb1,
+ QVector3D rgb2,
+ QVector3D rgb3) {
+ addTriangle(p1.x(),
+ p1.y(),
+ p2.x(),
+ p2.y(),
+ p3.x(),
+ p3.y(),
+ rgb1.x(),
+ rgb1.y(),
+ rgb1.z(),
+ rgb2.x(),
+ rgb2.y(),
+ rgb2.z(),
+ rgb3.x(),
+ rgb3.y(),
+ rgb3.z());
+ }
+ int index() const {
+ return static_cast(m_pWrite - m_pData);
+ }
+
+ private:
+ 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, r1, g1, b1, r2, g2, b2);
+ addTriangle(x1, y2, x2, y2, x2, y1, r2, g2, b2, r2, g2, b2, r1, g1, b1);
+ }
+ 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}};
+ }
+
+ 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 00000000000..29aa5beca04
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h
@@ -0,0 +1,70 @@
+#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) {
+ addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), 0.f, 0.f, 1.f, 1.f);
+ }
+ 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 addTriangle(QVector2D p1,
+ QVector2D p2,
+ QVector2D p3,
+ QVector2D tp1,
+ QVector2D tp2,
+ QVector2D tp3) {
+ addTriangle(p1.x(),
+ p1.y(),
+ p2.x(),
+ p2.y(),
+ p3.x(),
+ p3.y(),
+ tp1.x(),
+ tp1.y(),
+ tp2.x(),
+ tp2.y(),
+ tp3.x(),
+ tp3.y());
+ }
+ int index() const {
+ return static_cast(m_pWrite - m_pData);
+ }
+
+ private:
+ 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}};
+ }
+ 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 00000000000..4ae381db209
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h
@@ -0,0 +1,40 @@
+#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 addTriangle(QVector2D p1, QVector2D p2, QVector2D p3) {
+ addTriangle(p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y());
+ }
+ int index() const {
+ return static_cast(m_pWrite - m_pData);
+ }
+
+ private:
+ 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}};
+ }
+ Geometry::Point2D* const m_pData;
+ Geometry::Point2D* m_pWrite;
+};
diff --git a/src/rendergraph/common/types.cpp b/src/rendergraph/common/types.cpp
new file mode 100644
index 00000000000..211538e5af4
--- /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 00000000000..fb5b7c40e5d
--- /dev/null
+++ b/src/rendergraph/examples/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.16)
+project(rendergraph LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
+
+if(NOT(QT_VERSION VERSION_LESS "6.3.0"))
+ qt_standard_project_setup()
+endif()
+
+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 00000000000..6168efc09c4
--- /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/examplenode.cpp b/src/rendergraph/examples/common/examplenode.cpp
new file mode 100644
index 00000000000..8af058ad3a2
--- /dev/null
+++ b/src/rendergraph/examples/common/examplenode.cpp
@@ -0,0 +1,52 @@
+#include "examplenode.h"
+
+#include
+#include
+#include
+
+#include "rendergraph/geometry.h"
+#include "rendergraph/material/rgbmaterial.h"
+#include "rendergraph/material/texturematerial.h"
+#include "rendergraph/material/unicolormaterial.h"
+#include "rendergraph/vertexupdaters/rgbvertexupdater.h"
+#include "rendergraph/vertexupdaters/texturedvertexupdater.h"
+#include "rendergraph/vertexupdaters/vertexupdater.h"
+
+using namespace rendergraph;
+
+ExampleNode::ExampleNode(rendergraph::Context* pContext) {
+ {
+ auto pNode = std::make_unique();
+ pNode->initForRectangles(1);
+ auto& material = dynamic_cast(pNode->material());
+ material.setTexture(std::make_unique(
+ pContext, QImage(":/example/images/test.png")));
+ TexturedVertexUpdater vertexUpdater{
+ pNode->geometry().vertexDataAs()};
+ vertexUpdater.addRectangle({0, 0}, {100, 100}, {0.f, 0.f}, {1.f, 1.f});
+ appendChildNode(std::move(pNode));
+ }
+ {
+ auto pNode = std::make_unique();
+ pNode->initForRectangles(2);
+ pNode->material().setUniform(1, QColor(255, 127, 0));
+ pNode->geometry().setDrawingMode(DrawingMode::Triangles);
+ rendergraph::VertexUpdater vertexUpdater{
+ pNode->geometry()
+ .vertexDataAs()};
+ vertexUpdater.addRectangle({100, 100}, {160, 160});
+ vertexUpdater.addRectangle({200, 160}, {240, 190});
+ appendChildNode(std::move(pNode));
+ }
+ {
+ auto pNode = std::make_unique();
+ pNode->initForRectangles(2);
+ pNode->geometry().setDrawingMode(DrawingMode::Triangles);
+ rendergraph::RGBVertexUpdater vertexUpdater{
+ pNode->geometry().vertexDataAs()};
+ vertexUpdater.addRectangle({300, 100}, {340, 140}, {1.f, 0.f, 0.5f});
+ vertexUpdater.addRectangleHGradient(
+ {340, 100}, {440, 130}, {0.f, 1.f, 0.5f}, {0.5f, 0.f, 1.f});
+ appendChildNode(std::move(pNode));
+ }
+}
diff --git a/src/rendergraph/examples/common/examplenode.h b/src/rendergraph/examples/common/examplenode.h
new file mode 100644
index 00000000000..d46ec2979ab
--- /dev/null
+++ b/src/rendergraph/examples/common/examplenode.h
@@ -0,0 +1,12 @@
+#include "rendergraph/geometrynode.h"
+#include "rendergraph/node.h"
+#include "rendergraph/texture.h"
+
+namespace rendergraph {
+class ExampleNode;
+} // namespace rendergraph
+
+class rendergraph::ExampleNode : public rendergraph::Node {
+ public:
+ ExampleNode(rendergraph::Context* pContext);
+};
diff --git a/src/rendergraph/examples/gl_example/CMakeLists.txt b/src/rendergraph/examples/gl_example/CMakeLists.txt
new file mode 100644
index 00000000000..5a4239d6e6c
--- /dev/null
+++ b/src/rendergraph/examples/gl_example/CMakeLists.txt
@@ -0,0 +1,38 @@
+qt_add_executable(gl_example
+ window.cpp window.h
+ main.cpp
+ ../common/examplenode.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)
+target_compile_definitions(gl_example PRIVATE rendergraph=rendergraph_gl)
+
+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}
+)
+
+if(NOT(QT_VERSION VERSION_LESS "6.3.0"))
+qt_generate_deploy_app_script(
+ TARGET gl_example
+ OUTPUT_SCRIPT deploy_script
+ NO_UNSUPPORTED_PLATFORM_ERROR
+)
+install(SCRIPT ${deploy_script})
+endif()
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 00000000000..f9580346eb8
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 00000000000..7b62238b429
--- /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 00000000000..de643ee8ba7
--- /dev/null
+++ b/src/rendergraph/examples/gl_example/window.cpp
@@ -0,0 +1,37 @@
+#include "window.h"
+
+#include "examplenode.h"
+#include "rendergraph/context.h"
+#include "rendergraph/engine.h"
+
+Window::Window() {
+ resize(640, 480);
+}
+
+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 pExampleNode = std::make_unique(&context);
+ m_pEngine = std::make_unique(std::move(pExampleNode));
+}
+
+void Window::resizeGL(int w, int h) {
+ m_pEngine->resize(w, h);
+}
+
+void Window::paintGL() {
+ glClearColor(0.f, 0.f, 1.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 00000000000..8c2a5646bef
--- /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 00000000000..303987868d9
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/CMakeLists.txt
@@ -0,0 +1,44 @@
+qt_add_executable(sg_example WIN32 MACOSX_BUNDLE
+ customitem.cpp
+ customitem.h
+ main.cpp
+ ../common/examplenode.cpp
+)
+
+target_link_libraries(sg_example PRIVATE
+ rendergraph_sg
+)
+target_include_directories(sg_example PRIVATE ../common)
+target_compile_definitions(sg_example PRIVATE rendergraph=rendergraph_sg)
+
+if(QT_VERSION VERSION_LESS "6.3.0")
+ set_target_properties(sg_example PROPERTIES AUTOMOC ON)
+endif()
+
+qt_add_qml_module(sg_example
+ VERSION 1.0
+ 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}
+)
+
+if(NOT(QT_VERSION VERSION_LESS "6.3.0"))
+ 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})
+endif()
diff --git a/src/rendergraph/examples/sg_example/customitem.cpp b/src/rendergraph/examples/sg_example/customitem.cpp
new file mode 100644
index 00000000000..720e6723278
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/customitem.cpp
@@ -0,0 +1,62 @@
+#include "customitem.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "examplenode.h"
+#include "rendergraph/context.h"
+#include "rendergraph/geometrynode.h"
+#include "rendergraph/material/unicolormaterial.h"
+#include "rendergraph/node.h"
+#include "rendergraph/vertexupdaters/vertexupdater.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, 255, 255));
+ bgNode->setRect(boundingRect());
+
+ QSGGeometry* geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 3);
+
+ geometry->vertexDataAsPoint2D()[0].set(10, 10);
+ geometry->vertexDataAsPoint2D()[1].set(100, 10);
+ geometry->vertexDataAsPoint2D()[2].set(10, 100);
+
+ QSGFlatColorMaterial* material = new QSGFlatColorMaterial;
+ material->setColor(QColor(255, 0, 0));
+
+ rendergraph::Context context(window());
+ auto pExampleNode = std::make_unique(&context);
+
+ bgNode->appendChildNode(pExampleNode.release());
+
+ 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 00000000000..87938f41653
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/customitem.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+#include
+
+#include "rendergraph/node.h"
+
+class CustomItem : public QQuickItem {
+ Q_OBJECT
+ QML_ELEMENT
+
+ public:
+ explicit CustomItem(QQuickItem* parent = nullptr);
+ ~CustomItem() override;
+
+ protected:
+ QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override;
+ void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override;
+
+ bool m_geometryChanged{};
+};
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 00000000000..f9580346eb8
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 00000000000..ab2e5b34072
--- /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 00000000000..22404c8553c
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/qml/main.qml
@@ -0,0 +1,20 @@
+import QtQuick
+import RenderGraph
+
+Item {
+ id: root
+
+ width: 680
+ height: 520
+
+ CustomItem {
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.topMargin: 20
+ anchors.bottomMargin: 20
+ anchors.leftMargin: 20
+ anchors.rightMargin: 20
+ }
+}
diff --git a/src/rendergraph/opengl/CMakeLists.txt b/src/rendergraph/opengl/CMakeLists.txt
new file mode 100644
index 00000000000..d6ca282017b
--- /dev/null
+++ b/src/rendergraph/opengl/CMakeLists.txt
@@ -0,0 +1,87 @@
+add_library(rendergraph_gl
+../common/rendergraph/attributeinit.h
+../common/rendergraph/attributeset.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
+attributeset.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/basenode.cpp
+backend/baseopacitynode.h
+backend/baseopenglnode.cpp
+backend/baseopenglnode.h
+backend/basetexture.h
+backend/shadercache.h
+engine.cpp
+geometry.cpp
+geometrynode.cpp
+material.cpp
+materialshader.cpp
+rendergraph/context.h
+rendergraph/engine.h
+rendergraph/openglnode.h
+texture.cpp
+)
+
+target_link_libraries(rendergraph_gl PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+)
+if(WIN32)
+ find_package(Microsoft.GSL CONFIG)
+ if(Microsoft.GSL_FOUND)
+ target_link_libraries(rendergraph_gl PRIVATE Microsoft.GSL::GSL)
+ else()
+ # check if the headers have been installed without cmake config (< 3.1.0)
+ check_include_file_cxx(gsl/gsl HAVE_GSL_GSL)
+ if(NOT HAVE_GSL_GSL)
+ unset(HAVE_GSL_GSL CACHE) # unset cache to re-evaluate this until it succeeds. check_include_file_cxx() has no REQUIRED flag.
+ message(FATAL_ERROR "ms-gsl development headers (libmsgsl-dev) not found")
+ endif()
+ endif()
+endif()
+target_compile_definitions(rendergraph_gl PRIVATE rendergraph=rendergraph_gl)
+
+# 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/attributeset.cpp b/src/rendergraph/opengl/attributeset.cpp
new file mode 100644
index 00000000000..302c2ad5c9c
--- /dev/null
+++ b/src/rendergraph/opengl/attributeset.cpp
@@ -0,0 +1,8 @@
+#include "rendergraph/attributeset.h"
+
+using namespace rendergraph;
+
+AttributeSet::AttributeSet(std::initializer_list list,
+ const std::vector& names)
+ : BaseAttributeSet(list, names) {
+}
diff --git a/src/rendergraph/opengl/backend/baseattributeset.cpp b/src/rendergraph/opengl/backend/baseattributeset.cpp
new file mode 100644
index 00000000000..87e1ea333d0
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseattributeset.cpp
@@ -0,0 +1,18 @@
+#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;
+ int offset = 0;
+ m_attributes.reserve(list.size());
+ for (auto item : list) {
+ m_attributes.push_back(BaseGeometry::Attribute{
+ offset, item.m_tupleSize, item.m_primitiveType, names[i++]});
+ offset += item.m_tupleSize * sizeOf(item.m_primitiveType);
+ }
+ m_sizeOfVertex = offset;
+}
diff --git a/src/rendergraph/opengl/backend/baseattributeset.h b/src/rendergraph/opengl/backend/baseattributeset.h
new file mode 100644
index 00000000000..c2c1c2cda2a
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseattributeset.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include
+#include
+
+#include "backend/basegeometry.h"
+#include "rendergraph/attributeinit.h"
+
+namespace rendergraph {
+class BaseAttributeSet;
+}
+
+class rendergraph::BaseAttributeSet {
+ protected:
+ BaseAttributeSet(std::initializer_list list, const std::vector& names);
+
+ public:
+ const std::vector& attributes() const {
+ return m_attributes;
+ }
+ int sizeOfVertex() const {
+ return m_sizeOfVertex;
+ }
+
+ protected:
+ std::vector m_attributes;
+ int m_sizeOfVertex;
+};
diff --git a/src/rendergraph/opengl/backend/basegeometry.cpp b/src/rendergraph/opengl/backend/basegeometry.cpp
new file mode 100644
index 00000000000..09854386dbe
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basegeometry.cpp
@@ -0,0 +1,21 @@
+#include "backend/basegeometry.h"
+
+#include "backend/baseattributeset.h"
+#include "rendergraph/geometry.h"
+
+using namespace rendergraph;
+
+namespace {
+// to mimic sg default
+constexpr auto defaultDrawingMode = DrawingMode::TriangleStrip;
+} // namespace
+
+BaseGeometry::BaseGeometry(
+ const BaseAttributeSet& attributeSet, int vertexCount)
+ : m_pAttributes(attributeSet.attributes().data()),
+ m_attributeCount(attributeSet.attributes().size()),
+ m_sizeOfVertex(attributeSet.sizeOfVertex()),
+ m_drawingMode(defaultDrawingMode),
+ m_vertexCount(vertexCount),
+ m_vertexData(m_vertexCount * m_sizeOfVertex / sizeof(float)) {
+}
diff --git a/src/rendergraph/opengl/backend/basegeometry.h b/src/rendergraph/opengl/backend/basegeometry.h
new file mode 100644
index 00000000000..b2e26a32b42
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basegeometry.h
@@ -0,0 +1,56 @@
+#pragma once
+
+#include
+#include
+
+#include "rendergraph/types.h"
+
+namespace rendergraph {
+class BaseAttributeSet; // fwd decl to avoid circular dependency
+class BaseGeometry;
+}
+
+// TODO this assumes all vertices consist of floats
+class rendergraph::BaseGeometry {
+ protected:
+ BaseGeometry(const BaseAttributeSet& attributeSet, int vertexCount);
+
+ public:
+ struct Attribute {
+ const int m_offset;
+ const int m_tupleSize;
+ const PrimitiveType m_primitiveType;
+ const QString m_name;
+ };
+
+ float* vertexData() {
+ return m_vertexData.data();
+ }
+ const float* vertexData() const {
+ return m_vertexData.data();
+ }
+ const Attribute* attributes() const {
+ return m_pAttributes;
+ }
+ int attributeCount() const {
+ return m_attributeCount;
+ }
+ int vertexCount() const {
+ return m_vertexCount;
+ }
+ int sizeOfVertex() const { // in bytes
+ return m_sizeOfVertex;
+ }
+ void allocate(int vertexCount) {
+ m_vertexCount = vertexCount;
+ m_vertexData.resize(m_vertexCount * sizeOfVertex() / sizeof(float));
+ }
+
+ protected:
+ const Attribute* m_pAttributes;
+ const int m_attributeCount;
+ const int m_sizeOfVertex;
+ DrawingMode m_drawingMode;
+ int m_vertexCount;
+ std::vector m_vertexData;
+};
diff --git a/src/rendergraph/opengl/backend/basegeometrynode.cpp b/src/rendergraph/opengl/backend/basegeometrynode.cpp
new file mode 100644
index 00000000000..a4b2e303f86
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basegeometrynode.cpp
@@ -0,0 +1,112 @@
+#include "backend/basegeometrynode.h"
+
+#include
+
+#include "backend/shadercache.h"
+#include "rendergraph/engine.h"
+#include "rendergraph/geometrynode.h"
+#include "rendergraph/texture.h"
+
+using namespace rendergraph;
+
+namespace {
+GLenum toGlDrawingMode(DrawingMode mode) {
+ switch (mode) {
+ case DrawingMode::Triangles:
+ return GL_TRIANGLES;
+ case DrawingMode::TriangleStrip:
+ return GL_TRIANGLE_STRIP;
+ }
+}
+} // namespace
+
+void BaseGeometryNode::initialize() {
+ initializeOpenGLFunctions();
+ GeometryNode* pThis = static_cast(this);
+ pThis->material().setShader(ShaderCache::getShaderForMaterial(&pThis->material()));
+ pThis->material().setUniform(0, engine()->matrix());
+}
+
+void BaseGeometryNode::render() {
+ 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;
+ }
+ }
+ }
+
+ // TODO this code assumes all vertices are floats
+ int vertexOffset = 0;
+ for (int i = 0; i < geometry.attributeCount(); i++) {
+ const Geometry::Attribute& attribute = geometry.attributes()[i];
+ int location = material.attributeLocation(i);
+ shader.enableAttributeArray(location);
+ shader.setAttributeArray(location,
+ geometry.vertexDataAs() + vertexOffset,
+ attribute.m_tupleSize,
+ geometry.sizeOfVertex());
+ vertexOffset += attribute.m_tupleSize;
+ }
+
+ // 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();
+}
+
+void BaseGeometryNode::resize(int, int) {
+ assert(engine() != nullptr);
+ GeometryNode* pThis = static_cast(this);
+ pThis->material().setUniform(0, engine()->matrix());
+}
diff --git a/src/rendergraph/opengl/backend/basegeometrynode.h b/src/rendergraph/opengl/backend/basegeometrynode.h
new file mode 100644
index 00000000000..a4a7f609c4a
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basegeometrynode.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+
+#include "backend/basenode.h"
+
+namespace rendergraph {
+class BaseGeometryNode;
+}
+
+class rendergraph::BaseGeometryNode : public rendergraph::BaseNode,
+ public QOpenGLFunctions {
+ public:
+ BaseGeometryNode() = default;
+ virtual ~BaseGeometryNode() = default;
+
+ // called by Engine
+ void initialize() override;
+ void render() override;
+ void resize(int w, int h) override;
+};
diff --git a/src/rendergraph/opengl/backend/basematerial.cpp b/src/rendergraph/opengl/backend/basematerial.cpp
new file mode 100644
index 00000000000..ae6817ce878
--- /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 00000000000..6f2a22ccba2
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basematerial.h
@@ -0,0 +1,32 @@
+#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;
+
+ private:
+ 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 00000000000..498ed8fbe4f
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basematerialshader.cpp
@@ -0,0 +1,19 @@
+#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 00000000000..f5e1b3e195e
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basematerialshader.h
@@ -0,0 +1,26 @@
+#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);
+
+ protected:
+ 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 00000000000..50b925d0bf4
--- /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.cpp b/src/rendergraph/opengl/backend/basenode.cpp
new file mode 100644
index 00000000000..a413325ee29
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basenode.cpp
@@ -0,0 +1,62 @@
+#include "backend/basenode.h"
+
+#include "rendergraph/assert.h"
+#include "rendergraph/engine.h"
+
+using namespace rendergraph;
+
+BaseNode::~BaseNode() {
+ DEBUG_ASSERT(m_pParent == nullptr);
+ DEBUG_ASSERT(m_pEngine == nullptr);
+
+ while (m_pFirstChild) {
+ auto pChild = m_pFirstChild;
+ removeChildNode(pChild);
+ delete pChild;
+ }
+}
+
+// This mimics QSGNode::appendChildNode.
+// Use NodeInterface::appendChildNode(std::unique_ptr pNode)
+// for a more clear transfer of ownership. pChild is considered owned by
+// this at this point.
+void BaseNode::appendChildNode(BaseNode* pChild) {
+ if (m_pLastChild) {
+ pChild->m_pPreviousSibling = m_pLastChild;
+ m_pLastChild->m_pNextSibling = pChild;
+ } else {
+ m_pFirstChild = pChild;
+ }
+ m_pLastChild = pChild;
+ m_pLastChild->m_pParent = this;
+
+ DEBUG_ASSERT(m_pLastChild->m_pNextSibling == nullptr);
+
+ if (m_pEngine) {
+ m_pEngine->add(pChild);
+ }
+}
+
+// This mimics QSGNode::removeChildNode.
+// Use NodeInterface::detachChildNode(BaseNode* pNode)
+// for a more clear transfer of ownership. Otherwise,
+// deleting pChild is responsibility of the caller.
+void BaseNode::removeChildNode(BaseNode* pChild) {
+ if (pChild == m_pFirstChild) {
+ m_pFirstChild = pChild->m_pNextSibling;
+ } else {
+ pChild->m_pPreviousSibling->m_pNextSibling = pChild->m_pNextSibling;
+ }
+
+ if (pChild == m_pLastChild) {
+ m_pLastChild = nullptr;
+ }
+
+ if (pChild->m_pEngine) {
+ pChild->m_pEngine->remove(pChild);
+ }
+
+ pChild->m_pNextSibling = nullptr;
+ pChild->m_pPreviousSibling = nullptr;
+ pChild->m_pParent = nullptr;
+}
diff --git a/src/rendergraph/opengl/backend/basenode.h b/src/rendergraph/opengl/backend/basenode.h
new file mode 100644
index 00000000000..b0079954697
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basenode.h
@@ -0,0 +1,70 @@
+#pragma once
+
+#include
+
+namespace rendergraph {
+class BaseNode;
+class Engine;
+} // namespace rendergraph
+
+class rendergraph::BaseNode {
+ public:
+ BaseNode() = default;
+ virtual ~BaseNode();
+
+ void setUsePreprocess(bool value) {
+ m_usePreprocess = value;
+ }
+ bool usePreprocess() const {
+ return m_usePreprocess;
+ }
+ virtual bool isSubtreeBlocked() const {
+ return false;
+ }
+ virtual void preprocess() {
+ }
+ virtual void render() {
+ }
+ virtual void initialize() {
+ }
+ virtual void resize(int, int) {
+ }
+ void setEngine(Engine* engine) {
+ m_pEngine = engine;
+ }
+ Engine* engine() const {
+ return m_pEngine;
+ }
+
+ // Prefer using NodeInterface::appendChildNode(std::unique_ptr pNode);
+ void appendChildNode(BaseNode* pChild);
+ // Prefer using std::unique_ptr NodeInterface::detachChildNode(BaseNode* pNode);
+ void removeChildNode(BaseNode* pChild);
+
+ BaseNode* parent() const {
+ return m_pParent;
+ }
+ BaseNode* firstChild() const {
+ return m_pFirstChild;
+ }
+ BaseNode* lastChild() const {
+ return m_pLastChild;
+ }
+ BaseNode* nextSibling() const {
+ return m_pNextSibling;
+ }
+ BaseNode* previousSibling() const {
+ return m_pPreviousSibling;
+ }
+
+ private:
+ Engine* m_pEngine{};
+ bool m_usePreprocess{};
+
+ // Mimicking scenegraph node hierarchy. A parent owns its children.
+ BaseNode* m_pParent{};
+ BaseNode* m_pFirstChild{};
+ BaseNode* m_pLastChild{};
+ BaseNode* m_pNextSibling{};
+ BaseNode* m_pPreviousSibling{};
+};
diff --git a/src/rendergraph/opengl/backend/baseopacitynode.h b/src/rendergraph/opengl/backend/baseopacitynode.h
new file mode 100644
index 00000000000..1bd11da2fe0
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseopacitynode.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "backend/basenode.h"
+
+namespace rendergraph {
+class BaseOpacityNode;
+} // namespace rendergraph
+
+class rendergraph::BaseOpacityNode : public rendergraph::BaseNode {
+ public:
+ BaseOpacityNode() = default;
+ virtual ~BaseOpacityNode() = default;
+
+ 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 00000000000..13c3bdfc783
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseopenglnode.cpp
@@ -0,0 +1,18 @@
+#include "backend/baseopenglnode.h"
+
+#include "rendergraph/openglnode.h"
+
+using namespace rendergraph;
+
+void BaseOpenGLNode::initialize() {
+ initializeOpenGLFunctions();
+ initializeGL();
+}
+
+void BaseOpenGLNode::render() {
+ paintGL();
+}
+
+void BaseOpenGLNode::resize(int w, int h) {
+ resizeGL(w, h);
+}
diff --git a/src/rendergraph/opengl/backend/baseopenglnode.h b/src/rendergraph/opengl/backend/baseopenglnode.h
new file mode 100644
index 00000000000..545f5381630
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseopenglnode.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include
+
+#include "backend/basenode.h"
+
+namespace rendergraph {
+class BaseOpenGLNode;
+} // namespace rendergraph
+
+class rendergraph::BaseOpenGLNode : public rendergraph::BaseNode,
+ public QOpenGLFunctions {
+ public:
+ BaseOpenGLNode() = default;
+ virtual ~BaseOpenGLNode() = default;
+
+ void initialize() override;
+ void render() override;
+ void resize(int w, int h) override;
+
+ virtual void initializeGL() {
+ }
+ virtual void paintGL() {
+ }
+ virtual void resizeGL(int, int) {
+ }
+};
diff --git a/src/rendergraph/opengl/backend/basetexture.h b/src/rendergraph/opengl/backend/basetexture.h
new file mode 100644
index 00000000000..1a47541fe24
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basetexture.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include
+
+namespace rendergraph {
+using BaseTexture = QOpenGLTexture;
+} // namespace rendergraph
diff --git a/src/rendergraph/opengl/backend/shadercache.h b/src/rendergraph/opengl/backend/shadercache.h
new file mode 100644
index 00000000000..557a0b6a582
--- /dev/null
+++ b/src/rendergraph/opengl/backend/shadercache.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include
+
+#include "rendergraph/material.h"
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+
+namespace rendergraph {
+class ShaderCache;
+} // namespace rendergraph
+
+class rendergraph::ShaderCache {
+ private:
+ static std::unordered_map>&
+ map() {
+ static std::unordered_map>
+ s_map;
+ return s_map;
+ }
+
+ public:
+ static std::shared_ptr getShaderForMaterial(
+ Material* pMaterial) {
+ auto iter = map().find(pMaterial->type());
+ if (iter != map().end()) {
+ return iter->second;
+ }
+ auto pResult = std::shared_ptr(
+ pMaterial->createShader());
+ map().insert(std::pair>{
+ pMaterial->type(), pResult});
+ return pResult;
+ }
+ static void purge() {
+ std::erase_if(map(), [](const auto& item) {
+ auto const& [key, value] = item;
+ return value.use_count() == 1;
+ });
+ }
+};
diff --git a/src/rendergraph/opengl/engine.cpp b/src/rendergraph/opengl/engine.cpp
new file mode 100644
index 00000000000..f8188375e36
--- /dev/null
+++ b/src/rendergraph/opengl/engine.cpp
@@ -0,0 +1,98 @@
+#include "rendergraph/engine.h"
+
+#include
+#include
+
+using namespace rendergraph;
+
+Engine::Engine(std::unique_ptr pRootNode)
+ : m_pRootNode(std::move(pRootNode)) {
+ add(m_pRootNode.get());
+}
+
+Engine::~Engine() {
+ // Explicitly remove the root node (and tree from the engine before deallocating its vectors)
+ remove(m_pRootNode.get());
+}
+
+void Engine::add(BaseNode* pNode) {
+ assert(pNode->engine() == nullptr || pNode->engine() == this);
+ if (pNode->engine() == nullptr) {
+ pNode->setEngine(this);
+ m_pInitializeNodes.push_back(pNode);
+ if (pNode->usePreprocess()) {
+ m_pPreprocessNodes.push_back(pNode);
+ }
+ pNode = pNode->firstChild();
+ while (pNode) {
+ add(pNode);
+ pNode = pNode->nextSibling();
+ }
+ }
+}
+
+void Engine::remove(BaseNode* pNode) {
+ assert(pNode->engine() == this);
+ pNode->setEngine(nullptr);
+
+ std::erase(m_pInitializeNodes, pNode);
+ std::erase(m_pPreprocessNodes, pNode);
+
+ if (m_pRootNode.get() == pNode) {
+ m_pRootNode.reset();
+ }
+}
+
+void Engine::render() {
+ if (!m_pInitializeNodes.empty()) {
+ for (auto pNode : m_pInitializeNodes) {
+ pNode->initialize();
+ }
+ m_pInitializeNodes.clear();
+ }
+ if (m_pRootNode && !m_pRootNode->isSubtreeBlocked()) {
+ render(m_pRootNode.get());
+ }
+}
+
+void Engine::render(BaseNode* pNode) {
+ pNode->render();
+ pNode = pNode->firstChild();
+ while (pNode) {
+ if (!pNode->isSubtreeBlocked()) {
+ render(pNode);
+ }
+ pNode = pNode->nextSibling();
+ }
+}
+
+void Engine::preprocess() {
+ for (auto pNode : m_pPreprocessNodes) {
+ if (!pNode->isSubtreeBlocked()) {
+ pNode->preprocess();
+ }
+ }
+}
+
+void Engine::resize(int w, int h) {
+ m_matrix.setToIdentity();
+ m_matrix.ortho(QRectF(0.0f, 0.0f, w, h));
+ // TODO
+ // if (waveformRenderer->getOrientation() == Qt::Vertical) {
+ // matrix.rotate(90.f, 0.0f, 0.0f, 1.0f);
+ // matrix.translate(0.f, -waveformRenderer->getWidth() * ratio, 0.f);
+ //}
+
+ if (m_pRootNode) {
+ resize(m_pRootNode.get(), w, h);
+ }
+}
+
+void Engine::resize(BaseNode* pNode, int w, int h) {
+ pNode->resize(w, h);
+ pNode = pNode->firstChild();
+ while (pNode) {
+ resize(pNode, w, h);
+ pNode = pNode->nextSibling();
+ }
+}
diff --git a/src/rendergraph/opengl/geometry.cpp b/src/rendergraph/opengl/geometry.cpp
new file mode 100644
index 00000000000..b2fbfbbcd2a
--- /dev/null
+++ b/src/rendergraph/opengl/geometry.cpp
@@ -0,0 +1,45 @@
+#include "rendergraph/geometry.h"
+
+#include "rendergraph/assert.h"
+#include "rendergraph/attributeset.h"
+
+using namespace rendergraph;
+
+Geometry::Geometry(const AttributeSet& attributeSet, int vertexCount)
+ : BaseGeometry(attributeSet, vertexCount) {
+}
+
+void Geometry::setAttributeValues(int attributePosition, const float* from, int numTuples) {
+ // TODO this code assumes all vertices are floats
+ VERIFY_OR_DEBUG_ASSERT(attributePosition < attributeCount()) {
+ return;
+ }
+ const int vertexOffset = attributes()[attributePosition].m_offset / sizeof(float);
+ const int tupleSize = attributes()[attributePosition].m_tupleSize;
+ const int vertexStride = sizeOfVertex() / sizeof(float);
+ const int vertexSkip = vertexStride - tupleSize;
+
+ VERIFY_OR_DEBUG_ASSERT(vertexOffset + numTuples * vertexStride - vertexSkip <=
+ static_cast(m_vertexData.size())) {
+ return;
+ }
+
+ float* to = m_vertexData.data();
+ to += vertexOffset;
+
+ while (numTuples--) {
+ int k = tupleSize;
+ while (k--) {
+ *to++ = *from++;
+ }
+ to += vertexSkip;
+ }
+}
+
+void Geometry::setDrawingMode(DrawingMode mode) {
+ m_drawingMode = mode;
+}
+
+DrawingMode Geometry::drawingMode() const {
+ return m_drawingMode;
+}
diff --git a/src/rendergraph/opengl/geometrynode.cpp b/src/rendergraph/opengl/geometrynode.cpp
new file mode 100644
index 00000000000..7c5020974bc
--- /dev/null
+++ b/src/rendergraph/opengl/geometrynode.cpp
@@ -0,0 +1,33 @@
+#include "rendergraph/geometrynode.h"
+
+using namespace rendergraph;
+
+GeometryNode::GeometryNode() = default;
+
+void GeometryNode::setUsePreprocess(bool value) {
+ BaseNode::setUsePreprocess(value);
+}
+
+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::markDirtyGeometry() {
+ // not (yet) needed for opengl
+}
+
+void GeometryNode::markDirtyMaterial() {
+ // not (yet) needed for opengl
+}
diff --git a/src/rendergraph/opengl/material.cpp b/src/rendergraph/opengl/material.cpp
new file mode 100644
index 00000000000..4913e8d5b61
--- /dev/null
+++ b/src/rendergraph/opengl/material.cpp
@@ -0,0 +1,9 @@
+#include "rendergraph/material.h"
+
+using namespace rendergraph;
+
+Material::Material(const UniformSet& uniformSet)
+ : m_uniformsCache(uniformSet) {
+}
+
+Material::~Material() = default;
diff --git a/src/rendergraph/opengl/materialshader.cpp b/src/rendergraph/opengl/materialshader.cpp
new file mode 100644
index 00000000000..e8fd233a21d
--- /dev/null
+++ b/src/rendergraph/opengl/materialshader.cpp
@@ -0,0 +1,58 @@
+#include "rendergraph/materialshader.h"
+
+#include
+#ifdef USE_QSHADER_FOR_GL
+#include
+#endif
+
+using namespace rendergraph;
+
+namespace {
+#ifdef USE_QSHADER_FOR_GL
+QString resource(const QString& filename) {
+ return QStringLiteral(":/shaders/rendergraph/%1.qsb").arg(filename);
+}
+
+QByteArray loadShaderCodeFromFile(const QString& path) {
+ QFile file(path);
+ file.open(QIODeviceBase::ReadOnly);
+ QShader qsbShader = QShader::fromSerialized(file.readAll());
+ QShaderKey key(QShader::GlslShader, 120);
+ return qsbShader.shader(key).shader();
+}
+#else
+QString resource(const QString& filename) {
+ return QStringLiteral(":/shaders/rendergraph/%1.gl").arg(filename);
+}
+
+QByteArray loadShaderCodeFromFile(const QString& path) {
+ QFile file(path);
+ file.open(QIODeviceBase::ReadOnly);
+ return file.readAll();
+}
+#endif
+} // namespace
+
+MaterialShader::MaterialShader(const char* vertexShaderFilename,
+ const char* fragmentShaderFilename,
+ const UniformSet& uniformSet,
+ const AttributeSet& attributeSet) {
+ const QString vertexShaderFileFullPath = resource(vertexShaderFilename);
+ const QString fragmentShaderFileFullPath = resource(fragmentShaderFilename);
+
+ addShaderFromSourceCode(QOpenGLShader::Vertex,
+ loadShaderCodeFromFile(vertexShaderFileFullPath));
+ addShaderFromSourceCode(QOpenGLShader::Fragment,
+ loadShaderCodeFromFile(fragmentShaderFileFullPath));
+
+ link();
+
+ for (const auto& attribute : attributeSet.attributes()) {
+ int location = QOpenGLShaderProgram::attributeLocation(attribute.m_name);
+ m_attributeLocations.push_back(location);
+ }
+ for (const auto& uniform : uniformSet.uniforms()) {
+ int location = QOpenGLShaderProgram::uniformLocation(uniform.m_name);
+ m_uniformLocations.push_back(location);
+ }
+}
diff --git a/src/rendergraph/opengl/rendergraph/context.h b/src/rendergraph/opengl/rendergraph/context.h
new file mode 100644
index 00000000000..d5d0611408f
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/context.h
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace rendergraph {
+class Context;
+}
+
+class rendergraph::Context {
+};
diff --git a/src/rendergraph/opengl/rendergraph/engine.h b/src/rendergraph/opengl/rendergraph/engine.h
new file mode 100644
index 00000000000..83e480727e0
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/engine.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "rendergraph/node.h"
+
+namespace rendergraph {
+class Engine;
+} // namespace rendergraph
+
+class rendergraph::Engine {
+ public:
+ Engine(std::unique_ptr pRootNode);
+ ~Engine();
+
+ void render();
+ void resize(int w, int h);
+ void preprocess();
+ void add(BaseNode* pNode);
+ void remove(BaseNode* pNode);
+ const QMatrix4x4& matrix() const {
+ return m_matrix;
+ }
+
+ private:
+ void render(BaseNode* pNode);
+ void resize(BaseNode* pNode, int, int);
+
+ QMatrix4x4 m_matrix;
+ std::unique_ptr m_pRootNode;
+ std::vector m_pPreprocessNodes;
+ std::vector m_pInitializeNodes;
+};
diff --git a/src/rendergraph/opengl/rendergraph/nodeinterface.h b/src/rendergraph/opengl/rendergraph/nodeinterface.h
new file mode 100644
index 00000000000..45159724c49
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/nodeinterface.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "backend/basenode.h"
+
+namespace rendergraph {
+
+template
+class NodeInterface : public T_Node {
+ public:
+ void appendChildNode(std::unique_ptr pNode) {
+ // Transfers ownership to this.
+ BaseNode* pRawNode = pNode.release();
+ // Note: Ideally we would use unique_ptrs internally, but
+ // Qt uses raw pointers for QSGNode hierarchy. For simplicity
+ // we mimic this.
+
+ T_Node::appendChildNode(pRawNode);
+ }
+
+ std::unique_ptr detachChildNode(BaseNode* pNode) {
+ // After removeChildNode, the caller has the responsibility
+ // to deal with the child node. By returning a unique_ptr
+ // we preoprtly transfer ownership to the caller (which
+ // can result in deleting pNode if the caller doesn't
+ // take the unique_ptr).
+ T_Node::removeChildNode(pNode);
+ return std::unique_ptr(pNode);
+ }
+};
+
+} // namespace rendergraph
diff --git a/src/rendergraph/opengl/rendergraph/openglnode.h b/src/rendergraph/opengl/rendergraph/openglnode.h
new file mode 100644
index 00000000000..7499312cb08
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/openglnode.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "backend/baseopenglnode.h"
+
+namespace rendergraph {
+using OpenGLNode = BaseOpenGLNode;
+} // namespace rendergraph
diff --git a/src/rendergraph/opengl/texture.cpp b/src/rendergraph/opengl/texture.cpp
new file mode 100644
index 00000000000..88921bde9df
--- /dev/null
+++ b/src/rendergraph/opengl/texture.cpp
@@ -0,0 +1,58 @@
+#include "rendergraph/texture.h"
+
+#include
+#include
+
+#include "rendergraph/assert.h"
+#include "rendergraph/context.h"
+
+using namespace rendergraph;
+
+namespace {
+QImage premultiplyAlpha(const QImage& image) {
+ // Since the image is passed by const reference, implicit copy cannot be
+ // used, and this Qimage::bits will return a ref the QImage buffer, which
+ // may have a shorter lifecycle that the texture buffer. In order to
+ // workaround this, and because we cannot copy the image as we need to use
+ // the raw bitmap with an explicit image format, we make a manual copy of
+ // the buffer
+ QImage result(image.width(), image.height(), QImage::Format_RGBA8888);
+ if (image.format() == QImage::Format_RGBA8888_Premultiplied) {
+ VERIFY_OR_DEBUG_ASSERT(result.sizeInBytes() == image.sizeInBytes()) {
+ result.fill(QColor(Qt::transparent).rgba());
+ return result;
+ }
+ std::memcpy(result.bits(), image.bits(), result.sizeInBytes());
+ } else {
+ auto convertedImage = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
+ VERIFY_OR_DEBUG_ASSERT(result.sizeInBytes() == convertedImage.sizeInBytes()) {
+ result.fill(QColor(Qt::transparent).rgba());
+ return result;
+ }
+ std::memcpy(result.bits(), convertedImage.bits(), result.sizeInBytes());
+ }
+ return result;
+ /* TODO rendergraph ASK @acolombier to try if the following works as well
+ * (added the .copy())
+ if (image.format() == QImage::Format_RGBA8888_Premultiplied) {
+ return QImage(image.bits(), image.width(), image.height(), QImage::Format_RGBA8888).copy();
+ }
+ return QImage(
+ image.convertToFormat(QImage::Format_RGBA8888_Premultiplied)
+ .bits(),
+ image.width(),
+ image.height(),
+ QImage::Format_RGBA8888).copy();
+ */
+}
+} // namespace
+
+Texture::Texture(Context*, const QImage& image)
+ : m_pTexture(std::make_unique(premultiplyAlpha(image))) {
+ m_pTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
+ m_pTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
+}
+
+qint64 Texture::comparisonKey() const {
+ return static_cast(m_pTexture->textureId());
+}
diff --git a/src/rendergraph/scenegraph/CMakeLists.txt b/src/rendergraph/scenegraph/CMakeLists.txt
new file mode 100644
index 00000000000..ad96eb1e491
--- /dev/null
+++ b/src/rendergraph/scenegraph/CMakeLists.txt
@@ -0,0 +1,73 @@
+add_library(rendergraph_sg
+../common/rendergraph/attributeinit.h
+../common/rendergraph/attributeset.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
+attributeset.cpp
+backend/baseattributeset.cpp
+backend/baseattributeset.h
+backend/basegeometry.h
+backend/basegeometrynode.h
+backend/basematerial.cpp
+backend/basematerial.h
+backend/basematerialshader.cpp
+backend/basematerialtype.h
+backend/basenode.h
+backend/baseopacitynode.h
+backend/basetexture.h
+context.cpp
+geometry.cpp
+geometrynode.cpp
+material.cpp
+materialshader.cpp
+rendergraph/context.h
+texture.cpp
+)
+
+target_link_libraries(rendergraph_sg PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+if(WIN32)
+ find_package(Microsoft.GSL CONFIG)
+ if(Microsoft.GSL_FOUND)
+ target_link_libraries(rendergraph_sg PRIVATE Microsoft.GSL::GSL)
+ else()
+ # check if the headers have been installed without cmake config (< 3.1.0)
+ check_include_file_cxx(gsl/gsl HAVE_GSL_GSL)
+ if(NOT HAVE_GSL_GSL)
+ unset(HAVE_GSL_GSL CACHE) # unset cache to re-evaluate this until it succeeds. check_include_file_cxx() has no REQUIRED flag.
+ message(FATAL_ERROR "ms-gsl development headers (libmsgsl-dev) not found")
+ endif()
+ endif()
+endif()
+target_compile_definitions(rendergraph_sg PRIVATE rendergraph=rendergraph_sg)
+
+target_include_directories(rendergraph_sg PUBLIC . ../common)
diff --git a/src/rendergraph/scenegraph/attributeset.cpp b/src/rendergraph/scenegraph/attributeset.cpp
new file mode 100644
index 00000000000..04d55fcae8f
--- /dev/null
+++ b/src/rendergraph/scenegraph/attributeset.cpp
@@ -0,0 +1,10 @@
+#include "rendergraph/attributeset.h"
+
+#include "backend/baseattributeset.h"
+
+using namespace rendergraph;
+
+AttributeSet::AttributeSet(std::initializer_list list, const std::vector&)
+ : BaseAttributeSet(list) {
+ // names are not used in scenegraph
+}
diff --git a/src/rendergraph/scenegraph/backend/baseattributeset.cpp b/src/rendergraph/scenegraph/backend/baseattributeset.cpp
new file mode 100644
index 00000000000..0f0bf0b8e7a
--- /dev/null
+++ b/src/rendergraph/scenegraph/backend/baseattributeset.cpp
@@ -0,0 +1,27 @@
+#include "backend/baseattributeset.h"
+
+using namespace rendergraph;
+
+namespace {
+int toQSGGeometryType(const PrimitiveType& t) {
+ switch (t) {
+ case PrimitiveType::Float:
+ return QSGGeometry::FloatType;
+ case PrimitiveType::UInt:
+ return QSGGeometry::UnsignedIntType;
+ }
+}
+} // namespace
+
+BaseAttributeSetHelper::BaseAttributeSetHelper(std::initializer_list list) {
+ int i = 0;
+ m_sgAttributes.reserve(list.size());
+ for (auto item : list) {
+ const int count = static_cast(m_sgAttributes.size());
+ const bool isPosition = count == 0;
+ m_sgAttributes.push_back(QSGGeometry::Attribute::create(count,
+ item.m_tupleSize,
+ toQSGGeometryType(item.m_primitiveType),
+ isPosition));
+ }
+}
diff --git a/src/rendergraph/scenegraph/backend/baseattributeset.h b/src/rendergraph/scenegraph/backend/baseattributeset.h
new file mode 100644
index 00000000000..7c33e25192a
--- /dev/null
+++ b/src/rendergraph/scenegraph/backend/baseattributeset.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "rendergraph/attributeinit.h"
+
+namespace rendergraph {
+class BaseAttributeSet;
+class BaseAttributeSetHelper;
+} // namespace rendergraph
+
+class rendergraph::BaseAttributeSetHelper {
+ protected:
+ BaseAttributeSetHelper(std::initializer_list list);
+ std::vector m_sgAttributes;
+};
+
+class rendergraph::BaseAttributeSet
+ : protected rendergraph::BaseAttributeSetHelper,
+ public QSGGeometry::AttributeSet {
+ protected:
+ BaseAttributeSet(std::initializer_list list)
+ : BaseAttributeSetHelper(list),
+ QSGGeometry::AttributeSet{static_cast(m_sgAttributes.size()),
+ calculateSizeOfVertex(list),
+ m_sgAttributes.data()} {
+ }
+ static int calculateSizeOfVertex(std::initializer_list