diff --git a/CMakeLists.txt b/CMakeLists.txt index 134c27e3c51b..39b784079cf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3809,6 +3809,11 @@ if(VINYLCONTROL) target_link_libraries(mixxx-lib PRIVATE mixxx-xwax) endif() +# rendergraph +add_subdirectory(src/rendergraph/opengl) +add_subdirectory(res/shaders/rendergraph) +target_link_libraries(mixxx-lib PUBLIC rendergraph_gl) + # WavPack audio file support find_package(wavpack) default_option(WAVPACK "WavPack audio file support" "wavpack_FOUND") diff --git a/res/mixxx.qrc b/res/mixxx.qrc index 8011fe39f943..c21ec6a8008b 100644 --- a/res/mixxx.qrc +++ b/res/mixxx.qrc @@ -96,5 +96,9 @@ shaders/passthrough.vert shaders/rgbsignal.frag shaders/stackedsignal.frag + shaders/rendergraph/endoftrack.frag.gl + shaders/rendergraph/endoftrack.vert.gl + shaders/rendergraph/texture.frag.gl + shaders/rendergraph/texture.vert.gl diff --git a/res/shaders/rendergraph/CMakeLists.txt b/res/shaders/rendergraph/CMakeLists.txt new file mode 100644 index 000000000000..dfcecc593a46 --- /dev/null +++ b/res/shaders/rendergraph/CMakeLists.txt @@ -0,0 +1,47 @@ +set(shaders + endoftrack.frag + endoftrack.vert + pattern.frag + pattern.vert + rgb.frag + rgb.vert + rgba.frag + rgba.vert + texture.frag + texture.vert + unicolor.frag + unicolor.vert +) + +qt6_add_shaders(rendergraph_sg "shaders-qsb" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + /shaders/rendergraph + FILES + ${shaders} +) + +if(USE_QSHADER_FOR_GL) + message(STATUS "Adding qsb shaders to rendergraph_gl") + qt6_add_shaders(rendergraph_gl "shaders-qsb" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + /shaders/rendergraph + FILES + ${shaders} + ) +else() + message(STATUS "Adding gl shaders to rendergraph_gl") + include(generated_shaders_gl.cmake) + + qt_add_resources(rendergraph_gl "shaders-gl" + PREFIX + /shaders/rendergraph + FILES + ${generated_shaders_gl} + ) +endif() diff --git a/res/shaders/rendergraph/README b/res/shaders/rendergraph/README new file mode 100644 index 000000000000..644c2ce76477 --- /dev/null +++ b/res/shaders/rendergraph/README @@ -0,0 +1,10 @@ +The CMakeLists.txt in this folder generates qsb shader bundles from the spirv shaders. + +The qsb files can be used by QML / Qt scene graph QSGShader. Since Qt >= 6.6 we can load the qsb files and extract the glsl shaders +programmatically with QShader and use the with QOpenGLShader. For Qt < 6.6 the files have to generated manually with the script: + +generate_shaders_gl.pl + +(Make sure qsb and spirv commands are in your path. E.g: +export PATH=$PATH:~/VulkanSDK/1.3.283.0/macOS/bin:~/Qt/6.7.2/macos/bin +) diff --git a/res/shaders/rendergraph/endoftrack.frag b/res/shaders/rendergraph/endoftrack.frag new file mode 100644 index 000000000000..0f6712e87d5d --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.frag @@ -0,0 +1,17 @@ +#version 440 + +layout(location = 0) in float vGradient; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + vec4 color; +} +ubuf; + +void main() { + float minAlpha = 0.5 * ubuf.color.w; + float maxAlpha = 0.83 * ubuf.color.w; + float alpha = mix(minAlpha, maxAlpha, max(0.0, vGradient)); + // premultiple alpha + fragColor = vec4(ubuf.color.xyz * alpha, alpha); +} diff --git a/res/shaders/rendergraph/endoftrack.frag.gl b/res/shaders/rendergraph/endoftrack.frag.gl new file mode 100644 index 000000000000..78de913751a3 --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.frag.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + vec4 color; +}; + +uniform buf ubuf; + +varying float vGradient; + +void main() +{ + float minAlpha = 0.5 * ubuf.color.w; + float maxAlpha = 0.829999983310699462890625 * ubuf.color.w; + float alpha = mix(minAlpha, maxAlpha, max(0.0, vGradient)); + gl_FragData[0] = vec4(ubuf.color.xyz * alpha, alpha); +} diff --git a/res/shaders/rendergraph/endoftrack.vert b/res/shaders/rendergraph/endoftrack.vert new file mode 100644 index 000000000000..101cdb324e4e --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.vert @@ -0,0 +1,10 @@ +#version 440 + +layout(location = 0) in vec4 position; +layout(location = 1) in float gradient; +layout(location = 0) out float vGradient; + +void main() { + vGradient = gradient; + gl_Position = position; +} diff --git a/res/shaders/rendergraph/endoftrack.vert.gl b/res/shaders/rendergraph/endoftrack.vert.gl new file mode 100644 index 000000000000..da4626bbf7c5 --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.vert.gl @@ -0,0 +1,12 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying float vGradient; +attribute float gradient; +attribute vec4 position; + +void main() +{ + vGradient = gradient; + gl_Position = position; +} diff --git a/res/shaders/rendergraph/generate_shaders_gl.pl b/res/shaders/rendergraph/generate_shaders_gl.pl new file mode 100755 index 000000000000..c528eb947475 --- /dev/null +++ b/res/shaders/rendergraph/generate_shaders_gl.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +my @files = (glob("*.vert"),glob("*.frag")); + +open(GENERATED,">generated_shaders_gl.cmake"); +print(GENERATED "set(generated_shaders_gl\n"); +for $file (@files) +{ + system("qsb","--glsl","120",$file,"-o","/tmp/$$-$file.qsb"); + open(INFILE,"qsb --dump /tmp/$$-$file.qsb|"); + open(OUTFILE,">$file.gl"); + $ok = 0; + $comment_added = 0; + print "Generating $file.gl from $file\n"; + while () + { + if ($in_shader_block == 2) + { + if (m/^\*\*/) + { + $in_shader_block = 0; + $ok = 1; + } + else + { + if (!$comment_added) + { + if (!m/^#/) + { + print(OUTFILE "//// GENERATED - EDITS WILL BE OVERWRITTEN\n"); + $comment_added = 1; + } + } + print OUTFILE "$_"; + } + } + elsif ($in_shader_block == 1) + { + chomp($_); + if ($_ eq "Contents:") + { + $in_shader_block = 2; + } + } + else + { + chomp($_); + if ($_ eq "Shader 1: GLSL 120 [Standard]") + { + $in_shader_block = 1; + } + } + } + close INFILE; + close OUTFILE; + if($ok) + { + print(GENERATED " $file.gl\n"); + } + else + { + print STDERR "Failed to generated $file.gl"; + unlink("$file.gl") + } + unlink("/tmp/$$-$file.qsb"); +} +print(GENERATED ")\n"); +close GENERATED; diff --git a/res/shaders/rendergraph/generated_shaders_gl.cmake b/res/shaders/rendergraph/generated_shaders_gl.cmake new file mode 100644 index 000000000000..951c85c3669a --- /dev/null +++ b/res/shaders/rendergraph/generated_shaders_gl.cmake @@ -0,0 +1,14 @@ +set(generated_shaders_gl + endoftrack.vert.gl + pattern.vert.gl + rgb.vert.gl + rgba.vert.gl + texture.vert.gl + unicolor.vert.gl + endoftrack.frag.gl + pattern.frag.gl + rgb.frag.gl + rgba.frag.gl + texture.frag.gl + unicolor.frag.gl +) diff --git a/res/shaders/rendergraph/pattern.frag b/res/shaders/rendergraph/pattern.frag new file mode 100644 index 000000000000..5aa3d1556b1e --- /dev/null +++ b/res/shaders/rendergraph/pattern.frag @@ -0,0 +1,9 @@ +#version 440 + +layout(binding = 1) uniform sampler2D texture1; +layout(location = 0) in vec2 vTexcoord; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = texture(texture1, fract(vTexcoord)); +} diff --git a/res/shaders/rendergraph/pattern.frag.gl b/res/shaders/rendergraph/pattern.frag.gl new file mode 100644 index 000000000000..376c71668ba4 --- /dev/null +++ b/res/shaders/rendergraph/pattern.frag.gl @@ -0,0 +1,11 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +uniform sampler2D texture1; + +varying vec2 vTexcoord; + +void main() +{ + gl_FragData[0] = texture2D(texture1, fract(vTexcoord)); +} diff --git a/res/shaders/rendergraph/pattern.vert b/res/shaders/rendergraph/pattern.vert new file mode 100644 index 000000000000..07b3d7f1f3ba --- /dev/null +++ b/res/shaders/rendergraph/pattern.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; +layout(location = 0) out vec2 vTexcoord; + +void main() { + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/pattern.vert.gl b/res/shaders/rendergraph/pattern.vert.gl new file mode 100644 index 000000000000..a3d58014be32 --- /dev/null +++ b/res/shaders/rendergraph/pattern.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec2 vTexcoord; +attribute vec2 texcoord; +attribute vec4 position; + +void main() +{ + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgb.frag b/res/shaders/rendergraph/rgb.frag new file mode 100644 index 000000000000..0a808489f5b2 --- /dev/null +++ b/res/shaders/rendergraph/rgb.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec3 vColor; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(vColor, 1.0); +} diff --git a/res/shaders/rendergraph/rgb.frag.gl b/res/shaders/rendergraph/rgb.frag.gl new file mode 100644 index 000000000000..b8a61f8682f9 --- /dev/null +++ b/res/shaders/rendergraph/rgb.frag.gl @@ -0,0 +1,9 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying vec3 vColor; + +void main() +{ + gl_FragData[0] = vec4(vColor, 1.0); +} diff --git a/res/shaders/rendergraph/rgb.vert b/res/shaders/rendergraph/rgb.vert new file mode 100644 index 000000000000..6568d01f187c --- /dev/null +++ b/res/shaders/rendergraph/rgb.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec3 color; +layout(location = 0) out vec3 vColor; + +void main() { + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgb.vert.gl b/res/shaders/rendergraph/rgb.vert.gl new file mode 100644 index 000000000000..53e86e4501cc --- /dev/null +++ b/res/shaders/rendergraph/rgb.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec3 vColor; +attribute vec3 color; +attribute vec4 position; + +void main() +{ + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgba.frag b/res/shaders/rendergraph/rgba.frag new file mode 100644 index 000000000000..5cf90a770eae --- /dev/null +++ b/res/shaders/rendergraph/rgba.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(vColor.xyz * vColor.w, vColor.w); // premultiple alpha +} diff --git a/res/shaders/rendergraph/rgba.frag.gl b/res/shaders/rendergraph/rgba.frag.gl new file mode 100644 index 000000000000..a831457b9681 --- /dev/null +++ b/res/shaders/rendergraph/rgba.frag.gl @@ -0,0 +1,9 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying vec4 vColor; + +void main() +{ + gl_FragData[0] = vec4(vColor.xyz * vColor.w, vColor.w); +} diff --git a/res/shaders/rendergraph/rgba.vert b/res/shaders/rendergraph/rgba.vert new file mode 100644 index 000000000000..d5ce8b2d9db6 --- /dev/null +++ b/res/shaders/rendergraph/rgba.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec4 color; +layout(location = 0) out vec4 vColor; + +void main() { + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgba.vert.gl b/res/shaders/rendergraph/rgba.vert.gl new file mode 100644 index 000000000000..df2bcf93236e --- /dev/null +++ b/res/shaders/rendergraph/rgba.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec4 vColor; +attribute vec4 color; +attribute vec4 position; + +void main() +{ + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/texture.frag b/res/shaders/rendergraph/texture.frag new file mode 100644 index 000000000000..bbe37bccd69c --- /dev/null +++ b/res/shaders/rendergraph/texture.frag @@ -0,0 +1,9 @@ +#version 440 + +layout(binding = 1) uniform sampler2D texture1; +layout(location = 0) in vec2 vTexcoord; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = texture(texture1, vTexcoord); +} diff --git a/res/shaders/rendergraph/texture.frag.gl b/res/shaders/rendergraph/texture.frag.gl new file mode 100644 index 000000000000..b2d03f1352c6 --- /dev/null +++ b/res/shaders/rendergraph/texture.frag.gl @@ -0,0 +1,11 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +uniform sampler2D texture1; + +varying vec2 vTexcoord; + +void main() +{ + gl_FragData[0] = texture2D(texture1, vTexcoord); +} diff --git a/res/shaders/rendergraph/texture.vert b/res/shaders/rendergraph/texture.vert new file mode 100644 index 000000000000..07b3d7f1f3ba --- /dev/null +++ b/res/shaders/rendergraph/texture.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; +layout(location = 0) out vec2 vTexcoord; + +void main() { + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/texture.vert.gl b/res/shaders/rendergraph/texture.vert.gl new file mode 100644 index 000000000000..a3d58014be32 --- /dev/null +++ b/res/shaders/rendergraph/texture.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec2 vTexcoord; +attribute vec2 texcoord; +attribute vec4 position; + +void main() +{ + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/texture.vert.qsb b/res/shaders/rendergraph/texture.vert.qsb new file mode 100644 index 000000000000..84fa7e916fe4 Binary files /dev/null and b/res/shaders/rendergraph/texture.vert.qsb differ diff --git a/res/shaders/rendergraph/unicolor.frag b/res/shaders/rendergraph/unicolor.frag new file mode 100644 index 000000000000..7099bb8f3dd5 --- /dev/null +++ b/res/shaders/rendergraph/unicolor.frag @@ -0,0 +1,13 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; + vec4 color; +} +ubuf; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w); // premultiply alpha +} diff --git a/res/shaders/rendergraph/unicolor.frag.gl b/res/shaders/rendergraph/unicolor.frag.gl new file mode 100644 index 000000000000..bfef503120bd --- /dev/null +++ b/res/shaders/rendergraph/unicolor.frag.gl @@ -0,0 +1,15 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; + vec4 color; +}; + +uniform buf ubuf; + +void main() +{ + gl_FragData[0] = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w); +} diff --git a/res/shaders/rendergraph/unicolor.vert b/res/shaders/rendergraph/unicolor.vert new file mode 100644 index 000000000000..9e268c18fbaa --- /dev/null +++ b/res/shaders/rendergraph/unicolor.vert @@ -0,0 +1,13 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; + vec4 color; +} +ubuf; + +layout(location = 0) in vec4 position; + +void main() { + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/unicolor.vert.gl b/res/shaders/rendergraph/unicolor.vert.gl new file mode 100644 index 000000000000..d126f3dab584 --- /dev/null +++ b/res/shaders/rendergraph/unicolor.vert.gl @@ -0,0 +1,17 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; + vec4 color; +}; + +uniform buf ubuf; + +attribute vec4 position; + +void main() +{ + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/CMakeLists.txt b/src/rendergraph/CMakeLists.txt new file mode 100644 index 000000000000..059bbdaa0504 --- /dev/null +++ b/src/rendergraph/CMakeLists.txt @@ -0,0 +1,10 @@ +find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core) +message(STATUS "Qt version ${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}") + +if (QT_VERSION_MINOR GREATER_EQUAL 6) +set(USE_QSHADER_FOR_GL ON) +endif() + +add_subdirectory(opengl) +add_subdirectory(scenegraph) +add_subdirectory(../../res/shaders/rendergraph shaders) diff --git a/src/rendergraph/README b/src/rendergraph/README new file mode 100644 index 000000000000..8b1720e22c8c --- /dev/null +++ b/src/rendergraph/README @@ -0,0 +1,63 @@ +rendergraph is an abstraction layer that can be compiled with one of two backends: + +- scenegraph (QtQuick scene graph classes) +- opengl (custom classes using QOpenGL) + +This abstraction layer follows the design of the QtQuick scene graph, with classes such as Material, Geometry, Node and GeometryNode, but it only gives access to and only uses a subset of its functionality. + +The scenegraph backend class the underlying QtQuick scene graph classes directly or almost directly. The opengl layer implements classes that mimic the behaviour of the +Qt scene graph classes. + +The objective of rendergraph is to be able to write code that can be used verbatim within the context of both QWidgets applications and QML applications. This includes using the same Vulkan-style GLSL shader code for both. (The QSGMaterialShader uses the qsb files, QOpenGLShader uses the OpenGL shader code generated by qsb) + +Primarily, rendering with rendergraph uses a graph of Node's, typically of type GeometryNode. A GeometryNode uses a Geometry, which contains the vertex data of what the node renders (positional, texture coordinates, color information...) and a Material, which is the interface to the underlying shader. Uniform data is set through the Material. The QtQuick scene graph documentation is a good started point to understand the design. + +The code is organized as follows: + +common/rendergraph + Public header files. This is the primary API, identical for both backends, and the only files that should be included to write code compatible with both backends. + +common + Implementation of the functionality declared in the common/rendergraph headers that is identical for both backends. + The general guideline is "code duplicated between the backends should be in the common layer". + +common/rendergraph/material + Several basic 'materials' for common rendering operations (e.g. uniform or variying color, texture) + +scenegraph + Implementation of the functionality declared in the common/rendergraph headers that is specific to call the scenegraph backend + +scenegraph/rendergraph + Public header files specific for the opengl backend. This is the layer between QtQuick application code and the common rendergraph API. + +scenegraph/backend + The scenegraph backend implementation, the layer between the common/rendergraph API and the underlying QtQuick scene graph classes. + +opengl + Implementation of the functionality declared in the common/rendergraph headers that is specific to call the opengl backend + +opengl/rendergraph + Public header files specific for the opengl backend. This is the layer between QWidgets/QOpenGL application code and the common rendergraph API. + +opengl/backend + The opengl backend implementation, the layer between the common/rendergraph API and custom implemented classes that mimic the behaviour QtQuick scene graph classes. + +Furthermore, some simple examples: + +examples/sg_example + An example QtQuick application that uses the scenegraph backend + +examples/gl_example + An example QOpenGLWindow based application that uses the opengl backend + +examples/common + The graphics code that is identical for both example + +../../res/shaders/rendergraph/ + The qsb and (generated) gl shader files + See ../../res/shaders/rendergraph/README for more info + +More advanced use of rendergraph can be found in the waveform/renderers/allshader classes, which have partially been ported the rendergraph. + + +m0dB diff --git a/src/rendergraph/common/attributeset.cpp b/src/rendergraph/common/attributeset.cpp new file mode 100644 index 000000000000..a2fef348d9c0 --- /dev/null +++ b/src/rendergraph/common/attributeset.cpp @@ -0,0 +1,12 @@ +#include "rendergraph/attributeset.h" + +using namespace rendergraph; + +AttributeSet::AttributeSet(std::initializer_list list, + const std::vector& names) + : BaseAttributeSet(list, names) { +} + +const std::vector& AttributeSet::attributes() const { + return m_attributes; +} diff --git a/src/rendergraph/common/node.cpp b/src/rendergraph/common/node.cpp new file mode 100644 index 000000000000..24f5d29d3eeb --- /dev/null +++ b/src/rendergraph/common/node.cpp @@ -0,0 +1,7 @@ +#include "rendergraph/node.h" + +using namespace rendergraph; + +Node::Node() + : TreeNode(this) { +} diff --git a/src/rendergraph/common/opacitynode.cpp b/src/rendergraph/common/opacitynode.cpp new file mode 100644 index 000000000000..caa6526626f6 --- /dev/null +++ b/src/rendergraph/common/opacitynode.cpp @@ -0,0 +1,7 @@ +#include "rendergraph/opacitynode.h" + +using namespace rendergraph; + +OpacityNode::OpacityNode() + : TreeNode(this) { +} diff --git a/src/rendergraph/common/rendergraph/assert.h b/src/rendergraph/common/rendergraph/assert.h new file mode 100644 index 000000000000..431ddbd12d75 --- /dev/null +++ b/src/rendergraph/common/rendergraph/assert.h @@ -0,0 +1 @@ +#include "../../../util/assert.h" diff --git a/src/rendergraph/common/rendergraph/attribute.h b/src/rendergraph/common/rendergraph/attribute.h new file mode 100644 index 000000000000..150ad00c3aaf --- /dev/null +++ b/src/rendergraph/common/rendergraph/attribute.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "rendergraph/types.h" + +namespace rendergraph { +struct Attribute; +} + +struct rendergraph::Attribute { + const int m_tupleSize; + const PrimitiveType m_primitiveType; + const QString m_name; + + Attribute(int tupleSize, PrimitiveType primitiveType, QString name = {}) + : m_tupleSize{tupleSize}, + m_primitiveType{primitiveType}, + m_name{std::move(name)} { + } + + template + static Attribute create() { + return Attribute(tupleSizeOf(), primitiveTypeOf()); + } +}; diff --git a/src/rendergraph/common/rendergraph/attributeset.h b/src/rendergraph/common/rendergraph/attributeset.h new file mode 100644 index 000000000000..589a6e51bfc3 --- /dev/null +++ b/src/rendergraph/common/rendergraph/attributeset.h @@ -0,0 +1,20 @@ +#pragma once + +#include "backend/baseattributeset.h" + +namespace rendergraph { +class AttributeSet; +} + +class rendergraph::AttributeSet : public rendergraph::BaseAttributeSet { + public: + AttributeSet(std::initializer_list list, const std::vector& names); + const std::vector& attributes() const; +}; + +namespace rendergraph { +template +AttributeSet makeAttributeSet(const std::vector& names) { + return AttributeSet({(Attribute::create())...}, names); +} +} // namespace rendergraph diff --git a/src/rendergraph/common/rendergraph/geometry.h b/src/rendergraph/common/rendergraph/geometry.h new file mode 100644 index 000000000000..c44572c3b9a2 --- /dev/null +++ b/src/rendergraph/common/rendergraph/geometry.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +#include "backend/basegeometry.h" +#include "rendergraph/attributeset.h" + +namespace rendergraph { +class Geometry; +} // namespace rendergraph + +class rendergraph::Geometry : public rendergraph::BaseGeometry { + public: + struct Point2D { + QVector2D position2D; + Point2D(float x, float y) + : position2D{x, y} { + } + }; + + struct TexturedPoint2D { + QVector2D position2D; + QVector2D texcoord2D; + TexturedPoint2D(float x, float y, float tx, float ty) + : position2D{x, y}, + texcoord2D{tx, ty} { + } + }; + + struct RGBColoredPoint2D { + QVector2D position2D; + QVector3D color3D; + RGBColoredPoint2D(float x, float y, float r, float g, float b) + : position2D{x, y}, + color3D{r, g, b} { + } + }; + + struct RGBAColoredPoint2D { + QVector2D position2D; + QVector4D color4D; + RGBAColoredPoint2D(float x, float y, float r, float g, float b, float a) + : position2D{x, y}, + color4D{r, g, b, a} { + } + }; + + enum class DrawingMode { + Triangles, + TriangleStrip + }; + + Geometry(const rendergraph::AttributeSet& attributeSet, int vertexCount); + ~Geometry(); + + void allocate(int vertexCount); + + void setAttributeValues(int attributePosition, const float* data, int numTuples); + + float* vertexData(); + + template + T* vertexDataAs() { + return reinterpret_cast(vertexData()); + } + + DrawingMode drawingMode() const; + void setDrawingMode(DrawingMode mode); +}; diff --git a/src/rendergraph/common/rendergraph/geometrynode.h b/src/rendergraph/common/rendergraph/geometrynode.h new file mode 100644 index 000000000000..44384b370bf5 --- /dev/null +++ b/src/rendergraph/common/rendergraph/geometrynode.h @@ -0,0 +1,35 @@ +#pragma once + +#include "backend/basegeometrynode.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material.h" +#include "rendergraph/treenode.h" + +namespace rendergraph { +class GeometryNode; +} // namespace rendergraph + +class rendergraph::GeometryNode : public rendergraph::BaseGeometryNode, + public rendergraph::TreeNode { + public: + GeometryNode(); + + template + void initForRectangles(int numRectangles) { + const int verticesPerRectangle = 6; // 2 rectangles + setGeometry(std::make_unique(T_Material::attributes(), + numRectangles * verticesPerRectangle)); + setMaterial(std::make_unique()); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); + } + + void setMaterial(std::unique_ptr material); + void setGeometry(std::unique_ptr geometry); + + Geometry& geometry() const; + Material& material() const; + + private: + std::unique_ptr m_pMaterial; + std::unique_ptr m_pGeometry; +}; diff --git a/src/rendergraph/common/rendergraph/material.h b/src/rendergraph/common/rendergraph/material.h new file mode 100644 index 000000000000..e15ef5c0f1ce --- /dev/null +++ b/src/rendergraph/common/rendergraph/material.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include "backend/basematerial.h" +#include "rendergraph/assert.h" +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformscache.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class Material; +} // namespace rendergraph + +class rendergraph::Material : public rendergraph::BaseMaterial { + public: + Material(const UniformSet& uniformSet); + virtual ~Material(); + + // see QSGMaterial::compare. + // TODO decide if this should be virtual. QSGMaterial::compare is virtual + // to concrete Material can implement a compare function, but in rendergraph + // we can compare the uniforms cache and texture already here, which seems + // sufficient. + int compare(const Material* pOther) const { + DEBUG_ASSERT(type() == pOther->type()); + int cacheCompareResult = std::memcmp(m_uniformsCache.data(), + pOther->m_uniformsCache.data(), + m_uniformsCache.size()); + if (cacheCompareResult != 0) { + return cacheCompareResult; + } + // TODO multiple textures + if (!texture(0) || !pOther->texture(0)) { + return texture(0) ? 1 : -1; + } + + const qint64 diff = texture(0)->comparisonKey() - pOther->texture(0)->comparisonKey(); + return diff < 0 ? -1 : (diff > 0 ? 1 : 0); + } + + virtual std::unique_ptr createShader() const = 0; + + template + void setUniform(int uniformIndex, const T& value) { + m_uniformsCache.set(uniformIndex, value); + m_uniformsCacheDirty = true; + } + + const UniformsCache& uniformsCache() const { + return m_uniformsCache; + } + + bool clearUniformsCacheDirty() { + if (m_uniformsCacheDirty) { + m_uniformsCacheDirty = false; + return true; + } + return false; + } + + virtual Texture* texture(int) const { + return nullptr; + } + + private: + UniformsCache m_uniformsCache; + bool m_uniformsCacheDirty{}; +}; diff --git a/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp new file mode 100644 index 000000000000..ee35b465934b --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp @@ -0,0 +1,33 @@ +#include "endoftrackmaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +EndOfTrackMaterial::EndOfTrackMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& EndOfTrackMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "gradient"}); + return set; +} + +/* static */ const UniformSet& EndOfTrackMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.color"}); + return set; +} + +MaterialType* EndOfTrackMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr EndOfTrackMaterial::createShader() const { + return std::make_unique( + "endoftrack.vert", "endoftrack.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h new file mode 100644 index 000000000000..94c80693799e --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h @@ -0,0 +1,19 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class EndOfTrackMaterial; +} + +class rendergraph::EndOfTrackMaterial : public rendergraph::Material { + public: + EndOfTrackMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/material/patternmaterial.cpp b/src/rendergraph/common/rendergraph/material/patternmaterial.cpp new file mode 100644 index 000000000000..864f4a60adf9 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/patternmaterial.cpp @@ -0,0 +1,33 @@ +#include "patternmaterial.h" + +#include +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +using namespace rendergraph; + +PatternMaterial::PatternMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& PatternMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "texcoord"}); + return set; +} + +/* static */ const UniformSet& PatternMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* PatternMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr PatternMaterial::createShader() const { + return std::make_unique( + "pattern.vert", "pattern.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/patternmaterial.h b/src/rendergraph/common/rendergraph/material/patternmaterial.h new file mode 100644 index 000000000000..661246abd056 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/patternmaterial.h @@ -0,0 +1,32 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class PatternMaterial; +} + +class rendergraph::PatternMaterial : public rendergraph::Material { + public: + PatternMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; + + Texture* texture(int) const override { + return m_pTexture.get(); + } + + void setTexture(std::unique_ptr texture) { + m_pTexture = std::move(texture); + } + + private: + std::unique_ptr m_pTexture; +}; diff --git a/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp b/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp new file mode 100644 index 000000000000..e81382bf9ce9 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp @@ -0,0 +1,34 @@ +#include "rgbamaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +RGBAMaterial::RGBAMaterial() + : Material(uniforms()) { +} + +// static +const AttributeSet& RGBAMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "color"}); + return set; +} + +/* static */ const UniformSet& RGBAMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* RGBAMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr RGBAMaterial::createShader() const { + return std::make_unique( + "rgba.vert", "rgba.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/rgbamaterial.h b/src/rendergraph/common/rendergraph/material/rgbamaterial.h new file mode 100644 index 000000000000..0921ebb4faef --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.h @@ -0,0 +1,19 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class RGBAMaterial; +} + +class rendergraph::RGBAMaterial : public rendergraph::Material { + public: + RGBAMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp b/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp new file mode 100644 index 000000000000..455555d1e97a --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp @@ -0,0 +1,33 @@ +#include "rgbmaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +RGBMaterial::RGBMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& RGBMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "color"}); + return set; +} + +/* static */ const UniformSet& RGBMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* RGBMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr RGBMaterial::createShader() const { + return std::make_unique( + "rgb.vert", "rgb.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/rgbmaterial.h b/src/rendergraph/common/rendergraph/material/rgbmaterial.h new file mode 100644 index 000000000000..cfe1fde7782d --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.h @@ -0,0 +1,19 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class RGBMaterial; +} + +class rendergraph::RGBMaterial : public rendergraph::Material { + public: + RGBMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/material/texturematerial.cpp b/src/rendergraph/common/rendergraph/material/texturematerial.cpp new file mode 100644 index 000000000000..b3f61767626c --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/texturematerial.cpp @@ -0,0 +1,33 @@ +#include "texturematerial.h" + +#include +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +using namespace rendergraph; + +TextureMaterial::TextureMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& TextureMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "texcoord"}); + return set; +} + +/* static */ const UniformSet& TextureMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* TextureMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr TextureMaterial::createShader() const { + return std::make_unique( + "texture.vert", "texture.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/texturematerial.h b/src/rendergraph/common/rendergraph/material/texturematerial.h new file mode 100644 index 000000000000..2fa5a2310cca --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/texturematerial.h @@ -0,0 +1,32 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class TextureMaterial; +} + +class rendergraph::TextureMaterial : public rendergraph::Material { + public: + TextureMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; + + Texture* texture(int /*binding*/) const override { + return m_pTexture.get(); + } + + void setTexture(std::unique_ptr texture) { + m_pTexture = std::move(texture); + } + + private: + std::unique_ptr m_pTexture; +}; diff --git a/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp b/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp new file mode 100644 index 000000000000..d67fd16320c9 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp @@ -0,0 +1,33 @@ +#include "unicolormaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniColorMaterial::UniColorMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& UniColorMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position"}); + return set; +} + +/* static */ const UniformSet& UniColorMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix", "ubuf.color"}); + return set; +} + +MaterialType* UniColorMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr UniColorMaterial::createShader() const { + return std::make_unique( + "unicolor.vert", "unicolor.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/unicolormaterial.h b/src/rendergraph/common/rendergraph/material/unicolormaterial.h new file mode 100644 index 000000000000..26225f4d3c68 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.h @@ -0,0 +1,19 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class UniColorMaterial; +} + +class rendergraph::UniColorMaterial : public rendergraph::Material { + public: + UniColorMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/materialshader.h b/src/rendergraph/common/rendergraph/materialshader.h new file mode 100644 index 000000000000..43c811ebebd7 --- /dev/null +++ b/src/rendergraph/common/rendergraph/materialshader.h @@ -0,0 +1,17 @@ +#pragma once + +#include "backend/basematerialshader.h" +#include "rendergraph/attributeset.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class MaterialShader; +} // namespace rendergraph + +class rendergraph::MaterialShader : public rendergraph::BaseMaterialShader { + public: + MaterialShader(const char* vertexShaderFile, + const char* fragmentShaderFile, + const UniformSet& uniforms, + const AttributeSet& attributeSet); +}; diff --git a/src/rendergraph/common/rendergraph/materialtype.h b/src/rendergraph/common/rendergraph/materialtype.h new file mode 100644 index 000000000000..67da71e91f34 --- /dev/null +++ b/src/rendergraph/common/rendergraph/materialtype.h @@ -0,0 +1,10 @@ +#pragma once + +#include "backend/basematerialtype.h" + +namespace rendergraph { +class MaterialType; +} + +class rendergraph::MaterialType : public rendergraph::BaseMaterialType { +}; diff --git a/src/rendergraph/common/rendergraph/node.h b/src/rendergraph/common/rendergraph/node.h new file mode 100644 index 000000000000..7ce62dd08cc8 --- /dev/null +++ b/src/rendergraph/common/rendergraph/node.h @@ -0,0 +1,13 @@ +#pragma once + +#include "backend/basenode.h" +#include "rendergraph/treenode.h" + +namespace rendergraph { +class Node; +} // namespace rendergraph + +class rendergraph::Node : public rendergraph::BaseNode, public rendergraph::TreeNode { + public: + Node(); +}; diff --git a/src/rendergraph/common/rendergraph/opacitynode.h b/src/rendergraph/common/rendergraph/opacitynode.h new file mode 100644 index 000000000000..ac64340520a9 --- /dev/null +++ b/src/rendergraph/common/rendergraph/opacitynode.h @@ -0,0 +1,14 @@ +#pragma once + +#include "backend/baseopacitynode.h" +#include "rendergraph/treenode.h" + +namespace rendergraph { +class OpacityNode; +} // namespace rendergraph + +class rendergraph::OpacityNode : public rendergraph::BaseOpacityNode, + public rendergraph::TreeNode { + public: + OpacityNode(); +}; diff --git a/src/rendergraph/common/rendergraph/texture.h b/src/rendergraph/common/rendergraph/texture.h new file mode 100644 index 000000000000..8e095ca31209 --- /dev/null +++ b/src/rendergraph/common/rendergraph/texture.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "backend/basetexture.h" +#include "rendergraph/context.h" + +namespace rendergraph { +class Texture; +} // namespace rendergraph + +class rendergraph::Texture { + public: + Texture(Context& context, const QImage& image); + + BaseTexture* backendTexture() const { + return m_pTexture.get(); + } + + // used by Material::compare + qint64 comparisonKey() const; + + private: + const std::unique_ptr m_pTexture{}; +}; diff --git a/src/rendergraph/common/rendergraph/treenode.h b/src/rendergraph/common/rendergraph/treenode.h new file mode 100644 index 000000000000..2cd943ce838c --- /dev/null +++ b/src/rendergraph/common/rendergraph/treenode.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +#include "backend/basenode.h" + +namespace rendergraph { +class TreeNode; +} // namespace rendergraph + +class rendergraph::TreeNode { + public: + TreeNode(rendergraph::BaseNode* pBackendNode) + : m_pBackendNode(pBackendNode) { + } + virtual ~TreeNode() = default; + + void appendChildNode(std::unique_ptr&& pChild); + + // remove all child nodes. returns the list of child nodes + // by returning the node in the list. the caller can keep + // the pointer, thus transferring ownership. otherwise the + // nodes will be destroyed + std::unique_ptr removeAllChildNodes(); + + // remove a single child node for the list of child nodes. + // the caller can keep the pointer, thus transferring ownership. + // otherwise the node will be destroyed + std::unique_ptr removeChildNode(TreeNode* pChild); + + TreeNode* parent() const { + return m_pParent; + } + TreeNode* firstChild() const { + return m_pFirstChild.get(); + } + TreeNode* lastChild() const { + return m_pLastChild; + } + TreeNode* nextSibling() const { + return m_pNextSibling.get(); + } + TreeNode* previousSibling() const { + return m_pPreviousSibling; + } + + void setUsePreprocess(bool value); + + rendergraph::BaseNode* backendNode() { + return m_pBackendNode; + } + + virtual void initialize() { + } + virtual void resize(int, int) { + } + + private: + void onAppendChildNode(TreeNode* pChild); + void onRemoveAllChildNodes(); + void onRemoveChildNode(TreeNode* pChild); + + rendergraph::BaseNode* m_pBackendNode; + TreeNode* m_pParent{}; + std::unique_ptr m_pFirstChild; + TreeNode* m_pLastChild{}; + std::unique_ptr m_pNextSibling; + TreeNode* m_pPreviousSibling{}; +}; diff --git a/src/rendergraph/common/rendergraph/types.h b/src/rendergraph/common/rendergraph/types.h new file mode 100644 index 000000000000..768738964fd6 --- /dev/null +++ b/src/rendergraph/common/rendergraph/types.h @@ -0,0 +1,31 @@ +#pragma once + +namespace rendergraph { + +enum class PrimitiveType { + UInt, + Float, +}; + +enum class Type { + UInt, + Float, + Vector2D, + Vector3D, + Vector4D, + Matrix4x4 +}; + +int sizeOf(Type type); +int sizeOf(PrimitiveType primitiveType); + +template +Type typeOf(); + +template +PrimitiveType primitiveTypeOf(); + +template +int tupleSizeOf(); + +} // namespace rendergraph diff --git a/src/rendergraph/common/rendergraph/uniform.h b/src/rendergraph/common/rendergraph/uniform.h new file mode 100644 index 000000000000..19a6a9c74a35 --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniform.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "rendergraph/types.h" + +namespace rendergraph { +struct Uniform; +} + +struct rendergraph::Uniform { + const Type m_type; + const QString m_name; + + Uniform(Type type) + : m_type{type} { + } + + Uniform(Type type, QString name) + : m_type{type}, + m_name{std::move(name)} { + } + + template + static Uniform create() { + return Uniform(typeOf()); + } +}; diff --git a/src/rendergraph/common/rendergraph/uniformscache.cpp b/src/rendergraph/common/rendergraph/uniformscache.cpp new file mode 100644 index 000000000000..c94539631d76 --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniformscache.cpp @@ -0,0 +1,27 @@ +#include "rendergraph/uniformscache.h" + +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniformsCache::UniformsCache(const UniformSet& uniformSet) { + int offset = 0; + m_infos.reserve(uniformSet.uniforms().size()); + for (const auto& uniform : uniformSet.uniforms()) { + const int size = sizeOf(uniform.m_type); + m_infos.push_back({uniform.m_type, offset}); + offset += size; + } + m_byteArray.resize(offset); + m_byteArray.fill('\0'); +} + +UniformsCache::~UniformsCache() = default; + +void UniformsCache::set(int uniformIndex, const void* ptr, int size) { + memcpy(m_byteArray.data() + m_infos[uniformIndex].m_offset, ptr, size); +} + +void UniformsCache::get(int uniformIndex, void* ptr, int size) const { + memcpy(ptr, m_byteArray.data() + m_infos[uniformIndex].m_offset, size); +} diff --git a/src/rendergraph/common/rendergraph/uniformscache.h b/src/rendergraph/common/rendergraph/uniformscache.h new file mode 100644 index 000000000000..045887f0c37e --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniformscache.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "rendergraph/assert.h" +#include "rendergraph/types.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class UniformsCache; +} // namespace rendergraph + +class rendergraph::UniformsCache { + public: + UniformsCache(const UniformSet& uniformSet); + ~UniformsCache(); + + template + void set(int uniformIndex, const T& value) { + DEBUG_ASSERT(type(uniformIndex) == typeOf()); + DEBUG_ASSERT(std::is_trivially_copyable()); + set(uniformIndex, static_cast(&value), sizeOf(typeOf())); + } + + template + T get(int uniformIndex) const { + DEBUG_ASSERT(type(uniformIndex) == typeOf()); + DEBUG_ASSERT(std::is_trivially_copyable()); + T value; + get(uniformIndex, static_cast(&value), sizeof(T)); + return value; + } + Type type(int uniformIndex) const { + return m_infos[uniformIndex].m_type; + } + + const char* data() const { + return m_byteArray.data(); + } + qsizetype size() const { + return m_byteArray.size(); + } + int count() const { + return static_cast(m_infos.size()); + } + + private: + void set(int uniformIndex, const void* ptr, int size); + void get(int uniformIndex, void* ptr, int size) const; + + struct Info { + const Type m_type; + const int m_offset; + }; + + std::vector m_infos; + QByteArray m_byteArray; +}; + +template<> +inline void rendergraph::UniformsCache::set(int uniformIndex, const QColor& color) { + set(uniformIndex, QVector4D{color.redF(), color.greenF(), color.blueF(), color.alphaF()}); +} + +template<> +inline void rendergraph::UniformsCache::set( + int uniformIndex, const QMatrix4x4& matrix) { + DEBUG_ASSERT(type(uniformIndex) == typeOf()); + set(uniformIndex, matrix.constData(), sizeOf(typeOf())); +} diff --git a/src/rendergraph/common/rendergraph/uniformset.cpp b/src/rendergraph/common/rendergraph/uniformset.cpp new file mode 100644 index 000000000000..68e077613bd8 --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniformset.cpp @@ -0,0 +1,20 @@ +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniformSet::UniformSet(std::initializer_list list, const std::vector& names) { + int i = 0; + for (auto item : list) { + add(Uniform{item.m_type, names[i++]}); + } +} + +UniformSet::~UniformSet() = default; + +void UniformSet::add(const Uniform& uniform) { + m_uniforms.push_back(uniform); +} + +const std::vector& UniformSet::uniforms() const { + return m_uniforms; +} diff --git a/src/rendergraph/common/rendergraph/uniformset.h b/src/rendergraph/common/rendergraph/uniformset.h new file mode 100644 index 000000000000..34af87e5c94c --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniformset.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +#include "rendergraph/uniform.h" + +namespace rendergraph { +class UniformSet; +} + +class rendergraph::UniformSet { + public: + UniformSet(std::initializer_list list, const std::vector& names); + + ~UniformSet(); + + const std::vector& uniforms() const; + + private: + void add(const Uniform& uniform); + std::vector m_uniforms; +}; + +namespace rendergraph { +template +UniformSet makeUniformSet(const std::vector& names) { + return UniformSet({(Uniform::create())...}, names); +} +} // namespace rendergraph diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h new file mode 100644 index 000000000000..2f2a46dc2ebe --- /dev/null +++ b/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h @@ -0,0 +1,137 @@ +#pragma once + +#include "rendergraph/geometry.h" + +namespace rendergraph { +class RGBAVertexUpdater; +} + +class rendergraph::RGBAVertexUpdater { + public: + RGBAVertexUpdater(Geometry::RGBAColoredPoint2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + + void addRectangle( + QVector2D lt, + QVector2D rb, + QVector4D rgba) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), rgba.x(), rgba.y(), rgba.z(), rgba.w()); + } + void addRectangleVGradient( + QVector2D lt, + QVector2D rb, + QVector4D rgbat, + QVector4D rgbab) { + addRectangleVGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbat.x(), + rgbat.y(), + rgbat.z(), + rgbat.w(), + rgbab.x(), + rgbab.y(), + rgbab.z(), + rgbab.w()); + } + void addRectangleHGradient( + QVector2D lt, + QVector2D rb, + QVector4D rgbal, + QVector4D rgbar) { + addRectangleHGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbal.x(), + rgbal.y(), + rgbal.z(), + rgbal.w(), + rgbar.x(), + rgbar.y(), + rgbar.z(), + rgbar.w()); + } + void addRectangle(float x1, float y1, float x2, float y2, float r, float g, float b, float a) { + addTriangle(x1, y1, x2, y1, x1, y2, r, g, b, a); + addTriangle(x1, y2, x2, y2, x2, y1, r, g, b, a); + } + void addRectangleVGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float a1, + float r2, + float g2, + float b2, + float a2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, a1, r1, g1, b1, a1, r2, g2, b2, a2); + addTriangle(x1, y2, x2, y2, x2, y1, r2, g2, b2, a2, r2, g2, b2, a2, r1, g1, b1, a1); + } + void addRectangleHGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float a1, + float r2, + float g2, + float b2, + float a2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, a1, r2, g2, b2, a2, r1, g1, b1, a1); + addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, a1, r2, g2, b2, a2, r2, g2, b2, a2); + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r, + float g, + float b, + float a) { + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x1, y1, r, g, b, a}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x2, y2, r, g, b, a}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x3, y3, r, g, b, a}; + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r1, + float g1, + float b1, + float a1, + float r2, + float g2, + float b2, + float a2, + float r3, + float g3, + float b3, + float a3) { + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x1, y1, r1, g1, b1, a1}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x2, y2, r2, g2, b2, a2}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x3, y3, r3, g3, b3, a3}; + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + Geometry::RGBAColoredPoint2D* const m_pData; + Geometry::RGBAColoredPoint2D* m_pWrite; +}; diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h new file mode 100644 index 000000000000..e85f525a5edf --- /dev/null +++ b/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h @@ -0,0 +1,127 @@ +#pragma once + +#include "rendergraph/geometry.h" + +namespace rendergraph { +class RGBVertexUpdater; +} + +class rendergraph::RGBVertexUpdater { + public: + RGBVertexUpdater(Geometry::RGBColoredPoint2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + + void addRectangle( + QVector2D lt, + QVector2D rb, + QVector3D rgb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), rgb.x(), rgb.y(), rgb.z()); + } + void addRectangleVGradient( + QVector2D lt, + QVector2D rb, + QVector3D rgbt, + QVector3D rgbb) { + addRectangleVGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbt.x(), + rgbt.y(), + rgbt.z(), + rgbb.x(), + rgbb.y(), + rgbb.z()); + } + void addRectangleHGradient( + QVector2D lt, + QVector2D rb, + QVector3D rgbl, + QVector3D rgbr) { + addRectangleHGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbl.x(), + rgbl.y(), + rgbl.z(), + rgbr.x(), + rgbr.y(), + rgbr.z()); + } + void addRectangle(float x1, float y1, float x2, float y2, float r, float g, float b) { + addTriangle(x1, y1, x2, y1, x1, y2, r, g, b); + addTriangle(x1, y2, x2, y2, x2, y1, r, g, b); + } + void addRectangleVGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float r2, + float g2, + float b2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, r2, g2, b2, r1, g1, b1); + addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, r2, g2, b2, r2, g2, b2); + } + void addRectangleHGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float r2, + float g2, + float b2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, r2, g2, b2, r1, g1, b1); + addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, r2, g2, b2, r2, g2, b2); + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r, + float g, + float b) { + *m_pWrite++ = Geometry::RGBColoredPoint2D{x1, y1, r, g, b}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{x2, y2, r, g, b}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{x3, y3, r, g, b}; + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r1, + float g1, + float b1, + + float r2, + float g2, + float b2, + + float r3, + float g3, + float b3) { + *m_pWrite++ = Geometry::RGBColoredPoint2D{x1, y1, r1, g1, b1}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{x2, y2, r2, g2, b2}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{x3, y3, r3, g3, b3}; + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + Geometry::RGBColoredPoint2D* const m_pData; + Geometry::RGBColoredPoint2D* m_pWrite; +}; diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h new file mode 100644 index 000000000000..e754fb509a0c --- /dev/null +++ b/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h @@ -0,0 +1,47 @@ +#pragma once + +#include "rendergraph/geometry.h" + +namespace rendergraph { +class TexturedVertexUpdater; +} + +class rendergraph::TexturedVertexUpdater { + public: + TexturedVertexUpdater(Geometry::TexturedPoint2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + void addRectangle( + QVector2D lt, QVector2D rb, QVector2D tlr, QVector2D trb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), tlr.x(), tlr.y(), trb.x(), trb.y()); + } + void addRectangle( + float x1, float y1, float x2, float y2, float tx1, float ty1, float tx2, float ty2) { + addTriangle(x1, y1, x2, y1, x1, y2, tx1, ty1, tx2, ty1, tx1, ty2); + addTriangle(x1, y2, x2, y2, x2, y1, tx1, ty2, tx2, ty2, tx2, ty1); + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float tx1, + float ty1, + float tx2, + float ty2, + float tx3, + float ty3) { + *m_pWrite++ = Geometry::TexturedPoint2D{x1, y1, tx1, ty1}; + *m_pWrite++ = Geometry::TexturedPoint2D{x2, y2, tx2, ty2}; + *m_pWrite++ = Geometry::TexturedPoint2D{x3, y3, tx3, ty3}; + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + Geometry::TexturedPoint2D* const m_pData; + Geometry::TexturedPoint2D* m_pWrite; +}; diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h new file mode 100644 index 000000000000..d98d40f8c195 --- /dev/null +++ b/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h @@ -0,0 +1,37 @@ +#pragma once + +namespace rendergraph { +class VertexUpdater; +} + +class rendergraph::VertexUpdater { + public: + VertexUpdater(Geometry::Point2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + void addRectangle( + QVector2D lt, QVector2D rb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y()); + } + void addRectangle( + float x1, + float y1, + float x2, + float y2) { + addTriangle(x1, y1, x2, y1, x1, y2); + addTriangle(x1, y2, x2, y2, x2, y1); + } + void addTriangle(float x1, float y1, float x2, float y2, float x3, float y3) { + *m_pWrite++ = Geometry::Point2D{x1, y1}; + *m_pWrite++ = Geometry::Point2D{x2, y2}; + *m_pWrite++ = Geometry::Point2D{x3, y3}; + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + Geometry::Point2D* const m_pData; + Geometry::Point2D* m_pWrite; +}; diff --git a/src/rendergraph/common/treenode.cpp b/src/rendergraph/common/treenode.cpp new file mode 100644 index 000000000000..63fe5667ffc7 --- /dev/null +++ b/src/rendergraph/common/treenode.cpp @@ -0,0 +1,50 @@ +#include "rendergraph/treenode.h" + +#include "backend/basenode.h" + +using namespace rendergraph; + +void TreeNode::appendChildNode(std::unique_ptr&& pChild) { + auto pChildRawPtr = pChild.get(); + if (m_pLastChild) { + pChild->m_pPreviousSibling = m_pLastChild; + m_pLastChild->m_pNextSibling = std::move(pChild); + } else { + m_pFirstChild = std::move(pChild); + } + m_pLastChild = pChildRawPtr; + m_pLastChild->m_pParent = this; + + onAppendChildNode(pChildRawPtr); +} + +std::unique_ptr TreeNode::removeAllChildNodes() { + onRemoveAllChildNodes(); + + m_pLastChild = nullptr; + TreeNode* pChild = m_pFirstChild.get(); + while (pChild) { + pChild->m_pParent = nullptr; + pChild = pChild->m_pNextSibling.get(); + } + return std::move(m_pFirstChild); +} + +std::unique_ptr TreeNode::removeChildNode(TreeNode* pChild) { + onRemoveChildNode(pChild); + + std::unique_ptr pRemoved; + if (pChild == m_pFirstChild.get()) { + pRemoved = std::move(m_pFirstChild); + m_pFirstChild = std::move(pChild->m_pNextSibling); + } else { + pRemoved = std::move(pChild->m_pPreviousSibling->m_pNextSibling); + pChild->m_pPreviousSibling->m_pNextSibling = std::move(pChild->m_pNextSibling); + pChild->m_pPreviousSibling = nullptr; + } + if (pChild == m_pLastChild) { + m_pLastChild = nullptr; + } + pChild->m_pParent = nullptr; + return pRemoved; +} diff --git a/src/rendergraph/common/types.cpp b/src/rendergraph/common/types.cpp new file mode 100644 index 000000000000..211538e5af47 --- /dev/null +++ b/src/rendergraph/common/types.cpp @@ -0,0 +1,111 @@ +#include "rendergraph/types.h" + +#include +#include +#include +#include +#include + +using namespace rendergraph; + +int rendergraph::sizeOf(PrimitiveType type) { + switch (type) { + case PrimitiveType::UInt: + return sizeof(uint32_t); + case PrimitiveType::Float: + return sizeof(float); + } + return 0; +} + +int rendergraph::sizeOf(Type type) { + switch (type) { + case Type::UInt: + return sizeof(uint32_t); + case Type::Float: + return sizeof(float); + case Type::Vector2D: + return sizeof(float) * 2; + case Type::Vector3D: + return sizeof(float) * 3; + case Type::Vector4D: + return sizeof(float) * 4; + case Type::Matrix4x4: + return sizeof(float) * 4 * 4; + } + return 0; +} + +template<> +Type rendergraph::typeOf() { + return Type::UInt; +} + +template<> +Type rendergraph::typeOf() { + return Type::Float; +} + +template<> +Type rendergraph::typeOf() { + return Type::Vector2D; +} + +template<> +Type rendergraph::typeOf() { + return Type::Vector3D; +} + +template<> +Type rendergraph::typeOf() { + return Type::Vector4D; +} + +template<> +Type rendergraph::typeOf() { + return Type::Matrix4x4; +} + +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::Float; +} + +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::UInt; +} + +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::Float; +} +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::Float; +} +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::Float; +} + +template<> +int rendergraph::tupleSizeOf() { + return 1; +} +template<> +int rendergraph::tupleSizeOf() { + return 1; +} +template<> +int rendergraph::tupleSizeOf() { + return 2; +} +template<> +int rendergraph::tupleSizeOf() { + return 3; +} +template<> +int rendergraph::tupleSizeOf() { + return 4; +} diff --git a/src/rendergraph/examples/CMakeLists.txt b/src/rendergraph/examples/CMakeLists.txt new file mode 100644 index 000000000000..b826c663aa45 --- /dev/null +++ b/src/rendergraph/examples/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.16) +project(rendergraph LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools) + +qt_standard_project_setup() + +add_subdirectory(../ rendergraph) + +target_compile_definitions(rendergraph_gl PUBLIC + $<$:MIXXX_DEBUG_ASSERTIONS_ENABLED> +) +target_compile_definitions(rendergraph_sg PUBLIC + $<$:MIXXX_DEBUG_ASSERTIONS_ENABLED> +) + +add_subdirectory(gl_example) +add_subdirectory(sg_example) diff --git a/src/rendergraph/examples/README b/src/rendergraph/examples/README new file mode 100644 index 000000000000..6168efc09c47 --- /dev/null +++ b/src/rendergraph/examples/README @@ -0,0 +1,6 @@ +Build examples with + +$ cd ../../../ +$ mkdir build-rendergraph_examples +$ cd build-rendergraph_examples +$ cmake -DCMAKE_PREFIX_PATH=~/Qt/6.7.2/macos/ ../src/rendergraph/examples diff --git a/src/rendergraph/examples/common/examplenodes.cpp b/src/rendergraph/examples/common/examplenodes.cpp new file mode 100644 index 000000000000..588b5a43ef46 --- /dev/null +++ b/src/rendergraph/examples/common/examplenodes.cpp @@ -0,0 +1,73 @@ +#include "examplenodes.h" + +#include +#include +#include + +#include "rendergraph/geometry.h" +#include "rendergraph/material/endoftrackmaterial.h" +#include "rendergraph/material/texturematerial.h" + +using namespace rendergraph; + +ExampleNode1::ExampleNode1() { + setMaterial(std::make_unique()); + setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4)); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, horizontalGradientArray, 4); + + QColor color("red"); + material().setUniform(0, + QVector4D{color.redF(), + color.greenF(), + color.blueF(), + color.alphaF()}); +} + +ExampleNode2::ExampleNode2() { + setMaterial(std::make_unique()); + setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4)); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, horizontalGradientArray, 4); + + QColor color("blue"); + material().setUniform(0, + QVector4D{color.redF(), + color.greenF(), + color.blueF(), + color.alphaF()}); +} + +ExampleNode3::ExampleNode3() { + auto* m = new TextureMaterial; + + setMaterial(std::make_unique()); + setGeometry(std::make_unique(TextureMaterial::attributes(), 4)); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, texcoordArray, 4); + + QMatrix4x4 matrix; + + matrix.scale(0.3); + material().setUniform(0, matrix); +} + +void ExampleNode3::setTexture(std::unique_ptr texture) { + dynamic_cast(material()).setTexture(std::move(texture)); +} + +ExampleTopNode::ExampleTopNode(rendergraph::Context& context) { + TreeNode::appendChildNode(std::make_unique()); + TreeNode::appendChildNode(std::make_unique()); + TreeNode::appendChildNode(std::make_unique()); + + { + QImage img(":/example/images/test.png"); + static_cast(TreeNode::lastChild()) + ->setTexture( + std::make_unique(context, img)); + } +} diff --git a/src/rendergraph/examples/common/examplenodes.h b/src/rendergraph/examples/common/examplenodes.h new file mode 100644 index 000000000000..ca0efe732234 --- /dev/null +++ b/src/rendergraph/examples/common/examplenodes.h @@ -0,0 +1,43 @@ +#include "rendergraph/geometrynode.h" +#include "rendergraph/node.h" +#include "rendergraph/texture.h" + +namespace rendergraph { +class ExampleNode1; +class ExampleNode2; +class ExampleNode3; +class ExampleTopNode; +} // namespace rendergraph + +class rendergraph::ExampleNode1 : public rendergraph::GeometryNode { + public: + static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; + static constexpr float verticalGradientArray[] = {1.f, 1.f, -1.f, -1.f}; + static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; + + ExampleNode1(); +}; + +class rendergraph::ExampleNode2 : public rendergraph::GeometryNode { + public: + static constexpr float positionArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f}; + static constexpr float verticalGradientArray[] = {1.f, 1.f, 0.f, 0.f}; + static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; + + ExampleNode2(); +}; + +class rendergraph::ExampleNode3 : public rendergraph::GeometryNode { + public: + static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; + static constexpr float texcoordArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f}; + + ExampleNode3(); + + void setTexture(std::unique_ptr texture); +}; + +class rendergraph::ExampleTopNode : public rendergraph::Node { + public: + ExampleTopNode(rendergraph::Context& context); +}; diff --git a/src/rendergraph/examples/gl_example/CMakeLists.txt b/src/rendergraph/examples/gl_example/CMakeLists.txt new file mode 100644 index 000000000000..0d28d7a7d7d1 --- /dev/null +++ b/src/rendergraph/examples/gl_example/CMakeLists.txt @@ -0,0 +1,35 @@ +qt_add_executable(gl_example + window.cpp window.h + main.cpp + ../common/examplenodes.cpp +) + +set_target_properties(gl_example PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(gl_example PRIVATE + rendergraph_gl +) +target_include_directories(gl_example PRIVATE ../common) + +qt_add_resources(gl_example "gl_example" + PREFIX + /example + FILES + images/test.png +) + +install(TARGETS gl_example + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET gl_example + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR +) +install(SCRIPT ${deploy_script}) diff --git a/src/rendergraph/examples/gl_example/images/test.png b/src/rendergraph/examples/gl_example/images/test.png new file mode 100644 index 000000000000..f9580346eb8b Binary files /dev/null and b/src/rendergraph/examples/gl_example/images/test.png differ diff --git a/src/rendergraph/examples/gl_example/main.cpp b/src/rendergraph/examples/gl_example/main.cpp new file mode 100644 index 000000000000..7b62238b429b --- /dev/null +++ b/src/rendergraph/examples/gl_example/main.cpp @@ -0,0 +1,12 @@ +#include + +#include "window.h" + +int main(int argc, char* argv[]) { + QGuiApplication app(argc, argv); + + Window window; + window.show(); + + return app.exec(); +} diff --git a/src/rendergraph/examples/gl_example/window.cpp b/src/rendergraph/examples/gl_example/window.cpp new file mode 100644 index 000000000000..a5e5085d2570 --- /dev/null +++ b/src/rendergraph/examples/gl_example/window.cpp @@ -0,0 +1,36 @@ +#include "window.h" + +#include "examplenodes.h" +#include "rendergraph/context.h" + +Window::Window() { +} + +void Window::closeEvent(QCloseEvent*) { + // since this is the only and last window, we need to cleanup before destruction, + // because at destruction the context can't be used anymore + m_pEngine.reset(); +} + +void Window::initializeGL() { + rendergraph::Context context; + + auto node = std::make_unique(context); + + m_pEngine = std::make_unique(std::move(node)); + m_pEngine->initialize(); +} + +void Window::resizeGL(int, int) { +} + +void Window::paintGL() { + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_BLEND); + // qt scene graph uses premultiplied alpha color in the shader, + // so we need to do the same + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + m_pEngine->render(); +} diff --git a/src/rendergraph/examples/gl_example/window.h b/src/rendergraph/examples/gl_example/window.h new file mode 100644 index 000000000000..8c2a5646bef0 --- /dev/null +++ b/src/rendergraph/examples/gl_example/window.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#include "rendergraph/engine.h" + +namespace rendergraph { +class Graph; +} + +class Window : public QOpenGLWindow { + public: + Window(); + + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + void closeEvent(QCloseEvent* ev) override; + + private: + std::unique_ptr m_pEngine; +}; diff --git a/src/rendergraph/examples/sg_example/CMakeLists.txt b/src/rendergraph/examples/sg_example/CMakeLists.txt new file mode 100644 index 000000000000..378e062fcf22 --- /dev/null +++ b/src/rendergraph/examples/sg_example/CMakeLists.txt @@ -0,0 +1,36 @@ +qt_add_executable(sg_example WIN32 MACOSX_BUNDLE + customitem.cpp + customitem.h + main.cpp + ../common/examplenodes.cpp +) + +target_link_libraries(sg_example PRIVATE + rendergraph_sg +) +target_include_directories(sg_example PRIVATE ../common) + +qt_add_qml_module(sg_example + URI RenderGraph + QML_FILES + qml/main.qml + RESOURCES + images/test.png + RESOURCE_PREFIX /example + NO_RESOURCE_TARGET_PATH +) + +install(TARGETS sg_example + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET sg_example + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/src/rendergraph/examples/sg_example/customitem.cpp b/src/rendergraph/examples/sg_example/customitem.cpp new file mode 100644 index 000000000000..8651791c0a08 --- /dev/null +++ b/src/rendergraph/examples/sg_example/customitem.cpp @@ -0,0 +1,48 @@ +#include "customitem.h" + +#include +#include +#include +#include +#include + +#include "examplenodes.h" +#include "rendergraph/context.h" +#include "rendergraph/node.h" + +CustomItem::CustomItem(QQuickItem* parent) + : QQuickItem(parent) { + setFlag(ItemHasContents, true); +} + +CustomItem::~CustomItem() = default; + +void CustomItem::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) { + m_geometryChanged = true; + update(); + QQuickItem::geometryChange(newGeometry, oldGeometry); +} + +QSGNode* CustomItem::updatePaintNode(QSGNode* node, UpdatePaintNodeData*) { + QSGRectangleNode* bgNode; + if (!node) { + bgNode = window()->createRectangleNode(); + bgNode->setColor(QColor(0, 0, 0, 255)); + bgNode->setRect(boundingRect()); + + rendergraph::Context context(window()); + m_node = std::make_unique(context); + bgNode->appendChildNode(m_node->backendNode()); + + node = bgNode; + } else { + bgNode = static_cast(node); + } + + if (m_geometryChanged) { + bgNode->setRect(boundingRect()); + m_geometryChanged = false; + } + + return node; +} diff --git a/src/rendergraph/examples/sg_example/customitem.h b/src/rendergraph/examples/sg_example/customitem.h new file mode 100644 index 000000000000..304fb5c8f5ec --- /dev/null +++ b/src/rendergraph/examples/sg_example/customitem.h @@ -0,0 +1,26 @@ +#ifndef CUSTOMITEM_H +#define CUSTOMITEM_H + +#include + +namespace rendergraph { +class Node; +} + +class CustomItem : public QQuickItem { + Q_OBJECT + QML_ELEMENT + + public: + explicit CustomItem(QQuickItem* parent = nullptr); + ~CustomItem(); + + protected: + QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override; + void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; + + bool m_geometryChanged{}; + std::unique_ptr m_node; +}; + +#endif // CUSTOMITEM_H diff --git a/src/rendergraph/examples/sg_example/images/test.png b/src/rendergraph/examples/sg_example/images/test.png new file mode 100644 index 000000000000..f9580346eb8b Binary files /dev/null and b/src/rendergraph/examples/sg_example/images/test.png differ diff --git a/src/rendergraph/examples/sg_example/main.cpp b/src/rendergraph/examples/sg_example/main.cpp new file mode 100644 index 000000000000..ab2e5b34072d --- /dev/null +++ b/src/rendergraph/examples/sg_example/main.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +#include "customitem.h" + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///example/qml/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/src/rendergraph/examples/sg_example/qml/main.qml b/src/rendergraph/examples/sg_example/qml/main.qml new file mode 100644 index 000000000000..2410ad1a5604 --- /dev/null +++ b/src/rendergraph/examples/sg_example/qml/main.qml @@ -0,0 +1,13 @@ +import QtQuick +import RenderGraph + +Item { + id: root + + width: 640 + height: 480 + + CustomItem { + anchors.fill: parent + } +} diff --git a/src/rendergraph/opengl/CMakeLists.txt b/src/rendergraph/opengl/CMakeLists.txt new file mode 100644 index 000000000000..968b690e3daf --- /dev/null +++ b/src/rendergraph/opengl/CMakeLists.txt @@ -0,0 +1,78 @@ +add_library(rendergraph_gl +../common/attributeset.cpp +../common/treenode.cpp +../common/node.cpp +../common/opacitynode.cpp +../common/rendergraph/attribute.h +../common/rendergraph/attributeset.h +../common/rendergraph/treenode.h +../common/rendergraph/geometry.h +../common/rendergraph/geometrynode.h +../common/rendergraph/material.h +../common/rendergraph/material/endoftrackmaterial.cpp +../common/rendergraph/material/endoftrackmaterial.h +../common/rendergraph/material/patternmaterial.cpp +../common/rendergraph/material/patternmaterial.h +../common/rendergraph/material/rgbamaterial.cpp +../common/rendergraph/material/rgbamaterial.h +../common/rendergraph/material/rgbmaterial.cpp +../common/rendergraph/material/rgbmaterial.h +../common/rendergraph/material/texturematerial.cpp +../common/rendergraph/material/texturematerial.h +../common/rendergraph/material/unicolormaterial.cpp +../common/rendergraph/material/unicolormaterial.h +../common/rendergraph/materialshader.h +../common/rendergraph/materialtype.h +../common/rendergraph/node.h +../common/rendergraph/opacitynode.h +../common/rendergraph/texture.h +../common/rendergraph/types.h +../common/rendergraph/uniform.h +../common/rendergraph/uniformscache.cpp +../common/rendergraph/uniformscache.h +../common/rendergraph/uniformset.cpp +../common/rendergraph/uniformset.h +../common/types.cpp +backend/baseattributeset.cpp +backend/baseattributeset.h +backend/basegeometry.cpp +backend/basegeometry.h +backend/basegeometrynode.cpp +backend/basegeometrynode.h +backend/basematerial.cpp +backend/basematerial.h +backend/basematerialshader.cpp +backend/basematerialtype.h +backend/basenode.h +backend/baseopacitynode.h +backend/baseopenglnode.cpp +backend/baseopenglnode.h +backend/shadercache.h +backend/basetexture.h +treenode.cpp +engine.cpp +geometry.cpp +geometrynode.cpp +material.cpp +materialshader.cpp +openglnode.cpp +rendergraph/context.h +rendergraph/engine.h +rendergraph/openglnode.h +texture.cpp +) + +target_link_libraries(rendergraph_gl PUBLIC + Qt6::Core + Qt6::Gui + Qt6::OpenGL +) + +# USE_QSHADER_FOR_GL is set in rendergraph/CMakeLists.txt +if(USE_QSHADER_FOR_GL) + message(STATUS "Using QShader to load qsb shaders for opengl") + target_link_libraries(rendergraph_gl PUBLIC Qt6::GuiPrivate) + target_compile_definitions(rendergraph_gl PRIVATE USE_QSHADER_FOR_GL) +endif() + +target_include_directories(rendergraph_gl PUBLIC . ../common) diff --git a/src/rendergraph/opengl/backend/baseattributeset.cpp b/src/rendergraph/opengl/backend/baseattributeset.cpp new file mode 100644 index 000000000000..9ddec2306765 --- /dev/null +++ b/src/rendergraph/opengl/backend/baseattributeset.cpp @@ -0,0 +1,14 @@ +#include "rendergraph/assert.h" +#include "rendergraph/attributeset.h" + +using namespace rendergraph; + +BaseAttributeSet::BaseAttributeSet(std::initializer_list list, + const std::vector& names) { + DEBUG_ASSERT(list.size() == names.size()); + int i = 0; + m_attributes.reserve(list.size()); + for (auto item : list) { + m_attributes.push_back(Attribute{item.m_tupleSize, item.m_primitiveType, names[i++]}); + } +} diff --git a/src/rendergraph/opengl/backend/baseattributeset.h b/src/rendergraph/opengl/backend/baseattributeset.h new file mode 100644 index 000000000000..57b7fcf76ec3 --- /dev/null +++ b/src/rendergraph/opengl/backend/baseattributeset.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#include "rendergraph/attribute.h" + +namespace rendergraph { +class BaseAttributeSet; +} + +class rendergraph::BaseAttributeSet { + protected: + BaseAttributeSet(std::initializer_list list, const std::vector& names); + std::vector m_attributes; +}; diff --git a/src/rendergraph/opengl/backend/basegeometry.cpp b/src/rendergraph/opengl/backend/basegeometry.cpp new file mode 100644 index 000000000000..03ffd7ac9882 --- /dev/null +++ b/src/rendergraph/opengl/backend/basegeometry.cpp @@ -0,0 +1,41 @@ +#include "backend/basegeometry.h" + +#include "rendergraph/geometry.h" + +using namespace rendergraph; + +BaseGeometry::BaseGeometry( + const AttributeSet& attributeSet, int vertexCount) + : m_drawingMode(static_cast(Geometry::DrawingMode:: + TriangleStrip)) // to mimic sg default + , + m_vertexCount(vertexCount) { + int offset = 0; + for (const auto& attribute : attributeSet.attributes()) { + m_offsets.push_back(offset); + offset += attribute.m_tupleSize; + m_tupleSizes.push_back(attribute.m_tupleSize); + } + m_stride = offset * sizeof(float); + m_data.resize(offset * vertexCount); +} + +int BaseGeometry::attributeCount() const { + return m_tupleSizes.size(); +} + +int BaseGeometry::vertexCount() const { + return m_vertexCount; +} + +int BaseGeometry::offset(int attributeIndex) const { + return m_offsets[attributeIndex]; +} + +int BaseGeometry::tupleSize(int attributeIndex) const { + return m_tupleSizes[attributeIndex]; +} + +int BaseGeometry::stride() const { + return m_stride; +} diff --git a/src/rendergraph/opengl/backend/basegeometry.h b/src/rendergraph/opengl/backend/basegeometry.h new file mode 100644 index 000000000000..60281f6cb4b6 --- /dev/null +++ b/src/rendergraph/opengl/backend/basegeometry.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "rendergraph/attributeset.h" + +namespace rendergraph { +class BaseGeometry; +} + +class rendergraph::BaseGeometry { + protected: + BaseGeometry(const AttributeSet& attributeSet, int vertexCount); + + public: + int attributeCount() const; + int vertexCount() const; + int offset(int attributeIndex) const; + int tupleSize(int attributeIndex) const; + int stride() const; + + protected: + int m_drawingMode; + int m_vertexCount; + std::vector m_tupleSizes; + std::vector m_offsets; + int m_stride; + std::vector m_data; +}; diff --git a/src/rendergraph/opengl/backend/basegeometrynode.cpp b/src/rendergraph/opengl/backend/basegeometrynode.cpp new file mode 100644 index 000000000000..889b654deca1 --- /dev/null +++ b/src/rendergraph/opengl/backend/basegeometrynode.cpp @@ -0,0 +1,100 @@ +#include "backend/basegeometrynode.h" + +#include + +#include "backend/shadercache.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/texture.h" + +using namespace rendergraph; + +namespace { +GLenum toGlDrawingMode(Geometry::DrawingMode mode) { + switch (mode) { + case Geometry::DrawingMode::Triangles: + return GL_TRIANGLES; + case Geometry::DrawingMode::TriangleStrip: + return GL_TRIANGLE_STRIP; + } +} +} // namespace + +void BaseGeometryNode::initializeBackend() { + initializeOpenGLFunctions(); + GeometryNode* pThis = static_cast(this); + pThis->material().setShader(ShaderCache::getShaderForMaterial(&pThis->material())); +} + +void BaseGeometryNode::renderBackend() { + GeometryNode* pThis = static_cast(this); + Geometry& geometry = pThis->geometry(); + Material& material = pThis->material(); + + if (geometry.vertexCount() == 0) { + return; + } + + glEnable(GL_BLEND); + // qt scene graph uses premultiplied alpha color in the shader, + // so we need to do the same + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + QOpenGLShaderProgram& shader = material.shader(); + shader.bind(); + + if (material.clearUniformsCacheDirty() || !material.isLastModifierOfShader()) { + material.modifyShader(); + const UniformsCache& cache = material.uniformsCache(); + for (int i = 0; i < cache.count(); i++) { + int location = material.uniformLocation(i); + switch (cache.type(i)) { + case Type::UInt: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Float: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Vector2D: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Vector3D: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Vector4D: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Matrix4x4: + shader.setUniformValue(location, cache.get(i)); + break; + } + } + } + + for (int i = 0; i < geometry.attributeCount(); i++) { + int location = material.attributeLocation(i); + shader.enableAttributeArray(location); + shader.setAttributeArray(location, + geometry.vertexData() + geometry.offset(i), + geometry.tupleSize(i), + geometry.stride()); + } + + // TODO multiple textures + auto pTexture = material.texture(1); + if (pTexture) { + pTexture->backendTexture()->bind(); + } + + glDrawArrays(toGlDrawingMode(geometry.drawingMode()), 0, geometry.vertexCount()); + + if (pTexture) { + pTexture->backendTexture()->release(); + } + + for (int i = 0; i < geometry.attributeCount(); i++) { + int location = material.attributeLocation(i); + shader.disableAttributeArray(location); + } + + shader.release(); +} diff --git a/src/rendergraph/opengl/backend/basegeometrynode.h b/src/rendergraph/opengl/backend/basegeometrynode.h new file mode 100644 index 000000000000..b215fa5dda34 --- /dev/null +++ b/src/rendergraph/opengl/backend/basegeometrynode.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "backend/basenode.h" + +namespace rendergraph { +class BaseGeometryNode; +} + +class rendergraph::BaseGeometryNode : public rendergraph::BaseNode, + public QOpenGLFunctions { + protected: + BaseGeometryNode() = default; + + public: + void initializeBackend() override; + void renderBackend() override; +}; diff --git a/src/rendergraph/opengl/backend/basematerial.cpp b/src/rendergraph/opengl/backend/basematerial.cpp new file mode 100644 index 000000000000..ae6817ce878e --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerial.cpp @@ -0,0 +1,34 @@ +#include "backend/basematerial.h" + +#include "rendergraph/material.h" + +using namespace rendergraph; + +void BaseMaterial::setShader(std::shared_ptr pShader) { + m_pShader = pShader; +} + +MaterialShader& BaseMaterial::shader() const { + return *m_pShader; +} + +int BaseMaterial::uniformLocation(int uniformIndex) const { + return m_pShader->uniformLocation(uniformIndex); +} + +int BaseMaterial::attributeLocation(int attributeIndex) const { + return m_pShader->attributeLocation(attributeIndex); +} + +void BaseMaterial::modifyShader() { + m_pShader->setLastModifiedByMaterial(this); +} + +bool BaseMaterial::isLastModifierOfShader() const { + return this == m_pShader->lastModifiedByMaterial(); +} + +int BaseMaterial::compare(const BaseMaterial* other) const { + auto pThis = static_cast(this); + return pThis->compare(static_cast(other)); +} diff --git a/src/rendergraph/opengl/backend/basematerial.h b/src/rendergraph/opengl/backend/basematerial.h new file mode 100644 index 000000000000..c08b60daab27 --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerial.h @@ -0,0 +1,30 @@ +#pragma once + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +namespace rendergraph { +class BaseMaterial; +} + +class rendergraph::BaseMaterial { + protected: + BaseMaterial() = default; + + public: + virtual MaterialType* type() const = 0; + + // For parity with QSGMaterial, not used yet + int compare(const BaseMaterial* other) const; + + void setShader(std::shared_ptr pShader); + + MaterialShader& shader() const; + + int uniformLocation(int uniformIndex) const; + int attributeLocation(int attributeIndex) const; + + void modifyShader(); + bool isLastModifierOfShader() const; + std::shared_ptr m_pShader; +}; diff --git a/src/rendergraph/opengl/backend/basematerialshader.cpp b/src/rendergraph/opengl/backend/basematerialshader.cpp new file mode 100644 index 000000000000..d4b245e482a0 --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerialshader.cpp @@ -0,0 +1,18 @@ +#include "backend/basematerialshader.h" + +using namespace rendergraph; + +int BaseMaterialShader::attributeLocation(int attributeIndex) const { + return m_attributeLocations[attributeIndex]; +} + +int BaseMaterialShader::uniformLocation(int uniformIndex) const { + return m_uniformLocations[uniformIndex]; +} + +BaseMaterial* BaseMaterialShader::lastModifiedByMaterial() const { + return m_pLastModifiedByMaterial; +} +void BaseMaterialShader::setLastModifiedByMaterial(BaseMaterial* pMaterial) { + m_pLastModifiedByMaterial = pMaterial; +} diff --git a/src/rendergraph/opengl/backend/basematerialshader.h b/src/rendergraph/opengl/backend/basematerialshader.h new file mode 100644 index 000000000000..f7082c15ef01 --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerialshader.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class BaseMaterial; // fwd decl to avoid circular dependency +class BaseMaterialShader; +} // namespace rendergraph + +class rendergraph::BaseMaterialShader : public QOpenGLShaderProgram { + protected: + BaseMaterialShader() = default; + + public: + int attributeLocation(int attributeIndex) const; + int uniformLocation(int uniformIndex) const; + + BaseMaterial* lastModifiedByMaterial() const; + void setLastModifiedByMaterial(BaseMaterial* pMaterial); + + std::vector m_attributeLocations; + std::vector m_uniformLocations; + BaseMaterial* m_pLastModifiedByMaterial{}; +}; diff --git a/src/rendergraph/opengl/backend/basematerialtype.h b/src/rendergraph/opengl/backend/basematerialtype.h new file mode 100644 index 000000000000..50b925d0bf4f --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerialtype.h @@ -0,0 +1,6 @@ +#pragma once + +namespace rendergraph { +class BaseMaterialType { +}; +} // namespace rendergraph diff --git a/src/rendergraph/opengl/backend/basenode.h b/src/rendergraph/opengl/backend/basenode.h new file mode 100644 index 000000000000..d224e4cc8b3c --- /dev/null +++ b/src/rendergraph/opengl/backend/basenode.h @@ -0,0 +1,44 @@ +#pragma once + +namespace rendergraph { +class Engine; // fwd decl to avoid circular dependency +} + +namespace rendergraph { +class BaseNode; +} + +class rendergraph::BaseNode { + protected: + BaseNode() = default; + + public: + void setUsePreprocessFlag(bool value) { + m_usePreprocess = value; + } + bool usePreprocess() const { + return m_usePreprocess; + } + virtual bool isSubtreeBlocked() const { + return false; + } + virtual void preprocess() { + } + virtual void renderBackend() { + } + virtual void initializeBackend() { + } + virtual void resizeBackend(int, int) { + } + + void setEngine(Engine* pEngine) { + m_pEngine = pEngine; + } + Engine* engine() const { + return m_pEngine; + } + + private: + bool m_usePreprocess{}; + Engine* m_pEngine{}; +}; diff --git a/src/rendergraph/opengl/backend/baseopacitynode.h b/src/rendergraph/opengl/backend/baseopacitynode.h new file mode 100644 index 000000000000..e07f7d141fbe --- /dev/null +++ b/src/rendergraph/opengl/backend/baseopacitynode.h @@ -0,0 +1,23 @@ +#pragma once + +#include "backend/basenode.h" + +namespace rendergraph { +class BaseOpacityNode; +} + +class rendergraph::BaseOpacityNode : public rendergraph::BaseNode { + protected: + BaseOpacityNode() = default; + + public: + void setOpacity(float opacity) { + m_opacity = opacity; + } + bool isSubtreeBlocked() const override { + return m_opacity == 0.f; + } + + private: + float m_opacity{1.f}; +}; diff --git a/src/rendergraph/opengl/backend/baseopenglnode.cpp b/src/rendergraph/opengl/backend/baseopenglnode.cpp new file mode 100644 index 000000000000..1ba415696607 --- /dev/null +++ b/src/rendergraph/opengl/backend/baseopenglnode.cpp @@ -0,0 +1,21 @@ +#include "backend/baseopenglnode.h" + +#include "rendergraph/openglnode.h" + +using namespace rendergraph; + +void BaseOpenGLNode::initializeBackend() { + initializeOpenGLFunctions(); + OpenGLNode* pThis = static_cast(this); + pThis->initializeGL(); +} + +void BaseOpenGLNode::renderBackend() { + OpenGLNode* pThis = static_cast(this); + pThis->paintGL(); +} + +void BaseOpenGLNode::resizeBackend(int w, int h) { + OpenGLNode* pThis = static_cast(this); + pThis->resizeGL(w, h); +} diff --git a/src/rendergraph/opengl/backend/baseopenglnode.h b/src/rendergraph/opengl/backend/baseopenglnode.h new file mode 100644 index 000000000000..70c33642413b --- /dev/null +++ b/src/rendergraph/opengl/backend/baseopenglnode.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "backend/basenode.h" + +namespace rendergraph { +class BaseOpenGLNode; +} + +class rendergraph::BaseOpenGLNode : public rendergraph::BaseNode, + public QOpenGLFunctions { + protected: + BaseOpenGLNode() = default; + + public: + void initializeBackend() override; + void renderBackend() override; + void resizeBackend(int w, int h) override; +}; diff --git a/src/rendergraph/opengl/backend/basetexture.h b/src/rendergraph/opengl/backend/basetexture.h new file mode 100644 index 000000000000..f260dc91ebbe --- /dev/null +++ b/src/rendergraph/opengl/backend/basetexture.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseTexture = QOpenGLTexture; +} diff --git a/src/rendergraph/opengl/backend/shadercache.h b/src/rendergraph/opengl/backend/shadercache.h new file mode 100644 index 000000000000..9c4653e493c7 --- /dev/null +++ b/src/rendergraph/opengl/backend/shadercache.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include "rendergraph/material.h" +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +namespace rendergraph { +class ShaderCache; +} + +class rendergraph::ShaderCache { + private: + static std::map>& + map() { + static std::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() { + auto iter = map().begin(); + while (iter != map().end()) { + if (iter->second.use_count() == 1) { + iter = map().erase(iter); + } else { + ++iter; + } + } + } +}; diff --git a/src/rendergraph/opengl/engine.cpp b/src/rendergraph/opengl/engine.cpp new file mode 100644 index 000000000000..700b3b8994c3 --- /dev/null +++ b/src/rendergraph/opengl/engine.cpp @@ -0,0 +1,79 @@ +#include "rendergraph/engine.h" + +#include + +#include "rendergraph/node.h" + +using namespace rendergraph; + +Engine::Engine(std::unique_ptr pNode) + : m_pTopNode(std::move(pNode)) { + addToEngine(m_pTopNode.get()); +} + +void Engine::initialize() { + for (auto pNode : m_pInitializeNodes) { + pNode->backendNode()->initializeBackend(); + pNode->initialize(); + } + m_pInitializeNodes.clear(); +} + +void Engine::render() { + if (!m_pInitializeNodes.empty()) { + initialize(); + } + if (!m_pTopNode->backendNode()->isSubtreeBlocked()) { + render(m_pTopNode.get()); + } +} + +void Engine::resize(int w, int h) { + resize(m_pTopNode.get(), w, h); +} + +void Engine::preprocess() { + for (auto pNode : m_pPreprocessNodes) { + if (!pNode->backendNode()->isSubtreeBlocked()) { + pNode->backendNode()->preprocess(); + } + } +} + +void Engine::render(TreeNode* pNode) { + pNode->backendNode()->renderBackend(); + pNode = pNode->firstChild(); + while (pNode) { + if (!pNode->backendNode()->isSubtreeBlocked()) { + render(pNode); + } + pNode = pNode->nextSibling(); + } +} + +void Engine::resize(TreeNode* pNode, int w, int h) { + pNode->backendNode()->resizeBackend(w, h); + pNode->resize(w, h); + pNode = pNode->firstChild(); + while (pNode) { + resize(pNode, w, h); + pNode = pNode->nextSibling(); + } +} + +void Engine::addToEngine(TreeNode* pNode) { + assert(pNode->backendNode()->engine() == nullptr); + + pNode->backendNode()->setEngine(this); + m_pInitializeNodes.push_back(pNode); + if (pNode->backendNode()->usePreprocess()) { + m_pPreprocessNodes.push_back(pNode); + } + pNode = pNode->firstChild(); + while (pNode) { + if (pNode->backendNode()->engine() != this) { + addToEngine(pNode); + } + pNode = pNode->nextSibling(); + } +} diff --git a/src/rendergraph/opengl/geometry.cpp b/src/rendergraph/opengl/geometry.cpp new file mode 100644 index 000000000000..0c4c7434ed67 --- /dev/null +++ b/src/rendergraph/opengl/geometry.cpp @@ -0,0 +1,53 @@ +#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) { +} + +Geometry::~Geometry() = default; + +void Geometry::setAttributeValues(int attributePosition, const float* from, int numTuples) { + const int offset = m_offsets[attributePosition]; + const int tupleSize = m_tupleSizes[attributePosition]; + const int strideNumberOfFloats = m_stride / sizeof(float); + const int skip = strideNumberOfFloats - tupleSize; + + VERIFY_OR_DEBUG_ASSERT(offset + numTuples * strideNumberOfFloats - skip <= + static_cast(m_data.size())) { + return; + } + + float* to = m_data.data(); + to += offset; + + while (numTuples--) { + int k = tupleSize; + while (k--) { + *to++ = *from++; + } + to += skip; + } +} + +float* Geometry::vertexData() { + return m_data.data(); +} + +void Geometry::allocate(int vertexCount) { + const int strideNumberOfFloats = m_stride / sizeof(float); + m_vertexCount = vertexCount; + m_data.resize(strideNumberOfFloats * m_vertexCount); +} + +void Geometry::setDrawingMode(Geometry::DrawingMode mode) { + m_drawingMode = static_cast(mode); +} + +Geometry::DrawingMode Geometry::drawingMode() const { + return static_cast(m_drawingMode); +} diff --git a/src/rendergraph/opengl/geometrynode.cpp b/src/rendergraph/opengl/geometrynode.cpp new file mode 100644 index 000000000000..ad5c4448dceb --- /dev/null +++ b/src/rendergraph/opengl/geometrynode.cpp @@ -0,0 +1,23 @@ +#include "rendergraph/geometrynode.h" + +using namespace rendergraph; + +GeometryNode::GeometryNode() + : TreeNode(this) { +} + +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; +} diff --git a/src/rendergraph/opengl/material.cpp b/src/rendergraph/opengl/material.cpp new file mode 100644 index 000000000000..4913e8d5b619 --- /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 000000000000..e8fd233a21dd --- /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/openglnode.cpp b/src/rendergraph/opengl/openglnode.cpp new file mode 100644 index 000000000000..c9e3e17ff879 --- /dev/null +++ b/src/rendergraph/opengl/openglnode.cpp @@ -0,0 +1,7 @@ +#include "rendergraph/openglnode.h" + +using namespace rendergraph; + +OpenGLNode::OpenGLNode() + : TreeNode(this) { +} diff --git a/src/rendergraph/opengl/rendergraph/context.h b/src/rendergraph/opengl/rendergraph/context.h new file mode 100644 index 000000000000..d5d0611408ff --- /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 000000000000..fc5935fafd07 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/engine.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "rendergraph/node.h" + +namespace rendergraph { +class Engine; +} // namespace rendergraph + +class rendergraph::Engine { + public: + Engine(std::unique_ptr pNode); + void initialize(); + void render(); + void resize(int w, int h); + void preprocess(); + void addToEngine(TreeNode* pNode); + + private: + void initialize(TreeNode* pNode); + void render(TreeNode* pNode); + void resize(TreeNode* pNode, int, int); + + const std::unique_ptr m_pTopNode; + std::vector m_pPreprocessNodes; + std::vector m_pInitializeNodes; +}; diff --git a/src/rendergraph/opengl/rendergraph/openglnode.h b/src/rendergraph/opengl/rendergraph/openglnode.h new file mode 100644 index 000000000000..5cb3497edb3a --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/openglnode.h @@ -0,0 +1,21 @@ +#pragma once + +#include "backend/baseopenglnode.h" +#include "rendergraph/treenode.h" + +namespace rendergraph { +class OpenGLNode; +} // namespace rendergraph + +class rendergraph::OpenGLNode : public rendergraph::BaseOpenGLNode, + public rendergraph::TreeNode { + public: + OpenGLNode(); + + virtual void initializeGL() { + } + virtual void paintGL() { + } + virtual void resizeGL(int, int) { + } +}; diff --git a/src/rendergraph/opengl/texture.cpp b/src/rendergraph/opengl/texture.cpp new file mode 100644 index 000000000000..2ef65673e27f --- /dev/null +++ b/src/rendergraph/opengl/texture.cpp @@ -0,0 +1,29 @@ +#include "rendergraph/texture.h" + +#include "rendergraph/context.h" + +using namespace rendergraph; + +namespace { +QImage premultiplyAlpha(const QImage& image) { + if (image.format() == QImage::Format_RGBA8888_Premultiplied) { + return QImage(image.bits(), image.width(), image.height(), QImage::Format_RGBA8888); + } + return QImage( + image.convertToFormat(QImage::Format_RGBA8888_Premultiplied) + .bits(), + image.width(), + image.height(), + QImage::Format_RGBA8888); +} +} // namespace + +Texture::Texture(Context& 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/opengl/treenode.cpp b/src/rendergraph/opengl/treenode.cpp new file mode 100644 index 000000000000..949e6e10a2b8 --- /dev/null +++ b/src/rendergraph/opengl/treenode.cpp @@ -0,0 +1,22 @@ +#include "rendergraph/treenode.h" + +#include "rendergraph/engine.h" + +using namespace rendergraph; + +void TreeNode::setUsePreprocess(bool value) { + backendNode()->setUsePreprocessFlag(value); +} + +void TreeNode::onAppendChildNode(TreeNode* pChild) { + if (backendNode()->engine() != nullptr && + backendNode()->engine() != pChild->backendNode()->engine()) { + backendNode()->engine()->addToEngine(pChild); + } +} + +void TreeNode::onRemoveChildNode(TreeNode*) { +} + +void TreeNode::onRemoveAllChildNodes() { +} diff --git a/src/rendergraph/scenegraph/CMakeLists.txt b/src/rendergraph/scenegraph/CMakeLists.txt new file mode 100644 index 000000000000..3fe8fd57bd9d --- /dev/null +++ b/src/rendergraph/scenegraph/CMakeLists.txt @@ -0,0 +1,64 @@ +add_library(rendergraph_sg +../common/attributeset.cpp +../common/treenode.cpp +../common/node.cpp +../common/opacitynode.cpp +../common/rendergraph/attribute.h +../common/rendergraph/attributeset.h +../common/rendergraph/treenode.h +../common/rendergraph/geometry.h +../common/rendergraph/geometrynode.h +../common/rendergraph/material.h +../common/rendergraph/material/endoftrackmaterial.cpp +../common/rendergraph/material/endoftrackmaterial.h +../common/rendergraph/material/patternmaterial.cpp +../common/rendergraph/material/patternmaterial.h +../common/rendergraph/material/rgbamaterial.cpp +../common/rendergraph/material/rgbamaterial.h +../common/rendergraph/material/rgbmaterial.cpp +../common/rendergraph/material/rgbmaterial.h +../common/rendergraph/material/texturematerial.cpp +../common/rendergraph/material/texturematerial.h +../common/rendergraph/material/unicolormaterial.cpp +../common/rendergraph/material/unicolormaterial.h +../common/rendergraph/materialshader.h +../common/rendergraph/materialtype.h +../common/rendergraph/node.h +../common/rendergraph/opacitynode.h +../common/rendergraph/texture.h +../common/rendergraph/types.h +../common/rendergraph/uniform.h +../common/rendergraph/uniformscache.cpp +../common/rendergraph/uniformscache.h +../common/rendergraph/uniformset.cpp +../common/rendergraph/uniformset.h +../common/types.cpp +backend/baseattributeset.cpp +backend/baseattributeset.h +backend/basegeometry.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 +treenode.cpp +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 +) + +target_include_directories(rendergraph_sg PUBLIC . ../common) diff --git a/src/rendergraph/scenegraph/backend/baseattributeset.cpp b/src/rendergraph/scenegraph/backend/baseattributeset.cpp new file mode 100644 index 000000000000..23c173285dbb --- /dev/null +++ b/src/rendergraph/scenegraph/backend/baseattributeset.cpp @@ -0,0 +1,37 @@ +#include "backend/baseattributeset.h" + +#include "rendergraph/assert.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, + const std::vector& names) { + int i = 0; + DEBUG_ASSERT(list.size() == names.size()); + m_attributes.reserve(list.size()); + m_sgAttributes.reserve(list.size()); + for (auto item : list) { + m_attributes.push_back(Attribute{item.m_tupleSize, item.m_primitiveType, names[i++]}); + + const auto& attribute = m_attributes.back(); + + const int count = static_cast(m_sgAttributes.size()); + const bool isPosition = count == 0; + m_sgAttributes.push_back(QSGGeometry::Attribute::create(count, + attribute.m_tupleSize, + toQSGGeometryType(attribute.m_primitiveType), + isPosition)); + m_stride += attribute.m_tupleSize * sizeOf(attribute.m_primitiveType); + } +} diff --git a/src/rendergraph/scenegraph/backend/baseattributeset.h b/src/rendergraph/scenegraph/backend/baseattributeset.h new file mode 100644 index 000000000000..1d6d35638c67 --- /dev/null +++ b/src/rendergraph/scenegraph/backend/baseattributeset.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +#include "rendergraph/attribute.h" + +namespace rendergraph { +class BaseAttributeSet; +class BaseAttributeSetHelper; +} // namespace rendergraph + +class rendergraph::BaseAttributeSetHelper { + protected: + // helper base class for BaseAttributeSet to populate m_attributes, and m_sgAttributes + // needed to construct BaseAttributeSet's other base class, QSGGeometry::AttributeSet + int m_stride{}; + + BaseAttributeSetHelper(std::initializer_list list, + const std::vector& names); + + const std::vector& attributes() const { + return m_attributes; + } + + std::vector m_attributes; + std::vector m_sgAttributes; +}; + +class rendergraph::BaseAttributeSet + : protected rendergraph::BaseAttributeSetHelper, + public QSGGeometry::AttributeSet { + protected: + BaseAttributeSet(std::initializer_list list, + const std::vector& names) + : BaseAttributeSetHelper(list, names), + QSGGeometry::AttributeSet{static_cast(m_sgAttributes.size()), + m_stride, + m_sgAttributes.data()} { + } +}; diff --git a/src/rendergraph/scenegraph/backend/basegeometry.h b/src/rendergraph/scenegraph/backend/basegeometry.h new file mode 100644 index 000000000000..4eb75b7be10a --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basegeometry.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "rendergraph/attributeset.h" + +namespace rendergraph { +class BaseGeometry; +} + +class rendergraph::BaseGeometry : public QSGGeometry { + protected: + BaseGeometry(const AttributeSet& attributeSet, int vertexCount) + : QSGGeometry(attributeSet, vertexCount), + m_stride(attributeSet.stride) { + QSGGeometry::setDrawingMode(QSGGeometry::DrawTriangleStrip); + } + const int m_stride; +}; diff --git a/src/rendergraph/scenegraph/backend/basegeometrynode.h b/src/rendergraph/scenegraph/backend/basegeometrynode.h new file mode 100644 index 000000000000..e665e8451aa0 --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basegeometrynode.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseGeometryNode = QSGGeometryNode; +} diff --git a/src/rendergraph/scenegraph/backend/basematerial.cpp b/src/rendergraph/scenegraph/backend/basematerial.cpp new file mode 100644 index 000000000000..6c882d5e7593 --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerial.cpp @@ -0,0 +1,27 @@ +#include "rendergraph/material.h" +#include "rendergraph/materialshader.h" + +using namespace rendergraph; + +bool BaseMaterial::updateUniformsByteArray(QByteArray* buf) { + auto pThis = static_cast(this); + if (pThis->clearUniformsCacheDirty()) { + memcpy(buf->data(), pThis->uniformsCache().data(), pThis->uniformsCache().size()); + return true; + } + return false; +} + +int BaseMaterial::compare(const QSGMaterial* other) const { + auto pThis = static_cast(this); + return pThis->compare(static_cast(other)); +} + +QSGMaterialShader* BaseMaterial::createShader(QSGRendererInterface::RenderMode) const { + // This looks like a leak but it isn't: we pass ownership to Qt. Qt will + // cache and reuse the shader for all Material of the same type. + // TODO make sure that RenderMode is always the same. + auto pThis = static_cast(this); + auto pShader = pThis->createShader().release(); + return pShader; +} diff --git a/src/rendergraph/scenegraph/backend/basematerial.h b/src/rendergraph/scenegraph/backend/basematerial.h new file mode 100644 index 000000000000..7c745f3d991c --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerial.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace rendergraph { +class BaseMaterial; +} + +class rendergraph::BaseMaterial : public QSGMaterial { + protected: + BaseMaterial() = default; + + public: + QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override; + + int compare(const QSGMaterial* other) const override; + + bool updateUniformsByteArray(QByteArray* buf); +}; diff --git a/src/rendergraph/scenegraph/backend/basematerialshader.cpp b/src/rendergraph/scenegraph/backend/basematerialshader.cpp new file mode 100644 index 000000000000..b75fc8cdf98c --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerialshader.cpp @@ -0,0 +1,24 @@ +#include "backend/basematerialshader.h" + +#include + +#include "rendergraph/material.h" + +using namespace rendergraph; + +bool BaseMaterialShader::updateUniformData(RenderState& state, + QSGMaterial* newMaterial, + QSGMaterial* oldMaterial) { + return static_cast(newMaterial)->updateUniformsByteArray(state.uniformData()); +} + +// override for QSGMaterialShader; this function is called by the Qt scene graph to prepare use of +// sampled images in the shader, typically in the form of combined image samplers. +void BaseMaterialShader::updateSampledImage(RenderState& state, + int binding, + QSGTexture** texture, + QSGMaterial* newMaterial, + QSGMaterial* oldMaterial) { + *texture = static_cast(newMaterial)->texture(binding)->backendTexture(); + (*texture)->commitTextureOperations(state.rhi(), state.resourceUpdateBatch()); +} diff --git a/src/rendergraph/scenegraph/backend/basematerialshader.h b/src/rendergraph/scenegraph/backend/basematerialshader.h new file mode 100644 index 000000000000..d955c4052e5b --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerialshader.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class BaseMaterialShader; +} + +class rendergraph::BaseMaterialShader : public QSGMaterialShader { + protected: + BaseMaterialShader() = default; + + public: + bool updateUniformData(RenderState& state, + QSGMaterial* newMaterial, + QSGMaterial* oldMaterial) override; + + void updateSampledImage(RenderState& state, + int binding, + QSGTexture** texture, + QSGMaterial* newMaterial, + QSGMaterial* oldMaterial) override; +}; diff --git a/src/rendergraph/scenegraph/backend/basematerialtype.h b/src/rendergraph/scenegraph/backend/basematerialtype.h new file mode 100644 index 000000000000..b3606003e037 --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerialtype.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseMaterialType = QSGMaterialType; +} diff --git a/src/rendergraph/scenegraph/backend/basenode.h b/src/rendergraph/scenegraph/backend/basenode.h new file mode 100644 index 000000000000..3ece5661d801 --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basenode.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseNode = QSGNode; +} diff --git a/src/rendergraph/scenegraph/backend/baseopacitynode.h b/src/rendergraph/scenegraph/backend/baseopacitynode.h new file mode 100644 index 000000000000..3a3ef1b8661b --- /dev/null +++ b/src/rendergraph/scenegraph/backend/baseopacitynode.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseOpacityNode = QSGOpacityNode; +} diff --git a/src/rendergraph/scenegraph/backend/basetexture.h b/src/rendergraph/scenegraph/backend/basetexture.h new file mode 100644 index 000000000000..10840145d52b --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basetexture.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseTexture = QSGTexture; +} diff --git a/src/rendergraph/scenegraph/context.cpp b/src/rendergraph/scenegraph/context.cpp new file mode 100644 index 000000000000..61c5e12caa7c --- /dev/null +++ b/src/rendergraph/scenegraph/context.cpp @@ -0,0 +1,11 @@ +#include "rendergraph/context.h" + +using namespace rendergraph; + +Context::Context(QQuickWindow* pWindow) + : m_pWindow(pWindow) { +} + +QQuickWindow* Context::window() const { + return m_pWindow; +} diff --git a/src/rendergraph/scenegraph/geometry.cpp b/src/rendergraph/scenegraph/geometry.cpp new file mode 100644 index 000000000000..6fff53e37890 --- /dev/null +++ b/src/rendergraph/scenegraph/geometry.cpp @@ -0,0 +1,75 @@ +#include "rendergraph/geometry.h" + +#include "rendergraph/assert.h" + +using namespace rendergraph; + +namespace { +QSGGeometry::DrawingMode toSgDrawingMode(Geometry::DrawingMode mode) { + switch (mode) { + case Geometry::DrawingMode::Triangles: + return QSGGeometry::DrawTriangles; + case Geometry::DrawingMode::TriangleStrip: + return QSGGeometry::DrawTriangleStrip; + } +} + +Geometry::DrawingMode fromSgDrawingMode(unsigned int mode) { + switch (mode) { + case QSGGeometry::DrawTriangles: + return Geometry::DrawingMode::Triangles; + case QSGGeometry::DrawTriangleStrip: + return Geometry::DrawingMode::TriangleStrip; + default: + throw "not implemented"; + } +} +} // namespace + +Geometry::Geometry(const rendergraph::AttributeSet& attributeSet, int vertexCount) + : BaseGeometry(attributeSet, vertexCount) { +} + +Geometry::~Geometry() = default; + +void Geometry::setAttributeValues(int attributePosition, const float* from, int numTuples) { + const auto attributeArray = QSGGeometry::attributes(); + int offset = 0; + for (int i = 0; i < attributePosition; i++) { + offset += attributeArray[i].tupleSize; + } + const int tupleSize = attributeArray[attributePosition].tupleSize; + const int strideNumberOfFloats = m_stride / sizeof(float); + const int skip = strideNumberOfFloats - tupleSize; + + VERIFY_OR_DEBUG_ASSERT(offset + numTuples * strideNumberOfFloats - skip <= + vertexCount() * strideNumberOfFloats) { + return; + } + + float* to = static_cast(QSGGeometry::vertexData()); + to += offset; + while (numTuples--) { + int k = tupleSize; + while (k--) { + *to++ = *from++; + } + to += skip; + } +} + +float* Geometry::vertexData() { + return static_cast(QSGGeometry::vertexData()); +} + +void Geometry::allocate(int vertexCount) { + QSGGeometry::allocate(vertexCount); +} + +void Geometry::setDrawingMode(Geometry::DrawingMode mode) { + QSGGeometry::setDrawingMode(toSgDrawingMode(mode)); +} + +Geometry::DrawingMode Geometry::drawingMode() const { + return fromSgDrawingMode(QSGGeometry::drawingMode()); +} diff --git a/src/rendergraph/scenegraph/geometrynode.cpp b/src/rendergraph/scenegraph/geometrynode.cpp new file mode 100644 index 000000000000..bd9f26570e8e --- /dev/null +++ b/src/rendergraph/scenegraph/geometrynode.cpp @@ -0,0 +1,25 @@ +#include "rendergraph/geometrynode.h" + +using namespace rendergraph; + +GeometryNode::GeometryNode() + : TreeNode(this) { +} + +void GeometryNode::setGeometry(std::unique_ptr pGeometry) { + m_pGeometry = std::move(pGeometry); + QSGGeometryNode::setGeometry(m_pGeometry.get()); +} + +void GeometryNode::setMaterial(std::unique_ptr pMaterial) { + m_pMaterial = std::move(pMaterial); + QSGGeometryNode::setMaterial(m_pMaterial.get()); +} + +Geometry& GeometryNode::geometry() const { + return *m_pGeometry; +} + +Material& GeometryNode::material() const { + return *m_pMaterial; +} diff --git a/src/rendergraph/scenegraph/material.cpp b/src/rendergraph/scenegraph/material.cpp new file mode 100644 index 000000000000..79cd048b0113 --- /dev/null +++ b/src/rendergraph/scenegraph/material.cpp @@ -0,0 +1,10 @@ +#include "rendergraph/material.h" + +using namespace rendergraph; + +Material::Material(const UniformSet& uniformSet) + : m_uniformsCache(uniformSet) { + setFlag(QSGMaterial::Blending); +} + +Material::~Material() = default; diff --git a/src/rendergraph/scenegraph/materialshader.cpp b/src/rendergraph/scenegraph/materialshader.cpp new file mode 100644 index 000000000000..3a34c80274d4 --- /dev/null +++ b/src/rendergraph/scenegraph/materialshader.cpp @@ -0,0 +1,19 @@ +#include "rendergraph/materialshader.h" + +using namespace rendergraph; + +namespace { +static QString resource(const QString& filename) { + return QStringLiteral(":/shaders/rendergraph/%1.qsb").arg(filename); +} +} // namespace + +MaterialShader::MaterialShader(const char* vertexShaderFile, + const char* fragmentShaderFile, + const UniformSet& uniformSet, + const AttributeSet& attributeSet) { + (void)uniformSet; + (void)attributeSet; + setShaderFileName(VertexStage, resource(vertexShaderFile)); + setShaderFileName(FragmentStage, resource(fragmentShaderFile)); +} diff --git a/src/rendergraph/scenegraph/rendergraph/context.h b/src/rendergraph/scenegraph/rendergraph/context.h new file mode 100644 index 000000000000..e26e6eb78599 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/context.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace rendergraph { +class Context; +} + +class rendergraph::Context { + public: + Context(QQuickWindow* pWindow); + QQuickWindow* window() const; + + private: + QQuickWindow* m_pWindow; +}; diff --git a/src/rendergraph/scenegraph/texture.cpp b/src/rendergraph/scenegraph/texture.cpp new file mode 100644 index 000000000000..9e923332a2fa --- /dev/null +++ b/src/rendergraph/scenegraph/texture.cpp @@ -0,0 +1,13 @@ +#include "rendergraph/texture.h" + +#include "rendergraph/context.h" + +using namespace rendergraph; + +Texture::Texture(Context& context, const QImage& image) + : m_pTexture(context.window()->createTextureFromImage(image)) { +} + +qint64 Texture::comparisonKey() const { + return m_pTexture->comparisonKey(); +} diff --git a/src/rendergraph/scenegraph/treenode.cpp b/src/rendergraph/scenegraph/treenode.cpp new file mode 100644 index 000000000000..904afe7b0be0 --- /dev/null +++ b/src/rendergraph/scenegraph/treenode.cpp @@ -0,0 +1,19 @@ +#include "rendergraph/treenode.h" + +using namespace rendergraph; + +void TreeNode::setUsePreprocess(bool value) { + backendNode()->setFlag(QSGNode::UsePreprocess, value); +} + +void TreeNode::onAppendChildNode(TreeNode* pChild) { + backendNode()->appendChildNode(pChild->backendNode()); +} + +void TreeNode::onRemoveChildNode(TreeNode* pChild) { + backendNode()->removeChildNode(pChild->backendNode()); +} + +void TreeNode::onRemoveAllChildNodes() { + backendNode()->removeAllChildNodes(); +} diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 2c6d2f7d1479..156f3d9601f9 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -5,17 +5,21 @@ #include #include #include -#include #include #include #include #include "./util/assert.h" +#include "rendergraph/context.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/texturematerial.h" +#include "rendergraph/vertexupdaters/texturedvertexupdater.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/vertexdata.h" // Render digits using a texture (generated) with digits with blurred dark outline +using namespace rendergraph; + namespace { // The texture will contain 12 characters: 10 digits, colon and dot @@ -56,18 +60,19 @@ static_assert(checkCharToIndex()); } // namespace -allshader::DigitsRenderer::~DigitsRenderer() = default; - -void allshader::DigitsRenderer::init() { - initializeOpenGLFunctions(); - m_shader.init(); +allshader::DigitsRenderNode::DigitsRenderNode() { + setGeometry(std::make_unique(TextureMaterial::attributes(), 0)); + setMaterial(std::make_unique()); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); } -float allshader::DigitsRenderer::height() const { +allshader::DigitsRenderNode::~DigitsRenderNode() = default; + +float allshader::DigitsRenderNode::height() const { return m_height; } -void allshader::DigitsRenderer::updateTexture( +void allshader::DigitsRenderNode::updateTexture( float fontPointSize, float maxHeight, float devicePixelRatio) { if (fontPointSize == m_fontPointSize && maxHeight == m_maxHeight) { return; @@ -202,64 +207,69 @@ void allshader::DigitsRenderer::updateTexture( m_offset[NUM_CHARS] = 1.f; } - m_texture.setData(image); + Context context; + dynamic_cast(material()) + .setTexture(std::make_unique(context, image)); } -float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, +void allshader::DigitsRenderNode::update(const QMatrix4x4& matrix, + float x, + float y, + bool multiLine, + const QString& s1, + const QString& s2) { + const int numVerticesPerRectangle = 6; + const int reserved = (s1.length() + s2.length()) * numVerticesPerRectangle; + geometry().allocate(reserved); + TexturedVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + + const float ch = height(); + if (!s1.isEmpty()) { + const auto w = addVertices(vertexUpdater, + x, + y, + s1); + if (multiLine) { + y += ch; + } else { + x += w + ch * 0.75f; + } + } + if (!s2.isEmpty()) { + addVertices(vertexUpdater, + x, + y, + s2); + } + + DEBUG_ASSERT(reserved == vertexUpdater.index()); + + material().setUniform(0, matrix); +} + +void allshader::DigitsRenderNode::clear() { + geometry().allocate(0); +} + +float allshader::DigitsRenderNode::addVertices(TexturedVertexUpdater& vertexUpdater, float x, float y, const QString& s) { - const int n = s.length(); const float x0 = x; const float space = static_cast(m_penWidth) / 2; - VertexData posVertices; - VertexData texVertices; - - posVertices.reserve(n * 6); // two triangles per character - texVertices.reserve(n * 6); - for (QChar c : s) { if (x != x0) { x -= space; } int index = charToIndex(c); - texVertices.addRectangle(m_offset[index], 0.f, m_offset[index + 1], 1.f); - posVertices.addRectangle(x, - y, - x + m_width[index], - y + height()); + vertexUpdater.addRectangle({x, y}, + {x + m_width[index], y + height()}, + {m_offset[index], 0.f}, + {m_offset[index + 1], 1.f}); x += m_width[index]; } - m_shader.bind(); - - const int matrixLocation = m_shader.uniformLocation("matrix"); - const int textureLocation = m_shader.uniformLocation("texture"); - const int positionLocation = m_shader.attributeLocation("position"); - const int texcoordLocation = m_shader.attributeLocation("texcoord"); - - m_shader.setUniformValue(matrixLocation, matrix); - - m_shader.enableAttributeArray(positionLocation); - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, posVertices.constData(), 2); - m_shader.enableAttributeArray(texcoordLocation); - m_shader.setAttributeArray( - texcoordLocation, GL_FLOAT, texVertices.constData(), 2); - - m_shader.setUniformValue(textureLocation, 0); - - m_texture.bind(); - - glDrawArrays(GL_TRIANGLES, 0, posVertices.size()); - - m_texture.release(); - - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(texcoordLocation); - m_shader.release(); - return x - x0; } diff --git a/src/waveform/renderers/allshader/digitsrenderer.h b/src/waveform/renderers/allshader/digitsrenderer.h index 4d280bbdaad2..b55c72b884be 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.h +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -1,30 +1,42 @@ #pragma once -#include +#include -#include "shaders/textureshader.h" -#include "util/opengltexture2d.h" +#include "rendergraph/geometrynode.h" +#include "util/class.h" + +namespace rendergraph { +class TexturedVertexUpdater; +} namespace allshader { -class DigitsRenderer; +class DigitsRenderNode; } -class allshader::DigitsRenderer : public QOpenGLFunctions { +class allshader::DigitsRenderNode : public rendergraph::GeometryNode { public: - DigitsRenderer() = default; - ~DigitsRenderer(); + DigitsRenderNode(); + ~DigitsRenderNode(); - void init(); void updateTexture(float fontPointSize, float maxHeight, float devicePixelRatio); - float draw(const QMatrix4x4& matrix, + + void update(const QMatrix4x4& matrix, float x, float y, - const QString& s); + bool multiLine, + const QString& s1, + const QString& s2); + + void clear(); + float height() const; private: - mixxx::TextureShader m_shader; - OpenGLTexture2D m_texture; + float addVertices(rendergraph::TexturedVertexUpdater& vertexUpdater, + float x, + float y, + const QString& s); + int m_penWidth; float m_offset[13]; float m_width[12]; @@ -32,5 +44,5 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { float m_height{}; float m_maxHeight{}; float m_adjustedFontPointSize{}; - DISALLOW_COPY_AND_ASSIGN(DigitsRenderer); + DISALLOW_COPY_AND_ASSIGN(DigitsRenderNode); }; diff --git a/src/waveform/renderers/allshader/waveformrenderbackground.h b/src/waveform/renderers/allshader/waveformrenderbackground.h index 8a913e277425..97c82c7d122e 100644 --- a/src/waveform/renderers/allshader/waveformrenderbackground.h +++ b/src/waveform/renderers/allshader/waveformrenderbackground.h @@ -2,6 +2,7 @@ #include +#include "rendergraph/openglnode.h" #include "util/class.h" #include "waveform/renderers/allshader/waveformrenderer.h" @@ -9,7 +10,9 @@ namespace allshader { class WaveformRenderBackground; } -class allshader::WaveformRenderBackground final : public allshader::WaveformRenderer { +class allshader::WaveformRenderBackground final + : public allshader::WaveformRenderer, + public rendergraph::OpenGLNode { public: explicit WaveformRenderBackground(WaveformWidgetRenderer* waveformWidgetRenderer); diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.cpp b/src/waveform/renderers/allshader/waveformrenderbeat.cpp index 3f793efd706b..d71d0c31ef8c 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.cpp +++ b/src/waveform/renderers/allshader/waveformrenderbeat.cpp @@ -2,23 +2,25 @@ #include +#include "rendergraph/geometry.h" +#include "rendergraph/material/unicolormaterial.h" +#include "rendergraph/vertexupdaters/vertexupdater.h" #include "skin/legacy/skincontext.h" #include "track/track.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "widget/wskincolor.h" +using namespace rendergraph; + namespace allshader { WaveformRenderBeat::WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type) - : WaveformRenderer(waveformWidget), + : ::WaveformRendererAbstract(waveformWidget), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { -} - -void WaveformRenderBeat::initializeGL() { - WaveformRenderer::initializeGL(); - m_shader.init(); + initForRectangles(0); + setUsePreprocess(true); } void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& context) { @@ -26,11 +28,23 @@ void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& context) m_color = WSkinColor::getCorrectColor(m_color).toRgb(); } -void WaveformRenderBeat::paintGL() { - TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); +void WaveformRenderBeat::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); +} + +void WaveformRenderBeat::preprocess() { + if (!preprocessInner()) { + geometry().allocate(0); + } +} + +bool WaveformRenderBeat::preprocessInner() { + const TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); if (!trackInfo || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { - return; + return false; } auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip @@ -38,24 +52,21 @@ void WaveformRenderBeat::paintGL() { mixxx::BeatsPointer trackBeats = trackInfo->getBeats(); if (!trackBeats) { - return; + return false; } int alpha = m_waveformRenderer->getBeatGridAlpha(); if (alpha == 0) { - return; + return false; } const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - m_color.setAlphaF(alpha / 100.0f); const double trackSamples = m_waveformRenderer->getTrackSamples(); - if (trackSamples <= 0) { - return; + if (trackSamples <= 0.0) { + return false; } const double firstDisplayedPosition = @@ -69,7 +80,7 @@ void WaveformRenderBeat::paintGL() { lastDisplayedPosition * trackSamples); if (!startPosition.isValid() || !endPosition.isValid()) { - return; + return false; } const float rendererBreadth = m_waveformRenderer->getBreadth(); @@ -88,8 +99,10 @@ void WaveformRenderBeat::paintGL() { } const int reserved = numBeatsInRange * numVerticesPerLine; - m_vertices.clear(); - m_vertices.reserve(reserved); + geometry().allocate(reserved); + // TODO set dirty for scenegraph + + VertexUpdater vertexUpdater{geometry().vertexDataAs()}; for (auto it = trackBeats->iteratorFrom(startPosition); it != trackBeats->cend() && *it <= endPosition; @@ -104,33 +117,18 @@ void WaveformRenderBeat::paintGL() { const float x1 = static_cast(xBeatPoint); const float x2 = x1 + 1.f; - m_vertices.addRectangle(x1, - 0.f, - x2, - m_isSlipRenderer ? rendererBreadth / 2 : rendererBreadth); + vertexUpdater.addRectangle({x1, 0.f}, + {x2, m_isSlipRenderer ? rendererBreadth / 2 : rendererBreadth}); } - DEBUG_ASSERT(reserved == m_vertices.size()); - - const int positionLocation = m_shader.positionLocation(); - const int matrixLocation = m_shader.matrixLocation(); - const int colorLocation = m_shader.colorLocation(); - - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); + DEBUG_ASSERT(reserved == vertexUpdater.index()); const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, m_vertices.constData(), 2); - - m_shader.setUniformValue(matrixLocation, matrix); - m_shader.setUniformValue(colorLocation, m_color); - - glDrawArrays(GL_TRIANGLES, 0, m_vertices.size()); + material().setUniform(0, matrix); + material().setUniform(1, m_color); - m_shader.disableAttributeArray(positionLocation); - m_shader.release(); + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.h b/src/waveform/renderers/allshader/waveformrenderbeat.h index d3b2b79d1bce..c0c13126dc25 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.h +++ b/src/waveform/renderers/allshader/waveformrenderbeat.h @@ -1,11 +1,11 @@ #pragma once #include +#include -#include "shaders/unicolorshader.h" +#include "rendergraph/geometrynode.h" #include "util/class.h" -#include "waveform/renderers/allshader/vertexdata.h" -#include "waveform/renderers/allshader/waveformrenderer.h" +#include "waveform/renderers/waveformrendererabstract.h" class QDomNode; class SkinContext; @@ -14,22 +14,27 @@ namespace allshader { class WaveformRenderBeat; } -class allshader::WaveformRenderBeat final : public allshader::WaveformRenderer { +class allshader::WaveformRenderBeat final + : public ::WaveformRendererAbstract, + public rendergraph::GeometryNode { public: explicit WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& context) override; - void paintGL() override; - void initializeGL() override; + + // Virtuals for rendergraph::Node + void preprocess() override; private: - mixxx::UnicolorShader m_shader; QColor m_color; - VertexData m_vertices; - bool m_isSlipRenderer; + bool preprocessInner(); + DISALLOW_COPY_AND_ASSIGN(WaveformRenderBeat); }; diff --git a/src/waveform/renderers/allshader/waveformrenderer.h b/src/waveform/renderers/allshader/waveformrenderer.h index 04b4b4279356..11c486eb49ce 100644 --- a/src/waveform/renderers/allshader/waveformrenderer.h +++ b/src/waveform/renderers/allshader/waveformrenderer.h @@ -1,6 +1,5 @@ #pragma once -#include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/waveformrendererabstract.h" class WaveformWidgetRenderer; @@ -9,28 +8,15 @@ namespace allshader { class WaveformRenderer; } -class allshader::WaveformRenderer : public ::WaveformRendererAbstract, - public allshader::WaveformRendererAbstract { +class allshader::WaveformRenderer : public ::WaveformRendererAbstract { public: explicit WaveformRenderer(WaveformWidgetRenderer* widget); - // Pure virtual from allshader::WaveformRendererAbstract. + // Pure virtual from WaveformRendererAbstract // Renderers that use QPainter functionality implement this. // But as all classes derived from allshader::WaveformRenderer // will only use openGL functions (combining QPainter and // QOpenGLWindow has bad performance), we leave this empty. // Should never be called. void draw(QPainter* painter, QPaintEvent* event) override final; - - allshader::WaveformRendererAbstract* allshaderWaveformRenderer() override final { - // This class is indirectly derived from - // WaveformWidgetRenderer, which has a member - // QList m_rendererStack; - // In the case of allshader::WaveformRenderer widgets, - // all the items on this stack are derived from - // allshader::WaveformRendererAbstract and we use this method to - // access them as such. (We could also have used a - // dynamic cast (or even static cast instead) - return this; - } }; diff --git a/src/waveform/renderers/allshader/waveformrendererabstract.h b/src/waveform/renderers/allshader/waveformrendererabstract.h deleted file mode 100644 index 5c530a00e225..000000000000 --- a/src/waveform/renderers/allshader/waveformrendererabstract.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -namespace allshader { -class WaveformRendererAbstract; -} - -class allshader::WaveformRendererAbstract : public QOpenGLFunctions { - public: - /// This interface class is called by class allshader::WaveformWidget. - /// Class allshader::WaveformWidget is derived from the allshader-based - /// class WGLWidget and, in its implementation of the WGLWidget virtual - /// methods, calls a stack of allshader::WaveformRendererAbstract instances, for - /// the different layers of the waveform graphics (background, beat - /// markers, the actual waveform, etc). In other word, this interface - /// mimics the WGLWidget virtuals, but to be called as layers of an - /// actual WGLWidget. - virtual ~WaveformRendererAbstract() = default; - virtual void initializeGL() { - initializeOpenGLFunctions(); - } - virtual void resizeGL(int /* w */, int /* h */) { - } - virtual void paintGL() = 0; -}; diff --git a/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp b/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp index 10df71210207..6b5b5f12f1cf 100644 --- a/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp +++ b/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp @@ -1,9 +1,12 @@ #include "waveform/renderers/allshader/waveformrendererendoftrack.h" #include +#include #include #include "control/controlproxy.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/endoftrackmaterial.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveformwidgetfactory.h" #include "widget/wskincolor.h" @@ -11,19 +14,31 @@ namespace { constexpr int kBlinkingPeriodMillis = 1000; -constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; -constexpr float verticalGradientArray[] = {1.f, 1.f, -1.f, -1.f}; -constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; } // anonymous namespace +using namespace rendergraph; + namespace allshader { WaveformRendererEndOfTrack::WaveformRendererEndOfTrack( WaveformWidgetRenderer* waveformWidget) - : WaveformRenderer(waveformWidget), + : ::WaveformRendererAbstract(waveformWidget), m_pEndOfTrackControl(nullptr), m_pTimeRemainingControl(nullptr) { + setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4)); + setMaterial(std::make_unique()); + setUsePreprocess(true); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, horizontalGradientArray, 4); + material().setUniform(0, QVector4D{0.f, 0.f, 0.f, 0.f}); +} + +void WaveformRendererEndOfTrack::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } bool WaveformRendererEndOfTrack::init() { @@ -46,43 +61,7 @@ void WaveformRendererEndOfTrack::setup(const QDomNode& node, const SkinContext& } } -void WaveformRendererEndOfTrack::initializeGL() { - WaveformRenderer::initializeGL(); - m_shader.init(); -} - -void WaveformRendererEndOfTrack::fillWithGradient(QColor color) { - const int colorLocation = m_shader.colorLocation(); - const int positionLocation = m_shader.positionLocation(); - const int gradientLocation = m_shader.gradientLocation(); - - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); - m_shader.enableAttributeArray(gradientLocation); - - m_shader.setUniformValue(colorLocation, color); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, positionArray, 2); - m_shader.setAttributeArray(gradientLocation, - GL_FLOAT, - m_waveformRenderer->getOrientation() == Qt::Vertical - ? verticalGradientArray - : horizontalGradientArray, - 1); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(gradientLocation); - m_shader.release(); -} - -void WaveformRendererEndOfTrack::paintGL() { - if (!m_pEndOfTrackControl->toBool()) { - return; - } - +void WaveformRendererEndOfTrack::preprocess() { const int elapsed = m_timer.elapsed().toIntegerMillis() % kBlinkingPeriodMillis; const double blinkIntensity = (double)(2 * abs(elapsed - kBlinkingPeriodMillis / 2)) / @@ -100,11 +79,12 @@ void WaveformRendererEndOfTrack::paintGL() { QColor color = m_color; color.setAlphaF(static_cast(alpha)); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - fillWithGradient(color); + material().setUniform(0, color); } } +bool WaveformRendererEndOfTrack::isSubtreeBlocked() const { + return !m_pEndOfTrackControl->toBool(); +} + } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererendoftrack.h b/src/waveform/renderers/allshader/waveformrendererendoftrack.h index 664a2cac16e5..9b7ec182de5f 100644 --- a/src/waveform/renderers/allshader/waveformrendererendoftrack.h +++ b/src/waveform/renderers/allshader/waveformrendererendoftrack.h @@ -3,10 +3,11 @@ #include #include -#include "shaders/endoftrackshader.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/opacitynode.h" #include "util/class.h" #include "util/performancetimer.h" -#include "waveform/renderers/allshader/waveformrenderer.h" +#include "waveform/renderers/waveformrendererabstract.h" class ControlProxy; class QDomNode; @@ -16,22 +17,29 @@ namespace allshader { class WaveformRendererEndOfTrack; } -class allshader::WaveformRendererEndOfTrack final : public allshader::WaveformRenderer { +class allshader::WaveformRendererEndOfTrack final + : public ::WaveformRendererAbstract, + public rendergraph::GeometryNode { public: explicit WaveformRendererEndOfTrack( WaveformWidgetRenderer* waveformWidget); + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& context) override; bool init() override; - void initializeGL() override; - void paintGL() override; + // Virtual for rendergraph::Node + void preprocess() override; + bool isSubtreeBlocked() const override; private: - void fillWithGradient(QColor color); + static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; + static constexpr float verticalGradientArray[] = {1.f, 1.f, -1.f, -1.f}; + static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; - mixxx::EndOfTrackShader m_shader; std::unique_ptr m_pEndOfTrackControl; std::unique_ptr m_pTimeRemainingControl; diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp index 0ddf164cc3e6..f48b1136f595 100644 --- a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp +++ b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp @@ -19,7 +19,6 @@ void WaveformRendererFiltered::onSetup(const QDomNode& node) { } void WaveformRendererFiltered::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.h b/src/waveform/renderers/allshader/waveformrendererfiltered.h index 158ff880c01f..43441adafa56 100644 --- a/src/waveform/renderers/allshader/waveformrendererfiltered.h +++ b/src/waveform/renderers/allshader/waveformrendererfiltered.h @@ -1,5 +1,6 @@ #pragma once +#include "rendergraph/openglnode.h" #include "shaders/unicolorshader.h" #include "util/class.h" #include "waveform/renderers/allshader/vertexdata.h" @@ -9,7 +10,9 @@ namespace allshader { class WaveformRendererFiltered; } -class allshader::WaveformRendererFiltered final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererFiltered final + : public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { public: explicit WaveformRendererFiltered(WaveformWidgetRenderer* waveformWidget, bool rgbStacked); diff --git a/src/waveform/renderers/allshader/waveformrendererhsv.cpp b/src/waveform/renderers/allshader/waveformrendererhsv.cpp index 31b9785acdfd..75b34dbae000 100644 --- a/src/waveform/renderers/allshader/waveformrendererhsv.cpp +++ b/src/waveform/renderers/allshader/waveformrendererhsv.cpp @@ -19,7 +19,6 @@ void WaveformRendererHSV::onSetup(const QDomNode& node) { } void WaveformRendererHSV::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererhsv.h b/src/waveform/renderers/allshader/waveformrendererhsv.h index 0fa0b6bf8636..a6fdbe95f515 100644 --- a/src/waveform/renderers/allshader/waveformrendererhsv.h +++ b/src/waveform/renderers/allshader/waveformrendererhsv.h @@ -1,5 +1,6 @@ #pragma once +#include "rendergraph/openglnode.h" #include "shaders/rgbshader.h" #include "util/class.h" #include "waveform/renderers/allshader/rgbdata.h" @@ -10,7 +11,9 @@ namespace allshader { class WaveformRendererHSV; } -class allshader::WaveformRendererHSV final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererHSV final + : public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { public: explicit WaveformRendererHSV(WaveformWidgetRenderer* waveformWidget); diff --git a/src/waveform/renderers/allshader/waveformrendererpreroll.cpp b/src/waveform/renderers/allshader/waveformrendererpreroll.cpp index eea01c5bdada..45bfd4026081 100644 --- a/src/waveform/renderers/allshader/waveformrendererpreroll.cpp +++ b/src/waveform/renderers/allshader/waveformrendererpreroll.cpp @@ -1,10 +1,13 @@ #include "waveform/renderers/allshader/waveformrendererpreroll.h" #include -#include #include #include +#include "rendergraph/context.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/patternmaterial.h" +#include "rendergraph/vertexupdaters/texturedvertexupdater.h" #include "skin/legacy/skincontext.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" @@ -20,7 +23,7 @@ QImage drawPrerollImage(float markerLength, const float imageW = static_cast(imagePixelW) / devicePixelRatio; const float imageH = static_cast(imagePixelH) / devicePixelRatio; - QImage image(imagePixelW, imagePixelH, QImage::Format_ARGB32_Premultiplied); + QImage image(imagePixelW, imagePixelH, QImage::Format_RGBA8888_Premultiplied); image.setDevicePixelRatio(devicePixelRatio); const float penWidth = 1.5f; @@ -47,7 +50,7 @@ QImage drawPrerollImage(float markerLength, path.lineTo(p0); path.closeSubpath(); QColor fillColor = color; - fillColor.setAlphaF(0.5f); + fillColor.setAlphaF(0.25f); painter.fillPath(path, QBrush(fillColor)); painter.drawPath(path); @@ -57,13 +60,19 @@ QImage drawPrerollImage(float markerLength, } } // anonymous namespace +using namespace rendergraph; + namespace allshader { WaveformRendererPreroll::WaveformRendererPreroll( WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type) - : WaveformRenderer(waveformWidget), + : ::WaveformRendererAbstract(waveformWidget), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { + setGeometry(std::make_unique(PatternMaterial::attributes(), 0)); + setMaterial(std::make_unique()); + setUsePreprocess(true); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); } WaveformRendererPreroll::~WaveformRendererPreroll() = default; @@ -74,15 +83,23 @@ void WaveformRendererPreroll::setup( m_color = WSkinColor::getCorrectColor(m_color); } -void WaveformRendererPreroll::initializeGL() { - WaveformRenderer::initializeGL(); - m_shader.init(); +void WaveformRendererPreroll::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } -void WaveformRendererPreroll::paintGL() { - const TrackPointer track = m_waveformRenderer->getTrackInfo(); - if (!track || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { - return; +void WaveformRendererPreroll::preprocess() { + if (!preprocessInner()) { + geometry().allocate(0); + } +} + +bool WaveformRendererPreroll::preprocessInner() { + const TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); + + if (!trackInfo || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { + return false; } auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip @@ -96,11 +113,15 @@ void WaveformRendererPreroll::paintGL() { // to indicate the respective zones. const bool preRollVisible = firstDisplayedPosition < 0; const bool postRollVisible = lastDisplayedPosition > 1; + const int numVerticesPerRectangle = 6; - if (!(preRollVisible || postRollVisible)) { - return; + if (!preRollVisible && !postRollVisible) { + return false; } + const int reserved = (preRollVisible ? numVerticesPerRectangle : 0) + + (postRollVisible ? numVerticesPerRectangle : 0); + const double playMarkerPosition = m_waveformRenderer->getPlayMarkerPosition(); const double vSamplesPerPixel = m_waveformRenderer->getVisualSamplePerPixel(); const double numberOfVSamples = m_waveformRenderer->getLength() * vSamplesPerPixel; @@ -126,36 +147,21 @@ void WaveformRendererPreroll::paintGL() { // has changed size last time. m_markerLength = markerLength; m_markerBreadth = markerBreadth; - m_texture.setData(drawPrerollImage(m_markerLength, - m_markerBreadth, - m_waveformRenderer->getDevicePixelRatio(), - m_color)); - } - - if (!m_texture.isStorageAllocated()) { - return; + Context context; + dynamic_cast(material()) + .setTexture(std::make_unique(context, + drawPrerollImage(m_markerLength, + m_markerBreadth, + m_waveformRenderer->getDevicePixelRatio(), + m_color))); } - const int matrixLocation = m_shader.matrixLocation(); - const int textureLocation = m_shader.textureLocation(); - const int positionLocation = m_shader.positionLocation(); - const int texcoordLocation = m_shader.texcoordLocation(); - - // Set up the shader - m_shader.bind(); - - m_shader.enableAttributeArray(positionLocation); - m_shader.enableAttributeArray(texcoordLocation); - - const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); - - m_shader.setUniformValue(matrixLocation, matrix); - m_shader.setUniformValue(textureLocation, 0); - - m_texture.bind(); + geometry().allocate(reserved); const float end = m_waveformRenderer->getLength(); + TexturedVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + if (preRollVisible) { // VSample position of the right-most triangle's tip const double triangleTipVSamplePosition = @@ -169,11 +175,14 @@ void WaveformRendererPreroll::paintGL() { x -= std::ceil((x - limit) / markerLength) * markerLength; } - drawPattern(x, - halfBreadth - halfMarkerBreadth, - 0.f, - m_isSlipRenderer ? halfBreadth : halfBreadth + halfMarkerBreadth, - x / markerLength); + const float repetitions = x / markerLength; + + vertexUpdater.addRectangle({x, halfBreadth - halfMarkerBreadth}, + {0, + m_isSlipRenderer ? halfBreadth + : halfBreadth + halfMarkerBreadth}, + {0.f, 0.f}, + {repetitions, m_isSlipRenderer ? 0.5f : 1.f}); } if (postRollVisible) { @@ -190,44 +199,23 @@ void WaveformRendererPreroll::paintGL() { x += std::ceil((limit - x) / markerLength) * markerLength; } - drawPattern(x, - halfBreadth - halfMarkerBreadth, - end, - m_isSlipRenderer ? halfBreadth : halfBreadth + halfMarkerBreadth, - (end - x) / markerLength); + const float repetitions = (end - x) / markerLength; + + vertexUpdater.addRectangle({x, halfBreadth - halfMarkerBreadth}, + {end, + m_isSlipRenderer ? halfBreadth + : halfBreadth + halfMarkerBreadth}, + {0.f, 0.f}, + {repetitions, m_isSlipRenderer ? 0.5f : 1.f}); } - m_texture.release(); + DEBUG_ASSERT(reserved == vertexUpdater.index()); - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(texcoordLocation); - m_shader.release(); -} + const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); + + material().setUniform(0, matrix); -void WaveformRendererPreroll::drawPattern( - float x1, float y1, float x2, float y2, float repetitions) { - // Draw a large rectangle with a repeating pattern of the texture - const int repetitionsLocation = m_shader.repetitionsLocation(); - const int positionLocation = m_shader.positionLocation(); - const int texcoordLocation = m_shader.texcoordLocation(); - - const std::array positionArray = {x1, y1, x2, y1, x1, y2, x2, y2}; - const std::array texcoordArray = {0.f, - 0.f, - 1.f, - 0.f, - 0.f, - m_isSlipRenderer ? 0.5f : 1.f, - 1.f, - m_isSlipRenderer ? 0.5f : 1.f}; - m_shader.setUniformValue(repetitionsLocation, QVector2D(repetitions, 1.0)); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, positionArray.data(), 2); - m_shader.setAttributeArray( - texcoordLocation, GL_FLOAT, texcoordArray.data(), 2); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererpreroll.h b/src/waveform/renderers/allshader/waveformrendererpreroll.h index 742320424c57..7e3f10ea6281 100644 --- a/src/waveform/renderers/allshader/waveformrendererpreroll.h +++ b/src/waveform/renderers/allshader/waveformrendererpreroll.h @@ -1,46 +1,44 @@ #pragma once #include -#include #include -#include "shaders/patternshader.h" +#include "rendergraph/geometrynode.h" #include "util/class.h" -#include "util/opengltexture2d.h" -#include "waveform/renderers/allshader/vertexdata.h" -#include "waveform/renderers/allshader/waveformrenderer.h" +#include "waveform/renderers/waveformrendererabstract.h" class QDomNode; class SkinContext; -class QOpenGLTexture; namespace allshader { class WaveformRendererPreroll; -class WaveformRendererSlipPreroll; } -class allshader::WaveformRendererPreroll : public allshader::WaveformRenderer { +class allshader::WaveformRendererPreroll final + : public ::WaveformRendererAbstract, + public rendergraph::GeometryNode { public: explicit WaveformRendererPreroll( - WaveformWidgetRenderer* waveformWidgetRenderer, + WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); ~WaveformRendererPreroll() override; + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& context) override; - void paintGL() override; - void initializeGL() override; - private: - void drawPattern(float x1, float y1, float x2, float y2, float repetitions); + // Virtual for rendergraph::Node + void preprocess() override; - mixxx::PatternShader m_shader; + private: QColor m_color; float m_markerBreadth{}; float m_markerLength{}; - OpenGLTexture2D m_texture; - bool m_isSlipRenderer; + bool preprocessInner(); + DISALLOW_COPY_AND_ASSIGN(WaveformRendererPreroll); }; diff --git a/src/waveform/renderers/allshader/waveformrendererrgb.cpp b/src/waveform/renderers/allshader/waveformrendererrgb.cpp index 54cea5376406..6e36e1d78b47 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.cpp +++ b/src/waveform/renderers/allshader/waveformrendererrgb.cpp @@ -1,11 +1,15 @@ #include "waveform/renderers/allshader/waveformrendererrgb.h" +#include "rendergraph/material/rgbmaterial.h" +#include "rendergraph/vertexupdaters/rgbvertexupdater.h" #include "track/track.h" #include "util/math.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveform.h" +using namespace rendergraph; + namespace allshader { namespace { @@ -20,21 +24,25 @@ WaveformRendererRGB::WaveformRendererRGB(WaveformWidgetRenderer* waveformWidget, : WaveformRendererSignalBase(waveformWidget), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip), m_options(options) { + initForRectangles(0); + setUsePreprocess(true); } void WaveformRendererRGB::onSetup(const QDomNode& node) { Q_UNUSED(node); } -void WaveformRendererRGB::initializeGL() { - WaveformRendererSignalBase::initializeGL(); - m_shader.init(); +void WaveformRendererRGB::preprocess() { + if (!preprocessInner()) { + geometry().allocate(0); + } } -void WaveformRendererRGB::paintGL() { +bool WaveformRendererRGB::preprocessInner() { TrackPointer pTrack = m_waveformRenderer->getTrackInfo(); + if (!pTrack || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { - return; + return false; } auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip @@ -42,23 +50,23 @@ void WaveformRendererRGB::paintGL() { ConstWaveformPointer waveform = pTrack->getWaveform(); if (waveform.isNull()) { - return; + return false; } const int dataSize = waveform->getDataSize(); if (dataSize <= 1) { - return; + return false; } const WaveformData* data = waveform->data(); if (data == nullptr) { - return; + return false; } #ifdef __STEM__ auto stemInfo = pTrack->getStemInfo(); // If this track is a stem track, skip the rendering if (!stemInfo.isEmpty() && waveform->hasStem()) { - return; + return false; } #endif @@ -108,16 +116,14 @@ void WaveformRendererRGB::paintGL() { // Slip rendere only render a single channel, so the vertices count doesn't change ((splitLeftRight && !m_isSlipRenderer ? length * 2 : length) + 1); - m_vertices.clear(); - m_vertices.reserve(reserved); - m_colors.clear(); - m_colors.reserve(reserved); + geometry().allocate(reserved); + // TODO set dirty for scenegraph - m_vertices.addRectangle(0.f, + RGBVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + vertexUpdater.addRectangle(0.f, halfBreadth - 0.5f * devicePixelRatio, static_cast(length), - m_isSlipRenderer ? halfBreadth : halfBreadth + 0.5f * devicePixelRatio); - m_colors.addForRectangle( + m_isSlipRenderer ? halfBreadth : halfBreadth + 0.5f * devicePixelRatio, static_cast(m_axesColor_r), static_cast(m_axesColor_g), static_cast(m_axesColor_b)); @@ -215,51 +221,37 @@ void WaveformRendererRGB::paintGL() { // Lines are thin rectangles if (!splitLeftRight) { - m_vertices.addRectangle(fpos - 0.5f, + vertexUpdater.addRectangle(fpos - 0.5f, halfBreadth - heightFactorAbs * maxAllChn[0], fpos + 0.5f, m_isSlipRenderer ? halfBreadth - : halfBreadth + heightFactorAbs * maxAllChn[1]); + : halfBreadth + heightFactorAbs * maxAllChn[1], + red, + green, + blue); } else { // note: heightFactor is the same for left and right, // but negative for left (chn 0) and positive for right (chn 1) - m_vertices.addRectangle(fpos - 0.5f, + vertexUpdater.addRectangle(fpos - 0.5f, halfBreadth, fpos + 0.5f, - halfBreadth + heightFactor[chn] * maxAllChn[chn]); + halfBreadth + heightFactor[chn] * maxAllChn[chn], + red, + green, + blue); } - m_colors.addForRectangle(red, green, blue); } xVisualFrame += visualIncrementPerPixel; } - DEBUG_ASSERT(reserved == m_vertices.size()); - DEBUG_ASSERT(reserved == m_colors.size()); + DEBUG_ASSERT(reserved == vertexUpdater.index()); const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, true); + material().setUniform(0, matrix); - const int matrixLocation = m_shader.matrixLocation(); - const int positionLocation = m_shader.positionLocation(); - const int colorLocation = m_shader.colorLocation(); - - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); - m_shader.enableAttributeArray(colorLocation); - - m_shader.setUniformValue(matrixLocation, matrix); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, m_vertices.constData(), 2); - m_shader.setAttributeArray( - colorLocation, GL_FLOAT, m_colors.constData(), 3); - - glDrawArrays(GL_TRIANGLES, 0, m_vertices.size()); - - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(colorLocation); - m_shader.release(); + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererrgb.h b/src/waveform/renderers/allshader/waveformrendererrgb.h index e9c12b52a428..a829cf3af2fc 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.h +++ b/src/waveform/renderers/allshader/waveformrendererrgb.h @@ -1,39 +1,37 @@ #pragma once -#include "shaders/rgbshader.h" +#include "rendergraph/geometrynode.h" #include "util/class.h" -#include "waveform/renderers/allshader/rgbdata.h" -#include "waveform/renderers/allshader/vertexdata.h" #include "waveform/renderers/allshader/waveformrenderersignalbase.h" namespace allshader { class WaveformRendererRGB; } -class allshader::WaveformRendererRGB final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererRGB final + : public allshader::WaveformRendererSignalBase, + public rendergraph::GeometryNode { public: explicit WaveformRendererRGB(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play, WaveformRendererSignalBase::Options options = WaveformRendererSignalBase::Option::None); - // override ::WaveformRendererSignalBase + // Pure virtual from WaveformRendererSignalBase, not used void onSetup(const QDomNode& node) override; - void initializeGL() override; - void paintGL() override; - bool supportsSlip() const override { return true; } - private: - mixxx::RGBShader m_shader; - VertexData m_vertices; - RGBData m_colors; + // Virtuals for rendergraph::Node + void preprocess() override; + private: bool m_isSlipRenderer; WaveformRendererSignalBase::Options m_options; + bool preprocessInner(); + DISALLOW_COPY_AND_ASSIGN(WaveformRendererRGB); }; diff --git a/src/waveform/renderers/allshader/waveformrenderersignalbase.h b/src/waveform/renderers/allshader/waveformrenderersignalbase.h index 0e72d9364c77..0c4217723d8e 100644 --- a/src/waveform/renderers/allshader/waveformrenderersignalbase.h +++ b/src/waveform/renderers/allshader/waveformrenderersignalbase.h @@ -4,7 +4,6 @@ #include #include "util/class.h" -#include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/waveformrenderersignalbase.h" class WaveformWidgetRenderer; @@ -13,8 +12,7 @@ namespace allshader { class WaveformRendererSignalBase; } -class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBase, - public allshader::WaveformRendererAbstract { +class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBase { public: enum class Option { None = 0b0, @@ -37,9 +35,5 @@ class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBas Q_UNUSED(event); } - allshader::WaveformRendererAbstract* allshaderWaveformRenderer() override { - return this; - } - DISALLOW_COPY_AND_ASSIGN(WaveformRendererSignalBase); }; diff --git a/src/waveform/renderers/allshader/waveformrenderersimple.cpp b/src/waveform/renderers/allshader/waveformrenderersimple.cpp index 02e9453afdff..90a707d23735 100644 --- a/src/waveform/renderers/allshader/waveformrenderersimple.cpp +++ b/src/waveform/renderers/allshader/waveformrenderersimple.cpp @@ -18,7 +18,6 @@ void WaveformRendererSimple::onSetup(const QDomNode& node) { } void WaveformRendererSimple::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrenderersimple.h b/src/waveform/renderers/allshader/waveformrenderersimple.h index a1e92c8d2f04..ba79752539c3 100644 --- a/src/waveform/renderers/allshader/waveformrenderersimple.h +++ b/src/waveform/renderers/allshader/waveformrenderersimple.h @@ -1,5 +1,6 @@ #pragma once +#include "rendergraph/openglnode.h" #include "shaders/unicolorshader.h" #include "util/class.h" #include "waveform/renderers/allshader/vertexdata.h" @@ -9,7 +10,9 @@ namespace allshader { class WaveformRendererSimple; } -class allshader::WaveformRendererSimple final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererSimple final + : public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { public: explicit WaveformRendererSimple(WaveformWidgetRenderer* waveformWidget); diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp index 4311f00a4ceb..8ddf0be80e55 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp @@ -63,7 +63,6 @@ void WaveformRendererSlipMode::setup(const QDomNode& node, const SkinContext& co } void WaveformRendererSlipMode::initializeGL() { - WaveformRenderer::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.h b/src/waveform/renderers/allshader/waveformrendererslipmode.h index 5e7b189ca57a..c5a8ce9e92c7 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.h +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.h @@ -3,6 +3,7 @@ #include #include +#include "rendergraph/openglnode.h" #include "shaders/slipmodeshader.h" #include "util/class.h" #include "util/performancetimer.h" @@ -16,7 +17,9 @@ namespace allshader { class WaveformRendererSlipMode; } -class allshader::WaveformRendererSlipMode final : public allshader::WaveformRenderer { +class allshader::WaveformRendererSlipMode final + : public allshader::WaveformRenderer, + public rendergraph::OpenGLNode { public: explicit WaveformRendererSlipMode( WaveformWidgetRenderer* waveformWidget); diff --git a/src/waveform/renderers/allshader/waveformrendererstem.cpp b/src/waveform/renderers/allshader/waveformrendererstem.cpp index bacb18d32cae..c8dbe5a949cb 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.cpp +++ b/src/waveform/renderers/allshader/waveformrendererstem.cpp @@ -29,7 +29,6 @@ void WaveformRendererStem::onSetup(const QDomNode& node) { } void WaveformRendererStem::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); m_textureShader.init(); auto group = m_pEQEnabled->getKey().group; diff --git a/src/waveform/renderers/allshader/waveformrendererstem.h b/src/waveform/renderers/allshader/waveformrendererstem.h index 757bfe763d30..997281945748 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.h +++ b/src/waveform/renderers/allshader/waveformrendererstem.h @@ -2,6 +2,7 @@ #include +#include "rendergraph/openglnode.h" #include "shaders/rgbashader.h" #include "shaders/textureshader.h" #include "util/class.h" @@ -15,7 +16,9 @@ namespace allshader { class WaveformRendererStem; } -class allshader::WaveformRendererStem final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererStem final + : public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { public: explicit WaveformRendererStem(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.cpp b/src/waveform/renderers/allshader/waveformrenderertextured.cpp index dc137b73208f..de21a013259c 100644 --- a/src/waveform/renderers/allshader/waveformrenderertextured.cpp +++ b/src/waveform/renderers/allshader/waveformrenderertextured.cpp @@ -219,8 +219,6 @@ void WaveformRendererTextured::createFrameBuffers() { } void WaveformRendererTextured::initializeGL() { - WaveformRendererSignalBase::initializeGL(); - m_textureRenderedWaveformCompletion = 0; if (!m_frameShaderProgram) { diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.h b/src/waveform/renderers/allshader/waveformrenderertextured.h index 16680f1bc5de..dccf1d975b65 100644 --- a/src/waveform/renderers/allshader/waveformrenderertextured.h +++ b/src/waveform/renderers/allshader/waveformrenderertextured.h @@ -2,6 +2,7 @@ #ifndef QT_OPENGL_ES_2 +#include "rendergraph/openglnode.h" #include "shaders/rgbshader.h" #include "track/track_decl.h" #include "util/class.h" @@ -20,7 +21,8 @@ class WaveformRendererTextured; // Based on GLSLWaveformRendererSignal (waveform/renderers/glslwaveformrenderersignal.h) class allshader::WaveformRendererTextured : public QObject, - public allshader::WaveformRendererSignalBase { + public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { Q_OBJECT public: explicit WaveformRendererTextured(WaveformWidgetRenderer* waveformWidget, diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 1a66df4deb4d..e0947543379d 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -1,16 +1,24 @@ #include "waveform/renderers/allshader/waveformrendermark.h" -#include #include +#include "rendergraph/context.h" +#include "rendergraph/geometry.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/material/rgbamaterial.h" +#include "rendergraph/material/texturematerial.h" +#include "rendergraph/texture.h" +#include "rendergraph/vertexupdaters/rgbavertexupdater.h" +#include "rendergraph/vertexupdaters/texturedvertexupdater.h" #include "track/track.h" #include "util/colorcomponents.h" +#include "waveform/renderers/allshader/digitsrenderer.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/rgbadata.h" -#include "waveform/renderers/allshader/vertexdata.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveformwidgetfactory.h" +using namespace rendergraph; + // On the use of QPainter: // // The renderers in this folder are optimized to use GLSL shaders and refrain @@ -20,17 +28,87 @@ // only to draw on a QImage. This is only done once when needed and the images are // then used as textures to be drawn with a GLSL shader. -class TextureGraphics : public WaveformMark::Graphics { +class WaveformMarkNode : public rendergraph::GeometryNode { + public: + WaveformMark* m_pOwner{}; + + WaveformMarkNode(WaveformMark* pOwner, const QImage& image) + : m_pOwner(pOwner) { + initForRectangles(1); + updateTexture(image); + } + void updateTexture(const QImage& image) { + Context context; + dynamic_cast(material()) + .setTexture(std::make_unique(context, image)); + m_textureWidth = image.width(); + m_textureHeight = image.height(); + } + void update(const QMatrix4x4& matrix, float x, float y, float devicePixelRatio) { + material().setUniform(0, matrix); + + TexturedVertexUpdater vertexUpdater{ + geometry().vertexDataAs()}; + vertexUpdater.addRectangle({x, y}, + {x + m_textureWidth / devicePixelRatio, + y + m_textureHeight / devicePixelRatio}, + {0.f, 0.f}, + {1.f, 1.f}); + } + float textureWidth() const { + return m_textureWidth; + } + float textureHeight() const { + return m_textureHeight; + } + public: - TextureGraphics(const QImage& image) { - m_texture.setData(image); + float m_textureWidth{}; + float m_textureHeight{}; +}; + +class WaveformMarkNodeGraphics : public WaveformMark::Graphics { + public: + WaveformMarkNodeGraphics(WaveformMark* pOwner, const QImage& image) + : m_pNode(std::make_unique(pOwner, image)) { + } + void updateTexture(const QImage& image) { + if (!m_pNode) { + return; + } + waveformMarkNode()->updateTexture(image); + } + void update(const QMatrix4x4& matrix, float x, float y, float devicePixelRatio) { + if (!m_pNode) { + return; + } + waveformMarkNode()->update(matrix, x, y, devicePixelRatio); + } + float textureWidth() const { + if (!m_pNode) { + return 0.0; + } + return waveformMarkNode()->textureWidth(); } - QOpenGLTexture* texture() { - return &m_texture; + float textureHeight() const { + if (!m_pNode) { + return 0.0; + } + return waveformMarkNode()->textureHeight(); + } + void setNode(std::unique_ptr&& pNode) { + m_pNode = std::move(pNode); + } + void moveNodeToChildrenOf(TreeNode* pParent) { + pParent->appendChildNode(std::move(m_pNode)); } private: - OpenGLTexture2D m_texture; + WaveformMarkNode* waveformMarkNode() const { + return static_cast(m_pNode.get()); + } + + std::unique_ptr m_pNode; }; // Both allshader::WaveformRenderMark and the non-GL ::WaveformRenderMark derive @@ -66,6 +144,24 @@ allshader::WaveformRenderMark::WaveformRenderMark( m_timeUntilMark(0.0), m_pTimeRemainingControl(nullptr), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { + appendChildNode(std::make_unique()); + m_pRangeNodesParent = lastChild(); + + appendChildNode(std::make_unique()); + m_pMarkNodesParent = lastChild(); + + appendChildNode(std::make_unique()); + m_pDigitsRenderNode = static_cast(lastChild()); + + appendChildNode(std::make_unique()); + m_pPlayPosNode = static_cast(lastChild()); + m_pPlayPosNode->initForRectangles(1); +} + +void allshader::WaveformRenderMark::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } bool allshader::WaveformRenderMark::init() { @@ -74,69 +170,22 @@ bool allshader::WaveformRenderMark::init() { return true; } -void allshader::WaveformRenderMark::initializeGL() { - allshader::WaveformRendererAbstract::initializeGL(); - m_digitsRenderer.init(); - m_rgbaShader.init(); - m_textureShader.init(); - +void allshader::WaveformRenderMark::initialize() { // Will create textures so requires OpenGL context updateMarkImages(); updatePlayPosMarkTexture(); const auto untilMarkTextPointSize = WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); - m_digitsRenderer.updateTexture(untilMarkTextPointSize, + m_pDigitsRenderNode->updateTexture(untilMarkTextPointSize, getMaxHeightForText(), m_waveformRenderer->getDevicePixelRatio()); + Node::initialize(); } -void allshader::WaveformRenderMark::drawTexture( - const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture) { - const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - const float texx1 = 0.f; - const float texy1 = 0.f; - const float texx2 = 1.f; - const float texy2 = 1.f; - - const float posx1 = x; - const float posx2 = x + static_cast(texture->width() / devicePixelRatio); - const float posy1 = y; - const float posy2 = y + static_cast(texture->height() / devicePixelRatio); - - const float posarray[] = {posx1, posy1, posx2, posy1, posx1, posy2, posx2, posy2}; - const float texarray[] = {texx1, texy1, texx2, texy1, texx1, texy2, texx2, texy2}; - - m_textureShader.bind(); - - const int matrixLocation = m_textureShader.uniformLocation("matrix"); - const int textureLocation = m_textureShader.uniformLocation("texture"); - const int positionLocation = m_textureShader.attributeLocation("position"); - const int texcoordLocation = m_textureShader.attributeLocation("texcoord"); - - m_textureShader.setUniformValue(matrixLocation, matrix); - - m_textureShader.enableAttributeArray(positionLocation); - m_textureShader.setAttributeArray( - positionLocation, GL_FLOAT, posarray, 2); - m_textureShader.enableAttributeArray(texcoordLocation); - m_textureShader.setAttributeArray( - texcoordLocation, GL_FLOAT, texarray, 2); - - m_textureShader.setUniformValue(textureLocation, 0); - - texture->bind(); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - texture->release(); - - m_textureShader.disableAttributeArray(positionLocation); - m_textureShader.disableAttributeArray(texcoordLocation); - m_textureShader.release(); -} - -void allshader::WaveformRenderMark::drawMark( - const QMatrix4x4& matrix, const QRectF& rect, QColor color) { +void allshader::WaveformRenderMark::updateRangeNode(GeometryNode* pNode, + const QMatrix4x4& matrix, + const QRectF& rect, + QColor color) { // draw a gradient towards transparency at the upper and lower 25% of the waveform view const float qh = static_cast(std::floor(rect.height() * 0.25)); @@ -151,43 +200,44 @@ void allshader::WaveformRenderMark::drawMark( getRgbF(color, &r, &g, &b, &a); - VertexData vertices; - vertices.reserve(12); // 4 triangles - vertices.addRectangle(posx1, posy1, posx2, posy2); - vertices.addRectangle(posx1, posy4, posx2, posy3); - - RGBAData rgbaData; - rgbaData.reserve(12); // 4 triangles - rgbaData.addForRectangleGradient(r, g, b, a, r, g, b, 0.f); - rgbaData.addForRectangleGradient(r, g, b, a, r, g, b, 0.f); - - m_rgbaShader.bind(); + RGBAVertexUpdater vertexUpdater{pNode->geometry().vertexDataAs()}; + vertexUpdater.addRectangleVGradient( + {posx1, posy1}, {posx2, posy2}, {r, g, b, a}, {r, g, b, 0.f}); + vertexUpdater.addRectangleVGradient( + {posx1, posy4}, {posx2, posy3}, {r, g, b, a}, {r, g, b, 0.f}); - const int matrixLocation = m_rgbaShader.matrixLocation(); - const int positionLocation = m_rgbaShader.positionLocation(); - const int colorLocation = m_rgbaShader.colorLocation(); - - m_rgbaShader.setUniformValue(matrixLocation, matrix); - - m_rgbaShader.enableAttributeArray(positionLocation); - m_rgbaShader.setAttributeArray( - positionLocation, GL_FLOAT, vertices.constData(), 2); - m_rgbaShader.enableAttributeArray(colorLocation); - m_rgbaShader.setAttributeArray( - colorLocation, GL_FLOAT, rgbaData.constData(), 4); - - glDrawArrays(GL_TRIANGLES, 0, vertices.size()); + pNode->material().setUniform(0, matrix); +} - m_rgbaShader.disableAttributeArray(positionLocation); - m_rgbaShader.disableAttributeArray(colorLocation); - m_rgbaShader.release(); +bool allshader::WaveformRenderMark::isSubtreeBlocked() const { + return m_isSlipRenderer && !m_waveformRenderer->isSlipActive(); } -void allshader::WaveformRenderMark::paintGL() { - if (m_isSlipRenderer && !m_waveformRenderer->isSlipActive()) { +void allshader::WaveformRenderMark::update() { + if (isSubtreeBlocked()) { return; } + // For each WaveformMark we create a GeometryNode with Texture + // (in updateMarkImage). Of these GeometryNodes, we append the + // the ones that need to be shown on screen as children to + // m_pMarkNodesParent (transferring ownership). + // + // At the beginning of a new frame, we remove all the child nodes + // from m_pMarkNodesParent and store each with their mark + // (transferring ownership). Later in this function we move the + // visible nodes back to m_pMarkNodesParent children. + while (auto pChild = m_pMarkNodesParent->firstChild()) { + // Pop child from front of m_pMarkNodesParent + auto pRemoved = m_pMarkNodesParent->removeChildNode(pChild); + // Determine its WaveformMark + auto pMarkNode = static_cast(pRemoved.get()); + auto pMark = pMarkNode->m_pOwner; + auto pGraphics = static_cast(pMark->m_pGraphics.get()); + // Store the node with the WaveformMark + pGraphics->setNode(std::move(pRemoved)); + } + auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip : ::WaveformRendererAbstract::Play; bool slipActive = m_waveformRenderer->isSlipActive(); @@ -195,14 +245,14 @@ void allshader::WaveformRenderMark::paintGL() { const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); QList marksOnScreen; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - for (const auto& pMark : std::as_const(m_marks)) { pMark->setBreadth(slipActive ? m_waveformRenderer->getBreadth() / 2 : m_waveformRenderer->getBreadth()); } - // Will create textures so requires OpenGL context + + // Generate initial node or update its texture if needed for each of + // the WaveformMarks (in which case updateMarkImage is called) + // (Will create textures so requires OpenGL context) updateMarkImages(); QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); @@ -210,6 +260,8 @@ void allshader::WaveformRenderMark::paintGL() { const double playPosition = m_waveformRenderer->getTruePosSample(positionType); double nextMarkPosition = std::numeric_limits::max(); + TreeNode* pRangeChild = m_pRangeNodesParent->firstChild(); + for (const auto& pMark : std::as_const(m_marks)) { if (!pMark->isValid()) { continue; @@ -221,13 +273,10 @@ void allshader::WaveformRenderMark::paintGL() { continue; } - QOpenGLTexture* pTexture = - static_cast(pMark->m_pGraphics.get()) - ->texture(); - - if (!pTexture) { + auto pMarkGraphics = pMark->m_pGraphics.get(); + auto pMarkNodeGraphics = static_cast(pMarkGraphics); + if (!pMarkGraphics) // is this even possible? continue; - } const float currentMarkPoint = std::round( @@ -247,7 +296,7 @@ void allshader::WaveformRenderMark::paintGL() { // Pixmaps are expected to have the mark stroke at the center, // and preferably have an odd width in order to have the stroke // exactly at the sample position. - const float markHalfWidth = pTexture->width() / devicePixelRatio / 2.f; + const float markHalfWidth = pMarkNodeGraphics->textureWidth() / devicePixelRatio / 2.f; const float drawOffset = currentMarkPoint - markHalfWidth; bool visible = false; @@ -255,12 +304,16 @@ void allshader::WaveformRenderMark::paintGL() { if (drawOffset > -markHalfWidth && drawOffset < m_waveformRenderer->getLength() + markHalfWidth) { - drawTexture(matrix, + pMarkNodeGraphics->update(matrix, drawOffset, !m_isSlipRenderer && slipActive ? m_waveformRenderer->getBreadth() / 2 : 0, - pTexture); + devicePixelRatio); + + // transfer back to m_pMarkNodesParent children, for rendering + pMarkNodeGraphics->moveNodeToChildrenOf(m_pMarkNodesParent); + visible = true; } @@ -277,13 +330,22 @@ void allshader::WaveformRenderMark::paintGL() { QColor color = pMark->fillColor(); color.setAlphaF(0.4f); - drawMark(matrix, + // Reuse, or create new when needed + if (!pRangeChild) { + m_pRangeNodesParent->appendChildNode(std::make_unique()); + pRangeChild = m_pRangeNodesParent->lastChild(); + static_cast(pRangeChild)->initForRectangles(2); + } + + updateRangeNode(static_cast(pRangeChild), + matrix, QRectF(QPointF(currentMarkPoint, 0), QPointF(currentMarkEndPoint, - m_waveformRenderer - ->getBreadth())), + m_waveformRenderer->getBreadth())), color); + visible = true; + pRangeChild = pRangeChild->nextSibling(); } } @@ -293,6 +355,14 @@ void allshader::WaveformRenderMark::paintGL() { pMark, static_cast(drawOffset)}); } } + + // Remove unused nodes + while (pRangeChild) { + auto pNext = static_cast(pRangeChild->nextSibling()); + m_pRangeNodesParent->removeChildNode(pRangeChild); + pRangeChild = pNext; + } + m_waveformRenderer->setMarkPositions(marksOnScreen); const float currentMarkPoint = @@ -302,11 +372,19 @@ void allshader::WaveformRenderMark::paintGL() { devicePixelRatio) / devicePixelRatio; - if (m_playPosMarkTexture.isStorageAllocated()) { - const float markHalfWidth = m_playPosMarkTexture.width() / devicePixelRatio / 2.f; + { + const float markHalfWidth = 11.f / 2.f; const float drawOffset = currentMarkPoint - markHalfWidth; - drawTexture(matrix, drawOffset, 0.f, &m_playPosMarkTexture); + m_pPlayPosNode->material().setUniform(0, matrix); + + TexturedVertexUpdater vertexUpdater{ + m_pPlayPosNode->geometry() + .vertexDataAs()}; + vertexUpdater.addRectangle({drawOffset, 0.f}, + {drawOffset + 11.f, static_cast(m_waveformRenderer->getBreadth())}, + {0.f, 0.f}, + {1.f, 1.f}); } if (WaveformWidgetFactory::instance()->getUntilMarkShowBeats() || @@ -323,14 +401,15 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa const auto untilMarkTextPointSize = WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); - m_digitsRenderer.updateTexture(untilMarkTextPointSize, + m_pDigitsRenderNode->updateTexture(untilMarkTextPointSize, getMaxHeightForText(), m_waveformRenderer->getDevicePixelRatio()); if (m_timeUntilMark == 0.0) { + m_pDigitsRenderNode->clear(); return; } - const float ch = m_digitsRenderer.height(); + const float ch = m_pDigitsRenderNode->height(); float y = untilMarkAlign == Qt::AlignTop ? 0.f : untilMarkAlign == Qt::AlignBottom @@ -350,24 +429,12 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa } } - if (untilMarkShowBeats) { - const auto w = m_digitsRenderer.draw(matrix, - x, - y, - QString::number(m_beatsUntilMark)); - if (multiLine) { - y += ch; - } else { - x += w + ch * 0.75f; - } - } - - if (untilMarkShowTime) { - m_digitsRenderer.draw(matrix, - x, - y, - timeSecToString(m_timeUntilMark)); - } + m_pDigitsRenderNode->update(matrix, + x, + y, + multiLine, + untilMarkShowBeats ? QString::number(m_beatsUntilMark) : QString{}, + untilMarkShowTime ? timeSecToString(m_timeUntilMark) : QString{}); } // Generate the texture used to draw the play position marker. @@ -434,7 +501,9 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { } painter.end(); - m_playPosMarkTexture.setData(image); + Context context; + dynamic_cast(m_pPlayPosNode->material()) + .setTexture(std::make_unique(context, image)); } void allshader::WaveformRenderMark::drawTriangle(QPainter* painter, @@ -451,15 +520,22 @@ void allshader::WaveformRenderMark::drawTriangle(QPainter* painter, painter->fillPath(triangle, fillColor); } -void allshader::WaveformRenderMark::resizeGL(int, int) { +void allshader::WaveformRenderMark::resize(int, int) { // Will create textures so requires OpenGL context updateMarkImages(); updatePlayPosMarkTexture(); } void allshader::WaveformRenderMark::updateMarkImage(WaveformMarkPointer pMark) { - pMark->m_pGraphics = std::make_unique( - pMark->generateImage(m_waveformRenderer->getDevicePixelRatio())); + if (!pMark->m_pGraphics) { + pMark->m_pGraphics = + std::make_unique(pMark.get(), + pMark->generateImage( + m_waveformRenderer->getDevicePixelRatio())); + } else { + auto pGraphics = static_cast(pMark->m_pGraphics.get()); + pGraphics->updateTexture(pMark->generateImage(m_waveformRenderer->getDevicePixelRatio())); + } } void allshader::WaveformRenderMark::updateUntilMark( diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index 317f132e26d6..2eedbadc604b 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -2,42 +2,39 @@ #include -#include "shaders/rgbashader.h" -#include "shaders/textureshader.h" -#include "util/opengltexture2d.h" -#include "waveform/renderers/allshader/digitsrenderer.h" -#include "waveform/renderers/allshader/waveformrendererabstract.h" +#include "rendergraph/node.h" #include "waveform/renderers/waveformrendermarkbase.h" class QDomNode; class SkinContext; -class QOpenGLTexture; + +namespace rendergraph { +class GeometryNode; +} namespace allshader { +class DigitsRenderNode; class WaveformRenderMark; } class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, - public allshader::WaveformRendererAbstract { + public rendergraph::Node { public: explicit WaveformRenderMark(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); - void draw(QPainter* painter, QPaintEvent* event) override { - Q_UNUSED(painter); - Q_UNUSED(event); - } - - allshader::WaveformRendererAbstract* allshaderWaveformRenderer() override { - return this; - } + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; bool init() override; - void initializeGL() override; - void paintGL() override; - void resizeGL(int w, int h) override; + void update(); + + // Virtual for rendergraph::Node + void initialize() override; + void resize(int, int) override; + bool isSubtreeBlocked() const override; private: void updateMarkImage(WaveformMarkPointer pMark) override; @@ -51,15 +48,14 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, QPointF p3); void drawMark(const QMatrix4x4& matrix, const QRectF& rect, QColor color); - void drawTexture(const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture); void updateUntilMark(double playPosition, double markerPosition); void drawUntilMark(const QMatrix4x4& matrix, float x); float getMaxHeightForText() const; + void updateRangeNode(rendergraph::GeometryNode* pNode, + const QMatrix4x4& matrix, + const QRectF& rect, + QColor color); - mixxx::RGBAShader m_rgbaShader; - mixxx::TextureShader m_textureShader; - OpenGLTexture2D m_playPosMarkTexture; - DigitsRenderer m_digitsRenderer; int m_beatsUntilMark; double m_timeUntilMark; double m_currentBeatPosition; @@ -68,5 +64,10 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, bool m_isSlipRenderer; + rendergraph::TreeNode* m_pRangeNodesParent{}; + rendergraph::TreeNode* m_pMarkNodesParent{}; + rendergraph::GeometryNode* m_pPlayPosNode; + DigitsRenderNode* m_pDigitsRenderNode{}; + DISALLOW_COPY_AND_ASSIGN(WaveformRenderMark); }; diff --git a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp index 6b3a33d49360..d7a77bfed1a3 100644 --- a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp +++ b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp @@ -1,41 +1,23 @@ #include "waveform/renderers/allshader/waveformrendermarkrange.h" +#include "rendergraph/geometry.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/material/unicolormaterial.h" +#include "rendergraph/vertexupdaters/vertexupdater.h" #include "skin/legacy/skincontext.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" -allshader::WaveformRenderMarkRange::WaveformRenderMarkRange(WaveformWidgetRenderer* waveformWidget) - : WaveformRenderer(waveformWidget) { -} - -void allshader::WaveformRenderMarkRange::initializeGL() { - WaveformRenderer::initializeGL(); - m_shader.init(); -} - -void allshader::WaveformRenderMarkRange::fillRect( - const QRectF& rect, QColor color) { - const float posx1 = static_cast(rect.x()); - const float posx2 = static_cast(rect.x() + rect.width()); - const float posy1 = static_cast(rect.y()); - const float posy2 = static_cast(rect.y() + rect.height()); - - const float posarray[] = {posx1, posy1, posx2, posy1, posx1, posy2, posx2, posy2}; +using namespace rendergraph; - const int colorLocation = m_shader.colorLocation(); - const int positionLocation = m_shader.positionLocation(); +namespace allshader { - m_shader.setUniformValue(colorLocation, color); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, posarray, 2); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +WaveformRenderMarkRange::WaveformRenderMarkRange(WaveformWidgetRenderer* waveformWidget) + : ::WaveformRendererAbstract(waveformWidget) { } -void allshader::WaveformRenderMarkRange::setup(const QDomNode& node, const SkinContext& context) { +void WaveformRenderMarkRange::setup(const QDomNode& node, const SkinContext& context) { m_markRanges.clear(); - m_markRanges.reserve(1); QDomNode child = node.firstChild(); while (!child.isNull()) { @@ -51,21 +33,18 @@ void allshader::WaveformRenderMarkRange::setup(const QDomNode& node, const SkinC } } -void allshader::WaveformRenderMarkRange::paintGL() { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +void WaveformRenderMarkRange::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); +} +void WaveformRenderMarkRange::update() { const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); - const int positionLocation = m_shader.positionLocation(); - const int matrixLocation = m_shader.matrixLocation(); - - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); + TreeNode* pChild = firstChild(); - m_shader.setUniformValue(matrixLocation, matrix); - - for (auto&& markRange : m_markRanges) { + for (const auto& markRange : m_markRanges) { // If the mark range is not active we should not draw it. if (!markRange.active()) { continue; @@ -89,8 +68,6 @@ void allshader::WaveformRenderMarkRange::paintGL() { startPosition = std::floor(startPosition); endPosition = std::floor(endPosition); - const double span = std::max(endPosition - startPosition, 1.0); - // range not in the current display if (startPosition > m_waveformRenderer->getLength() || endPosition < 0) { continue; @@ -99,8 +76,37 @@ void allshader::WaveformRenderMarkRange::paintGL() { QColor color = markRange.enabled() ? markRange.m_activeColor : markRange.m_disabledColor; color.setAlphaF(0.3f); - fillRect(QRectF(startPosition, 0, span, m_waveformRenderer->getBreadth()), color); + if (!pChild) { + appendChildNode(std::make_unique()); + pChild = lastChild(); + static_cast(pChild)->initForRectangles(1); + } + + updateNode(static_cast(pChild), + matrix, + color, + {static_cast(startPosition), 0.f}, + {static_cast(endPosition) + 1.f, + static_cast(m_waveformRenderer->getBreadth())}); + + pChild = static_cast(pChild->nextSibling()); } - m_shader.disableAttributeArray(positionLocation); - m_shader.release(); + while (pChild) { + auto pNext = pChild->nextSibling(); + removeChildNode(pChild); + pChild = pNext; + } +} + +void WaveformRenderMarkRange::updateNode(GeometryNode* pChild, + const QMatrix4x4& matrix, + QColor color, + QVector2D lt, + QVector2D rb) { + VertexUpdater vertexUpdater{pChild->geometry().vertexDataAs()}; + vertexUpdater.addRectangle(lt, rb); + pChild->material().setUniform(0, matrix); + pChild->material().setUniform(1, color); } + +} // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendermarkrange.h b/src/waveform/renderers/allshader/waveformrendermarkrange.h index 0c46a8dd5b81..416f9fce03db 100644 --- a/src/waveform/renderers/allshader/waveformrendermarkrange.h +++ b/src/waveform/renderers/allshader/waveformrendermarkrange.h @@ -1,33 +1,45 @@ #pragma once #include -#include +#include +#include +#include -#include "shaders/unicolorshader.h" +#include "rendergraph/node.h" #include "util/class.h" -#include "waveform/renderers/allshader/waveformrenderer.h" #include "waveform/renderers/waveformmarkrange.h" +#include "waveform/renderers/waveformrendererabstract.h" class QDomNode; class SkinContext; +namespace rendergraph { +class GeometryNode; +} + namespace allshader { class WaveformRenderMarkRange; } -class allshader::WaveformRenderMarkRange final : public allshader::WaveformRenderer { +class allshader::WaveformRenderMarkRange final : public ::WaveformRendererAbstract, + public rendergraph::Node { public: explicit WaveformRenderMarkRange(WaveformWidgetRenderer* waveformWidget); + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& context) override; - void initializeGL() override; - void paintGL() override; + void update(); private: - void fillRect(const QRectF& rect, QColor color); + void updateNode(rendergraph::GeometryNode* pChild, + const QMatrix4x4& matrix, + QColor color, + QVector2D lt, + QVector2D rb); - mixxx::UnicolorShader m_shader; std::vector m_markRanges; DISALLOW_COPY_AND_ASSIGN(WaveformRenderMarkRange); diff --git a/src/waveform/renderers/waveformrendererabstract.h b/src/waveform/renderers/waveformrendererabstract.h index 6c15dd523d5d..9d4f84a629e1 100644 --- a/src/waveform/renderers/waveformrendererabstract.h +++ b/src/waveform/renderers/waveformrendererabstract.h @@ -6,13 +6,13 @@ QT_FORWARD_DECLARE_CLASS(QDomNode) QT_FORWARD_DECLARE_CLASS(QPaintEvent) QT_FORWARD_DECLARE_CLASS(QPainter) +namespace rendergraph { +class Node; +} + class SkinContext; class WaveformWidgetRenderer; -namespace allshader { -class WaveformRendererAbstract; -} - class WaveformRendererAbstract { public: /// The type of cursor for which the waveform is rendered @@ -32,9 +32,6 @@ class WaveformRendererAbstract { virtual void onResize() {} virtual void onSetTrack() {} - virtual allshader::WaveformRendererAbstract* allshaderWaveformRenderer() { - return nullptr; - } protected: bool isDirty() const { diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index 2b507ff7afc4..37ffba98cdc1 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -3,10 +3,10 @@ #include #include +#include #include "waveform/renderers/allshader/waveformrenderbackground.h" #include "waveform/renderers/allshader/waveformrenderbeat.h" -#include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/allshader/waveformrendererendoftrack.h" #include "waveform/renderers/allshader/waveformrendererfiltered.h" #include "waveform/renderers/allshader/waveformrendererhsv.h" @@ -20,6 +20,16 @@ #include "waveform/renderers/allshader/waveformrendermarkrange.h" #include "waveform/widgets/allshader/moc_waveformwidget.cpp" +namespace { +void appendChildTo(std::unique_ptr& pNode, rendergraph::TreeNode* pChild) { + pNode->appendChildNode(std::unique_ptr(pChild)); +} +void appendChildTo(std::unique_ptr& pNode, + rendergraph::TreeNode* pChild) { + pNode->appendChildNode(std::unique_ptr(pChild)); +} +} // namespace + namespace allshader { WaveformWidget::WaveformWidget(QWidget* parent, @@ -27,47 +37,66 @@ WaveformWidget::WaveformWidget(QWidget* parent, const QString& group, WaveformRendererSignalBase::Options options) : WGLWidget(parent), WaveformWidgetAbstract(group) { - addRenderer(); - addRenderer(); - addRenderer(); - addRenderer(); + auto pTopNode = std::make_unique(); + auto pOpacityNode = std::make_unique(); + + appendChildTo(pTopNode, addRenderer()); + appendChildTo(pOpacityNode, addRenderer()); + appendChildTo(pOpacityNode, addRenderer()); + m_pWaveformRenderMarkRange = addRenderer(); + appendChildTo(pOpacityNode, m_pWaveformRenderMarkRange); #ifdef __STEM__ // The following two renderers work in tandem: if the rendered waveform is // for a stem track, WaveformRendererSignalBase will skip rendering and let // WaveformRendererStem do the rendering, and vice-versa. - addRenderer(); + appendChildTo(pOpacityNode, addRenderer()); #endif allshader::WaveformRendererSignalBase* waveformSignalRenderer = addWaveformSignalRenderer( type, options, ::WaveformRendererAbstract::Play); + appendChildTo(pOpacityNode, dynamic_cast(waveformSignalRenderer)); - addRenderer(); - addRenderer(); + appendChildTo(pOpacityNode, addRenderer()); + m_pWaveformRenderMark = addRenderer(); + appendChildTo(pOpacityNode, m_pWaveformRenderMark); // if the signal renderer supports slip, we add it again, now for slip, together with the // other slip renderers if (waveformSignalRenderer && waveformSignalRenderer->supportsSlip()) { // The following renderer will add an overlay waveform if a slip is in progress - addRenderer(); - addRenderer(::WaveformRendererAbstract::Slip); + appendChildTo(pOpacityNode, addRenderer()); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); #ifdef __STEM__ - addRenderer(::WaveformRendererAbstract::Slip); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); #endif - addWaveformSignalRenderer(type, options, ::WaveformRendererAbstract::Slip); - addRenderer(::WaveformRendererAbstract::Slip); - addRenderer(::WaveformRendererAbstract::Slip); + appendChildTo(pOpacityNode, + dynamic_cast(addWaveformSignalRenderer( + type, options, ::WaveformRendererAbstract::Slip))); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); } m_initSuccess = init(); + + m_pOpacityNode = pOpacityNode.get(); + pTopNode->appendChildNode(std::move(pOpacityNode)); + + m_pEngine = std::make_unique(std::move(pTopNode)); } WaveformWidget::~WaveformWidget() { makeCurrentIfNeeded(); - for (auto* pRenderer : std::as_const(m_rendererStack)) { - delete pRenderer; - } m_rendererStack.clear(); + m_pEngine.reset(); doneCurrent(); } @@ -117,15 +146,14 @@ mixxx::Duration WaveformWidget::render() { } void WaveformWidget::paintGL() { - if (shouldOnlyDrawBackground()) { - if (!m_rendererStack.empty()) { - m_rendererStack[0]->allshaderWaveformRenderer()->paintGL(); - } - } else { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->paintGL(); - } - } + // opacity of 0.f effectively skips the subtree rendering + m_pOpacityNode->setOpacity(shouldOnlyDrawBackground() ? 0.f : 1.f); + + m_pWaveformRenderMark->update(); + m_pWaveformRenderMarkRange->update(); + + m_pEngine->preprocess(); + m_pEngine->render(); } void WaveformWidget::castToQWidget() { @@ -133,15 +161,11 @@ void WaveformWidget::castToQWidget() { } void WaveformWidget::initializeGL() { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->initializeGL(); - } + m_pEngine->initialize(); } void WaveformWidget::resizeGL(int w, int h) { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->resizeGL(w, h); - } + m_pEngine->resize(w, h); } void WaveformWidget::paintEvent(QPaintEvent* event) { diff --git a/src/waveform/widgets/allshader/waveformwidget.h b/src/waveform/widgets/allshader/waveformwidget.h index f6c0877975e0..9bd57c608c8f 100644 --- a/src/waveform/widgets/allshader/waveformwidget.h +++ b/src/waveform/widgets/allshader/waveformwidget.h @@ -1,5 +1,7 @@ #pragma once +#include "rendergraph/engine.h" +#include "rendergraph/opacitynode.h" #include "waveform/renderers/allshader/waveformrenderersignalbase.h" #include "waveform/widgets/waveformwidgetabstract.h" #include "waveform/widgets/waveformwidgetvars.h" @@ -7,6 +9,8 @@ namespace allshader { class WaveformWidget; +class WaveformRenderMark; +class WaveformRenderMarkRange; } class allshader::WaveformWidget final : public ::WGLWidget, @@ -48,6 +52,10 @@ class allshader::WaveformWidget final : public ::WGLWidget, ::WaveformRendererAbstract::PositionSource positionSource); WaveformWidgetType::Type m_type; + std::unique_ptr m_pEngine; + rendergraph::OpacityNode* m_pOpacityNode; + WaveformRenderMark* m_pWaveformRenderMark; + WaveformRenderMarkRange* m_pWaveformRenderMarkRange; DISALLOW_COPY_AND_ASSIGN(WaveformWidget); };