├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── FindSPIRV-Headers.cmake └── FindSPIRV-Tools.cmake ├── gui ├── include │ └── proto │ │ ├── AssemblyTextView.h │ │ ├── ComboBox.h │ │ ├── EditorGraph.h │ │ ├── FundamentalTypeComboBox.h │ │ ├── Logger.h │ │ ├── OpNodeExpr.h │ │ ├── ProtoWindow.h │ │ ├── TypedDataInput.h │ │ ├── Validator.h │ │ ├── WinHeapDbg.h │ │ └── Window.h └── source │ ├── AssemblyTextView.cpp │ ├── EditorGraph.cpp │ ├── FundamentalTypeComboBox.cpp │ ├── Logger.cpp │ ├── OpNodeExpr.cpp │ ├── ProtoWindow.cpp │ ├── TypedDataInput.cpp │ ├── Validator.cpp │ ├── WinHeapDbg.cpp │ ├── Window.cpp │ └── main.cpp └── proto.png /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "SpvGenTwo"] 2 | path = SpvGenTwo 3 | url = git@github.com:rAzoR8/SpvGenTwo.git 4 | [submodule "thirdparty/imgui"] 5 | path = thirdparty/imgui 6 | url = https://github.com/ocornut/imgui.git 7 | [submodule "thirdparty/glfw"] 8 | path = thirdparty/glfw 9 | url = https://github.com/glfw/glfw.git 10 | [submodule "thirdparty/ImNodes"] 11 | path = thirdparty/ImNodes 12 | url = git@github.com:rAzoR8/ImNodes.git 13 | [submodule "thirdparty/SPIRV-Tools"] 14 | path = thirdparty/SPIRV-Tools 15 | url = https://github.com/KhronosGroup/SPIRV-Tools.git 16 | [submodule "thirdparty/SPIRV-Headers"] 17 | path = thirdparty/SPIRV-Headers 18 | url = https://github.com/KhronosGroup/SPIRV-Headers.git 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12.0 FATAL_ERROR) 2 | 3 | macro(add_sources expression sources) 4 | file(GLOB source_files RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "${expression}") 5 | set(${sources} "${${sources}};${source_files}") 6 | endmacro() 7 | 8 | macro(add_include_folder include_folder includes) 9 | set(${includes} "${${includes}};${CMAKE_CURRENT_LIST_DIR}/${include_folder}") 10 | endmacro() 11 | 12 | function(cmake_option option description default) 13 | option(${option} description ${default}) 14 | if(${option}) 15 | message(STATUS "'${option}' is TRUE") 16 | add_definitions(-D"${option}"=1) 17 | else() 18 | message(STATUS "'${option}' is FALSE") 19 | endif() 20 | endfunction() 21 | 22 | set(CMAKE_CXX_STANDARD 17) 23 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 24 | 25 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) 26 | 27 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 28 | 29 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") 30 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") 31 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 32 | 33 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 34 | 35 | #this project 36 | project(Proto) 37 | 38 | #dependencies 39 | find_package(Vulkan REQUIRED) 40 | find_package(SPIRV-Headers REQUIRED) 41 | 42 | #glfw 43 | option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" OFF) 44 | option(GLFW_BUILD_TESTS "Build the GLFW test programs" OFF) 45 | option(GLFW_BUILD_DOCS "Build the GLFW documentation" OFF) 46 | option(GLFW_INSTALL "Generate installation target" OFF) 47 | option(GLFW_DOCUMENT_INTERNALS "Include internals in documentation" OFF) 48 | add_subdirectory(thirdparty/glfw) 49 | 50 | #spir-v tools 51 | option(SKIP_SPIRV_TOOLS_INSTALL "Skip installation" OFF) 52 | option(SPIRV_SKIP_TESTS "Skip tests" ON) 53 | 54 | file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/SPIRV-Headers" DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/SPIRV-Tools/external/") 55 | option(SPIRV_HEADERS_SKIP_EXAMPLES "" ON) 56 | option(SPIRV_HEADERS_SKIP_INSTALL "" ON) 57 | add_subdirectory(thirdparty/SPIRV-Tools) 58 | 59 | add_subdirectory(SpvGenTwo) 60 | 61 | #proto sources 62 | add_sources("gui/source/*.cpp" "proto_sources") 63 | add_sources("gui/include/proto/*.h" "proto_sources") 64 | add_include_folder("gui/include" "proto_includes") 65 | 66 | #imgui sources 67 | add_sources("thirdparty/imgui/imgui.cpp" "imgui_sources") 68 | add_sources("thirdparty/imgui/imgui_draw.cpp" "imgui_sources") 69 | add_sources("thirdparty/imgui/imgui_widgets.cpp" "imgui_sources") 70 | add_sources("thirdparty/imgui/imgui_demo.cpp" "imgui_sources") 71 | add_sources("thirdparty/imgui/examples/imgui_impl_vulkan.cpp" "imgui_sources") 72 | add_sources("thirdparty/imgui/examples/imgui_impl_glfw.cpp" "imgui_sources") 73 | add_include_folder("thirdparty/imgui" "imgui_includes") 74 | add_include_folder("thirdparty/imgui/examples" "imgui_includes") 75 | 76 | #imnodes sources 77 | add_sources("thirdparty/ImNodes/ImNodes.cpp" "imgui_sources") 78 | add_sources("thirdparty/ImNodes/ImNodesEz.cpp" "imgui_sources") 79 | add_include_folder("thirdparty/ImNodes" "imgui_includes") 80 | 81 | #lib project 82 | add_executable(proto "${proto_sources};${imgui_sources}") 83 | target_include_directories(proto PUBLIC "${proto_includes};${imgui_includes}") 84 | 85 | #link SpvGenTwo 86 | target_link_libraries(proto PUBLIC SpvGenTwoLib SpvGenTwoCommon) 87 | 88 | #link vulkan 89 | target_include_directories(proto PRIVATE "${Vulkan_INCLUDE_DIR}") 90 | target_link_libraries(proto PRIVATE "${Vulkan_LIBRARIES}") 91 | 92 | #link spirv-tools 93 | target_include_directories(proto PRIVATE "thirdparty/SPIRV-Tools/include") 94 | target_link_libraries(proto PRIVATE SPIRV-Tools) 95 | 96 | #link glfw 97 | target_include_directories(proto PRIVATE "${glfw_INCLUDE_DIRS}") 98 | target_link_libraries(proto PRIVATE glfw) 99 | 100 | message(STATUS "") -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Fabian Wahlster 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Proto 2 | Proof Of Concept SPIR-V Code Generator / Graph Editor GUI 3 | 4 | This is my test playground project for my IR-builder library SpvGenTwo 5 | 6 | ![Proto](proto.png) 7 | 8 | ## Usage 9 | 10 | Use right click context menu to crate new nodes. 11 | Click to select node. 12 | Press DEL to delete a node. 13 | 14 | Code will only be generated if control flow reaches output variables. 15 | 16 | ## Dependencies (Submodules) 17 | * [SpvGenTwo](https://github.com/rAzoR8/SpvGenTwo) 18 | * [ImGUI](https://github.com/ocornut/imgui) 19 | * [ImNodes](https://github.com/rAzoR8/ImNodes) 20 | * [GLFW](https://github.com/glfw/glfw) 21 | * [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools) 22 | * [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) 23 | * [Vulkan](https://www.lunarg.com/vulkan-sdk/) 24 | 25 | ## Building 26 | 27 | Use the supplied CMakeLists.txt to generate project files for your build system. 28 | 29 | # Copyright and Licensing 30 | 31 | The Proto open source project is licensed under [MIT license](LICENSE). 32 | Any contribution you make to this original repository shall be licensed under same license. You are still free to distribute your contributions (in your own fork) under the license you desire. -------------------------------------------------------------------------------- /cmake/FindSPIRV-Headers.cmake: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2 | # file Copyright.txt or https://cmake.org/licensing for details. 3 | 4 | #.rst: 5 | # FindSPIRV-Headers 6 | # ---------- 7 | # 8 | # Try to find SPIRV-Headers 9 | # 10 | # IMPORTED Targets 11 | # ^^^^^^^^^^^^^^^^ 12 | # 13 | # This module defines :prop_tgt:`IMPORTED` target ``SPIRV-Headers::SPIRV-Headers``, if 14 | # SPIRV-Headers has been found. 15 | # 16 | # Result Variables 17 | # ^^^^^^^^^^^^^^^^ 18 | # 19 | # This module defines the following variables:: 20 | # 21 | # SPIRV-Headers_FOUND - True if SPIRV-Headers was found 22 | # SPIRV-Headers_INCLUDE_DIRS - include directories for SPIRV-Headers 23 | # 24 | # The module will also define two cache variables:: 25 | # 26 | # SPIRV-Headers_INCLUDE_DIR - the SPIRV-Headers include directory 27 | # 28 | 29 | find_path(SPIRV-Headers_INCLUDE_DIR 30 | NAMES spirv-headers/spirv.hpp11 31 | PATHS 32 | "$ENV{VULKAN_SDK}/include") 33 | 34 | set(SPIRV-Headers_INCLUDE_DIRS ${SPIRV-Headers_INCLUDE_DIR}) 35 | 36 | #include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) 37 | include(FindPackageHandleStandardArgs) 38 | find_package_handle_standard_args(SPIRV-Headers 39 | DEFAULT_MSG 40 | SPIRV-Headers_INCLUDE_DIR) 41 | 42 | mark_as_advanced(SPIRV-Headers_INCLUDE_DIR) 43 | 44 | if(SPIRV-Headers_FOUND AND NOT TARGET SPIRV-Headers::SPIRV-Headers) 45 | add_library(SPIRV-Headers::SPIRV-Headers UNKNOWN IMPORTED) 46 | set_target_properties(SPIRV-Headers::SPIRV-Headers PROPERTIES 47 | INTERFACE_INCLUDE_DIRECTORIES "${SPIRV-Headers_INCLUDE_DIRS}") 48 | endif() 49 | -------------------------------------------------------------------------------- /cmake/FindSPIRV-Tools.cmake: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2 | # file Copyright.txt or https://cmake.org/licensing for details. 3 | 4 | #.rst: 5 | # FindSPIRV-Tools 6 | # ---------- 7 | # 8 | # Try to find SPIRV-Tools 9 | # 10 | # IMPORTED Targets 11 | # ^^^^^^^^^^^^^^^^ 12 | # 13 | # This module defines :prop_tgt:`IMPORTED` target ``SPIRV-Tools::SPIRV-Tools``, if 14 | # SPIRV-Tools has been found. 15 | # 16 | # Result Variables 17 | # ^^^^^^^^^^^^^^^^ 18 | # 19 | # This module defines the following variables:: 20 | # 21 | # SPIRV-Tools_FOUND - True if SPIRV-Tools was found 22 | # SPIRV-Tools_INCLUDE_DIRS - include directories for SPIRV-Tools 23 | # SPIRV-Tools_LIBRARIES - link against this library to use SPIRV-Tools 24 | # 25 | # The module will also define two cache variables:: 26 | # 27 | # SPIRV-Tools_INCLUDE_DIR - the SPIRV-Tools include directory 28 | # SPIRV-Tools_LIBRARY - the path to the SPIRV-Tools library 29 | # 30 | 31 | if(WIN32) 32 | find_path(SPIRV-Tools_INCLUDE_DIR 33 | NAMES spirv-tools/libspirv.hpp 34 | PATHS 35 | "$ENV{VULKAN_SDK}/Include" 36 | ) 37 | 38 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 39 | find_library(SPIRV-Tools_LIBRARY 40 | NAMES SPIRV-Tools SPIRV-Tools-shared 41 | PATHS 42 | "$ENV{VULKAN_SDK}/Lib" 43 | "$ENV{VULKAN_SDK}/Bin" 44 | ) 45 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) 46 | find_library(SPIRV-Tools_LIBRARY 47 | NAMES SPIRV-Tools SPIRV-Tools-shared 48 | PATHS 49 | "$ENV{VULKAN_SDK}/Lib32" 50 | "$ENV{VULKAN_SDK}/Bin32" 51 | NO_SYSTEM_ENVIRONMENT_PATH 52 | ) 53 | endif() 54 | else() 55 | find_path(SPIRV-Tools_INCLUDE_DIR 56 | NAMES spirv-tools/libspirv.hpp 57 | PATHS 58 | "$ENV{VULKAN_SDK}/include") 59 | find_library(SPIRV-Tools_LIBRARY 60 | NAMES SPIRV-Tools SPIRV-Tools-shared 61 | PATHS 62 | "$ENV{VULKAN_SDK}/lib") 63 | endif() 64 | 65 | set(SPIRV-Tools_LIBRARIES ${SPIRV-Tools_LIBRARY}) 66 | set(SPIRV-Tools_INCLUDE_DIRS ${SPIRV-Tools_INCLUDE_DIR}) 67 | 68 | #include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) 69 | include(FindPackageHandleStandardArgs) 70 | find_package_handle_standard_args(SPIRV-Tools 71 | DEFAULT_MSG 72 | SPIRV-Tools_LIBRARY SPIRV-Tools_INCLUDE_DIR) 73 | 74 | mark_as_advanced(SPIRV-Tools_INCLUDE_DIR SPIRV-Tools_LIBRARY) 75 | 76 | if(SPIRV-Tools_FOUND AND NOT TARGET SPIRV-Tools::SPIRV-Tools) 77 | add_library(SPIRV-Tools::SPIRV-Tools UNKNOWN IMPORTED) 78 | set_target_properties(SPIRV-Tools::SPIRV-Tools PROPERTIES 79 | IMPORTED_LOCATION "${SPIRV-Tools_LIBRARIES}" 80 | INTERFACE_INCLUDE_DIRECTORIES "${SPIRV-Tools_INCLUDE_DIRS}") 81 | endif() 82 | -------------------------------------------------------------------------------- /gui/include/proto/AssemblyTextView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/HeapVector.h" 4 | 5 | // forward delcs 6 | 7 | namespace spvgentwo 8 | { 9 | class Module; 10 | } 11 | 12 | namespace proto 13 | { 14 | class AssemblyTextView 15 | { 16 | public: 17 | AssemblyTextView(); 18 | ~AssemblyTextView(); 19 | 20 | void update(const spvgentwo::HeapVector& _module); 21 | private: 22 | bool m_indent = true; 23 | bool m_offsets = false; 24 | bool m_friendlyNames = true; 25 | bool m_autoScroll = false; 26 | }; 27 | } // !proto -------------------------------------------------------------------------------- /gui/include/proto/ComboBox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/HeapString.h" 4 | #include "common/HeapVector.h" 5 | #include "common/HeapCallable.h" 6 | 7 | namespace proto 8 | { 9 | 10 | namespace detail 11 | { 12 | struct Empty {}; 13 | } 14 | 15 | template 16 | struct ComboBoxEntry 17 | { 18 | ComboBoxEntry(const char* _name = nullptr) : 19 | name(_name) {} 20 | 21 | ComboBoxEntry(const char* _name, const Data& _data) : 22 | name(_name), data(_data) {} 23 | 24 | const char* name; 25 | Data data; 26 | }; 27 | 28 | template 29 | class ComboBox : public spvgentwo::HeapVector> 30 | { 31 | public: 32 | using OnSelectCallback = spvgentwo::HeapCallable; 33 | 34 | ComboBox(ComboBox&& _other) noexcept; 35 | 36 | ComboBox(const ComboBox& _other); 37 | 38 | ComboBox(const char* _pTitle = nullptr); 39 | 40 | template 41 | ComboBox(const char* _pTitle, Args&& ... _args); 42 | 43 | virtual ~ComboBox() override; 44 | 45 | void update(); 46 | 47 | template 48 | void setOnSelectCallback(const Functor& _func); 49 | 50 | // if callback is not called onSelect will be called 51 | virtual void onSelect(unsigned int _index) {}; 52 | 53 | int getSelectedIndex() const { return m_selected; } 54 | void setSelectedIndex(int _index) { m_selected = _index; } 55 | 56 | const ComboBoxEntry& getSelected() const { return this->operator[](m_selected < 0 ? 0 : m_selected); } 57 | const X& getSelectedItem() const { return this->operator[](m_selected < 0 ? 0 : m_selected).data; } 58 | 59 | template 60 | void emplace_back_args2(const char* _name, const X& _data, Args&& ..._tail); 61 | 62 | private: 63 | const char* m_pTitle = nullptr; 64 | // selection index, selected item 65 | OnSelectCallback m_onSelect; 66 | int m_selected = -1; 67 | }; 68 | 69 | template 70 | void add(ComboBox* ptr, const char* _name = nullptr, const X& _data = {}) 71 | { 72 | ptr->emplace_back(ComboBoxEntry{ _name, _data }); 73 | } 74 | 75 | template 76 | template 77 | inline ComboBox::ComboBox(const char* _pTitle, Args&& ..._args) : 78 | m_pTitle(_pTitle) 79 | { 80 | emplace_back_args2(stdrep::forward(_args)...); 81 | } 82 | 83 | template 84 | template 85 | inline void ComboBox::setOnSelectCallback(const Functor& _func) 86 | { 87 | m_onSelect = _func; 88 | } 89 | 90 | template 91 | template 92 | inline void ComboBox::emplace_back_args2(const char* _name, const X& _data, Args&& ..._tail) 93 | { 94 | this->emplace_back(_name, _data); 95 | 96 | if constexpr (sizeof...(_tail) > 1) 97 | { 98 | emplace_back_args2(stdrep::forward(_tail)...); 99 | } 100 | } 101 | 102 | template 103 | ComboBox::ComboBox(ComboBox&& _other) noexcept : spvgentwo::HeapVector>(stdrep::move(_other)), 104 | m_pTitle(_other.m_pTitle), 105 | m_onSelect(stdrep::move(_other.m_onSelect)), 106 | m_selected(_other.m_selected) 107 | { 108 | _other.m_selected = 0u; 109 | _other.m_pTitle = nullptr; 110 | } 111 | 112 | template 113 | ComboBox::ComboBox(const ComboBox& _other) : spvgentwo::HeapVector>(_other), 114 | m_pTitle(_other.m_pTitle), 115 | m_onSelect(_other.m_onSelect), 116 | m_selected(_other.m_selected) 117 | { 118 | } 119 | 120 | template 121 | ComboBox::ComboBox(const char* _pTitle) : 122 | spvgentwo::HeapVector>(), 123 | m_pTitle(_pTitle), 124 | m_onSelect() 125 | { 126 | } 127 | 128 | template 129 | ComboBox::~ComboBox() 130 | { 131 | } 132 | 133 | template 134 | void ComboBox::update() 135 | { 136 | if (this->empty()) 137 | return; 138 | 139 | if (m_selected == -1) 140 | { 141 | if (m_onSelect) 142 | { 143 | m_onSelect(0); 144 | } 145 | else 146 | { 147 | onSelect(0); 148 | } 149 | } 150 | 151 | m_selected = m_selected >= this->m_elements ? 0 : m_selected; 152 | const char* current_item = this->operator[](m_selected).name; 153 | 154 | if (ImGui::BeginCombo(m_pTitle, current_item)) 155 | { 156 | for (int n = 0; n < this->m_elements; n++) 157 | { 158 | const char* item = this->operator[](n).name; 159 | 160 | bool is_selected = (current_item == item); 161 | 162 | if (ImGui::Selectable(item, is_selected /*,0, ImVec2(1.f, 2.f)*/)) 163 | { 164 | m_selected = n; 165 | 166 | ImGui::SetItemDefaultFocus(); 167 | if (m_onSelect) 168 | { 169 | m_onSelect(m_selected); 170 | } 171 | else 172 | { 173 | onSelect(m_selected); 174 | } 175 | } 176 | } 177 | ImGui::EndCombo(); 178 | } 179 | } 180 | 181 | } // !proto -------------------------------------------------------------------------------- /gui/include/proto/EditorGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Module.h" 4 | #include "AssemblyTextView.h" 5 | #include "OpNodeExpr.h" 6 | #include "Validator.h" 7 | 8 | // forward decls 9 | namespace ImNodes 10 | { 11 | struct CanvasState; 12 | } 13 | 14 | namespace proto 15 | { 16 | class EditorGraph 17 | { 18 | public: 19 | EditorGraph(spvgentwo::ILogger* _pLogger, const char* _pName); 20 | ~EditorGraph(); 21 | 22 | void update(); 23 | 24 | void clear(); 25 | 26 | void save(); 27 | 28 | private: 29 | void createCanvas(); 30 | 31 | void updateContextMenu(); 32 | 33 | void evaluateExprGraph(); 34 | 35 | private: 36 | ImNodes::CanvasState* m_pCanvas = nullptr; 37 | spvgentwo::Module m_module; 38 | spvgentwo::HeapVector m_moduleBinary; 39 | spvgentwo::BasicBlock* m_pBB = nullptr; 40 | 41 | Validator m_validator; 42 | AssemblyTextView m_textView; 43 | 44 | const char* m_pName = nullptr; 45 | 46 | spvgentwo::ExprGraph m_nodes; 47 | }; 48 | } //!proto -------------------------------------------------------------------------------- /gui/include/proto/FundamentalTypeComboBox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // forward decls 4 | #include "spvgentwo/Type.h" 5 | #include "ComboBox.h" 6 | 7 | namespace proto 8 | { 9 | class FundamentalTypeComboBox : public ComboBox<> 10 | { 11 | public: 12 | FundamentalTypeComboBox(const char* _pTitle); 13 | ~FundamentalTypeComboBox() override; 14 | 15 | const spvgentwo::Type& getType() const { return m_type; } 16 | 17 | private: 18 | void onSelect(unsigned int _index) final; 19 | 20 | private: 21 | spvgentwo::Type m_type; 22 | }; 23 | } // !proto -------------------------------------------------------------------------------- /gui/include/proto/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Logger.h" 4 | #include "common/HeapString.h" 5 | #include "common/HeapList.h" 6 | 7 | namespace proto 8 | { 9 | class Logger : public spvgentwo::ILogger 10 | { 11 | public: 12 | Logger(unsigned int _maxHistory = 100u); 13 | ~Logger() override; 14 | 15 | void update(); 16 | 17 | static Logger* instance(); 18 | 19 | void setMaxHistory(unsigned int _maxHistory) { m_maxHistory = _maxHistory; } 20 | 21 | private: 22 | static void LogImpl(ILogger* _pInstance, spvgentwo::LogLevel _level, const char* _pFormat, ...); 23 | 24 | void addMsg(const spvgentwo::LogLevel _level, const char* _pMsg); 25 | 26 | private: 27 | unsigned int m_maxHistory = 0u; 28 | bool m_isOpen = true; 29 | bool m_autoScroll = true; 30 | 31 | char m_filter[64]{ }; 32 | 33 | spvgentwo::HeapList m_buffer; 34 | }; 35 | 36 | template 37 | void log(spvgentwo::LogLevel _level, const char* _pFormat, Args&& ..._args) 38 | { 39 | Logger::instance()->log(_level, _pFormat, spvgentwo::stdrep::forward(_args)...); 40 | } 41 | 42 | template 43 | void logInfo(const char* _pFormat, Args&& ..._args) 44 | { 45 | Logger::instance()->logInfo(_pFormat, spvgentwo::stdrep::forward(_args)...); 46 | } 47 | 48 | template 49 | void logDebug(const char* _pFormat, Args&& ..._args) 50 | { 51 | Logger::instance()->logDebug(_pFormat, spvgentwo::stdrep::forward(_args)...); 52 | } 53 | 54 | template 55 | void logWarning(const char* _pFormat, Args&& ..._args) 56 | { 57 | Logger::instance()->logWarning(_pFormat, spvgentwo::stdrep::forward(_args)...); 58 | } 59 | 60 | template 61 | void logError(const char* _pFormat, Args&& ..._args) 62 | { 63 | Logger::instance()->logError(_pFormat, spvgentwo::stdrep::forward(_args)...); 64 | } 65 | 66 | template 67 | void logFatal(const char* _pFormat, Args&& ..._args) 68 | { 69 | Logger::instance()->logFatal(_pFormat, spvgentwo::stdrep::forward(_args)...); 70 | } 71 | } // !proto -------------------------------------------------------------------------------- /gui/include/proto/OpNodeExpr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/HeapList.h" 4 | #include "common/HeapVector.h" 5 | #include "common/HeapString.h" 6 | #include "common/ExprGraph.h" 7 | #include "spvgentwo/Constant.h" 8 | 9 | #include "proto/FundamentalTypeComboBox.h" 10 | #include "proto/TypedDataInput.h" 11 | 12 | #include 13 | #include "ImNodesEz.h" 14 | 15 | //forward decl 16 | namespace spvgentwo 17 | { 18 | class Instruction; 19 | class BasicBlock; 20 | } // !spvgentwo 21 | 22 | namespace proto 23 | { 24 | enum class OpNodeType : uint32_t 25 | { 26 | InVar = 0, // needs load 27 | OutVar, // needs store 28 | Const, 29 | 30 | Equal, 31 | NotEqual, 32 | Less, 33 | LessEqual, 34 | Greater, 35 | GreaterEqual, 36 | 37 | Add, 38 | Sub, 39 | Mul, 40 | Div, 41 | Dot, 42 | 43 | Select, 44 | //Phi, 45 | 46 | Cast, 47 | 48 | NumOf 49 | }; 50 | 51 | struct OpNodeDesc 52 | { 53 | const char* name; 54 | spvgentwo::HeapVector inputs; 55 | spvgentwo::HeapVector outputs; 56 | }; 57 | 58 | static const OpNodeDesc g_OpNodeDesc[uint32_t(OpNodeType::NumOf)] = { 59 | { "InVar", {}, {"Value"}}, 60 | { "OutVar", "Value", {}}, 61 | { "Const", {}, "Value"}, 62 | { "Equal", {"X", "Y"}, {"bool"}}, 63 | { "NotEqual", {"X", "Y"}, {"bool"}}, 64 | { "Less", {"X", "Y"}, {"bool"}}, 65 | { "LessEqual", {"X", "Y"}, {"bool"}}, 66 | { "Greater", {"X", "Y"}, {"bool"}}, 67 | { "GreaterEqual", {"X", "Y"}, {"bool"}}, 68 | { "Add", {"X", "Y"}, {"Value"}}, 69 | { "Sub",{"X", "Y"}, {"Value"}}, 70 | { "Mul", {"X", "Y"}, {"Value"}}, 71 | { "Div", {"X", "Y"}, {"Value"}}, 72 | { "Dot", {"X", "Y"}, {"Value"}}, 73 | { "Select",{"bool", "X", "Y"}, {"Value"}}, 74 | //{ OpNodeType::Phi, ~0u, 1u}, 75 | { "Cast", {"Type1"}, {"Type2"}}, 76 | }; 77 | 78 | struct VarDesc 79 | { 80 | spvgentwo::Type type; 81 | spvgentwo::spv::StorageClass storageClass = spvgentwo::spv::StorageClass::UniformConstant; 82 | const char* name = nullptr; 83 | }; 84 | 85 | struct ConstDesc 86 | { 87 | spvgentwo::Constant constant; 88 | const char* name = nullptr; 89 | }; 90 | 91 | // forward decl 92 | class OpNodeExpr; 93 | 94 | /// A structure defining a connection between two slots of two nodes. 95 | struct Connection 96 | { 97 | /// `id` that was passed to BeginNode() of input node. 98 | OpNodeExpr* input_node = nullptr; 99 | /// Descriptor of input slot. 100 | const char* input_slot = nullptr; 101 | /// `id` that was passed to BeginNode() of output node. 102 | OpNodeExpr* output_node = nullptr; 103 | /// Descriptor of output slot. 104 | const char* output_slot = nullptr; 105 | 106 | bool operator==(const Connection& other) const 107 | { 108 | return input_node == other.input_node && 109 | input_slot == other.input_slot && 110 | output_node == other.output_node && 111 | output_slot == other.output_slot; 112 | } 113 | 114 | bool operator!=(const Connection& other) const 115 | { 116 | return !operator ==(other); 117 | } 118 | }; 119 | 120 | class OpNodeExpr 121 | { 122 | public: 123 | OpNodeExpr(OpNodeExpr&& _other) noexcept; 124 | OpNodeExpr(ImVec2 _pos = ImVec2(), OpNodeType _type = OpNodeType::NumOf); 125 | ~OpNodeExpr(); 126 | 127 | void operator()(const spvgentwo::List& _inputs, const spvgentwo::List& _outputs); 128 | 129 | OpNodeType getType() const { return m_type; } 130 | OpNodeDesc getInfo() { return g_OpNodeDesc[uint32_t(m_type)]; } 131 | 132 | void setBasicBlock(spvgentwo::BasicBlock* _pBB); 133 | void setParent(spvgentwo::ExprGraph* _pGraph, typename spvgentwo::ExprGraph::NodeType* _pParent); 134 | 135 | // returns true if nodes is to be removed 136 | bool update(); 137 | 138 | bool isSelected() const { return m_selected; } 139 | 140 | ImVec2 getPosition() const { return m_pos; } 141 | 142 | private: 143 | bool allowedDisconnection(const Connection& _con); 144 | bool allowedConnection(const Connection& _con); 145 | spvgentwo::List::Iterator remove(const Connection& _con); 146 | 147 | // expr evaluation 148 | bool makeVar(); 149 | bool makeConst(); 150 | 151 | // ui update: 152 | void updateOpDesc(); 153 | void updateVarDesc(); 154 | void updateConstDesc(); 155 | 156 | private: 157 | OpNodeType m_type = OpNodeType::NumOf; 158 | spvgentwo::BasicBlock* m_pBB = nullptr; 159 | spvgentwo::Instruction* m_pResult = nullptr; 160 | spvgentwo::Instruction* m_pVar = nullptr; 161 | 162 | ImVec2 m_pos{}; 163 | bool m_selected = false; 164 | 165 | VarDesc m_varDesc{}; 166 | ConstDesc m_constDesc{}; 167 | 168 | spvgentwo::HeapVector m_inputSlots; 169 | spvgentwo::HeapVector m_outputSlots; 170 | spvgentwo::HeapList m_connections; 171 | 172 | spvgentwo::ExprGraph* m_pGraph = nullptr; 173 | typename spvgentwo::ExprGraph::NodeType* m_pParent = nullptr; 174 | 175 | private: 176 | FundamentalTypeComboBox m_typeComboBox; 177 | ComboBox m_storageClassCombobox; 178 | TypedDataInput m_dataInput; 179 | }; 180 | } // !proto -------------------------------------------------------------------------------- /gui/include/proto/ProtoWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "proto/Window.h" 4 | #include "proto/EditorGraph.h" 5 | 6 | namespace proto 7 | { 8 | class ProtoWindow : public Window 9 | { 10 | public: 11 | ProtoWindow(); 12 | ~ProtoWindow(); 13 | 14 | bool updateUI() final; 15 | 16 | private: 17 | void updateMenu(); 18 | void updateMenuFile(); 19 | 20 | void updateGraphs(); 21 | 22 | private: 23 | bool m_shouldClose = false; 24 | 25 | EditorGraph m_graph; 26 | }; 27 | 28 | } // !proto -------------------------------------------------------------------------------- /gui/include/proto/TypedDataInput.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace spvgentwo 7 | { 8 | class Type; 9 | class Constant; 10 | } 11 | 12 | namespace proto 13 | { 14 | // for small scalars, vectors and matrices (upto 4x4) 15 | class TypedDataInput 16 | { 17 | public: 18 | TypedDataInput(); 19 | ~TypedDataInput(); 20 | 21 | // returns true if type is valid / input was matched 22 | bool update(const spvgentwo::Type& _type); 23 | bool update(const spvgentwo::Type& _type, spvgentwo::Constant& _outConstant); 24 | 25 | auto get() const { return m_data; } 26 | private: 27 | 28 | const char* m_format = nullptr; 29 | 30 | union Scalar 31 | { 32 | bool b; 33 | float f32; 34 | double f64; 35 | int16_t s16; 36 | uint16_t u16; 37 | int32_t s32; 38 | uint32_t u32; 39 | int64_t s64; 40 | uint64_t u64; 41 | }; 42 | 43 | // drag 44 | Scalar m_min; 45 | Scalar m_max; 46 | float m_speed = 1.f; 47 | float m_power = 1.f; 48 | 49 | // input 50 | Scalar m_step; 51 | Scalar m_fastStep; 52 | 53 | // options: 54 | int m_activeOption = 0; 55 | bool m_inputDrag = true; 56 | bool m_inputScalar = false; 57 | bool m_inputColor = false; 58 | 59 | bool m_showOptions = false; 60 | bool m_inputInModal = false; 61 | bool m_inputModalOpen = false; 62 | 63 | union 64 | { 65 | bool b; 66 | bool bv2[2]; 67 | bool bv3[3]; 68 | bool bv4[4]; 69 | 70 | uint8_t u8; 71 | 72 | // 16 bit vec 73 | int16_t s16; 74 | int16_t s16v2[2]; 75 | int16_t s16v3[3]; 76 | int16_t s16v4[4]; 77 | 78 | uint16_t u16; 79 | uint16_t u16v2[2]; 80 | uint16_t u16v3[3]; 81 | uint16_t u16v4[4]; 82 | 83 | // 32 bit vec 84 | int32_t s32; 85 | int32_t s32v2[2]; 86 | int32_t s32v3[3]; 87 | int32_t s32v4[4]; 88 | 89 | uint32_t u32; 90 | uint32_t u32v2[2]; 91 | uint32_t u32v3[3]; 92 | uint32_t u32v4[4]; 93 | 94 | float f32; 95 | float f32v2[2]; 96 | float f32v3[3]; 97 | float f32v4[4]; 98 | 99 | // 64 bit vec 100 | int64_t s64; 101 | int64_t s64v2[2]; 102 | int64_t s64v3[3]; 103 | int64_t s64v4[4]; 104 | 105 | uint64_t u64; 106 | uint64_t u64v2[2]; 107 | uint64_t u64v3[3]; 108 | uint64_t u64v4[4]; 109 | 110 | double f64; 111 | double f64v2[2]; 112 | double f64v3[3]; 113 | double f64v4[4]; 114 | 115 | // 2x2 116 | int16_t s16m22[2][2]; 117 | int32_t s32m22[2][2]; 118 | int64_t s64m22[2][2]; 119 | 120 | uint16_t u16m22[2][2]; 121 | uint32_t u32m22[2][2]; 122 | uint64_t u64m22[2][2]; 123 | 124 | float f32m22[2][2]; 125 | double f64m22[2][2]; 126 | 127 | // 3x3 128 | int16_t s16m33[3][3]; 129 | int32_t s32m33[3][3]; 130 | int64_t s64m33[3][3]; 131 | 132 | uint16_t u16m33[3][3]; 133 | uint32_t u32m33[3][3]; 134 | uint64_t u64m33[3][3]; 135 | 136 | float f32m33[3][3]; 137 | double f64m33[3][3]; 138 | 139 | // todo: 3x4 and 4x3 140 | 141 | // 4x4 142 | int16_t s16m44[4][4]; 143 | int32_t s32m44[4][4]; 144 | int64_t s64m44[4][4]; 145 | 146 | uint16_t u16m44[4][4]; 147 | uint32_t u32m44[4][4]; 148 | uint64_t u64m44[4][4]; 149 | 150 | float f32m44[4][4]; 151 | double f64m44[4][4]; 152 | } m_data; 153 | }; 154 | } -------------------------------------------------------------------------------- /gui/include/proto/Validator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | template 6 | class Vector; 7 | } 8 | 9 | namespace proto 10 | { 11 | class Validator 12 | { 13 | public: 14 | Validator(); 15 | ~Validator(); 16 | 17 | bool validate(const spvgentwo::Vector& _module); 18 | private: 19 | unsigned long long m_hash = 0u; 20 | bool m_valid = false; 21 | }; 22 | } // !proto -------------------------------------------------------------------------------- /gui/include/proto/WinHeapDbg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace proto 4 | { 5 | class WinHeapDbg 6 | { 7 | public: 8 | static void init(); 9 | static void setBreakpoint(int _alloc); 10 | }; 11 | } // !proto -------------------------------------------------------------------------------- /gui/include/proto/Window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "imgui.h" 4 | #include "imgui_impl_vulkan.h" 5 | 6 | // foward decls: 7 | struct GLFWwindow; 8 | 9 | namespace proto 10 | { 11 | class Window 12 | { 13 | public: 14 | Window(); 15 | Window(int _width, int _height); 16 | virtual ~Window(); 17 | 18 | bool init(int _width, int _height); 19 | 20 | virtual bool updateUI() = 0; 21 | 22 | int exec(); 23 | 24 | private: 25 | static void glfwResizeCallback(GLFWwindow* _pWindow, int w, int h); 26 | static void glfwErrorCallback(int error, const char* description); 27 | 28 | void setupVulkanWindow(VkSurfaceKHR surface, int width, int height); 29 | void frameRender(); 30 | void cleanupVulkan(); 31 | void framePresent(); 32 | 33 | VkResult setupVulkan(const char** extensions, uint32_t extensions_count); 34 | 35 | private: 36 | GLFWwindow* m_pGLFWWindow = nullptr; 37 | ImGui_ImplVulkanH_Window m_VulkanWindow{}; 38 | 39 | ImVec4 m_clearColor = { 0.45f, 0.55f, 0.60f, 1.00f }; 40 | 41 | bool m_SwapChainRebuild = false; 42 | int m_SwapChainResizeWidth = 0; 43 | int m_SwapChainResizeHeight = 0; 44 | 45 | const int m_minImageCount = 2; 46 | }; 47 | } // !proto -------------------------------------------------------------------------------- /gui/source/AssemblyTextView.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/AssemblyTextView.h" 2 | #include "imgui.h" 3 | #include "spvgentwo/Module.h" 4 | 5 | #include 6 | #include "common/BinaryVectorWriter.h" 7 | 8 | using namespace spvgentwo; 9 | 10 | proto::AssemblyTextView::AssemblyTextView() 11 | { 12 | } 13 | 14 | proto::AssemblyTextView::~AssemblyTextView() 15 | { 16 | } 17 | 18 | void proto::AssemblyTextView::update(const spvgentwo::HeapVector& _module) 19 | { 20 | if (ImGui::Begin("Text View")) 21 | { 22 | ImGui::Checkbox("Indent", &m_indent); 23 | ImGui::SameLine(); 24 | ImGui::Checkbox("Show offsets", &m_offsets); 25 | ImGui::SameLine(); 26 | ImGui::Checkbox("Friendly names", &m_friendlyNames); 27 | ImGui::SameLine(); 28 | ImGui::Checkbox("Scroll", &m_autoScroll); 29 | 30 | uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE; 31 | if (m_indent) options |= SPV_BINARY_TO_TEXT_OPTION_INDENT; 32 | if (m_offsets) options |= SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET; 33 | if (m_friendlyNames) options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES; 34 | 35 | spv_text text = nullptr; 36 | spv_diagnostic diagnostic = nullptr; 37 | spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_5); 38 | spv_result_t error = spvBinaryToText(context, _module.data(), _module.size(), options, &text, &diagnostic); 39 | spvContextDestroy(context); 40 | 41 | if (error) 42 | { 43 | spvDiagnosticPrint(diagnostic); 44 | spvDiagnosticDestroy(diagnostic); 45 | } 46 | 47 | ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar); 48 | 49 | if (text != nullptr) 50 | { 51 | ImGui::Text("%s", text->str); 52 | } 53 | 54 | if (m_autoScroll) 55 | { 56 | ImGui::SetScrollHereY(); 57 | } 58 | 59 | ImGui::EndChild(); 60 | 61 | spvTextDestroy(text); 62 | } 63 | 64 | ImGui::End(); 65 | } 66 | -------------------------------------------------------------------------------- /gui/source/EditorGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/EditorGraph.h" 2 | 3 | #include "ImNodesEz.h" 4 | #include "common/BinaryFileWriter.h" 5 | #include "common/HeapAllocator.h" 6 | #include "proto/Logger.h" 7 | #include "common/BinaryVectorWriter.h" 8 | 9 | using namespace spvgentwo; 10 | 11 | proto::EditorGraph::EditorGraph(spvgentwo::ILogger* _pLogger, const char* _pName) : 12 | m_module(HeapAllocator::instance(), spv::Version, _pLogger), 13 | m_pName(_pName), 14 | m_nodes(HeapAllocator::instance()) 15 | { 16 | } 17 | 18 | proto::EditorGraph::~EditorGraph() 19 | { 20 | if (m_pCanvas != nullptr) 21 | { 22 | HeapAllocator::instance()->destruct(m_pCanvas); 23 | m_pCanvas = nullptr; 24 | } 25 | } 26 | 27 | void proto::EditorGraph::update() 28 | { 29 | createCanvas(); 30 | 31 | m_module.reset(); 32 | // configure capabilities and extensions 33 | m_module.addCapability(spv::Capability::Shader); 34 | Function& main = m_module.addEntryPoint(spv::ExecutionModel::Vertex, "main"); 35 | m_pBB = &main.front(); 36 | 37 | if (ImGui::Begin(m_pName, nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) 38 | { 39 | ImNodes::BeginCanvas(m_pCanvas); 40 | 41 | updateContextMenu(); 42 | 43 | for (auto it = m_nodes.begin(); it != m_nodes.end();) 44 | { 45 | OpNodeExpr& expr = it->data().get(); 46 | expr.setBasicBlock(m_pBB); 47 | 48 | if (expr.update()) // update UI, check if node is to be delted 49 | { 50 | it = m_nodes.erase(it); 51 | } 52 | else 53 | { 54 | ++it; 55 | } 56 | } 57 | 58 | ImNodes::EndCanvas(); 59 | } 60 | ImGui::End(); 61 | 62 | evaluateExprGraph(); 63 | 64 | m_textView.update(m_moduleBinary); 65 | } 66 | 67 | void proto::EditorGraph::clear() 68 | { 69 | m_nodes.clear(); 70 | } 71 | 72 | void proto::EditorGraph::save() 73 | { 74 | BinaryFileWriter writer("proto.spv"); 75 | m_module.write(&writer); 76 | system("spirv-dis proto.spv"); 77 | assert(system("spirv-val proto.spv") == 0); 78 | } 79 | 80 | void proto::EditorGraph::createCanvas() 81 | { 82 | if (m_pCanvas == nullptr) 83 | { 84 | m_pCanvas = HeapAllocator::instance()->construct(); 85 | } 86 | } 87 | 88 | void proto::EditorGraph::updateContextMenu() 89 | { 90 | ImVec2 pos = ImGui::GetCursorPos(); 91 | 92 | if (ImGui::IsMouseReleased(1) && ImGui::IsWindowHovered() && !ImGui::IsMouseDragging(1)) 93 | { 94 | bool anySelected = false; 95 | 96 | //for (const auto& node : m_nodes) 97 | //{ 98 | // anySelected |= node.data().get().isSelected(); 99 | //} 100 | 101 | if (anySelected == false) 102 | { 103 | ImGui::FocusWindow(ImGui::GetCurrentWindow()); 104 | ImGui::OpenPopup("NodesContextMenu"); 105 | } 106 | } 107 | 108 | if (ImGui::BeginPopup("NodesContextMenu")) 109 | { 110 | spvgentwo::ExprGraph::NodeType* pNode = nullptr; 111 | 112 | if (ImGui::MenuItem("Constant")) 113 | { 114 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Const }); 115 | } 116 | if (ImGui::MenuItem("InputVar")) 117 | { 118 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::InVar }); 119 | } 120 | if (ImGui::MenuItem("OutputVar")) 121 | { 122 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::OutVar }); 123 | } 124 | if (ImGui::MenuItem("+ Add")) 125 | { 126 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Add }); 127 | } 128 | if (ImGui::MenuItem("- Sub")) 129 | { 130 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Sub }); 131 | } 132 | if (ImGui::MenuItem("* Mul")) 133 | { 134 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Mul }); 135 | } 136 | if (ImGui::MenuItem("/ Div")) 137 | { 138 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Div }); 139 | } 140 | if (ImGui::MenuItem(". Dot")) 141 | { 142 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Dot }); 143 | } 144 | if (ImGui::MenuItem("? Select")) 145 | { 146 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Select }); 147 | } 148 | if (ImGui::MenuItem("= Equal")) 149 | { 150 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Equal }); 151 | } 152 | if (ImGui::MenuItem("!= NotEqual")) 153 | { 154 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::NotEqual }); 155 | } 156 | if (ImGui::MenuItem("< Less")) 157 | { 158 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Less }); 159 | } 160 | if (ImGui::MenuItem("<= LessEqual")) 161 | { 162 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::LessEqual }); 163 | } 164 | if (ImGui::MenuItem("> Greater")) 165 | { 166 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::Greater }); 167 | } 168 | if (ImGui::MenuItem(">+ GreaterEqual")) 169 | { 170 | pNode = m_nodes.emplace(OpNodeExpr{ pos, OpNodeType::GreaterEqual }); 171 | } 172 | 173 | if (pNode != nullptr) 174 | { 175 | logInfo("%s node added", pNode->data()->getInfo().name); 176 | auto& editorNode = pNode->data().get(); 177 | editorNode.setBasicBlock(m_pBB); 178 | editorNode.setParent(&m_nodes, pNode); 179 | ImNodes::AutoPositionNode(&editorNode); 180 | } 181 | 182 | if (ImGui::IsAnyMouseDown() && !ImGui::IsWindowHovered()) 183 | { 184 | ImGui::CloseCurrentPopup(); 185 | } 186 | ImGui::EndPopup(); 187 | } 188 | } 189 | 190 | void proto::EditorGraph::evaluateExprGraph() 191 | { 192 | m_nodes.resetEvaluationState(); 193 | 194 | for(auto& node : m_nodes) 195 | { 196 | // output vars are cfg sinks 197 | if (node.data()->getType() == OpNodeType::OutVar) 198 | { 199 | spvgentwo::ExprGraph::evaluateRecursive(&node); 200 | } 201 | } 202 | 203 | spvgentwo::BasicBlock& bb = m_module.getEntryPoints().front().front(); 204 | bb.returnValue(); 205 | 206 | m_moduleBinary.reserve(1024); 207 | m_moduleBinary.reset(); 208 | 209 | spvgentwo::BinaryVectorWriter writer(m_moduleBinary); 210 | m_module.write(&writer); 211 | 212 | m_validator.validate(m_moduleBinary); 213 | } -------------------------------------------------------------------------------- /gui/source/FundamentalTypeComboBox.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/FundamentalTypeComboBox.h" 2 | #include "imgui.h" 3 | 4 | struct Item 5 | { 6 | const char* name; 7 | spvgentwo::spv::Op type; 8 | int width; 9 | int rows = 1; 10 | int columns = 0; 11 | }; 12 | 13 | constexpr Item items[] = { 14 | {"u32", spvgentwo::spv::Op::OpTypeInt, 32}, 15 | {"f32", spvgentwo::spv::Op::OpTypeFloat, 32}, 16 | {"f32v2", spvgentwo::spv::Op::OpTypeFloat, 32, 2}, 17 | {"f32v3", spvgentwo::spv::Op::OpTypeFloat, 32, 3}, 18 | {"f32v4", spvgentwo::spv::Op::OpTypeFloat, 32, 4}, 19 | {"mat2", spvgentwo::spv::Op::OpTypeFloat, 32, 2, 2}, 20 | {"mat3", spvgentwo::spv::Op::OpTypeFloat, 32, 3, 3}, 21 | {"mat4", spvgentwo::spv::Op::OpTypeFloat, 32, 4, 4}, 22 | {"f64", spvgentwo::spv::Op::OpTypeFloat, 64}, 23 | {"s16", spvgentwo::spv::Op::OpTypeInt, -16}, 24 | {"u16", spvgentwo::spv::Op::OpTypeInt, 16}, 25 | {"s32", spvgentwo::spv::Op::OpTypeInt, -32}, 26 | {"bool", spvgentwo::spv::Op::OpTypeBool, 0}, 27 | {"void", spvgentwo::spv::Op::OpTypeVoid, 0}, 28 | {"bvec2", spvgentwo::spv::Op::OpTypeBool, 0, 2}, 29 | {"bvec3", spvgentwo::spv::Op::OpTypeBool, 0, 3}, 30 | {"bvec4", spvgentwo::spv::Op::OpTypeBool, 0, 4}, 31 | }; 32 | 33 | proto::FundamentalTypeComboBox::FundamentalTypeComboBox(const char* _pTitle) : ComboBox(_pTitle), 34 | m_type(spvgentwo::HeapAllocator::instance()) 35 | { 36 | for (auto item : items) 37 | { 38 | emplace_back(item.name); 39 | } 40 | } 41 | 42 | proto::FundamentalTypeComboBox::~FundamentalTypeComboBox() 43 | { 44 | } 45 | 46 | void proto::FundamentalTypeComboBox::onSelect(unsigned int _index) 47 | { 48 | auto item = items[_index]; 49 | 50 | m_type.reset(); 51 | 52 | m_type.setType(item.type); 53 | if (m_type.isInt()) 54 | { 55 | m_type.setIntWidth(labs(item.width)); 56 | m_type.setIntSign(item.width < 0); 57 | } 58 | else if (m_type.isFloat()) 59 | { 60 | m_type.setFloatWidth(labs(item.width)); 61 | } 62 | 63 | if (item.rows > 1) 64 | { 65 | m_type = m_type.wrapVector(item.rows); 66 | 67 | if (item.columns > 1) 68 | { 69 | m_type = m_type.wrapMatrix(item.columns); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /gui/source/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/Logger.h" 2 | #include "imgui.h" 3 | #include 4 | 5 | proto::Logger::Logger(unsigned int _maxHistory) : ILogger(LogImpl), 6 | m_maxHistory(_maxHistory) 7 | { 8 | } 9 | 10 | proto::Logger::~Logger() 11 | { 12 | } 13 | 14 | void proto::Logger::addMsg(const spvgentwo::LogLevel _level, const char* _pMsg) 15 | { 16 | if (m_buffer.size() >= m_maxHistory) 17 | { 18 | m_buffer.pop_front(); 19 | } 20 | 21 | m_buffer.emplace_back(_pMsg); 22 | } 23 | 24 | void proto::Logger::LogImpl(ILogger* _pInstance, spvgentwo::LogLevel _level, const char* _pFormat, ...) 25 | { 26 | char buffer[512]{}; 27 | 28 | int offset = 0u; 29 | 30 | switch (_level) 31 | { 32 | case spvgentwo::LogLevel::Debug: 33 | offset = sprintf_s(buffer, sizeof(buffer), "Debug: "); break; 34 | case spvgentwo::LogLevel::Info: 35 | offset = sprintf_s(buffer, sizeof(buffer), "Info: "); break; 36 | case spvgentwo::LogLevel::Warning: 37 | offset = sprintf_s(buffer, sizeof(buffer), "Warning: "); break; 38 | case spvgentwo::LogLevel::Error: 39 | offset = sprintf_s(buffer, sizeof(buffer), "Error: "); break; 40 | case spvgentwo::LogLevel::Fatal: 41 | offset = sprintf_s(buffer, sizeof(buffer), "Fatal: "); break; 42 | default: 43 | break; 44 | } 45 | 46 | 47 | va_list args; 48 | va_start(args, _pFormat); 49 | vsprintf_s(&buffer[offset], sizeof(buffer) - offset, _pFormat, args); 50 | va_end(args); 51 | 52 | reinterpret_cast(_pInstance)->addMsg(_level, buffer); 53 | } 54 | 55 | void proto::Logger::update() 56 | { 57 | if (ImGui::Begin("Log", &m_isOpen)) 58 | { 59 | if (ImGui::RadioButton("AutoScroll", m_autoScroll)) 60 | { 61 | m_autoScroll = !m_autoScroll; 62 | } 63 | ImGui::SameLine(); 64 | ImGui::InputText("##Filter", m_filter, sizeof(m_filter)); 65 | ImGui::SameLine(); 66 | if (ImGui::Button("Clear")) 67 | { 68 | m_buffer.clear(); 69 | } 70 | 71 | auto len = strnlen(m_filter, sizeof(m_filter)); 72 | 73 | ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar); 74 | 75 | for (const auto& msg : m_buffer) 76 | { 77 | if (len == 0 || strstr(msg.c_str(), m_filter) != nullptr) 78 | { 79 | bool pop_color = strstr(msg.c_str(), "error") != nullptr || strstr(msg.c_str(), "Error") != nullptr || strstr(msg.c_str(), "ERROR") != nullptr; 80 | if (pop_color) 81 | { 82 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); 83 | } 84 | 85 | ImGui::Text("%s", msg.c_str()); 86 | 87 | if (pop_color) 88 | { 89 | ImGui::PopStyleColor(); 90 | } 91 | } 92 | } 93 | 94 | if (m_autoScroll) 95 | { 96 | ImGui::SetScrollHereY(); 97 | } 98 | 99 | ImGui::EndChild(); 100 | } 101 | 102 | ImGui::End(); 103 | } 104 | 105 | proto::Logger* proto::Logger::instance() 106 | { 107 | static Logger logger; 108 | return &logger; 109 | } 110 | -------------------------------------------------------------------------------- /gui/source/OpNodeExpr.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/OpNodeExpr.h" 2 | #include "spvgentwo/Module.h" 3 | #include "proto/Logger.h" 4 | 5 | using namespace proto; 6 | using namespace spvgentwo; 7 | 8 | OpNodeExpr::OpNodeExpr(OpNodeExpr&& _other) noexcept: 9 | m_type(_other.m_type), 10 | m_pBB(_other.m_pBB), 11 | m_pResult(_other.m_pResult), 12 | m_pVar(_other.m_pVar), 13 | m_pos(_other.m_pos), 14 | m_selected(_other.m_selected), 15 | m_varDesc(stdrep::move(_other.m_varDesc)), 16 | m_constDesc(stdrep::move(_other.m_constDesc)), 17 | m_inputSlots(stdrep::move(_other.m_inputSlots)), 18 | m_outputSlots(stdrep::move(_other.m_outputSlots)), 19 | m_connections(stdrep::move(_other.m_connections)), 20 | m_pGraph(_other.m_pGraph), 21 | m_pParent(_other.m_pParent), 22 | m_typeComboBox(stdrep::move(_other.m_typeComboBox)), 23 | m_storageClassCombobox(stdrep::move(_other.m_storageClassCombobox)) 24 | { 25 | _other.m_pBB = nullptr; 26 | _other.m_pResult = nullptr; 27 | _other.m_pVar = nullptr; 28 | _other.m_pGraph = nullptr; 29 | _other.m_pGraph = nullptr; 30 | } 31 | 32 | OpNodeExpr::OpNodeExpr(ImVec2 _pos, OpNodeType _type) : 33 | m_type(_type), 34 | m_pos(_pos), 35 | m_varDesc{ HeapAllocator::instance() }, 36 | m_constDesc{ HeapAllocator::instance() }, 37 | m_inputSlots(), 38 | m_outputSlots(), 39 | m_connections(), 40 | m_typeComboBox("Type"), 41 | m_storageClassCombobox("StorageClass", 42 | "Input", spv::StorageClass::Input, 43 | "Output", spv::StorageClass::Output, 44 | "Uniform", spv::StorageClass::Uniform, 45 | "UniformConstant", spv::StorageClass::UniformConstant, 46 | "StorageBuffer", spv::StorageClass::StorageBuffer) 47 | { 48 | const auto& info = getInfo(); 49 | for (const char* in : info.inputs) 50 | { 51 | m_inputSlots.emplace_back(in, 1); 52 | } 53 | for (const char* out : info.outputs) 54 | { 55 | m_outputSlots.emplace_back(out, 1); 56 | } 57 | 58 | if (_type == OpNodeType::InVar) 59 | { 60 | m_storageClassCombobox.setSelectedIndex(0); 61 | } 62 | else if (_type == OpNodeType::OutVar) 63 | { 64 | m_storageClassCombobox.setSelectedIndex(1); 65 | } 66 | } 67 | 68 | OpNodeExpr::~OpNodeExpr() 69 | { 70 | } 71 | 72 | void OpNodeExpr::operator()(const List& _inputs, const List& _outputs) 73 | { 74 | Instruction* lhs = _inputs.empty() ? nullptr : _inputs.front()->m_pResult; 75 | Instruction* rhs = _inputs.size() > 1u ? (*(_inputs.begin()+1))->m_pResult : nullptr; 76 | 77 | if ((lhs != nullptr && lhs->getOperation() == spv::Op::OpNop) || (rhs != nullptr && rhs->getOperation() == spv::Op::OpNop)) 78 | { 79 | return; 80 | } 81 | 82 | switch (m_type) 83 | { 84 | case OpNodeType::InVar: // turn var desc into opVar & load 85 | if (makeVar()) 86 | { 87 | m_pResult = (*m_pBB)->opLoad(m_pVar); 88 | } 89 | break; 90 | case OpNodeType::OutVar: // turn var desc into opVar & store 91 | if(makeVar() && lhs != nullptr) 92 | { 93 | (*m_pBB)->opStore(m_pVar, lhs); 94 | } 95 | break; 96 | case OpNodeType::Const: 97 | makeConst(); 98 | break; 99 | case OpNodeType::Equal: 100 | if (lhs != nullptr && rhs != nullptr) 101 | { 102 | m_pResult = (*m_pBB)->Equal(lhs, rhs); 103 | } 104 | break; 105 | case OpNodeType::NotEqual: 106 | if (lhs != nullptr && rhs != nullptr) 107 | { 108 | m_pResult = (*m_pBB)->NotEqual(lhs, rhs); 109 | } 110 | break; 111 | case OpNodeType::Less: 112 | if (lhs != nullptr && rhs != nullptr) 113 | { 114 | m_pResult = (*m_pBB)->Less(lhs, rhs); 115 | } 116 | break; 117 | case OpNodeType::LessEqual: 118 | if (lhs != nullptr && rhs != nullptr) 119 | { 120 | m_pResult = (*m_pBB)->LessEqual(lhs, rhs); 121 | } 122 | break; 123 | case OpNodeType::Greater: 124 | if (lhs != nullptr && rhs != nullptr) 125 | { 126 | m_pResult = (*m_pBB)->Greater(lhs, rhs); 127 | } 128 | break; 129 | case OpNodeType::GreaterEqual: 130 | if (lhs != nullptr && rhs != nullptr) 131 | { 132 | m_pResult = (*m_pBB)->GreaterEqual(lhs, rhs); 133 | } 134 | break; 135 | case OpNodeType::Add: 136 | if (lhs != nullptr && rhs != nullptr) 137 | { 138 | m_pResult = (*m_pBB)->Add(lhs, rhs); 139 | } 140 | break; 141 | case OpNodeType::Sub: 142 | if (lhs != nullptr && rhs != nullptr) 143 | { 144 | m_pResult = (*m_pBB)->Sub(lhs, rhs); 145 | } 146 | break; 147 | case OpNodeType::Mul: 148 | if (lhs != nullptr && rhs != nullptr) 149 | { 150 | m_pResult = (*m_pBB)->Mul(lhs, rhs); 151 | } 152 | break; 153 | case OpNodeType::Div: 154 | if (lhs != nullptr && rhs != nullptr) 155 | { 156 | m_pResult = (*m_pBB)->Div(lhs, rhs); 157 | } 158 | break; 159 | case OpNodeType::Dot: 160 | if (lhs != nullptr && rhs != nullptr) 161 | { 162 | m_pResult = (*m_pBB)->opDot(lhs, rhs); 163 | } 164 | break; 165 | case OpNodeType::Select: 166 | if (_inputs.size() == 3u && _inputs.back()->m_pResult != nullptr) 167 | { 168 | m_pResult = (*m_pBB)->opSelect(lhs, rhs, _inputs.back()->m_pResult); 169 | } 170 | break; 171 | case OpNodeType::Cast: 172 | break; 173 | default: 174 | break; 175 | } 176 | } 177 | 178 | void OpNodeExpr::setBasicBlock(spvgentwo::BasicBlock* _pBB) 179 | { 180 | m_pBB = _pBB; 181 | } 182 | 183 | void OpNodeExpr::setParent(spvgentwo::ExprGraph* _pGraph, typename spvgentwo::ExprGraph::NodeType* _pParent) 184 | { 185 | m_pGraph = _pGraph; 186 | m_pParent = _pParent; 187 | } 188 | 189 | bool OpNodeExpr::makeVar() 190 | { 191 | if (m_varDesc.type.isVoid()) 192 | return false; 193 | 194 | Module* pModule = m_pBB->getModule(); 195 | Instruction* pType = pModule->addType(m_varDesc.type); // type needs to be a pointer with storage class 196 | if (m_varDesc.storageClass == spv::StorageClass::Function) 197 | { 198 | m_pVar = m_pBB->getFunction()->variable(pType, m_varDesc.name); 199 | } 200 | else 201 | { 202 | m_pVar = pModule->variable(pType, m_varDesc.storageClass, m_varDesc.name, nullptr); 203 | } 204 | 205 | return m_pVar != nullptr; 206 | } 207 | 208 | bool OpNodeExpr::makeConst() 209 | { 210 | m_pResult = m_pBB->getModule()->addConstant(m_constDesc.constant, m_constDesc.name); 211 | return m_pResult != nullptr; 212 | } 213 | 214 | void OpNodeExpr::updateOpDesc() 215 | { 216 | switch (m_type) 217 | { 218 | case OpNodeType::InVar: 219 | case OpNodeType::OutVar: 220 | updateVarDesc(); 221 | break; 222 | case OpNodeType::Const: 223 | updateConstDesc(); 224 | break; 225 | default: 226 | break; 227 | } 228 | } 229 | 230 | void OpNodeExpr::updateVarDesc() 231 | { 232 | m_typeComboBox.update(); 233 | m_storageClassCombobox.update(); 234 | m_varDesc.storageClass = m_storageClassCombobox.getSelectedItem(); 235 | m_varDesc.type = m_typeComboBox.getType().wrapPointer(m_varDesc.storageClass); 236 | } 237 | 238 | void OpNodeExpr::updateConstDesc() 239 | { 240 | m_constDesc.constant.reset(); 241 | m_typeComboBox.update(); 242 | 243 | const Type& t = m_typeComboBox.getType(); 244 | m_dataInput.update(t, m_constDesc.constant); 245 | } 246 | 247 | bool OpNodeExpr::update() 248 | { 249 | const char* name = getInfo().name; 250 | if (ImNodes::Ez::BeginNode(this, name, &m_pos, &m_selected)) 251 | { 252 | ImNodes::Ez::InputSlots(m_inputSlots.data(), (int)m_inputSlots.size()); 253 | 254 | ImGui::BeginGroup(); 255 | 256 | ImGui::PushItemWidth(400/*ImGui::GetContentRegionAvail().x * 0.25f*/); 257 | 258 | // draw body here 259 | updateOpDesc(); 260 | 261 | ImGui::PopItemWidth(); 262 | 263 | ImGui::EndGroup(); 264 | 265 | ImNodes::Ez::OutputSlots(m_outputSlots.data(), (int)m_outputSlots.size()); 266 | 267 | Connection con{}; 268 | if (ImNodes::GetNewConnection((void**)&con.input_node, &con.input_slot, 269 | (void**)&con.output_node, &con.output_slot) && allowedConnection(con)) 270 | { 271 | logInfo("Connected %s -> %s", con.output_node->getInfo().name, con.input_node->getInfo().name); 272 | 273 | con.input_node->m_connections.emplace_back(con); 274 | con.output_node->m_connections.emplace_back(con); 275 | 276 | con.output_node->m_pParent->connect(con.input_node->m_pParent); 277 | } 278 | 279 | // only render outputs 280 | for (auto it = m_connections.begin(); it != m_connections.end();) 281 | { 282 | const Connection& con = *it; 283 | if (con.output_node != this) 284 | { 285 | ++it; 286 | continue; 287 | } 288 | 289 | if (ImNodes::Connection(con.input_node, con.input_slot, 290 | con.output_node, con.output_slot) == false && allowedDisconnection(con)) 291 | { 292 | logInfo("Disconnected %s -> %s", con.output_node->getInfo().name, con.input_node->getInfo().name); 293 | 294 | con.output_node->m_pParent->disconnect(con.input_node->m_pParent); 295 | 296 | // Remove deleted connections 297 | con.input_node->remove(con); 298 | it = con.output_node->remove(con); // output node == this 299 | } 300 | else 301 | { 302 | ++it; 303 | } 304 | } 305 | } 306 | ImNodes::Ez::EndNode(); 307 | 308 | if (m_selected && ImGui::IsKeyPressedMap(ImGuiKey_Delete)) 309 | { 310 | for (auto& con : m_connections) 311 | { 312 | logInfo("Disconnected %s -> %s", con.output_node->getInfo().name, con.input_node->getInfo().name); 313 | con.output_node->m_pParent->disconnect(con.input_node->m_pParent); 314 | 315 | if (con.output_node == this) 316 | { 317 | con.input_node->remove(con); 318 | } 319 | else 320 | { 321 | con.output_node->remove(con); 322 | } 323 | } 324 | 325 | m_connections.clear(); 326 | return true; // remove node 327 | } 328 | 329 | return false; 330 | } 331 | 332 | bool OpNodeExpr::allowedDisconnection(const Connection& _con) 333 | { 334 | return true; 335 | } 336 | 337 | bool OpNodeExpr::allowedConnection(const Connection& _con) 338 | { 339 | // check if the input slot is taken 340 | for (const Connection& con : m_connections) 341 | { 342 | if (strcmp(con.input_slot, _con.input_slot) == 0) 343 | { 344 | return false; 345 | } 346 | } 347 | 348 | return true; 349 | } 350 | 351 | spvgentwo::List::Iterator OpNodeExpr::remove(const Connection& _con) 352 | { 353 | auto it = m_connections.find(_con); 354 | 355 | if (it != nullptr) 356 | { 357 | return m_connections.erase(it); 358 | } 359 | 360 | return it; 361 | } -------------------------------------------------------------------------------- /gui/source/ProtoWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/ProtoWindow.h" 2 | #include "proto/Logger.h" 3 | 4 | #include "imgui.h" 5 | 6 | proto::ProtoWindow::ProtoWindow() : Window(), 7 | m_graph(Logger::instance(), "Code Graph") 8 | { 9 | } 10 | 11 | proto::ProtoWindow::~ProtoWindow() 12 | { 13 | } 14 | 15 | bool proto::ProtoWindow::updateUI() 16 | { 17 | //ImGui::ShowDemoWindow(); 18 | 19 | Logger::instance()->update(); 20 | 21 | updateGraphs(); 22 | 23 | updateMenu(); 24 | 25 | return m_shouldClose == false; 26 | } 27 | 28 | void proto::ProtoWindow::updateMenu() 29 | { 30 | if (ImGui::BeginMainMenuBar()) 31 | { 32 | if (ImGui::BeginMenu("File")) 33 | { 34 | updateMenuFile(); 35 | 36 | ImGui::EndMenu(); 37 | } 38 | 39 | ImGui::EndMainMenuBar(); 40 | } 41 | } 42 | 43 | void proto::ProtoWindow::updateMenuFile() 44 | { 45 | if (ImGui::MenuItem("New")) 46 | { 47 | m_graph.clear(); 48 | } 49 | 50 | if (ImGui::MenuItem("Save", "Ctrl+S") /*|| (ImGui::GetIO().KeyCtrl && ImGui::GetIO().KeyShift*/) 51 | { 52 | logInfo("Saving..."); 53 | 54 | m_graph.save(); 55 | } 56 | 57 | if (ImGui::MenuItem("Quit", "Alt+F4")) 58 | { 59 | m_shouldClose = true; 60 | } 61 | } 62 | 63 | void proto::ProtoWindow::updateGraphs() 64 | { 65 | m_graph.update(); 66 | } 67 | -------------------------------------------------------------------------------- /gui/source/TypedDataInput.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/TypedDataInput.h" 2 | #include "spvgentwo/Constant.h" 3 | #include "imgui.h" 4 | 5 | using namespace spvgentwo; 6 | 7 | proto::TypedDataInput::TypedDataInput() : 8 | m_min{}, 9 | m_max{}, 10 | m_step{}, 11 | m_fastStep{}, 12 | m_data{} 13 | { 14 | } 15 | 16 | proto::TypedDataInput::~TypedDataInput() 17 | { 18 | } 19 | 20 | bool proto::TypedDataInput::update(const Type& _type) 21 | { 22 | uint32_t rows = 0; 23 | uint32_t cols = 1; 24 | ImGuiDataType_ type = ImGuiDataType_COUNT; 25 | 26 | if (_type.isScalar()) 27 | { 28 | rows = 1u; 29 | } 30 | else if (_type.isVector()) 31 | { 32 | rows = _type.getVectorComponentCount(); 33 | } 34 | else if (_type.isMatrix()) 35 | { 36 | rows = _type.getMatrixRowCount(); 37 | cols = _type.getMatrixColumnCount(); 38 | } 39 | else 40 | { 41 | return false; 42 | } 43 | 44 | const Type& base = _type.getBaseType(); 45 | 46 | if (base.isBool()) 47 | { 48 | // dont do anything 49 | } 50 | else if (base.isS16()) 51 | { 52 | type = ImGuiDataType_S16; 53 | m_step.s16 = m_step.s16 == 0 ? 1 : m_step.s16; 54 | } 55 | else if (base.isS32()) 56 | { 57 | type = ImGuiDataType_S32; 58 | m_step.s32 = m_step.s32 == 0 ? 1 : m_step.s32; 59 | } 60 | else if (base.isS64()) 61 | { 62 | type = ImGuiDataType_S64; 63 | m_step.s64 = m_step.s64 == 0 ? 1 : m_step.s64; 64 | } 65 | else if (base.isU16()) 66 | { 67 | type = ImGuiDataType_U16; 68 | m_step.u16 = m_step.u16 == 0 ? 1 : m_step.u16; 69 | } 70 | else if (base.isU32()) 71 | { 72 | type = ImGuiDataType_U32; 73 | m_step.u32 = m_step.u32 == 0 ? 1 : m_step.u32; 74 | } 75 | else if (base.isU64()) 76 | { 77 | type = ImGuiDataType_U64; 78 | m_step.u64 = m_step.u64 == 0 ? 1 : m_step.u64; 79 | } 80 | else if (base.isF32()) 81 | { 82 | type = ImGuiDataType_Float; 83 | m_step.f32 = m_step.s32 == 0 || m_step.s32 == 1 ? 1.f : m_step.f32; 84 | } 85 | else if (base.isF64()) 86 | { 87 | type = ImGuiDataType_Double; 88 | m_step.f64 = m_step.s64 == 0 || m_step.s64 == 1 ? 1.0 : m_step.f64; 89 | } 90 | else 91 | { 92 | return false; 93 | } 94 | 95 | const uint32_t rowLength = (base.getIntWidth() / 4) * rows; 96 | 97 | if (ImGui::RadioButton("Input Popup", m_inputInModal)) 98 | { 99 | m_inputInModal = !m_inputInModal; 100 | } 101 | ImGui::SameLine(); 102 | if (ImGui::Button("Input Options")) 103 | { 104 | m_showOptions = !m_showOptions; 105 | } 106 | 107 | if (m_inputInModal) 108 | { 109 | ImGui::OpenPopup("Input Value"); 110 | m_inputModalOpen = ImGui::BeginPopupModal("Input Value", &m_inputInModal); 111 | } 112 | 113 | if (base.isBool()) 114 | { 115 | uint32_t i = 0u; 116 | for (auto c = 0u; c < cols; ++c) 117 | { 118 | for (auto r = 0u;r < rows; ++r) 119 | { 120 | ImGui::PushID(i); 121 | bool& active = (&m_data.b)[i]; 122 | if (ImGui::RadioButton("##Value", active)) 123 | { 124 | active = !active; 125 | } 126 | ImGui::PopID(); 127 | ImGui::SameLine(); 128 | ++i; 129 | } 130 | } 131 | } 132 | else if (m_inputDrag) 133 | { 134 | for (auto i = 0u; i < cols; ++i) 135 | { 136 | ImGui::PushID(i); 137 | ImGui::DragScalarN("##Value", type, &m_data.u8 + (uint64_t)i * rowLength, rows, m_speed, &m_min, &m_max, m_format, m_power); 138 | ImGui::PopID(); 139 | } 140 | } 141 | else if (m_inputScalar) 142 | { 143 | for (auto i = 0u; i < cols; ++i) 144 | { 145 | ImGui::PushID(i); 146 | ImGui::InputScalarN("##Value", type, &m_data.u8 + (uint64_t)i * rowLength, rows, &m_step, &m_fastStep); 147 | ImGui::PopID(); 148 | } 149 | } 150 | else if (m_inputColor && type == ImGuiDataType_Float && cols < 2) 151 | { 152 | if (rows == 3) 153 | { 154 | ImGui::ColorEdit3("##Value", m_data.f32v3); 155 | } 156 | else if (rows == 4) 157 | { 158 | ImGui::ColorEdit4("##Value", m_data.f32v4); 159 | } 160 | } 161 | 162 | if (m_inputModalOpen) 163 | { 164 | ImGui::EndPopup(); 165 | } 166 | 167 | if (m_showOptions) 168 | { 169 | ImGui::OpenPopup("Options"); 170 | } 171 | 172 | if (ImGui::BeginPopupModal("Options", &m_showOptions)) 173 | { 174 | ImGui::RadioButton("Drag", &m_activeOption, 0); 175 | ImGui::SameLine(); 176 | ImGui::RadioButton("Scalar", &m_activeOption, 1); 177 | ImGui::SameLine(); 178 | ImGui::RadioButton("Color", &m_activeOption, 2); 179 | 180 | m_inputDrag = m_activeOption == 0; 181 | m_inputScalar = m_activeOption == 1; 182 | m_inputColor = m_activeOption == 2; 183 | 184 | if (m_inputDrag) 185 | { 186 | ImGui::DragScalar("Min", type, &m_min, 0.01f); 187 | ImGui::DragScalar("Max", type, &m_max, 0.01f); 188 | ImGui::DragFloat("Speed", &m_speed, 0.01f); 189 | ImGui::DragFloat("Power", &m_power, 0.01f); 190 | } 191 | 192 | if (m_inputScalar) 193 | { 194 | ImGui::DragScalar("Step", type, &m_step, 0.01f); 195 | ImGui::DragScalar("FastStep", type, &m_fastStep, 0.01f); 196 | } 197 | ImGui::EndPopup(); 198 | } 199 | 200 | return true; 201 | } 202 | 203 | bool proto::TypedDataInput::update(const spvgentwo::Type& _type, spvgentwo::Constant& _outConstant) 204 | { 205 | if (update(_type) == false) 206 | { 207 | return false; 208 | } 209 | 210 | // integral scalar types 211 | 212 | if (_type.isBool()) 213 | { 214 | _outConstant.make(m_data.b); 215 | } 216 | else if (_type.isS16()) 217 | { 218 | _outConstant.make(m_data.s16); 219 | } 220 | else if (_type.isS32()) 221 | { 222 | _outConstant.make(m_data.s32); 223 | } 224 | else if (_type.isS64()) 225 | { 226 | _outConstant.make(m_data.s64); 227 | } 228 | else if (_type.isU16()) 229 | { 230 | _outConstant.make(m_data.u16); 231 | } 232 | else if (_type.isU32()) 233 | { 234 | _outConstant.make(m_data.u32); 235 | } 236 | else if (_type.isU64()) 237 | { 238 | _outConstant.make(m_data.u64); 239 | } 240 | 241 | // floats 242 | else if (_type.isF32()) 243 | { 244 | _outConstant.make(m_data.f32); 245 | } 246 | else if (_type.isF64()) 247 | { 248 | _outConstant.make(m_data.f64); 249 | } 250 | 251 | // bool vecs 252 | else if (_type.isVectorOfBool(2u)) 253 | { 254 | _outConstant.make(make_vector(m_data.bv2)); 255 | } 256 | else if (_type.isVectorOfBool(3u)) 257 | { 258 | _outConstant.make(make_vector(m_data.bv3)); 259 | } 260 | else if (_type.isVectorOfBool(4u)) 261 | { 262 | _outConstant.make(make_vector(m_data.bv4)); 263 | } 264 | 265 | // signed int 16 vec 266 | else if (_type.isVectorOfSInt(2u, 16u)) 267 | { 268 | _outConstant.make(make_vector(m_data.s16v2)); 269 | } 270 | else if (_type.isVectorOfSInt(3u, 16u)) 271 | { 272 | _outConstant.make(make_vector(m_data.s16v3)); 273 | } 274 | else if (_type.isVectorOfSInt(4u, 16u)) 275 | { 276 | _outConstant.make(make_vector(m_data.s16v4)); 277 | } 278 | 279 | // unsinged int 16 vec 280 | else if (_type.isVectorOfUInt(2u, 16u)) 281 | { 282 | _outConstant.make(make_vector(m_data.u16v2)); 283 | } 284 | else if (_type.isVectorOfUInt(3u, 16u)) 285 | { 286 | _outConstant.make(make_vector(m_data.u16v3)); 287 | } 288 | else if (_type.isVectorOfUInt(4u, 16u)) 289 | { 290 | _outConstant.make(make_vector(m_data.u16v4)); 291 | } 292 | 293 | // signed int 32 vec 294 | else if (_type.isVectorOfSInt(2u, 32u)) 295 | { 296 | _outConstant.make(make_vector(m_data.s32v2)); 297 | } 298 | else if (_type.isVectorOfSInt(3u, 32u)) 299 | { 300 | _outConstant.make(make_vector(m_data.s32v3)); 301 | } 302 | else if (_type.isVectorOfSInt(4u, 32u)) 303 | { 304 | _outConstant.make(make_vector(m_data.s32v4)); 305 | } 306 | 307 | // unsinged int 32 vec 308 | else if (_type.isVectorOfUInt(2u, 32u)) 309 | { 310 | _outConstant.make(make_vector(m_data.u32v2)); 311 | } 312 | else if (_type.isVectorOfUInt(3u, 32u)) 313 | { 314 | _outConstant.make(make_vector(m_data.u32v3)); 315 | } 316 | else if (_type.isVectorOfUInt(4u, 32u)) 317 | { 318 | _outConstant.make(make_vector(m_data.u32v4)); 319 | } 320 | 321 | // signed int 64 vec 322 | else if (_type.isVectorOfSInt(2u, 64u)) 323 | { 324 | _outConstant.make(make_vector(m_data.s64v2)); 325 | } 326 | else if (_type.isVectorOfSInt(3u, 64u)) 327 | { 328 | _outConstant.make(make_vector(m_data.s64v3)); 329 | } 330 | else if (_type.isVectorOfSInt(4u, 64u)) 331 | { 332 | _outConstant.make(make_vector(m_data.s64v4)); 333 | } 334 | 335 | // unsinged int 64 vec 336 | else if (_type.isVectorOfUInt(2u, 64u)) 337 | { 338 | _outConstant.make(make_vector(m_data.u64v2)); 339 | } 340 | else if (_type.isVectorOfUInt(3u, 64u)) 341 | { 342 | _outConstant.make(make_vector(m_data.u64v3)); 343 | } 344 | else if (_type.isVectorOfUInt(4u, 64u)) 345 | { 346 | _outConstant.make(make_vector(m_data.u64v4)); 347 | } 348 | 349 | // float vec 32 350 | else if (_type.isVectorOfFloat(2u, 32u)) 351 | { 352 | _outConstant.make(make_vector(m_data.f32v2)); 353 | } 354 | else if (_type.isVectorOfFloat(3u, 32u)) 355 | { 356 | _outConstant.make(make_vector(m_data.f32v3)); 357 | } 358 | else if (_type.isVectorOfFloat(4u, 32u)) 359 | { 360 | _outConstant.make(make_vector(m_data.f32v4)); 361 | } 362 | 363 | // float vec 64 364 | else if (_type.isVectorOfFloat(2u, 64u)) 365 | { 366 | _outConstant.make(make_vector(m_data.f64v2)); 367 | } 368 | else if (_type.isVectorOfFloat(3u, 64u)) 369 | { 370 | _outConstant.make(make_vector(m_data.f64v3)); 371 | } 372 | else if (_type.isVectorOfFloat(4u, 64u)) 373 | { 374 | _outConstant.make(make_vector(m_data.f64v4)); 375 | } 376 | 377 | // s16 mat 378 | else if (_type.isMatrixOfInt(2u, 2u, 16u, Sign::Signed)) 379 | { 380 | _outConstant.make(make_matrix(m_data.s16m22)); 381 | } 382 | else if (_type.isMatrixOfInt(3u, 3u, 16u, Sign::Signed)) 383 | { 384 | _outConstant.make(make_matrix(m_data.s16m33)); 385 | } 386 | else if (_type.isMatrixOfInt(4u, 4u, 16u, Sign::Signed)) 387 | { 388 | _outConstant.make(make_matrix(m_data.s16m44)); 389 | } 390 | 391 | // u16 mat 392 | else if (_type.isMatrixOfInt(2u, 2u, 16u, Sign::Unsigned)) 393 | { 394 | _outConstant.make(make_matrix(m_data.u16m22)); 395 | } 396 | else if (_type.isMatrixOfInt(3u, 3u, 16u, Sign::Unsigned)) 397 | { 398 | _outConstant.make(make_matrix(m_data.u16m33)); 399 | } 400 | else if (_type.isMatrixOfInt(4u, 4u, 16u, Sign::Unsigned)) 401 | { 402 | _outConstant.make(make_matrix(m_data.u16m44)); 403 | } 404 | 405 | 406 | // s32 mat 407 | else if (_type.isMatrixOfInt(2u, 2u, 32u, Sign::Signed)) 408 | { 409 | _outConstant.make(make_matrix(m_data.s32m22)); 410 | } 411 | else if (_type.isMatrixOfInt(3u, 3u, 32u, Sign::Signed)) 412 | { 413 | _outConstant.make(make_matrix(m_data.s32m33)); 414 | } 415 | else if (_type.isMatrixOfInt(4u, 4u, 32u, Sign::Signed)) 416 | { 417 | _outConstant.make(make_matrix(m_data.s32m44)); 418 | } 419 | 420 | // u32 mat 421 | else if (_type.isMatrixOfInt(2u, 2u, 32u, Sign::Unsigned)) 422 | { 423 | _outConstant.make(make_matrix(m_data.u32m22)); 424 | } 425 | else if (_type.isMatrixOfInt(3u, 3u, 32u, Sign::Unsigned)) 426 | { 427 | _outConstant.make(make_matrix(m_data.u32m33)); 428 | } 429 | else if (_type.isMatrixOfInt(4u, 4u, 32u, Sign::Unsigned)) 430 | { 431 | _outConstant.make(make_matrix(m_data.u32m44)); 432 | } 433 | 434 | // s64 mat 435 | else if (_type.isMatrixOfInt(2u, 2u, 64u, Sign::Signed)) 436 | { 437 | _outConstant.make(make_matrix(m_data.s64m22)); 438 | } 439 | else if (_type.isMatrixOfInt(3u, 3u, 64u, Sign::Signed)) 440 | { 441 | _outConstant.make(make_matrix(m_data.s64m33)); 442 | } 443 | else if (_type.isMatrixOfInt(4u, 4u, 64u, Sign::Signed)) 444 | { 445 | _outConstant.make(make_matrix(m_data.s64m44)); 446 | } 447 | 448 | // u64 mat 449 | else if (_type.isMatrixOfInt(2u, 2u, 64u, Sign::Unsigned)) 450 | { 451 | _outConstant.make(make_matrix(m_data.u64m22)); 452 | } 453 | else if (_type.isMatrixOfInt(3u, 3u, 64u, Sign::Unsigned)) 454 | { 455 | _outConstant.make(make_matrix(m_data.u64m33)); 456 | } 457 | else if (_type.isMatrixOfInt(4u, 4u, 64u, Sign::Unsigned)) 458 | { 459 | _outConstant.make(make_matrix(m_data.u64m44)); 460 | } 461 | 462 | // f32mat 463 | else if (_type.isMatrixOfFloat(2u, 2u, 32u)) 464 | { 465 | _outConstant.make(make_matrix(m_data.f32m22)); 466 | } 467 | else if (_type.isMatrixOfFloat(3u, 3u, 32u)) 468 | { 469 | _outConstant.make(make_matrix(m_data.f32m33)); 470 | } 471 | else if (_type.isMatrixOfFloat(4u, 4u, 32u)) 472 | { 473 | _outConstant.make(make_matrix(m_data.f32m44)); 474 | } 475 | 476 | // f64 mat 477 | else if (_type.isMatrixOfFloat(2u, 2u, 64u)) 478 | { 479 | _outConstant.make(make_matrix(m_data.f64m22)); 480 | } 481 | else if (_type.isMatrixOfFloat(3u, 3u, 64u)) 482 | { 483 | _outConstant.make(make_matrix(m_data.f64m33)); 484 | } 485 | else if (_type.isMatrixOfFloat(4u, 4u, 64u)) 486 | { 487 | _outConstant.make(make_matrix(m_data.f64m44)); 488 | } 489 | 490 | else // could not match type 491 | { 492 | return false; 493 | } 494 | 495 | return _outConstant.getType() == _type; 496 | } 497 | -------------------------------------------------------------------------------- /gui/source/Validator.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/Validator.h" 2 | #include "common/HeapVector.h" 3 | #include 4 | #include "proto/Logger.h" 5 | 6 | #include "spvgentwo/FNV1aHasher.h" 7 | 8 | proto::Validator::Validator() 9 | { 10 | } 11 | 12 | proto::Validator::~Validator() 13 | { 14 | } 15 | 16 | void CLIMessageConsumer(spv_message_level_t level, const char* source, const spv_position_t& position, const char* message) 17 | { 18 | spvgentwo::LogLevel lvl = spvgentwo::LogLevel::Info; 19 | 20 | switch (level) { 21 | case SPV_MSG_FATAL: 22 | lvl = spvgentwo::LogLevel::Fatal; 23 | break; 24 | case SPV_MSG_INTERNAL_ERROR: 25 | case SPV_MSG_ERROR: 26 | lvl = spvgentwo::LogLevel::Error; 27 | break; 28 | case SPV_MSG_WARNING: 29 | lvl = spvgentwo::LogLevel::Warning; 30 | break; 31 | case SPV_MSG_INFO: 32 | lvl = spvgentwo::LogLevel::Info; 33 | break; 34 | default: 35 | break; 36 | } 37 | 38 | proto::log(lvl, "line %llu %s", position.index, message); 39 | } 40 | 41 | bool proto::Validator::validate(const spvgentwo::Vector& _module) 42 | { 43 | spvgentwo::FNV1aHasher hash; 44 | 45 | if (auto h = hash.add(_module.data(), _module.size() * sizeof(unsigned int)); h != m_hash) 46 | { 47 | m_hash = h; 48 | 49 | spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_5); 50 | 51 | tools.SetMessageConsumer(CLIMessageConsumer); 52 | spvtools::ValidatorOptions options; 53 | 54 | m_valid = tools.Validate(_module.data(), _module.size(), options); 55 | } 56 | 57 | return m_valid; 58 | } 59 | -------------------------------------------------------------------------------- /gui/source/WinHeapDbg.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/WinHeapDbg.h" 2 | 3 | #ifdef _WIN32 4 | 5 | #ifndef _CRTDBG_MAP_ALLOC 6 | #define _CRTDBG_MAP_ALLOC 7 | #endif 8 | 9 | #include 10 | 11 | #endif 12 | 13 | void proto::WinHeapDbg::init() 14 | { 15 | #ifdef _WIN32 16 | _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 17 | _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); 18 | #endif 19 | } 20 | 21 | void proto::WinHeapDbg::setBreakpoint(int _alloc) 22 | { 23 | #ifdef _WIN32 24 | _CrtSetBreakAlloc(_alloc); 25 | #endif 26 | } 27 | -------------------------------------------------------------------------------- /gui/source/Window.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/Window.h" 2 | 3 | #include "imgui_impl_glfw.h" 4 | 5 | #include // printf, fprintf 6 | #include // abort 7 | #define GLFW_INCLUDE_NONE 8 | #define GLFW_INCLUDE_VULKAN 9 | #include 10 | #include 11 | 12 | //#define IMGUI_UNLIMITED_FRAME_RATE 13 | #ifdef _DEBUG 14 | #define IMGUI_VULKAN_DEBUG_REPORT 15 | #endif 16 | 17 | static VkAllocationCallbacks* g_Allocator = NULL; 18 | static VkInstance g_Instance = VK_NULL_HANDLE; 19 | static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; 20 | static VkDevice g_Device = VK_NULL_HANDLE; 21 | static uint32_t g_QueueFamily = (uint32_t)-1; 22 | static VkQueue g_Queue = VK_NULL_HANDLE; 23 | static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; 24 | static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; 25 | static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; 26 | 27 | static void check_vk_result(VkResult err) 28 | { 29 | if (err == 0) return; 30 | printf("VkResult %d\n", err); 31 | if (err < 0) 32 | abort(); 33 | } 34 | 35 | #ifdef IMGUI_VULKAN_DEBUG_REPORT 36 | static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) 37 | { 38 | (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments 39 | fprintf(stderr, "[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); 40 | return VK_FALSE; 41 | } 42 | #endif // IMGUI_VULKAN_DEBUG_REPORT 43 | 44 | VkResult proto::Window::setupVulkan(const char** extensions, uint32_t extensions_count) 45 | { 46 | VkResult err; 47 | 48 | // Create Vulkan Instance 49 | { 50 | VkInstanceCreateInfo create_info = {}; 51 | create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 52 | create_info.enabledExtensionCount = extensions_count; 53 | create_info.ppEnabledExtensionNames = extensions; 54 | 55 | #ifdef IMGUI_VULKAN_DEBUG_REPORT 56 | // Enabling multiple validation layers grouped as LunarG standard validation 57 | const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; 58 | create_info.enabledLayerCount = 1; 59 | create_info.ppEnabledLayerNames = layers; 60 | 61 | // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) 62 | const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); 63 | memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); 64 | extensions_ext[extensions_count] = "VK_EXT_debug_report"; 65 | create_info.enabledExtensionCount = extensions_count + 1; 66 | create_info.ppEnabledExtensionNames = extensions_ext; 67 | 68 | // Create Vulkan Instance 69 | err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); 70 | check_vk_result(err); // leaks memory! 71 | free(extensions_ext); 72 | 73 | // Get the function pointer (required for any extensions) 74 | auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); 75 | IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); 76 | 77 | // Setup the debug report callback 78 | VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; 79 | debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 80 | debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; 81 | debug_report_ci.pfnCallback = debug_report; 82 | debug_report_ci.pUserData = NULL; 83 | err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); 84 | check_vk_result(err); 85 | #else 86 | // Create Vulkan Instance without any debug feature 87 | err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); 88 | check_vk_result(err); 89 | IM_UNUSED(g_DebugReport); 90 | #endif 91 | } 92 | 93 | // Select GPU 94 | { 95 | uint32_t gpu_count; 96 | err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, NULL); 97 | check_vk_result(err); 98 | IM_ASSERT(gpu_count > 0); 99 | 100 | VkPhysicalDevice* gpus = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * gpu_count); 101 | err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus); 102 | check_vk_result(err); 103 | 104 | // If a number >1 of GPUs got reported, you should find the best fit GPU for your purpose 105 | // e.g. VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU if available, or with the greatest memory available, etc. 106 | // for sake of simplicity we'll just take the first one, assuming it has a graphics queue family. 107 | g_PhysicalDevice = gpus[0]; 108 | free(gpus); 109 | } 110 | 111 | // Select graphics queue family 112 | { 113 | uint32_t count; 114 | vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, NULL); 115 | VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count); 116 | vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues); 117 | for (uint32_t i = 0; i < count; i++) 118 | if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) 119 | { 120 | g_QueueFamily = i; 121 | break; 122 | } 123 | free(queues); 124 | IM_ASSERT(g_QueueFamily != (uint32_t)-1); 125 | } 126 | 127 | // Create Logical Device (with 1 queue) 128 | { 129 | int device_extension_count = 1; 130 | const char* device_extensions[] = { "VK_KHR_swapchain" }; 131 | const float queue_priority[] = { 1.0f }; 132 | VkDeviceQueueCreateInfo queue_info[1] = {}; 133 | queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 134 | queue_info[0].queueFamilyIndex = g_QueueFamily; 135 | queue_info[0].queueCount = 1; 136 | queue_info[0].pQueuePriorities = queue_priority; 137 | VkDeviceCreateInfo create_info = {}; 138 | create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 139 | create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); 140 | create_info.pQueueCreateInfos = queue_info; 141 | create_info.enabledExtensionCount = device_extension_count; 142 | create_info.ppEnabledExtensionNames = device_extensions; 143 | err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device); 144 | check_vk_result(err); 145 | vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); 146 | } 147 | 148 | // Create Descriptor Pool 149 | { 150 | VkDescriptorPoolSize pool_sizes[] = 151 | { 152 | { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, 153 | { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, 154 | { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, 155 | { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, 156 | { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, 157 | { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, 158 | { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, 159 | { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, 160 | { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, 161 | { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, 162 | { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } 163 | }; 164 | VkDescriptorPoolCreateInfo pool_info = {}; 165 | pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; 166 | pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; 167 | pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes); 168 | pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); 169 | pool_info.pPoolSizes = pool_sizes; 170 | err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); 171 | check_vk_result(err); 172 | } 173 | 174 | return err; 175 | } 176 | 177 | // All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo. 178 | // Your real engine/app may not use them. 179 | void proto::Window::setupVulkanWindow(VkSurfaceKHR surface, int width, int height) 180 | { 181 | m_VulkanWindow.Surface = surface; 182 | 183 | // Check for WSI support 184 | VkBool32 res; 185 | vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, m_VulkanWindow.Surface, &res); 186 | if (res != VK_TRUE) 187 | { 188 | fprintf(stderr, "Error no WSI support on physical device 0\n"); 189 | exit(-1); 190 | } 191 | 192 | // Select Surface Format 193 | const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; 194 | const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; 195 | m_VulkanWindow.SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, m_VulkanWindow.Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); 196 | 197 | // Select Present Mode 198 | #ifdef IMGUI_UNLIMITED_FRAME_RATE 199 | VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; 200 | #else 201 | VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; 202 | #endif 203 | m_VulkanWindow.PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, m_VulkanWindow.Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); 204 | //printf("[vulkan] Selected PresentMode = %d\n", m_pVulkanWindow->PresentMode); 205 | 206 | // Create SwapChain, RenderPass, Framebuffer, etc. 207 | IM_ASSERT(m_minImageCount >= 2); 208 | ImGui_ImplVulkanH_CreateWindow(g_Instance, g_PhysicalDevice, g_Device, &m_VulkanWindow, g_QueueFamily, g_Allocator, width, height, m_minImageCount); 209 | } 210 | 211 | void proto::Window::cleanupVulkan() 212 | { 213 | vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); 214 | 215 | #ifdef IMGUI_VULKAN_DEBUG_REPORT 216 | // Remove the debug report callback 217 | auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); 218 | vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); 219 | #endif // IMGUI_VULKAN_DEBUG_REPORT 220 | 221 | vkDestroyDevice(g_Device, g_Allocator); 222 | vkDestroyInstance(g_Instance, g_Allocator); 223 | } 224 | 225 | void proto::Window::frameRender() 226 | { 227 | VkResult err; 228 | 229 | VkSemaphore image_acquired_semaphore = m_VulkanWindow.FrameSemaphores[m_VulkanWindow.SemaphoreIndex].ImageAcquiredSemaphore; 230 | VkSemaphore render_complete_semaphore = m_VulkanWindow.FrameSemaphores[m_VulkanWindow.SemaphoreIndex].RenderCompleteSemaphore; 231 | err = vkAcquireNextImageKHR(g_Device, m_VulkanWindow.Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &m_VulkanWindow.FrameIndex); 232 | check_vk_result(err); 233 | 234 | ImGui_ImplVulkanH_Frame* fd = &m_VulkanWindow.Frames[m_VulkanWindow.FrameIndex]; 235 | { 236 | err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking 237 | check_vk_result(err); 238 | 239 | err = vkResetFences(g_Device, 1, &fd->Fence); 240 | check_vk_result(err); 241 | } 242 | { 243 | err = vkResetCommandPool(g_Device, fd->CommandPool, 0); 244 | check_vk_result(err); 245 | VkCommandBufferBeginInfo info = {}; 246 | info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 247 | info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; 248 | err = vkBeginCommandBuffer(fd->CommandBuffer, &info); 249 | check_vk_result(err); 250 | } 251 | { 252 | VkRenderPassBeginInfo info = {}; 253 | info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; 254 | info.renderPass = m_VulkanWindow.RenderPass; 255 | info.framebuffer = fd->Framebuffer; 256 | info.renderArea.extent.width = m_VulkanWindow.Width; 257 | info.renderArea.extent.height = m_VulkanWindow.Height; 258 | info.clearValueCount = 1; 259 | info.pClearValues = &m_VulkanWindow.ClearValue; 260 | vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); 261 | } 262 | 263 | // Record Imgui Draw Data and draw funcs into command buffer 264 | ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), fd->CommandBuffer); 265 | 266 | // Submit command buffer 267 | vkCmdEndRenderPass(fd->CommandBuffer); 268 | { 269 | VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; 270 | VkSubmitInfo info = {}; 271 | info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 272 | info.waitSemaphoreCount = 1; 273 | info.pWaitSemaphores = &image_acquired_semaphore; 274 | info.pWaitDstStageMask = &wait_stage; 275 | info.commandBufferCount = 1; 276 | info.pCommandBuffers = &fd->CommandBuffer; 277 | info.signalSemaphoreCount = 1; 278 | info.pSignalSemaphores = &render_complete_semaphore; 279 | 280 | err = vkEndCommandBuffer(fd->CommandBuffer); 281 | check_vk_result(err); 282 | err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); 283 | check_vk_result(err); 284 | } 285 | } 286 | 287 | void proto::Window::framePresent() 288 | { 289 | VkSemaphore render_complete_semaphore = m_VulkanWindow.FrameSemaphores[m_VulkanWindow.SemaphoreIndex].RenderCompleteSemaphore; 290 | VkPresentInfoKHR info = {}; 291 | info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; 292 | info.waitSemaphoreCount = 1; 293 | info.pWaitSemaphores = &render_complete_semaphore; 294 | info.swapchainCount = 1; 295 | info.pSwapchains = &m_VulkanWindow.Swapchain; 296 | info.pImageIndices = &m_VulkanWindow.FrameIndex; 297 | VkResult err = vkQueuePresentKHR(g_Queue, &info); 298 | check_vk_result(err); 299 | m_VulkanWindow.SemaphoreIndex = (m_VulkanWindow.SemaphoreIndex + 1) % m_VulkanWindow.ImageCount; // Now we can use the next set of semaphores 300 | } 301 | 302 | void proto::Window::glfwErrorCallback(int error, const char* description) 303 | { 304 | fprintf(stderr, "Glfw Error %d: %s\n", error, description); 305 | } 306 | 307 | void proto::Window::glfwResizeCallback(GLFWwindow* _pWindow, int w, int h) 308 | { 309 | proto::Window* pProtoWnd = reinterpret_cast(glfwGetWindowUserPointer(_pWindow)); 310 | if (pProtoWnd != nullptr) 311 | { 312 | pProtoWnd->m_SwapChainRebuild = true; 313 | pProtoWnd->m_SwapChainResizeWidth = w; 314 | pProtoWnd->m_SwapChainResizeHeight = h; 315 | } 316 | } 317 | 318 | proto::Window::Window() 319 | { 320 | } 321 | 322 | proto::Window::Window(int _width, int _height) 323 | { 324 | init(_width, _height); 325 | } 326 | 327 | proto::Window::~Window() 328 | { 329 | } 330 | 331 | bool proto::Window::init(int _width, int _height) 332 | { 333 | if (m_pGLFWWindow != nullptr) 334 | { 335 | return false; 336 | } 337 | 338 | // Setup GLFW m_pGLFWWindow 339 | glfwSetErrorCallback(glfwErrorCallback); 340 | if (!glfwInit()) 341 | return false; 342 | 343 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 344 | //glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); 345 | 346 | m_pGLFWWindow = glfwCreateWindow(_width, _height, "Proto", NULL, NULL); 347 | glfwSetWindowUserPointer(m_pGLFWWindow, this); 348 | 349 | // Setup Vulkan 350 | if (!glfwVulkanSupported()) 351 | { 352 | printf("GLFW: Vulkan Not Supported\n"); 353 | return false; 354 | } 355 | 356 | uint32_t extensions_count = 0; 357 | const char** extensions = glfwGetRequiredInstanceExtensions(&extensions_count); 358 | if (setupVulkan(extensions, extensions_count) != VK_SUCCESS) 359 | { 360 | return false; 361 | } 362 | 363 | // Create Window Surface 364 | VkSurfaceKHR surface; 365 | if (glfwCreateWindowSurface(g_Instance, m_pGLFWWindow, g_Allocator, &surface) != VK_SUCCESS) 366 | { 367 | return false; 368 | } 369 | 370 | // Create Framebuffers 371 | int w, h; 372 | glfwGetFramebufferSize(m_pGLFWWindow, &w, &h); 373 | glfwSetFramebufferSizeCallback(m_pGLFWWindow, glfwResizeCallback); 374 | setupVulkanWindow(surface, w, h); 375 | 376 | // Setup Dear ImGui context 377 | IMGUI_CHECKVERSION(); 378 | ImGui::CreateContext(); 379 | ImGuiIO& io = ImGui::GetIO(); (void)io; 380 | //io.DisplayFramebufferScale = { float(w) / float(_width), float(h) / float(_height) }; 381 | io.FontGlobalScale = 2.f; 382 | 383 | //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls 384 | //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls 385 | 386 | // Setup Dear ImGui style 387 | ImGui::StyleColorsDark(); 388 | 389 | // Setup Platform/Renderer bindings 390 | ImGui_ImplGlfw_InitForVulkan(m_pGLFWWindow, true); 391 | ImGui_ImplVulkan_InitInfo init_info{}; 392 | init_info.Instance = g_Instance; 393 | init_info.PhysicalDevice = g_PhysicalDevice; 394 | init_info.Device = g_Device; 395 | init_info.QueueFamily = g_QueueFamily; 396 | init_info.Queue = g_Queue; 397 | init_info.PipelineCache = g_PipelineCache; 398 | init_info.DescriptorPool = g_DescriptorPool; 399 | init_info.Allocator = g_Allocator; 400 | init_info.MinImageCount = m_minImageCount; 401 | init_info.ImageCount = m_VulkanWindow.ImageCount; 402 | init_info.CheckVkResultFn = check_vk_result; 403 | ImGui_ImplVulkan_Init(&init_info, m_VulkanWindow.RenderPass); 404 | 405 | // Load Fonts 406 | // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. 407 | // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. 408 | // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). 409 | // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. 410 | // - Read 'docs/FONTS.txt' for more instructions and details. 411 | // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! 412 | //io.Fonts->AddFontDefault(); 413 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); 414 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); 415 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); 416 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); 417 | //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); 418 | //IM_ASSERT(font != NULL); 419 | 420 | // Upload Fonts 421 | { 422 | // Use any command queue 423 | VkCommandPool command_pool = m_VulkanWindow.Frames[m_VulkanWindow.FrameIndex].CommandPool; 424 | VkCommandBuffer command_buffer = m_VulkanWindow.Frames[m_VulkanWindow.FrameIndex].CommandBuffer; 425 | 426 | VkResult err = vkResetCommandPool(g_Device, command_pool, 0); 427 | check_vk_result(err); 428 | VkCommandBufferBeginInfo begin_info = {}; 429 | begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 430 | begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; 431 | err = vkBeginCommandBuffer(command_buffer, &begin_info); 432 | check_vk_result(err); 433 | 434 | ImGui_ImplVulkan_CreateFontsTexture(command_buffer); 435 | 436 | VkSubmitInfo end_info = {}; 437 | end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 438 | end_info.commandBufferCount = 1; 439 | end_info.pCommandBuffers = &command_buffer; 440 | err = vkEndCommandBuffer(command_buffer); 441 | check_vk_result(err); 442 | err = vkQueueSubmit(g_Queue, 1, &end_info, VK_NULL_HANDLE); 443 | check_vk_result(err); 444 | 445 | err = vkDeviceWaitIdle(g_Device); 446 | check_vk_result(err); 447 | ImGui_ImplVulkan_DestroyFontUploadObjects(); 448 | } 449 | 450 | return true; 451 | } 452 | 453 | int proto::Window::exec() 454 | { 455 | if (m_pGLFWWindow == nullptr) 456 | return 1; 457 | 458 | // Main loop 459 | while (!glfwWindowShouldClose(m_pGLFWWindow)) 460 | { 461 | // Poll and handle events (inputs, window resize, etc.) 462 | // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. 463 | // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. 464 | // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. 465 | // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. 466 | glfwPollEvents(); 467 | 468 | if (m_SwapChainRebuild) 469 | { 470 | m_SwapChainRebuild = false; 471 | ImGui_ImplVulkan_SetMinImageCount(m_minImageCount); 472 | ImGui_ImplVulkanH_CreateWindow(g_Instance, g_PhysicalDevice, g_Device, &m_VulkanWindow, g_QueueFamily, g_Allocator, m_SwapChainResizeWidth, m_SwapChainResizeHeight, m_minImageCount); 473 | m_VulkanWindow.FrameIndex = 0; 474 | } 475 | 476 | // Start the Dear ImGui frame 477 | ImGui_ImplVulkan_NewFrame(); 478 | ImGui_ImplGlfw_NewFrame(); 479 | 480 | ImGui::NewFrame(); 481 | 482 | if (updateUI() == false) 483 | { 484 | break; 485 | } 486 | 487 | // Rendering 488 | ImGui::Render(); 489 | 490 | memcpy(&m_VulkanWindow.ClearValue.color.float32[0], &m_clearColor, 4 * sizeof(float)); 491 | 492 | frameRender(); 493 | framePresent(); 494 | } 495 | 496 | // Cleanup 497 | VkResult err = vkDeviceWaitIdle(g_Device); 498 | check_vk_result(err); 499 | 500 | ImGui_ImplVulkan_Shutdown(); 501 | ImGui_ImplGlfw_Shutdown(); 502 | ImGui::DestroyContext(); 503 | 504 | ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &m_VulkanWindow, g_Allocator); 505 | cleanupVulkan(); 506 | 507 | glfwDestroyWindow(m_pGLFWWindow); 508 | glfwTerminate(); 509 | 510 | return 0; 511 | } 512 | -------------------------------------------------------------------------------- /gui/source/main.cpp: -------------------------------------------------------------------------------- 1 | #include "proto/ProtoWindow.h" 2 | #include "proto/WinHeapDbg.h" 3 | 4 | int main(int, char**) 5 | { 6 | proto::WinHeapDbg::init(); 7 | 8 | proto::ProtoWindow wnd; 9 | 10 | if (wnd.init(1920, 1080)) 11 | { 12 | return wnd.exec(); 13 | } 14 | 15 | return -1; 16 | } 17 | -------------------------------------------------------------------------------- /proto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/Proto/328e5c171ae85d9ba73d8b9fc4d504308199881e/proto.png --------------------------------------------------------------------------------