1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-07-22 15:17:45 +00:00

renderer: Add support for imgui

This commit is contained in:
Joshua Goins 2023-09-26 17:09:12 -04:00
parent 43c394bbf4
commit 5c9284106f
24 changed files with 51999 additions and 6 deletions

View file

@ -13,4 +13,8 @@ License: MIT
Files: extern/magic_enum/*
Copyright: 2019 - 2023 Daniil Goncharov
License: MIT
Files: extern/imgui/*
Copyright: 2014-2023 Omar Cornut
License: MIT

View file

@ -27,6 +27,7 @@ target_link_libraries(armoury PUBLIC
magic_enum
physis z
mdlpart
imgui
NovusCommon)
install(TARGETS armoury

View file

@ -7,6 +7,7 @@
#include <QThreadPool>
#include <QVBoxLayout>
#include <QtConcurrent>
#include <imgui.h>
#include "filecache.h"
#include "magic_enum.hpp"
@ -21,6 +22,8 @@ GearView::GearView(GameData* data, FileCache& cache) : data(data), cache(cache)
setLayout(layout);
mdlPart->requestUpdate = [this] {
ImGui::Text("Hello, world!");
if (updating) {
return;
}

View file

@ -17,4 +17,5 @@ target_include_directories(physis INTERFACE ${libphysis_SOURCE_DIR}/target/publi
target_link_libraries(physis INTERFACE unshield)
add_subdirectory(magic_enum)
add_subdirectory(tinygltf)
add_subdirectory(tinygltf)
add_subdirectory(imgui)

13
extern/imgui/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,13 @@
add_library(imgui STATIC)
target_sources(imgui PRIVATE
include/imconfig.h
include/imgui.h
src/imgui.cpp
src/imgui_demo.cpp
src/imgui_draw.cpp
src/imgui_internal.h
src/imgui_tables.cpp
src/imgui_widgets.cpp)
target_include_directories(imgui PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(imgui PUBLIC stb::stb)

135
extern/imgui/include/imconfig.h vendored Normal file
View file

@ -0,0 +1,135 @@
//-----------------------------------------------------------------------------
// DEAR IMGUI COMPILE-TIME OPTIONS
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
//-----------------------------------------------------------------------------
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
//---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
// #define IM_ASSERT(_EXPR) MyAssert(_EXPR)
// #define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI
// compatibility. DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() for
// each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
// #define IMGUI_API __declspec( dllexport )
// #define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
// #define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// #define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded
// into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read
// comments in imgui_demo.cpp.
// #define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
// #define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
// #define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and
// ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
//---- Don't implement some functions to reduce linkage requirements.
// #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with
// OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) #define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32]
// [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) #define
// IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require
// imm32.lib/.a) #define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). #define
// IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this
// is why this is not the default). #define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can
// implement them yourself (e.g. if you don't want to link with vsnprintf) #define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement
// ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. #define IMGUI_DISABLE_FILE_FUNCTIONS // Don't
// implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) #define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS //
// Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with
// fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. #define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement
// default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). #define IMGUI_DISABLE_SSE //
// Disable use of SSE intrinsics even if available
//---- Include imgui_user.h at the end of imgui.h as a convenience
// #define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
// #define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient
// languages, etc...) #define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
// #define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
// #define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
// #define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
// #define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
// #define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
// #define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
// #define IMGUI_USE_STB_SPRINTF
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) +
// the FreeType library (not provided). On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
// #define IMGUI_ENABLE_FREETYPE
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
// Only works in combination with IMGUI_ENABLE_FREETYPE.
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
// #define IMGUI_ENABLE_FREETYPE_LUNASVG
//---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
// #define IMGUI_ENABLE_STB_TRUETYPE
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- ...Or use Dear ImGui's own very basic math operators.
// #define IMGUI_DEFINE_MATH_OPERATORS
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
// #define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
// struct ImDrawList;
// struct ImDrawCmd;
// typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
// #define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
// #define IM_DEBUG_BREAK IM_ASSERT(0)
// #define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Enable slower asserts
// #define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
/*
namespace ImGui
{
void MyFunction(const char* name, MyMatrix44* mtx);
}
*/

