From c8f4e53244572ef9ce7142e84f494c4ee2e01140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Thu, 14 Jan 2021 22:02:34 +0100 Subject: [PATCH] ShaderNode: Add code output window --- src/ShaderNode/ShaderGraph.cpp | 59 +++++++- src/ShaderNode/ShaderGraph.hpp | 6 +- src/ShaderNode/Widgets/CodeOutputWidget.cpp | 105 ++++++++++++++ src/ShaderNode/Widgets/CodeOutputWidget.hpp | 32 +++++ src/ShaderNode/Widgets/CodeOutputWidget.inl | 1 + src/ShaderNode/Widgets/MainWindow.cpp | 145 ++++++-------------- src/ShaderNode/Widgets/MainWindow.hpp | 4 - 7 files changed, 245 insertions(+), 107 deletions(-) create mode 100644 src/ShaderNode/Widgets/CodeOutputWidget.cpp create mode 100644 src/ShaderNode/Widgets/CodeOutputWidget.hpp create mode 100644 src/ShaderNode/Widgets/CodeOutputWidget.inl diff --git a/src/ShaderNode/ShaderGraph.cpp b/src/ShaderNode/ShaderGraph.cpp index 21be5ec9a..4900c5893 100644 --- a/src/ShaderNode/ShaderGraph.cpp +++ b/src/ShaderNode/ShaderGraph.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -62,6 +63,7 @@ m_type(ShaderType::NotSet) }); // Test + m_type = ShaderType::Fragment; AddInput("UV", PrimitiveType::Float2, InputRole::TexCoord, 0, 0); AddOutput("RenderTarget0", PrimitiveType::Float4, 0); AddTexture("Potato", TextureType::Sampler2D, 1); @@ -440,7 +442,7 @@ QJsonObject ShaderGraph::Save() return sceneJson; } -Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst() +Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst() const { std::vector statements; @@ -580,6 +582,61 @@ Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst() return Nz::ShaderNodes::StatementBlock::Build(std::move(statements)); } +Nz::ShaderAst ShaderGraph::ToShader() const +{ + Nz::ShaderAst shader(ShaderGraph::ToShaderStageType(m_type)); //< FIXME + for (const auto& condition : m_conditions) + shader.AddCondition(condition.name); + + for (const auto& input : m_inputs) + shader.AddInput(input.name, ToShaderExpressionType(input.type), input.locationIndex); + + for (const auto& output : m_outputs) + shader.AddOutput(output.name, ToShaderExpressionType(output.type), output.locationIndex); + + for (const auto& buffer : m_buffers) + { + if (buffer.structIndex >= m_structs.size()) + throw std::runtime_error("buffer " + buffer.name + " references out-of-bounds struct #" + std::to_string(buffer.structIndex)); + + const auto& structInfo = m_structs[buffer.structIndex]; + shader.AddUniform(buffer.name, structInfo.name, buffer.bindingIndex, Nz::ShaderNodes::MemoryLayout::Std140); + } + + for (const auto& uniform : m_textures) + shader.AddUniform(uniform.name, ToShaderExpressionType(uniform.type), uniform.bindingIndex, {}); + + for (const auto& s : m_structs) + { + std::vector members; + for (const auto& sMember : s.members) + { + auto& member = members.emplace_back(); + member.name = sMember.name; + + std::visit([&](auto&& arg) + { + using T = std::decay_t; + if constexpr (std::is_same_v) + member.type = ToShaderExpressionType(arg); + else if constexpr (std::is_same_v) + { + if (arg >= m_structs.size()) + throw std::runtime_error("struct " + s.name + " references out-of-bounds struct #" + std::to_string(arg)); + + member.type = m_structs[arg].name; + } + else + static_assert(AlwaysFalse::value, "non-exhaustive visitor"); + }, sMember.type); + } + + shader.AddStruct(s.name, std::move(members)); + } + + return shader; +} + Nz::ShaderExpressionType ShaderGraph::ToShaderExpressionType(const std::variant& type) const { return std::visit([&](auto&& arg) -> Nz::ShaderExpressionType diff --git a/src/ShaderNode/ShaderGraph.hpp b/src/ShaderNode/ShaderGraph.hpp index 44fb4bf77..1ca776305 100644 --- a/src/ShaderNode/ShaderGraph.hpp +++ b/src/ShaderNode/ShaderGraph.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -66,7 +67,8 @@ class ShaderGraph void Load(const QJsonObject& data); QJsonObject Save(); - Nz::ShaderNodes::StatementPtr ToAst(); + Nz::ShaderNodes::StatementPtr ToAst() const; + Nz::ShaderAst ToShader() const; Nz::ShaderExpressionType ToShaderExpressionType(const std::variant& type) const; void UpdateBuffer(std::size_t bufferIndex, std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex); @@ -152,7 +154,7 @@ class ShaderGraph private: std::shared_ptr BuildRegistry(); - QtNodes::FlowScene m_flowScene; + mutable QtNodes::FlowScene m_flowScene; std::vector m_buffers; std::vector m_conditions; std::vector m_inputs; diff --git a/src/ShaderNode/Widgets/CodeOutputWidget.cpp b/src/ShaderNode/Widgets/CodeOutputWidget.cpp new file mode 100644 index 000000000..73e9c7d3f --- /dev/null +++ b/src/ShaderNode/Widgets/CodeOutputWidget.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum class OutputLanguage +{ + GLSL, + SpirV +}; + +CodeOutputWidget::CodeOutputWidget(const ShaderGraph& shaderGraph) : +m_shaderGraph(shaderGraph) +{ + m_textOutput = new QTextEdit; + + m_outputLang = new QComboBox; + m_outputLang->addItem("GLSL", int(OutputLanguage::GLSL)); + m_outputLang->addItem("SPIR-V", int(OutputLanguage::SpirV)); + connect(m_outputLang, qOverload(&QComboBox::currentIndexChanged), [this](int) + { + Refresh(); + }); + + m_optimisationCheckbox = new QCheckBox; + m_optimisationCheckbox->setText("Enable optimisation"); + connect(m_optimisationCheckbox, &QCheckBox::stateChanged, [this](int) + { + Refresh(); + }); + + QVBoxLayout* verticalLayout = new QVBoxLayout; + verticalLayout->addWidget(m_textOutput); + verticalLayout->addWidget(m_outputLang); + verticalLayout->addWidget(m_optimisationCheckbox); + + setLayout(verticalLayout); + + Refresh(); +} + +void CodeOutputWidget::Refresh() +{ + try + { + Nz::UInt64 enabledConditions = 0; + for (std::size_t i = 0; i < m_shaderGraph.GetConditionCount(); ++i) + { + if (m_shaderGraph.IsConditionEnabled(i)) + enabledConditions = Nz::SetBit(enabledConditions, i); + } + + Nz::ShaderAst shaderAst = m_shaderGraph.ToShader(); + + Nz::ShaderNodes::StatementPtr mainAst = m_shaderGraph.ToAst(); + if (m_optimisationCheckbox->isChecked()) + { + Nz::ShaderAstOptimizer optimiser; + mainAst = optimiser.Optimise(mainAst, shaderAst, enabledConditions); + } + + shaderAst.AddFunction("main", mainAst); + + Nz::ShaderWriter::States states; + states.enabledConditions = enabledConditions; + + std::string output; + OutputLanguage outputLang = static_cast(m_outputLang->currentIndex()); + switch (outputLang) + { + case OutputLanguage::GLSL: + { + Nz::GlslWriter writer; + output = writer.Generate(shaderAst, states); + break; + } + + case OutputLanguage::SpirV: + { + Nz::SpirvWriter writer; + std::vector spirv = writer.Generate(shaderAst, states); + + Nz::SpirvPrinter printer; + output = printer.Print(spirv.data(), spirv.size()); + break; + } + } + + int scrollValue = m_textOutput->verticalScrollBar()->value(); + m_textOutput->setText(QString::fromStdString(output)); + m_textOutput->verticalScrollBar()->setValue(scrollValue); + } + catch (const std::exception& e) + { + m_textOutput->setText("Generation failed: " + QString::fromStdString(e.what())); + } +} diff --git a/src/ShaderNode/Widgets/CodeOutputWidget.hpp b/src/ShaderNode/Widgets/CodeOutputWidget.hpp new file mode 100644 index 000000000..ecea4d54b --- /dev/null +++ b/src/ShaderNode/Widgets/CodeOutputWidget.hpp @@ -0,0 +1,32 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_CODEOUTPUTWIDGET_HPP +#define NAZARA_SHADERNODES_CODEOUTPUTWIDGET_HPP + +#include +#include +#include + +class QCheckBox; +class QComboBox; +class QTextEdit; +class ShaderGraph; + +class CodeOutputWidget : public QWidget +{ + public: + CodeOutputWidget(const ShaderGraph& shaderGraph); + ~CodeOutputWidget() = default; + + void Refresh(); + + private: + const ShaderGraph& m_shaderGraph; + QCheckBox* m_optimisationCheckbox; + QComboBox* m_outputLang; + QTextEdit* m_textOutput; +}; + +#include + +#endif diff --git a/src/ShaderNode/Widgets/CodeOutputWidget.inl b/src/ShaderNode/Widgets/CodeOutputWidget.inl new file mode 100644 index 000000000..26a824b63 --- /dev/null +++ b/src/ShaderNode/Widgets/CodeOutputWidget.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/Widgets/MainWindow.cpp b/src/ShaderNode/Widgets/MainWindow.cpp index aa01c3c97..c131a123c 100644 --- a/src/ShaderNode/Widgets/MainWindow.cpp +++ b/src/ShaderNode/Widgets/MainWindow.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +37,6 @@ m_shaderGraph(graph) InputEditor* inputEditor = new InputEditor(m_shaderGraph); QDockWidget* inputDock = new QDockWidget(tr("Inputs")); - inputDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); inputDock->setWidget(inputEditor); addDockWidget(Qt::LeftDockWidgetArea, inputDock); @@ -44,7 +45,6 @@ m_shaderGraph(graph) OutputEditor* outputEditor = new OutputEditor(m_shaderGraph); QDockWidget* outputDock = new QDockWidget(tr("Outputs")); - outputDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); outputDock->setWidget(outputEditor); addDockWidget(Qt::LeftDockWidgetArea, outputDock); @@ -53,7 +53,6 @@ m_shaderGraph(graph) TextureEditor* textureEditor = new TextureEditor(m_shaderGraph); QDockWidget* textureDock = new QDockWidget(tr("Textures")); - textureDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); textureDock->setWidget(textureEditor); addDockWidget(Qt::LeftDockWidgetArea, textureDock); @@ -62,7 +61,6 @@ m_shaderGraph(graph) m_nodeEditor = new NodeEditor; QDockWidget* nodeEditorDock = new QDockWidget(tr("Node editor")); - nodeEditorDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); nodeEditorDock->setWidget(m_nodeEditor); addDockWidget(Qt::RightDockWidgetArea, nodeEditorDock); @@ -71,7 +69,6 @@ m_shaderGraph(graph) BufferEditor* bufferEditor = new BufferEditor(m_shaderGraph); QDockWidget* bufferDock = new QDockWidget(tr("Buffers")); - bufferDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); bufferDock->setWidget(bufferEditor); addDockWidget(Qt::RightDockWidgetArea, bufferDock); @@ -80,7 +77,6 @@ m_shaderGraph(graph) StructEditor* structEditor = new StructEditor(m_shaderGraph); QDockWidget* structDock = new QDockWidget(tr("Structs")); - structDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); structDock->setWidget(structEditor); addDockWidget(Qt::RightDockWidgetArea, structDock); @@ -89,11 +85,18 @@ m_shaderGraph(graph) ConditionEditor* conditionEditor = new ConditionEditor(m_shaderGraph); QDockWidget* conditionDock = new QDockWidget(tr("Conditions")); - conditionDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); conditionDock->setWidget(conditionEditor); addDockWidget(Qt::RightDockWidgetArea, conditionDock); + // Code output + CodeOutputWidget* codeOutput = new CodeOutputWidget(m_shaderGraph); + + QDockWidget* codeOutputDock = new QDockWidget(tr("Code output")); + codeOutputDock->setWidget(codeOutput); + + addDockWidget(Qt::BottomDockWidgetArea, codeOutputDock); + m_onSelectedNodeUpdate.Connect(m_shaderGraph.OnSelectedNodeUpdate, [&](ShaderGraph*, ShaderNode* node) { if (node) @@ -109,21 +112,46 @@ m_shaderGraph(graph) BuildMenu(); - - m_codeOutput = new QTextEdit; - m_codeOutput->setReadOnly(true); - m_codeOutput->setWindowTitle("GLSL Output"); - - m_onConditionUpdate.Connect(m_shaderGraph.OnConditionUpdate, [&](ShaderGraph*, std::size_t conditionIndex) { - if (m_codeOutput->isVisible()) - OnGenerateGLSL(); + QMenu* view = menuBar()->addMenu("View"); + view->addAction(inputDock->toggleViewAction()); + view->addAction(outputDock->toggleViewAction()); + view->addAction(textureDock->toggleViewAction()); + view->addAction(nodeEditorDock->toggleViewAction()); + view->addAction(bufferDock->toggleViewAction()); + view->addAction(structDock->toggleViewAction()); + view->addAction(conditionDock->toggleViewAction()); + view->addAction(codeOutputDock->toggleViewAction()); + } + + connect(scene, &QtNodes::FlowScene::connectionCreated, [=](const QtNodes::Connection& /*connection*/) + { + QTimer::singleShot(0, [=] + { + if (codeOutput->isVisible()) + codeOutput->Refresh(); + }); + }); + + connect(scene, &QtNodes::FlowScene::connectionDeleted, [=](const QtNodes::Connection& /*connection*/) + { + QTimer::singleShot(0, [=] + { + if (codeOutput->isVisible()) + codeOutput->Refresh(); + }); + }); + + m_onConditionUpdate.Connect(m_shaderGraph.OnConditionUpdate, [=](ShaderGraph*, std::size_t /*conditionIndex*/) + { + if (codeOutput->isVisible()) + codeOutput->Refresh(); }); } MainWindow::~MainWindow() { - delete m_codeOutput; + m_shaderGraph.Clear(); } void MainWindow::BuildMenu() @@ -147,19 +175,13 @@ void MainWindow::BuildMenu() QAction* compileShader = shader->addAction(tr("Compile...")); QObject::connect(compileShader, &QAction::triggered, this, &MainWindow::OnCompile); } - - QMenu* generateMenu = menu->addMenu(tr("&Generate")); - { - QAction* generateGlsl = generateMenu->addAction(tr("GLSL")); - connect(generateGlsl, &QAction::triggered, [&](bool) { OnGenerateGLSL(); }); - } } void MainWindow::OnCompile() { try { - auto shader = ToShader(); + auto shader = m_shaderGraph.ToShader(); QString fileName = QFileDialog::getSaveFileName(nullptr, tr("Save shader"), QString(), tr("Shader Files (*.shader)")); if (fileName.isEmpty()) @@ -177,32 +199,6 @@ void MainWindow::OnCompile() } } -void MainWindow::OnGenerateGLSL() -{ - try - { - Nz::GlslWriter writer; - - Nz::GlslWriter::States states; - for (std::size_t i = 0; i < m_shaderGraph.GetConditionCount(); ++i) - { - if (m_shaderGraph.IsConditionEnabled(i)) - states.enabledConditions = Nz::SetBit(states.enabledConditions, i); - } - - std::string glsl = writer.Generate(ToShader(), states); - - std::cout << glsl << std::endl; - - m_codeOutput->setText(QString::fromStdString(glsl)); - m_codeOutput->show(); - } - catch (const std::exception& e) - { - QMessageBox::critical(this, tr("Generation failed"), QString("Generation failed: ") + e.what()); - } -} - void MainWindow::OnLoad() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open shader flow"), QString(), tr("Shader Flow Files (*.shaderflow)")); @@ -263,54 +259,3 @@ void MainWindow::OnUpdateInfo() dialog->open(); } - -Nz::ShaderAst MainWindow::ToShader() -{ - Nz::ShaderNodes::StatementPtr shaderAst = m_shaderGraph.ToAst(); - - Nz::ShaderAst shader(ShaderGraph::ToShaderStageType(m_shaderGraph.GetType())); //< FIXME - for (const auto& condition : m_shaderGraph.GetConditions()) - shader.AddCondition(condition.name); - - for (const auto& input : m_shaderGraph.GetInputs()) - shader.AddInput(input.name, m_shaderGraph.ToShaderExpressionType(input.type), input.locationIndex); - - for (const auto& output : m_shaderGraph.GetOutputs()) - shader.AddOutput(output.name, m_shaderGraph.ToShaderExpressionType(output.type), output.locationIndex); - - for (const auto& buffer : m_shaderGraph.GetBuffers()) - { - const auto& structInfo = m_shaderGraph.GetStruct(buffer.structIndex); - shader.AddUniform(buffer.name, structInfo.name, buffer.bindingIndex, Nz::ShaderNodes::MemoryLayout::Std140); - } - - for (const auto& uniform : m_shaderGraph.GetTextures()) - shader.AddUniform(uniform.name, m_shaderGraph.ToShaderExpressionType(uniform.type), uniform.bindingIndex, {}); - - for (const auto& s : m_shaderGraph.GetStructs()) - { - std::vector members; - for (const auto& sMember : s.members) - { - auto& member = members.emplace_back(); - member.name = sMember.name; - - std::visit([&](auto&& arg) - { - using T = std::decay_t; - if constexpr (std::is_same_v) - member.type = m_shaderGraph.ToShaderExpressionType(arg); - else if constexpr (std::is_same_v) - member.type = m_shaderGraph.GetStruct(arg).name; - else - static_assert(AlwaysFalse::value, "non-exhaustive visitor"); - }, sMember.type); - } - - shader.AddStruct(s.name, std::move(members)); - } - - shader.AddFunction("main", shaderAst); - - return shader; -} diff --git a/src/ShaderNode/Widgets/MainWindow.hpp b/src/ShaderNode/Widgets/MainWindow.hpp index 10d925190..1031eca9a 100644 --- a/src/ShaderNode/Widgets/MainWindow.hpp +++ b/src/ShaderNode/Widgets/MainWindow.hpp @@ -8,7 +8,6 @@ #include class NodeEditor; -class QTextEdit; namespace Nz { @@ -24,18 +23,15 @@ class MainWindow : public QMainWindow private: void BuildMenu(); void OnCompile(); - void OnGenerateGLSL(); void OnLoad(); void OnSave(); void OnUpdateInfo(); - Nz::ShaderAst ToShader(); NazaraSlot(ShaderGraph, OnConditionUpdate, m_onConditionUpdate); NazaraSlot(ShaderGraph, OnSelectedNodeUpdate, m_onSelectedNodeUpdate); NodeEditor* m_nodeEditor; ShaderGraph& m_shaderGraph; - QTextEdit* m_codeOutput; }; #include