diff --git a/CMakeLists.txt b/CMakeLists.txt index 437d3c0a154..055f374b4c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1514,7 +1514,6 @@ if(QOPENGL) src/shaders/vinylqualityshader.cpp src/util/opengltexture2d.cpp src/waveform/renderers/allshader/digitsrenderer.cpp - src/waveform/renderers/allshader/matrixforwidgetgeometry.cpp src/waveform/renderers/allshader/waveformrenderbackground.cpp src/waveform/renderers/allshader/waveformrenderbeat.cpp src/waveform/renderers/allshader/waveformrenderer.cpp @@ -3810,6 +3809,12 @@ if(VINYLCONTROL) target_link_libraries(mixxx-lib PRIVATE mixxx-xwax) endif() +# rendergraph +add_subdirectory(src/rendergraph/opengl) +add_subdirectory(res/shaders/rendergraph) +target_link_libraries(mixxx-lib PUBLIC rendergraph_gl) +target_compile_definitions(mixxx-lib PRIVATE rendergraph=rendergraph_gl) + # WavPack audio file support find_package(wavpack) default_option(WAVPACK "WavPack audio file support" "wavpack_FOUND") diff --git a/res/mixxx.qrc b/res/mixxx.qrc index 8011fe39f94..c21ec6a8008 100644 --- a/res/mixxx.qrc +++ b/res/mixxx.qrc @@ -96,5 +96,9 @@ shaders/passthrough.vert shaders/rgbsignal.frag shaders/stackedsignal.frag + shaders/rendergraph/endoftrack.frag.gl + shaders/rendergraph/endoftrack.vert.gl + shaders/rendergraph/texture.frag.gl + shaders/rendergraph/texture.vert.gl diff --git a/res/shaders/rendergraph/CMakeLists.txt b/res/shaders/rendergraph/CMakeLists.txt new file mode 100644 index 00000000000..dfcecc593a4 --- /dev/null +++ b/res/shaders/rendergraph/CMakeLists.txt @@ -0,0 +1,47 @@ +set(shaders + endoftrack.frag + endoftrack.vert + pattern.frag + pattern.vert + rgb.frag + rgb.vert + rgba.frag + rgba.vert + texture.frag + texture.vert + unicolor.frag + unicolor.vert +) + +qt6_add_shaders(rendergraph_sg "shaders-qsb" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + /shaders/rendergraph + FILES + ${shaders} +) + +if(USE_QSHADER_FOR_GL) + message(STATUS "Adding qsb shaders to rendergraph_gl") + qt6_add_shaders(rendergraph_gl "shaders-qsb" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + /shaders/rendergraph + FILES + ${shaders} + ) +else() + message(STATUS "Adding gl shaders to rendergraph_gl") + include(generated_shaders_gl.cmake) + + qt_add_resources(rendergraph_gl "shaders-gl" + PREFIX + /shaders/rendergraph + FILES + ${generated_shaders_gl} + ) +endif() diff --git a/res/shaders/rendergraph/README b/res/shaders/rendergraph/README new file mode 100644 index 00000000000..644c2ce7647 --- /dev/null +++ b/res/shaders/rendergraph/README @@ -0,0 +1,10 @@ +The CMakeLists.txt in this folder generates qsb shader bundles from the spirv shaders. + +The qsb files can be used by QML / Qt scene graph QSGShader. Since Qt >= 6.6 we can load the qsb files and extract the glsl shaders +programmatically with QShader and use the with QOpenGLShader. For Qt < 6.6 the files have to generated manually with the script: + +generate_shaders_gl.pl + +(Make sure qsb and spirv commands are in your path. E.g: +export PATH=$PATH:~/VulkanSDK/1.3.283.0/macOS/bin:~/Qt/6.7.2/macos/bin +) diff --git a/res/shaders/rendergraph/endoftrack.frag b/res/shaders/rendergraph/endoftrack.frag new file mode 100644 index 00000000000..0f6712e87d5 --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.frag @@ -0,0 +1,17 @@ +#version 440 + +layout(location = 0) in float vGradient; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + vec4 color; +} +ubuf; + +void main() { + float minAlpha = 0.5 * ubuf.color.w; + float maxAlpha = 0.83 * ubuf.color.w; + float alpha = mix(minAlpha, maxAlpha, max(0.0, vGradient)); + // premultiple alpha + fragColor = vec4(ubuf.color.xyz * alpha, alpha); +} diff --git a/res/shaders/rendergraph/endoftrack.frag.gl b/res/shaders/rendergraph/endoftrack.frag.gl new file mode 100644 index 00000000000..78de913751a --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.frag.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + vec4 color; +}; + +uniform buf ubuf; + +varying float vGradient; + +void main() +{ + float minAlpha = 0.5 * ubuf.color.w; + float maxAlpha = 0.829999983310699462890625 * ubuf.color.w; + float alpha = mix(minAlpha, maxAlpha, max(0.0, vGradient)); + gl_FragData[0] = vec4(ubuf.color.xyz * alpha, alpha); +} diff --git a/res/shaders/rendergraph/endoftrack.vert b/res/shaders/rendergraph/endoftrack.vert new file mode 100644 index 00000000000..101cdb324e4 --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.vert @@ -0,0 +1,10 @@ +#version 440 + +layout(location = 0) in vec4 position; +layout(location = 1) in float gradient; +layout(location = 0) out float vGradient; + +void main() { + vGradient = gradient; + gl_Position = position; +} diff --git a/res/shaders/rendergraph/endoftrack.vert.gl b/res/shaders/rendergraph/endoftrack.vert.gl new file mode 100644 index 00000000000..da4626bbf7c --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.vert.gl @@ -0,0 +1,12 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying float vGradient; +attribute float gradient; +attribute vec4 position; + +void main() +{ + vGradient = gradient; + gl_Position = position; +} diff --git a/res/shaders/rendergraph/generate_shaders_gl.pl b/res/shaders/rendergraph/generate_shaders_gl.pl new file mode 100755 index 00000000000..c528eb94747 --- /dev/null +++ b/res/shaders/rendergraph/generate_shaders_gl.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +my @files = (glob("*.vert"),glob("*.frag")); + +open(GENERATED,">generated_shaders_gl.cmake"); +print(GENERATED "set(generated_shaders_gl\n"); +for $file (@files) +{ + system("qsb","--glsl","120",$file,"-o","/tmp/$$-$file.qsb"); + open(INFILE,"qsb --dump /tmp/$$-$file.qsb|"); + open(OUTFILE,">$file.gl"); + $ok = 0; + $comment_added = 0; + print "Generating $file.gl from $file\n"; + while () + { + if ($in_shader_block == 2) + { + if (m/^\*\*/) + { + $in_shader_block = 0; + $ok = 1; + } + else + { + if (!$comment_added) + { + if (!m/^#/) + { + print(OUTFILE "//// GENERATED - EDITS WILL BE OVERWRITTEN\n"); + $comment_added = 1; + } + } + print OUTFILE "$_"; + } + } + elsif ($in_shader_block == 1) + { + chomp($_); + if ($_ eq "Contents:") + { + $in_shader_block = 2; + } + } + else + { + chomp($_); + if ($_ eq "Shader 1: GLSL 120 [Standard]") + { + $in_shader_block = 1; + } + } + } + close INFILE; + close OUTFILE; + if($ok) + { + print(GENERATED " $file.gl\n"); + } + else + { + print STDERR "Failed to generated $file.gl"; + unlink("$file.gl") + } + unlink("/tmp/$$-$file.qsb"); +} +print(GENERATED ")\n"); +close GENERATED; diff --git a/res/shaders/rendergraph/generated_shaders_gl.cmake b/res/shaders/rendergraph/generated_shaders_gl.cmake new file mode 100644 index 00000000000..951c85c3669 --- /dev/null +++ b/res/shaders/rendergraph/generated_shaders_gl.cmake @@ -0,0 +1,14 @@ +set(generated_shaders_gl + endoftrack.vert.gl + pattern.vert.gl + rgb.vert.gl + rgba.vert.gl + texture.vert.gl + unicolor.vert.gl + endoftrack.frag.gl + pattern.frag.gl + rgb.frag.gl + rgba.frag.gl + texture.frag.gl + unicolor.frag.gl +) diff --git a/res/shaders/rendergraph/pattern.frag b/res/shaders/rendergraph/pattern.frag new file mode 100644 index 00000000000..5aa3d1556b1 --- /dev/null +++ b/res/shaders/rendergraph/pattern.frag @@ -0,0 +1,9 @@ +#version 440 + +layout(binding = 1) uniform sampler2D texture1; +layout(location = 0) in vec2 vTexcoord; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = texture(texture1, fract(vTexcoord)); +} diff --git a/res/shaders/rendergraph/pattern.frag.gl b/res/shaders/rendergraph/pattern.frag.gl new file mode 100644 index 00000000000..376c71668ba --- /dev/null +++ b/res/shaders/rendergraph/pattern.frag.gl @@ -0,0 +1,11 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +uniform sampler2D texture1; + +varying vec2 vTexcoord; + +void main() +{ + gl_FragData[0] = texture2D(texture1, fract(vTexcoord)); +} diff --git a/res/shaders/rendergraph/pattern.vert b/res/shaders/rendergraph/pattern.vert new file mode 100644 index 00000000000..07b3d7f1f3b --- /dev/null +++ b/res/shaders/rendergraph/pattern.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; +layout(location = 0) out vec2 vTexcoord; + +void main() { + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/pattern.vert.gl b/res/shaders/rendergraph/pattern.vert.gl new file mode 100644 index 00000000000..a3d58014be3 --- /dev/null +++ b/res/shaders/rendergraph/pattern.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec2 vTexcoord; +attribute vec2 texcoord; +attribute vec4 position; + +void main() +{ + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgb.frag b/res/shaders/rendergraph/rgb.frag new file mode 100644 index 00000000000..0a808489f5b --- /dev/null +++ b/res/shaders/rendergraph/rgb.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec3 vColor; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(vColor, 1.0); +} diff --git a/res/shaders/rendergraph/rgb.frag.gl b/res/shaders/rendergraph/rgb.frag.gl new file mode 100644 index 00000000000..b8a61f8682f --- /dev/null +++ b/res/shaders/rendergraph/rgb.frag.gl @@ -0,0 +1,9 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying vec3 vColor; + +void main() +{ + gl_FragData[0] = vec4(vColor, 1.0); +} diff --git a/res/shaders/rendergraph/rgb.vert b/res/shaders/rendergraph/rgb.vert new file mode 100644 index 00000000000..6568d01f187 --- /dev/null +++ b/res/shaders/rendergraph/rgb.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec3 color; +layout(location = 0) out vec3 vColor; + +void main() { + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgb.vert.gl b/res/shaders/rendergraph/rgb.vert.gl new file mode 100644 index 00000000000..53e86e4501c --- /dev/null +++ b/res/shaders/rendergraph/rgb.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec3 vColor; +attribute vec3 color; +attribute vec4 position; + +void main() +{ + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgba.frag b/res/shaders/rendergraph/rgba.frag new file mode 100644 index 00000000000..5cf90a770ea --- /dev/null +++ b/res/shaders/rendergraph/rgba.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(vColor.xyz * vColor.w, vColor.w); // premultiple alpha +} diff --git a/res/shaders/rendergraph/rgba.frag.gl b/res/shaders/rendergraph/rgba.frag.gl new file mode 100644 index 00000000000..a831457b968 --- /dev/null +++ b/res/shaders/rendergraph/rgba.frag.gl @@ -0,0 +1,9 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying vec4 vColor; + +void main() +{ + gl_FragData[0] = vec4(vColor.xyz * vColor.w, vColor.w); +} diff --git a/res/shaders/rendergraph/rgba.vert b/res/shaders/rendergraph/rgba.vert new file mode 100644 index 00000000000..d5ce8b2d9db --- /dev/null +++ b/res/shaders/rendergraph/rgba.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec4 color; +layout(location = 0) out vec4 vColor; + +void main() { + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgba.vert.gl b/res/shaders/rendergraph/rgba.vert.gl new file mode 100644 index 00000000000..df2bcf93236 --- /dev/null +++ b/res/shaders/rendergraph/rgba.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec4 vColor; +attribute vec4 color; +attribute vec4 position; + +void main() +{ + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/texture.frag b/res/shaders/rendergraph/texture.frag new file mode 100644 index 00000000000..bbe37bccd69 --- /dev/null +++ b/res/shaders/rendergraph/texture.frag @@ -0,0 +1,9 @@ +#version 440 + +layout(binding = 1) uniform sampler2D texture1; +layout(location = 0) in vec2 vTexcoord; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = texture(texture1, vTexcoord); +} diff --git a/res/shaders/rendergraph/texture.frag.gl b/res/shaders/rendergraph/texture.frag.gl new file mode 100644 index 00000000000..b2d03f1352c --- /dev/null +++ b/res/shaders/rendergraph/texture.frag.gl @@ -0,0 +1,11 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +uniform sampler2D texture1; + +varying vec2 vTexcoord; + +void main() +{ + gl_FragData[0] = texture2D(texture1, vTexcoord); +} diff --git a/res/shaders/rendergraph/texture.vert b/res/shaders/rendergraph/texture.vert new file mode 100644 index 00000000000..07b3d7f1f3b --- /dev/null +++ b/res/shaders/rendergraph/texture.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; +layout(location = 0) out vec2 vTexcoord; + +void main() { + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/texture.vert.gl b/res/shaders/rendergraph/texture.vert.gl new file mode 100644 index 00000000000..a3d58014be3 --- /dev/null +++ b/res/shaders/rendergraph/texture.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec2 vTexcoord; +attribute vec2 texcoord; +attribute vec4 position; + +void main() +{ + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/unicolor.frag b/res/shaders/rendergraph/unicolor.frag new file mode 100644 index 00000000000..7099bb8f3dd --- /dev/null +++ b/res/shaders/rendergraph/unicolor.frag @@ -0,0 +1,13 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; + vec4 color; +} +ubuf; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w); // premultiply alpha +} diff --git a/res/shaders/rendergraph/unicolor.frag.gl b/res/shaders/rendergraph/unicolor.frag.gl new file mode 100644 index 00000000000..bfef503120b --- /dev/null +++ b/res/shaders/rendergraph/unicolor.frag.gl @@ -0,0 +1,15 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; + vec4 color; +}; + +uniform buf ubuf; + +void main() +{ + gl_FragData[0] = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w); +} diff --git a/res/shaders/rendergraph/unicolor.vert b/res/shaders/rendergraph/unicolor.vert new file mode 100644 index 00000000000..9e268c18fba --- /dev/null +++ b/res/shaders/rendergraph/unicolor.vert @@ -0,0 +1,13 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; + vec4 color; +} +ubuf; + +layout(location = 0) in vec4 position; + +void main() { + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/unicolor.vert.gl b/res/shaders/rendergraph/unicolor.vert.gl new file mode 100644 index 00000000000..d126f3dab58 --- /dev/null +++ b/res/shaders/rendergraph/unicolor.vert.gl @@ -0,0 +1,17 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; + vec4 color; +}; + +uniform buf ubuf; + +attribute vec4 position; + +void main() +{ + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/CMakeLists.txt b/src/rendergraph/CMakeLists.txt new file mode 100644 index 00000000000..059bbdaa050 --- /dev/null +++ b/src/rendergraph/CMakeLists.txt @@ -0,0 +1,10 @@ +find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core) +message(STATUS "Qt version ${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}") + +if (QT_VERSION_MINOR GREATER_EQUAL 6) +set(USE_QSHADER_FOR_GL ON) +endif() + +add_subdirectory(opengl) +add_subdirectory(scenegraph) +add_subdirectory(../../res/shaders/rendergraph shaders) diff --git a/src/rendergraph/README b/src/rendergraph/README new file mode 100644 index 00000000000..8b1720e22c8 --- /dev/null +++ b/src/rendergraph/README @@ -0,0 +1,63 @@ +rendergraph is an abstraction layer that can be compiled with one of two backends: + +- scenegraph (QtQuick scene graph classes) +- opengl (custom classes using QOpenGL) + +This abstraction layer follows the design of the QtQuick scene graph, with classes such as Material, Geometry, Node and GeometryNode, but it only gives access to and only uses a subset of its functionality. + +The scenegraph backend class the underlying QtQuick scene graph classes directly or almost directly. The opengl layer implements classes that mimic the behaviour of the +Qt scene graph classes. + +The objective of rendergraph is to be able to write code that can be used verbatim within the context of both QWidgets applications and QML applications. This includes using the same Vulkan-style GLSL shader code for both. (The QSGMaterialShader uses the qsb files, QOpenGLShader uses the OpenGL shader code generated by qsb) + +Primarily, rendering with rendergraph uses a graph of Node's, typically of type GeometryNode. A GeometryNode uses a Geometry, which contains the vertex data of what the node renders (positional, texture coordinates, color information...) and a Material, which is the interface to the underlying shader. Uniform data is set through the Material. The QtQuick scene graph documentation is a good started point to understand the design. + +The code is organized as follows: + +common/rendergraph + Public header files. This is the primary API, identical for both backends, and the only files that should be included to write code compatible with both backends. + +common + Implementation of the functionality declared in the common/rendergraph headers that is identical for both backends. + The general guideline is "code duplicated between the backends should be in the common layer". + +common/rendergraph/material + Several basic 'materials' for common rendering operations (e.g. uniform or variying color, texture) + +scenegraph + Implementation of the functionality declared in the common/rendergraph headers that is specific to call the scenegraph backend + +scenegraph/rendergraph + Public header files specific for the opengl backend. This is the layer between QtQuick application code and the common rendergraph API. + +scenegraph/backend + The scenegraph backend implementation, the layer between the common/rendergraph API and the underlying QtQuick scene graph classes. + +opengl + Implementation of the functionality declared in the common/rendergraph headers that is specific to call the opengl backend + +opengl/rendergraph + Public header files specific for the opengl backend. This is the layer between QWidgets/QOpenGL application code and the common rendergraph API. + +opengl/backend + The opengl backend implementation, the layer between the common/rendergraph API and custom implemented classes that mimic the behaviour QtQuick scene graph classes. + +Furthermore, some simple examples: + +examples/sg_example + An example QtQuick application that uses the scenegraph backend + +examples/gl_example + An example QOpenGLWindow based application that uses the opengl backend + +examples/common + The graphics code that is identical for both example + +../../res/shaders/rendergraph/ + The qsb and (generated) gl shader files + See ../../res/shaders/rendergraph/README for more info + +More advanced use of rendergraph can be found in the waveform/renderers/allshader classes, which have partially been ported the rendergraph. + + +m0dB diff --git a/src/rendergraph/common/rendergraph/assert.h b/src/rendergraph/common/rendergraph/assert.h new file mode 100644 index 00000000000..6b47ad6a7f4 --- /dev/null +++ b/src/rendergraph/common/rendergraph/assert.h @@ -0,0 +1,3 @@ +// rendergraph is a module but we want to include util/assert.h from Mixxx, +// without adding the entire src/ dir to the include paths. +#include "../../../util/assert.h" diff --git a/src/rendergraph/common/rendergraph/attributeinit.h b/src/rendergraph/common/rendergraph/attributeinit.h new file mode 100644 index 00000000000..ccbbbf16b9b --- /dev/null +++ b/src/rendergraph/common/rendergraph/attributeinit.h @@ -0,0 +1,18 @@ +#pragma once + +#include "rendergraph/types.h" + +namespace rendergraph { +struct AttributeInit; +} + +// helper to create an AttributeSet using an initializer_list +struct rendergraph::AttributeInit { + int m_tupleSize; + PrimitiveType m_primitiveType; + + template + static AttributeInit create() { + return AttributeInit{tupleSizeOf(), primitiveTypeOf()}; + } +}; diff --git a/src/rendergraph/common/rendergraph/attributeset.h b/src/rendergraph/common/rendergraph/attributeset.h new file mode 100644 index 00000000000..717f47a3084 --- /dev/null +++ b/src/rendergraph/common/rendergraph/attributeset.h @@ -0,0 +1,20 @@ +#pragma once + +#include "backend/baseattributeset.h" +#include "rendergraph/attributeinit.h" + +namespace rendergraph { + +class AttributeSet : public BaseAttributeSet { + public: + AttributeSet(std::initializer_list list, const std::vector& names); +}; + +template +AttributeSet makeAttributeSet(const QString (&names)[N]) { + static_assert(sizeof...(Ts) == N); + return AttributeSet({(AttributeInit::create())...}, + std::vector(std::cbegin(names), std::cend(names))); +} + +} // namespace rendergraph diff --git a/src/rendergraph/common/rendergraph/geometry.h b/src/rendergraph/common/rendergraph/geometry.h new file mode 100644 index 00000000000..47108777493 --- /dev/null +++ b/src/rendergraph/common/rendergraph/geometry.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include + +#include "backend/basegeometry.h" +#include "rendergraph/attributeset.h" + +namespace rendergraph { +class Geometry; +} // namespace rendergraph + +class rendergraph::Geometry : public rendergraph::BaseGeometry { + public: + using DrawingMode = rendergraph::DrawingMode; + + struct Point2D { + QVector2D position2D; + }; + + struct TexturedPoint2D { + QVector2D position2D; + QVector2D texcoord2D; + }; + + struct RGBColoredPoint2D { + QVector2D position2D; + QVector3D color3D; + }; + + struct RGBAColoredPoint2D { + QVector2D position2D; + QVector4D color4D; + }; + + Geometry(const rendergraph::AttributeSet& attributeSet, int vertexCount); + + const Attribute* attributes() const { + return BaseGeometry::attributes(); + } + + void setAttributeValues(int attributePosition, const float* data, int numTuples); + + int attributeCount() const { + return BaseGeometry::attributeCount(); + } + + void allocate(int vertexCount) { + BaseGeometry::allocate(vertexCount); + } + + int sizeOfVertex() const { + return BaseGeometry::sizeOfVertex(); + } + int vertexCount() const { + return BaseGeometry::vertexCount(); + } + + template + T* vertexDataAs() { + return reinterpret_cast(vertexData()); + } + + DrawingMode drawingMode() const; + + void setDrawingMode(DrawingMode mode); +}; diff --git a/src/rendergraph/common/rendergraph/geometrynode.h b/src/rendergraph/common/rendergraph/geometrynode.h new file mode 100644 index 00000000000..37db115ce40 --- /dev/null +++ b/src/rendergraph/common/rendergraph/geometrynode.h @@ -0,0 +1,39 @@ +#pragma once + +#include "backend/basegeometrynode.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material.h" +#include "rendergraph/nodeinterface.h" + +namespace rendergraph { +class GeometryNode; +} // namespace rendergraph + +class rendergraph::GeometryNode : public rendergraph::NodeInterface { + public: + GeometryNode(); + virtual ~GeometryNode() = default; + + template + void initForRectangles(int numRectangles) { + const int verticesPerRectangle = 6; // 2 triangles + setGeometry(std::make_unique(T_Material::attributes(), + numRectangles * verticesPerRectangle)); + setMaterial(std::make_unique()); + geometry().setDrawingMode(DrawingMode::Triangles); + } + + void setUsePreprocess(bool value); + void setMaterial(std::unique_ptr material); + void setGeometry(std::unique_ptr geometry); + + Geometry& geometry() const; + Material& material() const; + + void markDirtyGeometry(); + void markDirtyMaterial(); + + private: + std::unique_ptr m_pMaterial; + std::unique_ptr m_pGeometry; +}; diff --git a/src/rendergraph/common/rendergraph/material.h b/src/rendergraph/common/rendergraph/material.h new file mode 100644 index 00000000000..ab18e89166a --- /dev/null +++ b/src/rendergraph/common/rendergraph/material.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include "backend/basematerial.h" +#include "rendergraph/assert.h" +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformscache.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class Material; +} // namespace rendergraph + +class rendergraph::Material : public rendergraph::BaseMaterial { + public: + Material(const UniformSet& uniformSet); + virtual ~Material(); + + // see QSGMaterial::compare. + // TODO decide if this should be virtual. QSGMaterial::compare is virtual + // to concrete Material can implement a compare function, but in rendergraph + // we can compare the uniforms cache and texture already here, which seems + // sufficient. + int compare(const Material* pOther) const { + DEBUG_ASSERT(type() == pOther->type()); + int cacheCompareResult = std::memcmp(m_uniformsCache.data(), + pOther->m_uniformsCache.data(), + m_uniformsCache.size()); + if (cacheCompareResult != 0) { + return cacheCompareResult < 0 ? -1 : 1; + } + // TODO multiple textures + if (!texture(0) || !pOther->texture(0)) { + return texture(0) ? 1 : -1; + } + + const qint64 diff = texture(0)->comparisonKey() - pOther->texture(0)->comparisonKey(); + return diff < 0 ? -1 : (diff > 0 ? 1 : 0); + } + + virtual std::unique_ptr createShader() const = 0; + + template + void setUniform(int uniformIndex, const T& value) { + m_uniformsCache.set(uniformIndex, value); + m_uniformsCacheDirty = true; + } + + const UniformsCache& uniformsCache() const { + return m_uniformsCache; + } + + bool clearUniformsCacheDirty() { + if (m_uniformsCacheDirty) { + m_uniformsCacheDirty = false; + return true; + } + return false; + } + + virtual Texture* texture(int) const { + return nullptr; + } + + private: + UniformsCache m_uniformsCache; + bool m_uniformsCacheDirty{}; +}; diff --git a/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp new file mode 100644 index 00000000000..ee35b465934 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp @@ -0,0 +1,33 @@ +#include "endoftrackmaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +EndOfTrackMaterial::EndOfTrackMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& EndOfTrackMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "gradient"}); + return set; +} + +/* static */ const UniformSet& EndOfTrackMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.color"}); + return set; +} + +MaterialType* EndOfTrackMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr EndOfTrackMaterial::createShader() const { + return std::make_unique( + "endoftrack.vert", "endoftrack.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h new file mode 100644 index 00000000000..94c80693799 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h @@ -0,0 +1,19 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class EndOfTrackMaterial; +} + +class rendergraph::EndOfTrackMaterial : public rendergraph::Material { + public: + EndOfTrackMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/material/patternmaterial.cpp b/src/rendergraph/common/rendergraph/material/patternmaterial.cpp new file mode 100644 index 00000000000..864f4a60adf --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/patternmaterial.cpp @@ -0,0 +1,33 @@ +#include "patternmaterial.h" + +#include +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +using namespace rendergraph; + +PatternMaterial::PatternMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& PatternMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "texcoord"}); + return set; +} + +/* static */ const UniformSet& PatternMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* PatternMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr PatternMaterial::createShader() const { + return std::make_unique( + "pattern.vert", "pattern.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/patternmaterial.h b/src/rendergraph/common/rendergraph/material/patternmaterial.h new file mode 100644 index 00000000000..661246abd05 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/patternmaterial.h @@ -0,0 +1,32 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class PatternMaterial; +} + +class rendergraph::PatternMaterial : public rendergraph::Material { + public: + PatternMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; + + Texture* texture(int) const override { + return m_pTexture.get(); + } + + void setTexture(std::unique_ptr texture) { + m_pTexture = std::move(texture); + } + + private: + std::unique_ptr m_pTexture; +}; diff --git a/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp b/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp new file mode 100644 index 00000000000..e81382bf9ce --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp @@ -0,0 +1,34 @@ +#include "rgbamaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +RGBAMaterial::RGBAMaterial() + : Material(uniforms()) { +} + +// static +const AttributeSet& RGBAMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "color"}); + return set; +} + +/* static */ const UniformSet& RGBAMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* RGBAMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr RGBAMaterial::createShader() const { + return std::make_unique( + "rgba.vert", "rgba.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/rgbamaterial.h b/src/rendergraph/common/rendergraph/material/rgbamaterial.h new file mode 100644 index 00000000000..0921ebb4fae --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.h @@ -0,0 +1,19 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class RGBAMaterial; +} + +class rendergraph::RGBAMaterial : public rendergraph::Material { + public: + RGBAMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp b/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp new file mode 100644 index 00000000000..455555d1e97 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp @@ -0,0 +1,33 @@ +#include "rgbmaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +RGBMaterial::RGBMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& RGBMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "color"}); + return set; +} + +/* static */ const UniformSet& RGBMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* RGBMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr RGBMaterial::createShader() const { + return std::make_unique( + "rgb.vert", "rgb.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/rgbmaterial.h b/src/rendergraph/common/rendergraph/material/rgbmaterial.h new file mode 100644 index 00000000000..cfe1fde7782 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.h @@ -0,0 +1,19 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class RGBMaterial; +} + +class rendergraph::RGBMaterial : public rendergraph::Material { + public: + RGBMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/material/texturematerial.cpp b/src/rendergraph/common/rendergraph/material/texturematerial.cpp new file mode 100644 index 00000000000..b3f61767626 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/texturematerial.cpp @@ -0,0 +1,33 @@ +#include "texturematerial.h" + +#include +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +using namespace rendergraph; + +TextureMaterial::TextureMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& TextureMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "texcoord"}); + return set; +} + +/* static */ const UniformSet& TextureMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* TextureMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr TextureMaterial::createShader() const { + return std::make_unique( + "texture.vert", "texture.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/texturematerial.h b/src/rendergraph/common/rendergraph/material/texturematerial.h new file mode 100644 index 00000000000..2fa5a2310cc --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/texturematerial.h @@ -0,0 +1,32 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class TextureMaterial; +} + +class rendergraph::TextureMaterial : public rendergraph::Material { + public: + TextureMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; + + Texture* texture(int /*binding*/) const override { + return m_pTexture.get(); + } + + void setTexture(std::unique_ptr texture) { + m_pTexture = std::move(texture); + } + + private: + std::unique_ptr m_pTexture; +}; diff --git a/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp b/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp new file mode 100644 index 00000000000..d67fd16320c --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp @@ -0,0 +1,33 @@ +#include "unicolormaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniColorMaterial::UniColorMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& UniColorMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position"}); + return set; +} + +/* static */ const UniformSet& UniColorMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix", "ubuf.color"}); + return set; +} + +MaterialType* UniColorMaterial::type() const { + static MaterialType type; + return &type; +} + +std::unique_ptr UniColorMaterial::createShader() const { + return std::make_unique( + "unicolor.vert", "unicolor.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/unicolormaterial.h b/src/rendergraph/common/rendergraph/material/unicolormaterial.h new file mode 100644 index 00000000000..26225f4d3c6 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.h @@ -0,0 +1,19 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class UniColorMaterial; +} + +class rendergraph::UniColorMaterial : public rendergraph::Material { + public: + UniColorMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/materialshader.h b/src/rendergraph/common/rendergraph/materialshader.h new file mode 100644 index 00000000000..43c811ebebd --- /dev/null +++ b/src/rendergraph/common/rendergraph/materialshader.h @@ -0,0 +1,17 @@ +#pragma once + +#include "backend/basematerialshader.h" +#include "rendergraph/attributeset.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class MaterialShader; +} // namespace rendergraph + +class rendergraph::MaterialShader : public rendergraph::BaseMaterialShader { + public: + MaterialShader(const char* vertexShaderFile, + const char* fragmentShaderFile, + const UniformSet& uniforms, + const AttributeSet& attributeSet); +}; diff --git a/src/rendergraph/common/rendergraph/materialtype.h b/src/rendergraph/common/rendergraph/materialtype.h new file mode 100644 index 00000000000..67da71e91f3 --- /dev/null +++ b/src/rendergraph/common/rendergraph/materialtype.h @@ -0,0 +1,10 @@ +#pragma once + +#include "backend/basematerialtype.h" + +namespace rendergraph { +class MaterialType; +} + +class rendergraph::MaterialType : public rendergraph::BaseMaterialType { +}; diff --git a/src/rendergraph/common/rendergraph/node.h b/src/rendergraph/common/rendergraph/node.h new file mode 100644 index 00000000000..3060648e846 --- /dev/null +++ b/src/rendergraph/common/rendergraph/node.h @@ -0,0 +1,11 @@ +#pragma once + +#include "backend/basenode.h" +#include "rendergraph/nodeinterface.h" + +namespace rendergraph { +class Node; +} // namespace rendergraph + +class rendergraph::Node : public rendergraph::NodeInterface { +}; diff --git a/src/rendergraph/common/rendergraph/opacitynode.h b/src/rendergraph/common/rendergraph/opacitynode.h new file mode 100644 index 00000000000..d69f0b1c42a --- /dev/null +++ b/src/rendergraph/common/rendergraph/opacitynode.h @@ -0,0 +1,11 @@ +#pragma once + +#include "backend/baseopacitynode.h" +#include "rendergraph/nodeinterface.h" + +namespace rendergraph { +class OpacityNode; +} // namespace rendergraph + +class rendergraph::OpacityNode : public rendergraph::NodeInterface { +}; diff --git a/src/rendergraph/common/rendergraph/texture.h b/src/rendergraph/common/rendergraph/texture.h new file mode 100644 index 00000000000..aa6bbe95ccf --- /dev/null +++ b/src/rendergraph/common/rendergraph/texture.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "backend/basetexture.h" +#include "rendergraph/context.h" + +namespace rendergraph { +class Texture; +} // namespace rendergraph + +class rendergraph::Texture { + public: + Texture(Context* pContext, const QImage& image); + + BaseTexture* backendTexture() const { + return m_pTexture.get(); + } + + // used by Material::compare + qint64 comparisonKey() const; + + private: + const std::unique_ptr m_pTexture{}; +}; diff --git a/src/rendergraph/common/rendergraph/types.h b/src/rendergraph/common/rendergraph/types.h new file mode 100644 index 00000000000..f92e8de6cda --- /dev/null +++ b/src/rendergraph/common/rendergraph/types.h @@ -0,0 +1,36 @@ +#pragma once + +namespace rendergraph { + +enum class DrawingMode { + Triangles, + TriangleStrip +}; + +enum class PrimitiveType { + UInt, + Float, +}; + +enum class Type { + UInt, + Float, + Vector2D, + Vector3D, + Vector4D, + Matrix4x4 +}; + +int sizeOf(Type type); +int sizeOf(PrimitiveType primitiveType); + +template +Type typeOf(); + +template +PrimitiveType primitiveTypeOf(); + +template +int tupleSizeOf(); + +} // namespace rendergraph diff --git a/src/rendergraph/common/rendergraph/uniform.h b/src/rendergraph/common/rendergraph/uniform.h new file mode 100644 index 00000000000..19a6a9c74a3 --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniform.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "rendergraph/types.h" + +namespace rendergraph { +struct Uniform; +} + +struct rendergraph::Uniform { + const Type m_type; + const QString m_name; + + Uniform(Type type) + : m_type{type} { + } + + Uniform(Type type, QString name) + : m_type{type}, + m_name{std::move(name)} { + } + + template + static Uniform create() { + return Uniform(typeOf()); + } +}; diff --git a/src/rendergraph/common/rendergraph/uniformscache.cpp b/src/rendergraph/common/rendergraph/uniformscache.cpp new file mode 100644 index 00000000000..069469b0310 --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniformscache.cpp @@ -0,0 +1,26 @@ +#include "rendergraph/uniformscache.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniformsCache::UniformsCache(const UniformSet& uniformSet) { + int offset = 0; + m_infos.reserve(uniformSet.uniforms().size()); + for (const auto& uniform : uniformSet.uniforms()) { + const int size = sizeOf(uniform.m_type); + m_infos.push_back({uniform.m_type, offset}); + offset += size; + } + m_byteArray.resize(offset); + m_byteArray.fill('\0'); +} + +UniformsCache::~UniformsCache() = default; + +void UniformsCache::set(int uniformIndex, const void* ptr, int size) { + memcpy(m_byteArray.data() + m_infos[uniformIndex].m_offset, ptr, size); +} + +void UniformsCache::get(int uniformIndex, void* ptr, int size) const { + memcpy(ptr, m_byteArray.data() + m_infos[uniformIndex].m_offset, size); +} diff --git a/src/rendergraph/common/rendergraph/uniformscache.h b/src/rendergraph/common/rendergraph/uniformscache.h new file mode 100644 index 00000000000..045887f0c37 --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniformscache.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "rendergraph/assert.h" +#include "rendergraph/types.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class UniformsCache; +} // namespace rendergraph + +class rendergraph::UniformsCache { + public: + UniformsCache(const UniformSet& uniformSet); + ~UniformsCache(); + + template + void set(int uniformIndex, const T& value) { + DEBUG_ASSERT(type(uniformIndex) == typeOf()); + DEBUG_ASSERT(std::is_trivially_copyable()); + set(uniformIndex, static_cast(&value), sizeOf(typeOf())); + } + + template + T get(int uniformIndex) const { + DEBUG_ASSERT(type(uniformIndex) == typeOf()); + DEBUG_ASSERT(std::is_trivially_copyable()); + T value; + get(uniformIndex, static_cast(&value), sizeof(T)); + return value; + } + Type type(int uniformIndex) const { + return m_infos[uniformIndex].m_type; + } + + const char* data() const { + return m_byteArray.data(); + } + qsizetype size() const { + return m_byteArray.size(); + } + int count() const { + return static_cast(m_infos.size()); + } + + private: + void set(int uniformIndex, const void* ptr, int size); + void get(int uniformIndex, void* ptr, int size) const; + + struct Info { + const Type m_type; + const int m_offset; + }; + + std::vector m_infos; + QByteArray m_byteArray; +}; + +template<> +inline void rendergraph::UniformsCache::set(int uniformIndex, const QColor& color) { + set(uniformIndex, QVector4D{color.redF(), color.greenF(), color.blueF(), color.alphaF()}); +} + +template<> +inline void rendergraph::UniformsCache::set( + int uniformIndex, const QMatrix4x4& matrix) { + DEBUG_ASSERT(type(uniformIndex) == typeOf()); + set(uniformIndex, matrix.constData(), sizeOf(typeOf())); +} diff --git a/src/rendergraph/common/rendergraph/uniformset.cpp b/src/rendergraph/common/rendergraph/uniformset.cpp new file mode 100644 index 00000000000..68e077613bd --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniformset.cpp @@ -0,0 +1,20 @@ +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniformSet::UniformSet(std::initializer_list list, const std::vector& names) { + int i = 0; + for (auto item : list) { + add(Uniform{item.m_type, names[i++]}); + } +} + +UniformSet::~UniformSet() = default; + +void UniformSet::add(const Uniform& uniform) { + m_uniforms.push_back(uniform); +} + +const std::vector& UniformSet::uniforms() const { + return m_uniforms; +} diff --git a/src/rendergraph/common/rendergraph/uniformset.h b/src/rendergraph/common/rendergraph/uniformset.h new file mode 100644 index 00000000000..34af87e5c94 --- /dev/null +++ b/src/rendergraph/common/rendergraph/uniformset.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +#include "rendergraph/uniform.h" + +namespace rendergraph { +class UniformSet; +} + +class rendergraph::UniformSet { + public: + UniformSet(std::initializer_list list, const std::vector& names); + + ~UniformSet(); + + const std::vector& uniforms() const; + + private: + void add(const Uniform& uniform); + std::vector m_uniforms; +}; + +namespace rendergraph { +template +UniformSet makeUniformSet(const std::vector& names) { + return UniformSet({(Uniform::create())...}, names); +} +} // namespace rendergraph diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h new file mode 100644 index 00000000000..afa7e466d1c --- /dev/null +++ b/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h @@ -0,0 +1,174 @@ +#pragma once + +#include "rendergraph/geometry.h" + +namespace rendergraph { +class RGBAVertexUpdater; +} + +class rendergraph::RGBAVertexUpdater { + public: + RGBAVertexUpdater(Geometry::RGBAColoredPoint2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + + void addRectangle( + QVector2D lt, + QVector2D rb, + QVector4D rgba) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), rgba.x(), rgba.y(), rgba.z(), rgba.w()); + } + void addRectangleVGradient( + QVector2D lt, + QVector2D rb, + QVector4D rgbat, + QVector4D rgbab) { + addRectangleVGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbat.x(), + rgbat.y(), + rgbat.z(), + rgbat.w(), + rgbab.x(), + rgbab.y(), + rgbab.z(), + rgbab.w()); + } + void addRectangleHGradient( + QVector2D lt, + QVector2D rb, + QVector4D rgbal, + QVector4D rgbar) { + addRectangleHGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbal.x(), + rgbal.y(), + rgbal.z(), + rgbal.w(), + rgbar.x(), + rgbar.y(), + rgbar.z(), + rgbar.w()); + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + void addTriangle(QVector2D p1, QVector2D p2, QVector2D p3, QVector4D rgba) { + addTriangle(p1.x(), + p1.y(), + p2.x(), + p2.y(), + p3.x(), + p3.y(), + rgba.x(), + rgba.y(), + rgba.z(), + rgba.w()); + } + void addTriangle(QVector2D p1, + QVector2D p2, + QVector2D p3, + QVector4D rgba1, + QVector4D rgba2, + QVector4D rgba3) { + addTriangle(p1.x(), + p1.y(), + p2.x(), + p2.y(), + p3.x(), + p3.y(), + rgba1.x(), + rgba1.y(), + rgba1.z(), + rgba1.w(), + rgba2.x(), + rgba2.y(), + rgba2.z(), + rgba2.w(), + rgba3.x(), + rgba3.y(), + rgba3.z(), + rgba3.w()); + } + + private: + void addRectangle(float x1, float y1, float x2, float y2, float r, float g, float b, float a) { + addTriangle(x1, y1, x2, y1, x1, y2, r, g, b, a); + addTriangle(x1, y2, x2, y2, x2, y1, r, g, b, a); + } + void addRectangleVGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float a1, + float r2, + float g2, + float b2, + float a2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, a1, r1, g1, b1, a1, r2, g2, b2, a2); + addTriangle(x1, y2, x2, y2, x2, y1, r2, g2, b2, a2, r2, g2, b2, a2, r1, g1, b1, a1); + } + void addRectangleHGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float a1, + float r2, + float g2, + float b2, + float a2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, a1, r2, g2, b2, a2, r1, g1, b1, a1); + addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, a1, r2, g2, b2, a2, r2, g2, b2, a2); + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r, + float g, + float b, + float a) { + *m_pWrite++ = Geometry::RGBAColoredPoint2D{{x1, y1}, {r, g, b, a}}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{{x2, y2}, {r, g, b, a}}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{{x3, y3}, {r, g, b, a}}; + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r1, + float g1, + float b1, + float a1, + float r2, + float g2, + float b2, + float a2, + float r3, + float g3, + float b3, + float a3) { + *m_pWrite++ = Geometry::RGBAColoredPoint2D{{x1, y1}, {r1, g1, b1, a1}}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{{x2, y2}, {r2, g2, b2, a2}}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{{x3, y3}, {r3, g3, b3, a3}}; + } + Geometry::RGBAColoredPoint2D* const m_pData; + Geometry::RGBAColoredPoint2D* m_pWrite; +}; diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h new file mode 100644 index 00000000000..c7191e902ec --- /dev/null +++ b/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h @@ -0,0 +1,152 @@ +#pragma once + +#include "rendergraph/geometry.h" + +namespace rendergraph { +class RGBVertexUpdater; +} + +class rendergraph::RGBVertexUpdater { + public: + RGBVertexUpdater(Geometry::RGBColoredPoint2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + + void addRectangle( + QVector2D lt, + QVector2D rb, + QVector3D rgb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), rgb.x(), rgb.y(), rgb.z()); + } + void addRectangleVGradient( + QVector2D lt, + QVector2D rb, + QVector3D rgbt, + QVector3D rgbb) { + addRectangleVGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbt.x(), + rgbt.y(), + rgbt.z(), + rgbb.x(), + rgbb.y(), + rgbb.z()); + } + void addRectangleHGradient( + QVector2D lt, + QVector2D rb, + QVector3D rgbl, + QVector3D rgbr) { + addRectangleHGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbl.x(), + rgbl.y(), + rgbl.z(), + rgbr.x(), + rgbr.y(), + rgbr.z()); + } + void addTriangle(QVector2D p1, QVector2D p2, QVector2D p3, QVector3D rgb) { + addTriangle(p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y(), rgb.x(), rgb.y(), rgb.z()); + } + void addTriangle(QVector2D p1, + QVector2D p2, + QVector2D p3, + QVector3D rgb1, + QVector3D rgb2, + QVector3D rgb3) { + addTriangle(p1.x(), + p1.y(), + p2.x(), + p2.y(), + p3.x(), + p3.y(), + rgb1.x(), + rgb1.y(), + rgb1.z(), + rgb2.x(), + rgb2.y(), + rgb2.z(), + rgb3.x(), + rgb3.y(), + rgb3.z()); + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + void addRectangle(float x1, float y1, float x2, float y2, float r, float g, float b) { + addTriangle(x1, y1, x2, y1, x1, y2, r, g, b); + addTriangle(x1, y2, x2, y2, x2, y1, r, g, b); + } + void addRectangleVGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float r2, + float g2, + float b2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, r1, g1, b1, r2, g2, b2); + addTriangle(x1, y2, x2, y2, x2, y1, r2, g2, b2, r2, g2, b2, r1, g1, b1); + } + void addRectangleHGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float r2, + float g2, + float b2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, r2, g2, b2, r1, g1, b1); + addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, r2, g2, b2, r2, g2, b2); + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r, + float g, + float b) { + *m_pWrite++ = Geometry::RGBColoredPoint2D{{x1, y1}, {r, g, b}}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{{x2, y2}, {r, g, b}}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{{x3, y3}, {r, g, b}}; + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r1, + float g1, + float b1, + float r2, + float g2, + float b2, + float r3, + float g3, + float b3) { + *m_pWrite++ = Geometry::RGBColoredPoint2D{{x1, y1}, {r1, g1, b1}}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{{x2, y2}, {r2, g2, b2}}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{{x3, y3}, {r3, g3, b3}}; + } + + private: + Geometry::RGBColoredPoint2D* const m_pData; + Geometry::RGBColoredPoint2D* m_pWrite; +}; diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h new file mode 100644 index 00000000000..29aa5beca04 --- /dev/null +++ b/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h @@ -0,0 +1,70 @@ +#pragma once + +#include "rendergraph/geometry.h" + +namespace rendergraph { +class TexturedVertexUpdater; +} + +class rendergraph::TexturedVertexUpdater { + public: + TexturedVertexUpdater(Geometry::TexturedPoint2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + void addRectangle( + QVector2D lt, QVector2D rb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), 0.f, 0.f, 1.f, 1.f); + } + void addRectangle( + QVector2D lt, QVector2D rb, QVector2D tlr, QVector2D trb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), tlr.x(), tlr.y(), trb.x(), trb.y()); + } + void addTriangle(QVector2D p1, + QVector2D p2, + QVector2D p3, + QVector2D tp1, + QVector2D tp2, + QVector2D tp3) { + addTriangle(p1.x(), + p1.y(), + p2.x(), + p2.y(), + p3.x(), + p3.y(), + tp1.x(), + tp1.y(), + tp2.x(), + tp2.y(), + tp3.x(), + tp3.y()); + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + void addRectangle( + float x1, float y1, float x2, float y2, float tx1, float ty1, float tx2, float ty2) { + addTriangle(x1, y1, x2, y1, x1, y2, tx1, ty1, tx2, ty1, tx1, ty2); + addTriangle(x1, y2, x2, y2, x2, y1, tx1, ty2, tx2, ty2, tx2, ty1); + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float tx1, + float ty1, + float tx2, + float ty2, + float tx3, + float ty3) { + *m_pWrite++ = Geometry::TexturedPoint2D{{x1, y1}, {tx1, ty1}}; + *m_pWrite++ = Geometry::TexturedPoint2D{{x2, y2}, {tx2, ty2}}; + *m_pWrite++ = Geometry::TexturedPoint2D{{x3, y3}, {tx3, ty3}}; + } + Geometry::TexturedPoint2D* const m_pData; + Geometry::TexturedPoint2D* m_pWrite; +}; diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h new file mode 100644 index 00000000000..4ae381db209 --- /dev/null +++ b/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h @@ -0,0 +1,40 @@ +#pragma once + +namespace rendergraph { +class VertexUpdater; +} + +class rendergraph::VertexUpdater { + public: + VertexUpdater(Geometry::Point2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + void addRectangle( + QVector2D lt, QVector2D rb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y()); + } + void addTriangle(QVector2D p1, QVector2D p2, QVector2D p3) { + addTriangle(p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y()); + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + void addRectangle( + float x1, + float y1, + float x2, + float y2) { + addTriangle(x1, y1, x2, y1, x1, y2); + addTriangle(x1, y2, x2, y2, x2, y1); + } + void addTriangle(float x1, float y1, float x2, float y2, float x3, float y3) { + *m_pWrite++ = Geometry::Point2D{{x1, y1}}; + *m_pWrite++ = Geometry::Point2D{{x2, y2}}; + *m_pWrite++ = Geometry::Point2D{{x3, y3}}; + } + Geometry::Point2D* const m_pData; + Geometry::Point2D* m_pWrite; +}; diff --git a/src/rendergraph/common/types.cpp b/src/rendergraph/common/types.cpp new file mode 100644 index 00000000000..211538e5af4 --- /dev/null +++ b/src/rendergraph/common/types.cpp @@ -0,0 +1,111 @@ +#include "rendergraph/types.h" + +#include +#include +#include +#include +#include + +using namespace rendergraph; + +int rendergraph::sizeOf(PrimitiveType type) { + switch (type) { + case PrimitiveType::UInt: + return sizeof(uint32_t); + case PrimitiveType::Float: + return sizeof(float); + } + return 0; +} + +int rendergraph::sizeOf(Type type) { + switch (type) { + case Type::UInt: + return sizeof(uint32_t); + case Type::Float: + return sizeof(float); + case Type::Vector2D: + return sizeof(float) * 2; + case Type::Vector3D: + return sizeof(float) * 3; + case Type::Vector4D: + return sizeof(float) * 4; + case Type::Matrix4x4: + return sizeof(float) * 4 * 4; + } + return 0; +} + +template<> +Type rendergraph::typeOf() { + return Type::UInt; +} + +template<> +Type rendergraph::typeOf() { + return Type::Float; +} + +template<> +Type rendergraph::typeOf() { + return Type::Vector2D; +} + +template<> +Type rendergraph::typeOf() { + return Type::Vector3D; +} + +template<> +Type rendergraph::typeOf() { + return Type::Vector4D; +} + +template<> +Type rendergraph::typeOf() { + return Type::Matrix4x4; +} + +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::Float; +} + +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::UInt; +} + +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::Float; +} +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::Float; +} +template<> +PrimitiveType rendergraph::primitiveTypeOf() { + return PrimitiveType::Float; +} + +template<> +int rendergraph::tupleSizeOf() { + return 1; +} +template<> +int rendergraph::tupleSizeOf() { + return 1; +} +template<> +int rendergraph::tupleSizeOf() { + return 2; +} +template<> +int rendergraph::tupleSizeOf() { + return 3; +} +template<> +int rendergraph::tupleSizeOf() { + return 4; +} diff --git a/src/rendergraph/examples/CMakeLists.txt b/src/rendergraph/examples/CMakeLists.txt new file mode 100644 index 00000000000..fb5b7c40e5d --- /dev/null +++ b/src/rendergraph/examples/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.16) +project(rendergraph LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools) + +if(NOT(QT_VERSION VERSION_LESS "6.3.0")) + qt_standard_project_setup() +endif() + +add_subdirectory(../ rendergraph) + +target_compile_definitions(rendergraph_gl PUBLIC + $<$:MIXXX_DEBUG_ASSERTIONS_ENABLED> +) +target_compile_definitions(rendergraph_sg PUBLIC + $<$:MIXXX_DEBUG_ASSERTIONS_ENABLED> +) + +add_subdirectory(gl_example) +add_subdirectory(sg_example) diff --git a/src/rendergraph/examples/README b/src/rendergraph/examples/README new file mode 100644 index 00000000000..6168efc09c4 --- /dev/null +++ b/src/rendergraph/examples/README @@ -0,0 +1,6 @@ +Build examples with + +$ cd ../../../ +$ mkdir build-rendergraph_examples +$ cd build-rendergraph_examples +$ cmake -DCMAKE_PREFIX_PATH=~/Qt/6.7.2/macos/ ../src/rendergraph/examples diff --git a/src/rendergraph/examples/common/examplenode.cpp b/src/rendergraph/examples/common/examplenode.cpp new file mode 100644 index 00000000000..8af058ad3a2 --- /dev/null +++ b/src/rendergraph/examples/common/examplenode.cpp @@ -0,0 +1,52 @@ +#include "examplenode.h" + +#include +#include +#include + +#include "rendergraph/geometry.h" +#include "rendergraph/material/rgbmaterial.h" +#include "rendergraph/material/texturematerial.h" +#include "rendergraph/material/unicolormaterial.h" +#include "rendergraph/vertexupdaters/rgbvertexupdater.h" +#include "rendergraph/vertexupdaters/texturedvertexupdater.h" +#include "rendergraph/vertexupdaters/vertexupdater.h" + +using namespace rendergraph; + +ExampleNode::ExampleNode(rendergraph::Context* pContext) { + { + auto pNode = std::make_unique(); + pNode->initForRectangles(1); + auto& material = dynamic_cast(pNode->material()); + material.setTexture(std::make_unique( + pContext, QImage(":/example/images/test.png"))); + TexturedVertexUpdater vertexUpdater{ + pNode->geometry().vertexDataAs()}; + vertexUpdater.addRectangle({0, 0}, {100, 100}, {0.f, 0.f}, {1.f, 1.f}); + appendChildNode(std::move(pNode)); + } + { + auto pNode = std::make_unique(); + pNode->initForRectangles(2); + pNode->material().setUniform(1, QColor(255, 127, 0)); + pNode->geometry().setDrawingMode(DrawingMode::Triangles); + rendergraph::VertexUpdater vertexUpdater{ + pNode->geometry() + .vertexDataAs()}; + vertexUpdater.addRectangle({100, 100}, {160, 160}); + vertexUpdater.addRectangle({200, 160}, {240, 190}); + appendChildNode(std::move(pNode)); + } + { + auto pNode = std::make_unique(); + pNode->initForRectangles(2); + pNode->geometry().setDrawingMode(DrawingMode::Triangles); + rendergraph::RGBVertexUpdater vertexUpdater{ + pNode->geometry().vertexDataAs()}; + vertexUpdater.addRectangle({300, 100}, {340, 140}, {1.f, 0.f, 0.5f}); + vertexUpdater.addRectangleHGradient( + {340, 100}, {440, 130}, {0.f, 1.f, 0.5f}, {0.5f, 0.f, 1.f}); + appendChildNode(std::move(pNode)); + } +} diff --git a/src/rendergraph/examples/common/examplenode.h b/src/rendergraph/examples/common/examplenode.h new file mode 100644 index 00000000000..d46ec2979ab --- /dev/null +++ b/src/rendergraph/examples/common/examplenode.h @@ -0,0 +1,12 @@ +#include "rendergraph/geometrynode.h" +#include "rendergraph/node.h" +#include "rendergraph/texture.h" + +namespace rendergraph { +class ExampleNode; +} // namespace rendergraph + +class rendergraph::ExampleNode : public rendergraph::Node { + public: + ExampleNode(rendergraph::Context* pContext); +}; diff --git a/src/rendergraph/examples/gl_example/CMakeLists.txt b/src/rendergraph/examples/gl_example/CMakeLists.txt new file mode 100644 index 00000000000..5a4239d6e6c --- /dev/null +++ b/src/rendergraph/examples/gl_example/CMakeLists.txt @@ -0,0 +1,38 @@ +qt_add_executable(gl_example + window.cpp window.h + main.cpp + ../common/examplenode.cpp +) + +set_target_properties(gl_example PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(gl_example PRIVATE + rendergraph_gl +) +target_include_directories(gl_example PRIVATE ../common) +target_compile_definitions(gl_example PRIVATE rendergraph=rendergraph_gl) + +qt_add_resources(gl_example "gl_example" + PREFIX + /example + FILES + images/test.png +) + +install(TARGETS gl_example + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +if(NOT(QT_VERSION VERSION_LESS "6.3.0")) +qt_generate_deploy_app_script( + TARGET gl_example + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR +) +install(SCRIPT ${deploy_script}) +endif() diff --git a/src/rendergraph/examples/gl_example/images/test.png b/src/rendergraph/examples/gl_example/images/test.png new file mode 100644 index 00000000000..f9580346eb8 Binary files /dev/null and b/src/rendergraph/examples/gl_example/images/test.png differ diff --git a/src/rendergraph/examples/gl_example/main.cpp b/src/rendergraph/examples/gl_example/main.cpp new file mode 100644 index 00000000000..7b62238b429 --- /dev/null +++ b/src/rendergraph/examples/gl_example/main.cpp @@ -0,0 +1,12 @@ +#include + +#include "window.h" + +int main(int argc, char* argv[]) { + QGuiApplication app(argc, argv); + + Window window; + window.show(); + + return app.exec(); +} diff --git a/src/rendergraph/examples/gl_example/window.cpp b/src/rendergraph/examples/gl_example/window.cpp new file mode 100644 index 00000000000..de643ee8ba7 --- /dev/null +++ b/src/rendergraph/examples/gl_example/window.cpp @@ -0,0 +1,37 @@ +#include "window.h" + +#include "examplenode.h" +#include "rendergraph/context.h" +#include "rendergraph/engine.h" + +Window::Window() { + resize(640, 480); +} + +void Window::closeEvent(QCloseEvent*) { + // since this is the only and last window, we need to cleanup before destruction, + // because at destruction the context can't be used anymore + m_pEngine.reset(); +} + +void Window::initializeGL() { + rendergraph::Context context; + + auto pExampleNode = std::make_unique(&context); + m_pEngine = std::make_unique(std::move(pExampleNode)); +} + +void Window::resizeGL(int w, int h) { + m_pEngine->resize(w, h); +} + +void Window::paintGL() { + glClearColor(0.f, 0.f, 1.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_BLEND); + // qt scene graph uses premultiplied alpha color in the shader, + // so we need to do the same + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + m_pEngine->render(); +} diff --git a/src/rendergraph/examples/gl_example/window.h b/src/rendergraph/examples/gl_example/window.h new file mode 100644 index 00000000000..8c2a5646bef --- /dev/null +++ b/src/rendergraph/examples/gl_example/window.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#include "rendergraph/engine.h" + +namespace rendergraph { +class Graph; +} + +class Window : public QOpenGLWindow { + public: + Window(); + + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + void closeEvent(QCloseEvent* ev) override; + + private: + std::unique_ptr m_pEngine; +}; diff --git a/src/rendergraph/examples/sg_example/CMakeLists.txt b/src/rendergraph/examples/sg_example/CMakeLists.txt new file mode 100644 index 00000000000..303987868d9 --- /dev/null +++ b/src/rendergraph/examples/sg_example/CMakeLists.txt @@ -0,0 +1,44 @@ +qt_add_executable(sg_example WIN32 MACOSX_BUNDLE + customitem.cpp + customitem.h + main.cpp + ../common/examplenode.cpp +) + +target_link_libraries(sg_example PRIVATE + rendergraph_sg +) +target_include_directories(sg_example PRIVATE ../common) +target_compile_definitions(sg_example PRIVATE rendergraph=rendergraph_sg) + +if(QT_VERSION VERSION_LESS "6.3.0") + set_target_properties(sg_example PROPERTIES AUTOMOC ON) +endif() + +qt_add_qml_module(sg_example + VERSION 1.0 + URI RenderGraph + QML_FILES + qml/main.qml + RESOURCES + images/test.png + RESOURCE_PREFIX /example + NO_RESOURCE_TARGET_PATH +) + +install(TARGETS sg_example + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +if(NOT(QT_VERSION VERSION_LESS "6.3.0")) + qt_generate_deploy_qml_app_script( + TARGET sg_example + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM + ) + install(SCRIPT ${deploy_script}) +endif() diff --git a/src/rendergraph/examples/sg_example/customitem.cpp b/src/rendergraph/examples/sg_example/customitem.cpp new file mode 100644 index 00000000000..720e6723278 --- /dev/null +++ b/src/rendergraph/examples/sg_example/customitem.cpp @@ -0,0 +1,62 @@ +#include "customitem.h" + +#include +#include +#include +#include +#include +#include + +#include "examplenode.h" +#include "rendergraph/context.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/material/unicolormaterial.h" +#include "rendergraph/node.h" +#include "rendergraph/vertexupdaters/vertexupdater.h" + +CustomItem::CustomItem(QQuickItem* parent) + : QQuickItem(parent) { + setFlag(ItemHasContents, true); +} + +CustomItem::~CustomItem() = default; + +void CustomItem::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) { + m_geometryChanged = true; + update(); + QQuickItem::geometryChange(newGeometry, oldGeometry); +} + +QSGNode* CustomItem::updatePaintNode(QSGNode* node, UpdatePaintNodeData*) { + QSGRectangleNode* bgNode; + if (!node) { + bgNode = window()->createRectangleNode(); + bgNode->setColor(QColor(0, 0, 255, 255)); + bgNode->setRect(boundingRect()); + + QSGGeometry* geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 3); + + geometry->vertexDataAsPoint2D()[0].set(10, 10); + geometry->vertexDataAsPoint2D()[1].set(100, 10); + geometry->vertexDataAsPoint2D()[2].set(10, 100); + + QSGFlatColorMaterial* material = new QSGFlatColorMaterial; + material->setColor(QColor(255, 0, 0)); + + rendergraph::Context context(window()); + auto pExampleNode = std::make_unique(&context); + + bgNode->appendChildNode(pExampleNode.release()); + + node = bgNode; + } else { + bgNode = static_cast(node); + } + + if (m_geometryChanged) { + bgNode->setRect(boundingRect()); + m_geometryChanged = false; + } + + return node; +} diff --git a/src/rendergraph/examples/sg_example/customitem.h b/src/rendergraph/examples/sg_example/customitem.h new file mode 100644 index 00000000000..87938f41653 --- /dev/null +++ b/src/rendergraph/examples/sg_example/customitem.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "rendergraph/node.h" + +class CustomItem : public QQuickItem { + Q_OBJECT + QML_ELEMENT + + public: + explicit CustomItem(QQuickItem* parent = nullptr); + ~CustomItem() override; + + protected: + QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override; + void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; + + bool m_geometryChanged{}; +}; diff --git a/src/rendergraph/examples/sg_example/images/test.png b/src/rendergraph/examples/sg_example/images/test.png new file mode 100644 index 00000000000..f9580346eb8 Binary files /dev/null and b/src/rendergraph/examples/sg_example/images/test.png differ diff --git a/src/rendergraph/examples/sg_example/main.cpp b/src/rendergraph/examples/sg_example/main.cpp new file mode 100644 index 00000000000..ab2e5b34072 --- /dev/null +++ b/src/rendergraph/examples/sg_example/main.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +#include "customitem.h" + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///example/qml/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/src/rendergraph/examples/sg_example/qml/main.qml b/src/rendergraph/examples/sg_example/qml/main.qml new file mode 100644 index 00000000000..22404c8553c --- /dev/null +++ b/src/rendergraph/examples/sg_example/qml/main.qml @@ -0,0 +1,20 @@ +import QtQuick +import RenderGraph + +Item { + id: root + + width: 680 + height: 520 + + CustomItem { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 20 + anchors.bottomMargin: 20 + anchors.leftMargin: 20 + anchors.rightMargin: 20 + } +} diff --git a/src/rendergraph/opengl/CMakeLists.txt b/src/rendergraph/opengl/CMakeLists.txt new file mode 100644 index 00000000000..d6ca282017b --- /dev/null +++ b/src/rendergraph/opengl/CMakeLists.txt @@ -0,0 +1,87 @@ +add_library(rendergraph_gl +../common/rendergraph/attributeinit.h +../common/rendergraph/attributeset.h +../common/rendergraph/geometry.h +../common/rendergraph/geometrynode.h +../common/rendergraph/material.h +../common/rendergraph/material/endoftrackmaterial.cpp +../common/rendergraph/material/endoftrackmaterial.h +../common/rendergraph/material/patternmaterial.cpp +../common/rendergraph/material/patternmaterial.h +../common/rendergraph/material/rgbamaterial.cpp +../common/rendergraph/material/rgbamaterial.h +../common/rendergraph/material/rgbmaterial.cpp +../common/rendergraph/material/rgbmaterial.h +../common/rendergraph/material/texturematerial.cpp +../common/rendergraph/material/texturematerial.h +../common/rendergraph/material/unicolormaterial.cpp +../common/rendergraph/material/unicolormaterial.h +../common/rendergraph/materialshader.h +../common/rendergraph/materialtype.h +../common/rendergraph/node.h +../common/rendergraph/opacitynode.h +../common/rendergraph/texture.h +../common/rendergraph/types.h +../common/rendergraph/uniform.h +../common/rendergraph/uniformscache.cpp +../common/rendergraph/uniformscache.h +../common/rendergraph/uniformset.cpp +../common/rendergraph/uniformset.h +../common/types.cpp +attributeset.cpp +backend/baseattributeset.cpp +backend/baseattributeset.h +backend/basegeometry.cpp +backend/basegeometry.h +backend/basegeometrynode.cpp +backend/basegeometrynode.h +backend/basematerial.cpp +backend/basematerial.h +backend/basematerialshader.cpp +backend/basematerialtype.h +backend/basenode.h +backend/basenode.cpp +backend/baseopacitynode.h +backend/baseopenglnode.cpp +backend/baseopenglnode.h +backend/basetexture.h +backend/shadercache.h +engine.cpp +geometry.cpp +geometrynode.cpp +material.cpp +materialshader.cpp +rendergraph/context.h +rendergraph/engine.h +rendergraph/openglnode.h +texture.cpp +) + +target_link_libraries(rendergraph_gl PUBLIC + Qt6::Core + Qt6::Gui + Qt6::OpenGL +) +if(WIN32) + find_package(Microsoft.GSL CONFIG) + if(Microsoft.GSL_FOUND) + target_link_libraries(rendergraph_gl PRIVATE Microsoft.GSL::GSL) + else() + # check if the headers have been installed without cmake config (< 3.1.0) + check_include_file_cxx(gsl/gsl HAVE_GSL_GSL) + if(NOT HAVE_GSL_GSL) + unset(HAVE_GSL_GSL CACHE) # unset cache to re-evaluate this until it succeeds. check_include_file_cxx() has no REQUIRED flag. + message(FATAL_ERROR "ms-gsl development headers (libmsgsl-dev) not found") + endif() + endif() +endif() +target_compile_definitions(rendergraph_gl PRIVATE rendergraph=rendergraph_gl) + +# USE_QSHADER_FOR_GL is set in rendergraph/CMakeLists.txt +if(USE_QSHADER_FOR_GL) + message(STATUS "Using QShader to load qsb shaders for opengl") + target_link_libraries(rendergraph_gl PUBLIC Qt6::GuiPrivate) + target_compile_definitions(rendergraph_gl PRIVATE USE_QSHADER_FOR_GL) +endif() + +target_include_directories(rendergraph_gl PUBLIC . ../common) diff --git a/src/rendergraph/opengl/attributeset.cpp b/src/rendergraph/opengl/attributeset.cpp new file mode 100644 index 00000000000..302c2ad5c9c --- /dev/null +++ b/src/rendergraph/opengl/attributeset.cpp @@ -0,0 +1,8 @@ +#include "rendergraph/attributeset.h" + +using namespace rendergraph; + +AttributeSet::AttributeSet(std::initializer_list list, + const std::vector& names) + : BaseAttributeSet(list, names) { +} diff --git a/src/rendergraph/opengl/backend/baseattributeset.cpp b/src/rendergraph/opengl/backend/baseattributeset.cpp new file mode 100644 index 00000000000..87e1ea333d0 --- /dev/null +++ b/src/rendergraph/opengl/backend/baseattributeset.cpp @@ -0,0 +1,18 @@ +#include "rendergraph/assert.h" +#include "rendergraph/attributeset.h" + +using namespace rendergraph; + +BaseAttributeSet::BaseAttributeSet(std::initializer_list list, + const std::vector& names) { + DEBUG_ASSERT(list.size() == names.size()); + int i = 0; + int offset = 0; + m_attributes.reserve(list.size()); + for (auto item : list) { + m_attributes.push_back(BaseGeometry::Attribute{ + offset, item.m_tupleSize, item.m_primitiveType, names[i++]}); + offset += item.m_tupleSize * sizeOf(item.m_primitiveType); + } + m_sizeOfVertex = offset; +} diff --git a/src/rendergraph/opengl/backend/baseattributeset.h b/src/rendergraph/opengl/backend/baseattributeset.h new file mode 100644 index 00000000000..c2c1c2cda2a --- /dev/null +++ b/src/rendergraph/opengl/backend/baseattributeset.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "backend/basegeometry.h" +#include "rendergraph/attributeinit.h" + +namespace rendergraph { +class BaseAttributeSet; +} + +class rendergraph::BaseAttributeSet { + protected: + BaseAttributeSet(std::initializer_list list, const std::vector& names); + + public: + const std::vector& attributes() const { + return m_attributes; + } + int sizeOfVertex() const { + return m_sizeOfVertex; + } + + protected: + std::vector m_attributes; + int m_sizeOfVertex; +}; diff --git a/src/rendergraph/opengl/backend/basegeometry.cpp b/src/rendergraph/opengl/backend/basegeometry.cpp new file mode 100644 index 00000000000..09854386dbe --- /dev/null +++ b/src/rendergraph/opengl/backend/basegeometry.cpp @@ -0,0 +1,21 @@ +#include "backend/basegeometry.h" + +#include "backend/baseattributeset.h" +#include "rendergraph/geometry.h" + +using namespace rendergraph; + +namespace { +// to mimic sg default +constexpr auto defaultDrawingMode = DrawingMode::TriangleStrip; +} // namespace + +BaseGeometry::BaseGeometry( + const BaseAttributeSet& attributeSet, int vertexCount) + : m_pAttributes(attributeSet.attributes().data()), + m_attributeCount(attributeSet.attributes().size()), + m_sizeOfVertex(attributeSet.sizeOfVertex()), + m_drawingMode(defaultDrawingMode), + m_vertexCount(vertexCount), + m_vertexData(m_vertexCount * m_sizeOfVertex / sizeof(float)) { +} diff --git a/src/rendergraph/opengl/backend/basegeometry.h b/src/rendergraph/opengl/backend/basegeometry.h new file mode 100644 index 00000000000..b2e26a32b42 --- /dev/null +++ b/src/rendergraph/opengl/backend/basegeometry.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +#include "rendergraph/types.h" + +namespace rendergraph { +class BaseAttributeSet; // fwd decl to avoid circular dependency +class BaseGeometry; +} + +// TODO this assumes all vertices consist of floats +class rendergraph::BaseGeometry { + protected: + BaseGeometry(const BaseAttributeSet& attributeSet, int vertexCount); + + public: + struct Attribute { + const int m_offset; + const int m_tupleSize; + const PrimitiveType m_primitiveType; + const QString m_name; + }; + + float* vertexData() { + return m_vertexData.data(); + } + const float* vertexData() const { + return m_vertexData.data(); + } + const Attribute* attributes() const { + return m_pAttributes; + } + int attributeCount() const { + return m_attributeCount; + } + int vertexCount() const { + return m_vertexCount; + } + int sizeOfVertex() const { // in bytes + return m_sizeOfVertex; + } + void allocate(int vertexCount) { + m_vertexCount = vertexCount; + m_vertexData.resize(m_vertexCount * sizeOfVertex() / sizeof(float)); + } + + protected: + const Attribute* m_pAttributes; + const int m_attributeCount; + const int m_sizeOfVertex; + DrawingMode m_drawingMode; + int m_vertexCount; + std::vector m_vertexData; +}; diff --git a/src/rendergraph/opengl/backend/basegeometrynode.cpp b/src/rendergraph/opengl/backend/basegeometrynode.cpp new file mode 100644 index 00000000000..a4b2e303f86 --- /dev/null +++ b/src/rendergraph/opengl/backend/basegeometrynode.cpp @@ -0,0 +1,112 @@ +#include "backend/basegeometrynode.h" + +#include + +#include "backend/shadercache.h" +#include "rendergraph/engine.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/texture.h" + +using namespace rendergraph; + +namespace { +GLenum toGlDrawingMode(DrawingMode mode) { + switch (mode) { + case DrawingMode::Triangles: + return GL_TRIANGLES; + case DrawingMode::TriangleStrip: + return GL_TRIANGLE_STRIP; + } +} +} // namespace + +void BaseGeometryNode::initialize() { + initializeOpenGLFunctions(); + GeometryNode* pThis = static_cast(this); + pThis->material().setShader(ShaderCache::getShaderForMaterial(&pThis->material())); + pThis->material().setUniform(0, engine()->matrix()); +} + +void BaseGeometryNode::render() { + GeometryNode* pThis = static_cast(this); + Geometry& geometry = pThis->geometry(); + Material& material = pThis->material(); + + if (geometry.vertexCount() == 0) { + return; + } + + glEnable(GL_BLEND); + // qt scene graph uses premultiplied alpha color in the shader, + // so we need to do the same + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + QOpenGLShaderProgram& shader = material.shader(); + shader.bind(); + + if (material.clearUniformsCacheDirty() || !material.isLastModifierOfShader()) { + material.modifyShader(); + const UniformsCache& cache = material.uniformsCache(); + for (int i = 0; i < cache.count(); i++) { + int location = material.uniformLocation(i); + switch (cache.type(i)) { + case Type::UInt: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Float: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Vector2D: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Vector3D: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Vector4D: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Matrix4x4: + shader.setUniformValue(location, cache.get(i)); + break; + } + } + } + + // TODO this code assumes all vertices are floats + int vertexOffset = 0; + for (int i = 0; i < geometry.attributeCount(); i++) { + const Geometry::Attribute& attribute = geometry.attributes()[i]; + int location = material.attributeLocation(i); + shader.enableAttributeArray(location); + shader.setAttributeArray(location, + geometry.vertexDataAs() + vertexOffset, + attribute.m_tupleSize, + geometry.sizeOfVertex()); + vertexOffset += attribute.m_tupleSize; + } + + // TODO multiple textures + auto pTexture = material.texture(1); + if (pTexture) { + pTexture->backendTexture()->bind(); + } + + glDrawArrays(toGlDrawingMode(geometry.drawingMode()), 0, geometry.vertexCount()); + + if (pTexture) { + pTexture->backendTexture()->release(); + } + + for (int i = 0; i < geometry.attributeCount(); i++) { + int location = material.attributeLocation(i); + shader.disableAttributeArray(location); + } + + shader.release(); +} + +void BaseGeometryNode::resize(int, int) { + assert(engine() != nullptr); + GeometryNode* pThis = static_cast(this); + pThis->material().setUniform(0, engine()->matrix()); +} diff --git a/src/rendergraph/opengl/backend/basegeometrynode.h b/src/rendergraph/opengl/backend/basegeometrynode.h new file mode 100644 index 00000000000..a4a7f609c4a --- /dev/null +++ b/src/rendergraph/opengl/backend/basegeometrynode.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "backend/basenode.h" + +namespace rendergraph { +class BaseGeometryNode; +} + +class rendergraph::BaseGeometryNode : public rendergraph::BaseNode, + public QOpenGLFunctions { + public: + BaseGeometryNode() = default; + virtual ~BaseGeometryNode() = default; + + // called by Engine + void initialize() override; + void render() override; + void resize(int w, int h) override; +}; diff --git a/src/rendergraph/opengl/backend/basematerial.cpp b/src/rendergraph/opengl/backend/basematerial.cpp new file mode 100644 index 00000000000..ae6817ce878 --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerial.cpp @@ -0,0 +1,34 @@ +#include "backend/basematerial.h" + +#include "rendergraph/material.h" + +using namespace rendergraph; + +void BaseMaterial::setShader(std::shared_ptr pShader) { + m_pShader = pShader; +} + +MaterialShader& BaseMaterial::shader() const { + return *m_pShader; +} + +int BaseMaterial::uniformLocation(int uniformIndex) const { + return m_pShader->uniformLocation(uniformIndex); +} + +int BaseMaterial::attributeLocation(int attributeIndex) const { + return m_pShader->attributeLocation(attributeIndex); +} + +void BaseMaterial::modifyShader() { + m_pShader->setLastModifiedByMaterial(this); +} + +bool BaseMaterial::isLastModifierOfShader() const { + return this == m_pShader->lastModifiedByMaterial(); +} + +int BaseMaterial::compare(const BaseMaterial* other) const { + auto pThis = static_cast(this); + return pThis->compare(static_cast(other)); +} diff --git a/src/rendergraph/opengl/backend/basematerial.h b/src/rendergraph/opengl/backend/basematerial.h new file mode 100644 index 00000000000..6f2a22ccba2 --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerial.h @@ -0,0 +1,32 @@ +#pragma once + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +namespace rendergraph { +class BaseMaterial; +} + +class rendergraph::BaseMaterial { + protected: + BaseMaterial() = default; + + public: + virtual MaterialType* type() const = 0; + + // For parity with QSGMaterial, not used yet + int compare(const BaseMaterial* other) const; + + void setShader(std::shared_ptr pShader); + + MaterialShader& shader() const; + + int uniformLocation(int uniformIndex) const; + int attributeLocation(int attributeIndex) const; + + void modifyShader(); + bool isLastModifierOfShader() const; + + private: + std::shared_ptr m_pShader; +}; diff --git a/src/rendergraph/opengl/backend/basematerialshader.cpp b/src/rendergraph/opengl/backend/basematerialshader.cpp new file mode 100644 index 00000000000..498ed8fbe4f --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerialshader.cpp @@ -0,0 +1,19 @@ +#include "backend/basematerialshader.h" + +using namespace rendergraph; + +int BaseMaterialShader::attributeLocation(int attributeIndex) const { + return m_attributeLocations[attributeIndex]; +} + +int BaseMaterialShader::uniformLocation(int uniformIndex) const { + return m_uniformLocations[uniformIndex]; +} + +BaseMaterial* BaseMaterialShader::lastModifiedByMaterial() const { + return m_pLastModifiedByMaterial; +} + +void BaseMaterialShader::setLastModifiedByMaterial(BaseMaterial* pMaterial) { + m_pLastModifiedByMaterial = pMaterial; +} diff --git a/src/rendergraph/opengl/backend/basematerialshader.h b/src/rendergraph/opengl/backend/basematerialshader.h new file mode 100644 index 00000000000..f5e1b3e195e --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerialshader.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class BaseMaterial; // fwd decl to avoid circular dependency +class BaseMaterialShader; +} // namespace rendergraph + +class rendergraph::BaseMaterialShader : public QOpenGLShaderProgram { + protected: + BaseMaterialShader() = default; + + public: + int attributeLocation(int attributeIndex) const; + int uniformLocation(int uniformIndex) const; + + BaseMaterial* lastModifiedByMaterial() const; + void setLastModifiedByMaterial(BaseMaterial* pMaterial); + + protected: + std::vector m_attributeLocations; + std::vector m_uniformLocations; + BaseMaterial* m_pLastModifiedByMaterial{}; +}; diff --git a/src/rendergraph/opengl/backend/basematerialtype.h b/src/rendergraph/opengl/backend/basematerialtype.h new file mode 100644 index 00000000000..50b925d0bf4 --- /dev/null +++ b/src/rendergraph/opengl/backend/basematerialtype.h @@ -0,0 +1,6 @@ +#pragma once + +namespace rendergraph { +class BaseMaterialType { +}; +} // namespace rendergraph diff --git a/src/rendergraph/opengl/backend/basenode.cpp b/src/rendergraph/opengl/backend/basenode.cpp new file mode 100644 index 00000000000..a413325ee29 --- /dev/null +++ b/src/rendergraph/opengl/backend/basenode.cpp @@ -0,0 +1,62 @@ +#include "backend/basenode.h" + +#include "rendergraph/assert.h" +#include "rendergraph/engine.h" + +using namespace rendergraph; + +BaseNode::~BaseNode() { + DEBUG_ASSERT(m_pParent == nullptr); + DEBUG_ASSERT(m_pEngine == nullptr); + + while (m_pFirstChild) { + auto pChild = m_pFirstChild; + removeChildNode(pChild); + delete pChild; + } +} + +// This mimics QSGNode::appendChildNode. +// Use NodeInterface::appendChildNode(std::unique_ptr pNode) +// for a more clear transfer of ownership. pChild is considered owned by +// this at this point. +void BaseNode::appendChildNode(BaseNode* pChild) { + if (m_pLastChild) { + pChild->m_pPreviousSibling = m_pLastChild; + m_pLastChild->m_pNextSibling = pChild; + } else { + m_pFirstChild = pChild; + } + m_pLastChild = pChild; + m_pLastChild->m_pParent = this; + + DEBUG_ASSERT(m_pLastChild->m_pNextSibling == nullptr); + + if (m_pEngine) { + m_pEngine->add(pChild); + } +} + +// This mimics QSGNode::removeChildNode. +// Use NodeInterface::detachChildNode(BaseNode* pNode) +// for a more clear transfer of ownership. Otherwise, +// deleting pChild is responsibility of the caller. +void BaseNode::removeChildNode(BaseNode* pChild) { + if (pChild == m_pFirstChild) { + m_pFirstChild = pChild->m_pNextSibling; + } else { + pChild->m_pPreviousSibling->m_pNextSibling = pChild->m_pNextSibling; + } + + if (pChild == m_pLastChild) { + m_pLastChild = nullptr; + } + + if (pChild->m_pEngine) { + pChild->m_pEngine->remove(pChild); + } + + pChild->m_pNextSibling = nullptr; + pChild->m_pPreviousSibling = nullptr; + pChild->m_pParent = nullptr; +} diff --git a/src/rendergraph/opengl/backend/basenode.h b/src/rendergraph/opengl/backend/basenode.h new file mode 100644 index 00000000000..b0079954697 --- /dev/null +++ b/src/rendergraph/opengl/backend/basenode.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +namespace rendergraph { +class BaseNode; +class Engine; +} // namespace rendergraph + +class rendergraph::BaseNode { + public: + BaseNode() = default; + virtual ~BaseNode(); + + void setUsePreprocess(bool value) { + m_usePreprocess = value; + } + bool usePreprocess() const { + return m_usePreprocess; + } + virtual bool isSubtreeBlocked() const { + return false; + } + virtual void preprocess() { + } + virtual void render() { + } + virtual void initialize() { + } + virtual void resize(int, int) { + } + void setEngine(Engine* engine) { + m_pEngine = engine; + } + Engine* engine() const { + return m_pEngine; + } + + // Prefer using NodeInterface::appendChildNode(std::unique_ptr pNode); + void appendChildNode(BaseNode* pChild); + // Prefer using std::unique_ptr NodeInterface::detachChildNode(BaseNode* pNode); + void removeChildNode(BaseNode* pChild); + + BaseNode* parent() const { + return m_pParent; + } + BaseNode* firstChild() const { + return m_pFirstChild; + } + BaseNode* lastChild() const { + return m_pLastChild; + } + BaseNode* nextSibling() const { + return m_pNextSibling; + } + BaseNode* previousSibling() const { + return m_pPreviousSibling; + } + + private: + Engine* m_pEngine{}; + bool m_usePreprocess{}; + + // Mimicking scenegraph node hierarchy. A parent owns its children. + BaseNode* m_pParent{}; + BaseNode* m_pFirstChild{}; + BaseNode* m_pLastChild{}; + BaseNode* m_pNextSibling{}; + BaseNode* m_pPreviousSibling{}; +}; diff --git a/src/rendergraph/opengl/backend/baseopacitynode.h b/src/rendergraph/opengl/backend/baseopacitynode.h new file mode 100644 index 00000000000..1bd11da2fe0 --- /dev/null +++ b/src/rendergraph/opengl/backend/baseopacitynode.h @@ -0,0 +1,23 @@ +#pragma once + +#include "backend/basenode.h" + +namespace rendergraph { +class BaseOpacityNode; +} // namespace rendergraph + +class rendergraph::BaseOpacityNode : public rendergraph::BaseNode { + public: + BaseOpacityNode() = default; + virtual ~BaseOpacityNode() = default; + + void setOpacity(float opacity) { + m_opacity = opacity; + } + bool isSubtreeBlocked() const override { + return m_opacity == 0.f; + } + + private: + float m_opacity{1.f}; +}; diff --git a/src/rendergraph/opengl/backend/baseopenglnode.cpp b/src/rendergraph/opengl/backend/baseopenglnode.cpp new file mode 100644 index 00000000000..13c3bdfc783 --- /dev/null +++ b/src/rendergraph/opengl/backend/baseopenglnode.cpp @@ -0,0 +1,18 @@ +#include "backend/baseopenglnode.h" + +#include "rendergraph/openglnode.h" + +using namespace rendergraph; + +void BaseOpenGLNode::initialize() { + initializeOpenGLFunctions(); + initializeGL(); +} + +void BaseOpenGLNode::render() { + paintGL(); +} + +void BaseOpenGLNode::resize(int w, int h) { + resizeGL(w, h); +} diff --git a/src/rendergraph/opengl/backend/baseopenglnode.h b/src/rendergraph/opengl/backend/baseopenglnode.h new file mode 100644 index 00000000000..545f5381630 --- /dev/null +++ b/src/rendergraph/opengl/backend/baseopenglnode.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "backend/basenode.h" + +namespace rendergraph { +class BaseOpenGLNode; +} // namespace rendergraph + +class rendergraph::BaseOpenGLNode : public rendergraph::BaseNode, + public QOpenGLFunctions { + public: + BaseOpenGLNode() = default; + virtual ~BaseOpenGLNode() = default; + + void initialize() override; + void render() override; + void resize(int w, int h) override; + + virtual void initializeGL() { + } + virtual void paintGL() { + } + virtual void resizeGL(int, int) { + } +}; diff --git a/src/rendergraph/opengl/backend/basetexture.h b/src/rendergraph/opengl/backend/basetexture.h new file mode 100644 index 00000000000..1a47541fe24 --- /dev/null +++ b/src/rendergraph/opengl/backend/basetexture.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseTexture = QOpenGLTexture; +} // namespace rendergraph diff --git a/src/rendergraph/opengl/backend/shadercache.h b/src/rendergraph/opengl/backend/shadercache.h new file mode 100644 index 00000000000..557a0b6a582 --- /dev/null +++ b/src/rendergraph/opengl/backend/shadercache.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include "rendergraph/material.h" +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +namespace rendergraph { +class ShaderCache; +} // namespace rendergraph + +class rendergraph::ShaderCache { + private: + static std::unordered_map>& + map() { + static std::unordered_map> + s_map; + return s_map; + } + + public: + static std::shared_ptr getShaderForMaterial( + Material* pMaterial) { + auto iter = map().find(pMaterial->type()); + if (iter != map().end()) { + return iter->second; + } + auto pResult = std::shared_ptr( + pMaterial->createShader()); + map().insert(std::pair>{ + pMaterial->type(), pResult}); + return pResult; + } + static void purge() { + std::erase_if(map(), [](const auto& item) { + auto const& [key, value] = item; + return value.use_count() == 1; + }); + } +}; diff --git a/src/rendergraph/opengl/engine.cpp b/src/rendergraph/opengl/engine.cpp new file mode 100644 index 00000000000..f8188375e36 --- /dev/null +++ b/src/rendergraph/opengl/engine.cpp @@ -0,0 +1,98 @@ +#include "rendergraph/engine.h" + +#include +#include + +using namespace rendergraph; + +Engine::Engine(std::unique_ptr pRootNode) + : m_pRootNode(std::move(pRootNode)) { + add(m_pRootNode.get()); +} + +Engine::~Engine() { + // Explicitly remove the root node (and tree from the engine before deallocating its vectors) + remove(m_pRootNode.get()); +} + +void Engine::add(BaseNode* pNode) { + assert(pNode->engine() == nullptr || pNode->engine() == this); + if (pNode->engine() == nullptr) { + pNode->setEngine(this); + m_pInitializeNodes.push_back(pNode); + if (pNode->usePreprocess()) { + m_pPreprocessNodes.push_back(pNode); + } + pNode = pNode->firstChild(); + while (pNode) { + add(pNode); + pNode = pNode->nextSibling(); + } + } +} + +void Engine::remove(BaseNode* pNode) { + assert(pNode->engine() == this); + pNode->setEngine(nullptr); + + std::erase(m_pInitializeNodes, pNode); + std::erase(m_pPreprocessNodes, pNode); + + if (m_pRootNode.get() == pNode) { + m_pRootNode.reset(); + } +} + +void Engine::render() { + if (!m_pInitializeNodes.empty()) { + for (auto pNode : m_pInitializeNodes) { + pNode->initialize(); + } + m_pInitializeNodes.clear(); + } + if (m_pRootNode && !m_pRootNode->isSubtreeBlocked()) { + render(m_pRootNode.get()); + } +} + +void Engine::render(BaseNode* pNode) { + pNode->render(); + pNode = pNode->firstChild(); + while (pNode) { + if (!pNode->isSubtreeBlocked()) { + render(pNode); + } + pNode = pNode->nextSibling(); + } +} + +void Engine::preprocess() { + for (auto pNode : m_pPreprocessNodes) { + if (!pNode->isSubtreeBlocked()) { + pNode->preprocess(); + } + } +} + +void Engine::resize(int w, int h) { + m_matrix.setToIdentity(); + m_matrix.ortho(QRectF(0.0f, 0.0f, w, h)); + // TODO + // if (waveformRenderer->getOrientation() == Qt::Vertical) { + // matrix.rotate(90.f, 0.0f, 0.0f, 1.0f); + // matrix.translate(0.f, -waveformRenderer->getWidth() * ratio, 0.f); + //} + + if (m_pRootNode) { + resize(m_pRootNode.get(), w, h); + } +} + +void Engine::resize(BaseNode* pNode, int w, int h) { + pNode->resize(w, h); + pNode = pNode->firstChild(); + while (pNode) { + resize(pNode, w, h); + pNode = pNode->nextSibling(); + } +} diff --git a/src/rendergraph/opengl/geometry.cpp b/src/rendergraph/opengl/geometry.cpp new file mode 100644 index 00000000000..b2fbfbbcd2a --- /dev/null +++ b/src/rendergraph/opengl/geometry.cpp @@ -0,0 +1,45 @@ +#include "rendergraph/geometry.h" + +#include "rendergraph/assert.h" +#include "rendergraph/attributeset.h" + +using namespace rendergraph; + +Geometry::Geometry(const AttributeSet& attributeSet, int vertexCount) + : BaseGeometry(attributeSet, vertexCount) { +} + +void Geometry::setAttributeValues(int attributePosition, const float* from, int numTuples) { + // TODO this code assumes all vertices are floats + VERIFY_OR_DEBUG_ASSERT(attributePosition < attributeCount()) { + return; + } + const int vertexOffset = attributes()[attributePosition].m_offset / sizeof(float); + const int tupleSize = attributes()[attributePosition].m_tupleSize; + const int vertexStride = sizeOfVertex() / sizeof(float); + const int vertexSkip = vertexStride - tupleSize; + + VERIFY_OR_DEBUG_ASSERT(vertexOffset + numTuples * vertexStride - vertexSkip <= + static_cast(m_vertexData.size())) { + return; + } + + float* to = m_vertexData.data(); + to += vertexOffset; + + while (numTuples--) { + int k = tupleSize; + while (k--) { + *to++ = *from++; + } + to += vertexSkip; + } +} + +void Geometry::setDrawingMode(DrawingMode mode) { + m_drawingMode = mode; +} + +DrawingMode Geometry::drawingMode() const { + return m_drawingMode; +} diff --git a/src/rendergraph/opengl/geometrynode.cpp b/src/rendergraph/opengl/geometrynode.cpp new file mode 100644 index 00000000000..7c5020974bc --- /dev/null +++ b/src/rendergraph/opengl/geometrynode.cpp @@ -0,0 +1,33 @@ +#include "rendergraph/geometrynode.h" + +using namespace rendergraph; + +GeometryNode::GeometryNode() = default; + +void GeometryNode::setUsePreprocess(bool value) { + BaseNode::setUsePreprocess(value); +} + +void GeometryNode::setGeometry(std::unique_ptr pGeometry) { + m_pGeometry = std::move(pGeometry); +} + +void GeometryNode::setMaterial(std::unique_ptr pMaterial) { + m_pMaterial = std::move(pMaterial); +} + +Geometry& GeometryNode::geometry() const { + return *m_pGeometry; +} + +Material& GeometryNode::material() const { + return *m_pMaterial; +} + +void GeometryNode::markDirtyGeometry() { + // not (yet) needed for opengl +} + +void GeometryNode::markDirtyMaterial() { + // not (yet) needed for opengl +} diff --git a/src/rendergraph/opengl/material.cpp b/src/rendergraph/opengl/material.cpp new file mode 100644 index 00000000000..4913e8d5b61 --- /dev/null +++ b/src/rendergraph/opengl/material.cpp @@ -0,0 +1,9 @@ +#include "rendergraph/material.h" + +using namespace rendergraph; + +Material::Material(const UniformSet& uniformSet) + : m_uniformsCache(uniformSet) { +} + +Material::~Material() = default; diff --git a/src/rendergraph/opengl/materialshader.cpp b/src/rendergraph/opengl/materialshader.cpp new file mode 100644 index 00000000000..e8fd233a21d --- /dev/null +++ b/src/rendergraph/opengl/materialshader.cpp @@ -0,0 +1,58 @@ +#include "rendergraph/materialshader.h" + +#include +#ifdef USE_QSHADER_FOR_GL +#include +#endif + +using namespace rendergraph; + +namespace { +#ifdef USE_QSHADER_FOR_GL +QString resource(const QString& filename) { + return QStringLiteral(":/shaders/rendergraph/%1.qsb").arg(filename); +} + +QByteArray loadShaderCodeFromFile(const QString& path) { + QFile file(path); + file.open(QIODeviceBase::ReadOnly); + QShader qsbShader = QShader::fromSerialized(file.readAll()); + QShaderKey key(QShader::GlslShader, 120); + return qsbShader.shader(key).shader(); +} +#else +QString resource(const QString& filename) { + return QStringLiteral(":/shaders/rendergraph/%1.gl").arg(filename); +} + +QByteArray loadShaderCodeFromFile(const QString& path) { + QFile file(path); + file.open(QIODeviceBase::ReadOnly); + return file.readAll(); +} +#endif +} // namespace + +MaterialShader::MaterialShader(const char* vertexShaderFilename, + const char* fragmentShaderFilename, + const UniformSet& uniformSet, + const AttributeSet& attributeSet) { + const QString vertexShaderFileFullPath = resource(vertexShaderFilename); + const QString fragmentShaderFileFullPath = resource(fragmentShaderFilename); + + addShaderFromSourceCode(QOpenGLShader::Vertex, + loadShaderCodeFromFile(vertexShaderFileFullPath)); + addShaderFromSourceCode(QOpenGLShader::Fragment, + loadShaderCodeFromFile(fragmentShaderFileFullPath)); + + link(); + + for (const auto& attribute : attributeSet.attributes()) { + int location = QOpenGLShaderProgram::attributeLocation(attribute.m_name); + m_attributeLocations.push_back(location); + } + for (const auto& uniform : uniformSet.uniforms()) { + int location = QOpenGLShaderProgram::uniformLocation(uniform.m_name); + m_uniformLocations.push_back(location); + } +} diff --git a/src/rendergraph/opengl/rendergraph/context.h b/src/rendergraph/opengl/rendergraph/context.h new file mode 100644 index 00000000000..d5d0611408f --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/context.h @@ -0,0 +1,8 @@ +#pragma once + +namespace rendergraph { +class Context; +} + +class rendergraph::Context { +}; diff --git a/src/rendergraph/opengl/rendergraph/engine.h b/src/rendergraph/opengl/rendergraph/engine.h new file mode 100644 index 00000000000..83e480727e0 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/engine.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +#include "rendergraph/node.h" + +namespace rendergraph { +class Engine; +} // namespace rendergraph + +class rendergraph::Engine { + public: + Engine(std::unique_ptr pRootNode); + ~Engine(); + + void render(); + void resize(int w, int h); + void preprocess(); + void add(BaseNode* pNode); + void remove(BaseNode* pNode); + const QMatrix4x4& matrix() const { + return m_matrix; + } + + private: + void render(BaseNode* pNode); + void resize(BaseNode* pNode, int, int); + + QMatrix4x4 m_matrix; + std::unique_ptr m_pRootNode; + std::vector m_pPreprocessNodes; + std::vector m_pInitializeNodes; +}; diff --git a/src/rendergraph/opengl/rendergraph/nodeinterface.h b/src/rendergraph/opengl/rendergraph/nodeinterface.h new file mode 100644 index 00000000000..45159724c49 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/nodeinterface.h @@ -0,0 +1,31 @@ +#pragma once + +#include "backend/basenode.h" + +namespace rendergraph { + +template +class NodeInterface : public T_Node { + public: + void appendChildNode(std::unique_ptr pNode) { + // Transfers ownership to this. + BaseNode* pRawNode = pNode.release(); + // Note: Ideally we would use unique_ptrs internally, but + // Qt uses raw pointers for QSGNode hierarchy. For simplicity + // we mimic this. + + T_Node::appendChildNode(pRawNode); + } + + std::unique_ptr detachChildNode(BaseNode* pNode) { + // After removeChildNode, the caller has the responsibility + // to deal with the child node. By returning a unique_ptr + // we preoprtly transfer ownership to the caller (which + // can result in deleting pNode if the caller doesn't + // take the unique_ptr). + T_Node::removeChildNode(pNode); + return std::unique_ptr(pNode); + } +}; + +} // namespace rendergraph diff --git a/src/rendergraph/opengl/rendergraph/openglnode.h b/src/rendergraph/opengl/rendergraph/openglnode.h new file mode 100644 index 00000000000..7499312cb08 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/openglnode.h @@ -0,0 +1,7 @@ +#pragma once + +#include "backend/baseopenglnode.h" + +namespace rendergraph { +using OpenGLNode = BaseOpenGLNode; +} // namespace rendergraph diff --git a/src/rendergraph/opengl/texture.cpp b/src/rendergraph/opengl/texture.cpp new file mode 100644 index 00000000000..88921bde9df --- /dev/null +++ b/src/rendergraph/opengl/texture.cpp @@ -0,0 +1,58 @@ +#include "rendergraph/texture.h" + +#include +#include + +#include "rendergraph/assert.h" +#include "rendergraph/context.h" + +using namespace rendergraph; + +namespace { +QImage premultiplyAlpha(const QImage& image) { + // Since the image is passed by const reference, implicit copy cannot be + // used, and this Qimage::bits will return a ref the QImage buffer, which + // may have a shorter lifecycle that the texture buffer. In order to + // workaround this, and because we cannot copy the image as we need to use + // the raw bitmap with an explicit image format, we make a manual copy of + // the buffer + QImage result(image.width(), image.height(), QImage::Format_RGBA8888); + if (image.format() == QImage::Format_RGBA8888_Premultiplied) { + VERIFY_OR_DEBUG_ASSERT(result.sizeInBytes() == image.sizeInBytes()) { + result.fill(QColor(Qt::transparent).rgba()); + return result; + } + std::memcpy(result.bits(), image.bits(), result.sizeInBytes()); + } else { + auto convertedImage = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + VERIFY_OR_DEBUG_ASSERT(result.sizeInBytes() == convertedImage.sizeInBytes()) { + result.fill(QColor(Qt::transparent).rgba()); + return result; + } + std::memcpy(result.bits(), convertedImage.bits(), result.sizeInBytes()); + } + return result; + /* TODO rendergraph ASK @acolombier to try if the following works as well + * (added the .copy()) + if (image.format() == QImage::Format_RGBA8888_Premultiplied) { + return QImage(image.bits(), image.width(), image.height(), QImage::Format_RGBA8888).copy(); + } + return QImage( + image.convertToFormat(QImage::Format_RGBA8888_Premultiplied) + .bits(), + image.width(), + image.height(), + QImage::Format_RGBA8888).copy(); + */ +} +} // namespace + +Texture::Texture(Context*, const QImage& image) + : m_pTexture(std::make_unique(premultiplyAlpha(image))) { + m_pTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); + m_pTexture->setWrapMode(QOpenGLTexture::ClampToEdge); +} + +qint64 Texture::comparisonKey() const { + return static_cast(m_pTexture->textureId()); +} diff --git a/src/rendergraph/scenegraph/CMakeLists.txt b/src/rendergraph/scenegraph/CMakeLists.txt new file mode 100644 index 00000000000..ad96eb1e491 --- /dev/null +++ b/src/rendergraph/scenegraph/CMakeLists.txt @@ -0,0 +1,73 @@ +add_library(rendergraph_sg +../common/rendergraph/attributeinit.h +../common/rendergraph/attributeset.h +../common/rendergraph/geometry.h +../common/rendergraph/geometrynode.h +../common/rendergraph/material.h +../common/rendergraph/material/endoftrackmaterial.cpp +../common/rendergraph/material/endoftrackmaterial.h +../common/rendergraph/material/patternmaterial.cpp +../common/rendergraph/material/patternmaterial.h +../common/rendergraph/material/rgbamaterial.cpp +../common/rendergraph/material/rgbamaterial.h +../common/rendergraph/material/rgbmaterial.cpp +../common/rendergraph/material/rgbmaterial.h +../common/rendergraph/material/texturematerial.cpp +../common/rendergraph/material/texturematerial.h +../common/rendergraph/material/unicolormaterial.cpp +../common/rendergraph/material/unicolormaterial.h +../common/rendergraph/materialshader.h +../common/rendergraph/materialtype.h +../common/rendergraph/node.h +../common/rendergraph/opacitynode.h +../common/rendergraph/texture.h +../common/rendergraph/types.h +../common/rendergraph/uniform.h +../common/rendergraph/uniformscache.cpp +../common/rendergraph/uniformscache.h +../common/rendergraph/uniformset.cpp +../common/rendergraph/uniformset.h +../common/types.cpp +attributeset.cpp +backend/baseattributeset.cpp +backend/baseattributeset.h +backend/basegeometry.h +backend/basegeometrynode.h +backend/basematerial.cpp +backend/basematerial.h +backend/basematerialshader.cpp +backend/basematerialtype.h +backend/basenode.h +backend/baseopacitynode.h +backend/basetexture.h +context.cpp +geometry.cpp +geometrynode.cpp +material.cpp +materialshader.cpp +rendergraph/context.h +texture.cpp +) + +target_link_libraries(rendergraph_sg PUBLIC + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick +) +if(WIN32) + find_package(Microsoft.GSL CONFIG) + if(Microsoft.GSL_FOUND) + target_link_libraries(rendergraph_sg PRIVATE Microsoft.GSL::GSL) + else() + # check if the headers have been installed without cmake config (< 3.1.0) + check_include_file_cxx(gsl/gsl HAVE_GSL_GSL) + if(NOT HAVE_GSL_GSL) + unset(HAVE_GSL_GSL CACHE) # unset cache to re-evaluate this until it succeeds. check_include_file_cxx() has no REQUIRED flag. + message(FATAL_ERROR "ms-gsl development headers (libmsgsl-dev) not found") + endif() + endif() +endif() +target_compile_definitions(rendergraph_sg PRIVATE rendergraph=rendergraph_sg) + +target_include_directories(rendergraph_sg PUBLIC . ../common) diff --git a/src/rendergraph/scenegraph/attributeset.cpp b/src/rendergraph/scenegraph/attributeset.cpp new file mode 100644 index 00000000000..04d55fcae8f --- /dev/null +++ b/src/rendergraph/scenegraph/attributeset.cpp @@ -0,0 +1,10 @@ +#include "rendergraph/attributeset.h" + +#include "backend/baseattributeset.h" + +using namespace rendergraph; + +AttributeSet::AttributeSet(std::initializer_list list, const std::vector&) + : BaseAttributeSet(list) { + // names are not used in scenegraph +} diff --git a/src/rendergraph/scenegraph/backend/baseattributeset.cpp b/src/rendergraph/scenegraph/backend/baseattributeset.cpp new file mode 100644 index 00000000000..0f0bf0b8e7a --- /dev/null +++ b/src/rendergraph/scenegraph/backend/baseattributeset.cpp @@ -0,0 +1,27 @@ +#include "backend/baseattributeset.h" + +using namespace rendergraph; + +namespace { +int toQSGGeometryType(const PrimitiveType& t) { + switch (t) { + case PrimitiveType::Float: + return QSGGeometry::FloatType; + case PrimitiveType::UInt: + return QSGGeometry::UnsignedIntType; + } +} +} // namespace + +BaseAttributeSetHelper::BaseAttributeSetHelper(std::initializer_list list) { + int i = 0; + m_sgAttributes.reserve(list.size()); + for (auto item : list) { + const int count = static_cast(m_sgAttributes.size()); + const bool isPosition = count == 0; + m_sgAttributes.push_back(QSGGeometry::Attribute::create(count, + item.m_tupleSize, + toQSGGeometryType(item.m_primitiveType), + isPosition)); + } +} diff --git a/src/rendergraph/scenegraph/backend/baseattributeset.h b/src/rendergraph/scenegraph/backend/baseattributeset.h new file mode 100644 index 00000000000..7c33e25192a --- /dev/null +++ b/src/rendergraph/scenegraph/backend/baseattributeset.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +#include "rendergraph/attributeinit.h" + +namespace rendergraph { +class BaseAttributeSet; +class BaseAttributeSetHelper; +} // namespace rendergraph + +class rendergraph::BaseAttributeSetHelper { + protected: + BaseAttributeSetHelper(std::initializer_list list); + std::vector m_sgAttributes; +}; + +class rendergraph::BaseAttributeSet + : protected rendergraph::BaseAttributeSetHelper, + public QSGGeometry::AttributeSet { + protected: + BaseAttributeSet(std::initializer_list list) + : BaseAttributeSetHelper(list), + QSGGeometry::AttributeSet{static_cast(m_sgAttributes.size()), + calculateSizeOfVertex(list), + m_sgAttributes.data()} { + } + static int calculateSizeOfVertex(std::initializer_list list) { + int numBytes = 0; + for (auto item : list) { + numBytes += item.m_tupleSize * sizeOf(item.m_primitiveType); + } + return numBytes; + } +}; diff --git a/src/rendergraph/scenegraph/backend/basegeometry.h b/src/rendergraph/scenegraph/backend/basegeometry.h new file mode 100644 index 00000000000..6d15a9f49dd --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basegeometry.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include "rendergraph/attributeset.h" + +namespace rendergraph { +using BaseGeometry = QSGGeometry; +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/backend/basegeometrynode.h b/src/rendergraph/scenegraph/backend/basegeometrynode.h new file mode 100644 index 00000000000..26dd0b323bc --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basegeometrynode.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseGeometryNode = QSGGeometryNode; +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/backend/basematerial.cpp b/src/rendergraph/scenegraph/backend/basematerial.cpp new file mode 100644 index 00000000000..e49606f8147 --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerial.cpp @@ -0,0 +1,28 @@ +#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 00000000000..ac434248d2f --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerial.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace rendergraph { +class BaseMaterial; +} // namespace rendergraph + +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 00000000000..7a49a3d5afd --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerialshader.cpp @@ -0,0 +1,37 @@ +#include "backend/basematerialshader.h" + +#include + +#include "rendergraph/material.h" + +using namespace rendergraph; + +bool BaseMaterialShader::updateUniformData(RenderState& state, + QSGMaterial* newMaterial, + QSGMaterial* oldMaterial) { + bool result = static_cast(newMaterial)->updateUniformsByteArray(state.uniformData()); + QByteArray* buf = state.uniformData(); + + if (state.isMatrixDirty()) { + const QMatrix4x4 m = state.combinedMatrix(); + memcpy(buf->data(), m.constData(), 64); + result = true; + } + + return result; +} + +// 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) { + if (!newMaterial || !static_cast(newMaterial)->texture(binding)) { + *texture = nullptr; + return; + } + *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 00000000000..cbf5cb206f3 --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerialshader.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class BaseMaterialShader; +} // namespace rendergraph + +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 00000000000..3bcefcf54e6 --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basematerialtype.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseMaterialType = QSGMaterialType; +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/backend/basenode.h b/src/rendergraph/scenegraph/backend/basenode.h new file mode 100644 index 00000000000..0eac56e1e0e --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basenode.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseNode = QSGNode; +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/backend/baseopacitynode.h b/src/rendergraph/scenegraph/backend/baseopacitynode.h new file mode 100644 index 00000000000..14ea7e9d75f --- /dev/null +++ b/src/rendergraph/scenegraph/backend/baseopacitynode.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseOpacityNode = QSGOpacityNode; +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/backend/basetexture.h b/src/rendergraph/scenegraph/backend/basetexture.h new file mode 100644 index 00000000000..287319af829 --- /dev/null +++ b/src/rendergraph/scenegraph/backend/basetexture.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace rendergraph { +using BaseTexture = QSGTexture; +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/context.cpp b/src/rendergraph/scenegraph/context.cpp new file mode 100644 index 00000000000..29c3f567e25 --- /dev/null +++ b/src/rendergraph/scenegraph/context.cpp @@ -0,0 +1,11 @@ +#include "rendergraph/context.h" + +using namespace rendergraph; + +Context::Context(gsl::not_null pWindow) + : m_pWindow(pWindow) { +} + +gsl::not_null 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 00000000000..b320e71d09c --- /dev/null +++ b/src/rendergraph/scenegraph/geometry.cpp @@ -0,0 +1,66 @@ +#include "rendergraph/geometry.h" + +#include "rendergraph/assert.h" + +using namespace rendergraph; + +namespace { +QSGGeometry::DrawingMode toSgDrawingMode(DrawingMode mode) { + switch (mode) { + case DrawingMode::Triangles: + return QSGGeometry::DrawTriangles; + case DrawingMode::TriangleStrip: + return QSGGeometry::DrawTriangleStrip; + } +} + +DrawingMode fromSgDrawingMode(unsigned int mode) { + switch (mode) { + case QSGGeometry::DrawTriangles: + return DrawingMode::Triangles; + case QSGGeometry::DrawTriangleStrip: + return DrawingMode::TriangleStrip; + default: + throw "not implemented"; + } +} +} // namespace + +Geometry::Geometry(const rendergraph::AttributeSet& attributeSet, int vertexCount) + : BaseGeometry(attributeSet, vertexCount) { +} + +void Geometry::setAttributeValues(int attributePosition, const float* from, int numTuples) { + // TODO this code assumes all vertices are floats + const auto attributeArray = QSGGeometry::attributes(); + int vertexOffset = 0; + for (int i = 0; i < attributePosition; i++) { + vertexOffset += attributeArray[i].tupleSize; + } + const int tupleSize = attributeArray[attributePosition].tupleSize; + const int vertexStride = sizeOfVertex() / sizeof(float); + const int vertexSkip = vertexStride - tupleSize; + + VERIFY_OR_DEBUG_ASSERT(vertexOffset + numTuples * vertexStride - vertexSkip <= + vertexCount() * vertexStride) { + return; + } + + float* to = static_cast(QSGGeometry::vertexData()); + to += vertexOffset; + while (numTuples--) { + int k = tupleSize; + while (k--) { + *to++ = *from++; + } + to += vertexSkip; + } +} + +void Geometry::setDrawingMode(DrawingMode mode) { + QSGGeometry::setDrawingMode(toSgDrawingMode(mode)); +} + +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 00000000000..21196f2b8a3 --- /dev/null +++ b/src/rendergraph/scenegraph/geometrynode.cpp @@ -0,0 +1,35 @@ +#include "rendergraph/geometrynode.h" + +using namespace rendergraph; + +GeometryNode::GeometryNode() = default; + +void GeometryNode::setUsePreprocess(bool value) { + setFlag(QSGNode::UsePreprocess, value); +} + +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; +} + +void GeometryNode::markDirtyMaterial() { + markDirty(QSGNode::DirtyMaterial); +} + +void GeometryNode::markDirtyGeometry() { + markDirty(QSGNode::DirtyGeometry); +} diff --git a/src/rendergraph/scenegraph/material.cpp b/src/rendergraph/scenegraph/material.cpp new file mode 100644 index 00000000000..79cd048b011 --- /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 00000000000..3a34c80274d --- /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 00000000000..431b41d027b --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/context.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class Context; +} + +class rendergraph::Context { + public: + Context(gsl::not_null pWindow); + gsl::not_null window() const; + + private: + QQuickWindow* m_pWindow; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/nodeinterface.h b/src/rendergraph/scenegraph/rendergraph/nodeinterface.h new file mode 100644 index 00000000000..f5c42cadf46 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/nodeinterface.h @@ -0,0 +1,26 @@ +#pragma once + +#include "backend/basenode.h" +#include "rendergraph/assert.h" + +namespace rendergraph { + +template +class NodeInterface : public T_Node { + public: + void appendChildNode(std::unique_ptr pNode) { + BaseNode* pRawNode = pNode.release(); + pRawNode->setFlag(QSGNode::OwnedByParent, true); + T_Node::appendChildNode(pRawNode); + DEBUG_ASSERT(pRawNode->flags() & QSGNode::OwnedByParent); + } + std::unique_ptr detachChildNode(BaseNode* pNode) { + DEBUG_ASSERT(pNode->flags() & QSGNode::OwnedByParent); + pNode->setFlag(QSGNode::OwnedByParent, false); + T_Node::removeChildNode(pNode); + DEBUG_ASSERT(!pNode->flags() & QSGNode::OwnedByParent); + return std::unique_ptr(pNode); + } +}; + +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/texture.cpp b/src/rendergraph/scenegraph/texture.cpp new file mode 100644 index 00000000000..130d10df821 --- /dev/null +++ b/src/rendergraph/scenegraph/texture.cpp @@ -0,0 +1,18 @@ +#include "rendergraph/texture.h" + +#include "rendergraph/assert.h" +#include "rendergraph/context.h" + +using namespace rendergraph; + +Texture::Texture(Context* pContext, const QImage& image) + : m_pTexture(pContext->window()->createTextureFromImage(image)) { + VERIFY_OR_DEBUG_ASSERT(pContext->window() != nullptr) { + return; + } + DEBUG_ASSERT(!m_pTexture->textureSize().isNull()); +} + +qint64 Texture::comparisonKey() const { + return m_pTexture->comparisonKey(); +} diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 2c6d2f7d147..ea8883e5fb8 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -5,17 +5,20 @@ #include #include #include -#include #include #include #include #include "./util/assert.h" -#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/vertexdata.h" +#include "rendergraph/context.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/texturematerial.h" +#include "rendergraph/vertexupdaters/texturedvertexupdater.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,19 +59,22 @@ 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( - float fontPointSize, float maxHeight, float devicePixelRatio) { +void allshader::DigitsRenderNode::updateTexture(rendergraph::Context* pContext, + float fontPointSize, + float maxHeight, + float devicePixelRatio) { if (fontPointSize == m_fontPointSize && maxHeight == m_maxHeight) { return; } @@ -202,64 +208,66 @@ void allshader::DigitsRenderer::updateTexture( m_offset[NUM_CHARS] = 1.f; } - m_texture.setData(image); + dynamic_cast(material()) + .setTexture(std::make_unique(pContext, image)); +} + +void allshader::DigitsRenderNode::update( + 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()); } -float allshader::DigitsRenderer::draw(const QMatrix4x4& 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 4d280bbdaad..9e7c1884e45 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.h +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -1,30 +1,44 @@ #pragma once -#include +#include "rendergraph/context.h" +#include "rendergraph/geometrynode.h" +#include "util/class.h" -#include "shaders/textureshader.h" -#include "util/opengltexture2d.h" +namespace rendergraph { +class TexturedVertexUpdater; +} // namespace rendergraph namespace allshader { -class DigitsRenderer; -} +class DigitsRenderNode; +} // namespace allshader -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 updateTexture(rendergraph::Context* pContext, + float fontPointSize, + float maxHeight, + float devicePixelRatio); + + void update( 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 +46,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/matrixforwidgetgeometry.cpp b/src/waveform/renderers/allshader/matrixforwidgetgeometry.cpp deleted file mode 100644 index 494bfc768ac..00000000000 --- a/src/waveform/renderers/allshader/matrixforwidgetgeometry.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" - -#include "waveform/renderers/waveformwidgetrenderer.h" - -QMatrix4x4 matrixForWidgetGeometry(WaveformWidgetRenderer* const waveformRenderer, - bool applyDevicePixelRatio) { - const float ratio = applyDevicePixelRatio ? waveformRenderer->getDevicePixelRatio() : 1.f; - QMatrix4x4 matrix; - - matrix.ortho(QRectF(0.0f, - 0.0f, - waveformRenderer->getWidth() * ratio, - waveformRenderer->getHeight() * ratio)); - if (waveformRenderer->getOrientation() == Qt::Vertical) { - matrix.rotate(90.f, 0.0f, 0.0f, 1.0f); - matrix.translate(0.f, -waveformRenderer->getWidth() * ratio, 0.f); - } - return matrix; -} diff --git a/src/waveform/renderers/allshader/matrixforwidgetgeometry.h b/src/waveform/renderers/allshader/matrixforwidgetgeometry.h deleted file mode 100644 index 3240104085c..00000000000 --- a/src/waveform/renderers/allshader/matrixforwidgetgeometry.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -class WaveformWidgetRenderer; - -// Calculate the orthogonal project matrix for the WaveformWidgetRenderer, taking into account -// width and height, but also orientation (to rotate the matrix). -// Note that this allows us to basically use the same renderer code for horizontal and vertical -// orientation of the waveform, as we simply multiple the vertices with a rotated matrix in -// the vertex shaders. -QMatrix4x4 matrixForWidgetGeometry( - WaveformWidgetRenderer* const widget, bool applyDevicePixelRatio); diff --git a/src/waveform/renderers/allshader/waveformrenderbackground.cpp b/src/waveform/renderers/allshader/waveformrenderbackground.cpp index beddbc000a5..504beade43c 100644 --- a/src/waveform/renderers/allshader/waveformrenderbackground.cpp +++ b/src/waveform/renderers/allshader/waveformrenderbackground.cpp @@ -11,10 +11,10 @@ WaveformRenderBackground::WaveformRenderBackground( } void WaveformRenderBackground::setup(const QDomNode& node, - const SkinContext& context) { + const SkinContext& skinContext) { m_backgroundColor = m_waveformRenderer->getWaveformSignalColors()->getBgColor(); - QString backgroundPixmapPath = context.selectString(node, "BgPixmap"); + QString backgroundPixmapPath = skinContext.selectString(node, "BgPixmap"); if (!backgroundPixmapPath.isEmpty()) { qWarning() << "WaveformView BgPixmap is not supported by " "allshader::WaveformRenderBackground"; diff --git a/src/waveform/renderers/allshader/waveformrenderbackground.h b/src/waveform/renderers/allshader/waveformrenderbackground.h index 8a913e27742..7567d2da7df 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,11 +10,13 @@ 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); - void setup(const QDomNode& node, const SkinContext& context) override; + void setup(const QDomNode& node, const SkinContext& skinContext) override; void paintGL() override; private: diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.cpp b/src/waveform/renderers/allshader/waveformrenderbeat.cpp index 3f793efd706..aa7de4ecfe5 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.cpp +++ b/src/waveform/renderers/allshader/waveformrenderbeat.cpp @@ -2,35 +2,49 @@ #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) { + initForRectangles(0); + setUsePreprocess(true); } -void WaveformRenderBeat::initializeGL() { - WaveformRenderer::initializeGL(); - m_shader.init(); +void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& skinContext) { + m_color = QColor(skinContext.selectString(node, "BeatColor")); + m_color = WSkinColor::getCorrectColor(m_color).toRgb(); } -void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& context) { - m_color = QColor(context.selectString(node, "BeatColor")); - m_color = WSkinColor::getCorrectColor(m_color).toRgb(); +void WaveformRenderBeat::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } -void WaveformRenderBeat::paintGL() { - TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); +void WaveformRenderBeat::preprocess() { + if (!preprocessInner()) { + geometry().allocate(0); + markDirtyGeometry(); + } +} + +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,9 @@ void WaveformRenderBeat::paintGL() { } const int reserved = numBeatsInRange * numVerticesPerLine; - m_vertices.clear(); - m_vertices.reserve(reserved); + geometry().allocate(reserved); + + VertexUpdater vertexUpdater{geometry().vertexDataAs()}; for (auto it = trackBeats->iteratorFrom(startPosition); it != trackBeats->cend() && *it <= endPosition; @@ -104,33 +116,17 @@ 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}); } + markDirtyGeometry(); - 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); - - 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); + DEBUG_ASSERT(reserved == vertexUpdater.index()); - glDrawArrays(GL_TRIANGLES, 0, m_vertices.size()); + material().setUniform(1, m_color); + markDirtyMaterial(); - 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 d3b2b79d1bc..f14520d8d39 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); - void setup(const QDomNode& node, const SkinContext& context) override; - void paintGL() override; - void initializeGL() override; + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + + void setup(const QDomNode& node, const SkinContext& skinContext) 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 04b4b427935..11c486eb49c 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 5c530a00e22..00000000000 --- 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 10df7121020..197d147b831 100644 --- a/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp +++ b/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp @@ -1,9 +1,14 @@ #include "waveform/renderers/allshader/waveformrendererendoftrack.h" #include +#include #include #include "control/controlproxy.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/rgbamaterial.h" +#include "rendergraph/vertexupdaters/rgbavertexupdater.h" +#include "util/colorcomponents.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveformwidgetfactory.h" #include "widget/wskincolor.h" @@ -11,19 +16,26 @@ 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) { + initForRectangles(0); + setUsePreprocess(true); +} + +void WaveformRendererEndOfTrack::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } bool WaveformRendererEndOfTrack::init() { @@ -37,50 +49,25 @@ bool WaveformRendererEndOfTrack::init() { return true; } -void WaveformRendererEndOfTrack::setup(const QDomNode& node, const SkinContext& context) { +void WaveformRendererEndOfTrack::setup(const QDomNode& node, const SkinContext& skinContext) { m_color = QColor(200, 25, 20); - const QString endOfTrackColorName = context.selectString(node, "EndOfTrackColor"); + const QString endOfTrackColorName = skinContext.selectString(node, "EndOfTrackColor"); if (!endOfTrackColorName.isNull()) { m_color = QColor(endOfTrackColorName); m_color = WSkinColor::getCorrectColor(m_color); } } -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::preprocess() { + if (!preprocessInner()) { + geometry().allocate(0); + markDirtyGeometry(); + } } -void WaveformRendererEndOfTrack::paintGL() { +bool WaveformRendererEndOfTrack::preprocessInner() { if (!m_pEndOfTrackControl->toBool()) { - return; + return false; } const int elapsed = m_timer.elapsed().toIntegerMillis() % kBlinkingPeriodMillis; @@ -94,17 +81,32 @@ void WaveformRendererEndOfTrack::paintGL() { const double criticalIntensity = (remainingTimeTriggerSeconds - remainingTime) / remainingTimeTriggerSeconds; - const double alpha = criticalIntensity * blinkIntensity; + const double alpha = std::min(1.0, std::max(0.0, criticalIntensity * blinkIntensity)); - if (alpha != 0.0) { - QColor color = m_color; - color.setAlphaF(static_cast(alpha)); + QSizeF size(m_waveformRenderer->getWidth(), m_waveformRenderer->getHeight()); + float r, g, b, a; + getRgbF(m_color, &r, &g, &b, &a); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + const float posx0 = 0.f; + const float posx1 = size.width() / 2.f; + const float posx2 = size.width(); + const float posy1 = 0.f; + const float posy2 = size.height(); - fillWithGradient(color); - } + float minAlpha = 0.5f * static_cast(alpha); + float maxAlpha = 0.83f * static_cast(alpha); + + geometry().allocate(6 * 2); + RGBAVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + vertexUpdater.addRectangleHGradient( + {posx0, posy1}, {posx1, posy2}, {r, g, b, minAlpha}, {r, g, b, minAlpha}); + vertexUpdater.addRectangleHGradient( + {posx1, posy1}, {posx2, posy2}, {r, g, b, minAlpha}, {r, g, b, maxAlpha}); + + markDirtyGeometry(); + markDirtyMaterial(); + + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererendoftrack.h b/src/waveform/renderers/allshader/waveformrendererendoftrack.h index 664a2cac16e..d43d901f677 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,27 +17,31 @@ 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); - void setup(const QDomNode& node, const SkinContext& context) override; + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + + void setup(const QDomNode& node, const SkinContext& skinContext) override; bool init() override; - void initializeGL() override; - void paintGL() override; + // Virtual for rendergraph::Node + void preprocess() override; private: - void fillWithGradient(QColor color); - - mixxx::EndOfTrackShader m_shader; std::unique_ptr m_pEndOfTrackControl; std::unique_ptr m_pTimeRemainingControl; QColor m_color; PerformanceTimer m_timer; + bool preprocessInner(); + DISALLOW_COPY_AND_ASSIGN(WaveformRendererEndOfTrack); }; diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp index 0ddf164cc3e..4e626da1b80 100644 --- a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp +++ b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp @@ -2,7 +2,6 @@ #include "track/track.h" #include "util/math.h" -#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveform.h" @@ -19,7 +18,6 @@ void WaveformRendererFiltered::onSetup(const QDomNode& node) { } void WaveformRendererFiltered::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } @@ -143,7 +141,7 @@ void WaveformRendererFiltered::paintGL() { xVisualFrame += visualIncrementPerPixel; } - const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, true); + const QMatrix4x4 matrix; // TODO = m_waveformRenderer->getMatrix(true); const int matrixLocation = m_shader.matrixLocation(); const int colorLocation = m_shader.colorLocation(); diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.h b/src/waveform/renderers/allshader/waveformrendererfiltered.h index 158ff880c01..43441adafa5 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 31b9785acdf..23b9ca8c1e9 100644 --- a/src/waveform/renderers/allshader/waveformrendererhsv.cpp +++ b/src/waveform/renderers/allshader/waveformrendererhsv.cpp @@ -3,7 +3,6 @@ #include "track/track.h" #include "util/colorcomponents.h" #include "util/math.h" -#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveform.h" @@ -19,7 +18,6 @@ void WaveformRendererHSV::onSetup(const QDomNode& node) { } void WaveformRendererHSV::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } @@ -180,7 +178,7 @@ void WaveformRendererHSV::paintGL() { DEBUG_ASSERT(reserved == m_vertices.size()); DEBUG_ASSERT(reserved == m_colors.size()); - const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, true); + const QMatrix4x4 matrix; // TODO = m_waveformRenderer->getMatrix(true); const int matrixLocation = m_shader.matrixLocation(); const int positionLocation = m_shader.positionLocation(); diff --git a/src/waveform/renderers/allshader/waveformrendererhsv.h b/src/waveform/renderers/allshader/waveformrendererhsv.h index 0fa0b6bf863..a6fdbe95f51 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 eea01c5bdad..47caa6f3300 100644 --- a/src/waveform/renderers/allshader/waveformrendererpreroll.cpp +++ b/src/waveform/renderers/allshader/waveformrendererpreroll.cpp @@ -1,12 +1,13 @@ #include "waveform/renderers/allshader/waveformrendererpreroll.h" #include -#include #include #include +#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" #include "widget/wskincolor.h" @@ -20,7 +21,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 +48,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,32 +58,52 @@ 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; void WaveformRendererPreroll::setup( - const QDomNode& node, const SkinContext& context) { - m_color = QColor(context.selectString(node, "SignalColor")); + const QDomNode& node, const SkinContext& skinContext) { + m_color = QColor(skinContext.selectString(node, "SignalColor")); 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::preprocess() { + if (!preprocessInner()) { + if (geometry().vertexCount() != 0) { + geometry().allocate(0); + markDirtyGeometry(); + } + } else { + markDirtyMaterial(); + markDirtyGeometry(); + } } -void WaveformRendererPreroll::paintGL() { - const TrackPointer track = m_waveformRenderer->getTrackInfo(); - if (!track || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { - return; +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 +117,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 +151,20 @@ 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)); + dynamic_cast(material()) + .setTexture(std::make_unique(m_waveformRenderer->getContext(), + drawPrerollImage(m_markerLength, + m_markerBreadth, + m_waveformRenderer->getDevicePixelRatio(), + m_color))); } - if (!m_texture.isStorageAllocated()) { - return; - } - - 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 +178,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 +202,19 @@ 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; - m_texture.release(); + vertexUpdater.addRectangle({x, halfBreadth - halfMarkerBreadth}, + {end, + m_isSlipRenderer ? halfBreadth + : halfBreadth + halfMarkerBreadth}, + {0.f, 0.f}, + {repetitions, m_isSlipRenderer ? 0.5f : 1.f}); + } - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(texcoordLocation); - m_shader.release(); -} + DEBUG_ASSERT(reserved == vertexUpdater.index()); -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 742320424c5..eb28459ef96 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; - void setup(const QDomNode& node, const SkinContext& context) override; - void paintGL() override; - void initializeGL() override; + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; - private: - void drawPattern(float x1, float y1, float x2, float y2, float repetitions); + void setup(const QDomNode& node, const SkinContext& skinContext) override; + + // 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 54cea537640..6dbc8d1752d 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.cpp +++ b/src/waveform/renderers/allshader/waveformrendererrgb.cpp @@ -1,11 +1,14 @@ #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 +23,28 @@ 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()) { + if (geometry().vertexCount() != 0) { + geometry().allocate(0); + markDirtyGeometry(); + } + } } -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,28 +52,31 @@ 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 const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - const int length = static_cast(m_waveformRenderer->getLength() * devicePixelRatio); + const int length = static_cast(m_waveformRenderer->getLength()); + const int pixelLength = static_cast(m_waveformRenderer->getLength() * devicePixelRatio); + const float invDevicePixelRatio = 1.f / devicePixelRatio; + const float halfPixelSize = 0.5 / devicePixelRatio; // See waveformrenderersimple.cpp for a detailed explanation of the frame and index calculation const int visualFramesSize = dataSize / 2; @@ -74,14 +87,14 @@ void WaveformRendererRGB::paintGL() { // Represents the # of visual frames per horizontal pixel. const double visualIncrementPerPixel = - (lastVisualFrame - firstVisualFrame) / static_cast(length); + (lastVisualFrame - firstVisualFrame) / static_cast(pixelLength); // Per-band gain from the EQ knobs. float allGain(1.0), lowGain(1.0), midGain(1.0), highGain(1.0); // applyCompensation = false, as we scale to match filtered.all getGains(&allGain, false, &lowGain, &midGain, &highGain); - const float breadth = static_cast(m_waveformRenderer->getBreadth()) * devicePixelRatio; + const float breadth = static_cast(m_waveformRenderer->getBreadth()); const float halfBreadth = breadth / 2.0f; const float heightFactorAbs = allGain * halfBreadth / m_maxValue; @@ -106,25 +119,24 @@ void WaveformRendererRGB::paintGL() { const int reserved = numVerticesPerLine * // 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); - - m_vertices.addRectangle(0.f, - halfBreadth - 0.5f * devicePixelRatio, - static_cast(length), - m_isSlipRenderer ? halfBreadth : halfBreadth + 0.5f * devicePixelRatio); - m_colors.addForRectangle( - static_cast(m_axesColor_r), - static_cast(m_axesColor_g), - static_cast(m_axesColor_b)); + ((splitLeftRight && !m_isSlipRenderer ? pixelLength * 2 : pixelLength) + 1); + + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); + geometry().allocate(reserved); + markDirtyGeometry(); + + RGBVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + vertexUpdater.addRectangle({0.f, + halfBreadth - 0.5f}, + {static_cast(length), + m_isSlipRenderer ? halfBreadth : halfBreadth + 0.5f}, + {static_cast(m_axesColor_r), + static_cast(m_axesColor_g), + static_cast(m_axesColor_b)}); const double maxSamplingRange = visualIncrementPerPixel / 2.0; - for (int pos = 0; pos < length; ++pos) { + for (int pos = 0; pos < pixelLength; ++pos) { const int visualFrameStart = std::lround(xVisualFrame - maxSamplingRange); const int visualFrameStop = std::lround(xVisualFrame + maxSamplingRange); @@ -132,7 +144,7 @@ void WaveformRendererRGB::paintGL() { const int visualIndexStop = std::min(std::max(visualFrameStop, visualFrameStart + 1) * 2, dataSize - 1); - const float fpos = static_cast(pos); + const float fpos = static_cast(pos) * invDevicePixelRatio; // Find the max values for low, mid, high and all in the waveform data. // - Max of left and right @@ -215,51 +227,36 @@ void WaveformRendererRGB::paintGL() { // Lines are thin rectangles if (!splitLeftRight) { - m_vertices.addRectangle(fpos - 0.5f, - halfBreadth - heightFactorAbs * maxAllChn[0], - fpos + 0.5f, - m_isSlipRenderer - ? halfBreadth - : halfBreadth + heightFactorAbs * maxAllChn[1]); + vertexUpdater.addRectangle({fpos - halfPixelSize, + halfBreadth - heightFactorAbs * maxAllChn[0]}, + {fpos + halfPixelSize, + m_isSlipRenderer + ? halfBreadth + : 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, - halfBreadth, - fpos + 0.5f, - halfBreadth + heightFactor[chn] * maxAllChn[chn]); + vertexUpdater.addRectangle({fpos - halfPixelSize, + halfBreadth}, + {fpos + halfPixelSize, + 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()); - - const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, true); - - 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); + DEBUG_ASSERT(reserved == vertexUpdater.index()); - glDrawArrays(GL_TRIANGLES, 0, m_vertices.size()); + markDirtyMaterial(); - 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 e9c12b52a42..a829cf3af2f 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 0e72d9364c7..0c4217723d8 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 02e9453afdf..c53c9596a13 100644 --- a/src/waveform/renderers/allshader/waveformrenderersimple.cpp +++ b/src/waveform/renderers/allshader/waveformrenderersimple.cpp @@ -2,7 +2,6 @@ #include "track/track.h" #include "util/math.h" -#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveform.h" @@ -18,7 +17,6 @@ void WaveformRendererSimple::onSetup(const QDomNode& node) { } void WaveformRendererSimple::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } @@ -138,7 +136,7 @@ void WaveformRendererSimple::paintGL() { xVisualFrame += visualIncrementPerPixel; } - const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, true); + const QMatrix4x4 matrix; // TODO = m_waveformRenderer->getMatrix(true); const int matrixLocation = m_shader.matrixLocation(); const int colorLocation = m_shader.colorLocation(); diff --git a/src/waveform/renderers/allshader/waveformrenderersimple.h b/src/waveform/renderers/allshader/waveformrenderersimple.h index a1e92c8d2f0..ba79752539c 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 4311f00a4ce..667b43f915c 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp @@ -43,19 +43,20 @@ bool WaveformRendererSlipMode::init() { return true; } -void WaveformRendererSlipMode::setup(const QDomNode& node, const SkinContext& context) { - const QString slipModeOutlineColorName = context.selectString(node, "SlipBorderOutlineColor"); +void WaveformRendererSlipMode::setup(const QDomNode& node, const SkinContext& skinContext) { + const QString slipModeOutlineColorName = + skinContext.selectString(node, "SlipBorderOutlineColor"); if (!slipModeOutlineColorName.isNull()) { m_color = WSkinColor::getCorrectColor(QColor(slipModeOutlineColorName)); } else { m_color = kDefaultColor; } - const float slipBorderTopOutlineSize = context.selectFloat( + const float slipBorderTopOutlineSize = skinContext.selectFloat( node, "SlipBorderTopOutlineSize", m_slipBorderTopOutlineSize); if (slipBorderTopOutlineSize >= 0) { m_slipBorderTopOutlineSize = slipBorderTopOutlineSize; } - const float slipBorderBottomOutlineSize = context.selectFloat( + const float slipBorderBottomOutlineSize = skinContext.selectFloat( node, "SlipBorderBottomOutlineSize", m_slipBorderBottomOutlineSize); if (slipBorderBottomOutlineSize >= 0) { m_slipBorderBottomOutlineSize = slipBorderBottomOutlineSize; @@ -63,7 +64,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 5e7b189ca57..b481142797f 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,12 +17,14 @@ 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); - void setup(const QDomNode& node, const SkinContext& context) override; + void setup(const QDomNode& node, const SkinContext& skinContext) override; bool init() override; diff --git a/src/waveform/renderers/allshader/waveformrendererstem.cpp b/src/waveform/renderers/allshader/waveformrendererstem.cpp index bacb18d32ca..aa372f1dc76 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.cpp +++ b/src/waveform/renderers/allshader/waveformrendererstem.cpp @@ -4,13 +4,16 @@ #include #include +#include "rendergraph/material/rgbamaterial.h" +#include "rendergraph/vertexupdaters/rgbavertexupdater.h" #include "track/track.h" +#include "util/assert.h" #include "util/math.h" -#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/rgbdata.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveform.h" +using namespace rendergraph; + namespace { constexpr int kMaxSupportedStems = 4; } // anonymous namespace @@ -22,65 +25,81 @@ WaveformRendererStem::WaveformRendererStem( ::WaveformRendererAbstract::PositionSource type) : WaveformRendererSignalBase(waveformWidget), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { + initForRectangles(0); + setUsePreprocess(true); } void WaveformRendererStem::onSetup(const QDomNode& node) { Q_UNUSED(node); } -void WaveformRendererStem::initializeGL() { - WaveformRendererSignalBase::initializeGL(); - m_shader.init(); - m_textureShader.init(); - auto group = m_pEQEnabled->getKey().group; +bool WaveformRendererStem::init() { + auto group = m_waveformRenderer->getGroup(); + VERIFY_OR_DEBUG_ASSERT(!group.isEmpty()) { + return false; + } for (int stemIdx = 1; stemIdx <= kMaxSupportedStems; stemIdx++) { DEBUG_ASSERT(group.endsWith("]")); QString stemGroup = QStringLiteral("%1Stem%2]") .arg(group.left(group.size() - 1), QString::number(stemIdx)); m_pStemGain.emplace_back( - std::make_unique(stemGroup, + std::make_unique(stemGroup, QStringLiteral("volume"))); - m_pStemMute.emplace_back(std::make_unique( + m_pStemMute.emplace_back(std::make_unique( stemGroup, QStringLiteral("mute"))); } + return true; } -void WaveformRendererStem::paintGL() { +void WaveformRendererStem::preprocess() { + if (!preprocessInner()) { + if (geometry().vertexCount() != 0) { + geometry().allocate(0); + markDirtyGeometry(); + } + } +} + +bool WaveformRendererStem::preprocessInner() { TrackPointer pTrack = m_waveformRenderer->getTrackInfo(); + if (!pTrack || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { - return; + return false; } auto stemInfo = pTrack->getStemInfo(); // If this track isn't a stem track, skip the rendering if (stemInfo.isEmpty()) { - return; + return false; } auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip : ::WaveformRendererAbstract::Play; 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; } // If this waveform doesn't contain stem data, skip the rendering if (!waveform->hasStem()) { - return; + return false; } const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - const int length = static_cast(m_waveformRenderer->getLength() * devicePixelRatio); + const int length = static_cast(m_waveformRenderer->getLength()); + const int pixelLength = static_cast(m_waveformRenderer->getLength() * devicePixelRatio); + const float invDevicePixelRatio = 1.f / devicePixelRatio; + const float halfPixelSize = 0.5 / devicePixelRatio; // See waveformrenderersimple.cpp for a detailed explanation of the frame and index calculation const int visualFramesSize = dataSize / 2; @@ -91,14 +110,14 @@ void WaveformRendererStem::paintGL() { // Represents the # of visual frames per horizontal pixel. const double visualIncrementPerPixel = - (lastVisualFrame - firstVisualFrame) / static_cast(length); + (lastVisualFrame - firstVisualFrame) / static_cast(pixelLength); // Per-band gain from the EQ knobs. float allGain(1.0); // applyCompensation = true, as we scale to match filtered.all getGains(&allGain, false, nullptr, nullptr, nullptr); - const float breadth = static_cast(m_waveformRenderer->getBreadth()) * devicePixelRatio; + const float breadth = static_cast(m_waveformRenderer->getBreadth()); const float halfBreadth = breadth / 2.0f; const float heightFactor = allGain * halfBreadth / m_maxValue; @@ -109,22 +128,22 @@ void WaveformRendererStem::paintGL() { const int numVerticesPerLine = 6; // 2 triangles - const int reserved = numVerticesPerLine * (8 * length + 1); + const int reserved = numVerticesPerLine * (8 * pixelLength + 1); - m_vertices.clear(); - m_vertices.reserve(reserved); - m_colors.clear(); - m_colors.reserve(reserved); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); + geometry().allocate(reserved); + markDirtyGeometry(); - m_vertices.addRectangle(0.f, - halfBreadth - 0.5f * devicePixelRatio, - static_cast(length), - m_isSlipRenderer ? halfBreadth : halfBreadth + 0.5f * devicePixelRatio); - m_colors.addForRectangle(0.f, 0.f, 0.f, 0.f); + RGBAVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + vertexUpdater.addRectangle({0.f, + halfBreadth - 0.5f}, + {static_cast(length), + m_isSlipRenderer ? halfBreadth : halfBreadth + 0.5f}, + {0.f, 0.f, 0.f, 0.f}); const double maxSamplingRange = visualIncrementPerPixel / 2.0; - for (int visualIdx = 0; visualIdx < length; ++visualIdx) { + for (int visualIdx = 0; visualIdx < pixelLength; ++visualIdx) { for (int stemIdx = 0; stemIdx < 4; stemIdx++) { // Stem is drawn twice with different opacity level, this allow to // see the maximum signal by transparency @@ -141,7 +160,7 @@ void WaveformRendererStem::paintGL() { const int visualIndexStop = std::min(std::max(visualFrameStop, visualFrameStart + 1) * 2, dataSize - 1); - const float fVisualIdx = static_cast(visualIdx); + const float fVisualIdx = static_cast(visualIdx) * invDevicePixelRatio; // Find the max values for current eq in the waveform data. // - Max of left and right @@ -166,43 +185,23 @@ void WaveformRendererStem::paintGL() { } // Lines are thin rectangles - // shawdow - m_vertices.addRectangle(fVisualIdx - 0.5f, - halfBreadth - heightFactor * max, - fVisualIdx + 0.5f, - m_isSlipRenderer ? halfBreadth : halfBreadth + heightFactor * max); - - m_colors.addForRectangle(color_r, color_g, color_b, color_a); + // shadow + vertexUpdater.addRectangle({fVisualIdx - halfPixelSize, + halfBreadth - heightFactor * max}, + {fVisualIdx + halfPixelSize, + m_isSlipRenderer ? halfBreadth : halfBreadth + heightFactor * max}, + {color_r, color_g, color_b, color_a}); } } + xVisualFrame += visualIncrementPerPixel; } - DEBUG_ASSERT(reserved == m_vertices.size()); - DEBUG_ASSERT(reserved == m_colors.size()); - - const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, true); - - 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(), 4); + DEBUG_ASSERT(reserved == vertexUpdater.index()); - glDrawArrays(GL_TRIANGLES, 0, m_vertices.size()); + markDirtyMaterial(); - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(colorLocation); - m_shader.release(); + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererstem.h b/src/waveform/renderers/allshader/waveformrendererstem.h index 757bfe763d3..1b8881d0cc6 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.h +++ b/src/waveform/renderers/allshader/waveformrendererstem.h @@ -2,11 +2,9 @@ #include -#include "shaders/rgbashader.h" -#include "shaders/textureshader.h" +#include "control/pollingcontrolproxy.h" +#include "rendergraph/geometrynode.h" #include "util/class.h" -#include "waveform/renderers/allshader/rgbadata.h" -#include "waveform/renderers/allshader/vertexdata.h" #include "waveform/renderers/allshader/waveformrenderersignalbase.h" class QOpenGLTexture; @@ -15,30 +13,33 @@ namespace allshader { class WaveformRendererStem; } -class allshader::WaveformRendererStem final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererStem final + : public allshader::WaveformRendererSignalBase, + public rendergraph::GeometryNode { public: explicit WaveformRendererStem(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); - // override ::WaveformRendererSignalBase + // Pure virtual from WaveformRendererSignalBase, not used void onSetup(const QDomNode& node) override; - void initializeGL() override; - void paintGL() override; + bool init() override; - private: - mixxx::RGBAShader m_shader; - mixxx::TextureShader m_textureShader; - VertexData m_vertices; - RGBAData m_colors; + bool supportsSlip() const override { + return true; + } + + // Virtuals for rendergraph::Node + void preprocess() override; + private: bool m_isSlipRenderer; - std::vector> m_pStemGain; - std::vector> m_pStemMute; + std::vector> m_pStemGain; + std::vector> m_pStemMute; - void drawTexture(float x, float y, QOpenGLTexture* texture); + bool preprocessInner(); DISALLOW_COPY_AND_ASSIGN(WaveformRendererStem); }; diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.cpp b/src/waveform/renderers/allshader/waveformrenderertextured.cpp index dc137b73208..de21a013259 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 16680f1bc5d..dccf1d975b6 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 1a66df4deb4..cec1b60c334 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -1,37 +1,53 @@ #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/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/rgbadata.h" -#include "waveform/renderers/allshader/vertexdata.h" +#include "waveform/renderers/allshader/digitsrenderer.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveformwidgetfactory.h" -// On the use of QPainter: -// -// The renderers in this folder are optimized to use GLSL shaders and refrain -// from using QPainter on the QOpenGLWindow, which causes degredated performance. -// -// This renderer does use QPainter (indirectly, in WaveformMark::generateImage), but -// 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 { - public: - TextureGraphics(const QImage& image) { - m_texture.setData(image); - } - QOpenGLTexture* texture() { - return &m_texture; - } +using namespace rendergraph; + +allshader::WaveformMarkNode::WaveformMarkNode(WaveformMark* pOwner, + rendergraph::Context* pContext, + const QImage& image) + : m_pOwner(pOwner) { + initForRectangles(1); + updateTexture(pContext, image); +} - private: - OpenGLTexture2D m_texture; -}; +void allshader::WaveformMarkNode::updateTexture( + rendergraph::Context* pContext, const QImage& image) { + dynamic_cast(material()) + .setTexture(std::make_unique(pContext, image)); + m_textureWidth = image.width(); + m_textureHeight = image.height(); +} +void allshader::WaveformMarkNode::update(float x, float y, float devicePixelRatio) { + TexturedVertexUpdater vertexUpdater{ + geometry().vertexDataAs()}; + vertexUpdater.addRectangle({x, y}, + {x + m_textureWidth / devicePixelRatio, + y + m_textureHeight / devicePixelRatio}, + {0.f, 0.f}, + {1.f, 1.f}); +} +allshader::WaveformMarkNodeGraphics::WaveformMarkNodeGraphics(WaveformMark* pOwner, + rendergraph::Context* pContext, + const QImage& image) + : m_pNode(std::make_unique( + pOwner, pContext, image)) { +} // Both allshader::WaveformRenderMark and the non-GL ::WaveformRenderMark derive // from WaveformRenderMarkBase. The base-class takes care of updating the marks @@ -65,78 +81,51 @@ allshader::WaveformRenderMark::WaveformRenderMark( m_beatsUntilMark(0), m_timeUntilMark(0.0), m_pTimeRemainingControl(nullptr), - m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { -} + m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip), + m_playPosHeight(0.f), + m_playPosDevicePixelRatio(0.f) { + { + auto pNode = std::make_unique(); + m_pRangeNodesParent = pNode.get(); + appendChildNode(std::move(pNode)); + } -bool allshader::WaveformRenderMark::init() { - m_pTimeRemainingControl = std::make_unique( - m_waveformRenderer->getGroup(), "time_remaining"); - return true; -} + { + auto pNode = std::make_unique(); + m_pMarkNodesParent = pNode.get(); + appendChildNode(std::move(pNode)); + } -void allshader::WaveformRenderMark::initializeGL() { - allshader::WaveformRendererAbstract::initializeGL(); - m_digitsRenderer.init(); - m_rgbaShader.init(); - m_textureShader.init(); + { + auto pNode = std::make_unique(); + m_pDigitsRenderNode = pNode.get(); + appendChildNode(std::move(pNode)); + } - // Will create textures so requires OpenGL context - updateMarkImages(); - updatePlayPosMarkTexture(); - const auto untilMarkTextPointSize = - WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); - m_digitsRenderer.updateTexture(untilMarkTextPointSize, - getMaxHeightForText(), - m_waveformRenderer->getDevicePixelRatio()); + { + auto pNode = std::make_unique(); + m_pPlayPosNode = pNode.get(); + m_pPlayPosNode->initForRectangles(1); + appendChildNode(std::move(pNode)); + } } -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(); +void allshader::WaveformRenderMark::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); +} - m_textureShader.disableAttributeArray(positionLocation); - m_textureShader.disableAttributeArray(texcoordLocation); - m_textureShader.release(); +bool allshader::WaveformRenderMark::init() { + m_pTimeRemainingControl = std::make_unique( + m_waveformRenderer->getGroup(), "time_remaining"); + ::WaveformRenderMarkBase::init(); + return true; } -void allshader::WaveformRenderMark::drawMark( - const QMatrix4x4& matrix, const QRectF& rect, QColor color) { +void allshader::WaveformRenderMark::updateRangeNode(GeometryNode* pNode, + 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 +140,41 @@ 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(); - - 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()); + 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}); +} - 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()) { + auto pNode = m_pMarkNodesParent->detachChildNode(pChild); + WaveformMarkNode* pWaveformMarkNode = static_cast(pNode.get()); + // Determine its WaveformMark + auto pMark = pWaveformMarkNode->m_pOwner; + auto pGraphics = static_cast(pMark->m_pGraphics.get()); + // Store the node with the WaveformMark + pGraphics->attachNode(std::move(pNode)); + } + auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip : ::WaveformRendererAbstract::Play; bool slipActive = m_waveformRenderer->isSlipActive(); @@ -195,21 +182,23 @@ 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 - updateMarkImages(); - QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); + updatePlayPosMarkTexture(m_waveformRenderer->getContext()); + + // 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(); const double playPosition = m_waveformRenderer->getTruePosSample(positionType); double nextMarkPosition = std::numeric_limits::max(); + GeometryNode* pRangeChild = static_cast(m_pRangeNodesParent->firstChild()); + for (const auto& pMark : std::as_const(m_marks)) { if (!pMark->isValid()) { continue; @@ -221,13 +210,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 +233,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 +241,16 @@ void allshader::WaveformRenderMark::paintGL() { if (drawOffset > -markHalfWidth && drawOffset < m_waveformRenderer->getLength() + markHalfWidth) { - drawTexture(matrix, + pMarkNodeGraphics->update( drawOffset, !m_isSlipRenderer && slipActive ? m_waveformRenderer->getBreadth() / 2 : 0, - pTexture); + devicePixelRatio); + + // transfer back to m_pMarkNodesParent children, for rendering + m_pMarkNodesParent->appendChildNode(pMarkNodeGraphics->detachNode()); + visible = true; } @@ -277,13 +267,22 @@ void allshader::WaveformRenderMark::paintGL() { QColor color = pMark->fillColor(); color.setAlphaF(0.4f); - drawMark(matrix, + // Reuse, or create new when needed + if (!pRangeChild) { + auto pNode = std::make_unique(); + pNode->initForRectangles(2); + pRangeChild = pNode.get(); + m_pRangeNodesParent->appendChildNode(std::move(pNode)); + } + + updateRangeNode(pRangeChild, QRectF(QPointF(currentMarkPoint, 0), QPointF(currentMarkEndPoint, - m_waveformRenderer - ->getBreadth())), + m_waveformRenderer->getBreadth())), color); + visible = true; + pRangeChild = static_cast(pRangeChild->nextSibling()); } } @@ -293,6 +292,13 @@ void allshader::WaveformRenderMark::paintGL() { pMark, static_cast(drawOffset)}); } } + + // Remove unused nodes + while (pRangeChild) { + auto pNode = m_pRangeNodesParent->detachChildNode(pRangeChild); + pRangeChild = static_cast(pRangeChild->nextSibling()); + } + m_waveformRenderer->setMarkPositions(marksOnScreen); const float currentMarkPoint = @@ -302,35 +308,43 @@ 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); + 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() || WaveformWidgetFactory::instance()->getUntilMarkShowTime()) { updateUntilMark(playPosition, nextMarkPosition); - drawUntilMark(matrix, currentMarkPoint + 20); + drawUntilMark(currentMarkPoint + 20); } } -void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, float x) { +void allshader::WaveformRenderMark::drawUntilMark(float x) { const bool untilMarkShowBeats = WaveformWidgetFactory::instance()->getUntilMarkShowBeats(); const bool untilMarkShowTime = WaveformWidgetFactory::instance()->getUntilMarkShowTime(); const auto untilMarkAlign = WaveformWidgetFactory::instance()->getUntilMarkAlign(); const auto untilMarkTextPointSize = WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); - m_digitsRenderer.updateTexture(untilMarkTextPointSize, + m_pDigitsRenderNode->updateTexture(m_waveformRenderer->getContext(), + 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,36 +364,30 @@ 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( + x, + y, + multiLine, + untilMarkShowBeats ? QString::number(m_beatsUntilMark) : QString{}, + untilMarkShowTime ? timeSecToString(m_timeUntilMark) : QString{}); } // Generate the texture used to draw the play position marker. // Note that in the legacy waveform widgets this is drawn directly // in the WaveformWidgetRenderer itself. Doing it here is cleaner. -void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { +void allshader::WaveformRenderMark::updatePlayPosMarkTexture(rendergraph::Context* pContext) { float imgwidth; float imgheight; const float height = m_waveformRenderer->getBreadth(); const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); + if (height == m_playPosHeight && devicePixelRatio == m_playPosDevicePixelRatio) { + return; + } + m_playPosHeight = height; + m_playPosDevicePixelRatio = devicePixelRatio; + const float lineX = 5.5f; imgwidth = 11.f; @@ -434,7 +442,8 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { } painter.end(); - m_playPosMarkTexture.setData(image); + dynamic_cast(m_pPlayPosNode->material()) + .setTexture(std::make_unique(pContext, image)); } void allshader::WaveformRenderMark::drawTriangle(QPainter* painter, @@ -451,15 +460,19 @@ void allshader::WaveformRenderMark::drawTriangle(QPainter* painter, painter->fillPath(triangle, fillColor); } -void allshader::WaveformRenderMark::resizeGL(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(), + m_waveformRenderer->getContext(), + pMark->generateImage( + m_waveformRenderer->getDevicePixelRatio())); + } else { + auto pGraphics = static_cast(pMark->m_pGraphics.get()); + pGraphics->updateTexture(m_waveformRenderer->getContext(), + 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 317f132e26d..e0ef9e1d386 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -2,47 +2,44 @@ #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/geometrynode.h" +#include "rendergraph/node.h" #include "waveform/renderers/waveformrendermarkbase.h" class QDomNode; -class SkinContext; -class QOpenGLTexture; + +namespace rendergraph { +class GeometryNode; +class Context; +} namespace allshader { +class DigitsRenderNode; class WaveformRenderMark; +class WaveformMarkNode; +class WaveformMarkNodeGraphics; } 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(); + + bool isSubtreeBlocked() const override; private: void updateMarkImage(WaveformMarkPointer pMark) override; - void updatePlayPosMarkTexture(); + void updatePlayPosMarkTexture(rendergraph::Context* pContext); void drawTriangle(QPainter* painter, const QBrush& fillColor, @@ -50,16 +47,13 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, QPointF p2, 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); + void drawUntilMark(float x); float getMaxHeightForText() const; + void updateRangeNode(rendergraph::GeometryNode* pNode, + 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 +62,76 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, bool m_isSlipRenderer; + rendergraph::Node* m_pRangeNodesParent{}; + rendergraph::Node* m_pMarkNodesParent{}; + + rendergraph::GeometryNode* m_pPlayPosNode; + float m_playPosHeight; + float m_playPosDevicePixelRatio; + + DigitsRenderNode* m_pDigitsRenderNode{}; + DISALLOW_COPY_AND_ASSIGN(WaveformRenderMark); }; + +// On the use of QPainter: +// +// The renderers in this folder are optimized to use GLSL shaders and refrain +// from using QPainter on the QOpenGLWindow, which causes degredated performance. +// +// This renderer does use QPainter (indirectly, in WaveformMark::generateImage), but +// 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 allshader::WaveformMarkNode : public rendergraph::GeometryNode { + public: + WaveformMark* m_pOwner{}; + + WaveformMarkNode(WaveformMark* pOwner, rendergraph::Context* pContext, const QImage& image); + void updateTexture(rendergraph::Context* pContext, const QImage& image); + void update(float x, float y, float devicePixelRatio); + float textureWidth() const { + return m_textureWidth; + } + float textureHeight() const { + return m_textureHeight; + } + + public: + float m_textureWidth{}; + float m_textureHeight{}; +}; + +class allshader::WaveformMarkNodeGraphics : public ::WaveformMark::Graphics { + public: + WaveformMarkNodeGraphics(WaveformMark* pOwner, + rendergraph::Context* pContext, + const QImage& image); + void updateTexture(rendergraph::Context* pContext, const QImage& image) { + waveformMarkNode()->updateTexture(pContext, image); + } + void update(float x, float y, float devicePixelRatio) { + waveformMarkNode()->update(x, y, devicePixelRatio); + } + float textureWidth() const { + return waveformMarkNode()->textureWidth(); + } + float textureHeight() const { + return waveformMarkNode()->textureHeight(); + } + void attachNode(std::unique_ptr pNode) { + DEBUG_ASSERT(!m_pNode); + m_pNode = std::move(pNode); + } + std::unique_ptr detachNode() { + return std::move(m_pNode); + } + + private: + WaveformMarkNode* waveformMarkNode() const { + DEBUG_ASSERT(m_pNode); + return static_cast(m_pNode.get()); + } + + std::unique_ptr m_pNode; +}; diff --git a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp index 6b3a33d4936..1de0c3c88af 100644 --- a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp +++ b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp @@ -1,71 +1,46 @@ #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& skinContext) { m_markRanges.clear(); - m_markRanges.reserve(1); QDomNode child = node.firstChild(); while (!child.isNull()) { if (child.nodeName() == "MarkRange") { - m_markRanges.push_back( - WaveformMarkRange( - m_waveformRenderer->getGroup(), - child, - context, - *m_waveformRenderer->getWaveformSignalColors())); + addRange(WaveformMarkRange( + m_waveformRenderer->getGroup(), + child, + skinContext, + *m_waveformRenderer->getWaveformSignalColors())); } child = child.nextSibling(); } } -void allshader::WaveformRenderMarkRange::paintGL() { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - 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); +void WaveformRenderMarkRange::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); +} - m_shader.setUniformValue(matrixLocation, matrix); +void WaveformRenderMarkRange::update() { + GeometryNode* pChild = static_cast(firstChild()); - 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 +64,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 +72,36 @@ 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) { + auto pNode = std::make_unique(); + pChild = pNode.get(); + pChild->initForRectangles(1); + appendChildNode(std::move(pNode)); + } + + updateNode(pChild, + 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 pNode = detachChildNode(pChild); + pChild = static_cast(pChild->nextSibling()); + } +} + +void WaveformRenderMarkRange::updateNode(GeometryNode* pChild, + QColor color, + QVector2D lt, + QVector2D rb) { + VertexUpdater vertexUpdater{pChild->geometry().vertexDataAs()}; + vertexUpdater.addRectangle(lt, rb); + pChild->material().setUniform(1, color); + pChild->markDirtyGeometry(); + pChild->markDirtyMaterial(); } + +} // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendermarkrange.h b/src/waveform/renderers/allshader/waveformrendermarkrange.h index 0c46a8dd5b8..140eca76c34 100644 --- a/src/waveform/renderers/allshader/waveformrendermarkrange.h +++ b/src/waveform/renderers/allshader/waveformrendermarkrange.h @@ -1,33 +1,46 @@ #pragma once #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 rendergraph + namespace allshader { class WaveformRenderMarkRange; -} +} // namespace allshader -class allshader::WaveformRenderMarkRange final : public allshader::WaveformRenderer { +class allshader::WaveformRenderMarkRange final : public ::WaveformRendererAbstract, + public rendergraph::Node { public: explicit WaveformRenderMarkRange(WaveformWidgetRenderer* waveformWidget); - void setup(const QDomNode& node, const SkinContext& context) override; + void addRange(WaveformMarkRange&& range) { + m_markRanges.push_back(std::move(range)); + } + + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + + void setup(const QDomNode& node, const SkinContext& skinContext) override; - void initializeGL() override; - void paintGL() override; + void update(); private: - void fillRect(const QRectF& rect, QColor color); + void updateNode(rendergraph::GeometryNode* pChild, + 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/waveformmark.h b/src/waveform/renderers/waveformmark.h index 66a9bbc40cf..623feb89ef6 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -5,10 +5,10 @@ #include "control/controlproxy.h" #include "track/cue.h" +#include "waveform/renderers/waveformsignalcolors.h" #include "waveform/waveformmarklabel.h" class SkinContext; -class WaveformSignalColors; class QOpenGLTexture; namespace allshader { diff --git a/src/waveform/renderers/waveformmarkrange.h b/src/waveform/renderers/waveformmarkrange.h index 15100bf8f03..5ac530e86af 100644 --- a/src/waveform/renderers/waveformmarkrange.h +++ b/src/waveform/renderers/waveformmarkrange.h @@ -56,7 +56,7 @@ class WaveformMarkRange { WaveformMarkLabel m_durationLabel; private: - void generateImage(int weidth, int height); + void generateImage(int width, int height); std::unique_ptr m_markStartPointControl; std::unique_ptr m_markEndPointControl; diff --git a/src/waveform/renderers/waveformrendererabstract.h b/src/waveform/renderers/waveformrendererabstract.h index 6c15dd523d5..62ab0a3930e 100644 --- a/src/waveform/renderers/waveformrendererabstract.h +++ b/src/waveform/renderers/waveformrendererabstract.h @@ -2,6 +2,8 @@ #include +#include "rendergraph/node.h" + QT_FORWARD_DECLARE_CLASS(QDomNode) QT_FORWARD_DECLARE_CLASS(QPaintEvent) QT_FORWARD_DECLARE_CLASS(QPainter) @@ -9,10 +11,6 @@ QT_FORWARD_DECLARE_CLASS(QPainter) class SkinContext; class WaveformWidgetRenderer; -namespace allshader { -class WaveformRendererAbstract; -} - class WaveformRendererAbstract { public: /// The type of cursor for which the waveform is rendered @@ -32,9 +30,6 @@ class WaveformRendererAbstract { virtual void onResize() {} virtual void onSetTrack() {} - virtual allshader::WaveformRendererAbstract* allshaderWaveformRenderer() { - return nullptr; - } protected: bool isDirty() const { diff --git a/src/waveform/renderers/waveformrendermarkbase.cpp b/src/waveform/renderers/waveformrendermarkbase.cpp index f250b73acc1..b2293d1448d 100644 --- a/src/waveform/renderers/waveformrendermarkbase.cpp +++ b/src/waveform/renderers/waveformrendermarkbase.cpp @@ -14,9 +14,13 @@ WaveformRenderMarkBase::WaveformRenderMarkBase( void WaveformRenderMarkBase::setup(const QDomNode& node, const SkinContext& context) { WaveformSignalColors signalColors = *m_waveformRenderer->getWaveformSignalColors(); m_marks.setup(m_waveformRenderer->getGroup(), node, context, signalColors); +} + +bool WaveformRenderMarkBase::init() { m_marks.connectSamplePositionChanged(this, &WaveformRenderMarkBase::onMarkChanged); m_marks.connectSampleEndPositionChanged(this, &WaveformRenderMarkBase::onMarkChanged); m_marks.connectVisibleChanged(this, &WaveformRenderMarkBase::onMarkChanged); + return true; } void WaveformRenderMarkBase::onSetTrack() { diff --git a/src/waveform/renderers/waveformrendermarkbase.h b/src/waveform/renderers/waveformrendermarkbase.h index b5e4d51dd25..8efc35aebd4 100644 --- a/src/waveform/renderers/waveformrendermarkbase.h +++ b/src/waveform/renderers/waveformrendermarkbase.h @@ -17,6 +17,8 @@ class WaveformRenderMarkBase : public QObject, public WaveformRendererAbstract { void setup(const QDomNode& node, const SkinContext& context) override; + bool init() override; + // Called when a new track is loaded. void onSetTrack() override; diff --git a/src/waveform/renderers/waveformwidgetrenderer.cpp b/src/waveform/renderers/waveformwidgetrenderer.cpp index ffefe5d6d45..0d5726b6054 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.cpp +++ b/src/waveform/renderers/waveformwidgetrenderer.cpp @@ -36,13 +36,11 @@ WaveformWidgetRenderer::WaveformWidgetRenderer(const QString& group) // Really create some to manage those; m_visualPlayPosition(nullptr), m_totalVSamples(0), - m_pRateRatioCO(nullptr), - m_pGainControlObject(nullptr), m_gain(1.0), - m_pTrackSamplesControlObject(nullptr), - m_trackSamples(0), + m_trackSamples(0.0), m_scaleFactor(1.0), m_playMarkerPosition(s_defaultPlayMarkerPosition), + m_pContext(nullptr), m_passthroughEnabled(false) { //qDebug() << "WaveformWidgetRenderer"; for (int type = ::WaveformRendererAbstract::Play; @@ -76,10 +74,6 @@ WaveformWidgetRenderer::~WaveformWidgetRenderer() { delete m_rendererStack[i]; } - delete m_pRateRatioCO; - delete m_pGainControlObject; - delete m_pTrackSamplesControlObject; - #ifdef WAVEFORMWIDGETRENDERER_DEBUG delete m_timer; #endif @@ -90,11 +84,11 @@ bool WaveformWidgetRenderer::init() { m_visualPlayPosition = VisualPlayPosition::getVisualPlayPosition(m_group); - m_pRateRatioCO = new ControlProxy( + m_pRateRatioCO = std::make_unique( m_group, "rate_ratio"); - m_pGainControlObject = new ControlProxy( + m_pGainControlObject = std::make_unique( m_group, "total_gain"); - m_pTrackSamplesControlObject = new ControlProxy( + m_pTrackSamplesControlObject = std::make_unique( m_group, "track_samples"); for (int i = 0; i < m_rendererStack.size(); ++i) { @@ -419,7 +413,7 @@ void WaveformWidgetRenderer::setDisplayBeatGridAlpha(int alpha) { void WaveformWidgetRenderer::setTrack(TrackPointer track) { m_pTrack = track; //used to postpone first display until track sample is actually available - m_trackSamples = -1; + m_trackSamples = -1.0; for (int i = 0; i < m_rendererStack.size(); ++i) { m_rendererStack[i]->onSetTrack(); diff --git a/src/waveform/renderers/waveformwidgetrenderer.h b/src/waveform/renderers/waveformwidgetrenderer.h index 47cd21b67a6..fc79ecf7b51 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.h +++ b/src/waveform/renderers/waveformwidgetrenderer.h @@ -15,6 +15,10 @@ class VSyncThread; class QPainter; class WaveformRendererAbstract; +namespace rendergraph { +class Context; +} + class WaveformWidgetRenderer { public: static const double s_waveformMinZoom; @@ -192,6 +196,14 @@ class WaveformWidgetRenderer { return m_trackSamples <= 0.0 || m_pos[::WaveformRendererAbstract::Play] == -1; } + void setContext(rendergraph::Context* pContext) { + m_pContext = pContext; + } + + rendergraph::Context* getContext() const { + return m_pContext; + } + protected: const QString m_group; TrackPointer m_pTrack; @@ -219,14 +231,16 @@ class WaveformWidgetRenderer { QSharedPointer m_visualPlayPosition; int m_posVSample[2]; int m_totalVSamples; - ControlProxy* m_pRateRatioCO; - ControlProxy* m_pGainControlObject; + std::unique_ptr m_pRateRatioCO; + std::unique_ptr m_pGainControlObject; + std::unique_ptr m_pTrackSamplesControlObject; double m_gain; - ControlProxy* m_pTrackSamplesControlObject; double m_trackSamples; double m_scaleFactor; double m_playMarkerPosition; // 0.0 - left, 0.5 - center, 1.0 - right + rendergraph::Context* m_pContext; + #ifdef WAVEFORMWIDGETRENDERER_DEBUG PerformanceTimer* m_timer; int m_lastFrameTime; diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index 2b507ff7afc..192392a57ed 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" @@ -22,59 +22,102 @@ namespace allshader { +template +std::unique_ptr castUniqueNodePtr(std::unique_ptr&& p) { + if (dynamic_cast(p.get())) { + return std::unique_ptr( + dynamic_cast(p.release())); + } + return std::unique_ptr(); +} + WaveformWidget::WaveformWidget(QWidget* parent, WaveformWidgetType::Type type, const QString& group, WaveformRendererSignalBase::Options options) : WGLWidget(parent), WaveformWidgetAbstract(group) { - addRenderer(); - addRenderer(); - addRenderer(); - addRenderer(); + auto pTopNode = std::make_unique(); + auto pOpacityNode = std::make_unique(); + + pTopNode->appendChildNode(addRendererNode()); + pOpacityNode->appendChildNode(addRendererNode()); + pOpacityNode->appendChildNode(addRendererNode()); + auto pWaveformRenderMarkRange = addRendererNode(); + m_pWaveformRenderMarkRange = pWaveformRenderMarkRange.get(); + pOpacityNode->appendChildNode(std::move(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(); + pOpacityNode->appendChildNode(addRendererNode()); #endif - allshader::WaveformRendererSignalBase* waveformSignalRenderer = - addWaveformSignalRenderer( - type, options, ::WaveformRendererAbstract::Play); - - addRenderer(); - addRenderer(); + auto pWaveformSignalRenderer = addWaveformSignalRenderer( + type, options, ::WaveformRendererAbstract::Play); + const bool supportsSlip = pWaveformSignalRenderer && pWaveformSignalRenderer->supportsSlip(); + auto pNode = castUniqueNodePtr(std::move(pWaveformSignalRenderer)); + if (pNode) { + pOpacityNode->appendChildNode(std::move(pNode)); + } + pOpacityNode->appendChildNode(addRendererNode()); + auto pWaveformRenderMark = addRendererNode(); + m_pWaveformRenderMark = pWaveformRenderMark.get(); + pOpacityNode->appendChildNode(std::move(pWaveformRenderMark)); // if the signal renderer supports slip, we add it again, now for slip, together with the // other slip renderers - if (waveformSignalRenderer && waveformSignalRenderer->supportsSlip()) { + if (supportsSlip) { // The following renderer will add an overlay waveform if a slip is in progress - addRenderer(); - addRenderer(::WaveformRendererAbstract::Slip); + pOpacityNode->appendChildNode(addRendererNode()); + pOpacityNode->appendChildNode( + addRendererNode( + ::WaveformRendererAbstract::Slip)); #ifdef __STEM__ - addRenderer(::WaveformRendererAbstract::Slip); + pOpacityNode->appendChildNode( + addRendererNode( + ::WaveformRendererAbstract::Slip)); #endif - addWaveformSignalRenderer(type, options, ::WaveformRendererAbstract::Slip); - addRenderer(::WaveformRendererAbstract::Slip); - addRenderer(::WaveformRendererAbstract::Slip); + auto pWaveformSignalRenderer = addWaveformSignalRenderer( + type, options, ::WaveformRendererAbstract::Slip); + auto pNode = castUniqueNodePtr(std::move(pWaveformSignalRenderer)); + if (pNode) { + pOpacityNode->appendChildNode(std::move(pNode)); + } + pOpacityNode->appendChildNode( + addRendererNode( + ::WaveformRendererAbstract::Slip)); + pOpacityNode->appendChildNode( + addRendererNode( + ::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(); } -allshader::WaveformRendererSignalBase* +std::unique_ptr WaveformWidget::addWaveformSignalRenderer(WaveformWidgetType::Type type, WaveformRendererSignalBase::Options options, ::WaveformRendererAbstract::PositionSource positionSource) { + return std::unique_ptr( + addWaveformSignalRendererInner(type, options, positionSource)); +} + +allshader::WaveformRendererSignalBase* +WaveformWidget::addWaveformSignalRendererInner(WaveformWidgetType::Type type, + WaveformRendererSignalBase::Options options, + ::WaveformRendererAbstract::PositionSource positionSource) { #ifndef QT_OPENGL_ES_2 if (options & allshader::WaveformRendererSignalBase::Option::HighDetail) { switch (type) { @@ -117,15 +160,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 +175,18 @@ void WaveformWidget::castToQWidget() { } void WaveformWidget::initializeGL() { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->initializeGL(); - } +} + +void WaveformWidget::resizeRenderer(int, int, float) { + // defer to resizeGL } void WaveformWidget::resizeGL(int w, int h) { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->resizeGL(w, h); - } + w = static_cast(std::lroundf(static_cast(w) / devicePixelRatio())); + h = static_cast(std::lroundf(static_cast(h) / devicePixelRatio())); + + m_pEngine->resize(w, h); + WaveformWidgetRenderer::resizeRenderer(w, h, devicePixelRatio()); } void WaveformWidget::paintEvent(QPaintEvent* event) { diff --git a/src/waveform/widgets/allshader/waveformwidget.h b/src/waveform/widgets/allshader/waveformwidget.h index f6c0877975e..41c195118f3 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, @@ -23,6 +27,8 @@ class allshader::WaveformWidget final : public ::WGLWidget, return m_type; } + void resizeRenderer(int width, int height, float devicePixelRatio) override; + // override for WaveformWidgetAbstract mixxx::Duration render() override; @@ -36,18 +42,32 @@ class allshader::WaveformWidget final : public ::WGLWidget, static WaveformWidgetVars vars(); static WaveformRendererSignalBase::Options supportedOptions(WaveformWidgetType::Type type); + template + inline std::unique_ptr addRendererNode(Args&&... args) { + return std::unique_ptr(addRenderer(std::forward(args)...)); + } + private: void castToQWidget() override; void paintEvent(QPaintEvent* event) override; void wheelEvent(QWheelEvent* event) override; void leaveEvent(QEvent* event) override; - allshader::WaveformRendererSignalBase* addWaveformSignalRenderer( + std::unique_ptr addWaveformSignalRenderer( + WaveformWidgetType::Type type, + WaveformRendererSignalBase::Options options, + ::WaveformRendererAbstract::PositionSource positionSource); + + allshader::WaveformRendererSignalBase* addWaveformSignalRendererInner( WaveformWidgetType::Type type, WaveformRendererSignalBase::Options options, ::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); };