4646
extern/imgui/include/imgui.h vendored Normal file

File diff suppressed because it is too large Load diff

15692
extern/imgui/src/imgui.cpp vendored Normal file

File diff suppressed because it is too large Load diff

8542
extern/imgui/src/imgui_demo.cpp vendored Normal file

File diff suppressed because it is too large Load diff

4575
extern/imgui/src/imgui_draw.cpp vendored Normal file

File diff suppressed because it is too large Load diff

4642
extern/imgui/src/imgui_internal.h vendored Normal file

File diff suppressed because it is too large Load diff

4178
extern/imgui/src/imgui_tables.cpp vendored Normal file

File diff suppressed because it is too large Load diff

9107
extern/imgui/src/imgui_widgets.cpp vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,7 @@
#include <glm/gtc/type_ptr.inl>
#include "filecache.h"
#include "imgui.h"
#include "tiny_gltf.h"
#ifndef USE_STANDALONE_WINDOW
@ -116,9 +117,18 @@ public:
}
void render() {
ImGui::SetCurrentContext(m_renderer->ctx);
auto &io = ImGui::GetIO();
io.DisplaySize = ImVec2(width(), height());
ImGui::NewFrame();
if (part->requestUpdate)
part->requestUpdate();
ImGui::Render();
glm::vec3 position(
part->cameraDistance * sin(part->yaw),
part->cameraDistance * part->pitch,

View file

@ -7,9 +7,9 @@ if(USE_STANDALONE_WINDOW)
set(EXTRA_LIBRARIES SDL2::SDL2)
endif()
add_library(renderer STATIC src/renderer.cpp ${EXTRA_SRC})
add_library(renderer STATIC src/renderer.cpp src/imguipass.cpp src/imguipass.h ${EXTRA_SRC})
target_include_directories(renderer PUBLIC include)
target_link_libraries(renderer PUBLIC Qt6::Core Vulkan::Vulkan physis z glm::glm ${EXTRA_LIBRARIES})
target_link_libraries(renderer PUBLIC Qt6::Core Vulkan::Vulkan physis z glm::glm imgui ${EXTRA_LIBRARIES})
target_compile_definitions(renderer PUBLIC GLM_FORCE_RADIANS GLM_FORCE_DEPTH_ZERO_TO_ONE)
if(USE_STANDALONE_WINDOW)

View file

@ -51,6 +51,9 @@ struct RenderModel {
VkDeviceMemory boneInfoMemory = VK_NULL_HANDLE;
};
class ImGuiPass;
struct ImGuiContext;
class Renderer {
public:
Renderer();
@ -124,6 +127,10 @@ public:
glm::mat4 view;
ImGuiContext *ctx = nullptr;
private:
void createDummyTexture();
ImGuiPass *imGuiPass = nullptr;
};

View file

@ -3,4 +3,6 @@
# SPDX-License-Identifier: CC0-1.0
glslc mesh.vert -o mesh.vert.spv &&
glslc mesh.frag -o mesh.frag.spv
glslc mesh.frag -o mesh.frag.spv &&
glslc imgui.vert -o imgui.vert.spv &&
glslc imgui.frag -o imgui.frag.spv

View file

@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: CC0-1.0
#version 460 core
layout(location = 0) in vec2 inUV;
layout(location = 1) in vec4 inColor;
layout(location = 0) out vec4 outColor;
layout(binding = 0) uniform sampler2D boundSampler;
void main() {
outColor = inColor * texture(boundSampler, inUV);
}

Binary file not shown.

View file

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: CC0-1.0
#version 460 core
layout(location = 0) in vec2 inPos;
layout(location = 1) in vec2 inUV;
layout(location = 2) in vec4 inColor;
layout(push_constant) uniform PushConstants {
vec2 scale, translate;
} pushConstants;
layout(location = 0) out vec2 outUV;
layout(location = 1) out vec4 outColor;
void main() {
gl_Position = vec4(inPos * pushConstants.scale + pushConstants.translate, 0.0, 1.0);
outUV = inUV;
outColor = inColor;
}

Binary file not shown.

341
renderer/src/imguipass.cpp Normal file
View file

@ -0,0 +1,341 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "imguipass.h"
#include <QDebug>
#include <array>
#include <glm/glm.hpp>
#include <imgui.h>
#include "renderer.hpp"
ImGuiPass::ImGuiPass(Renderer &renderer)
: renderer_(renderer)
{
createDescriptorSetLayout();
createPipeline();
createFontImage();
}
ImGuiPass::~ImGuiPass()
{
vkDestroySampler(renderer_.device, fontSampler_, nullptr);
vkDestroyImageView(renderer_.device, fontImageView_, nullptr);
vkFreeMemory(renderer_.device, fontMemory_, nullptr);
vkDestroyImage(renderer_.device, fontImage_, nullptr);
vkDestroyPipeline(renderer_.device, pipeline_, nullptr);
vkDestroyPipelineLayout(renderer_.device, pipelineLayout_, nullptr);
vkDestroyDescriptorSetLayout(renderer_.device, setLayout_, nullptr);
}
void ImGuiPass::render(VkCommandBuffer commandBuffer)
{
ImDrawData *drawData = ImGui::GetDrawData();
if (drawData == nullptr) {
return;
}
const size_t newVertexSize = drawData->TotalVtxCount * sizeof(ImDrawVert);
if (newVertexSize > vertexSize) {
createBuffer(vertexBuffer, vertexMemory, newVertexSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
vertexSize = newVertexSize;
}
const size_t newIndexSize = drawData->TotalIdxCount * sizeof(ImDrawIdx);
if (newIndexSize > indexSize) {
createBuffer(indexBuffer, indexMemory, newIndexSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
indexSize = newIndexSize;
}
if (vertexSize == 0 || indexSize == 0)
return;
ImDrawVert *vertexData = nullptr;
ImDrawIdx *indexData = nullptr;
vkMapMemory(renderer_.device, vertexMemory, 0, vertexSize, 0, reinterpret_cast<void **>(&vertexData));
vkMapMemory(renderer_.device, indexMemory, 0, indexSize, 0, reinterpret_cast<void **>(&indexData));
for (int i = 0; i < drawData->CmdListsCount; i++) {
const ImDrawList *cmd_list = drawData->CmdLists[i];
memcpy(vertexData, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
vertexData += cmd_list->VtxBuffer.Size;
memcpy(indexData, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
indexData += cmd_list->IdxBuffer.Size;
}
vkUnmapMemory(renderer_.device, vertexMemory);
vkUnmapMemory(renderer_.device, indexMemory);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_);
VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, &offset);
vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16);
float scale[2];
scale[0] = 2.0f / drawData->DisplaySize.x;
scale[1] = 2.0f / drawData->DisplaySize.y;
float translate[2];
translate[0] = -1.0f - drawData->DisplayPos.x * scale[0];
translate[1] = -1.0f - drawData->DisplayPos.y * scale[1];
vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale);
vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate);
int vertexOffset = 0, indexOffset = 0;
const ImVec2 displayPos = drawData->DisplayPos;
for (int n = 0; n < drawData->CmdListsCount; n++) {
const ImDrawList *cmd_list = drawData->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
if (descriptorSets_.count((VkImageView)pcmd->TextureId)) {
vkCmdBindDescriptorSets(commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelineLayout_,
0,
1,
&descriptorSets_[(VkImageView)pcmd->TextureId],
0,
nullptr);
} else {
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = renderer_.descriptorPool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &setLayout_;
VkDescriptorSet set = nullptr;
vkAllocateDescriptorSets(renderer_.device, &allocInfo, &set);
VkDescriptorImageInfo imageInfo = {};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = (VkImageView)pcmd->TextureId;
imageInfo.sampler = fontSampler_;
VkWriteDescriptorSet descriptorWrite = {};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.descriptorCount = 1;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite.dstSet = set;
descriptorWrite.pImageInfo = &imageInfo;
vkUpdateDescriptorSets(renderer_.device, 1, &descriptorWrite, 0, nullptr);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &set, 0, nullptr);
descriptorSets_[(VkImageView)pcmd->TextureId] = set;
}
if (pcmd->UserCallback) {
pcmd->UserCallback(cmd_list, pcmd);
} else {
VkRect2D scissor;
scissor.offset.x = (int32_t)(pcmd->ClipRect.x - displayPos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - displayPos.x) : 0;
scissor.offset.y = (int32_t)(pcmd->ClipRect.y - displayPos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - displayPos.y) : 0;
scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x);
scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1);
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdDrawIndexed(commandBuffer, pcmd->ElemCount, 1, indexOffset, vertexOffset, 0);
}
indexOffset += pcmd->ElemCount;
}
vertexOffset += cmd_list->VtxBuffer.Size;
}
}
void ImGuiPass::createDescriptorSetLayout()
{
VkDescriptorSetLayoutBinding samplerBinding = {};
samplerBinding.descriptorCount = 1;
samplerBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
createInfo.bindingCount = 1;
createInfo.pBindings = &samplerBinding;
vkCreateDescriptorSetLayout(renderer_.device, &createInfo, nullptr, &setLayout_);
}
void ImGuiPass::createPipeline()
{
VkShaderModule vertShaderModule = renderer_.loadShaderFromDisk("imgui.vert.spv");
VkShaderModule fragShaderModule = renderer_.loadShaderFromDisk("imgui.frag.spv");
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
const std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages = {vertShaderStageInfo, fragShaderStageInfo};
VkVertexInputBindingDescription vertexBindingDescription = {};
vertexBindingDescription.stride = sizeof(ImDrawVert);
vertexBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription positionAttribute = {};
positionAttribute.format = VK_FORMAT_R32G32_SFLOAT;
positionAttribute.offset = offsetof(ImDrawVert, pos);
VkVertexInputAttributeDescription uvAttribute = {};
uvAttribute.location = 1;
uvAttribute.format = VK_FORMAT_R32G32_SFLOAT;
uvAttribute.offset = offsetof(ImDrawVert, uv);
VkVertexInputAttributeDescription colorAttribute = {};
colorAttribute.location = 2;
colorAttribute.format = VK_FORMAT_R8G8B8A8_UNORM;
colorAttribute.offset = offsetof(ImDrawVert, col);
const std::array<VkVertexInputAttributeDescription, 3> attributes = {positionAttribute, uvAttribute, colorAttribute};
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDescription;
vertexInputInfo.vertexAttributeDescriptionCount = attributes.size();
vertexInputInfo.pVertexAttributeDescriptions = attributes.data();
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.cullMode = VK_CULL_MODE_NONE;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizer.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
const std::array<VkDynamicState, 2> dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = dynamicStates.size();
dynamicState.pDynamicStates = dynamicStates.data();
VkPushConstantRange pushConstant = {};
pushConstant.size = sizeof(glm::vec4);
pushConstant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &setLayout_;
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConstant;
vkCreatePipelineLayout(renderer_.device, &pipelineLayoutInfo, nullptr, &pipelineLayout_);
VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {};
depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = shaderStages.size();
pipelineInfo.pStages = shaderStages.data();
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = pipelineLayout_;
pipelineInfo.pDepthStencilState = &depthStencilStateCreateInfo;
pipelineInfo.renderPass = renderer_.renderPass;
vkCreateGraphicsPipelines(renderer_.device, nullptr, 1, &pipelineInfo, nullptr, &pipeline_);
vkDestroyShaderModule(renderer_.device, fragShaderModule, nullptr);
vkDestroyShaderModule(renderer_.device, vertShaderModule, nullptr);
}
void ImGuiPass::createFontImage()
{
ImGuiIO &io = ImGui::GetIO();
unsigned char *pixels = nullptr;
int width = 0, height = 0;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
qInfo() << "Uploading imgui font size" << width << "x" << height;
auto texture = renderer_.addTexture(width, height, pixels, width * height * 4);
fontImageView_ = texture.view;
fontSampler_ = texture.sampler;
io.Fonts->SetTexID(static_cast<ImTextureID>(fontImageView_));
}
void ImGuiPass::createBuffer(VkBuffer &buffer, VkDeviceMemory &memory, VkDeviceSize size, VkBufferUsageFlagBits bufferUsage)
{
if (buffer != nullptr)
vkDestroyBuffer(renderer_.device, buffer, nullptr);
if (memory != nullptr)
vkFreeMemory(renderer_.device, memory, nullptr);
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = bufferUsage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vkCreateBuffer(renderer_.device, &bufferInfo, nullptr, &buffer);
VkMemoryRequirements memRequirements = {};
vkGetBufferMemoryRequirements(renderer_.device, buffer, &memRequirements);
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex =
renderer_.findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkAllocateMemory(renderer_.device, &allocInfo, nullptr, &memory);
vkBindBufferMemory(renderer_.device, buffer, memory, 0);
}

