diff --git a/CMakeLists.txt b/CMakeLists.txt index 322ee607ae84..998fad49f87a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2744,6 +2744,10 @@ if(QML) target_link_libraries(mixxx-qml-lib PUBLIC mixxx-proto) target_link_libraries(mixxx-qml-libplugin PUBLIC mixxx-proto) + # Rendergraph + add_subdirectory(src/rendergraph/scenegraph) + target_link_libraries(mixxx-qml-lib PUBLIC rendergraph_sg) + target_precompile_headers(mixxx-qml-lib PUBLIC ${MIXXX_COMMON_PRECOMPILED_HEADER} ) @@ -2764,6 +2768,7 @@ if(QML) res/qml/Mixxx/Controls/WaveformOverviewHotcueMarker.qml res/qml/Mixxx/Controls/WaveformOverviewMarker.qml res/qml/Mixxx/Controls/WaveformOverview.qml + res/qml/Mixxx/Controls/WaveformDisplay.qml ) target_link_libraries(mixxx-qml-lib PRIVATE mixxx-qml-mixxxcontrolsplugin) @@ -2786,6 +2791,8 @@ if(QML) src/qml/qmlvisibleeffectsmodel.cpp src/qml/qmlchainpresetmodel.cpp src/qml/qmlwaveformoverview.cpp + src/qml/qmlwaveformdisplay.cpp + src/waveform/renderers/waveformdisplayrange.cpp # The following sources need to be in this target to get QML_ELEMENT properly interpreted src/control/controlmodel.cpp src/control/controlsortfiltermodel.cpp @@ -3804,6 +3811,11 @@ if(VINYLCONTROL) target_link_libraries(mixxx-lib PRIVATE mixxx-xwax) endif() +# rendergraph +add_subdirectory(src/rendergraph/opengl) +add_subdirectory(res/shaders/rendergraph) +target_link_libraries(mixxx-lib PUBLIC rendergraph_gl) + # WavPack audio file support find_package(wavpack) default_option(WAVPACK "WavPack audio file support" "wavpack_FOUND") diff --git a/res/mixxx.qrc b/res/mixxx.qrc index 8011fe39f943..c21ec6a8008b 100644 --- a/res/mixxx.qrc +++ b/res/mixxx.qrc @@ -96,5 +96,9 @@ shaders/passthrough.vert shaders/rgbsignal.frag shaders/stackedsignal.frag + shaders/rendergraph/endoftrack.frag.gl + shaders/rendergraph/endoftrack.vert.gl + shaders/rendergraph/texture.frag.gl + shaders/rendergraph/texture.vert.gl diff --git a/res/qml/Mixxx/Controls/WaveformDisplay.qml b/res/qml/Mixxx/Controls/WaveformDisplay.qml new file mode 100644 index 000000000000..3ed11593cadd --- /dev/null +++ b/res/qml/Mixxx/Controls/WaveformDisplay.qml @@ -0,0 +1,7 @@ +import Mixxx 1.0 as Mixxx + +Mixxx.WaveformDisplay { + id: root + + player: Mixxx.PlayerManager.getPlayer(root.group) +} diff --git a/res/qml/WaveformDisplay.qml b/res/qml/WaveformDisplay.qml new file mode 100644 index 000000000000..2dbcd17d74d7 --- /dev/null +++ b/res/qml/WaveformDisplay.qml @@ -0,0 +1,16 @@ +import "." as Skin +import Mixxx 1.0 as Mixxx +import Mixxx.Controls 1.0 as MixxxControls +import QtQuick 2.12 +import "Theme" + +Item { + id: root + + required property string group + + MixxxControls.WaveformDisplay { + anchors.fill: parent + group: root.group + } +} diff --git a/res/shaders/rendergraph/CMakeLists.txt b/res/shaders/rendergraph/CMakeLists.txt new file mode 100644 index 000000000000..3241c5a84c96 --- /dev/null +++ b/res/shaders/rendergraph/CMakeLists.txt @@ -0,0 +1,33 @@ +set(shaders + endoftrack.frag + endoftrack.vert + pattern.frag + pattern.vert + rgb.frag + rgb.vert + rgba.frag + rgba.vert + texture.frag + texture.vert + unicolor.frag + unicolor.vert +) + +qt6_add_shaders(rendergraph_sg "shaders-sg" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + /shaders/rendergraph + FILES + ${shaders} +) + +include(generated_shaders_gl.cmake) + +qt_add_resources(rendergraph_gl "shaders-gl" + PREFIX + /shaders/rendergraph + FILES + ${generated_shaders_gl} +) diff --git a/res/shaders/rendergraph/README b/res/shaders/rendergraph/README new file mode 100644 index 000000000000..04bb5086e24a --- /dev/null +++ b/res/shaders/rendergraph/README @@ -0,0 +1,9 @@ +Generate the GLSL shaders from the spirv shaders by running + +generate_shaders_gl.pl + +(Make sure qsb and spirv commands are in your path. E.g: +export PATH=$PATH:~/VulkanSDK/1.3.283.0/macOS/bin:~/Qt/6.7.2/macos/bin +) + +Since Qt 6.6 we should be able to access this programmatically with QShader, but for now I do it manually diff --git a/res/shaders/rendergraph/endoftrack.frag b/res/shaders/rendergraph/endoftrack.frag new file mode 100644 index 000000000000..0f6712e87d5d --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.frag @@ -0,0 +1,17 @@ +#version 440 + +layout(location = 0) in float vGradient; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + vec4 color; +} +ubuf; + +void main() { + float minAlpha = 0.5 * ubuf.color.w; + float maxAlpha = 0.83 * ubuf.color.w; + float alpha = mix(minAlpha, maxAlpha, max(0.0, vGradient)); + // premultiple alpha + fragColor = vec4(ubuf.color.xyz * alpha, alpha); +} diff --git a/res/shaders/rendergraph/endoftrack.frag.gl b/res/shaders/rendergraph/endoftrack.frag.gl new file mode 100644 index 000000000000..78de913751a3 --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.frag.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + vec4 color; +}; + +uniform buf ubuf; + +varying float vGradient; + +void main() +{ + float minAlpha = 0.5 * ubuf.color.w; + float maxAlpha = 0.829999983310699462890625 * ubuf.color.w; + float alpha = mix(minAlpha, maxAlpha, max(0.0, vGradient)); + gl_FragData[0] = vec4(ubuf.color.xyz * alpha, alpha); +} diff --git a/res/shaders/rendergraph/endoftrack.vert b/res/shaders/rendergraph/endoftrack.vert new file mode 100644 index 000000000000..101cdb324e4e --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.vert @@ -0,0 +1,10 @@ +#version 440 + +layout(location = 0) in vec4 position; +layout(location = 1) in float gradient; +layout(location = 0) out float vGradient; + +void main() { + vGradient = gradient; + gl_Position = position; +} diff --git a/res/shaders/rendergraph/endoftrack.vert.gl b/res/shaders/rendergraph/endoftrack.vert.gl new file mode 100644 index 000000000000..da4626bbf7c5 --- /dev/null +++ b/res/shaders/rendergraph/endoftrack.vert.gl @@ -0,0 +1,12 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying float vGradient; +attribute float gradient; +attribute vec4 position; + +void main() +{ + vGradient = gradient; + gl_Position = position; +} diff --git a/res/shaders/rendergraph/generate_shaders_gl.pl b/res/shaders/rendergraph/generate_shaders_gl.pl new file mode 100755 index 000000000000..c528eb947475 --- /dev/null +++ b/res/shaders/rendergraph/generate_shaders_gl.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +my @files = (glob("*.vert"),glob("*.frag")); + +open(GENERATED,">generated_shaders_gl.cmake"); +print(GENERATED "set(generated_shaders_gl\n"); +for $file (@files) +{ + system("qsb","--glsl","120",$file,"-o","/tmp/$$-$file.qsb"); + open(INFILE,"qsb --dump /tmp/$$-$file.qsb|"); + open(OUTFILE,">$file.gl"); + $ok = 0; + $comment_added = 0; + print "Generating $file.gl from $file\n"; + while () + { + if ($in_shader_block == 2) + { + if (m/^\*\*/) + { + $in_shader_block = 0; + $ok = 1; + } + else + { + if (!$comment_added) + { + if (!m/^#/) + { + print(OUTFILE "//// GENERATED - EDITS WILL BE OVERWRITTEN\n"); + $comment_added = 1; + } + } + print OUTFILE "$_"; + } + } + elsif ($in_shader_block == 1) + { + chomp($_); + if ($_ eq "Contents:") + { + $in_shader_block = 2; + } + } + else + { + chomp($_); + if ($_ eq "Shader 1: GLSL 120 [Standard]") + { + $in_shader_block = 1; + } + } + } + close INFILE; + close OUTFILE; + if($ok) + { + print(GENERATED " $file.gl\n"); + } + else + { + print STDERR "Failed to generated $file.gl"; + unlink("$file.gl") + } + unlink("/tmp/$$-$file.qsb"); +} +print(GENERATED ")\n"); +close GENERATED; diff --git a/res/shaders/rendergraph/generated_shaders_gl.cmake b/res/shaders/rendergraph/generated_shaders_gl.cmake new file mode 100644 index 000000000000..951c85c3669a --- /dev/null +++ b/res/shaders/rendergraph/generated_shaders_gl.cmake @@ -0,0 +1,14 @@ +set(generated_shaders_gl + endoftrack.vert.gl + pattern.vert.gl + rgb.vert.gl + rgba.vert.gl + texture.vert.gl + unicolor.vert.gl + endoftrack.frag.gl + pattern.frag.gl + rgb.frag.gl + rgba.frag.gl + texture.frag.gl + unicolor.frag.gl +) diff --git a/res/shaders/rendergraph/pattern.frag b/res/shaders/rendergraph/pattern.frag new file mode 100644 index 000000000000..5aa3d1556b1e --- /dev/null +++ b/res/shaders/rendergraph/pattern.frag @@ -0,0 +1,9 @@ +#version 440 + +layout(binding = 1) uniform sampler2D texture1; +layout(location = 0) in vec2 vTexcoord; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = texture(texture1, fract(vTexcoord)); +} diff --git a/res/shaders/rendergraph/pattern.frag.gl b/res/shaders/rendergraph/pattern.frag.gl new file mode 100644 index 000000000000..376c71668ba4 --- /dev/null +++ b/res/shaders/rendergraph/pattern.frag.gl @@ -0,0 +1,11 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +uniform sampler2D texture1; + +varying vec2 vTexcoord; + +void main() +{ + gl_FragData[0] = texture2D(texture1, fract(vTexcoord)); +} diff --git a/res/shaders/rendergraph/pattern.vert b/res/shaders/rendergraph/pattern.vert new file mode 100644 index 000000000000..07b3d7f1f3ba --- /dev/null +++ b/res/shaders/rendergraph/pattern.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; +layout(location = 0) out vec2 vTexcoord; + +void main() { + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/pattern.vert.gl b/res/shaders/rendergraph/pattern.vert.gl new file mode 100644 index 000000000000..a3d58014be32 --- /dev/null +++ b/res/shaders/rendergraph/pattern.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec2 vTexcoord; +attribute vec2 texcoord; +attribute vec4 position; + +void main() +{ + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgb.frag b/res/shaders/rendergraph/rgb.frag new file mode 100644 index 000000000000..0a808489f5b2 --- /dev/null +++ b/res/shaders/rendergraph/rgb.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec3 vColor; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(vColor, 1.0); +} diff --git a/res/shaders/rendergraph/rgb.frag.gl b/res/shaders/rendergraph/rgb.frag.gl new file mode 100644 index 000000000000..b8a61f8682f9 --- /dev/null +++ b/res/shaders/rendergraph/rgb.frag.gl @@ -0,0 +1,9 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying vec3 vColor; + +void main() +{ + gl_FragData[0] = vec4(vColor, 1.0); +} diff --git a/res/shaders/rendergraph/rgb.vert b/res/shaders/rendergraph/rgb.vert new file mode 100644 index 000000000000..6568d01f187c --- /dev/null +++ b/res/shaders/rendergraph/rgb.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec3 color; +layout(location = 0) out vec3 vColor; + +void main() { + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgb.vert.gl b/res/shaders/rendergraph/rgb.vert.gl new file mode 100644 index 000000000000..53e86e4501cc --- /dev/null +++ b/res/shaders/rendergraph/rgb.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec3 vColor; +attribute vec3 color; +attribute vec4 position; + +void main() +{ + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgba.frag b/res/shaders/rendergraph/rgba.frag new file mode 100644 index 000000000000..5cf90a770eae --- /dev/null +++ b/res/shaders/rendergraph/rgba.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(vColor.xyz * vColor.w, vColor.w); // premultiple alpha +} diff --git a/res/shaders/rendergraph/rgba.frag.gl b/res/shaders/rendergraph/rgba.frag.gl new file mode 100644 index 000000000000..a831457b9681 --- /dev/null +++ b/res/shaders/rendergraph/rgba.frag.gl @@ -0,0 +1,9 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying vec4 vColor; + +void main() +{ + gl_FragData[0] = vec4(vColor.xyz * vColor.w, vColor.w); +} diff --git a/res/shaders/rendergraph/rgba.vert b/res/shaders/rendergraph/rgba.vert new file mode 100644 index 000000000000..d5ce8b2d9db6 --- /dev/null +++ b/res/shaders/rendergraph/rgba.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec4 color; +layout(location = 0) out vec4 vColor; + +void main() { + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/rgba.vert.gl b/res/shaders/rendergraph/rgba.vert.gl new file mode 100644 index 000000000000..df2bcf93236e --- /dev/null +++ b/res/shaders/rendergraph/rgba.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec4 vColor; +attribute vec4 color; +attribute vec4 position; + +void main() +{ + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/texture.frag b/res/shaders/rendergraph/texture.frag new file mode 100644 index 000000000000..bbe37bccd69c --- /dev/null +++ b/res/shaders/rendergraph/texture.frag @@ -0,0 +1,9 @@ +#version 440 + +layout(binding = 1) uniform sampler2D texture1; +layout(location = 0) in vec2 vTexcoord; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = texture(texture1, vTexcoord); +} diff --git a/res/shaders/rendergraph/texture.frag.gl b/res/shaders/rendergraph/texture.frag.gl new file mode 100644 index 000000000000..b2d03f1352c6 --- /dev/null +++ b/res/shaders/rendergraph/texture.frag.gl @@ -0,0 +1,11 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +uniform sampler2D texture1; + +varying vec2 vTexcoord; + +void main() +{ + gl_FragData[0] = texture2D(texture1, vTexcoord); +} diff --git a/res/shaders/rendergraph/texture.vert b/res/shaders/rendergraph/texture.vert new file mode 100644 index 000000000000..07b3d7f1f3ba --- /dev/null +++ b/res/shaders/rendergraph/texture.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; +layout(location = 0) out vec2 vTexcoord; + +void main() { + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/texture.vert.gl b/res/shaders/rendergraph/texture.vert.gl new file mode 100644 index 000000000000..a3d58014be32 --- /dev/null +++ b/res/shaders/rendergraph/texture.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec2 vTexcoord; +attribute vec2 texcoord; +attribute vec4 position; + +void main() +{ + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/texture.vert.qsb b/res/shaders/rendergraph/texture.vert.qsb new file mode 100644 index 000000000000..84fa7e916fe4 Binary files /dev/null and b/res/shaders/rendergraph/texture.vert.qsb differ diff --git a/res/shaders/rendergraph/unicolor.frag b/res/shaders/rendergraph/unicolor.frag new file mode 100644 index 000000000000..e4a8535e3492 --- /dev/null +++ b/res/shaders/rendergraph/unicolor.frag @@ -0,0 +1,13 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; + vec4 color; +} +ubuf; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w); // premultiple alpha +} diff --git a/res/shaders/rendergraph/unicolor.frag.gl b/res/shaders/rendergraph/unicolor.frag.gl new file mode 100644 index 000000000000..bfef503120bd --- /dev/null +++ b/res/shaders/rendergraph/unicolor.frag.gl @@ -0,0 +1,15 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; + vec4 color; +}; + +uniform buf ubuf; + +void main() +{ + gl_FragData[0] = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w); +} diff --git a/res/shaders/rendergraph/unicolor.vert b/res/shaders/rendergraph/unicolor.vert new file mode 100644 index 000000000000..9e268c18fbaa --- /dev/null +++ b/res/shaders/rendergraph/unicolor.vert @@ -0,0 +1,13 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; + vec4 color; +} +ubuf; + +layout(location = 0) in vec4 position; + +void main() { + gl_Position = ubuf.matrix * position; +} diff --git a/res/shaders/rendergraph/unicolor.vert.gl b/res/shaders/rendergraph/unicolor.vert.gl new file mode 100644 index 000000000000..d126f3dab584 --- /dev/null +++ b/res/shaders/rendergraph/unicolor.vert.gl @@ -0,0 +1,17 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; + vec4 color; +}; + +uniform buf ubuf; + +attribute vec4 position; + +void main() +{ + gl_Position = ubuf.matrix * position; +} diff --git a/src/qml/qmlplayermanagerproxy.cpp b/src/qml/qmlplayermanagerproxy.cpp index d8c3fc4930e5..2544b34f3ad5 100644 --- a/src/qml/qmlplayermanagerproxy.cpp +++ b/src/qml/qmlplayermanagerproxy.cpp @@ -14,7 +14,7 @@ QmlPlayerManagerProxy::QmlPlayerManagerProxy( : QObject(parent), m_pPlayerManager(pPlayerManager) { } -QObject* QmlPlayerManagerProxy::getPlayer(const QString& group) { +QmlPlayerProxy* QmlPlayerManagerProxy::getPlayer(const QString& group) { BaseTrackPlayer* pPlayer = m_pPlayerManager->getPlayer(group); if (!pPlayer) { qWarning() << "PlayerManagerProxy failed to find player for group" << group; diff --git a/src/qml/qmlplayermanagerproxy.h b/src/qml/qmlplayermanagerproxy.h index e0226da802f6..004123e0bd05 100644 --- a/src/qml/qmlplayermanagerproxy.h +++ b/src/qml/qmlplayermanagerproxy.h @@ -18,7 +18,7 @@ class QmlPlayerManagerProxy : public QObject { std::shared_ptr pPlayerManager, QObject* parent = nullptr); - Q_INVOKABLE QObject* getPlayer(const QString& deck); + Q_INVOKABLE QmlPlayerProxy* getPlayer(const QString& deck); Q_INVOKABLE void loadLocationIntoNextAvailableDeck(const QString& location, bool play = false); Q_INVOKABLE void loadLocationUrlIntoNextAvailableDeck( const QUrl& locationUrl, bool play = false); diff --git a/src/qml/qmlwaveformdisplay.cpp b/src/qml/qmlwaveformdisplay.cpp new file mode 100644 index 000000000000..7bca2d3f88c2 --- /dev/null +++ b/src/qml/qmlwaveformdisplay.cpp @@ -0,0 +1,263 @@ +#include "qml/qmlwaveformdisplay.h" + +#include +#include +#include +#include +#include + +#include "mixer/basetrackplayer.h" +#include "moc_qmlwaveformdisplay.cpp" +#include "qml/qmlplayerproxy.h" +#include "rendergraph/shadercache.h" +#include "waveform/renderers/allshader/waveformrenderbackground.h" +#include "waveform/renderers/allshader/waveformrenderbeat.h" +#include "waveform/renderers/allshader/waveformrendererendoftrack.h" +#include "waveform/renderers/allshader/waveformrendererfiltered.h" +#include "waveform/renderers/allshader/waveformrendererhsv.h" +#include "waveform/renderers/allshader/waveformrendererpreroll.h" +#include "waveform/renderers/allshader/waveformrendererrgb.h" +#include "waveform/renderers/allshader/waveformrenderersimple.h" +#include "waveform/renderers/allshader/waveformrendererslipmode.h" +#include "waveform/renderers/allshader/waveformrendererstem.h" +#include "waveform/renderers/allshader/waveformrenderertextured.h" +#include "waveform/renderers/allshader/waveformrendermark.h" +#include "waveform/renderers/allshader/waveformrendermarkrange.h" + +namespace { +void appendChildTo(std::unique_ptr& pNode, rendergraph::Node* pChild) { + pNode->appendChildNode(std::unique_ptr(pChild)); +} +void appendChildTo(std::unique_ptr& pNode, rendergraph::Node* pChild) { + pNode->appendChildNode(std::unique_ptr(pChild)); +} +} // namespace + +namespace mixxx { +namespace qml { + +QmlWaveformDisplay::QmlWaveformDisplay(QQuickItem* parent) + : QQuickItem(parent), + m_pPlayer(nullptr) { + + setFlag(QQuickItem::ItemHasContents, true); + + connect(this, &QmlWaveformDisplay::windowChanged, this, &QmlWaveformDisplay::slotWindowChanged); + + auto pTopNode = std::make_unique(); + auto pOpacityNode = std::make_unique(); + + appendChildTo(pTopNode, addRenderer()); + appendChildTo(pOpacityNode, addRenderer()); + appendChildTo(pOpacityNode, addRenderer()); + m_pWaveformRenderMarkRange = addRenderer(); + appendChildTo(pOpacityNode, m_pWaveformRenderMarkRange); + +#ifdef __STEM__ + // The following two renderers work in tandem: if the rendered waveform is + // for a stem track, WaveformRendererSignalBase will skip rendering and let + // WaveformRendererStem do the rendering, and vice-versa. + appendChildTo(pOpacityNode, addRenderer()); +#endif + allshader::WaveformRendererSignalBase* waveformSignalRenderer = + addWaveformSignalRenderer( + type, options, ::WaveformRendererAbstract::Play); + appendChildTo(pOpacityNode, waveformSignalRenderer); + + appendChildTo(pOpacityNode, addRenderer()); + m_pWaveformRenderMark = addRenderer(); + appendChildTo(pOpacityNode, m_pWaveformRenderMark); + + // if the signal renderer supports slip, we add it again, now for slip, together with the + // other slip renderers + if (waveformSignalRenderer && waveformSignalRenderer->supportsSlip()) { + // The following renderer will add an overlay waveform if a slip is in progress + appendChildTo(pOpacityNode, addRenderer()); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); +#ifdef __STEM__ + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); +#endif + appendChildTo(pOpacityNode, + addWaveformSignalRenderer( + type, options, ::WaveformRendererAbstract::Slip)); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); + } + + m_pOpacityNode = pOpacityNode.get(); + pTopNode->appendChildNode(std::move(pOpacityNode)); + + m_pGraph = std::move(pTopNode); +} + +QmlWaveformDisplay::~QmlWaveformDisplay() { + m_pGraph.reset(); + rendergraph::ShaderCache::purge(); +} + +void QmlWaveformDisplay::slotWindowChanged(QQuickWindow* window) { + connect(window, &QQuickWindow::frameSwapped, this, &QmlWaveformDisplay::slotFrameSwapped); + m_timer.restart(); +} + +void QmlWaveformDisplay::slotFrameSwapped() { + m_timer.restart(); +} + +void QmlWaveformDisplay::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) { + m_geometryChanged = true; + update(); + QQuickItem::geometryChange(newGeometry, oldGeometry); +} + +QSGNode* QmlWaveformDisplay::updatePaintNode(QSGNode* old, QQuickItem::UpdatePaintNodeData*) { + QSGRectangleNode* clipNode; + + if (!old) { + // clipNode = window()->createRectangleNode(); + // clipNode->setColor(QColor(0, 0, 0, 255)); + // clipNode->setRect(boundingRect()); + + // m_node = std::make_unique(); + // m_node->appendChildNode(std::make_unique()); + // m_node->appendChildNode(std::make_unique()); + // m_node->appendChildNode(std::make_unique()); + + // { + // QImage img(":/example/images/test.png"); + // auto context = rendergraph::createSgContext(window()); + // static_cast(m_node->lastChild()) + // ->setTexture(std::make_unique( + // *context, img)); + // } + + // clipNode->appendChildNode(rendergraph::sgNode(m_node.get())); + m_pOpacityNode->setOpacity(shouldOnlyDrawBackground() ? 0.f : 1.f); + + m_pWaveformRenderMark->update(); + m_pWaveformRenderMarkRange->update(); + + m_pGraph->preprocess(); + m_pGraph->render(); + + old = clipNode; + } else { + clipNode = static_cast(old); + } + + if (m_geometryChanged) { + clipNode->setRect(boundingRect()); + m_geometryChanged = false; + } + + return clipNode; +} + +QmlPlayerProxy* QmlWaveformDisplay::getPlayer() const { + return m_pPlayer; +} + +void QmlWaveformDisplay::setPlayer(QmlPlayerProxy* pPlayer) { + if (m_pPlayer == pPlayer) { + return; + } + + if (m_pPlayer != nullptr) { + m_pPlayer->internalTrackPlayer()->disconnect(this); + } + + m_pPlayer = pPlayer; + + if (m_pPlayer != nullptr) { + setCurrentTrack(m_pPlayer->internalTrackPlayer()->getLoadedTrack()); + connect(m_pPlayer->internalTrackPlayer(), + &BaseTrackPlayer::newTrackLoaded, + this, + &QmlWaveformDisplay::slotTrackLoaded); + connect(m_pPlayer->internalTrackPlayer(), + &BaseTrackPlayer::loadingTrack, + this, + &QmlWaveformDisplay::slotTrackLoading); + connect(m_pPlayer->internalTrackPlayer(), + &BaseTrackPlayer::playerEmpty, + this, + &QmlWaveformDisplay::slotTrackUnloaded); + } + + emit playerChanged(); + update(); +} + +void QmlWaveformDisplay::setGroup(const QString& group) { + if (m_group == group) { + return; + } + + m_group = group; + emit groupChanged(group); + + // TODO m0dB unique_ptr ? + delete m_pWaveformDisplayRange; + m_pWaveformDisplayRange = new WaveformDisplayRange(m_group); + m_pWaveformDisplayRange->init(); +} + +const QString& QmlWaveformDisplay::getGroup() const { + return m_group; +} + +void QmlWaveformDisplay::slotTrackLoaded(TrackPointer pTrack) { + // TODO: Investigate if it's a bug that this debug assertion fails when + // passing tracks on the command line + // DEBUG_ASSERT(m_pCurrentTrack == pTrack); + setCurrentTrack(pTrack); +} + +void QmlWaveformDisplay::slotTrackLoading(TrackPointer pNewTrack, TrackPointer pOldTrack) { + Q_UNUSED(pOldTrack); // only used in DEBUG_ASSERT + DEBUG_ASSERT(m_pCurrentTrack == pOldTrack); + setCurrentTrack(pNewTrack); +} + +void QmlWaveformDisplay::slotTrackUnloaded() { + setCurrentTrack(nullptr); +} + +void QmlWaveformDisplay::setCurrentTrack(TrackPointer pTrack) { + // TODO: Check if this is actually possible + if (m_pCurrentTrack == pTrack) { + return; + } + + if (m_pCurrentTrack != nullptr) { + disconnect(m_pCurrentTrack.get(), nullptr, this, nullptr); + } + + m_pCurrentTrack = pTrack; + if (pTrack != nullptr) { + connect(pTrack.get(), + &Track::waveformSummaryUpdated, + this, + &QmlWaveformDisplay::slotWaveformUpdated); + } + slotWaveformUpdated(); + + if (m_pWaveformDisplayRange) { + m_pWaveformDisplayRange->setTrack(m_pCurrentTrack); + } +} + +void QmlWaveformDisplay::slotWaveformUpdated() { + update(); +} + +} // namespace qml +} // namespace mixxx diff --git a/src/qml/qmlwaveformdisplay.h b/src/qml/qmlwaveformdisplay.h new file mode 100644 index 000000000000..d0689edcfd37 --- /dev/null +++ b/src/qml/qmlwaveformdisplay.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "qml/qmlplayerproxy.h" +#include "track/track.h" +#include "util/performancetimer.h" +#include "waveform/isynctimeprovider.h" + +// #include "rendergraph/opengl/graph.h" +#include "rendergraph/opacitynode.h" + +class WaveformDisplayRange; + +namespace allshader { +class WaveformRenderMark; +class WaveformRenderMarkRange; +} // namespace allshader + +namespace mixxx { +namespace qml { + +class QmlPlayerProxy; + +class QmlWaveformDisplay : public QQuickItem, ISyncTimeProvider { + Q_OBJECT + Q_PROPERTY(QmlPlayerProxy* player READ getPlayer WRITE setPlayer + NOTIFY playerChanged REQUIRED) + Q_PROPERTY(QString group READ getGroup WRITE setGroup NOTIFY groupChanged REQUIRED) + QML_NAMED_ELEMENT(WaveformDisplay) + + public: + QmlWaveformDisplay(QQuickItem* parent = nullptr); + ~QmlWaveformDisplay() override; + + void setPlayer(QmlPlayerProxy* player); + QmlPlayerProxy* getPlayer() const; + + void setGroup(const QString& group); + const QString& getGroup() const; + + QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override; + void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; + + int fromTimerToNextSyncMicros(const PerformanceTimer& timer) override; + int getSyncIntervalTimeMicros() const override { + return m_syncIntervalTimeMicros; + } + private slots: + void slotTrackLoaded(TrackPointer pLoadedTrack); + void slotTrackLoading(TrackPointer pNewTrack, TrackPointer pOldTrack); + void slotTrackUnloaded(); + void slotWaveformUpdated(); + + void slotFrameSwapped(); + void slotWindowChanged(QQuickWindow* window); + signals: + void playerChanged(); + void groupChanged(const QString& group); + + private: + void setCurrentTrack(TrackPointer pTrack); + + QPointer m_pPlayer; + TrackPointer m_pCurrentTrack; + + PerformanceTimer m_timer; + + QString m_group; + WaveformDisplayRange* m_pWaveformDisplayRange{}; + int m_syncIntervalTimeMicros{1000000 / 60}; // TODO don't hardcode + + std::unique_ptr m_pGraph; + rendergraph::OpacityNode* m_pOpacityNode; + allshader::WaveformRenderMark* m_pWaveformRenderMark; + allshader::WaveformRenderMarkRange* m_pWaveformRenderMarkRange; +}; + +} // namespace qml +} // namespace mixxx diff --git a/src/rendergraph/CMakeLists.txt b/src/rendergraph/CMakeLists.txt new file mode 100644 index 000000000000..91bcb99beb5f --- /dev/null +++ b/src/rendergraph/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(opengl) +add_subdirectory(scenegraph) +add_subdirectory(../../res/shaders/rendergraph shaders) diff --git a/src/rendergraph/README b/src/rendergraph/README new file mode 100644 index 000000000000..8110ef888f8e --- /dev/null +++ b/src/rendergraph/README @@ -0,0 +1,29 @@ +rendergraph is an abstraction layer that can be compiled to be used with QtQuick scene graph classes +or with QOpenGL classes. This abstraction layer follows the design of the QtQuick scene graph, with +classes such as Material, Geometry, Node and GeometryNode, but it only gives access to a subset of +its functionality. + +The rendergraph/scenegraph implementation calls the underlying scenegraph classes directly or almost +directly. The opengl/layer implementation class implements classes that mimic the behaviour of the +scenegraph classes. + +The objective is to be able to write code that can be used both within the context of a QWidgets +applications and of a QML application. This includes using the same Vulkan-style GLSL shader code for +both. (The QSGMaterialShader uses the qsb files, QOpenGLShader uses the OpenGL shader code generated +by qsb) + +The common code is in library rendergraph, src/rendergraph + +The scene graph implementation is in library rendergraph_sg, src/rendergraph/scenegraph + +The OpenGL implementation is in library rendergraph_gl, src/rendergraph/opengl + +Example shader and nodes are in library rendergraph_examples, in src/rendergraph/examples. +Note that this code is agnostic of the layer that it will be linked against. + +Example applications are in src/rendergraph/example/sg_example and src/rendergraph/examples/gl_example, +which each link with rendergraph_examples and respectively with rendergraph_gl and rendergraph_sg. + +This is still very much work in progress! + +m0dB diff --git a/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp new file mode 100644 index 000000000000..fd9e5f73ec72 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp @@ -0,0 +1,39 @@ +#include "endoftrackmaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +EndOfTrackMaterial::EndOfTrackMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& EndOfTrackMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "gradient"}); + return set; +} + +/* static */ const UniformSet& EndOfTrackMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.color"}); + return set; +} + +MaterialType* EndOfTrackMaterial::type() const { + static MaterialType type; + return &type; +} + +int EndOfTrackMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr EndOfTrackMaterial::createShader() const { + return std::make_unique( + "endoftrack.vert", "endoftrack.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h new file mode 100644 index 000000000000..e66a825c1641 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h @@ -0,0 +1,21 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class EndOfTrackMaterial; +} + +class rendergraph::EndOfTrackMaterial : public rendergraph::Material { + public: + EndOfTrackMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/material/patternmaterial.cpp b/src/rendergraph/common/rendergraph/material/patternmaterial.cpp new file mode 100644 index 000000000000..e16ca4213d77 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/patternmaterial.cpp @@ -0,0 +1,39 @@ +#include "patternmaterial.h" + +#include +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +using namespace rendergraph; + +PatternMaterial::PatternMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& PatternMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "texcoord"}); + return set; +} + +/* static */ const UniformSet& PatternMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* PatternMaterial::type() const { + static MaterialType type; + return &type; +} + +int PatternMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr PatternMaterial::createShader() const { + return std::make_unique( + "pattern.vert", "pattern.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/patternmaterial.h b/src/rendergraph/common/rendergraph/material/patternmaterial.h new file mode 100644 index 000000000000..0df75c073714 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/patternmaterial.h @@ -0,0 +1,34 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class PatternMaterial; +} + +class rendergraph::PatternMaterial : public rendergraph::Material { + public: + PatternMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; + + Texture* texture(int /*binding*/) const override { + return m_pTexture.get(); + } + + void setTexture(std::unique_ptr texture) { + m_pTexture = std::move(texture); + } + + private: + std::unique_ptr m_pTexture; +}; diff --git a/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp b/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp new file mode 100644 index 000000000000..c1d53d3f8bcc --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp @@ -0,0 +1,39 @@ +#include "rgbamaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +RGBAMaterial::RGBAMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& RGBAMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "color"}); + return set; +} + +/* static */ const UniformSet& RGBAMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* RGBAMaterial::type() const { + static MaterialType type; + return &type; +} + +int RGBAMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr RGBAMaterial::createShader() const { + return std::make_unique( + "rgba.vert", "rgba.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/rgbamaterial.h b/src/rendergraph/common/rendergraph/material/rgbamaterial.h new file mode 100644 index 000000000000..b5b72a949c90 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.h @@ -0,0 +1,21 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class RGBAMaterial; +} + +class rendergraph::RGBAMaterial : public rendergraph::Material { + public: + RGBAMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp b/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp new file mode 100644 index 000000000000..3a2f014d07e4 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp @@ -0,0 +1,39 @@ +#include "rgbmaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +RGBMaterial::RGBMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& RGBMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "color"}); + return set; +} + +/* static */ const UniformSet& RGBMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* RGBMaterial::type() const { + static MaterialType type; + return &type; +} + +int RGBMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr RGBMaterial::createShader() const { + return std::make_unique( + "rgb.vert", "rgb.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/rgbmaterial.h b/src/rendergraph/common/rendergraph/material/rgbmaterial.h new file mode 100644 index 000000000000..6d4d74b25145 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.h @@ -0,0 +1,21 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class RGBMaterial; +} + +class rendergraph::RGBMaterial : public rendergraph::Material { + public: + RGBMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/common/rendergraph/material/texturematerial.cpp b/src/rendergraph/common/rendergraph/material/texturematerial.cpp new file mode 100644 index 000000000000..090e6070d988 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/texturematerial.cpp @@ -0,0 +1,39 @@ +#include "texturematerial.h" + +#include +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +using namespace rendergraph; + +TextureMaterial::TextureMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& TextureMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "texcoord"}); + return set; +} + +/* static */ const UniformSet& TextureMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* TextureMaterial::type() const { + static MaterialType type; + return &type; +} + +int TextureMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr TextureMaterial::createShader() const { + return std::make_unique( + "texture.vert", "texture.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/texturematerial.h b/src/rendergraph/common/rendergraph/material/texturematerial.h new file mode 100644 index 000000000000..4e0ac39b9891 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/texturematerial.h @@ -0,0 +1,34 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class TextureMaterial; +} + +class rendergraph::TextureMaterial : public rendergraph::Material { + public: + TextureMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; + + Texture* texture(int /*binding*/) const override { + return m_pTexture.get(); + } + + void setTexture(std::unique_ptr texture) { + m_pTexture = std::move(texture); + } + + private: + std::unique_ptr m_pTexture; +}; diff --git a/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp b/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp new file mode 100644 index 000000000000..8e4bf12ed9c9 --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp @@ -0,0 +1,39 @@ +#include "unicolormaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniColorMaterial::UniColorMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& UniColorMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position"}); + return set; +} + +/* static */ const UniformSet& UniColorMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix", "ubuf.color"}); + return set; +} + +MaterialType* UniColorMaterial::type() const { + static MaterialType type; + return &type; +} + +int UniColorMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr UniColorMaterial::createShader() const { + return std::make_unique( + "unicolor.vert", "unicolor.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/common/rendergraph/material/unicolormaterial.h b/src/rendergraph/common/rendergraph/material/unicolormaterial.h new file mode 100644 index 000000000000..9516bb8c731b --- /dev/null +++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.h @@ -0,0 +1,21 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class UniColorMaterial; +} + +class rendergraph::UniColorMaterial : public rendergraph::Material { + public: + UniColorMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/examples/CMakeLists.txt b/src/rendergraph/examples/CMakeLists.txt new file mode 100644 index 000000000000..61ba086297f6 --- /dev/null +++ b/src/rendergraph/examples/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.16) +project(rendergraph LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools) + +qt_standard_project_setup() + +add_subdirectory(../ rendergraph) +add_subdirectory(gl_example) +add_subdirectory(sg_example) diff --git a/src/rendergraph/examples/README b/src/rendergraph/examples/README new file mode 100644 index 000000000000..6168efc09c47 --- /dev/null +++ b/src/rendergraph/examples/README @@ -0,0 +1,6 @@ +Build examples with + +$ cd ../../../ +$ mkdir build-rendergraph_examples +$ cd build-rendergraph_examples +$ cmake -DCMAKE_PREFIX_PATH=~/Qt/6.7.2/macos/ ../src/rendergraph/examples diff --git a/src/rendergraph/examples/common/examplenodes.cpp b/src/rendergraph/examples/common/examplenodes.cpp new file mode 100644 index 000000000000..420bbae40cac --- /dev/null +++ b/src/rendergraph/examples/common/examplenodes.cpp @@ -0,0 +1,60 @@ +#include "examplenodes.h" + +#include +#include +#include + +#include "rendergraph/geometry.h" +#include "rendergraph/material/endoftrackmaterial.h" +#include "rendergraph/material/texturematerial.h" + +using namespace rendergraph; + +ExampleNode1::ExampleNode1() { + setMaterial(std::make_unique()); + setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4)); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, horizontalGradientArray, 4); + + QColor color("red"); + material().setUniform(0, + QVector4D{color.redF(), + color.greenF(), + color.blueF(), + color.alphaF()}); +} + +ExampleNode2::ExampleNode2() { + setMaterial(std::make_unique()); + setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4)); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, horizontalGradientArray, 4); + + QColor color("blue"); + material().setUniform(0, + QVector4D{color.redF(), + color.greenF(), + color.blueF(), + color.alphaF()}); +} + +ExampleNode3::ExampleNode3() { + auto* m = new TextureMaterial; + + setMaterial(std::make_unique()); + setGeometry(std::make_unique(TextureMaterial::attributes(), 4)); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, texcoordArray, 4); + + QMatrix4x4 matrix; + + matrix.scale(0.3); + material().setUniform(0, matrix); +} + +void ExampleNode3::setTexture(std::unique_ptr texture) { + dynamic_cast(material()).setTexture(std::move(texture)); +} diff --git a/src/rendergraph/examples/common/examplenodes.h b/src/rendergraph/examples/common/examplenodes.h new file mode 100644 index 000000000000..1ff3d01fb443 --- /dev/null +++ b/src/rendergraph/examples/common/examplenodes.h @@ -0,0 +1,36 @@ +#include "rendergraph/geometrynode.h" +#include "rendergraph/texture.h" + +namespace rendergraph { +class ExampleNode1; +class ExampleNode2; +class ExampleNode3; +} // namespace rendergraph + +class rendergraph::ExampleNode1 : public rendergraph::GeometryNode { + public: + static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; + static constexpr float verticalGradientArray[] = {1.f, 1.f, -1.f, -1.f}; + static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; + + ExampleNode1(); +}; + +class rendergraph::ExampleNode2 : public rendergraph::GeometryNode { + public: + static constexpr float positionArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f}; + static constexpr float verticalGradientArray[] = {1.f, 1.f, 0.f, 0.f}; + static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; + + ExampleNode2(); +}; + +class rendergraph::ExampleNode3 : public rendergraph::GeometryNode { + public: + static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; + static constexpr float texcoordArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f}; + + ExampleNode3(); + + void setTexture(std::unique_ptr texture); +}; diff --git a/src/rendergraph/examples/gl_example/CMakeLists.txt b/src/rendergraph/examples/gl_example/CMakeLists.txt new file mode 100644 index 000000000000..6a055ea5560e --- /dev/null +++ b/src/rendergraph/examples/gl_example/CMakeLists.txt @@ -0,0 +1,40 @@ +find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL) + +qt_add_executable(gl_example + window.cpp window.h + main.cpp + ../common/examplenodes.cpp +) + +set_target_properties(gl_example PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(gl_example PRIVATE + rendergraph_gl + Qt6::Core + Qt6::Gui + Qt6::OpenGL +) +target_include_directories(gl_example PRIVATE ../common) + +qt_add_resources(gl_example "gl_example" + PREFIX + /example + FILES + images/test.png +) + +install(TARGETS gl_example + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET gl_example + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR +) +install(SCRIPT ${deploy_script}) diff --git a/src/rendergraph/examples/gl_example/images/test.png b/src/rendergraph/examples/gl_example/images/test.png new file mode 100644 index 000000000000..f9580346eb8b Binary files /dev/null and b/src/rendergraph/examples/gl_example/images/test.png differ diff --git a/src/rendergraph/examples/gl_example/main.cpp b/src/rendergraph/examples/gl_example/main.cpp new file mode 100644 index 000000000000..7b62238b429b --- /dev/null +++ b/src/rendergraph/examples/gl_example/main.cpp @@ -0,0 +1,12 @@ +#include + +#include "window.h" + +int main(int argc, char* argv[]) { + QGuiApplication app(argc, argv); + + Window window; + window.show(); + + return app.exec(); +} diff --git a/src/rendergraph/examples/gl_example/window.cpp b/src/rendergraph/examples/gl_example/window.cpp new file mode 100644 index 000000000000..a74fc3afab39 --- /dev/null +++ b/src/rendergraph/examples/gl_example/window.cpp @@ -0,0 +1,48 @@ +#include "window.h" + +#include "examplenodes.h" +#include "rendergraph/context.h" +#include "rendergraph/graph.h" + +Window::Window() { +} + +Window::~Window() = default; + +void Window::closeEvent(QCloseEvent*) { + // since this is the only and last window, we need to cleanup before destruction, + // because at destruction the context can't be used anymore + m_rendergraph.reset(); +} + +void Window::initializeGL() { + auto node = std::make_unique(); + node->appendChildNode(std::make_unique()); + node->appendChildNode(std::make_unique()); + node->appendChildNode(std::make_unique()); + + { + QImage img(":/example/images/test.png"); + rendergraph::Context context; + static_cast(node->lastChild()) + ->setTexture( + std::make_unique(context, img)); + } + + m_rendergraph = std::make_unique(std::move(node)); + m_rendergraph->initialize(); +} + +void Window::resizeGL(int, int) { +} + +void Window::paintGL() { + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_BLEND); + // qt scene graph uses premultiplied alpha color in the shader, + // so we need to do the same + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + m_rendergraph->render(); +} diff --git a/src/rendergraph/examples/gl_example/window.h b/src/rendergraph/examples/gl_example/window.h new file mode 100644 index 000000000000..6d7d5f697498 --- /dev/null +++ b/src/rendergraph/examples/gl_example/window.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class Graph; +} + +class Window : public QOpenGLWindow { + public: + Window(); + ~Window(); + + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + void closeEvent(QCloseEvent* ev) override; + + private: + std::unique_ptr m_rendergraph; +}; diff --git a/src/rendergraph/examples/sg_example/CMakeLists.txt b/src/rendergraph/examples/sg_example/CMakeLists.txt new file mode 100644 index 000000000000..378e062fcf22 --- /dev/null +++ b/src/rendergraph/examples/sg_example/CMakeLists.txt @@ -0,0 +1,36 @@ +qt_add_executable(sg_example WIN32 MACOSX_BUNDLE + customitem.cpp + customitem.h + main.cpp + ../common/examplenodes.cpp +) + +target_link_libraries(sg_example PRIVATE + rendergraph_sg +) +target_include_directories(sg_example PRIVATE ../common) + +qt_add_qml_module(sg_example + URI RenderGraph + QML_FILES + qml/main.qml + RESOURCES + images/test.png + RESOURCE_PREFIX /example + NO_RESOURCE_TARGET_PATH +) + +install(TARGETS sg_example + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET sg_example + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/src/rendergraph/examples/sg_example/customitem.cpp b/src/rendergraph/examples/sg_example/customitem.cpp new file mode 100644 index 000000000000..bf87f780f179 --- /dev/null +++ b/src/rendergraph/examples/sg_example/customitem.cpp @@ -0,0 +1,59 @@ +#include "customitem.h" + +#include +#include +#include +#include +#include + +#include "examplenodes.h" +#include "rendergraph/context.h" +#include "rendergraph/scenegraph.h" + +CustomItem::CustomItem(QQuickItem* parent) + : QQuickItem(parent) { + setFlag(ItemHasContents, true); +} + +CustomItem::~CustomItem() = default; + +void CustomItem::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) { + m_geometryChanged = true; + update(); + QQuickItem::geometryChange(newGeometry, oldGeometry); +} + +QSGNode* CustomItem::updatePaintNode(QSGNode* node, UpdatePaintNodeData*) { + QSGRectangleNode* bgNode; + if (!node) { + bgNode = window()->createRectangleNode(); + bgNode->setColor(QColor(0, 0, 0, 255)); + bgNode->setRect(boundingRect()); + + m_node = std::make_unique(); + m_node->appendChildNode(std::make_unique()); + m_node->appendChildNode(std::make_unique()); + m_node->appendChildNode(std::make_unique()); + + { + QImage img(":/example/images/test.png"); + auto context = rendergraph::createSgContext(window()); + static_cast(m_node->lastChild()) + ->setTexture(std::make_unique( + *context, img)); + } + + bgNode->appendChildNode(rendergraph::sgNode(m_node.get())); + + node = bgNode; + } else { + bgNode = static_cast(node); + } + + if (m_geometryChanged) { + bgNode->setRect(boundingRect()); + m_geometryChanged = false; + } + + return node; +} diff --git a/src/rendergraph/examples/sg_example/customitem.h b/src/rendergraph/examples/sg_example/customitem.h new file mode 100644 index 000000000000..304fb5c8f5ec --- /dev/null +++ b/src/rendergraph/examples/sg_example/customitem.h @@ -0,0 +1,26 @@ +#ifndef CUSTOMITEM_H +#define CUSTOMITEM_H + +#include + +namespace rendergraph { +class Node; +} + +class CustomItem : public QQuickItem { + Q_OBJECT + QML_ELEMENT + + public: + explicit CustomItem(QQuickItem* parent = nullptr); + ~CustomItem(); + + protected: + QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override; + void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; + + bool m_geometryChanged{}; + std::unique_ptr m_node; +}; + +#endif // CUSTOMITEM_H diff --git a/src/rendergraph/examples/sg_example/images/test.png b/src/rendergraph/examples/sg_example/images/test.png new file mode 100644 index 000000000000..f9580346eb8b Binary files /dev/null and b/src/rendergraph/examples/sg_example/images/test.png differ diff --git a/src/rendergraph/examples/sg_example/main.cpp b/src/rendergraph/examples/sg_example/main.cpp new file mode 100644 index 000000000000..ab2e5b34072d --- /dev/null +++ b/src/rendergraph/examples/sg_example/main.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +#include "customitem.h" + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///example/qml/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/src/rendergraph/examples/sg_example/qml/main.qml b/src/rendergraph/examples/sg_example/qml/main.qml new file mode 100644 index 000000000000..2410ad1a5604 --- /dev/null +++ b/src/rendergraph/examples/sg_example/qml/main.qml @@ -0,0 +1,13 @@ +import QtQuick +import RenderGraph + +Item { + id: root + + width: 640 + height: 480 + + CustomItem { + anchors.fill: parent + } +} diff --git a/src/rendergraph/opengl/CMakeLists.txt b/src/rendergraph/opengl/CMakeLists.txt new file mode 100644 index 000000000000..5b8c9f819698 --- /dev/null +++ b/src/rendergraph/opengl/CMakeLists.txt @@ -0,0 +1,54 @@ +add_library(rendergraph_gl + rendergraph/attribute.h + rendergraph/attributeset.cpp + rendergraph/attributeset.h + rendergraph/context.cpp + rendergraph/context.h + rendergraph/geometry.cpp + rendergraph/geometry.h + rendergraph/geometrynode.cpp + rendergraph/geometrynode.h + rendergraph/material.cpp + rendergraph/material.h + rendergraph/materialshader.cpp + rendergraph/materialshader.h + rendergraph/materialtype.cpp + rendergraph/materialtype.h + rendergraph/node.cpp + rendergraph/node.h + rendergraph/opacitynode.cpp + rendergraph/opacitynode.h + rendergraph/texture.cpp + rendergraph/texture.h + rendergraph/types.cpp + rendergraph/types.h + rendergraph/uniform.h + rendergraph/uniformscache.cpp + rendergraph/uniformscache.h + rendergraph/uniformset.cpp + rendergraph/uniformset.h + ../common/rendergraph/material/endoftrackmaterial.cpp + ../common/rendergraph/material/endoftrackmaterial.h + ../common/rendergraph/material/patternmaterial.cpp + ../common/rendergraph/material/patternmaterial.h + ../common/rendergraph/material/texturematerial.cpp + ../common/rendergraph/material/texturematerial.h + ../common/rendergraph/material/rgbamaterial.cpp + ../common/rendergraph/material/rgbamaterial.h + ../common/rendergraph/material/rgbmaterial.cpp + ../common/rendergraph/material/rgbmaterial.h + ../common/rendergraph/material/unicolormaterial.cpp + ../common/rendergraph/material/unicolormaterial.h + rendergraph/graph.cpp + rendergraph/graph.h + rendergraph/openglnode.cpp + rendergraph/openglnode.h +) + +target_link_libraries(rendergraph_gl PUBLIC + Qt6::Core + Qt6::Gui + Qt6::OpenGL +) + +target_include_directories(rendergraph_gl PUBLIC . ../common) diff --git a/src/rendergraph/opengl/rendergraph/attribute.h b/src/rendergraph/opengl/rendergraph/attribute.h new file mode 100644 index 000000000000..5fae9e3df544 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/attribute.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "rendergraph/types.h" + +namespace rendergraph { +struct Attribute; +} + +struct rendergraph::Attribute { + const int m_tupleSize; + const PrimitiveType m_primitiveType; + const QString m_name; + + Attribute(int tupleSize, PrimitiveType primitiveType) + : m_tupleSize{tupleSize}, + m_primitiveType{primitiveType} { + } + + Attribute(int tupleSize, PrimitiveType primitiveType, QString name) + : m_tupleSize{tupleSize}, + m_primitiveType{primitiveType}, + m_name{std::move(name)} { + } + + template + static Attribute create() { + return Attribute(tupleSizeOf(), primitiveTypeOf()); + } +}; diff --git a/src/rendergraph/opengl/rendergraph/attributeset.cpp b/src/rendergraph/opengl/rendergraph/attributeset.cpp new file mode 100644 index 000000000000..6ca9d378983d --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/attributeset.cpp @@ -0,0 +1,23 @@ +#include "rendergraph/attributeset.h" + +using namespace rendergraph; + +AttributeSet::AttributeSet() = default; + +AttributeSet::AttributeSet(std::initializer_list list, const std::vector& names) + : AttributeSet() { + int i = 0; + for (auto item : list) { + add(Attribute{item.m_tupleSize, item.m_primitiveType, names[i++]}); + } +} + +AttributeSet::~AttributeSet() = default; + +void AttributeSet::add(const Attribute& attribute) { + m_attributes.push_back(attribute); +} + +const std::vector& AttributeSet::attributes() const { + return m_attributes; +} diff --git a/src/rendergraph/opengl/rendergraph/attributeset.h b/src/rendergraph/opengl/rendergraph/attributeset.h new file mode 100644 index 000000000000..8d21921ee5e5 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/attributeset.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include "rendergraph/attribute.h" + +namespace rendergraph { +class AttributeSet; +} + +class rendergraph::AttributeSet { + public: + AttributeSet(); + AttributeSet(std::initializer_list list, const std::vector& names); + + ~AttributeSet(); + + const std::vector& attributes() const; + + private: + void add(const Attribute& attribute); + + std::vector m_attributes; +}; + +namespace rendergraph { +template +AttributeSet makeAttributeSet(const std::vector& names) { + return AttributeSet({(Attribute::create())...}, names); +} +} // namespace rendergraph diff --git a/src/rendergraph/opengl/rendergraph/context.cpp b/src/rendergraph/opengl/rendergraph/context.cpp new file mode 100644 index 000000000000..c795cd2b00b0 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/context.cpp @@ -0,0 +1,6 @@ +#include "rendergraph/context.h" + +using namespace rendergraph; + +Context::Context() = default; +Context::~Context() = default; diff --git a/src/rendergraph/opengl/rendergraph/context.h b/src/rendergraph/opengl/rendergraph/context.h new file mode 100644 index 000000000000..99e7f8e0ec73 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/context.h @@ -0,0 +1,11 @@ +#pragma once + +namespace rendergraph { +class Context; +} + +class rendergraph::Context { + public: + Context(); + ~Context(); +}; diff --git a/src/rendergraph/opengl/rendergraph/examples/CMakeLists.txt b/src/rendergraph/opengl/rendergraph/examples/CMakeLists.txt new file mode 100644 index 000000000000..bd918d2f9ad3 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16) +project(rendergraph LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools) + +qt_standard_project_setup() + +add_subdirectory(../ rendergraph) +add_subdirectory(gl_example) +add_subdirectory(sg_example) +add_subdirectory(common) diff --git a/src/rendergraph/opengl/rendergraph/examples/README b/src/rendergraph/opengl/rendergraph/examples/README new file mode 100644 index 000000000000..6168efc09c47 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/README @@ -0,0 +1,6 @@ +Build examples with + +$ cd ../../../ +$ mkdir build-rendergraph_examples +$ cd build-rendergraph_examples +$ cmake -DCMAKE_PREFIX_PATH=~/Qt/6.7.2/macos/ ../src/rendergraph/examples diff --git a/src/rendergraph/opengl/rendergraph/examples/common/CMakeLists.txt b/src/rendergraph/opengl/rendergraph/examples/common/CMakeLists.txt new file mode 100644 index 000000000000..e7ee6c0160e1 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/common/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(rendergraph_examples + examplenodes.cpp + examplenodes.h +) + +target_link_libraries(rendergraph_examples PUBLIC + Qt6::Core + Qt6::Gui + Qt6::OpenGL +) + +target_include_directories(rendergraph_examples PUBLIC ../../../ .) diff --git a/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.cpp b/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.cpp new file mode 100644 index 000000000000..420bbae40cac --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.cpp @@ -0,0 +1,60 @@ +#include "examplenodes.h" + +#include +#include +#include + +#include "rendergraph/geometry.h" +#include "rendergraph/material/endoftrackmaterial.h" +#include "rendergraph/material/texturematerial.h" + +using namespace rendergraph; + +ExampleNode1::ExampleNode1() { + setMaterial(std::make_unique()); + setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4)); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, horizontalGradientArray, 4); + + QColor color("red"); + material().setUniform(0, + QVector4D{color.redF(), + color.greenF(), + color.blueF(), + color.alphaF()}); +} + +ExampleNode2::ExampleNode2() { + setMaterial(std::make_unique()); + setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4)); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, horizontalGradientArray, 4); + + QColor color("blue"); + material().setUniform(0, + QVector4D{color.redF(), + color.greenF(), + color.blueF(), + color.alphaF()}); +} + +ExampleNode3::ExampleNode3() { + auto* m = new TextureMaterial; + + setMaterial(std::make_unique()); + setGeometry(std::make_unique(TextureMaterial::attributes(), 4)); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, texcoordArray, 4); + + QMatrix4x4 matrix; + + matrix.scale(0.3); + material().setUniform(0, matrix); +} + +void ExampleNode3::setTexture(std::unique_ptr texture) { + dynamic_cast(material()).setTexture(std::move(texture)); +} diff --git a/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.h b/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.h new file mode 100644 index 000000000000..1ff3d01fb443 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.h @@ -0,0 +1,36 @@ +#include "rendergraph/geometrynode.h" +#include "rendergraph/texture.h" + +namespace rendergraph { +class ExampleNode1; +class ExampleNode2; +class ExampleNode3; +} // namespace rendergraph + +class rendergraph::ExampleNode1 : public rendergraph::GeometryNode { + public: + static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; + static constexpr float verticalGradientArray[] = {1.f, 1.f, -1.f, -1.f}; + static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; + + ExampleNode1(); +}; + +class rendergraph::ExampleNode2 : public rendergraph::GeometryNode { + public: + static constexpr float positionArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f}; + static constexpr float verticalGradientArray[] = {1.f, 1.f, 0.f, 0.f}; + static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; + + ExampleNode2(); +}; + +class rendergraph::ExampleNode3 : public rendergraph::GeometryNode { + public: + static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; + static constexpr float texcoordArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f}; + + ExampleNode3(); + + void setTexture(std::unique_ptr texture); +}; diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/CMakeLists.txt b/src/rendergraph/opengl/rendergraph/examples/gl_example/CMakeLists.txt new file mode 100644 index 000000000000..30f6a547b88c --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/gl_example/CMakeLists.txt @@ -0,0 +1,39 @@ +find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL) + +qt_add_executable(gl_example + window.cpp window.h + main.cpp +) + +set_target_properties(gl_example PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(gl_example PRIVATE + rendergraph_gl + rendergraph_examples + Qt6::Core + Qt6::Gui + Qt6::OpenGL +) + +qt_add_resources(gl_example "gl_example" + PREFIX + /example + FILES + images/test.png +) + +install(TARGETS gl_example + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET gl_example + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR +) +install(SCRIPT ${deploy_script}) diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/images/test.png b/src/rendergraph/opengl/rendergraph/examples/gl_example/images/test.png new file mode 100644 index 000000000000..f9580346eb8b Binary files /dev/null and b/src/rendergraph/opengl/rendergraph/examples/gl_example/images/test.png differ diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/main.cpp b/src/rendergraph/opengl/rendergraph/examples/gl_example/main.cpp new file mode 100644 index 000000000000..7b62238b429b --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/gl_example/main.cpp @@ -0,0 +1,12 @@ +#include + +#include "window.h" + +int main(int argc, char* argv[]) { + QGuiApplication app(argc, argv); + + Window window; + window.show(); + + return app.exec(); +} diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/window.cpp b/src/rendergraph/opengl/rendergraph/examples/gl_example/window.cpp new file mode 100644 index 000000000000..81bc0db439b1 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/gl_example/window.cpp @@ -0,0 +1,48 @@ +#include "window.h" + +#include "examplenodes.h" +#include "rendergraph/context.h" +#include "rendergraph/opengl/graph.h" + +Window::Window() { +} + +Window::~Window() = default; + +void Window::closeEvent(QCloseEvent*) { + // since this is the only and last window, we need to cleanup before destruction, + // because at destruction the context can't be used anymore + m_rendergraph.reset(); +} + +void Window::initializeGL() { + auto node = std::make_unique(); + node->appendChildNode(std::make_unique()); + node->appendChildNode(std::make_unique()); + node->appendChildNode(std::make_unique()); + + { + QImage img(":/example/images/test.png"); + rendergraph::Context context; + static_cast(node->lastChild()) + ->setTexture( + std::make_unique(context, img)); + } + + m_rendergraph = std::make_unique(std::move(node)); + m_rendergraph->initialize(); +} + +void Window::resizeGL(int, int) { +} + +void Window::paintGL() { + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_BLEND); + // qt scene graph uses premultiplied alpha color in the shader, + // so we need to do the same + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + m_rendergraph->render(); +} diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/window.h b/src/rendergraph/opengl/rendergraph/examples/gl_example/window.h new file mode 100644 index 000000000000..6d7d5f697498 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/gl_example/window.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class Graph; +} + +class Window : public QOpenGLWindow { + public: + Window(); + ~Window(); + + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + void closeEvent(QCloseEvent* ev) override; + + private: + std::unique_ptr m_rendergraph; +}; diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/CMakeLists.txt b/src/rendergraph/opengl/rendergraph/examples/sg_example/CMakeLists.txt new file mode 100644 index 000000000000..096e131e47e9 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/CMakeLists.txt @@ -0,0 +1,35 @@ +qt_add_executable(sg_example WIN32 MACOSX_BUNDLE + customitem.cpp + customitem.h + main.cpp +) + +target_link_libraries(sg_example PRIVATE + rendergraph_sg + rendergraph_examples +) + +qt_add_qml_module(sg_example + URI RenderGraph + QML_FILES + qml/main.qml + RESOURCES + images/test.png + RESOURCE_PREFIX /example + NO_RESOURCE_TARGET_PATH +) + +install(TARGETS sg_example + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET sg_example + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.cpp b/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.cpp new file mode 100644 index 000000000000..fcf1f0feb079 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.cpp @@ -0,0 +1,59 @@ +#include "customitem.h" + +#include +#include +#include +#include +#include + +#include "examplenodes.h" +#include "rendergraph/context.h" +#include "rendergraph/scenegraph/scenegraph.h" + +CustomItem::CustomItem(QQuickItem* parent) + : QQuickItem(parent) { + setFlag(ItemHasContents, true); +} + +CustomItem::~CustomItem() = default; + +void CustomItem::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) { + m_geometryChanged = true; + update(); + QQuickItem::geometryChange(newGeometry, oldGeometry); +} + +QSGNode* CustomItem::updatePaintNode(QSGNode* node, UpdatePaintNodeData*) { + QSGRectangleNode* bgNode; + if (!node) { + bgNode = window()->createRectangleNode(); + bgNode->setColor(QColor(0, 0, 0, 255)); + bgNode->setRect(boundingRect()); + + m_node = std::make_unique(); + m_node->appendChildNode(std::make_unique()); + m_node->appendChildNode(std::make_unique()); + m_node->appendChildNode(std::make_unique()); + + { + QImage img(":/example/images/test.png"); + auto context = rendergraph::createSgContext(window()); + static_cast(m_node->lastChild()) + ->setTexture(std::make_unique( + *context, img)); + } + + bgNode->appendChildNode(rendergraph::sgNode(m_node.get())); + + node = bgNode; + } else { + bgNode = static_cast(node); + } + + if (m_geometryChanged) { + bgNode->setRect(boundingRect()); + m_geometryChanged = false; + } + + return node; +} diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.h b/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.h new file mode 100644 index 000000000000..304fb5c8f5ec --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.h @@ -0,0 +1,26 @@ +#ifndef CUSTOMITEM_H +#define CUSTOMITEM_H + +#include + +namespace rendergraph { +class Node; +} + +class CustomItem : public QQuickItem { + Q_OBJECT + QML_ELEMENT + + public: + explicit CustomItem(QQuickItem* parent = nullptr); + ~CustomItem(); + + protected: + QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override; + void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; + + bool m_geometryChanged{}; + std::unique_ptr m_node; +}; + +#endif // CUSTOMITEM_H diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/images/test.png b/src/rendergraph/opengl/rendergraph/examples/sg_example/images/test.png new file mode 100644 index 000000000000..f9580346eb8b Binary files /dev/null and b/src/rendergraph/opengl/rendergraph/examples/sg_example/images/test.png differ diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/main.cpp b/src/rendergraph/opengl/rendergraph/examples/sg_example/main.cpp new file mode 100644 index 000000000000..ab2e5b34072d --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/main.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +#include "customitem.h" + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///example/qml/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/qml/main.qml b/src/rendergraph/opengl/rendergraph/examples/sg_example/qml/main.qml new file mode 100644 index 000000000000..2410ad1a5604 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/qml/main.qml @@ -0,0 +1,13 @@ +import QtQuick +import RenderGraph + +Item { + id: root + + width: 640 + height: 480 + + CustomItem { + anchors.fill: parent + } +} diff --git a/src/rendergraph/opengl/rendergraph/geometry.cpp b/src/rendergraph/opengl/rendergraph/geometry.cpp new file mode 100644 index 000000000000..c098f476e11d --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/geometry.cpp @@ -0,0 +1,94 @@ +#include "rendergraph/geometry.h" + +#include "rendergraph/attributeset.h" + +using namespace rendergraph; + +Geometry::Geometry(const AttributeSet& attributeSet, int vertexCount) + : m_drawingMode(Geometry::DrawingMode::TriangleStrip) // to mimic sg default + , + m_vertexCount(vertexCount) { + int offset = 0; + for (const auto& attribute : attributeSet.attributes()) { + m_offsets.push_back(offset); + offset += attribute.m_tupleSize; + m_tupleSizes.push_back(attribute.m_tupleSize); + } + m_stride = offset * sizeof(float); + m_data.resize(offset * vertexCount); +} + +Geometry::~Geometry() = default; + +void Geometry::setAttributeValues(int attributePosition, const float* from, int numTuples) { + const int offset = m_offsets[attributePosition]; + const int tupleSize = m_tupleSizes[attributePosition]; + const int skip = m_stride / sizeof(float) - tupleSize; + + float* to = m_data.data(); + to += offset; + while (numTuples--) { + int k = tupleSize; + while (k--) { + *to++ = *from++; + } + to += skip; + } +} + +float* Geometry::vertexData() { + return m_data.data(); +} + +template +T* Geometry::vertexDataAs() { + return reinterpret_cast(vertexData()); +} + +template Geometry::Point2D* Geometry::vertexDataAs(); + +template Geometry::TexturedPoint2D* Geometry::vertexDataAs(); + +template Geometry::RGBColoredPoint2D* Geometry::vertexDataAs(); + +template Geometry::RGBAColoredPoint2D* Geometry::vertexDataAs(); + +void Geometry::allocate(int vertexCount) { + m_vertexCount = vertexCount; + m_data.resize((m_stride / sizeof(float)) * m_vertexCount); +} + +void Geometry::setDrawingMode(Geometry::DrawingMode mode) { + m_drawingMode = mode; +} + +Geometry::DrawingMode Geometry::drawingMode() const { + return m_drawingMode; +} + +int Geometry::attributeCount() const { + return m_tupleSizes.size(); +} + +int Geometry::vertexCount() const { + return m_vertexCount; +} + +int Geometry::offset(int attributeIndex) const { + return m_offsets[attributeIndex]; +} + +int Geometry::tupleSize(int attributeIndex) const { + return m_tupleSizes[attributeIndex]; +} + +int Geometry::stride() const { + return m_stride; +} + +Geometry::DrawingMode m_drawingMode; +int m_vertexCount; +std::vector m_tupleSizes; +std::vector m_offsets; +int m_stride; +std::vector m_data; diff --git a/src/rendergraph/opengl/rendergraph/geometry.h b/src/rendergraph/opengl/rendergraph/geometry.h new file mode 100644 index 000000000000..a4fdf2fa90cb --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/geometry.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include "rendergraph/types.h" + +namespace rendergraph { +class Geometry; +class AttributeSet; +} // namespace rendergraph + +class rendergraph::Geometry { + public: + struct Point2D { + QVector2D position2D; + Point2D(float x, float y) + : position2D{x, y} { + } + }; + + struct TexturedPoint2D { + QVector2D position2D; + QVector2D texcoord2D; + TexturedPoint2D(float x, float y, float tx, float ty) + : position2D{x, y}, + texcoord2D{tx, ty} { + } + }; + + struct RGBColoredPoint2D { + QVector2D position2D; + QVector3D color3D; + RGBColoredPoint2D(float x, float y, float r, float g, float b) + : position2D{x, y}, + color3D{r, g, b} { + } + }; + + struct RGBAColoredPoint2D { + QVector2D position2D; + QVector4D color4D; + RGBAColoredPoint2D(float x, float y, float r, float g, float b, float a) + : position2D{x, y}, + color4D{r, g, b, a} { + } + }; + + enum class DrawingMode { + Triangles, + TriangleStrip + }; + + Geometry(const AttributeSet& attributeSet, int vertexCount); + ~Geometry(); + + void allocate(int vertexCount); + + void setAttributeValues(int attributePosition, const float* data, int numTuples); + + float* vertexData(); + + template + T* vertexDataAs(); + + DrawingMode drawingMode() const; + void setDrawingMode(DrawingMode mode); + + int attributeCount() const; + int vertexCount() const; + int offset(int attributeIndex) const; + int tupleSize(int attributeIndex) const; + int stride() const; + + private: + DrawingMode m_drawingMode; + int m_vertexCount; + std::vector m_tupleSizes; + std::vector m_offsets; + int m_stride; + std::vector m_data; +}; diff --git a/src/rendergraph/opengl/rendergraph/geometrynode.cpp b/src/rendergraph/opengl/rendergraph/geometrynode.cpp new file mode 100644 index 000000000000..67df96e4d114 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/geometrynode.cpp @@ -0,0 +1,120 @@ +#include "rendergraph/geometrynode.h" + +#include + +#include "rendergraph/shadercache.h" +#include "rendergraph/texture.h" + +using namespace rendergraph; + +GeometryNode::GeometryNode() = default; + +GeometryNode::~GeometryNode() = default; + +void GeometryNode::setGeometry(std::unique_ptr pGeometry) { + m_pGeometry = std::move(pGeometry); +} + +void GeometryNode::setMaterial(std::unique_ptr pMaterial) { + m_pMaterial = std::move(pMaterial); +} + +Geometry& GeometryNode::geometry() const { + return *m_pGeometry; +} + +Material& GeometryNode::material() const { + return *m_pMaterial; +} + +void GeometryNode::initialize() { + initializeOpenGLFunctions(); + m_pMaterial->setShader(ShaderCache::getShaderForMaterial(m_pMaterial.get())); + Node::initialize(); +} + +namespace { +GLenum toGlDrawingMode(Geometry::DrawingMode mode) { + switch (mode) { + case Geometry::DrawingMode::Triangles: + return GL_TRIANGLES; + case Geometry::DrawingMode::TriangleStrip: + return GL_TRIANGLE_STRIP; + } +} +} // namespace + +void GeometryNode::render() { + Geometry& geometry = *m_pGeometry; + Material& material = *m_pMaterial; + + if (geometry.vertexCount() == 0) { + return; + } + + glEnable(GL_BLEND); + // qt scene graph uses premultiplied alpha color in the shader, + // so we need to do the same + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + QOpenGLShaderProgram& shader = material.shader(); + shader.bind(); + + if (m_pMaterial->clearUniformsCacheDirty() || !material.isLastModifierOfShader()) { + material.modifyShader(); + const UniformsCache& cache = m_pMaterial->uniformsCache(); + for (int i = 0; i < cache.count(); i++) { + int location = material.uniformLocation(i); + switch (cache.type(i)) { + case Type::UInt: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Float: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Vector2D: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Vector3D: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Vector4D: + shader.setUniformValue(location, cache.get(i)); + break; + case Type::Matrix4x4: + shader.setUniformValue(location, cache.get(i)); + break; + } + } + } + + for (int i = 0; i < geometry.attributeCount(); i++) { + int location = material.attributeLocation(i); + shader.enableAttributeArray(location); + shader.setAttributeArray(location, + geometry.vertexData() + geometry.offset(i), + geometry.tupleSize(i), + geometry.stride()); + } + + // TODO multiple textures + auto pTexture = m_pMaterial->texture(1); + if (pTexture) { + pTexture->glTexture()->bind(); + } + + glDrawArrays(toGlDrawingMode(geometry.drawingMode()), 0, geometry.vertexCount()); + + if (pTexture) { + pTexture->glTexture()->release(); + } + + for (int i = 0; i < geometry.attributeCount(); i++) { + int location = material.attributeLocation(i); + shader.disableAttributeArray(location); + } + + shader.release(); + + Node::render(); +} diff --git a/src/rendergraph/opengl/rendergraph/geometrynode.h b/src/rendergraph/opengl/rendergraph/geometrynode.h new file mode 100644 index 000000000000..4804f123d93f --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/geometrynode.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include "rendergraph/geometry.h" +#include "rendergraph/material.h" +#include "rendergraph/node.h" + +namespace rendergraph { +class GeometryNode; +class Material; +} // namespace rendergraph + +class rendergraph::GeometryNode : public rendergraph::Node, public QOpenGLFunctions { + public: + GeometryNode(); + ~GeometryNode(); + + template + void initForRectangles(int numRectangles) { + const int verticesPerRectangle = 6; // 2 rectangles + setGeometry(std::make_unique(T_Material::attributes(), + numRectangles * verticesPerRectangle)); + setMaterial(std::make_unique()); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); + } + + void initialize() override; + void render() override; + + void setMaterial(std::unique_ptr material); + void setGeometry(std::unique_ptr geometry); + Geometry& geometry() const; + Material& material() const; + + private: + std::unique_ptr m_pMaterial; + std::unique_ptr m_pGeometry; +}; diff --git a/src/rendergraph/opengl/rendergraph/graph.cpp b/src/rendergraph/opengl/rendergraph/graph.cpp new file mode 100644 index 000000000000..cbb61e9e45ac --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/graph.cpp @@ -0,0 +1,81 @@ +#include "rendergraph/graph.h" + +#include + +#include "rendergraph/node.h" + +using namespace rendergraph; + +Graph::Graph(std::unique_ptr node) + : m_pTopNode(std::move(node)) { + if (m_pTopNode->graph() != this) { + addToGraph(m_pTopNode.get()); + } +} + +Graph::~Graph() = default; + +void Graph::initialize() { + for (auto pNode : m_pInitializeNodes) { + pNode->initialize(); + } + m_pInitializeNodes.clear(); +} + +void Graph::render() { + if (!m_pInitializeNodes.empty()) { + initialize(); + } + if (!m_pTopNode->isSubtreeBlocked()) { + render(m_pTopNode.get()); + } +} + +void Graph::resize(int w, int h) { + resize(m_pTopNode.get(), w, h); +} + +void Graph::preprocess() { + for (auto pNode : m_pPreprocessNodes) { + if (!pNode->isSubtreeBlocked()) { + pNode->preprocess(); + } + } +} + +void Graph::render(Node* pNode) { + pNode->render(); + pNode = pNode->firstChild(); + while (pNode) { + if (!pNode->isSubtreeBlocked()) { + render(pNode); + } + pNode = pNode->nextSibling(); + } +} + +void Graph::resize(Node* pNode, int w, int h) { + pNode->resize(w, h); + pNode = pNode->firstChild(); + while (pNode) { + resize(pNode, w, h); + pNode = pNode->nextSibling(); + } +} + +void Graph::addToGraph(Node* pNode) { + assert(pNode->graph() == nullptr); + + pNode->setGraph(this); + m_pInitializeNodes.push_back(pNode); + if (pNode->usePreprocess()) { + m_pPreprocessNodes.push_back(pNode); + } + pNode = pNode->firstChild(); + while (pNode) { + if (pNode->graph() != this) { + addToGraph(pNode); + } + pNode = pNode->nextSibling(); + } +} diff --git a/src/rendergraph/opengl/rendergraph/graph.h b/src/rendergraph/opengl/rendergraph/graph.h new file mode 100644 index 000000000000..e5179fdde786 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/graph.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class Graph; +class Node; +} // namespace rendergraph + +class rendergraph::Graph { + public: + Graph(std::unique_ptr node); + ~Graph(); + void initialize(); + void render(); + void resize(int w, int h); + void preprocess(); + void addToGraph(Node* pNode); + + private: + void render(Node* pNode); + void resize(Node* pNode, int, int h); + + const std::unique_ptr m_pTopNode; + std::vector m_pPreprocessNodes; + std::vector m_pInitializeNodes; +}; diff --git a/src/rendergraph/opengl/rendergraph/material.cpp b/src/rendergraph/opengl/rendergraph/material.cpp new file mode 100644 index 000000000000..6c525b1ebf5a --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material.cpp @@ -0,0 +1,36 @@ +#include "rendergraph/material.h" + +#include "rendergraph/shadercache.h" +#include "rendergraph/texture.h" + +using namespace rendergraph; + +Material::Material(const UniformSet& uniformSet) + : m_uniformsCache(uniformSet) { +} + +Material::~Material() = default; + +void Material::setShader(std::shared_ptr pShader) { + m_pShader = pShader; +} + +MaterialShader& Material::shader() const { + return *m_pShader; +} + +int Material::uniformLocation(int uniformIndex) const { + return m_pShader->uniformLocation(uniformIndex); +} + +int Material::attributeLocation(int attributeIndex) const { + return m_pShader->attributeLocation(attributeIndex); +} + +void Material::modifyShader() { + m_pShader->setLastModifiedByMaterial(this); +} + +bool Material::isLastModifierOfShader() const { + return this == m_pShader->lastModifiedByMaterial(); +} diff --git a/src/rendergraph/opengl/rendergraph/material.h b/src/rendergraph/opengl/rendergraph/material.h new file mode 100644 index 000000000000..8c54be251e20 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +#include "rendergraph/uniformscache.h" + +namespace rendergraph { +class UniformSet; +class Material; +class MaterialShader; +class MaterialType; +class Texture; +class GeometryNode; +} // namespace rendergraph + +class rendergraph::Material { + public: + Material(const UniformSet& uniformSet); + virtual ~Material(); + virtual int compare(const Material* other) const = 0; + virtual std::unique_ptr createShader() const = 0; + virtual MaterialType* type() const = 0; + + template + void setUniform(int uniformIndex, const T& value) { + m_uniformsCache.set(uniformIndex, value); + m_uniformsCacheDirty = true; + } + + void setShader(std::shared_ptr pShader); + + MaterialShader& shader() const; + + int uniformLocation(int uniformIndex) const; + int attributeLocation(int attributeIndex) const; + + private: + friend MaterialShader; + friend GeometryNode; + + void modifyShader(); + bool isLastModifierOfShader() const; + + const UniformsCache& uniformsCache() const { + return m_uniformsCache; + } + + bool clearUniformsCacheDirty() { + if (m_uniformsCacheDirty) { + m_uniformsCacheDirty = false; + return true; + } + return false; + } + + virtual Texture* texture(int /*binding*/) const { + return nullptr; + } + + std::shared_ptr m_pShader; + UniformsCache m_uniformsCache; + bool m_uniformsCacheDirty{}; +}; diff --git a/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.cpp b/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.cpp new file mode 100644 index 000000000000..fd9e5f73ec72 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.cpp @@ -0,0 +1,39 @@ +#include "endoftrackmaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +EndOfTrackMaterial::EndOfTrackMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& EndOfTrackMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "gradient"}); + return set; +} + +/* static */ const UniformSet& EndOfTrackMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.color"}); + return set; +} + +MaterialType* EndOfTrackMaterial::type() const { + static MaterialType type; + return &type; +} + +int EndOfTrackMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr EndOfTrackMaterial::createShader() const { + return std::make_unique( + "endoftrack.vert", "endoftrack.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.h b/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.h new file mode 100644 index 000000000000..e66a825c1641 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.h @@ -0,0 +1,21 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class EndOfTrackMaterial; +} + +class rendergraph::EndOfTrackMaterial : public rendergraph::Material { + public: + EndOfTrackMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/opengl/rendergraph/material/patternmaterial.cpp b/src/rendergraph/opengl/rendergraph/material/patternmaterial.cpp new file mode 100644 index 000000000000..e16ca4213d77 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/patternmaterial.cpp @@ -0,0 +1,39 @@ +#include "patternmaterial.h" + +#include +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +using namespace rendergraph; + +PatternMaterial::PatternMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& PatternMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "texcoord"}); + return set; +} + +/* static */ const UniformSet& PatternMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* PatternMaterial::type() const { + static MaterialType type; + return &type; +} + +int PatternMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr PatternMaterial::createShader() const { + return std::make_unique( + "pattern.vert", "pattern.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/opengl/rendergraph/material/patternmaterial.h b/src/rendergraph/opengl/rendergraph/material/patternmaterial.h new file mode 100644 index 000000000000..0df75c073714 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/patternmaterial.h @@ -0,0 +1,34 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class PatternMaterial; +} + +class rendergraph::PatternMaterial : public rendergraph::Material { + public: + PatternMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; + + Texture* texture(int /*binding*/) const override { + return m_pTexture.get(); + } + + void setTexture(std::unique_ptr texture) { + m_pTexture = std::move(texture); + } + + private: + std::unique_ptr m_pTexture; +}; diff --git a/src/rendergraph/opengl/rendergraph/material/rgbamaterial.cpp b/src/rendergraph/opengl/rendergraph/material/rgbamaterial.cpp new file mode 100644 index 000000000000..c1d53d3f8bcc --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/rgbamaterial.cpp @@ -0,0 +1,39 @@ +#include "rgbamaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +RGBAMaterial::RGBAMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& RGBAMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "color"}); + return set; +} + +/* static */ const UniformSet& RGBAMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* RGBAMaterial::type() const { + static MaterialType type; + return &type; +} + +int RGBAMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr RGBAMaterial::createShader() const { + return std::make_unique( + "rgba.vert", "rgba.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/opengl/rendergraph/material/rgbamaterial.h b/src/rendergraph/opengl/rendergraph/material/rgbamaterial.h new file mode 100644 index 000000000000..b5b72a949c90 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/rgbamaterial.h @@ -0,0 +1,21 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class RGBAMaterial; +} + +class rendergraph::RGBAMaterial : public rendergraph::Material { + public: + RGBAMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/opengl/rendergraph/material/rgbmaterial.cpp b/src/rendergraph/opengl/rendergraph/material/rgbmaterial.cpp new file mode 100644 index 000000000000..3a2f014d07e4 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/rgbmaterial.cpp @@ -0,0 +1,39 @@ +#include "rgbmaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +RGBMaterial::RGBMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& RGBMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "color"}); + return set; +} + +/* static */ const UniformSet& RGBMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* RGBMaterial::type() const { + static MaterialType type; + return &type; +} + +int RGBMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr RGBMaterial::createShader() const { + return std::make_unique( + "rgb.vert", "rgb.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/opengl/rendergraph/material/rgbmaterial.h b/src/rendergraph/opengl/rendergraph/material/rgbmaterial.h new file mode 100644 index 000000000000..6d4d74b25145 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/rgbmaterial.h @@ -0,0 +1,21 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class RGBMaterial; +} + +class rendergraph::RGBMaterial : public rendergraph::Material { + public: + RGBMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/opengl/rendergraph/material/texturematerial.cpp b/src/rendergraph/opengl/rendergraph/material/texturematerial.cpp new file mode 100644 index 000000000000..090e6070d988 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/texturematerial.cpp @@ -0,0 +1,39 @@ +#include "texturematerial.h" + +#include +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" + +using namespace rendergraph; + +TextureMaterial::TextureMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& TextureMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position", "texcoord"}); + return set; +} + +/* static */ const UniformSet& TextureMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix"}); + return set; +} + +MaterialType* TextureMaterial::type() const { + static MaterialType type; + return &type; +} + +int TextureMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr TextureMaterial::createShader() const { + return std::make_unique( + "texture.vert", "texture.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/opengl/rendergraph/material/texturematerial.h b/src/rendergraph/opengl/rendergraph/material/texturematerial.h new file mode 100644 index 000000000000..4e0ac39b9891 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/texturematerial.h @@ -0,0 +1,34 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" +#include "rendergraph/texture.h" +#include "rendergraph/uniformset.h" + +namespace rendergraph { +class TextureMaterial; +} + +class rendergraph::TextureMaterial : public rendergraph::Material { + public: + TextureMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; + + Texture* texture(int /*binding*/) const override { + return m_pTexture.get(); + } + + void setTexture(std::unique_ptr texture) { + m_pTexture = std::move(texture); + } + + private: + std::unique_ptr m_pTexture; +}; diff --git a/src/rendergraph/opengl/rendergraph/material/unicolormaterial.cpp b/src/rendergraph/opengl/rendergraph/material/unicolormaterial.cpp new file mode 100644 index 000000000000..8e4bf12ed9c9 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/unicolormaterial.cpp @@ -0,0 +1,39 @@ +#include "unicolormaterial.h" + +#include + +#include "rendergraph/materialshader.h" +#include "rendergraph/materialtype.h" +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniColorMaterial::UniColorMaterial() + : Material(uniforms()) { +} + +/* static */ const AttributeSet& UniColorMaterial::attributes() { + static AttributeSet set = makeAttributeSet({"position"}); + return set; +} + +/* static */ const UniformSet& UniColorMaterial::uniforms() { + static UniformSet set = makeUniformSet({"ubuf.matrix", "ubuf.color"}); + return set; +} + +MaterialType* UniColorMaterial::type() const { + static MaterialType type; + return &type; +} + +int UniColorMaterial::compare(const Material* other) const { + Q_ASSERT(other && type() == other->type()); + const auto* otherCasted = static_cast(other); + return otherCasted == this ? 0 : 1; +} + +std::unique_ptr UniColorMaterial::createShader() const { + return std::make_unique( + "unicolor.vert", "unicolor.frag", uniforms(), attributes()); +} diff --git a/src/rendergraph/opengl/rendergraph/material/unicolormaterial.h b/src/rendergraph/opengl/rendergraph/material/unicolormaterial.h new file mode 100644 index 000000000000..9516bb8c731b --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/material/unicolormaterial.h @@ -0,0 +1,21 @@ +#include "rendergraph/attributeset.h" +#include "rendergraph/material.h" + +namespace rendergraph { +class UniColorMaterial; +} + +class rendergraph::UniColorMaterial : public rendergraph::Material { + public: + UniColorMaterial(); + + static const AttributeSet& attributes(); + + static const UniformSet& uniforms(); + + MaterialType* type() const override; + + int compare(const Material* other) const override; + + std::unique_ptr createShader() const override; +}; diff --git a/src/rendergraph/opengl/rendergraph/materialshader.cpp b/src/rendergraph/opengl/rendergraph/materialshader.cpp new file mode 100644 index 000000000000..aaea06fff176 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/materialshader.cpp @@ -0,0 +1,45 @@ +#include "rendergraph/materialshader.h" + +#include + +#include "rendergraph/attributeset.h" +#include "rendergraph/uniformset.h" +using namespace rendergraph; + +MaterialShader::MaterialShader(const char* vertexShaderFile, + const char* fragmentShaderFile, + const UniformSet& uniformSet, + const AttributeSet& attributeSet) { + addShaderFromSourceFile(QOpenGLShader::Vertex, resource(vertexShaderFile)); + addShaderFromSourceFile(QOpenGLShader::Fragment, resource(fragmentShaderFile)); + 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); + } +} + +MaterialShader::~MaterialShader() = default; + +QOpenGLShaderProgram& MaterialShader::glShader() { + return *this; +} + +int MaterialShader::attributeLocation(int attributeIndex) const { + return m_attributeLocations[attributeIndex]; +} + +int MaterialShader::uniformLocation(int uniformIndex) const { + return m_uniformLocations[uniformIndex]; +} + +Material* MaterialShader::lastModifiedByMaterial() const { + return m_pLastModifiedByMaterial; +} +void MaterialShader::setLastModifiedByMaterial(Material* pMaterial) { + m_pLastModifiedByMaterial = pMaterial; +} diff --git a/src/rendergraph/opengl/rendergraph/materialshader.h b/src/rendergraph/opengl/rendergraph/materialshader.h new file mode 100644 index 000000000000..cc2ca6990615 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/materialshader.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class AttributeSet; +class UniformSet; +class MaterialShader; +class Material; +} // namespace rendergraph + +class rendergraph::MaterialShader : public QOpenGLShaderProgram { + public: + MaterialShader(const char* vertexShaderFile, + const char* fragmentShaderFile, + const UniformSet& uniforms, + const AttributeSet& attributeSet); + ~MaterialShader(); + + QOpenGLShaderProgram& glShader(); + int attributeLocation(int attributeIndex) const; + int uniformLocation(int uniformIndex) const; + + private: + friend Material; + + Material* lastModifiedByMaterial() const; + void setLastModifiedByMaterial(Material* pMaterial); + + static QString resource(const char* filename) { + return QString(":/shaders/rendergraph/") + QString(filename) + QString(".gl"); + } + std::vector m_attributeLocations; + std::vector m_uniformLocations; + Material* m_pLastModifiedByMaterial{}; +}; diff --git a/src/rendergraph/opengl/rendergraph/materialtype.cpp b/src/rendergraph/opengl/rendergraph/materialtype.cpp new file mode 100644 index 000000000000..3839d6f5b8eb --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/materialtype.cpp @@ -0,0 +1,6 @@ +#include "rendergraph/materialtype.h" + +using namespace rendergraph; + +MaterialType::MaterialType() = default; +MaterialType::~MaterialType() = default; diff --git a/src/rendergraph/opengl/rendergraph/materialtype.h b/src/rendergraph/opengl/rendergraph/materialtype.h new file mode 100644 index 000000000000..d5b5c980fbdc --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/materialtype.h @@ -0,0 +1,11 @@ +#pragma once + +namespace rendergraph { +class MaterialType; +} + +class rendergraph::MaterialType { + public: + MaterialType(); + ~MaterialType(); +}; diff --git a/src/rendergraph/opengl/rendergraph/node.cpp b/src/rendergraph/opengl/rendergraph/node.cpp new file mode 100644 index 000000000000..810aeef83e67 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/node.cpp @@ -0,0 +1,49 @@ +#include "rendergraph/node.h" + +#include "rendergraph/graph.h" + +using namespace rendergraph; + +Node::Node() = default; + +Node::~Node() = default; + +void Node::appendChildNode(std::unique_ptr&& pChild) { + auto pChildRawPtr = pChild.get(); + if (m_pLastChild) { + pChild->m_pPreviousSibling = m_pLastChild; + m_pLastChild->m_pNextSibling = std::move(pChild); + } else { + m_pFirstChild = std::move(pChild); + } + m_pLastChild = pChildRawPtr; + m_pLastChild->m_pParent = this; + if (graph() != nullptr && graph() != pChildRawPtr->graph()) { + graph()->addToGraph(pChildRawPtr); + } +} +std::unique_ptr Node::removeAllChildNodes() { + m_pLastChild = nullptr; + Node* pChild = m_pFirstChild.get(); + while (pChild) { + pChild->m_pParent = nullptr; + pChild = pChild->m_pNextSibling.get(); + } + return std::move(m_pFirstChild); +} +std::unique_ptr Node::removeChildNode(Node* pChild) { + std::unique_ptr pRemoved; + if (pChild == m_pFirstChild.get()) { + pRemoved = std::move(m_pFirstChild); + m_pFirstChild = std::move(pChild->m_pNextSibling); + } else { + pRemoved = std::move(pChild->m_pPreviousSibling->m_pNextSibling); + pChild->m_pPreviousSibling->m_pNextSibling = std::move(pChild->m_pNextSibling); + pChild->m_pPreviousSibling = nullptr; + } + if (pChild == m_pLastChild) { + m_pLastChild = nullptr; + } + pChild->m_pParent = nullptr; + return pRemoved; +} diff --git a/src/rendergraph/opengl/rendergraph/node.h b/src/rendergraph/opengl/rendergraph/node.h new file mode 100644 index 000000000000..f8f49a8bbeb3 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/node.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class Node; +class Graph; +} // namespace rendergraph + +class rendergraph::Node { + public: + Node(); + + virtual ~Node(); + + void appendChildNode(std::unique_ptr&& pChild); + std::unique_ptr removeAllChildNodes(); + std::unique_ptr removeChildNode(Node* pChild); + + Node* parent() const { + return m_pParent; + } + Node* firstChild() const { + return m_pFirstChild.get(); + } + Node* lastChild() const { + return m_pLastChild; + } + Node* nextSibling() const { + return m_pNextSibling.get(); + } + Node* previousSibling() const { + return m_pPreviousSibling; + } + + virtual bool isSubtreeBlocked() const { + return false; + } + + virtual void preprocess() { + } + + virtual void initialize() { + } + virtual void render() { + } + virtual void resize(int, int) { + } + + void setUsePreprocess(bool value) { + m_usePreprocess = value; + } + bool usePreprocess() const { + return m_usePreprocess; + } + + Graph* graph() const { + return m_pGraph; + } + void setGraph(Graph* pGraph) { + m_pGraph = pGraph; + } + + private: + Graph* m_pGraph{}; + Node* m_pParent{}; + std::unique_ptr m_pFirstChild; + Node* m_pLastChild{}; + std::unique_ptr m_pNextSibling; + Node* m_pPreviousSibling{}; + + bool m_usePreprocess{}; +}; diff --git a/src/rendergraph/opengl/rendergraph/opacitynode.cpp b/src/rendergraph/opengl/rendergraph/opacitynode.cpp new file mode 100644 index 000000000000..3059ae0886b6 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/opacitynode.cpp @@ -0,0 +1,15 @@ +#include "rendergraph/opacitynode.h" + +using namespace rendergraph; + +OpacityNode::OpacityNode() = default; + +OpacityNode::~OpacityNode() = default; + +void OpacityNode::setOpacity(float opacity) { + m_opacity = opacity; +} + +bool OpacityNode::isSubtreeBlocked() const { + return m_opacity == 0.f; +} diff --git a/src/rendergraph/opengl/rendergraph/opacitynode.h b/src/rendergraph/opengl/rendergraph/opacitynode.h new file mode 100644 index 000000000000..a9a7b0216dfa --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/opacitynode.h @@ -0,0 +1,18 @@ +#pragma once + +#include "rendergraph/node.h" + +namespace rendergraph { +class OpacityNode; +} // namespace rendergraph + +class rendergraph::OpacityNode : public rendergraph::Node { + public: + OpacityNode(); + ~OpacityNode(); + void setOpacity(float opacity); + bool isSubtreeBlocked() const override; + + private: + float m_opacity{1.f}; +}; diff --git a/src/rendergraph/opengl/rendergraph/openglnode.cpp b/src/rendergraph/opengl/rendergraph/openglnode.cpp new file mode 100644 index 000000000000..f13844449e82 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/openglnode.cpp @@ -0,0 +1,22 @@ +#include "rendergraph/openglnode.h" + +using namespace rendergraph; + +OpenGLNode::OpenGLNode() = default; +OpenGLNode::~OpenGLNode() = default; + +void OpenGLNode::initialize() { + initializeOpenGLFunctions(); + initializeGL(); + Node::initialize(); +} + +void OpenGLNode::render() { + paintGL(); + Node::render(); +} + +void OpenGLNode::resize(int w, int h) { + resizeGL(w, h); + Node::resize(w, h); +} diff --git a/src/rendergraph/opengl/rendergraph/openglnode.h b/src/rendergraph/opengl/rendergraph/openglnode.h new file mode 100644 index 000000000000..8ff3dedc74d0 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/openglnode.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "rendergraph/node.h" + +namespace rendergraph { +class OpenGLNode; +} // namespace rendergraph + +class rendergraph::OpenGLNode : public rendergraph::Node, public QOpenGLFunctions { + public: + OpenGLNode(); + ~OpenGLNode(); + + virtual void initializeGL() { + } + virtual void paintGL() { + } + virtual void resizeGL(int, int) { + } + void initialize() override; + void render() override; + void resize(int w, int h) override; +}; diff --git a/src/rendergraph/opengl/rendergraph/shadercache.h b/src/rendergraph/opengl/rendergraph/shadercache.h new file mode 100644 index 000000000000..15a4aee67995 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/shadercache.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "rendergraph/material.h" +#include "rendergraph/materialshader.h" + +namespace rendergraph { +class ShaderCache; +} + +class rendergraph::ShaderCache { + private: + static std::map>& map() { + static std::map> s_map; + return s_map; + } + + public: + static std::shared_ptr getShaderForMaterial(Material* pMaterial) { + auto iter = map().find(pMaterial->type()); + if (iter != map().end()) { + return iter->second; + } + auto pResult = std::shared_ptr(pMaterial->createShader().release()); + map().insert(std::pair>{ + pMaterial->type(), pResult}); + return pResult; + } + static void purge() { + auto iter = map().begin(); + while (iter != map().end()) { + if (iter->second.use_count() == 1) { + iter = map().erase(iter); + } else { + ++iter; + } + } + } +}; diff --git a/src/rendergraph/opengl/rendergraph/texture.cpp b/src/rendergraph/opengl/rendergraph/texture.cpp new file mode 100644 index 000000000000..fd3c9e16b191 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/texture.cpp @@ -0,0 +1,29 @@ +#include "rendergraph/texture.h" + +#include + +using namespace rendergraph; + +Texture::Texture(Context& context, const QImage& image) + : m_pTexture(new QOpenGLTexture(premultiplyAlpha(image))) { + m_pTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); + m_pTexture->setWrapMode(QOpenGLTexture::ClampToEdge); +} + +Texture::~Texture() = default; + +QOpenGLTexture* Texture::glTexture() const { + return m_pTexture.get(); +} + +QImage Texture::premultiplyAlpha(const QImage& image) { + if (image.format() == QImage::Format_RGBA8888_Premultiplied) { + return QImage(image.bits(), image.width(), image.height(), QImage::Format_RGBA8888); + } + return QImage( + image.convertToFormat(QImage::Format_RGBA8888_Premultiplied) + .bits(), + image.width(), + image.height(), + QImage::Format_RGBA8888); +} diff --git a/src/rendergraph/opengl/rendergraph/texture.h b/src/rendergraph/opengl/rendergraph/texture.h new file mode 100644 index 000000000000..a130e13d8b73 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/texture.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +class QImage; +class QOpenGLTexture; + +namespace rendergraph { +class Context; +class Texture; +} // namespace rendergraph + +class rendergraph::Texture { + public: + Texture(Context& context, const QImage& image); + ~Texture(); + + QOpenGLTexture* glTexture() const; + + private: + const std::unique_ptr m_pTexture{}; + static QImage premultiplyAlpha(const QImage& image); +}; diff --git a/src/rendergraph/opengl/rendergraph/types.cpp b/src/rendergraph/opengl/rendergraph/types.cpp new file mode 100644 index 000000000000..a44d113d7f83 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/types.cpp @@ -0,0 +1,107 @@ +#include "rendergraph/types.h" + +#include + +using namespace rendergraph; + +int rendergraph::sizeOf(PrimitiveType type) { + switch (type) { + case PrimitiveType::UInt: + return sizeof(GLuint); + case PrimitiveType::Float: + return sizeof(GLfloat); + } + return 0; +} + +int rendergraph::sizeOf(Type type) { + switch (type) { + case Type::UInt: + return sizeof(GLuint); + case Type::Float: + return sizeof(GLfloat); + case Type::Vector2D: + return sizeof(GLfloat) * 2; + case Type::Vector3D: + return sizeof(GLfloat) * 3; + case Type::Vector4D: + return sizeof(GLfloat) * 4; + case Type::Matrix4x4: + return sizeof(GLfloat) * 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/opengl/rendergraph/types.h b/src/rendergraph/opengl/rendergraph/types.h new file mode 100644 index 000000000000..c8aea683757c --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/types.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +namespace rendergraph { + +enum class PrimitiveType { + UInt, + Float, +}; + +enum class Type { + UInt, + Float, + Vector2D, + Vector3D, + Vector4D, + Matrix4x4 +}; + +int sizeOf(Type type); +int sizeOf(PrimitiveType primitiveType); + +template +Type typeOf(); + +template +PrimitiveType primitiveTypeOf(); + +template +int tupleSizeOf(); + +} // namespace rendergraph diff --git a/src/rendergraph/opengl/rendergraph/uniform.h b/src/rendergraph/opengl/rendergraph/uniform.h new file mode 100644 index 000000000000..19a6a9c74a35 --- /dev/null +++ b/src/rendergraph/opengl/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/opengl/rendergraph/uniformscache.cpp b/src/rendergraph/opengl/rendergraph/uniformscache.cpp new file mode 100644 index 000000000000..3e43b60aab67 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/uniformscache.cpp @@ -0,0 +1,28 @@ +#include "rendergraph/uniformscache.h" + +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniformsCache::UniformsCache(const UniformSet& uniformSet) { + int offset = 0; + 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, Type type, const void* ptr, int size) { + assert(type == m_infos[uniformIndex].m_type); + memcpy(m_byteArray.data() + m_infos[uniformIndex].m_offset, ptr, size); +} + +void UniformsCache::get(int uniformIndex, Type type, void* ptr, int size) const { + assert(type == m_infos[uniformIndex].m_type); + memcpy(ptr, m_byteArray.data() + m_infos[uniformIndex].m_offset, size); +} diff --git a/src/rendergraph/opengl/rendergraph/uniformscache.h b/src/rendergraph/opengl/rendergraph/uniformscache.h new file mode 100644 index 000000000000..5787570effd2 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/uniformscache.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include + +#include "rendergraph/types.h" + +namespace rendergraph { +class UniformSet; +class UniformsCache; +} // namespace rendergraph + +class rendergraph::UniformsCache { + public: + UniformsCache(const UniformSet& uniformSet); + ~UniformsCache(); + + template + void set(int uniformIndex, const T& value) { + set(uniformIndex, typeOf(), static_cast(&value), sizeOf(typeOf())); + } + + template + T get(int uniformIndex) const { + T value; + get(uniformIndex, typeOf(), 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, Type type, const void* ptr, int size); + void get(int uniformIndex, Type type, 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) { + set(uniformIndex, typeOf(), matrix.constData(), sizeOf(typeOf())); +} diff --git a/src/rendergraph/opengl/rendergraph/uniformset.cpp b/src/rendergraph/opengl/rendergraph/uniformset.cpp new file mode 100644 index 000000000000..68e077613bd8 --- /dev/null +++ b/src/rendergraph/opengl/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/opengl/rendergraph/uniformset.h b/src/rendergraph/opengl/rendergraph/uniformset.h new file mode 100644 index 000000000000..afa1cfc24265 --- /dev/null +++ b/src/rendergraph/opengl/rendergraph/uniformset.h @@ -0,0 +1,30 @@ +#pragma once + +#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/scenegraph/CMakeLists.txt b/src/rendergraph/scenegraph/CMakeLists.txt new file mode 100644 index 000000000000..9c1e88dad156 --- /dev/null +++ b/src/rendergraph/scenegraph/CMakeLists.txt @@ -0,0 +1,67 @@ +add_library(rendergraph_sg + rendergraph/attribute.h + rendergraph/attributeset.cpp + rendergraph/attributeset.h + rendergraph/context.cpp + rendergraph/context.h + rendergraph/geometry.cpp + rendergraph/geometry.h + rendergraph/geometrynode.cpp + rendergraph/geometrynode.h + rendergraph/material.cpp + rendergraph/material.h + rendergraph/materialshader.cpp + rendergraph/materialshader.h + rendergraph/materialtype.cpp + rendergraph/materialtype.h + rendergraph/node.cpp + rendergraph/node.h + rendergraph/opacitynode.cpp + rendergraph/opacitynode.h + rendergraph/texture.cpp + rendergraph/texture.h + rendergraph/types.cpp + rendergraph/types.h + rendergraph/uniform.h + rendergraph/uniformscache.cpp + rendergraph/uniformscache.h + rendergraph/uniformset.cpp + rendergraph/uniformset.h + rendergraph/openglnode.h + rendergraph/openglnode.cpp + ../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 + materialshader_impl.cpp + private/attributeset_impl.h + private/context_impl.h + private/geometry_impl.h + private/geometrynode_impl.h + private/material_impl.h + private/materialshader_impl.h + private/materialtype_impl.h + private/node_impl.h + private/opacitynode_impl.h + private/texture_impl.h + rendergraph/scenegraph.cpp + rendergraph/scenegraph.h + texture_impl.cpp +) + +target_link_libraries(rendergraph_sg PUBLIC + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick +) + +target_include_directories(rendergraph_sg PUBLIC . ../common PRIVATE private) diff --git a/src/rendergraph/scenegraph/materialshader_impl.cpp b/src/rendergraph/scenegraph/materialshader_impl.cpp new file mode 100644 index 000000000000..5b95250e6c8e --- /dev/null +++ b/src/rendergraph/scenegraph/materialshader_impl.cpp @@ -0,0 +1,22 @@ +#include "materialshader_impl.h" + +#include "material_impl.h" + +using namespace rendergraph; + +bool rendergraph::MaterialShader::Impl::updateUniformData(RenderState& state, + QSGMaterial* newMaterial, + QSGMaterial* oldMaterial) { + Material::Impl* pMaterialImpl = static_cast(newMaterial); + return pMaterialImpl->updateUniformsByteArray(state.uniformData()); +} + +void MaterialShader::Impl::updateSampledImage(RenderState& state, + int binding, + QSGTexture** texture, + QSGMaterial* newMaterial, + QSGMaterial* oldMaterial) { + Material::Impl* pMaterialImpl = static_cast(newMaterial); + *texture = pMaterialImpl->texture(binding); + (*texture)->commitTextureOperations(state.rhi(), state.resourceUpdateBatch()); +} diff --git a/src/rendergraph/scenegraph/private/attributeset_impl.h b/src/rendergraph/scenegraph/private/attributeset_impl.h new file mode 100644 index 000000000000..45cc8714cfce --- /dev/null +++ b/src/rendergraph/scenegraph/private/attributeset_impl.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "rendergraph/attributeset.h" + +class rendergraph::AttributeSet::Impl { + public: + void add(const Attribute& attribute) { + const int count = static_cast(m_sgAttributes.size()); + const bool isPosition = count == 0; + m_sgAttributes.push_back(QSGGeometry::Attribute::create(count, + attribute.m_tupleSize, + toQSGGeometryType(attribute.m_primitiveType), + isPosition)); + const int stride = m_sgAttributeSet.stride + + attribute.m_tupleSize * sizeOf(attribute.m_primitiveType); + m_sgAttributeSet = QSGGeometry::AttributeSet{count + 1, stride, m_sgAttributes.data()}; + } + + const QSGGeometry::AttributeSet& sgAttributeSet() const { + return m_sgAttributeSet; + } + + private: + QSGGeometry::AttributeSet m_sgAttributeSet{}; + std::vector m_sgAttributes; + + int toQSGGeometryType(const rendergraph::PrimitiveType& t) { + switch (t) { + case rendergraph::PrimitiveType::Float: + return QSGGeometry::FloatType; + case rendergraph::PrimitiveType::UInt: + return QSGGeometry::UnsignedIntType; + } + } +}; diff --git a/src/rendergraph/scenegraph/private/context_impl.h b/src/rendergraph/scenegraph/private/context_impl.h new file mode 100644 index 000000000000..bce2fb3ac76d --- /dev/null +++ b/src/rendergraph/scenegraph/private/context_impl.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include "rendergraph/context.h" + +class rendergraph::Context::Impl { + public: + void setWindow(QQuickWindow* pWindow) { + m_pWindow = pWindow; + } + QQuickWindow* window() const { + return m_pWindow; + } + + private: + QQuickWindow* m_pWindow; +}; diff --git a/src/rendergraph/scenegraph/private/geometry_impl.h b/src/rendergraph/scenegraph/private/geometry_impl.h new file mode 100644 index 000000000000..b3e1cd803893 --- /dev/null +++ b/src/rendergraph/scenegraph/private/geometry_impl.h @@ -0,0 +1,78 @@ +#pragma once + +#include + +#include "attributeset_impl.h" +#include "rendergraph/geometry.h" + +class rendergraph::Geometry::Impl : public QSGGeometry { + public: + Impl(const rendergraph::AttributeSet& rgAttributeSet, int vertexCount) + : QSGGeometry(rgAttributeSet.impl().sgAttributeSet(), vertexCount), + m_stride(rgAttributeSet.impl().sgAttributeSet().stride) { + QSGGeometry::setDrawingMode(QSGGeometry::DrawTriangleStrip); + } + + QSGGeometry* sgGeometry() { + return this; + } + + void setAttributeValues(int attributePosition, const float* from, int numTuples) { + const auto attributeArray = QSGGeometry::attributes(); + int offset = 0; + for (int i = 0; i < attributePosition; i++) { + offset += attributeArray[i].tupleSize; + } + const int tupleSize = attributeArray[attributePosition].tupleSize; + const int skip = m_stride / sizeof(float) - tupleSize; + + float* to = static_cast(QSGGeometry::vertexData()); + to += offset; + while (numTuples--) { + int k = tupleSize; + while (k--) { + *to++ = *from++; + } + to += skip; + } + } + + void setDrawingMode(Geometry::DrawingMode mode) { + QSGGeometry::setDrawingMode(toSgDrawingMode(mode)); + } + + Geometry::DrawingMode drawingMode() const { + return fromSgDrawingMode(QSGGeometry::drawingMode()); + } + + float* vertexData() { + return static_cast(QSGGeometry::vertexData()); + } + + template + T* vertexDataAs() { + return static_cast(QSGGeometry::vertexData()); + } + + private: + const int m_stride; + + static QSGGeometry::DrawingMode toSgDrawingMode(Geometry::DrawingMode mode) { + switch (mode) { + case Geometry::DrawingMode::Triangles: + return QSGGeometry::DrawTriangles; + case Geometry::DrawingMode::TriangleStrip: + return QSGGeometry::DrawTriangleStrip; + } + } + static Geometry::DrawingMode fromSgDrawingMode(unsigned int mode) { + switch (mode) { + case QSGGeometry::DrawTriangles: + return Geometry::DrawingMode::Triangles; + case QSGGeometry::DrawTriangleStrip: + return Geometry::DrawingMode::TriangleStrip; + default: + throw "not implemented"; + } + } +}; diff --git a/src/rendergraph/scenegraph/private/geometrynode_impl.h b/src/rendergraph/scenegraph/private/geometrynode_impl.h new file mode 100644 index 000000000000..7270d5e357d6 --- /dev/null +++ b/src/rendergraph/scenegraph/private/geometrynode_impl.h @@ -0,0 +1,29 @@ +#pragma once + +#include "geometry_impl.h" +#include "material_impl.h" +#include "node_impl.h" +#include "rendergraph/geometrynode.h" + +class rendergraph::GeometryNode::Impl : public QSGGeometryNode, public rendergraph::NodeImplBase { + public: + Impl(GeometryNode* pOwner) + : NodeImplBase(pOwner) { + } + + QSGNode* sgNode() override { + return this; + } + void setGeometry(Geometry* geometry) { + QSGGeometryNode::setGeometry(geometry->impl().sgGeometry()); + } + void setMaterial(Material* material) { + QSGGeometryNode::setMaterial(material->impl().sgMaterial()); + } + void preprocess() override { + owner()->preprocess(); + } + bool isSubtreeBlocked() const override { + return owner()->isSubtreeBlocked(); + } +}; diff --git a/src/rendergraph/scenegraph/private/material_impl.h b/src/rendergraph/scenegraph/private/material_impl.h new file mode 100644 index 000000000000..4b36371f0c8c --- /dev/null +++ b/src/rendergraph/scenegraph/private/material_impl.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include "materialshader_impl.h" +#include "materialtype_impl.h" +#include "rendergraph/material.h" +#include "texture_impl.h" + +class rendergraph::Material::Impl : public QSGMaterial { + public: + Impl(Material* pOwner) + : m_pOwner(pOwner) { + setFlag(QSGMaterial::Blending); + } + + QSGMaterial* sgMaterial() { + return this; + } + + bool updateUniformsByteArray(QByteArray* buf) { + if (m_pOwner->clearUniformsCacheDirty()) { + memcpy(buf->data(), m_pOwner->uniformsCache().data(), m_pOwner->uniformsCache().size()); + return true; + } + return false; + } + + QSGTexture* texture(int binding) { + return m_pOwner->texture(binding)->impl().sgTexture(); + } + + private: + QSGMaterialType* type() const override { + return m_pOwner->type()->impl().sgMaterialType(); + } + + int compare(const QSGMaterial* other) const override { + const Impl* otherCasted = static_cast(other); + return otherCasted && m_pOwner->compare(otherCasted->m_pOwner); + } + + QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override { + auto pShader = m_pOwner->createShader().release(); // This leaks + return pShader->impl().sgMaterialShader(); + } + + Material* m_pOwner; +}; diff --git a/src/rendergraph/scenegraph/private/materialshader_impl.h b/src/rendergraph/scenegraph/private/materialshader_impl.h new file mode 100644 index 000000000000..3067e605c0e8 --- /dev/null +++ b/src/rendergraph/scenegraph/private/materialshader_impl.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include "rendergraph/materialshader.h" + +class rendergraph::MaterialShader::Impl : public QSGMaterialShader { + public: + Impl(MaterialShader* pOwner, + const char* vertexShaderFile, + const char* fragmentShaderFile, + const UniformSet& uniformSet, + const AttributeSet& attributeSet) + : m_pOwner(pOwner) { + (void)uniformSet; + (void)attributeSet; + setShaderFileName(VertexStage, resource(vertexShaderFile)); + setShaderFileName(FragmentStage, resource(fragmentShaderFile)); + } + + QSGMaterialShader* sgMaterialShader() { + return this; + } + + private: + bool updateUniformData(RenderState& state, + QSGMaterial* newMaterial, + QSGMaterial* oldMaterial) override; + + void updateSampledImage(RenderState& state, + int binding, + QSGTexture** texture, + QSGMaterial* newMaterial, + QSGMaterial* oldMaterial) override; + + static QString resource(const char* filename) { + return QString(":/shaders/rendergraph/") + QString(filename) + QString(".qsb"); + } + + MaterialShader* m_pOwner; +}; diff --git a/src/rendergraph/scenegraph/private/materialtype_impl.h b/src/rendergraph/scenegraph/private/materialtype_impl.h new file mode 100644 index 000000000000..c48df4864465 --- /dev/null +++ b/src/rendergraph/scenegraph/private/materialtype_impl.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "rendergraph/materialtype.h" + +class rendergraph::MaterialType::Impl : public QSGMaterialType { + public: + QSGMaterialType* sgMaterialType() { + return this; + } +}; diff --git a/src/rendergraph/scenegraph/private/node_impl.h b/src/rendergraph/scenegraph/private/node_impl.h new file mode 100644 index 000000000000..7767ef33344b --- /dev/null +++ b/src/rendergraph/scenegraph/private/node_impl.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include "rendergraph/node.h" + +class rendergraph::NodeImplBase { + public: + NodeImplBase(Node* pOwner) + : m_pOwner(pOwner) { + } + + virtual ~NodeImplBase() = default; + + virtual QSGNode* sgNode() = 0; + + Node* owner() const { + return m_pOwner; + } + + void setUsePreprocess(bool value) { + sgNode()->setFlag(QSGNode::UsePreprocess, value); + } + + void onAppendChildNode(Node* pChild) { + sgNode()->appendChildNode(pChild->impl().sgNode()); + } + void onRemoveAllChildNodes() { + sgNode()->removeAllChildNodes(); + } + void onRemoveChildNode(Node* pChild) { + sgNode()->removeChildNode(pChild->impl().sgNode()); + } + + private: + Node* m_pOwner; +}; + +class rendergraph::Node::Impl : public QSGNode, public rendergraph::NodeImplBase { + public: + Impl(Node* pOwner) + : NodeImplBase(pOwner) { + } + virtual ~Impl() = default; + + QSGNode* sgNode() override { + return this; + } + bool isSubtreeBlocked() const override { + return owner()->isSubtreeBlocked(); + } + void preprocess() override { + owner()->preprocess(); + } +}; diff --git a/src/rendergraph/scenegraph/private/opacitynode_impl.h b/src/rendergraph/scenegraph/private/opacitynode_impl.h new file mode 100644 index 000000000000..aa19bc8f6e72 --- /dev/null +++ b/src/rendergraph/scenegraph/private/opacitynode_impl.h @@ -0,0 +1,23 @@ +#pragma once + +#include "node_impl.h" +#include "rendergraph/opacitynode.h" + +class rendergraph::OpacityNode::Impl : public QSGOpacityNode, public rendergraph::NodeImplBase { + public: + Impl(OpacityNode* pOwner) + : NodeImplBase(pOwner) { + } + + QSGNode* sgNode() override { + return this; + } + + void preprocess() override { + owner()->preprocess(); + } + + bool isSubtreeBlocked() const override { + return QSGOpacityNode::isSubtreeBlocked() || owner()->isSubtreeBlocked(); + } +}; diff --git a/src/rendergraph/scenegraph/private/texture_impl.h b/src/rendergraph/scenegraph/private/texture_impl.h new file mode 100644 index 000000000000..eea6d6d6a297 --- /dev/null +++ b/src/rendergraph/scenegraph/private/texture_impl.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "rendergraph/texture.h" + +// We can't use inheritance because QSGTexture has pure virtuals that we can't +// implement, so we encapsulate instead. +class rendergraph::Texture::Impl { + public: + Impl(Context& context, const QImage& image); + + QSGTexture* sgTexture() const { + return m_pTexture.get(); + } + + private: + std::unique_ptr m_pTexture{}; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/attribute.h b/src/rendergraph/scenegraph/rendergraph/attribute.h new file mode 100644 index 000000000000..5fae9e3df544 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/attribute.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "rendergraph/types.h" + +namespace rendergraph { +struct Attribute; +} + +struct rendergraph::Attribute { + const int m_tupleSize; + const PrimitiveType m_primitiveType; + const QString m_name; + + Attribute(int tupleSize, PrimitiveType primitiveType) + : m_tupleSize{tupleSize}, + m_primitiveType{primitiveType} { + } + + Attribute(int tupleSize, PrimitiveType primitiveType, QString name) + : m_tupleSize{tupleSize}, + m_primitiveType{primitiveType}, + m_name{std::move(name)} { + } + + template + static Attribute create() { + return Attribute(tupleSizeOf(), primitiveTypeOf()); + } +}; diff --git a/src/rendergraph/scenegraph/rendergraph/attributeset.cpp b/src/rendergraph/scenegraph/rendergraph/attributeset.cpp new file mode 100644 index 000000000000..92d05368a1be --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/attributeset.cpp @@ -0,0 +1,36 @@ +#include "rendergraph/attributeset.h" + +#include "attributeset_impl.h" + +using namespace rendergraph; + +AttributeSet::AttributeSet(AttributeSet::Impl* pImpl) + : m_pImpl(pImpl) { +} + +AttributeSet::AttributeSet() + : AttributeSet(new AttributeSet::Impl()) { +} + +AttributeSet::AttributeSet(std::initializer_list list, const std::vector& names) + : AttributeSet() { + int i = 0; + for (const auto& item : list) { + add(Attribute{item.m_tupleSize, item.m_primitiveType, names[i++]}); + } +} + +AttributeSet::~AttributeSet() = default; + +AttributeSet::Impl& AttributeSet::impl() const { + return *m_pImpl; +} + +void AttributeSet::add(const Attribute& attribute) { + m_attributes.push_back(attribute); + m_pImpl->add(attribute); +} + +const std::vector& AttributeSet::attributes() const { + return m_attributes; +} diff --git a/src/rendergraph/scenegraph/rendergraph/attributeset.h b/src/rendergraph/scenegraph/rendergraph/attributeset.h new file mode 100644 index 000000000000..a402973cb0de --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/attributeset.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include "rendergraph/attribute.h" + +namespace rendergraph { +class AttributeSet; +} + +class rendergraph::AttributeSet { + public: + class Impl; + + AttributeSet(); + AttributeSet(std::initializer_list list, const std::vector& names); + + ~AttributeSet(); + + const std::vector& attributes() const; + Impl& impl() const; + + private: + AttributeSet(Impl* pImpl); + void add(const Attribute& attribute); + + std::vector m_attributes; + const std::unique_ptr m_pImpl; +}; + +namespace rendergraph { +template +AttributeSet makeAttributeSet(const std::vector& names) { + return AttributeSet({(Attribute::create())...}, names); +} +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/rendergraph/context.cpp b/src/rendergraph/scenegraph/rendergraph/context.cpp new file mode 100644 index 000000000000..1c64ec6d851d --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/context.cpp @@ -0,0 +1,15 @@ +#include "rendergraph/context.h" + +#include "context_impl.h" + +using namespace rendergraph; + +Context::Context() + : m_pImpl(new Context::Impl()) { +} + +Context::~Context() = default; + +Context::Impl& Context::impl() const { + return *m_pImpl; +} diff --git a/src/rendergraph/scenegraph/rendergraph/context.h b/src/rendergraph/scenegraph/rendergraph/context.h new file mode 100644 index 000000000000..2659910bfdd3 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/context.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace rendergraph { +class Context; +} + +class rendergraph::Context { + public: + class Impl; + Context(); + ~Context(); + Impl& impl() const; + + private: + const std::unique_ptr m_pImpl; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/geometry.cpp b/src/rendergraph/scenegraph/rendergraph/geometry.cpp new file mode 100644 index 000000000000..67eb8e86fe2b --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/geometry.cpp @@ -0,0 +1,60 @@ +#include "rendergraph/geometry.h" + +#include + +#include "geometry_impl.h" + +using namespace rendergraph; + +Geometry::Geometry(Impl* pImpl) + : m_pImpl(pImpl) { +} + +Geometry::Geometry(const AttributeSet& attributeSet, int vertexCount) + : Geometry(new Geometry::Impl(attributeSet, vertexCount)){}; + +Geometry::~Geometry() = default; + +Geometry::Impl& Geometry::impl() const { + return *m_pImpl; +} + +void Geometry::setAttributeValues(int attributePosition, const float* data, int numTuples) { + m_pImpl->setAttributeValues(attributePosition, data, numTuples); +} + +float* Geometry::vertexData() { + return m_pImpl->vertexData(); +} + +template<> +Geometry::Point2D* Geometry::vertexDataAs() { + return m_pImpl->vertexDataAs(); +} + +template<> +Geometry::TexturedPoint2D* Geometry::vertexDataAs() { + return m_pImpl->vertexDataAs(); +} + +template<> +Geometry::RGBColoredPoint2D* Geometry::vertexDataAs() { + return m_pImpl->vertexDataAs(); +} + +template<> +Geometry::RGBAColoredPoint2D* Geometry::vertexDataAs() { + return m_pImpl->vertexDataAs(); +} + +void Geometry::allocate(int vertexCount) { + m_pImpl->allocate(vertexCount); +} + +void Geometry::setDrawingMode(Geometry::DrawingMode mode) { + m_pImpl->setDrawingMode(mode); +} + +Geometry::DrawingMode Geometry::drawingMode() const { + return m_pImpl->Impl::drawingMode(); +} diff --git a/src/rendergraph/scenegraph/rendergraph/geometry.h b/src/rendergraph/scenegraph/rendergraph/geometry.h new file mode 100644 index 000000000000..c83a8c6c5c5b --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/geometry.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class Geometry; +class AttributeSet; +} // namespace rendergraph + +class rendergraph::Geometry { + public: + struct Point2D { + QVector2D position2D; + Point2D(float x, float y) + : position2D{x, y} { + } + }; + + struct TexturedPoint2D { + QVector2D position2D; + QVector2D texcoord2D; + TexturedPoint2D(float x, float y, float tx, float ty) + : position2D{x, y}, + texcoord2D{tx, ty} { + } + }; + + struct RGBColoredPoint2D { + QVector2D position2D; + QVector3D color3D; + RGBColoredPoint2D(float x, float y, float r, float g, float b) + : position2D{x, y}, + color3D{r, g, b} { + } + }; + + struct RGBAColoredPoint2D { + QVector2D position2D; + QVector4D color4D; + RGBAColoredPoint2D(float x, float y, float r, float g, float b, float a) + : position2D{x, y}, + color4D{r, g, b, a} { + } + }; + + enum class DrawingMode { + Triangles, + TriangleStrip + }; + class Impl; + + Geometry(const AttributeSet& attributeSet, int vertexCount); + ~Geometry(); + + void setAttributeValues(int attributePosition, const float* data, int numTuples); + Impl& impl() const; + + float* vertexData(); + + template + T* vertexDataAs(); + + void allocate(int vertexCount); + + DrawingMode drawingMode() const; + void setDrawingMode(DrawingMode mode); + + private: + Geometry(Impl* pImpl); + + const std::unique_ptr m_pImpl; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/geometrynode.cpp b/src/rendergraph/scenegraph/rendergraph/geometrynode.cpp new file mode 100644 index 000000000000..e083d56f7185 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/geometrynode.cpp @@ -0,0 +1,34 @@ +#include "rendergraph/geometrynode.h" + +#include "geometrynode_impl.h" +#include "rendergraph/geometry.h" + +using namespace rendergraph; + +GeometryNode::GeometryNode(NodeImplBase* pImpl) + : Node(pImpl) { +} + +GeometryNode::GeometryNode() + : GeometryNode(new GeometryNode::Impl(this)) { +} + +GeometryNode::~GeometryNode() = default; + +void GeometryNode::setGeometry(std::unique_ptr geometry) { + m_geometry = std::move(geometry); + static_cast(impl()).setGeometry(m_geometry.get()); +} + +void GeometryNode::setMaterial(std::unique_ptr material) { + m_material = std::move(material); + static_cast(impl()).setMaterial(m_material.get()); +} + +Geometry& GeometryNode::geometry() const { + return *m_geometry; +} + +Material& GeometryNode::material() const { + return *m_material; +} diff --git a/src/rendergraph/scenegraph/rendergraph/geometrynode.h b/src/rendergraph/scenegraph/rendergraph/geometrynode.h new file mode 100644 index 000000000000..1b0784a42cfe --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/geometrynode.h @@ -0,0 +1,35 @@ +#pragma once + +#include "rendergraph/geometry.h" +#include "rendergraph/node.h" + +namespace rendergraph { +class GeometryNode; +class Geometry; +class Material; +} // namespace rendergraph + +class rendergraph::GeometryNode : public rendergraph::Node { + public: + class Impl; + + GeometryNode(); + ~GeometryNode(); + + template + void initForRectangles(int numRectangles) { + setGeometry(std::make_unique(T_Material::attributes(), numRectangles * 6)); + setMaterial(std::make_unique()); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); + } + + void setMaterial(std::unique_ptr material); + void setGeometry(std::unique_ptr geometry); + Geometry& geometry() const; + Material& material() const; + + private: + GeometryNode(NodeImplBase* pImpl); + std::unique_ptr m_material; + std::unique_ptr m_geometry; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/material.cpp b/src/rendergraph/scenegraph/rendergraph/material.cpp new file mode 100644 index 000000000000..7f45a2ead86c --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/material.cpp @@ -0,0 +1,20 @@ +#include "rendergraph/material.h" + +#include "material_impl.h" + +using namespace rendergraph; + +Material::Material(Impl* pImpl, const UniformSet& uniformSet) + : m_pImpl(pImpl), + m_uniformsCache(uniformSet) { +} + +Material::Material(const UniformSet& uniformSet) + : Material(new Material::Impl(this), uniformSet) { +} + +Material::~Material() = default; + +Material::Impl& Material::impl() const { + return *m_pImpl; +} diff --git a/src/rendergraph/scenegraph/rendergraph/material.h b/src/rendergraph/scenegraph/rendergraph/material.h new file mode 100644 index 000000000000..39ca86a68710 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/material.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "rendergraph/uniformscache.h" + +namespace rendergraph { +class UniformSet; +class Material; +class MaterialShader; +class MaterialType; +class Texture; +} // namespace rendergraph + +class rendergraph::Material { + public: + class Impl; + + Material(const UniformSet& uniformSet); + virtual ~Material(); + virtual int compare(const Material* other) const = 0; + virtual std::unique_ptr createShader() const = 0; + virtual MaterialType* type() const = 0; + + template + void setUniform(int uniformIndex, const T& value) { + m_uniformsCache.set(uniformIndex, value); + m_uniformsCacheDirty = true; + } + + Impl& impl() const; + const UniformsCache& uniformsCache() const { + return m_uniformsCache; + } + + bool clearUniformsCacheDirty() { + if (m_uniformsCacheDirty) { + m_uniformsCacheDirty = false; + return true; + } + return false; + } + + virtual Texture* texture(int /*binding*/) const { + return nullptr; + } + + private: + Material(Impl* pImpl, const UniformSet& uniformSet); + + const std::unique_ptr m_pImpl; + UniformsCache m_uniformsCache; + bool m_uniformsCacheDirty{}; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/materialshader.cpp b/src/rendergraph/scenegraph/rendergraph/materialshader.cpp new file mode 100644 index 000000000000..6d27917b4bef --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/materialshader.cpp @@ -0,0 +1,25 @@ +#include "rendergraph/materialshader.h" + +#include "materialshader_impl.h" + +using namespace rendergraph; + +MaterialShader::MaterialShader(Impl* pImpl) + : m_pImpl(pImpl) { +} + +MaterialShader::MaterialShader(const char* vertexShaderFile, + const char* fragmentShaderFile, + const UniformSet& uniformSet, + const AttributeSet& attributeSet) + : MaterialShader(new MaterialShader::Impl(this, + vertexShaderFile, + fragmentShaderFile, + uniformSet, + attributeSet)){}; + +MaterialShader::~MaterialShader() = default; + +MaterialShader::Impl& MaterialShader::impl() const { + return *m_pImpl; +} diff --git a/src/rendergraph/scenegraph/rendergraph/materialshader.h b/src/rendergraph/scenegraph/rendergraph/materialshader.h new file mode 100644 index 000000000000..e696a520b6b4 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/materialshader.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace rendergraph { +class AttributeSet; +class UniformSet; +class MaterialShader; +} // namespace rendergraph + +class rendergraph::MaterialShader { + public: + class Impl; + + MaterialShader(const char* vertexShaderFile, + const char* fragmentShaderFile, + const UniformSet& uniforms, + const AttributeSet& attributeSet); + ~MaterialShader(); + Impl& impl() const; + + private: + MaterialShader(Impl* pImpl); + + const std::unique_ptr m_pImpl; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/materialtype.cpp b/src/rendergraph/scenegraph/rendergraph/materialtype.cpp new file mode 100644 index 000000000000..8249ee89f033 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/materialtype.cpp @@ -0,0 +1,18 @@ +#include "rendergraph/materialtype.h" + +#include "materialtype_impl.h" + +using namespace rendergraph; + +MaterialType::MaterialType(Impl* pImpl) + : m_pImpl(pImpl) { +} + +MaterialType::MaterialType() + : MaterialType(new MaterialType::Impl()){}; + +MaterialType::~MaterialType() = default; + +MaterialType::Impl& MaterialType::impl() const { + return *m_pImpl; +} diff --git a/src/rendergraph/scenegraph/rendergraph/materialtype.h b/src/rendergraph/scenegraph/rendergraph/materialtype.h new file mode 100644 index 000000000000..06d85f131cf3 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/materialtype.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace rendergraph { +class MaterialType; +} + +class rendergraph::MaterialType { + public: + class Impl; + + MaterialType(); + ~MaterialType(); + Impl& impl() const; + + private: + MaterialType(Impl* pImpl); + + const std::unique_ptr m_pImpl; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/node.cpp b/src/rendergraph/scenegraph/rendergraph/node.cpp new file mode 100644 index 000000000000..bd2ec2eed3e7 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/node.cpp @@ -0,0 +1,35 @@ +#include "rendergraph/node.h" + +#include "node_impl.h" + +using namespace rendergraph; + +Node::Node(NodeImplBase* pImpl) + : m_pImpl(pImpl) { +} + +Node::Node() + : Node(new Node::Impl(this)) { +} + +Node::~Node() = default; + +NodeImplBase& Node::impl() const { + return *m_pImpl; +} + +void Node::setUsePreprocess(bool value) { + impl().setUsePreprocess(value); +} + +void Node::onAppendChildNode(Node* pChild) { + impl().onAppendChildNode(pChild); +} + +void Node::onRemoveChildNode(Node* pChild) { + impl().onRemoveChildNode(pChild); +} + +void Node::onRemoveAllChildNodes() { + impl().onRemoveAllChildNodes(); +} diff --git a/src/rendergraph/scenegraph/rendergraph/node.h b/src/rendergraph/scenegraph/rendergraph/node.h new file mode 100644 index 000000000000..cd8b86467676 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/node.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include + +namespace rendergraph { +class Node; +class NodeImplBase; +} // namespace rendergraph + +class rendergraph::Node { + public: + class Impl; + + Node(); + + virtual ~Node(); + + void appendChildNode(std::unique_ptr&& pChild) { + auto pChildRawPtr = pChild.get(); + if (m_pLastChild) { + pChild->m_pPreviousSibling = m_pLastChild; + m_pLastChild->m_pNextSibling = std::move(pChild); + } else { + m_pFirstChild = std::move(pChild); + } + m_pLastChild = pChildRawPtr; + m_pLastChild->m_pParent = this; + onAppendChildNode(m_pLastChild); + } + std::unique_ptr removeAllChildNodes() { + onRemoveAllChildNodes(); + m_pLastChild = nullptr; + Node* pChild = m_pFirstChild.get(); + while (pChild) { + pChild->m_pParent = nullptr; + pChild = pChild->m_pNextSibling.get(); + } + return std::move(m_pFirstChild); + } + std::unique_ptr removeChildNode(Node* pChild) { + std::unique_ptr pRemoved; + if (pChild == m_pFirstChild.get()) { + pRemoved = std::move(m_pFirstChild); + m_pFirstChild = std::move(pChild->m_pNextSibling); + } else { + pRemoved = std::move(pChild->m_pPreviousSibling->m_pNextSibling); + pChild->m_pPreviousSibling->m_pNextSibling = std::move(pChild->m_pNextSibling); + pChild->m_pPreviousSibling = nullptr; + } + if (pChild == m_pLastChild) { + m_pLastChild = nullptr; + } + pChild->m_pParent = nullptr; + return pRemoved; + } + Node* parent() const { + return m_pParent; + } + Node* firstChild() const { + return m_pFirstChild.get(); + } + Node* lastChild() const { + return m_pLastChild; + } + Node* nextSibling() const { + return m_pNextSibling.get(); + } + Node* previousSibling() const { + return m_pPreviousSibling; + } + NodeImplBase& impl() const; + + virtual bool isSubtreeBlocked() const { + return false; + } + + virtual void preprocess() { + } + + void setUsePreprocess(bool value); + + protected: + Node(NodeImplBase* impl); + + private: + const std::unique_ptr m_pImpl; + Node* m_pParent{}; + std::unique_ptr m_pFirstChild; + Node* m_pLastChild{}; + std::unique_ptr m_pNextSibling; + Node* m_pPreviousSibling{}; + + void onAppendChildNode(Node* pChild); + void onRemoveChildNode(Node* pChild); + void onRemoveAllChildNodes(); +}; diff --git a/src/rendergraph/scenegraph/rendergraph/opacitynode.cpp b/src/rendergraph/scenegraph/rendergraph/opacitynode.cpp new file mode 100644 index 000000000000..9be655c4ee4e --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/opacitynode.cpp @@ -0,0 +1,19 @@ +#include "rendergraph/opacitynode.h" + +#include "opacitynode_impl.h" + +using namespace rendergraph; + +OpacityNode::OpacityNode(NodeImplBase* pImpl) + : Node(pImpl) { +} + +OpacityNode::OpacityNode() + : OpacityNode(new OpacityNode::Impl(this)) { +} + +OpacityNode::~OpacityNode() = default; + +void OpacityNode::setOpacity(float opacity) { + static_cast(impl()).setOpacity(opacity); +} diff --git a/src/rendergraph/scenegraph/rendergraph/opacitynode.h b/src/rendergraph/scenegraph/rendergraph/opacitynode.h new file mode 100644 index 000000000000..a95bc3a58f47 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/opacitynode.h @@ -0,0 +1,19 @@ +#pragma once + +#include "rendergraph/node.h" + +namespace rendergraph { +class OpacityNode; +} // namespace rendergraph + +class rendergraph::OpacityNode : public rendergraph::Node { + public: + class Impl; + + OpacityNode(); + ~OpacityNode(); + void setOpacity(float opacity); + + private: + OpacityNode(NodeImplBase* pImpl); +}; diff --git a/src/rendergraph/scenegraph/rendergraph/openglnode.cpp b/src/rendergraph/scenegraph/rendergraph/openglnode.cpp new file mode 100644 index 000000000000..5196c013824b --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/openglnode.cpp @@ -0,0 +1,22 @@ +#include "rendergraph/openglnode.h" + +using namespace rendergraph; + +OpenGLNode::OpenGLNode() = default; +OpenGLNode::~OpenGLNode() = default; + +// void OpenGLNode::initialize() { +// initializeOpenGLFunctions(); +// initializeGL(); +// Node::initialize(); +// } + +// void OpenGLNode::render() { +// paintGL(); +// Node::render(); +// } + +// void OpenGLNode::resize(int w, int h) { +// resizeGL(w, h); +// Node::resize(w, h); +// } diff --git a/src/rendergraph/scenegraph/rendergraph/openglnode.h b/src/rendergraph/scenegraph/rendergraph/openglnode.h new file mode 100644 index 000000000000..07481779beb4 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/openglnode.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "rendergraph/node.h" + +namespace rendergraph { +class OpenGLNode; +} // namespace rendergraph + +class rendergraph::OpenGLNode : public rendergraph::Node, public QOpenGLFunctions { + public: + OpenGLNode(); + ~OpenGLNode(); + + virtual void initializeGL() { + } + virtual void paintGL() { + } + virtual void resizeGL(int, int) { + } + // void initialize() override; + // void render() override; + // void resize(int w, int h) override; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/scenegraph.cpp b/src/rendergraph/scenegraph/rendergraph/scenegraph.cpp new file mode 100644 index 000000000000..efb0f63a0f8f --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/scenegraph.cpp @@ -0,0 +1,16 @@ +#include "scenegraph.h" + +#include "context_impl.h" +#include "node_impl.h" + +using namespace rendergraph; + +QSGNode* rendergraph::sgNode(Node* pNode) { + return pNode->impl().sgNode(); +} + +std::unique_ptr rendergraph::createSgContext(QQuickWindow* window) { + auto context = std::make_unique(); + context->impl().setWindow(window); + return context; +} diff --git a/src/rendergraph/scenegraph/rendergraph/scenegraph.h b/src/rendergraph/scenegraph/rendergraph/scenegraph.h new file mode 100644 index 000000000000..3924cf35119b --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/scenegraph.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include + +namespace rendergraph { +class Context; +class Node; + +std::unique_ptr createSgContext(QQuickWindow* window); +QSGNode* sgNode(Node* pNode); +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/rendergraph/texture.cpp b/src/rendergraph/scenegraph/rendergraph/texture.cpp new file mode 100644 index 000000000000..3d16c12189e2 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/texture.cpp @@ -0,0 +1,19 @@ +#include "rendergraph/texture.h" + +#include "texture_impl.h" + +using namespace rendergraph; + +Texture::Texture(Impl* pImpl) + : m_pImpl(pImpl) { +} + +Texture::Texture(Context& context, const QImage& image) + : Texture(new Texture::Impl(context, image)) { +} + +Texture::~Texture() = default; + +Texture::Impl& Texture::impl() const { + return *m_pImpl; +} diff --git a/src/rendergraph/scenegraph/rendergraph/texture.h b/src/rendergraph/scenegraph/rendergraph/texture.h new file mode 100644 index 000000000000..8069a8192bbf --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/texture.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace rendergraph { +class Context; +class Texture; +} // namespace rendergraph + +class rendergraph::Texture { + public: + class Impl; + + Texture(Context& context, const QImage& image); + ~Texture(); + Impl& impl() const; + + private: + Texture(Impl* pImpl); + + const std::unique_ptr m_pImpl; +}; diff --git a/src/rendergraph/scenegraph/rendergraph/types.cpp b/src/rendergraph/scenegraph/rendergraph/types.cpp new file mode 100644 index 000000000000..654fa6567e1b --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/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(GLuint); + case PrimitiveType::Float: + return sizeof(GLfloat); + } + return 0; +} + +int rendergraph::sizeOf(Type type) { + switch (type) { + case Type::UInt: + return sizeof(GLuint); + case Type::Float: + return sizeof(GLfloat); + case Type::Vector2D: + return sizeof(GLfloat) * 2; + case Type::Vector3D: + return sizeof(GLfloat) * 3; + case Type::Vector4D: + return sizeof(GLfloat) * 4; + case Type::Matrix4x4: + return sizeof(GLfloat) * 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/scenegraph/rendergraph/types.h b/src/rendergraph/scenegraph/rendergraph/types.h new file mode 100644 index 000000000000..768738964fd6 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/types.h @@ -0,0 +1,31 @@ +#pragma once + +namespace rendergraph { + +enum class PrimitiveType { + UInt, + Float, +}; + +enum class Type { + UInt, + Float, + Vector2D, + Vector3D, + Vector4D, + Matrix4x4 +}; + +int sizeOf(Type type); +int sizeOf(PrimitiveType primitiveType); + +template +Type typeOf(); + +template +PrimitiveType primitiveTypeOf(); + +template +int tupleSizeOf(); + +} // namespace rendergraph diff --git a/src/rendergraph/scenegraph/rendergraph/uniform.h b/src/rendergraph/scenegraph/rendergraph/uniform.h new file mode 100644 index 000000000000..19a6a9c74a35 --- /dev/null +++ b/src/rendergraph/scenegraph/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/scenegraph/rendergraph/uniformscache.cpp b/src/rendergraph/scenegraph/rendergraph/uniformscache.cpp new file mode 100644 index 000000000000..3e43b60aab67 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/uniformscache.cpp @@ -0,0 +1,28 @@ +#include "rendergraph/uniformscache.h" + +#include "rendergraph/uniformset.h" + +using namespace rendergraph; + +UniformsCache::UniformsCache(const UniformSet& uniformSet) { + int offset = 0; + 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, Type type, const void* ptr, int size) { + assert(type == m_infos[uniformIndex].m_type); + memcpy(m_byteArray.data() + m_infos[uniformIndex].m_offset, ptr, size); +} + +void UniformsCache::get(int uniformIndex, Type type, void* ptr, int size) const { + assert(type == m_infos[uniformIndex].m_type); + memcpy(ptr, m_byteArray.data() + m_infos[uniformIndex].m_offset, size); +} diff --git a/src/rendergraph/scenegraph/rendergraph/uniformscache.h b/src/rendergraph/scenegraph/rendergraph/uniformscache.h new file mode 100644 index 000000000000..5787570effd2 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/uniformscache.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include + +#include "rendergraph/types.h" + +namespace rendergraph { +class UniformSet; +class UniformsCache; +} // namespace rendergraph + +class rendergraph::UniformsCache { + public: + UniformsCache(const UniformSet& uniformSet); + ~UniformsCache(); + + template + void set(int uniformIndex, const T& value) { + set(uniformIndex, typeOf(), static_cast(&value), sizeOf(typeOf())); + } + + template + T get(int uniformIndex) const { + T value; + get(uniformIndex, typeOf(), 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, Type type, const void* ptr, int size); + void get(int uniformIndex, Type type, 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) { + set(uniformIndex, typeOf(), matrix.constData(), sizeOf(typeOf())); +} diff --git a/src/rendergraph/scenegraph/rendergraph/uniformset.cpp b/src/rendergraph/scenegraph/rendergraph/uniformset.cpp new file mode 100644 index 000000000000..e463c7050fa5 --- /dev/null +++ b/src/rendergraph/scenegraph/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 (const 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/scenegraph/rendergraph/uniformset.h b/src/rendergraph/scenegraph/rendergraph/uniformset.h new file mode 100644 index 000000000000..afa1cfc24265 --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/uniformset.h @@ -0,0 +1,30 @@ +#pragma once + +#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/scenegraph/texture_impl.cpp b/src/rendergraph/scenegraph/texture_impl.cpp new file mode 100644 index 000000000000..1416d4312e4e --- /dev/null +++ b/src/rendergraph/scenegraph/texture_impl.cpp @@ -0,0 +1,9 @@ +#include "texture_impl.h" + +#include "context_impl.h" + +using namespace rendergraph; + +Texture::Impl::Impl(Context& context, const QImage& image) + : m_pTexture(context.impl().window()->createTextureFromImage(image)) { +} diff --git a/src/rendergraph/trash b/src/rendergraph/trash new file mode 100644 index 000000000000..55a577b26d01 --- /dev/null +++ b/src/rendergraph/trash @@ -0,0 +1,25 @@ +#pragma once + +#pragma once + +#include +#include +#include +#include + +namespace rendergraph { +struct Attribute; +class AttributeSet; +struct Uniform; +class UniformSet; +class UniformsCache; +class MaterialType; +class MaterialShader; +class Texture; +class Material; +class Geometry; +class Node; +class GeometryNode; +class RenderGraph; +class Context; +} diff --git a/src/waveform/isynctimeprovider.h b/src/waveform/isynctimeprovider.h new file mode 100644 index 000000000000..9432249702bd --- /dev/null +++ b/src/waveform/isynctimeprovider.h @@ -0,0 +1,9 @@ +#pragma once + +#include "util/performancetimer.h" + +class ISyncTimeProvider { + public: + virtual int fromTimerToNextSyncMicros(const PerformanceTimer& timer) = 0; + virtual int getSyncIntervalTimeMicros() const = 0; +}; diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 2c6d2f7d1479..c4c2a77d3491 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -5,17 +5,21 @@ #include #include #include -#include #include #include #include #include "./util/assert.h" +#include "rendergraph/context.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/texturematerial.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/vertexdata.h" +#include "waveform/renderers/allshader/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,18 +60,19 @@ static_assert(checkCharToIndex()); } // namespace -allshader::DigitsRenderer::~DigitsRenderer() = default; - -void allshader::DigitsRenderer::init() { - initializeOpenGLFunctions(); - m_shader.init(); +allshader::DigitsRenderNode::DigitsRenderNode() { + setGeometry(std::make_unique(TextureMaterial::attributes(), 0)); + setMaterial(std::make_unique()); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); } -float allshader::DigitsRenderer::height() const { +allshader::DigitsRenderNode::~DigitsRenderNode() = default; + +float allshader::DigitsRenderNode::height() const { return m_height; } -void allshader::DigitsRenderer::updateTexture( +void allshader::DigitsRenderNode::updateTexture( float fontPointSize, float maxHeight, float devicePixelRatio) { if (fontPointSize == m_fontPointSize && maxHeight == m_maxHeight) { return; @@ -202,64 +207,69 @@ void allshader::DigitsRenderer::updateTexture( m_offset[NUM_CHARS] = 1.f; } - m_texture.setData(image); + Context context; + dynamic_cast(material()) + .setTexture(std::make_unique(context, image)); } -float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, +void allshader::DigitsRenderNode::update(const QMatrix4x4& matrix, + float x, + float y, + bool multiLine, + const QString& s1, + const QString& s2) { + const int numVerticesPerRectangle = 6; + const int reserved = (s1.length() + s2.length()) * numVerticesPerRectangle; + geometry().allocate(reserved); + TexturedVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + + const float ch = height(); + if (!s1.isEmpty()) { + const auto w = addVertices(vertexUpdater, + x, + y, + s1); + if (multiLine) { + y += ch; + } else { + x += w + ch * 0.75f; + } + } + if (!s2.isEmpty()) { + addVertices(vertexUpdater, + x, + y, + s2); + } + + DEBUG_ASSERT(reserved == vertexUpdater.index()); + + material().setUniform(0, matrix); +} + +void allshader::DigitsRenderNode::clear() { + geometry().allocate(0); +} + +float allshader::DigitsRenderNode::addVertices(TexturedVertexUpdater& vertexUpdater, float x, float y, const QString& s) { - const int n = s.length(); const float x0 = x; const float space = static_cast(m_penWidth) / 2; - VertexData posVertices; - VertexData texVertices; - - posVertices.reserve(n * 6); // two triangles per character - texVertices.reserve(n * 6); - for (QChar c : s) { if (x != x0) { x -= space; } int index = charToIndex(c); - texVertices.addRectangle(m_offset[index], 0.f, m_offset[index + 1], 1.f); - posVertices.addRectangle(x, - y, - x + m_width[index], - y + height()); + vertexUpdater.addRectangle({x, y}, + {x + m_width[index], y + height()}, + {m_offset[index], 0.f}, + {m_offset[index + 1], 1.f}); x += m_width[index]; } - m_shader.bind(); - - const int matrixLocation = m_shader.uniformLocation("matrix"); - const int textureLocation = m_shader.uniformLocation("texture"); - const int positionLocation = m_shader.attributeLocation("position"); - const int texcoordLocation = m_shader.attributeLocation("texcoord"); - - m_shader.setUniformValue(matrixLocation, matrix); - - m_shader.enableAttributeArray(positionLocation); - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, posVertices.constData(), 2); - m_shader.enableAttributeArray(texcoordLocation); - m_shader.setAttributeArray( - texcoordLocation, GL_FLOAT, texVertices.constData(), 2); - - m_shader.setUniformValue(textureLocation, 0); - - m_texture.bind(); - - glDrawArrays(GL_TRIANGLES, 0, posVertices.size()); - - m_texture.release(); - - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(texcoordLocation); - m_shader.release(); - return x - x0; } diff --git a/src/waveform/renderers/allshader/digitsrenderer.h b/src/waveform/renderers/allshader/digitsrenderer.h index 4d280bbdaad2..b55c72b884be 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.h +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -1,30 +1,42 @@ #pragma once -#include +#include -#include "shaders/textureshader.h" -#include "util/opengltexture2d.h" +#include "rendergraph/geometrynode.h" +#include "util/class.h" + +namespace rendergraph { +class TexturedVertexUpdater; +} namespace allshader { -class DigitsRenderer; +class DigitsRenderNode; } -class allshader::DigitsRenderer : public QOpenGLFunctions { +class allshader::DigitsRenderNode : public rendergraph::GeometryNode { public: - DigitsRenderer() = default; - ~DigitsRenderer(); + DigitsRenderNode(); + ~DigitsRenderNode(); - void init(); void updateTexture(float fontPointSize, float maxHeight, float devicePixelRatio); - float draw(const QMatrix4x4& matrix, + + void update(const QMatrix4x4& matrix, float x, float y, - const QString& s); + bool multiLine, + const QString& s1, + const QString& s2); + + void clear(); + float height() const; private: - mixxx::TextureShader m_shader; - OpenGLTexture2D m_texture; + float addVertices(rendergraph::TexturedVertexUpdater& vertexUpdater, + float x, + float y, + const QString& s); + int m_penWidth; float m_offset[13]; float m_width[12]; @@ -32,5 +44,5 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { float m_height{}; float m_maxHeight{}; float m_adjustedFontPointSize{}; - DISALLOW_COPY_AND_ASSIGN(DigitsRenderer); + DISALLOW_COPY_AND_ASSIGN(DigitsRenderNode); }; diff --git a/src/waveform/renderers/allshader/rgbavertexupdater.h b/src/waveform/renderers/allshader/rgbavertexupdater.h new file mode 100644 index 000000000000..2f2a46dc2ebe --- /dev/null +++ b/src/waveform/renderers/allshader/rgbavertexupdater.h @@ -0,0 +1,137 @@ +#pragma once + +#include "rendergraph/geometry.h" + +namespace rendergraph { +class RGBAVertexUpdater; +} + +class rendergraph::RGBAVertexUpdater { + public: + RGBAVertexUpdater(Geometry::RGBAColoredPoint2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + + void addRectangle( + QVector2D lt, + QVector2D rb, + QVector4D rgba) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), rgba.x(), rgba.y(), rgba.z(), rgba.w()); + } + void addRectangleVGradient( + QVector2D lt, + QVector2D rb, + QVector4D rgbat, + QVector4D rgbab) { + addRectangleVGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbat.x(), + rgbat.y(), + rgbat.z(), + rgbat.w(), + rgbab.x(), + rgbab.y(), + rgbab.z(), + rgbab.w()); + } + void addRectangleHGradient( + QVector2D lt, + QVector2D rb, + QVector4D rgbal, + QVector4D rgbar) { + addRectangleHGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbal.x(), + rgbal.y(), + rgbal.z(), + rgbal.w(), + rgbar.x(), + rgbar.y(), + rgbar.z(), + rgbar.w()); + } + void addRectangle(float x1, float y1, float x2, float y2, float r, float g, float b, float a) { + addTriangle(x1, y1, x2, y1, x1, y2, r, g, b, a); + addTriangle(x1, y2, x2, y2, x2, y1, r, g, b, a); + } + void addRectangleVGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float a1, + float r2, + float g2, + float b2, + float a2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, a1, r1, g1, b1, a1, r2, g2, b2, a2); + addTriangle(x1, y2, x2, y2, x2, y1, r2, g2, b2, a2, r2, g2, b2, a2, r1, g1, b1, a1); + } + void addRectangleHGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float a1, + float r2, + float g2, + float b2, + float a2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, a1, r2, g2, b2, a2, r1, g1, b1, a1); + addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, a1, r2, g2, b2, a2, r2, g2, b2, a2); + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r, + float g, + float b, + float a) { + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x1, y1, r, g, b, a}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x2, y2, r, g, b, a}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x3, y3, r, g, b, a}; + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r1, + float g1, + float b1, + float a1, + float r2, + float g2, + float b2, + float a2, + float r3, + float g3, + float b3, + float a3) { + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x1, y1, r1, g1, b1, a1}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x2, y2, r2, g2, b2, a2}; + *m_pWrite++ = Geometry::RGBAColoredPoint2D{x3, y3, r3, g3, b3, a3}; + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + Geometry::RGBAColoredPoint2D* const m_pData; + Geometry::RGBAColoredPoint2D* m_pWrite; +}; diff --git a/src/waveform/renderers/allshader/rgbvertexupdater.h b/src/waveform/renderers/allshader/rgbvertexupdater.h new file mode 100644 index 000000000000..580883cb6e16 --- /dev/null +++ b/src/waveform/renderers/allshader/rgbvertexupdater.h @@ -0,0 +1,129 @@ +#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, + QVector4D rgb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), rgb.x(), rgb.y(), rgb.z(), rgb.w()); + } + void addRectangleVGradient( + QVector2D lt, + QVector2D rb, + QVector4D rgbt, + QVector4D 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, + QVector4D rgbl, + QVector4D rgbr) { + addRectangleHGradient(lt.x(), + lt.y(), + rb.x(), + rb.y(), + rgbl.x(), + rgbl.y(), + rgbl.z(), + rgbr.x(), + rgbr.y(), + rgbr.z()); + } + void addRectangle(float x1, float y1, float x2, float y2, float r, float g, float b) { + addTriangle(x1, y1, x2, y1, x1, y2, r, g, b, 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 r2, + float g2, + float b2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, r2, g2, b2, r1, g1, b1); + addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, r2, g2, b2, r2, g2, b2); + } + void addRectangleHGradient( + float x1, + float y1, + float x2, + float y2, + float r1, + float g1, + float b1, + float r2, + float g2, + floar b2) { + addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, r2, g2, b2, r1, g1, b1); + addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, r2, g2, b2, r2, g2, b2); + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r, + float g, + float b) { + *m_pWrite++ = Geometry::RGBColoredPoint2D{x1, y1, r, g, b}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{x2, y2, r, g, b}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{x3, y3, r, g, b}; + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float r1, + float g1, + float b1, + + float r2, + float g2, + float b2, + + float r3, + float g3, + float b3, + + ) { + *m_pWrite++ = Geometry::RGBColoredPoint2D{x1, y1, r1, g1, b1}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{x2, y2, r2, g2, b2}; + *m_pWrite++ = Geometry::RGBColoredPoint2D{x3, y3, r3, g3, b3}; + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + Geometry::RGBColoredPoint2D* const m_pData; + Geometry::RGBColoredPoint2D* m_pWrite; +}; diff --git a/src/waveform/renderers/allshader/texturedvertexupdater.h b/src/waveform/renderers/allshader/texturedvertexupdater.h new file mode 100644 index 000000000000..e754fb509a0c --- /dev/null +++ b/src/waveform/renderers/allshader/texturedvertexupdater.h @@ -0,0 +1,47 @@ +#pragma once + +#include "rendergraph/geometry.h" + +namespace rendergraph { +class TexturedVertexUpdater; +} + +class rendergraph::TexturedVertexUpdater { + public: + TexturedVertexUpdater(Geometry::TexturedPoint2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + void addRectangle( + QVector2D lt, QVector2D rb, QVector2D tlr, QVector2D trb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), tlr.x(), tlr.y(), trb.x(), trb.y()); + } + void addRectangle( + float x1, float y1, float x2, float y2, float tx1, float ty1, float tx2, float ty2) { + addTriangle(x1, y1, x2, y1, x1, y2, tx1, ty1, tx2, ty1, tx1, ty2); + addTriangle(x1, y2, x2, y2, x2, y1, tx1, ty2, tx2, ty2, tx2, ty1); + } + void addTriangle(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float tx1, + float ty1, + float tx2, + float ty2, + float tx3, + float ty3) { + *m_pWrite++ = Geometry::TexturedPoint2D{x1, y1, tx1, ty1}; + *m_pWrite++ = Geometry::TexturedPoint2D{x2, y2, tx2, ty2}; + *m_pWrite++ = Geometry::TexturedPoint2D{x3, y3, tx3, ty3}; + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + Geometry::TexturedPoint2D* const m_pData; + Geometry::TexturedPoint2D* m_pWrite; +}; diff --git a/src/waveform/renderers/allshader/vertexupdater.h b/src/waveform/renderers/allshader/vertexupdater.h new file mode 100644 index 000000000000..d98d40f8c195 --- /dev/null +++ b/src/waveform/renderers/allshader/vertexupdater.h @@ -0,0 +1,37 @@ +#pragma once + +namespace rendergraph { +class VertexUpdater; +} + +class rendergraph::VertexUpdater { + public: + VertexUpdater(Geometry::Point2D* pData) + : m_pData(pData), + m_pWrite(pData) { + } + void addRectangle( + QVector2D lt, QVector2D rb) { + addRectangle(lt.x(), lt.y(), rb.x(), rb.y()); + } + void addRectangle( + float x1, + float y1, + float x2, + float y2) { + addTriangle(x1, y1, x2, y1, x1, y2); + addTriangle(x1, y2, x2, y2, x2, y1); + } + void addTriangle(float x1, float y1, float x2, float y2, float x3, float y3) { + *m_pWrite++ = Geometry::Point2D{x1, y1}; + *m_pWrite++ = Geometry::Point2D{x2, y2}; + *m_pWrite++ = Geometry::Point2D{x3, y3}; + } + int index() const { + return static_cast(m_pWrite - m_pData); + } + + private: + Geometry::Point2D* const m_pData; + Geometry::Point2D* m_pWrite; +}; diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.cpp b/src/waveform/renderers/allshader/waveformrenderbeat.cpp index 3f793efd706b..0dc552e6cfb1 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.cpp +++ b/src/waveform/renderers/allshader/waveformrenderbeat.cpp @@ -2,23 +2,25 @@ #include +#include "rendergraph/geometry.h" +#include "rendergraph/material/unicolormaterial.h" #include "skin/legacy/skincontext.h" #include "track/track.h" +#include "vertexupdater.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "widget/wskincolor.h" +using namespace rendergraph; + namespace allshader { WaveformRenderBeat::WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type) - : WaveformRenderer(waveformWidget), + : ::WaveformRendererAbstract(waveformWidget), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { -} - -void WaveformRenderBeat::initializeGL() { - WaveformRenderer::initializeGL(); - m_shader.init(); + initForRectangles(0); + setUsePreprocess(true); } void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& context) { @@ -26,11 +28,23 @@ void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& context) m_color = WSkinColor::getCorrectColor(m_color).toRgb(); } -void WaveformRenderBeat::paintGL() { - TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); +void WaveformRenderBeat::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); +} + +void WaveformRenderBeat::preprocess() { + if (!preprocessInner()) { + geometry().allocate(0); + } +} + +bool WaveformRenderBeat::preprocessInner() { + const TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); if (!trackInfo || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { - return; + return false; } auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip @@ -38,24 +52,21 @@ void WaveformRenderBeat::paintGL() { mixxx::BeatsPointer trackBeats = trackInfo->getBeats(); if (!trackBeats) { - return; + return false; } int alpha = m_waveformRenderer->getBeatGridAlpha(); if (alpha == 0) { - return; + return false; } const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - m_color.setAlphaF(alpha / 100.0f); const double trackSamples = m_waveformRenderer->getTrackSamples(); - if (trackSamples <= 0) { - return; + if (trackSamples <= 0.0) { + return false; } const double firstDisplayedPosition = @@ -69,7 +80,7 @@ void WaveformRenderBeat::paintGL() { lastDisplayedPosition * trackSamples); if (!startPosition.isValid() || !endPosition.isValid()) { - return; + return false; } const float rendererBreadth = m_waveformRenderer->getBreadth(); @@ -88,8 +99,10 @@ void WaveformRenderBeat::paintGL() { } const int reserved = numBeatsInRange * numVerticesPerLine; - m_vertices.clear(); - m_vertices.reserve(reserved); + geometry().allocate(reserved); + // TODO set dirty for scenegraph + + VertexUpdater vertexUpdater{geometry().vertexDataAs()}; for (auto it = trackBeats->iteratorFrom(startPosition); it != trackBeats->cend() && *it <= endPosition; @@ -104,33 +117,18 @@ void WaveformRenderBeat::paintGL() { const float x1 = static_cast(xBeatPoint); const float x2 = x1 + 1.f; - m_vertices.addRectangle(x1, - 0.f, - x2, - m_isSlipRenderer ? rendererBreadth / 2 : rendererBreadth); + vertexUpdater.addRectangle({x1, 0.f}, + {x2, m_isSlipRenderer ? rendererBreadth / 2 : rendererBreadth}); } - DEBUG_ASSERT(reserved == m_vertices.size()); - - const int positionLocation = m_shader.positionLocation(); - const int matrixLocation = m_shader.matrixLocation(); - const int colorLocation = m_shader.colorLocation(); - - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); + DEBUG_ASSERT(reserved == vertexUpdater.index()); const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, m_vertices.constData(), 2); - - m_shader.setUniformValue(matrixLocation, matrix); - m_shader.setUniformValue(colorLocation, m_color); - - glDrawArrays(GL_TRIANGLES, 0, m_vertices.size()); + material().setUniform(0, matrix); + material().setUniform(1, m_color); - m_shader.disableAttributeArray(positionLocation); - m_shader.release(); + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.h b/src/waveform/renderers/allshader/waveformrenderbeat.h index d3b2b79d1bce..c0c13126dc25 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.h +++ b/src/waveform/renderers/allshader/waveformrenderbeat.h @@ -1,11 +1,11 @@ #pragma once #include +#include -#include "shaders/unicolorshader.h" +#include "rendergraph/geometrynode.h" #include "util/class.h" -#include "waveform/renderers/allshader/vertexdata.h" -#include "waveform/renderers/allshader/waveformrenderer.h" +#include "waveform/renderers/waveformrendererabstract.h" class QDomNode; class SkinContext; @@ -14,22 +14,27 @@ namespace allshader { class WaveformRenderBeat; } -class allshader::WaveformRenderBeat final : public allshader::WaveformRenderer { +class allshader::WaveformRenderBeat final + : public ::WaveformRendererAbstract, + public rendergraph::GeometryNode { public: explicit WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& context) override; - void paintGL() override; - void initializeGL() override; + + // Virtuals for rendergraph::Node + void preprocess() override; private: - mixxx::UnicolorShader m_shader; QColor m_color; - VertexData m_vertices; - bool m_isSlipRenderer; + bool preprocessInner(); + DISALLOW_COPY_AND_ASSIGN(WaveformRenderBeat); }; diff --git a/src/waveform/renderers/allshader/waveformrenderer.h b/src/waveform/renderers/allshader/waveformrenderer.h index 04b4b4279356..32dbc036b190 100644 --- a/src/waveform/renderers/allshader/waveformrenderer.h +++ b/src/waveform/renderers/allshader/waveformrenderer.h @@ -1,6 +1,6 @@ #pragma once -#include "waveform/renderers/allshader/waveformrendererabstract.h" +#include "rendergraph/openglnode.h" #include "waveform/renderers/waveformrendererabstract.h" class WaveformWidgetRenderer; @@ -10,27 +10,15 @@ class WaveformRenderer; } class allshader::WaveformRenderer : public ::WaveformRendererAbstract, - public allshader::WaveformRendererAbstract { + public rendergraph::OpenGLNode { public: explicit WaveformRenderer(WaveformWidgetRenderer* widget); - // Pure virtual from allshader::WaveformRendererAbstract. + // Pure virtual from WaveformRendererAbstract // Renderers that use QPainter functionality implement this. // But as all classes derived from allshader::WaveformRenderer // will only use openGL functions (combining QPainter and // QOpenGLWindow has bad performance), we leave this empty. // Should never be called. void draw(QPainter* painter, QPaintEvent* event) override final; - - allshader::WaveformRendererAbstract* allshaderWaveformRenderer() override final { - // This class is indirectly derived from - // WaveformWidgetRenderer, which has a member - // QList m_rendererStack; - // In the case of allshader::WaveformRenderer widgets, - // all the items on this stack are derived from - // allshader::WaveformRendererAbstract and we use this method to - // access them as such. (We could also have used a - // dynamic cast (or even static cast instead) - return this; - } }; diff --git a/src/waveform/renderers/allshader/waveformrendererabstract.h b/src/waveform/renderers/allshader/waveformrendererabstract.h deleted file mode 100644 index 5c530a00e225..000000000000 --- a/src/waveform/renderers/allshader/waveformrendererabstract.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -namespace allshader { -class WaveformRendererAbstract; -} - -class allshader::WaveformRendererAbstract : public QOpenGLFunctions { - public: - /// This interface class is called by class allshader::WaveformWidget. - /// Class allshader::WaveformWidget is derived from the allshader-based - /// class WGLWidget and, in its implementation of the WGLWidget virtual - /// methods, calls a stack of allshader::WaveformRendererAbstract instances, for - /// the different layers of the waveform graphics (background, beat - /// markers, the actual waveform, etc). In other word, this interface - /// mimics the WGLWidget virtuals, but to be called as layers of an - /// actual WGLWidget. - virtual ~WaveformRendererAbstract() = default; - virtual void initializeGL() { - initializeOpenGLFunctions(); - } - virtual void resizeGL(int /* w */, int /* h */) { - } - virtual void paintGL() = 0; -}; diff --git a/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp b/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp index 10df71210207..6b5b5f12f1cf 100644 --- a/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp +++ b/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp @@ -1,9 +1,12 @@ #include "waveform/renderers/allshader/waveformrendererendoftrack.h" #include +#include #include #include "control/controlproxy.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/endoftrackmaterial.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveformwidgetfactory.h" #include "widget/wskincolor.h" @@ -11,19 +14,31 @@ namespace { constexpr int kBlinkingPeriodMillis = 1000; -constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; -constexpr float verticalGradientArray[] = {1.f, 1.f, -1.f, -1.f}; -constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; } // anonymous namespace +using namespace rendergraph; + namespace allshader { WaveformRendererEndOfTrack::WaveformRendererEndOfTrack( WaveformWidgetRenderer* waveformWidget) - : WaveformRenderer(waveformWidget), + : ::WaveformRendererAbstract(waveformWidget), m_pEndOfTrackControl(nullptr), m_pTimeRemainingControl(nullptr) { + setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4)); + setMaterial(std::make_unique()); + setUsePreprocess(true); + + geometry().setAttributeValues(0, positionArray, 4); + geometry().setAttributeValues(1, horizontalGradientArray, 4); + material().setUniform(0, QVector4D{0.f, 0.f, 0.f, 0.f}); +} + +void WaveformRendererEndOfTrack::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } bool WaveformRendererEndOfTrack::init() { @@ -46,43 +61,7 @@ void WaveformRendererEndOfTrack::setup(const QDomNode& node, const SkinContext& } } -void WaveformRendererEndOfTrack::initializeGL() { - WaveformRenderer::initializeGL(); - m_shader.init(); -} - -void WaveformRendererEndOfTrack::fillWithGradient(QColor color) { - const int colorLocation = m_shader.colorLocation(); - const int positionLocation = m_shader.positionLocation(); - const int gradientLocation = m_shader.gradientLocation(); - - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); - m_shader.enableAttributeArray(gradientLocation); - - m_shader.setUniformValue(colorLocation, color); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, positionArray, 2); - m_shader.setAttributeArray(gradientLocation, - GL_FLOAT, - m_waveformRenderer->getOrientation() == Qt::Vertical - ? verticalGradientArray - : horizontalGradientArray, - 1); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(gradientLocation); - m_shader.release(); -} - -void WaveformRendererEndOfTrack::paintGL() { - if (!m_pEndOfTrackControl->toBool()) { - return; - } - +void WaveformRendererEndOfTrack::preprocess() { const int elapsed = m_timer.elapsed().toIntegerMillis() % kBlinkingPeriodMillis; const double blinkIntensity = (double)(2 * abs(elapsed - kBlinkingPeriodMillis / 2)) / @@ -100,11 +79,12 @@ void WaveformRendererEndOfTrack::paintGL() { QColor color = m_color; color.setAlphaF(static_cast(alpha)); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - fillWithGradient(color); + material().setUniform(0, color); } } +bool WaveformRendererEndOfTrack::isSubtreeBlocked() const { + return !m_pEndOfTrackControl->toBool(); +} + } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererendoftrack.h b/src/waveform/renderers/allshader/waveformrendererendoftrack.h index 664a2cac16e5..9b7ec182de5f 100644 --- a/src/waveform/renderers/allshader/waveformrendererendoftrack.h +++ b/src/waveform/renderers/allshader/waveformrendererendoftrack.h @@ -3,10 +3,11 @@ #include #include -#include "shaders/endoftrackshader.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/opacitynode.h" #include "util/class.h" #include "util/performancetimer.h" -#include "waveform/renderers/allshader/waveformrenderer.h" +#include "waveform/renderers/waveformrendererabstract.h" class ControlProxy; class QDomNode; @@ -16,22 +17,29 @@ namespace allshader { class WaveformRendererEndOfTrack; } -class allshader::WaveformRendererEndOfTrack final : public allshader::WaveformRenderer { +class allshader::WaveformRendererEndOfTrack final + : public ::WaveformRendererAbstract, + public rendergraph::GeometryNode { public: explicit WaveformRendererEndOfTrack( WaveformWidgetRenderer* waveformWidget); + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& context) override; bool init() override; - void initializeGL() override; - void paintGL() override; + // Virtual for rendergraph::Node + void preprocess() override; + bool isSubtreeBlocked() const override; private: - void fillWithGradient(QColor color); + static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; + static constexpr float verticalGradientArray[] = {1.f, 1.f, -1.f, -1.f}; + static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f}; - mixxx::EndOfTrackShader m_shader; std::unique_ptr m_pEndOfTrackControl; std::unique_ptr m_pTimeRemainingControl; diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp index 0ddf164cc3e6..f48b1136f595 100644 --- a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp +++ b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp @@ -19,7 +19,6 @@ void WaveformRendererFiltered::onSetup(const QDomNode& node) { } void WaveformRendererFiltered::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererhsv.cpp b/src/waveform/renderers/allshader/waveformrendererhsv.cpp index b03a17a304e8..bf6c9230542d 100644 --- a/src/waveform/renderers/allshader/waveformrendererhsv.cpp +++ b/src/waveform/renderers/allshader/waveformrendererhsv.cpp @@ -19,7 +19,6 @@ void WaveformRendererHSV::onSetup(const QDomNode& node) { } void WaveformRendererHSV::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererpreroll.cpp b/src/waveform/renderers/allshader/waveformrendererpreroll.cpp index eea01c5bdada..0dc044db41d5 100644 --- a/src/waveform/renderers/allshader/waveformrendererpreroll.cpp +++ b/src/waveform/renderers/allshader/waveformrendererpreroll.cpp @@ -1,11 +1,14 @@ #include "waveform/renderers/allshader/waveformrendererpreroll.h" #include -#include #include #include +#include "rendergraph/context.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/patternmaterial.h" #include "skin/legacy/skincontext.h" +#include "texturedvertexupdater.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "widget/wskincolor.h" @@ -20,7 +23,7 @@ QImage drawPrerollImage(float markerLength, const float imageW = static_cast(imagePixelW) / devicePixelRatio; const float imageH = static_cast(imagePixelH) / devicePixelRatio; - QImage image(imagePixelW, imagePixelH, QImage::Format_ARGB32_Premultiplied); + QImage image(imagePixelW, imagePixelH, QImage::Format_RGBA8888_Premultiplied); image.setDevicePixelRatio(devicePixelRatio); const float penWidth = 1.5f; @@ -47,7 +50,7 @@ QImage drawPrerollImage(float markerLength, path.lineTo(p0); path.closeSubpath(); QColor fillColor = color; - fillColor.setAlphaF(0.5f); + fillColor.setAlphaF(0.25f); painter.fillPath(path, QBrush(fillColor)); painter.drawPath(path); @@ -57,13 +60,19 @@ QImage drawPrerollImage(float markerLength, } } // anonymous namespace +using namespace rendergraph; + namespace allshader { WaveformRendererPreroll::WaveformRendererPreroll( WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type) - : WaveformRenderer(waveformWidget), + : ::WaveformRendererAbstract(waveformWidget), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { + setGeometry(std::make_unique(PatternMaterial::attributes(), 0)); + setMaterial(std::make_unique()); + setUsePreprocess(true); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); } WaveformRendererPreroll::~WaveformRendererPreroll() = default; @@ -74,15 +83,23 @@ void WaveformRendererPreroll::setup( m_color = WSkinColor::getCorrectColor(m_color); } -void WaveformRendererPreroll::initializeGL() { - WaveformRenderer::initializeGL(); - m_shader.init(); +void WaveformRendererPreroll::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } -void WaveformRendererPreroll::paintGL() { - const TrackPointer track = m_waveformRenderer->getTrackInfo(); - if (!track || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { - return; +void WaveformRendererPreroll::preprocess() { + if (!preprocessInner()) { + geometry().allocate(0); + } +} + +bool WaveformRendererPreroll::preprocessInner() { + const TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); + + if (!trackInfo || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { + return false; } auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip @@ -96,11 +113,15 @@ void WaveformRendererPreroll::paintGL() { // to indicate the respective zones. const bool preRollVisible = firstDisplayedPosition < 0; const bool postRollVisible = lastDisplayedPosition > 1; + const int numVerticesPerRectangle = 6; - if (!(preRollVisible || postRollVisible)) { - return; + if (!preRollVisible && !postRollVisible) { + return false; } + const int reserved = (preRollVisible ? numVerticesPerRectangle : 0) + + (postRollVisible ? numVerticesPerRectangle : 0); + const double playMarkerPosition = m_waveformRenderer->getPlayMarkerPosition(); const double vSamplesPerPixel = m_waveformRenderer->getVisualSamplePerPixel(); const double numberOfVSamples = m_waveformRenderer->getLength() * vSamplesPerPixel; @@ -126,36 +147,21 @@ void WaveformRendererPreroll::paintGL() { // has changed size last time. m_markerLength = markerLength; m_markerBreadth = markerBreadth; - m_texture.setData(drawPrerollImage(m_markerLength, - m_markerBreadth, - m_waveformRenderer->getDevicePixelRatio(), - m_color)); - } - - if (!m_texture.isStorageAllocated()) { - return; + Context context; + dynamic_cast(material()) + .setTexture(std::make_unique(context, + drawPrerollImage(m_markerLength, + m_markerBreadth, + m_waveformRenderer->getDevicePixelRatio(), + m_color))); } - const int matrixLocation = m_shader.matrixLocation(); - const int textureLocation = m_shader.textureLocation(); - const int positionLocation = m_shader.positionLocation(); - const int texcoordLocation = m_shader.texcoordLocation(); - - // Set up the shader - m_shader.bind(); - - m_shader.enableAttributeArray(positionLocation); - m_shader.enableAttributeArray(texcoordLocation); - - const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); - - m_shader.setUniformValue(matrixLocation, matrix); - m_shader.setUniformValue(textureLocation, 0); - - m_texture.bind(); + geometry().allocate(reserved); const float end = m_waveformRenderer->getLength(); + TexturedVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + if (preRollVisible) { // VSample position of the right-most triangle's tip const double triangleTipVSamplePosition = @@ -169,11 +175,14 @@ void WaveformRendererPreroll::paintGL() { x -= std::ceil((x - limit) / markerLength) * markerLength; } - drawPattern(x, - halfBreadth - halfMarkerBreadth, - 0.f, - m_isSlipRenderer ? halfBreadth : halfBreadth + halfMarkerBreadth, - x / markerLength); + const float repetitions = x / markerLength; + + vertexUpdater.addRectangle({x, halfBreadth - halfMarkerBreadth}, + {0, + m_isSlipRenderer ? halfBreadth + : halfBreadth + halfMarkerBreadth}, + {0.f, 0.f}, + {repetitions, m_isSlipRenderer ? 0.5f : 1.f}); } if (postRollVisible) { @@ -190,44 +199,23 @@ void WaveformRendererPreroll::paintGL() { x += std::ceil((limit - x) / markerLength) * markerLength; } - drawPattern(x, - halfBreadth - halfMarkerBreadth, - end, - m_isSlipRenderer ? halfBreadth : halfBreadth + halfMarkerBreadth, - (end - x) / markerLength); + const float repetitions = (end - x) / markerLength; + + vertexUpdater.addRectangle({x, halfBreadth - halfMarkerBreadth}, + {end, + m_isSlipRenderer ? halfBreadth + : halfBreadth + halfMarkerBreadth}, + {0.f, 0.f}, + {repetitions, m_isSlipRenderer ? 0.5f : 1.f}); } - m_texture.release(); + DEBUG_ASSERT(reserved == vertexUpdater.index()); - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(texcoordLocation); - m_shader.release(); -} + const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); + + material().setUniform(0, matrix); -void WaveformRendererPreroll::drawPattern( - float x1, float y1, float x2, float y2, float repetitions) { - // Draw a large rectangle with a repeating pattern of the texture - const int repetitionsLocation = m_shader.repetitionsLocation(); - const int positionLocation = m_shader.positionLocation(); - const int texcoordLocation = m_shader.texcoordLocation(); - - const std::array positionArray = {x1, y1, x2, y1, x1, y2, x2, y2}; - const std::array texcoordArray = {0.f, - 0.f, - 1.f, - 0.f, - 0.f, - m_isSlipRenderer ? 0.5f : 1.f, - 1.f, - m_isSlipRenderer ? 0.5f : 1.f}; - m_shader.setUniformValue(repetitionsLocation, QVector2D(repetitions, 1.0)); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, positionArray.data(), 2); - m_shader.setAttributeArray( - texcoordLocation, GL_FLOAT, texcoordArray.data(), 2); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererpreroll.h b/src/waveform/renderers/allshader/waveformrendererpreroll.h index 742320424c57..7e3f10ea6281 100644 --- a/src/waveform/renderers/allshader/waveformrendererpreroll.h +++ b/src/waveform/renderers/allshader/waveformrendererpreroll.h @@ -1,46 +1,44 @@ #pragma once #include -#include #include -#include "shaders/patternshader.h" +#include "rendergraph/geometrynode.h" #include "util/class.h" -#include "util/opengltexture2d.h" -#include "waveform/renderers/allshader/vertexdata.h" -#include "waveform/renderers/allshader/waveformrenderer.h" +#include "waveform/renderers/waveformrendererabstract.h" class QDomNode; class SkinContext; -class QOpenGLTexture; namespace allshader { class WaveformRendererPreroll; -class WaveformRendererSlipPreroll; } -class allshader::WaveformRendererPreroll : public allshader::WaveformRenderer { +class allshader::WaveformRendererPreroll final + : public ::WaveformRendererAbstract, + public rendergraph::GeometryNode { public: explicit WaveformRendererPreroll( - WaveformWidgetRenderer* waveformWidgetRenderer, + WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); ~WaveformRendererPreroll() override; + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& context) override; - void paintGL() override; - void initializeGL() override; - private: - void drawPattern(float x1, float y1, float x2, float y2, float repetitions); + // Virtual for rendergraph::Node + void preprocess() override; - mixxx::PatternShader m_shader; + private: QColor m_color; float m_markerBreadth{}; float m_markerLength{}; - OpenGLTexture2D m_texture; - bool m_isSlipRenderer; + bool preprocessInner(); + DISALLOW_COPY_AND_ASSIGN(WaveformRendererPreroll); }; diff --git a/src/waveform/renderers/allshader/waveformrendererrgb.cpp b/src/waveform/renderers/allshader/waveformrendererrgb.cpp index 828a8a71bbb0..1cbdfb7e621b 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.cpp +++ b/src/waveform/renderers/allshader/waveformrendererrgb.cpp @@ -27,7 +27,6 @@ void WaveformRendererRGB::onSetup(const QDomNode& node) { } void WaveformRendererRGB::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrenderersignalbase.h b/src/waveform/renderers/allshader/waveformrenderersignalbase.h index 0e72d9364c77..b09b528111a8 100644 --- a/src/waveform/renderers/allshader/waveformrenderersignalbase.h +++ b/src/waveform/renderers/allshader/waveformrenderersignalbase.h @@ -3,8 +3,8 @@ #include #include +#include "rendergraph/openglnode.h" #include "util/class.h" -#include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/waveformrenderersignalbase.h" class WaveformWidgetRenderer; @@ -14,7 +14,7 @@ class WaveformRendererSignalBase; } class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBase, - public allshader::WaveformRendererAbstract { + public rendergraph::OpenGLNode { public: enum class Option { None = 0b0, @@ -37,9 +37,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 9e7730b4f432..7111fedf764b 100644 --- a/src/waveform/renderers/allshader/waveformrenderersimple.cpp +++ b/src/waveform/renderers/allshader/waveformrenderersimple.cpp @@ -18,7 +18,6 @@ void WaveformRendererSimple::onSetup(const QDomNode& node) { } void WaveformRendererSimple::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp index 4311f00a4ceb..8ddf0be80e55 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp @@ -63,7 +63,6 @@ void WaveformRendererSlipMode::setup(const QDomNode& node, const SkinContext& co } void WaveformRendererSlipMode::initializeGL() { - WaveformRenderer::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererstem.cpp b/src/waveform/renderers/allshader/waveformrendererstem.cpp index bacb18d32cae..c8dbe5a949cb 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.cpp +++ b/src/waveform/renderers/allshader/waveformrendererstem.cpp @@ -29,7 +29,6 @@ void WaveformRendererStem::onSetup(const QDomNode& node) { } void WaveformRendererStem::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); m_textureShader.init(); auto group = m_pEQEnabled->getKey().group; diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.cpp b/src/waveform/renderers/allshader/waveformrenderertextured.cpp index dc137b73208f..de21a013259c 100644 --- a/src/waveform/renderers/allshader/waveformrenderertextured.cpp +++ b/src/waveform/renderers/allshader/waveformrenderertextured.cpp @@ -219,8 +219,6 @@ void WaveformRendererTextured::createFrameBuffers() { } void WaveformRendererTextured::initializeGL() { - WaveformRendererSignalBase::initializeGL(); - m_textureRenderedWaveformCompletion = 0; if (!m_frameShaderProgram) { diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 1a66df4deb4d..60f229402df0 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -1,16 +1,24 @@ #include "waveform/renderers/allshader/waveformrendermark.h" -#include #include +#include "rendergraph/context.h" +#include "rendergraph/geometry.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/material/rgbamaterial.h" +#include "rendergraph/material/texturematerial.h" +#include "rendergraph/texture.h" +#include "rgbavertexupdater.h" +#include "texturedvertexupdater.h" #include "track/track.h" #include "util/colorcomponents.h" +#include "waveform/renderers/allshader/digitsrenderer.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/rgbadata.h" -#include "waveform/renderers/allshader/vertexdata.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveformwidgetfactory.h" +using namespace rendergraph; + // On the use of QPainter: // // The renderers in this folder are optimized to use GLSL shaders and refrain @@ -20,17 +28,87 @@ // only to draw on a QImage. This is only done once when needed and the images are // then used as textures to be drawn with a GLSL shader. -class TextureGraphics : public WaveformMark::Graphics { +class WaveformMarkNode : public rendergraph::GeometryNode { + public: + WaveformMark* m_pOwner{}; + + WaveformMarkNode(WaveformMark* pOwner, const QImage& image) + : m_pOwner(pOwner) { + initForRectangles(1); + updateTexture(image); + } + void updateTexture(const QImage& image) { + Context context; + dynamic_cast(material()) + .setTexture(std::make_unique(context, image)); + m_textureWidth = image.width(); + m_textureHeight = image.height(); + } + void update(const QMatrix4x4& matrix, float x, float y, float devicePixelRatio) { + material().setUniform(0, matrix); + + TexturedVertexUpdater vertexUpdater{ + geometry().vertexDataAs()}; + vertexUpdater.addRectangle({x, y}, + {x + m_textureWidth / devicePixelRatio, + y + m_textureHeight / devicePixelRatio}, + {0.f, 0.f}, + {1.f, 1.f}); + } + float textureWidth() const { + return m_textureWidth; + } + float textureHeight() const { + return m_textureHeight; + } + public: - TextureGraphics(const QImage& image) { - m_texture.setData(image); + float m_textureWidth{}; + float m_textureHeight{}; +}; + +class WaveformMarkNodeGraphics : public WaveformMark::Graphics { + public: + WaveformMarkNodeGraphics(WaveformMark* pOwner, const QImage& image) + : m_pNode(std::make_unique(pOwner, image)) { + } + void updateTexture(const QImage& image) { + if (!m_pNode) { + return; + } + waveformMarkNode()->updateTexture(image); + } + void update(const QMatrix4x4& matrix, float x, float y, float devicePixelRatio) { + if (!m_pNode) { + return; + } + waveformMarkNode()->update(matrix, x, y, devicePixelRatio); + } + float textureWidth() const { + if (!m_pNode) { + return 0.0; + } + return waveformMarkNode()->textureWidth(); } - QOpenGLTexture* texture() { - return &m_texture; + float textureHeight() const { + if (!m_pNode) { + return 0.0; + } + return waveformMarkNode()->textureHeight(); + } + void setNode(std::unique_ptr&& pNode) { + m_pNode = std::move(pNode); + } + void moveNodeToChildrenOf(Node* pParent) { + pParent->appendChildNode(std::move(m_pNode)); } private: - OpenGLTexture2D m_texture; + WaveformMarkNode* waveformMarkNode() const { + return static_cast(m_pNode.get()); + } + + std::unique_ptr m_pNode; }; // Both allshader::WaveformRenderMark and the non-GL ::WaveformRenderMark derive @@ -66,6 +144,24 @@ allshader::WaveformRenderMark::WaveformRenderMark( m_timeUntilMark(0.0), m_pTimeRemainingControl(nullptr), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { + appendChildNode(std::make_unique()); + m_pRangeNodesParent = lastChild(); + + appendChildNode(std::make_unique()); + m_pMarkNodesParent = lastChild(); + + appendChildNode(std::make_unique()); + m_pDigitsRenderNode = static_cast(lastChild()); + + appendChildNode(std::make_unique()); + m_pPlayPosNode = static_cast(lastChild()); + m_pPlayPosNode->initForRectangles(1); +} + +void allshader::WaveformRenderMark::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } bool allshader::WaveformRenderMark::init() { @@ -74,69 +170,22 @@ bool allshader::WaveformRenderMark::init() { return true; } -void allshader::WaveformRenderMark::initializeGL() { - allshader::WaveformRendererAbstract::initializeGL(); - m_digitsRenderer.init(); - m_rgbaShader.init(); - m_textureShader.init(); - +void allshader::WaveformRenderMark::initialize() { // Will create textures so requires OpenGL context updateMarkImages(); updatePlayPosMarkTexture(); const auto untilMarkTextPointSize = WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); - m_digitsRenderer.updateTexture(untilMarkTextPointSize, + m_pDigitsRenderNode->updateTexture(untilMarkTextPointSize, getMaxHeightForText(), m_waveformRenderer->getDevicePixelRatio()); + Node::initialize(); } -void allshader::WaveformRenderMark::drawTexture( - const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture) { - const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - const float texx1 = 0.f; - const float texy1 = 0.f; - const float texx2 = 1.f; - const float texy2 = 1.f; - - const float posx1 = x; - const float posx2 = x + static_cast(texture->width() / devicePixelRatio); - const float posy1 = y; - const float posy2 = y + static_cast(texture->height() / devicePixelRatio); - - const float posarray[] = {posx1, posy1, posx2, posy1, posx1, posy2, posx2, posy2}; - const float texarray[] = {texx1, texy1, texx2, texy1, texx1, texy2, texx2, texy2}; - - m_textureShader.bind(); - - const int matrixLocation = m_textureShader.uniformLocation("matrix"); - const int textureLocation = m_textureShader.uniformLocation("texture"); - const int positionLocation = m_textureShader.attributeLocation("position"); - const int texcoordLocation = m_textureShader.attributeLocation("texcoord"); - - m_textureShader.setUniformValue(matrixLocation, matrix); - - m_textureShader.enableAttributeArray(positionLocation); - m_textureShader.setAttributeArray( - positionLocation, GL_FLOAT, posarray, 2); - m_textureShader.enableAttributeArray(texcoordLocation); - m_textureShader.setAttributeArray( - texcoordLocation, GL_FLOAT, texarray, 2); - - m_textureShader.setUniformValue(textureLocation, 0); - - texture->bind(); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - texture->release(); - - m_textureShader.disableAttributeArray(positionLocation); - m_textureShader.disableAttributeArray(texcoordLocation); - m_textureShader.release(); -} - -void allshader::WaveformRenderMark::drawMark( - const QMatrix4x4& matrix, const QRectF& rect, QColor color) { +void allshader::WaveformRenderMark::updateRangeNode(GeometryNode* pNode, + const QMatrix4x4& matrix, + const QRectF& rect, + QColor color) { // draw a gradient towards transparency at the upper and lower 25% of the waveform view const float qh = static_cast(std::floor(rect.height() * 0.25)); @@ -151,43 +200,44 @@ void allshader::WaveformRenderMark::drawMark( getRgbF(color, &r, &g, &b, &a); - VertexData vertices; - vertices.reserve(12); // 4 triangles - vertices.addRectangle(posx1, posy1, posx2, posy2); - vertices.addRectangle(posx1, posy4, posx2, posy3); - - RGBAData rgbaData; - rgbaData.reserve(12); // 4 triangles - rgbaData.addForRectangleGradient(r, g, b, a, r, g, b, 0.f); - rgbaData.addForRectangleGradient(r, g, b, a, r, g, b, 0.f); - - m_rgbaShader.bind(); + RGBAVertexUpdater vertexUpdater{pNode->geometry().vertexDataAs()}; + vertexUpdater.addRectangleVGradient( + {posx1, posy1}, {posx2, posy2}, {r, g, b, a}, {r, g, b, 0.f}); + vertexUpdater.addRectangleVGradient( + {posx1, posy4}, {posx2, posy3}, {r, g, b, a}, {r, g, b, 0.f}); - const int matrixLocation = m_rgbaShader.matrixLocation(); - const int positionLocation = m_rgbaShader.positionLocation(); - const int colorLocation = m_rgbaShader.colorLocation(); - - m_rgbaShader.setUniformValue(matrixLocation, matrix); - - m_rgbaShader.enableAttributeArray(positionLocation); - m_rgbaShader.setAttributeArray( - positionLocation, GL_FLOAT, vertices.constData(), 2); - m_rgbaShader.enableAttributeArray(colorLocation); - m_rgbaShader.setAttributeArray( - colorLocation, GL_FLOAT, rgbaData.constData(), 4); - - glDrawArrays(GL_TRIANGLES, 0, vertices.size()); + pNode->material().setUniform(0, matrix); +} - m_rgbaShader.disableAttributeArray(positionLocation); - m_rgbaShader.disableAttributeArray(colorLocation); - m_rgbaShader.release(); +bool allshader::WaveformRenderMark::isSubtreeBlocked() const { + return m_isSlipRenderer && !m_waveformRenderer->isSlipActive(); } -void allshader::WaveformRenderMark::paintGL() { - if (m_isSlipRenderer && !m_waveformRenderer->isSlipActive()) { +void allshader::WaveformRenderMark::update() { + if (isSubtreeBlocked()) { return; } + // For each WaveformMark we create a GeometryNode with Texture + // (in updateMarkImage). Of these GeometryNodes, we append the + // the ones that need to be shown on screen as children to + // m_pMarkNodesParent (transferring ownership). + // + // At the beginning of a new frame, we remove all the child nodes + // from m_pMarkNodesParent and store each with their mark + // (transferring ownership). Later in this function we move the + // visible nodes back to m_pMarkNodesParent children. + while (auto pChild = m_pMarkNodesParent->firstChild()) { + // Pop child from front of m_pMarkNodesParent + auto pRemoved = m_pMarkNodesParent->removeChildNode(pChild); + // Determine its WaveformMark + auto pMarkNode = static_cast(pRemoved.get()); + auto pMark = pMarkNode->m_pOwner; + auto pGraphics = static_cast(pMark->m_pGraphics.get()); + // Store the node with the WaveformMark + pGraphics->setNode(std::move(pRemoved)); + } + auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip : ::WaveformRendererAbstract::Play; bool slipActive = m_waveformRenderer->isSlipActive(); @@ -195,14 +245,14 @@ void allshader::WaveformRenderMark::paintGL() { const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); QList marksOnScreen; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - for (const auto& pMark : std::as_const(m_marks)) { pMark->setBreadth(slipActive ? m_waveformRenderer->getBreadth() / 2 : m_waveformRenderer->getBreadth()); } - // Will create textures so requires OpenGL context + + // Generate initial node or update its texture if needed for each of + // the WaveformMarks (in which case updateMarkImage is called) + // (Will create textures so requires OpenGL context) updateMarkImages(); QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); @@ -210,6 +260,8 @@ void allshader::WaveformRenderMark::paintGL() { const double playPosition = m_waveformRenderer->getTruePosSample(positionType); double nextMarkPosition = std::numeric_limits::max(); + Node* pRangeChild = m_pRangeNodesParent->firstChild(); + for (const auto& pMark : std::as_const(m_marks)) { if (!pMark->isValid()) { continue; @@ -221,13 +273,10 @@ void allshader::WaveformRenderMark::paintGL() { continue; } - QOpenGLTexture* pTexture = - static_cast(pMark->m_pGraphics.get()) - ->texture(); - - if (!pTexture) { + auto pMarkGraphics = pMark->m_pGraphics.get(); + auto pMarkNodeGraphics = static_cast(pMarkGraphics); + if (!pMarkGraphics) // is this even possible? continue; - } const float currentMarkPoint = std::round( @@ -247,7 +296,7 @@ void allshader::WaveformRenderMark::paintGL() { // Pixmaps are expected to have the mark stroke at the center, // and preferably have an odd width in order to have the stroke // exactly at the sample position. - const float markHalfWidth = pTexture->width() / devicePixelRatio / 2.f; + const float markHalfWidth = pMarkNodeGraphics->textureWidth() / devicePixelRatio / 2.f; const float drawOffset = currentMarkPoint - markHalfWidth; bool visible = false; @@ -255,12 +304,16 @@ void allshader::WaveformRenderMark::paintGL() { if (drawOffset > -markHalfWidth && drawOffset < m_waveformRenderer->getLength() + markHalfWidth) { - drawTexture(matrix, + pMarkNodeGraphics->update(matrix, drawOffset, !m_isSlipRenderer && slipActive ? m_waveformRenderer->getBreadth() / 2 : 0, - pTexture); + devicePixelRatio); + + // transfer back to m_pMarkNodesParent children, for rendering + pMarkNodeGraphics->moveNodeToChildrenOf(m_pMarkNodesParent); + visible = true; } @@ -277,13 +330,22 @@ void allshader::WaveformRenderMark::paintGL() { QColor color = pMark->fillColor(); color.setAlphaF(0.4f); - drawMark(matrix, + // Reuse, or create new when needed + if (!pRangeChild) { + m_pRangeNodesParent->appendChildNode(std::make_unique()); + pRangeChild = m_pRangeNodesParent->lastChild(); + static_cast(pRangeChild)->initForRectangles(2); + } + + updateRangeNode(static_cast(pRangeChild), + matrix, QRectF(QPointF(currentMarkPoint, 0), QPointF(currentMarkEndPoint, - m_waveformRenderer - ->getBreadth())), + m_waveformRenderer->getBreadth())), color); + visible = true; + pRangeChild = pRangeChild->nextSibling(); } } @@ -293,6 +355,14 @@ void allshader::WaveformRenderMark::paintGL() { pMark, static_cast(drawOffset)}); } } + + // Remove unused nodes + while (pRangeChild) { + auto pNext = static_cast(pRangeChild->nextSibling()); + m_pRangeNodesParent->removeChildNode(pRangeChild); + pRangeChild = pNext; + } + m_waveformRenderer->setMarkPositions(marksOnScreen); const float currentMarkPoint = @@ -302,11 +372,19 @@ void allshader::WaveformRenderMark::paintGL() { devicePixelRatio) / devicePixelRatio; - if (m_playPosMarkTexture.isStorageAllocated()) { - const float markHalfWidth = m_playPosMarkTexture.width() / devicePixelRatio / 2.f; + { + const float markHalfWidth = 11.f / 2.f; const float drawOffset = currentMarkPoint - markHalfWidth; - drawTexture(matrix, drawOffset, 0.f, &m_playPosMarkTexture); + m_pPlayPosNode->material().setUniform(0, matrix); + + TexturedVertexUpdater vertexUpdater{ + m_pPlayPosNode->geometry() + .vertexDataAs()}; + vertexUpdater.addRectangle({drawOffset, 0.f}, + {drawOffset + 11.f, static_cast(m_waveformRenderer->getBreadth())}, + {0.f, 0.f}, + {1.f, 1.f}); } if (WaveformWidgetFactory::instance()->getUntilMarkShowBeats() || @@ -323,14 +401,15 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa const auto untilMarkTextPointSize = WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); - m_digitsRenderer.updateTexture(untilMarkTextPointSize, + m_pDigitsRenderNode->updateTexture(untilMarkTextPointSize, getMaxHeightForText(), m_waveformRenderer->getDevicePixelRatio()); if (m_timeUntilMark == 0.0) { + m_pDigitsRenderNode->clear(); return; } - const float ch = m_digitsRenderer.height(); + const float ch = m_pDigitsRenderNode->height(); float y = untilMarkAlign == Qt::AlignTop ? 0.f : untilMarkAlign == Qt::AlignBottom @@ -350,24 +429,12 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa } } - if (untilMarkShowBeats) { - const auto w = m_digitsRenderer.draw(matrix, - x, - y, - QString::number(m_beatsUntilMark)); - if (multiLine) { - y += ch; - } else { - x += w + ch * 0.75f; - } - } - - if (untilMarkShowTime) { - m_digitsRenderer.draw(matrix, - x, - y, - timeSecToString(m_timeUntilMark)); - } + m_pDigitsRenderNode->update(matrix, + x, + y, + multiLine, + untilMarkShowBeats ? QString::number(m_beatsUntilMark) : QString{}, + untilMarkShowTime ? timeSecToString(m_timeUntilMark) : QString{}); } // Generate the texture used to draw the play position marker. @@ -434,7 +501,9 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { } painter.end(); - m_playPosMarkTexture.setData(image); + Context context; + dynamic_cast(m_pPlayPosNode->material()) + .setTexture(std::make_unique(context, image)); } void allshader::WaveformRenderMark::drawTriangle(QPainter* painter, @@ -451,15 +520,22 @@ void allshader::WaveformRenderMark::drawTriangle(QPainter* painter, painter->fillPath(triangle, fillColor); } -void allshader::WaveformRenderMark::resizeGL(int, int) { +void allshader::WaveformRenderMark::resize(int, int) { // Will create textures so requires OpenGL context updateMarkImages(); updatePlayPosMarkTexture(); } void allshader::WaveformRenderMark::updateMarkImage(WaveformMarkPointer pMark) { - pMark->m_pGraphics = std::make_unique( - pMark->generateImage(m_waveformRenderer->getDevicePixelRatio())); + if (!pMark->m_pGraphics) { + pMark->m_pGraphics = + std::make_unique(pMark.get(), + pMark->generateImage( + m_waveformRenderer->getDevicePixelRatio())); + } else { + auto pGraphics = static_cast(pMark->m_pGraphics.get()); + pGraphics->updateTexture(pMark->generateImage(m_waveformRenderer->getDevicePixelRatio())); + } } void allshader::WaveformRenderMark::updateUntilMark( diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index 317f132e26d6..585c147be949 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -2,42 +2,39 @@ #include -#include "shaders/rgbashader.h" -#include "shaders/textureshader.h" -#include "util/opengltexture2d.h" -#include "waveform/renderers/allshader/digitsrenderer.h" -#include "waveform/renderers/allshader/waveformrendererabstract.h" +#include "rendergraph/node.h" #include "waveform/renderers/waveformrendermarkbase.h" class QDomNode; class SkinContext; -class QOpenGLTexture; + +namespace rendergraph { +class GeometryNode; +} namespace allshader { +class DigitsRenderNode; class WaveformRenderMark; } class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, - public allshader::WaveformRendererAbstract { + public rendergraph::Node { public: explicit WaveformRenderMark(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); - void draw(QPainter* painter, QPaintEvent* event) override { - Q_UNUSED(painter); - Q_UNUSED(event); - } - - allshader::WaveformRendererAbstract* allshaderWaveformRenderer() override { - return this; - } + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; bool init() override; - void initializeGL() override; - void paintGL() override; - void resizeGL(int w, int h) override; + void update(); + + // Virtual for rendergraph::Node + void initialize() override; + void resize(int, int) override; + bool isSubtreeBlocked() const override; private: void updateMarkImage(WaveformMarkPointer pMark) override; @@ -51,15 +48,14 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, QPointF p3); void drawMark(const QMatrix4x4& matrix, const QRectF& rect, QColor color); - void drawTexture(const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture); void updateUntilMark(double playPosition, double markerPosition); void drawUntilMark(const QMatrix4x4& matrix, float x); float getMaxHeightForText() const; + void updateRangeNode(rendergraph::GeometryNode* pNode, + const QMatrix4x4& matrix, + const QRectF& rect, + QColor color); - mixxx::RGBAShader m_rgbaShader; - mixxx::TextureShader m_textureShader; - OpenGLTexture2D m_playPosMarkTexture; - DigitsRenderer m_digitsRenderer; int m_beatsUntilMark; double m_timeUntilMark; double m_currentBeatPosition; @@ -68,5 +64,10 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, bool m_isSlipRenderer; + rendergraph::Node* m_pRangeNodesParent{}; + rendergraph::Node* m_pMarkNodesParent{}; + rendergraph::GeometryNode* m_pPlayPosNode; + DigitsRenderNode* m_pDigitsRenderNode{}; + DISALLOW_COPY_AND_ASSIGN(WaveformRenderMark); }; diff --git a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp index 6b3a33d49360..6821658c0244 100644 --- a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp +++ b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp @@ -1,41 +1,23 @@ #include "waveform/renderers/allshader/waveformrendermarkrange.h" +#include "rendergraph/geometry.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/material/unicolormaterial.h" #include "skin/legacy/skincontext.h" +#include "vertexupdater.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" -allshader::WaveformRenderMarkRange::WaveformRenderMarkRange(WaveformWidgetRenderer* waveformWidget) - : WaveformRenderer(waveformWidget) { -} - -void allshader::WaveformRenderMarkRange::initializeGL() { - WaveformRenderer::initializeGL(); - m_shader.init(); -} - -void allshader::WaveformRenderMarkRange::fillRect( - const QRectF& rect, QColor color) { - const float posx1 = static_cast(rect.x()); - const float posx2 = static_cast(rect.x() + rect.width()); - const float posy1 = static_cast(rect.y()); - const float posy2 = static_cast(rect.y() + rect.height()); - - const float posarray[] = {posx1, posy1, posx2, posy1, posx1, posy2, posx2, posy2}; +using namespace rendergraph; - const int colorLocation = m_shader.colorLocation(); - const int positionLocation = m_shader.positionLocation(); +namespace allshader { - m_shader.setUniformValue(colorLocation, color); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, posarray, 2); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +WaveformRenderMarkRange::WaveformRenderMarkRange(WaveformWidgetRenderer* waveformWidget) + : ::WaveformRendererAbstract(waveformWidget) { } -void allshader::WaveformRenderMarkRange::setup(const QDomNode& node, const SkinContext& context) { +void WaveformRenderMarkRange::setup(const QDomNode& node, const SkinContext& context) { m_markRanges.clear(); - m_markRanges.reserve(1); QDomNode child = node.firstChild(); while (!child.isNull()) { @@ -51,21 +33,18 @@ void allshader::WaveformRenderMarkRange::setup(const QDomNode& node, const SkinC } } -void allshader::WaveformRenderMarkRange::paintGL() { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +void WaveformRenderMarkRange::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); +} +void WaveformRenderMarkRange::update() { const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); - const int positionLocation = m_shader.positionLocation(); - const int matrixLocation = m_shader.matrixLocation(); - - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); + Node* pChild = firstChild(); - m_shader.setUniformValue(matrixLocation, matrix); - - for (auto&& markRange : m_markRanges) { + for (const auto& markRange : m_markRanges) { // If the mark range is not active we should not draw it. if (!markRange.active()) { continue; @@ -89,8 +68,6 @@ void allshader::WaveformRenderMarkRange::paintGL() { startPosition = std::floor(startPosition); endPosition = std::floor(endPosition); - const double span = std::max(endPosition - startPosition, 1.0); - // range not in the current display if (startPosition > m_waveformRenderer->getLength() || endPosition < 0) { continue; @@ -99,8 +76,37 @@ void allshader::WaveformRenderMarkRange::paintGL() { QColor color = markRange.enabled() ? markRange.m_activeColor : markRange.m_disabledColor; color.setAlphaF(0.3f); - fillRect(QRectF(startPosition, 0, span, m_waveformRenderer->getBreadth()), color); + if (!pChild) { + appendChildNode(std::make_unique()); + pChild = lastChild(); + static_cast(pChild)->initForRectangles(1); + } + + updateNode(static_cast(pChild), + matrix, + color, + {static_cast(startPosition), 0.f}, + {static_cast(endPosition) + 1.f, + static_cast(m_waveformRenderer->getBreadth())}); + + pChild = static_cast(pChild->nextSibling()); } - m_shader.disableAttributeArray(positionLocation); - m_shader.release(); + while (pChild) { + auto pNext = pChild->nextSibling(); + removeChildNode(pChild); + pChild = pNext; + } +} + +void WaveformRenderMarkRange::updateNode(GeometryNode* pChild, + const QMatrix4x4& matrix, + QColor color, + QVector2D lt, + QVector2D rb) { + VertexUpdater vertexUpdater{pChild->geometry().vertexDataAs()}; + vertexUpdater.addRectangle(lt, rb); + pChild->material().setUniform(0, matrix); + pChild->material().setUniform(1, color); } + +} // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendermarkrange.h b/src/waveform/renderers/allshader/waveformrendermarkrange.h index 0c46a8dd5b81..416f9fce03db 100644 --- a/src/waveform/renderers/allshader/waveformrendermarkrange.h +++ b/src/waveform/renderers/allshader/waveformrendermarkrange.h @@ -1,33 +1,45 @@ #pragma once #include -#include +#include +#include +#include -#include "shaders/unicolorshader.h" +#include "rendergraph/node.h" #include "util/class.h" -#include "waveform/renderers/allshader/waveformrenderer.h" #include "waveform/renderers/waveformmarkrange.h" +#include "waveform/renderers/waveformrendererabstract.h" class QDomNode; class SkinContext; +namespace rendergraph { +class GeometryNode; +} + namespace allshader { class WaveformRenderMarkRange; } -class allshader::WaveformRenderMarkRange final : public allshader::WaveformRenderer { +class allshader::WaveformRenderMarkRange final : public ::WaveformRendererAbstract, + public rendergraph::Node { public: explicit WaveformRenderMarkRange(WaveformWidgetRenderer* waveformWidget); + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& context) override; - void initializeGL() override; - void paintGL() override; + void update(); private: - void fillRect(const QRectF& rect, QColor color); + void updateNode(rendergraph::GeometryNode* pChild, + const QMatrix4x4& matrix, + QColor color, + QVector2D lt, + QVector2D rb); - mixxx::UnicolorShader m_shader; std::vector m_markRanges; DISALLOW_COPY_AND_ASSIGN(WaveformRenderMarkRange); diff --git a/src/waveform/renderers/waveformdisplayrange.cpp b/src/waveform/renderers/waveformdisplayrange.cpp new file mode 100644 index 000000000000..c6aac4794647 --- /dev/null +++ b/src/waveform/renderers/waveformdisplayrange.cpp @@ -0,0 +1,177 @@ +#include "waveform/renderers/waveformdisplayrange.h" + +#include "control/controlobject.h" +#include "control/controlproxy.h" +#include "track/track.h" +#include "util/math.h" +#include "util/performancetimer.h" +#include "waveform/visualplayposition.h" +#include "waveform/waveform.h" +#include "widget/wwidget.h" + +const double WaveformDisplayRange::s_waveformMinZoom = 1.0; +const double WaveformDisplayRange::s_waveformMaxZoom = 10.0; +const double WaveformDisplayRange::s_waveformDefaultZoom = 3.0; +const double WaveformDisplayRange::s_defaultPlayMarkerPosition = 0.5; + +namespace { +constexpr int kDefaultDimBrightThreshold = 127; +} // namespace + +WaveformDisplayRange::WaveformDisplayRange(const QString& group) + : m_group(group), + m_orientation(Qt::Horizontal), + m_dimBrightThreshold(kDefaultDimBrightThreshold), + m_height(-1), + m_width(-1), + m_devicePixelRatio(1.0f), + + m_firstDisplayedPosition(0.0), + m_lastDisplayedPosition(0.0), + m_trackPixelCount(0.0), + + m_zoomFactor(1.0), + m_visualSamplePerPixel(1.0), + m_audioSamplePerPixel(1.0), + m_audioVisualRatio(1.0), + m_alphaBeatGrid(90), + // Really create some to manage those; + m_visualPlayPosition(nullptr), + m_playPosVSample(0), + m_totalVSamples(0), + m_pRateRatioCO(nullptr), + m_rateRatio(1.0), + m_pGainControlObject(nullptr), + m_gain(1.0), + m_pTrackSamplesControlObject(nullptr), + m_trackSamples(0.0), + m_scaleFactor(1.0), + m_playMarkerPosition(s_defaultPlayMarkerPosition), + m_playPos(-1.0), + m_truePosSample(-1.0) { + // qDebug() << "WaveformDisplayRange"; +} + +WaveformDisplayRange::~WaveformDisplayRange() { + // qDebug() << "~WaveformDisplayRange"; + + delete m_pRateRatioCO; + delete m_pGainControlObject; + delete m_pTrackSamplesControlObject; +} + +bool WaveformDisplayRange::init() { + // qDebug() << "WaveformDisplayRange::init, m_group=" << m_group; + + m_visualPlayPosition = VisualPlayPosition::getVisualPlayPosition(m_group); + + m_pRateRatioCO = new ControlProxy( + m_group, "rate_ratio"); + m_pGainControlObject = new ControlProxy( + m_group, "total_gain"); + m_pTrackSamplesControlObject = new ControlProxy( + m_group, "track_samples"); + + return true; +} + +void WaveformDisplayRange::onPreRender(ISyncTimeProvider* syncTimeProvider) { + // For a valid track to render we need + m_trackSamples = m_pTrackSamplesControlObject->get(); + if (m_trackSamples <= 0) { + return; + } + + // Fetch parameters before rendering in order the display all sub-renderers with the same values + m_rateRatio = m_pRateRatioCO->get(); + + // This gain adjustment compensates for an arbitrary /2 gain chop in + // EnginePregain. See the comment there. + m_gain = m_pGainControlObject->get() * 2; + + // Compute visual sample to pixel ratio + // Allow waveform to spread one visual sample across a hundred pixels + // NOTE: The hundred pixel limit is totally arbitrary. Theoretically, + // there should be no limit to how far the waveforms can be zoomed in. + double visualSamplePerPixel = m_zoomFactor * m_rateRatio / m_scaleFactor; + m_visualSamplePerPixel = math_max(0.01, visualSamplePerPixel); + + TrackPointer pTrack = m_pTrack; + if (pTrack) { + ConstWaveformPointer pWaveform = pTrack->getWaveform(); + if (pWaveform) { + m_audioVisualRatio = pWaveform->getAudioVisualRatio(); + } + } + + m_audioSamplePerPixel = m_visualSamplePerPixel * m_audioVisualRatio; + + double truePlayPos = m_visualPlayPosition->getAtNextVSync(syncTimeProvider); + // truePlayPos = -1 happens, when a new track is in buffer but + // m_visualPlayPosition was not updated + + if (m_audioSamplePerPixel > 0 && truePlayPos != -1) { + // Track length in pixels. + m_trackPixelCount = m_trackSamples / 2.0 / m_audioSamplePerPixel; + + // Avoid pixel jitter in play position by rounding to the nearest track + // pixel. + m_playPos = round(truePlayPos * m_trackPixelCount) / m_trackPixelCount; + // TODO m0dB shouldn't this be: + // round(truePlayPos * m_trackPixelCount * m_devicePixelRatio) / + // (m_trackPixelCount * m_devicePixelRatio); + m_totalVSamples = static_cast(m_trackPixelCount * m_visualSamplePerPixel); + m_playPosVSample = static_cast(m_playPos * m_totalVSamples); + m_truePosSample = truePlayPos * static_cast(m_trackSamples); + double leftOffset = m_playMarkerPosition; + double rightOffset = 1.0 - m_playMarkerPosition; + + double displayedLengthLeft = + (static_cast(getLength()) / m_trackPixelCount) * + leftOffset; + double displayedLengthRight = + (static_cast(getLength()) / m_trackPixelCount) * + rightOffset; + + // qDebug() << "WaveformDisplayRange::onPreRender" << + // "m_playMarkerPosition=" << m_playMarkerPosition << + // "leftOffset=" << leftOffset << + // "rightOffset=" << rightOffset << + // "displayedLengthLeft=" << displayedLengthLeft << + // "displayedLengthRight=" << displayedLengthRight; + + m_firstDisplayedPosition = m_playPos - displayedLengthLeft; + m_lastDisplayedPosition = m_playPos + displayedLengthRight; + } else { + m_playPos = -1.0; // disable renderers + m_truePosSample = -1.0; + } + + // qDebug() << "WaveformDisplayRange::onPreRender" << + // "m_group" << m_group << + // "m_trackSamples" << m_trackSamples << + // "m_playPos" << m_playPos << + // "m_rateRatio" << m_rate << + // "m_gain" << m_gain; +} + +void WaveformDisplayRange::resize(int width, int height, float devicePixelRatio) { + m_width = width; + m_height = height; + m_devicePixelRatio = devicePixelRatio; +} + +void WaveformDisplayRange::setZoom(double zoom) { + // qDebug() << "WaveformDisplayRange::setZoom" << zoom; + m_zoomFactor = math_clamp(zoom, s_waveformMinZoom, s_waveformMaxZoom); +} + +void WaveformDisplayRange::setDisplayBeatGridAlpha(int alpha) { + m_alphaBeatGrid = alpha; +} + +void WaveformDisplayRange::setTrack(TrackPointer track) { + m_pTrack = track; + // used to postpone first display until track sample is actually available + m_trackSamples = -1.0; +} diff --git a/src/waveform/renderers/waveformdisplayrange.h b/src/waveform/renderers/waveformdisplayrange.h new file mode 100644 index 000000000000..6118f474dc56 --- /dev/null +++ b/src/waveform/renderers/waveformdisplayrange.h @@ -0,0 +1,174 @@ +#pragma once + +#include +#include +#include +#include + +#include "track/track_decl.h" +#include "util/class.h" +#include "util/performancetimer.h" + +// Stripped-down WaveformWidgetRenderer and using ISyncTimeProvider instead of +// VSyncThread. +// TODO @m0dB remove code duplication by making WaveformWidgetRenderer a subclass + +class ControlProxy; +class VisualPlayPosition; +class ISyncTimeProvider; + +class WaveformDisplayRange { + public: + static const double s_waveformMinZoom; + static const double s_waveformMaxZoom; + static const double s_waveformDefaultZoom; + static const double s_defaultPlayMarkerPosition; + + public: + explicit WaveformDisplayRange(const QString& group); + virtual ~WaveformDisplayRange(); + + bool init(); + virtual bool onInit() { + return true; + } + + void onPreRender(ISyncTimeProvider* syncTimeProvider); + + const QString& getGroup() const { + return m_group; + } + const TrackPointer getTrackInfo() const { + return m_pTrack; + } + + double getFirstDisplayedPosition() const { + return m_firstDisplayedPosition; + } + double getLastDisplayedPosition() const { + return m_lastDisplayedPosition; + } + + void setZoom(double zoom); + + void setDisplayBeatGrid(bool set); + void setDisplayBeatGridAlpha(int alpha); + + double getVisualSamplePerPixel() const { + return m_visualSamplePerPixel; + } + double getAudioSamplePerPixel() const { + return m_audioSamplePerPixel; + } + + // those function replace at its best sample position to an admissible + // sample position according to the current visual resampling + // this make mark and signal deterministic + void regulateVisualSample(int& sampleIndex) const; + + // this "regulate" against visual sampling to make the position in widget + // stable and deterministic + // Transform sample index to pixel in track. + inline double transformSamplePositionInRendererWorld(double samplePosition) const { + if (std::abs(samplePosition - m_truePosSample) < 1.f) { + // When asked for the sample position that corresponds with the play + // marker, return the play market pixel position. This avoids a rare + // rounding issue where a marker at that sample position would be + // 1 pixel off. + return m_playMarkerPosition * getLength(); + } + const double relativePosition = samplePosition / m_trackSamples; + return transformPositionInRendererWorld(relativePosition); + } + // Transform position (percentage of track) to pixel in track. + inline double transformPositionInRendererWorld(double position) const { + return m_trackPixelCount * (position - m_firstDisplayedPosition); + } + + double getPlayPos() const { + return m_playPos; + } + int getPlayPosVSample() const { + return m_playPosVSample; + } + int getTotalVSample() const { + return m_totalVSamples; + } + double getZoomFactor() const { + return m_zoomFactor; + } + double getGain() const { + return m_gain; + } + double getTrackSamples() const { + return m_trackSamples; + } + + int getBeatGridAlpha() const { + return m_alphaBeatGrid; + } + + void resize(int width, int height, float devicePixelRatio); + int getHeight() const { + return m_height; + } + int getWidth() const { + return m_width; + } + float getDevicePixelRatio() const { + return m_devicePixelRatio; + } + int getLength() const { + return m_orientation == Qt::Horizontal ? m_width : m_height; + } + int getBreadth() const { + return m_orientation == Qt::Horizontal ? m_height : m_width; + } + Qt::Orientation getOrientation() const { + return m_orientation; + } + int getDimBrightThreshold() { + return m_dimBrightThreshold; + } + + void setTrack(TrackPointer track); + + protected: + const QString m_group; + TrackPointer m_pTrack; + Qt::Orientation m_orientation; + int m_dimBrightThreshold; + int m_height; + int m_width; + float m_devicePixelRatio; + + double m_firstDisplayedPosition; + double m_lastDisplayedPosition; + double m_trackPixelCount; + + double m_zoomFactor; + double m_visualSamplePerPixel; + double m_audioSamplePerPixel; + double m_audioVisualRatio; + + int m_alphaBeatGrid; + + // TODO: vRince create some class to manage control/value + // ControlConnection + QSharedPointer m_visualPlayPosition; + int m_playPosVSample; + int m_totalVSamples; + ControlProxy* m_pRateRatioCO; + double m_rateRatio; + ControlProxy* m_pGainControlObject; + double m_gain; + ControlProxy* m_pTrackSamplesControlObject; + double m_trackSamples; + double m_scaleFactor; + double m_playMarkerPosition; // 0.0 - left, 0.5 - center, 1.0 - right + + private: + DISALLOW_COPY_AND_ASSIGN(WaveformDisplayRange); + double m_playPos; + double m_truePosSample; +}; diff --git a/src/waveform/renderers/waveformrendererabstract.h b/src/waveform/renderers/waveformrendererabstract.h index 6c15dd523d5d..9d4f84a629e1 100644 --- a/src/waveform/renderers/waveformrendererabstract.h +++ b/src/waveform/renderers/waveformrendererabstract.h @@ -6,13 +6,13 @@ QT_FORWARD_DECLARE_CLASS(QDomNode) QT_FORWARD_DECLARE_CLASS(QPaintEvent) QT_FORWARD_DECLARE_CLASS(QPainter) +namespace rendergraph { +class Node; +} + class SkinContext; class WaveformWidgetRenderer; -namespace allshader { -class WaveformRendererAbstract; -} - class WaveformRendererAbstract { public: /// The type of cursor for which the waveform is rendered @@ -32,9 +32,6 @@ class WaveformRendererAbstract { virtual void onResize() {} virtual void onSetTrack() {} - virtual allshader::WaveformRendererAbstract* allshaderWaveformRenderer() { - return nullptr; - } protected: bool isDirty() const { diff --git a/src/waveform/renderers/waveformwidgetrenderer.cpp b/src/waveform/renderers/waveformwidgetrenderer.cpp index ffefe5d6d455..d087b6d25bb0 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.cpp +++ b/src/waveform/renderers/waveformwidgetrenderer.cpp @@ -8,6 +8,7 @@ #include "util/math.h" #include "waveform/renderers/waveformrendererabstract.h" #include "waveform/visualplayposition.h" +#include "waveform/vsyncthread.h" #include "waveform/waveform.h" const double WaveformWidgetRenderer::s_waveformMinZoom = 1.0; @@ -40,7 +41,7 @@ WaveformWidgetRenderer::WaveformWidgetRenderer(const QString& group) 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_passthroughEnabled(false) { @@ -419,7 +420,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/visualplayposition.cpp b/src/waveform/visualplayposition.cpp index bb7cfc9ea930..19fa476376c4 100644 --- a/src/waveform/visualplayposition.cpp +++ b/src/waveform/visualplayposition.cpp @@ -3,6 +3,7 @@ #include "moc_visualplayposition.cpp" #include "util/cmdlineargs.h" #include "util/math.h" +#include "waveform/isynctimeprovider.h" #include "waveform/vsyncthread.h" //static @@ -57,7 +58,7 @@ void VisualPlayPosition::set( } double VisualPlayPosition::calcOffsetAtNextVSync( - VSyncThread* pVSyncThread, const VisualPlayPositionData& data) { + ISyncTimeProvider* pSyncTimeProvider, const VisualPlayPositionData& data) { if (data.m_audioBufferMicroS != 0.0) { int refToVSync; int syncIntervalTimeMicros; @@ -68,8 +69,8 @@ double VisualPlayPosition::calcOffsetAtNextVSync( } else #endif { - refToVSync = pVSyncThread->fromTimerToNextSyncMicros(data.m_referenceTime); - syncIntervalTimeMicros = pVSyncThread->getSyncIntervalTimeMicros(); + refToVSync = pSyncTimeProvider->fromTimerToNextSyncMicros(data.m_referenceTime); + syncIntervalTimeMicros = pSyncTimeProvider->getSyncIntervalTimeMicros(); } // The positive offset is limited to the audio buffer + 2 x waveform sync interval // This should be sufficient to compensate jitter, but does not continue @@ -161,22 +162,23 @@ double VisualPlayPosition::determinePlayPosInLoopBoundries( return interpolatedPlayPos; } -double VisualPlayPosition::getAtNextVSync(VSyncThread* pVSyncThread) { +double VisualPlayPosition::getAtNextVSync(ISyncTimeProvider* pSyncTimeProvider) { if (m_valid) { const VisualPlayPositionData data = m_data.getValue(); - const double offset = calcOffsetAtNextVSync(pVSyncThread, data); + const double offset = calcOffsetAtNextVSync(pSyncTimeProvider, data); return determinePlayPosInLoopBoundries(data, offset); } return -1; } -void VisualPlayPosition::getPlaySlipAtNextVSync(VSyncThread* pVSyncThread, +void VisualPlayPosition::getPlaySlipAtNextVSync( + ISyncTimeProvider* pSyncTimeProvider, double* pPlayPosition, double* pSlipPosition) { if (m_valid) { const VisualPlayPositionData data = m_data.getValue(); - const double offset = calcOffsetAtNextVSync(pVSyncThread, data); + const double offset = calcOffsetAtNextVSync(pSyncTimeProvider, data); double interpolatedPlayPos = determinePlayPosInLoopBoundries(data, offset); *pPlayPosition = interpolatedPlayPos; diff --git a/src/waveform/visualplayposition.h b/src/waveform/visualplayposition.h index 71532d07d2f8..f0d9329d4941 100644 --- a/src/waveform/visualplayposition.h +++ b/src/waveform/visualplayposition.h @@ -9,7 +9,7 @@ #include "util/performancetimer.h" class ControlProxy; -class VSyncThread; +class ISyncTimeProvider; // This class is for synchronizing the sound device DAC time with the waveforms, displayed on the // graphic device, using the CPU time @@ -67,8 +67,8 @@ class VisualPlayPosition : public QObject { double tempoTrackSeconds, double audioBufferMicroS); - double getAtNextVSync(VSyncThread* pVSyncThread); - void getPlaySlipAtNextVSync(VSyncThread* pVSyncThread, + double getAtNextVSync(ISyncTimeProvider* pSyncTimeProvider); + void getPlaySlipAtNextVSync(ISyncTimeProvider* pSyncTimeProvider, double* playPosition, double* slipPosition); double determinePlayPosInLoopBoundries( @@ -89,7 +89,8 @@ class VisualPlayPosition : public QObject { } private: - double calcOffsetAtNextVSync(VSyncThread* pVSyncThread, const VisualPlayPositionData& data); + double calcOffsetAtNextVSync(ISyncTimeProvider* pSyncTimeProvider, + const VisualPlayPositionData& data); ControlValueAtomic m_data; bool m_valid; QString m_key; diff --git a/src/waveform/vsyncthread.h b/src/waveform/vsyncthread.h index 48ded9f5f089..60b8fbfaa510 100644 --- a/src/waveform/vsyncthread.h +++ b/src/waveform/vsyncthread.h @@ -6,10 +6,11 @@ #include #include "util/performancetimer.h" +#include "waveform/isynctimeprovider.h" class WGLWidget; -class VSyncThread : public QThread { +class VSyncThread : public QThread, public ISyncTimeProvider { Q_OBJECT public: enum VSyncMode { @@ -26,22 +27,25 @@ class VSyncThread : public QThread { VSyncThread(QObject* pParent, VSyncMode vSyncMode); ~VSyncThread(); - void run(); + void run() override; bool waitForVideoSync(WGLWidget* glw); int elapsed(); void setSyncIntervalTimeMicros(int usSyncTimer); int droppedFrames(); void setSwapWait(int sw); - int fromTimerToNextSyncMicros(const PerformanceTimer& timer); + + // ISyncTimerProvider + int fromTimerToNextSyncMicros(const PerformanceTimer& timer) override; + int getSyncIntervalTimeMicros() const override { + return m_syncIntervalTimeMicros; + } + void vsyncSlotFinished(); void getAvailableVSyncTypes(QList>* list); void setupSync(WGLWidget* glw, int index); void waitUntilSwap(WGLWidget* glw); mixxx::Duration sinceLastSwap() const; - int getSyncIntervalTimeMicros() const { - return m_syncIntervalTimeMicros; - } void updatePLL(); bool pllInitializing() const; VSyncMode vsyncMode() const { diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index 2b507ff7afc4..ef86bc981ac0 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -3,10 +3,11 @@ #include #include +#include +#include "rendergraph/shadercache.h" #include "waveform/renderers/allshader/waveformrenderbackground.h" #include "waveform/renderers/allshader/waveformrenderbeat.h" -#include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/allshader/waveformrendererendoftrack.h" #include "waveform/renderers/allshader/waveformrendererfiltered.h" #include "waveform/renderers/allshader/waveformrendererhsv.h" @@ -20,6 +21,15 @@ #include "waveform/renderers/allshader/waveformrendermarkrange.h" #include "waveform/widgets/allshader/moc_waveformwidget.cpp" +namespace { +void appendChildTo(std::unique_ptr& pNode, rendergraph::Node* pChild) { + pNode->appendChildNode(std::unique_ptr(pChild)); +} +void appendChildTo(std::unique_ptr& pNode, rendergraph::Node* pChild) { + pNode->appendChildNode(std::unique_ptr(pChild)); +} +} // namespace + namespace allshader { WaveformWidget::WaveformWidget(QWidget* parent, @@ -27,47 +37,67 @@ WaveformWidget::WaveformWidget(QWidget* parent, const QString& group, WaveformRendererSignalBase::Options options) : WGLWidget(parent), WaveformWidgetAbstract(group) { - addRenderer(); - addRenderer(); - addRenderer(); - addRenderer(); + auto pTopNode = std::make_unique(); + auto pOpacityNode = std::make_unique(); + + appendChildTo(pTopNode, addRenderer()); + appendChildTo(pOpacityNode, addRenderer()); + appendChildTo(pOpacityNode, addRenderer()); + m_pWaveformRenderMarkRange = addRenderer(); + appendChildTo(pOpacityNode, m_pWaveformRenderMarkRange); #ifdef __STEM__ // The following two renderers work in tandem: if the rendered waveform is // for a stem track, WaveformRendererSignalBase will skip rendering and let // WaveformRendererStem do the rendering, and vice-versa. - addRenderer(); + appendChildTo(pOpacityNode, addRenderer()); #endif allshader::WaveformRendererSignalBase* waveformSignalRenderer = addWaveformSignalRenderer( type, options, ::WaveformRendererAbstract::Play); + appendChildTo(pOpacityNode, waveformSignalRenderer); - addRenderer(); - addRenderer(); + appendChildTo(pOpacityNode, addRenderer()); + m_pWaveformRenderMark = addRenderer(); + appendChildTo(pOpacityNode, m_pWaveformRenderMark); // if the signal renderer supports slip, we add it again, now for slip, together with the // other slip renderers if (waveformSignalRenderer && waveformSignalRenderer->supportsSlip()) { // The following renderer will add an overlay waveform if a slip is in progress - addRenderer(); - addRenderer(::WaveformRendererAbstract::Slip); + appendChildTo(pOpacityNode, addRenderer()); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); #ifdef __STEM__ - addRenderer(::WaveformRendererAbstract::Slip); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); #endif - addWaveformSignalRenderer(type, options, ::WaveformRendererAbstract::Slip); - addRenderer(::WaveformRendererAbstract::Slip); - addRenderer(::WaveformRendererAbstract::Slip); + appendChildTo(pOpacityNode, + addWaveformSignalRenderer( + type, options, ::WaveformRendererAbstract::Slip)); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); + appendChildTo(pOpacityNode, + addRenderer( + ::WaveformRendererAbstract::Slip)); } m_initSuccess = init(); + + m_pOpacityNode = pOpacityNode.get(); + pTopNode->appendChildNode(std::move(pOpacityNode)); + + m_pGraph = std::make_unique(std::move(pTopNode)); } WaveformWidget::~WaveformWidget() { makeCurrentIfNeeded(); - for (auto* pRenderer : std::as_const(m_rendererStack)) { - delete pRenderer; - } m_rendererStack.clear(); + m_pGraph.reset(); + rendergraph::ShaderCache::purge(); doneCurrent(); } @@ -117,15 +147,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_pGraph->preprocess(); + m_pGraph->render(); } void WaveformWidget::castToQWidget() { @@ -133,15 +162,11 @@ void WaveformWidget::castToQWidget() { } void WaveformWidget::initializeGL() { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->initializeGL(); - } + m_pGraph->initialize(); } void WaveformWidget::resizeGL(int w, int h) { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->resizeGL(w, h); - } + m_pGraph->resize(w, h); } void WaveformWidget::paintEvent(QPaintEvent* event) { diff --git a/src/waveform/widgets/allshader/waveformwidget.h b/src/waveform/widgets/allshader/waveformwidget.h index f6c0877975e0..2ee2a6f1dae4 100644 --- a/src/waveform/widgets/allshader/waveformwidget.h +++ b/src/waveform/widgets/allshader/waveformwidget.h @@ -1,5 +1,7 @@ #pragma once +#include "rendergraph/graph.h" +#include "rendergraph/opacitynode.h" #include "waveform/renderers/allshader/waveformrenderersignalbase.h" #include "waveform/widgets/waveformwidgetabstract.h" #include "waveform/widgets/waveformwidgetvars.h" @@ -7,6 +9,8 @@ namespace allshader { class WaveformWidget; +class WaveformRenderMark; +class WaveformRenderMarkRange; } class allshader::WaveformWidget final : public ::WGLWidget, @@ -48,6 +52,10 @@ class allshader::WaveformWidget final : public ::WGLWidget, ::WaveformRendererAbstract::PositionSource positionSource); WaveformWidgetType::Type m_type; + std::unique_ptr m_pGraph; + rendergraph::OpacityNode* m_pOpacityNode; + WaveformRenderMark* m_pWaveformRenderMark; + WaveformRenderMarkRange* m_pWaveformRenderMarkRange; DISALLOW_COPY_AND_ASSIGN(WaveformWidget); }; diff --git a/src/widget/wspinnybase.cpp b/src/widget/wspinnybase.cpp index a0baf58f70e1..14414fecf32a 100644 --- a/src/widget/wspinnybase.cpp +++ b/src/widget/wspinnybase.cpp @@ -15,6 +15,7 @@ #include "util/fpclassify.h" #include "vinylcontrol/vinylcontrolmanager.h" #include "waveform/visualplayposition.h" +#include "waveform/vsyncthread.h" #include "wimagestore.h" // The SampleBuffers format enables antialiasing.