43
renderer/src/imguipass.h Normal file
View file

@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <map>
#include <vulkan/vulkan.h>
class Renderer;
struct RenderTarget;
class ImGuiPass
{
public:
ImGuiPass(Renderer &renderer);
~ImGuiPass();
void render(VkCommandBuffer commandBuffer);
private:
void createDescriptorSetLayout();
void createPipeline();
void createFontImage();
void createBuffer(VkBuffer &buffer, VkDeviceMemory &memory, VkDeviceSize size, VkBufferUsageFlagBits bufferUsage);
VkDescriptorSetLayout setLayout_ = nullptr;
VkPipelineLayout pipelineLayout_ = nullptr;
VkPipeline pipeline_ = nullptr;
VkImage fontImage_ = nullptr;
VkDeviceMemory fontMemory_ = nullptr;
VkImageView fontImageView_ = nullptr;
VkSampler fontSampler_ = nullptr;
VkBuffer vertexBuffer = VK_NULL_HANDLE, indexBuffer = VK_NULL_HANDLE;
VkDeviceMemory vertexMemory = VK_NULL_HANDLE, indexMemory = VK_NULL_HANDLE;
size_t vertexSize = 0, indexSize = 0;
std::map<VkImageView, VkDescriptorSet> descriptorSets_ = {};
Renderer &renderer_;
};

View file

@ -12,6 +12,9 @@
#include <vector>
#include <vulkan/vulkan.h>
#include "imgui.h"
#include "imguipass.h"
VkResult CreateDebugUtilsMessengerEXT(
VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
@ -39,6 +42,11 @@ DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
}
Renderer::Renderer() {
ctx = ImGui::CreateContext();
ImGui::SetCurrentContext(ctx);
ImGui::StyleColorsDark();
VkApplicationInfo applicationInfo = {};
std::vector<const char*> instanceExtensions = {"VK_EXT_debug_utils"};
@ -422,6 +430,9 @@ bool Renderer::initSwapchain(VkSurfaceKHR surface, int width, int height) {
vkCreateFence(device, &fenceCreateInfo, nullptr, &inFlightFences[i]);
}
ImGui::SetCurrentContext(ctx);
imGuiPass = new ImGuiPass(*this);
return true;
}
@ -538,8 +549,12 @@ void Renderer::render(std::vector<RenderModel> models) {
}
}
vkCmdEndRenderPass(commandBuffer);
if (imGuiPass != nullptr) {
ImGui::SetCurrentContext(ctx);
imGuiPass->render(commandBuffer);
}
vkCmdEndRenderPass(commandBuffer);
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo = {};
@ -1191,7 +1206,7 @@ VkDescriptorSet Renderer::createDescriptorFor(const RenderModel& model, const Re
vkAllocateDescriptorSets(device, &allocateInfo, &set);
if (set == VK_NULL_HANDLE) {
qFatal("Failed to create descriptor set!");
// qFatal("Failed to create descriptor set!");
return VK_NULL_HANDLE;
}