纯core, 不带任何渲染功能
This commit is contained in:
parent
0acf109ef2
commit
c6a8e16393
@ -58,38 +58,6 @@ endfunction()
|
||||
|
||||
set(SHADER_CPP_GENERATED_CMAKE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateShaderCPPFile.cmake" CACHE STRING "" FORCE)
|
||||
|
||||
function(compile_shader TARGET_NAME OUTPUT SHADER_INCLUDE_DIR)
|
||||
set(working_dir "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(GLSLANG_BIN $ENV{VULKAN_SDK}/Bin/glslangValidator.exe) # 设置glslangValidator的位置
|
||||
set(ALL_SHADER_FILE "")
|
||||
# get files in SHADER_INCLUDE_DIR
|
||||
file(GLOB_RECURSE ALL_SHADER_FILE "${SHADER_INCLUDE_DIR}/*.vert" "${SHADER_INCLUDE_DIR}/*.frag" "${SHADER_INCLUDE_DIR}/*.comp" "${SHADER_INCLUDE_DIR}/*.tesc" "${SHADER_INCLUDE_DIR}/*.tese" "${SHADER_INCLUDE_DIR}/*.geom")
|
||||
|
||||
|
||||
foreach(SHADER ${ALL_SHADER_FILE}) # 遍历每一个shader源文件
|
||||
get_filename_component(SHADER_NAME ${SHADER} NAME) # 获取shader的名字
|
||||
string(REPLACE "." "_" HEADER_NAME ${SHADER_NAME}) # 在生成的.h文件中将文件名的'.'换成'_'
|
||||
string(TOUPPER ${HEADER_NAME} GLOBAL_SHADER_VAR) # 将存储二进制内容的全局vector对象名改为全部大写
|
||||
set(SPV_FILE "${OUTPUT}/spv/${SHADER_NAME}.spv") # 生成的.spv文件
|
||||
set(CPP_FILE "${OUTPUT}/cpp/${HEADER_NAME}.h") # 生成的.h文件
|
||||
add_custom_command(
|
||||
OUTPUT ${SPV_FILE}
|
||||
COMMAND ${GLSLANG_BIN} -I${SHADER_INCLUDE_DIR} -V100 -o ${SPV_FILE} ${SHADER}
|
||||
DEPENDS ${SHADER}
|
||||
WORKING_DIRECTORY "${working_dir}") # 添加编译命令,在项目生成时执行
|
||||
list(APPEND ALL_GENERATED_SPV_FILES ${SPV_FILE})
|
||||
add_custom_command(
|
||||
OUTPUT ${CPP_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -DPATH=${SPV_FILE} -DHEADER="${CPP_FILE}"
|
||||
-DGLOBAL="${GLOBAL_SHADER_VAR}" -P "${SHADER_CPP_GENERATED_CMAKE}"
|
||||
DEPENDS ${SPV_FILE}
|
||||
WORKING_DIRECTORY "${working_dir}") # 添加执行将spv转换为h文件的cmake函数的命令
|
||||
list(APPEND ALL_GENERATED_CPP_FILES ${CPP_FILE})
|
||||
endforeach()
|
||||
add_custom_target(${TARGET_NAME} # 将上述过程添加到一个生成目标中
|
||||
DEPENDS ${ALL_GENERATED_SPV_FILES} ${ALL_GENERATED_CPP_FILES} SOURCES ${SHADERS})
|
||||
endfunction()
|
||||
|
||||
# setup portaudio
|
||||
set(PA_USE_ASIO ON CACHE BOOL "" FORCE)
|
||||
set(PA_BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
|
||||
@ -123,10 +91,8 @@ set(TF_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(TF_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(third_party/imgui)
|
||||
add_subdirectory(third_party/portaudio)
|
||||
add_subdirectory(third_party/spdlog)
|
||||
add_subdirectory(third_party/glfw)
|
||||
add_subdirectory(third_party/mempool)
|
||||
add_subdirectory(third_party/taskflow)
|
||||
|
||||
|
@ -8,13 +8,13 @@ retrieve_files(ALL_FILES)
|
||||
add_library(${PROJECT_NAME} SHARED ${ALL_FILES})
|
||||
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PortAudio spdlog imgui glfw mempool Taskflow)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PortAudio spdlog mempool Taskflow)
|
||||
|
||||
target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PortAudio spdlog imgui glfw mempool Taskflow)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC PortAudio imgui spdlog glfw mempool Taskflow)
|
||||
target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PortAudio spdlog mempool Taskflow)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC PortAudio spdlog mempool Taskflow)
|
||||
|
||||
target_precompile_headers(${PROJECT_NAME} PUBLIC extern.h)
|
||||
add_definitions(-Dcore_EXPORTS -DSTB_IMAGE_IMPLEMENTATION -DSTBI_WINDOWS_UTF8)
|
||||
add_definitions(-Dcore_EXPORTS)
|
||||
|
||||
if (MSVC)
|
||||
add_custom_command(
|
||||
|
@ -3,12 +3,6 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "command_line.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "window_manager.h"
|
||||
#include "filesystem/stb_image.h"
|
||||
#include "rhi/texture.h"
|
||||
#include "rhi/renderer.h"
|
||||
#include "spdlog/async.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
@ -18,10 +12,6 @@ bool g_is_running = true;
|
||||
bool g_exit_requested = false;
|
||||
application* g_app_instance = nullptr;
|
||||
|
||||
static void glfw_error_callback(int error, const char* description) {
|
||||
spdlog::error("Glfw Error {}: {}", error, description);
|
||||
}
|
||||
|
||||
void application::init(const window_params& in_window_params, int argc, char** argv) {
|
||||
try {
|
||||
async_spdlog_ = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/log.txt");
|
||||
@ -30,103 +20,14 @@ void application::init(const window_params& in_window_params, int argc, char** a
|
||||
}
|
||||
|
||||
command_line::instance().init(argc, argv);
|
||||
init_glfw();
|
||||
init_imgui();
|
||||
|
||||
renderer_ = new renderer();
|
||||
|
||||
renderer_->pre_init();
|
||||
|
||||
const auto window = g_window_manager.create_main_window(in_window_params.title.c_str(), in_window_params.width, in_window_params.height);
|
||||
// new glfw window
|
||||
if (!window) {
|
||||
spdlog::error("Failed to create glfw window");
|
||||
return;
|
||||
}
|
||||
|
||||
renderer_->init(window);
|
||||
g_is_running = true;
|
||||
}
|
||||
|
||||
int application::run() {
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
while (!g_exit_requested) {
|
||||
g_main_thread_hub.process_messages();
|
||||
g_window_manager.tick();
|
||||
g_exit_requested = g_exit_requested || g_window_manager.should_close();
|
||||
tick(io.DeltaTime);
|
||||
renderer_->new_frame(g_window_manager.get_main_window());
|
||||
draw_gui();
|
||||
renderer_->end_frame(g_window_manager.get_main_window());
|
||||
}
|
||||
|
||||
shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void application::shutdown() {
|
||||
renderer_->shutdown();
|
||||
destroy_imgui();
|
||||
|
||||
delete renderer_;
|
||||
renderer_ = nullptr;
|
||||
|
||||
destroy_glfw();
|
||||
|
||||
g_is_running = false;
|
||||
}
|
||||
|
||||
void application::request_exit() {
|
||||
g_window_manager.request_exit();
|
||||
g_exit_requested = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<texture> application::load_texture(const std::string& path, vk::Format format) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
uint8_t* image_data = stbi_load(path.c_str(), &width, &height, nullptr, 4);
|
||||
if (!image_data) {
|
||||
spdlog::error("Failed to load texture: {}", path.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
const auto texture = renderer::create_texture(image_data, width, height, format);
|
||||
stbi_image_free(image_data);
|
||||
return texture;
|
||||
}
|
||||
|
||||
std::shared_ptr<texture> application::create_texture(const unsigned char* data, const int width, const int height, vk::Format format) {
|
||||
return renderer::create_texture(data, width, height, format);
|
||||
}
|
||||
|
||||
void application::init_glfw() {
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit()) {
|
||||
spdlog::error("Failed to initialize GLFW");
|
||||
return;
|
||||
}
|
||||
// glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
}
|
||||
|
||||
void application::init_imgui() {
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
init_imgui(ImGui::CreateContext());
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
|
||||
// io.ConfigViewportsNoAutoMerge = true;
|
||||
// io.ConfigViewportsNoTaskBarIcon = false;
|
||||
ImGui::StyleColorsDark();
|
||||
//ImGui::StyleColorsLight();
|
||||
}
|
||||
|
||||
void application::destroy_glfw() {
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
void application::destroy_imgui() {
|
||||
init_imgui(nullptr);
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "imgui.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
class renderer;
|
||||
class render_target;
|
||||
@ -44,37 +41,12 @@ public:
|
||||
|
||||
virtual void init(const window_params& in_window_params, int argc, char** argv);
|
||||
|
||||
virtual int run();
|
||||
|
||||
virtual void shutdown();
|
||||
|
||||
void request_exit();
|
||||
|
||||
virtual void draw_gui() = 0;
|
||||
virtual void tick(float delta_time) {}
|
||||
|
||||
virtual const char* get_shader_path() = 0;
|
||||
|
||||
virtual void init_imgui(ImGuiContext* in_context) = 0;
|
||||
|
||||
[[nodiscard]] static std::shared_ptr<texture> load_texture(const std::string& path, vk::Format format = vk::Format::eR8G8B8A8Unorm);
|
||||
[[nodiscard]] static std::shared_ptr<texture> create_texture(const unsigned char* data, int width, int height, vk::Format format);
|
||||
|
||||
[[nodiscard]] virtual const char* get_entry_model() const = 0;
|
||||
|
||||
[[nodiscard]] virtual const char* get_draw_ps_vertex_shader_entry() const = 0; // Vertex Shader used for drawing ps
|
||||
|
||||
[[nodiscard]] renderer* get_renderer() const { return renderer_; }
|
||||
protected:
|
||||
renderer* renderer_ = nullptr;
|
||||
std::shared_ptr<spdlog::logger> async_spdlog_;
|
||||
|
||||
private:
|
||||
static void init_glfw();
|
||||
|
||||
void init_imgui();
|
||||
|
||||
void destroy_glfw();
|
||||
|
||||
void destroy_imgui();
|
||||
};
|
||||
|
@ -1,95 +0,0 @@
|
||||
#include "window_manager.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "audio/plugin_host/plugin_host.h"
|
||||
|
||||
window_manager::window_manager() {
|
||||
main_window_ = nullptr;
|
||||
}
|
||||
|
||||
void window_manager::init(singleton_initliazer& initliazer) {
|
||||
singleton_t<window_manager>::init(initliazer);
|
||||
// start_idle_thread();
|
||||
}
|
||||
|
||||
void window_manager::release(singleton_release_guard& release_guard) {
|
||||
singleton_t<window_manager>::release(release_guard);
|
||||
if (main_window_)
|
||||
glfwDestroyWindow(main_window_);
|
||||
}
|
||||
|
||||
void window_manager::tick() {
|
||||
glfwPollEvents();
|
||||
if (should_close()) {
|
||||
destroy_all_plugin_host_window();
|
||||
return;
|
||||
}
|
||||
update_host_window();
|
||||
}
|
||||
|
||||
void window_manager::destroy_all_plugin_host_window() {
|
||||
for (const auto& window: host_window_map_ | std::views::values) {
|
||||
glfwDestroyWindow(window);
|
||||
}
|
||||
host_window_map_.clear();
|
||||
}
|
||||
|
||||
GLFWwindow* window_manager::create_main_window(const char* title, int width, int height) {
|
||||
if (!main_window_)
|
||||
main_window_ = glfwCreateWindow(width, height, title, nullptr, nullptr);
|
||||
return main_window_;
|
||||
}
|
||||
|
||||
GLFWwindow* window_manager::create_plugin_host_window(plugin_host* host) {
|
||||
if (!host->has_editor())
|
||||
return nullptr;
|
||||
if (host_window_map_.contains(host))
|
||||
return host_window_map_[host];
|
||||
auto editor_size = host->get_editor_size();
|
||||
auto new_window = glfwCreateWindow(editor_size.x, editor_size.y, host->name.c_str(), nullptr, nullptr);
|
||||
if (host_window_pos_map_.contains(host)) {
|
||||
auto pos = host_window_pos_map_[host];
|
||||
glfwSetWindowPos(new_window, pos.x, pos.y);
|
||||
}else {
|
||||
glfwSetWindowPos(new_window, 100, 100);
|
||||
}
|
||||
host_window_map_[host] = new_window;
|
||||
glfwShowWindow(new_window);
|
||||
return new_window;
|
||||
}
|
||||
|
||||
void window_manager::destroy_plugin_host_window(plugin_host* host) {
|
||||
if (!host_window_map_.contains(host))
|
||||
return;
|
||||
int x, y;
|
||||
glfwGetWindowPos(host_window_map_[host], &x, &y);
|
||||
glfwDestroyWindow(host_window_map_[host]);
|
||||
host_window_map_.erase(host);
|
||||
host_window_pos_map_[host] = ImVec2(x, y);
|
||||
}
|
||||
|
||||
void window_manager::resize_plugin_host_window(plugin_host* host, int width, int height) {
|
||||
if (!host_window_map_.contains(host))
|
||||
return;
|
||||
glfwSetWindowSize(host_window_map_[host], width, height);
|
||||
}
|
||||
|
||||
void window_manager::idle_plugin_host_window() const {
|
||||
for (const auto& window_map: host_window_map_) {
|
||||
window_map.first->idle_editor();
|
||||
}
|
||||
}
|
||||
|
||||
void window_manager::update_host_window() {
|
||||
std::vector<plugin_host*> host_editor_to_close;
|
||||
for (const auto& [host, window]: host_window_map_) {
|
||||
if (glfwWindowShouldClose(window)) {
|
||||
host_editor_to_close.push_back(host);
|
||||
}
|
||||
}
|
||||
for (auto host: host_editor_to_close) {
|
||||
host->try_close_editor();
|
||||
}
|
||||
idle_plugin_host_window();
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "misc/singleton/singleton.h"
|
||||
#include <map>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
class plugin_host;
|
||||
|
||||
class window_manager : public singleton_t<window_manager> {
|
||||
public:
|
||||
window_manager();
|
||||
void init(singleton_initliazer& initliazer) override;
|
||||
void release(singleton_release_guard& release_guard) override;
|
||||
void tick();
|
||||
bool should_close() const { return glfwWindowShouldClose(main_window_); }
|
||||
void destroy_all_plugin_host_window();
|
||||
|
||||
GLFWwindow* create_main_window(const char* title, int width, int height);
|
||||
void request_exit() const { glfwSetWindowShouldClose(main_window_, GLFW_TRUE); }
|
||||
GLFWwindow* get_main_window() const { return main_window_; }
|
||||
|
||||
GLFWwindow* create_plugin_host_window(plugin_host* host);
|
||||
void destroy_plugin_host_window(plugin_host* host);
|
||||
void resize_plugin_host_window(plugin_host* host, int width, int height);
|
||||
void idle_plugin_host_window() const;
|
||||
|
||||
const char* get_name() override { return "window_manager"; }
|
||||
private:
|
||||
|
||||
void update_host_window();
|
||||
|
||||
GLFWwindow* main_window_;
|
||||
std::map<plugin_host*, GLFWwindow*> host_window_map_;
|
||||
std::map<plugin_host*, ImVec2> host_window_pos_map_;
|
||||
};
|
||||
|
||||
DEFINE_SINGLETON_INSTANCE(window_manager)
|
@ -1,6 +1,5 @@
|
||||
#include "plugin_host.h"
|
||||
|
||||
#include "application/window_manager.h"
|
||||
#include "audio/mixer/channel_interface.h"
|
||||
|
||||
plugin_host::~plugin_host() {
|
||||
@ -12,8 +11,8 @@ void plugin_host::try_open_editor() {
|
||||
return;
|
||||
if (editor_opened)
|
||||
return;
|
||||
editor_window = g_window_manager.create_plugin_host_window(this);
|
||||
open_editor(editor_window);
|
||||
// editor_window = g_window_manager.create_plugin_host_window(this);
|
||||
// open_editor(editor_window);
|
||||
editor_opened = true;
|
||||
}
|
||||
|
||||
@ -24,7 +23,7 @@ void plugin_host::try_close_editor() {
|
||||
return;
|
||||
close_editor();
|
||||
editor_opened = false;
|
||||
g_window_manager.destroy_plugin_host_window(this);
|
||||
// g_window_manager.destroy_plugin_host_window(this);
|
||||
}
|
||||
|
||||
void plugin_host::toggle_editor() {
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#include "imgui.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
class mixer_track;
|
||||
class channel_interface;
|
||||
@ -28,7 +26,7 @@ public:
|
||||
virtual void idle_editor() {}
|
||||
|
||||
[[nodiscard]] virtual bool has_editor() const { return false; }
|
||||
[[nodiscard]] virtual ImVec2 get_editor_size() const { return ImVec2(0, 0); }
|
||||
// [[nodiscard]] virtual ImVec2 get_editor_size() const { return ImVec2(0, 0); }
|
||||
|
||||
[[nodiscard]] virtual std::string load_name() const { return ""; }
|
||||
[[nodiscard]] virtual std::string load_vendor() const { return ""; }
|
||||
@ -39,9 +37,8 @@ public:
|
||||
std::string vendor;
|
||||
channel_interface* channel = nullptr;
|
||||
std::vector<mixer_track*> owner_tracks;
|
||||
GLFWwindow* editor_window = nullptr;
|
||||
bool editor_opened = false;
|
||||
protected:
|
||||
virtual void open_editor(GLFWwindow* window) = 0;
|
||||
virtual void open_editor() = 0;
|
||||
virtual void close_editor() = 0;
|
||||
};
|
||||
|
@ -1,9 +1,7 @@
|
||||
#include "vst2_plugin_host.h"
|
||||
|
||||
#include "application/window_manager.h"
|
||||
#include "audio/device/audio_device_manager.h"
|
||||
#include "audio/mixer/channel_interface.h"
|
||||
#include "misc/glfw_misc.h"
|
||||
|
||||
std::map<std::string, std::weak_ptr<dynamic_library>> vst2_library_map;
|
||||
VstTimeInfo vst2_plugin_host::vst_time_info{};
|
||||
@ -65,7 +63,7 @@ VstIntPtr vst_master_callback(AEffect* effect, VstInt32 opcode, VstInt32 index,
|
||||
case audioMasterSizeWindow:
|
||||
{
|
||||
// 设置插件窗口大小
|
||||
g_window_manager.resize_plugin_host_window((plugin_host*)effect->user, index, value);
|
||||
// g_window_manager.resize_plugin_host_window((plugin_host*)effect->user, index, value);
|
||||
return 1;
|
||||
}
|
||||
case audioMasterGetTime:
|
||||
@ -226,13 +224,13 @@ bool vst2_plugin_host::has_editor() const {
|
||||
return effect_->flags & effFlagsHasEditor;
|
||||
}
|
||||
|
||||
ImVec2 vst2_plugin_host::get_editor_size() const {
|
||||
ERect* EditorRect = nullptr;
|
||||
dispatch(effEditGetRect, 0, 0, &EditorRect);
|
||||
if (EditorRect)
|
||||
return ImVec2(EditorRect->right - EditorRect->left, EditorRect->bottom - EditorRect->top);
|
||||
return ImVec2(0, 0);
|
||||
}
|
||||
// ImVec2 vst2_plugin_host::get_editor_size() const {
|
||||
// ERect* EditorRect = nullptr;
|
||||
// dispatch(effEditGetRect, 0, 0, &EditorRect);
|
||||
// if (EditorRect)
|
||||
// return ImVec2(EditorRect->right - EditorRect->left, EditorRect->bottom - EditorRect->top);
|
||||
// return ImVec2(0, 0);
|
||||
// }
|
||||
|
||||
std::string vst2_plugin_host::load_name() const {
|
||||
char buffer[256];
|
||||
@ -246,12 +244,12 @@ std::string vst2_plugin_host::load_vendor() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void vst2_plugin_host::open_editor(GLFWwindow* window) {
|
||||
void vst2_plugin_host::open_editor() {
|
||||
if (!has_editor())
|
||||
return;
|
||||
void* window_handle = glfwGetWindowHandle(window);
|
||||
// void* window_handle = glfwGetWindowHandle(window);
|
||||
|
||||
dispatch(effEditOpen, 0, 0, window_handle);
|
||||
// dispatch(effEditOpen, 0, 0, window_handle);
|
||||
}
|
||||
|
||||
void vst2_plugin_host::close_editor() {
|
||||
|
@ -24,12 +24,12 @@ public:
|
||||
|
||||
void idle_editor() override;
|
||||
[[nodiscard]] bool has_editor() const override;
|
||||
[[nodiscard]] ImVec2 get_editor_size() const override;
|
||||
// [[nodiscard]] ImVec2 get_editor_size() const override;
|
||||
|
||||
[[nodiscard]] std::string load_name() const override;
|
||||
[[nodiscard]] std::string load_vendor() const override;
|
||||
protected:
|
||||
void open_editor(GLFWwindow* window) override;
|
||||
void open_editor() override;
|
||||
void close_editor() override;
|
||||
private:
|
||||
VstIntPtr dispatch(VstInt32 opcode, VstInt32 index = 0, VstIntPtr value = 0, void* ptr = nullptr, float opt = 0) const;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,20 +0,0 @@
|
||||
#include "glfw_misc.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define GLFW_EXPOSE_NATIVE_WIN32
|
||||
#elif defined(__APPLE__)
|
||||
#define GLFW_EXPOSE_NATIVE_COCOA
|
||||
#else
|
||||
#define GLFW_EXPOSE_NATIVE_X11
|
||||
#endif
|
||||
#include <GLFW/glfw3native.h>
|
||||
|
||||
void* glfwGetWindowHandle(GLFWwindow* window) {
|
||||
#ifdef WIN32
|
||||
return glfwGetWin32Window(window);
|
||||
#elif defined(__APPLE__)
|
||||
return glfwGetCocoaWindow(window);
|
||||
#else
|
||||
return glfwGetX11Window(window);
|
||||
#endif
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
CORE_API void* glfwGetWindowHandle(GLFWwindow* window);
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
#include "buffer_vk.h"
|
||||
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
buffer_vk::buffer_vk() {
|
||||
buffer = nullptr;
|
||||
memory = nullptr;
|
||||
descriptor_type = vk::DescriptorType::eUniformBuffer;
|
||||
}
|
||||
|
||||
void buffer_vk::create(uint32_t size, vk::BufferUsageFlagBits in_usage, vk::DescriptorType in_descriptor_type) {
|
||||
destroy();
|
||||
buffer_size = size;
|
||||
descriptor_type = in_descriptor_type;
|
||||
const renderer* render_vk = application::get()->get_renderer();
|
||||
const vk::Device& device = render_vk->device;
|
||||
|
||||
vk::BufferCreateInfo buffer_create_info;
|
||||
buffer_create_info.setSize(size);
|
||||
buffer_create_info.setUsage(in_usage);
|
||||
buffer_create_info.setSharingMode(vk::SharingMode::eExclusive);
|
||||
buffer = device.createBuffer(buffer_create_info);
|
||||
|
||||
const vk::PhysicalDeviceMemoryProperties memory_properties = render_vk->physical_device.getMemoryProperties();
|
||||
const vk::MemoryRequirements memory_requirements = device.getBufferMemoryRequirements(buffer);
|
||||
const auto memory_type = vk::su::findMemoryType(memory_properties, memory_requirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
|
||||
vk::MemoryAllocateInfo memory_allocate_info;
|
||||
memory_allocate_info.setAllocationSize(memory_requirements.size);
|
||||
memory_allocate_info.setMemoryTypeIndex(memory_type);
|
||||
memory = device.allocateMemory(memory_allocate_info);
|
||||
device.bindBufferMemory(buffer, memory, 0);
|
||||
}
|
||||
|
||||
void buffer_vk::create_storage(uint32_t size) {
|
||||
create(size, vk::BufferUsageFlagBits::eStorageBuffer, vk::DescriptorType::eStorageBuffer);
|
||||
}
|
||||
|
||||
void buffer_vk::create_uniform(uint32_t size) {
|
||||
create(size, vk::BufferUsageFlagBits::eUniformBuffer, vk::DescriptorType::eUniformBuffer);
|
||||
}
|
||||
|
||||
void buffer_vk::create_staging(uint32_t size) {
|
||||
create(size, vk::BufferUsageFlagBits::eTransferSrc, vk::DescriptorType::eUniformBuffer);
|
||||
}
|
||||
|
||||
void buffer_vk::destroy() {
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
const renderer* render_vk = application::get()->get_renderer();
|
||||
const vk::Device& device = render_vk->device;
|
||||
|
||||
device.destroyBuffer(buffer);
|
||||
device.freeMemory(memory);
|
||||
|
||||
buffer = nullptr;
|
||||
memory = nullptr;
|
||||
}
|
||||
|
||||
void buffer_vk::update(const void* data, uint32_t size) {
|
||||
const auto p = map<void>();
|
||||
memcpy(p, data, size);
|
||||
unmap();
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
#pragma once
|
||||
#include <application/application.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
class CORE_API buffer_vk {
|
||||
public:
|
||||
buffer_vk();
|
||||
~buffer_vk() { destroy(); }
|
||||
|
||||
void create(uint32_t size, vk::BufferUsageFlagBits in_usage, vk::DescriptorType in_descriptor_type);
|
||||
void create_storage(uint32_t size);
|
||||
void create_uniform(uint32_t size);
|
||||
void create_staging(uint32_t size);
|
||||
void destroy();
|
||||
|
||||
template <typename T>
|
||||
T* map() {
|
||||
const auto& device = application::get()->get_renderer()->device;
|
||||
void* mapped;
|
||||
const auto err = device.mapMemory(memory, 0, buffer_size, vk::MemoryMapFlags(), &mapped);
|
||||
check_vk_result(err);
|
||||
return static_cast<T*>(mapped);
|
||||
}
|
||||
void unmap() const {
|
||||
const auto& device = application::get()->get_renderer()->device;
|
||||
device.unmapMemory(memory);
|
||||
}
|
||||
|
||||
void update(const void* data, uint32_t size);
|
||||
template <typename T>
|
||||
void update(const T& data) {
|
||||
update(&data, sizeof(T));
|
||||
}
|
||||
template <typename T>
|
||||
void update(const std::vector<T>& data) {
|
||||
update(data.data(), data.size() * sizeof(T));
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::DescriptorBufferInfo get_descriptor_info() const {
|
||||
vk::DescriptorBufferInfo descriptor_buffer_info;
|
||||
descriptor_buffer_info.setBuffer(buffer);
|
||||
descriptor_buffer_info.setOffset(0);
|
||||
descriptor_buffer_info.setRange(buffer_size);
|
||||
return descriptor_buffer_info;
|
||||
}
|
||||
|
||||
vk::Buffer buffer;
|
||||
vk::DeviceMemory memory;
|
||||
vk::DescriptorType descriptor_type;
|
||||
uint32_t buffer_size;
|
||||
};
|
@ -1,73 +0,0 @@
|
||||
#include "compute_pipeline.h"
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
compute_pipeline::~compute_pipeline() {
|
||||
const renderer* render_vk = application::get()->get_renderer();
|
||||
const vk::Device& device = render_vk->device;
|
||||
|
||||
device.destroyShaderModule(shader_module_);
|
||||
}
|
||||
|
||||
void compute_pipeline::add_binding(uint32_t binding, vk::DescriptorType descriptor_type, uint32_t descriptor_count,
|
||||
vk::Sampler immutable_samplers) {
|
||||
const vk::ShaderStageFlags flag = vk::ShaderStageFlagBits::eCompute;
|
||||
|
||||
vk::DescriptorSetLayoutBinding descriptor_set_layout_binding;
|
||||
descriptor_set_layout_binding.setBinding(binding);
|
||||
descriptor_set_layout_binding.setDescriptorType(descriptor_type);
|
||||
descriptor_set_layout_binding.setDescriptorCount(descriptor_count);
|
||||
descriptor_set_layout_binding.setStageFlags(flag);
|
||||
if (immutable_samplers)
|
||||
descriptor_set_layout_binding.setImmutableSamplers(immutable_samplers);
|
||||
descriptor_set_layout_bindings_.push_back(descriptor_set_layout_binding);
|
||||
}
|
||||
|
||||
void compute_pipeline::create() {
|
||||
const renderer* render_vk = application::get()->get_renderer();
|
||||
const vk::Device& device = render_vk->device;
|
||||
|
||||
create_pipeline_layout();
|
||||
|
||||
vk::PipelineShaderStageCreateInfo pipeline_shader_stage_create_info;
|
||||
pipeline_shader_stage_create_info.setStage(vk::ShaderStageFlagBits::eCompute);
|
||||
pipeline_shader_stage_create_info.setModule(shader_module_);
|
||||
pipeline_shader_stage_create_info.setPName("main");
|
||||
|
||||
vk::ComputePipelineCreateInfo pipeline_create_info;
|
||||
pipeline_create_info.setLayout(pipeline_layout_);
|
||||
pipeline_create_info.setStage(pipeline_shader_stage_create_info);
|
||||
const auto pipeline_result = device.createComputePipeline(VK_NULL_HANDLE, pipeline_create_info);
|
||||
|
||||
check_vk_result(pipeline_result.result);
|
||||
pipeline_ = pipeline_result.value;
|
||||
}
|
||||
|
||||
void compute_pipeline::dispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) const {
|
||||
#ifdef _DEBUG
|
||||
if (pipeline_ == vk::Pipeline()) {
|
||||
throw std::runtime_error("Pipeline not created");
|
||||
}
|
||||
#endif
|
||||
renderer* render_vk = application::get()->get_renderer();
|
||||
|
||||
const vk::CommandBuffer command_buffer = render_vk->create_command_buffer(vk::CommandBufferLevel::ePrimary, true);
|
||||
|
||||
command_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline_);
|
||||
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipeline_layout_, 0, descriptor_set_, nullptr);
|
||||
command_buffer.dispatch(group_count_x, group_count_y, group_count_z);
|
||||
|
||||
render_vk->end_command_buffer(command_buffer, true);
|
||||
}
|
||||
|
||||
void compute_pipeline::set_shader(const uint8_t* shader_code, size_t shader_code_size) {
|
||||
const renderer* render_vk = application::get()->get_renderer();
|
||||
const vk::Device& device = render_vk->device;
|
||||
|
||||
vk::ShaderModuleCreateInfo shader_module_create_info;
|
||||
shader_module_create_info.setCodeSize(shader_code_size);
|
||||
shader_module_create_info.setPCode(reinterpret_cast<const uint32_t*>(shader_code));
|
||||
shader_module_ = device.createShaderModule(shader_module_create_info);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
#include "pipeline.h"
|
||||
|
||||
class CORE_API compute_pipeline : public pipeline {
|
||||
public:
|
||||
~compute_pipeline() override;
|
||||
void add_binding(uint32_t binding, vk::DescriptorType descriptor_type, uint32_t descriptor_count, vk::Sampler immutable_samplers) override;
|
||||
void create() override;
|
||||
|
||||
void dispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) const;
|
||||
|
||||
void set_shader(const uint8_t *shader_code, size_t shader_code_size);
|
||||
private:
|
||||
vk::ShaderModule shader_module_;
|
||||
};
|
@ -1,119 +0,0 @@
|
||||
#include "pipeline.h"
|
||||
|
||||
#include "renderer.h"
|
||||
#include "texture.h"
|
||||
|
||||
pipeline::~pipeline() {
|
||||
const renderer* render_vk = application::get()->get_renderer();
|
||||
const vk::Device& device = render_vk->device;
|
||||
|
||||
device.destroyDescriptorPool(descriptor_pool_);
|
||||
device.destroyDescriptorSetLayout(descriptor_set_layout_);
|
||||
device.destroyPipelineLayout(pipeline_layout_);
|
||||
device.destroyPipeline(pipeline_);
|
||||
}
|
||||
|
||||
void pipeline::add_uniform_buffer(uint32_t binding, std::shared_ptr<buffer_vk> buf) {
|
||||
add_binding(binding, vk::DescriptorType::eUniformBuffer, 1, nullptr);
|
||||
buffers_[binding] = buf;
|
||||
}
|
||||
|
||||
void pipeline::add_storage_buffer(uint32_t binding, std::shared_ptr<buffer_vk> buf) {
|
||||
add_binding(binding, vk::DescriptorType::eStorageBuffer, 1, nullptr);
|
||||
buffers_[binding] = buf;
|
||||
}
|
||||
|
||||
void pipeline::add_sampled_image(uint32_t binding, std::shared_ptr<render_resource> in_texture) {
|
||||
add_binding(binding, vk::DescriptorType::eSampledImage, 1, in_texture->sampler);
|
||||
textures_[binding] = in_texture;
|
||||
}
|
||||
|
||||
void pipeline::add_storage_image(uint32_t binding, std::shared_ptr<render_resource> in_texture) {
|
||||
add_binding(binding, vk::DescriptorType::eStorageImage, 1, in_texture->sampler);
|
||||
textures_[binding] = in_texture;
|
||||
}
|
||||
|
||||
void pipeline::add_input_attachment(uint32_t binding) {
|
||||
add_binding(binding, vk::DescriptorType::eInputAttachment, 1, nullptr);
|
||||
}
|
||||
|
||||
void pipeline::add_sampler(uint32_t binding, vk::Sampler immutable_samplers) {
|
||||
add_binding(binding, vk::DescriptorType::eSampler, 1, immutable_samplers);
|
||||
}
|
||||
|
||||
void pipeline::add_combined_image(uint32_t binding, std::shared_ptr<render_resource> in_texture) {
|
||||
add_binding(binding, vk::DescriptorType::eCombinedImageSampler, 1, in_texture->sampler);
|
||||
textures_[binding] = in_texture;
|
||||
}
|
||||
|
||||
void pipeline::create_pipeline_layout() {
|
||||
const renderer* render_vk = application::get()->get_renderer();
|
||||
const vk::Device& device = render_vk->device;
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo descriptor_set_layout_create_info;
|
||||
descriptor_set_layout_create_info.setBindings(descriptor_set_layout_bindings_);
|
||||
descriptor_set_layout_ = device.createDescriptorSetLayout(descriptor_set_layout_create_info);
|
||||
|
||||
std::vector<vk::DescriptorPoolSize> pool_sizes;
|
||||
{
|
||||
std::map<vk::DescriptorType, uint32_t> temp_pool_sizes;
|
||||
for (const auto& binding: descriptor_set_layout_bindings_) {
|
||||
temp_pool_sizes[binding.descriptorType]++;
|
||||
}
|
||||
for (const auto& pair: temp_pool_sizes) {
|
||||
pool_sizes.emplace_back(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
vk::DescriptorPoolCreateInfo descriptor_pool_create_info;
|
||||
descriptor_pool_create_info.setMaxSets(1);
|
||||
descriptor_pool_create_info.setPoolSizes(pool_sizes);
|
||||
descriptor_pool_ = device.createDescriptorPool(descriptor_pool_create_info);
|
||||
|
||||
vk::DescriptorSetAllocateInfo descriptor_set_allocate_info;
|
||||
descriptor_set_allocate_info.setDescriptorPool(descriptor_pool_);
|
||||
descriptor_set_allocate_info.setDescriptorSetCount(1);
|
||||
descriptor_set_allocate_info.setSetLayouts(descriptor_set_layout_);
|
||||
const std::vector<vk::DescriptorSet>& sets = device.allocateDescriptorSets(descriptor_set_allocate_info);
|
||||
descriptor_set_ = sets.front();
|
||||
|
||||
std::vector<vk::WriteDescriptorSet> write_descriptor_sets;
|
||||
std::vector<std::vector<vk::DescriptorBufferInfo>> temp_buffer_infos;
|
||||
std::vector<std::vector<vk::DescriptorImageInfo>> temp_image_infoses;
|
||||
|
||||
for (const auto& binding_info: descriptor_set_layout_bindings_) {
|
||||
vk::WriteDescriptorSet write_descriptor_set;
|
||||
write_descriptor_set.setDstSet(descriptor_set_);
|
||||
write_descriptor_set.setDstBinding(binding_info.binding);
|
||||
write_descriptor_set.setDescriptorType(binding_info.descriptorType);
|
||||
|
||||
switch (binding_info.descriptorType) {
|
||||
case vk::DescriptorType::eCombinedImageSampler:
|
||||
case vk::DescriptorType::eSampledImage:
|
||||
case vk::DescriptorType::eStorageImage: {
|
||||
const auto& t = textures_[binding_info.binding];
|
||||
vk::DescriptorImageInfo image_info = t->get_descriptor_info();
|
||||
temp_image_infoses.push_back({image_info});
|
||||
write_descriptor_set.setImageInfo(temp_image_infoses.back());
|
||||
}
|
||||
break;
|
||||
case vk::DescriptorType::eUniformBuffer:
|
||||
case vk::DescriptorType::eStorageBuffer: {
|
||||
const auto& b = buffers_[binding_info.binding];
|
||||
vk::DescriptorBufferInfo buffer_info = b->get_descriptor_info();
|
||||
temp_buffer_infos.push_back( {buffer_info} );
|
||||
write_descriptor_set.setBufferInfo(temp_buffer_infos.back());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
write_descriptor_sets.push_back(write_descriptor_set);
|
||||
}
|
||||
|
||||
device.updateDescriptorSets(write_descriptor_sets, nullptr);
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipeline_layout_create_info;
|
||||
pipeline_layout_create_info.setSetLayouts(descriptor_set_layout_);
|
||||
pipeline_layout_ = device.createPipelineLayout(pipeline_layout_create_info);
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <map>
|
||||
|
||||
#include "buffer_vk.h"
|
||||
|
||||
class render_resource;
|
||||
class texture;
|
||||
|
||||
/// 1. add_binding
|
||||
/// 2. create();
|
||||
/// 3. dispatch();
|
||||
class CORE_API pipeline {
|
||||
public:
|
||||
virtual ~pipeline();
|
||||
|
||||
virtual void add_binding(uint32_t binding, vk::DescriptorType descriptor_type, uint32_t descriptor_count, vk::Sampler immutable_samplers) = 0;
|
||||
|
||||
void add_uniform_buffer(uint32_t binding, std::shared_ptr<buffer_vk> buf);
|
||||
|
||||
void add_storage_buffer(uint32_t binding, std::shared_ptr<buffer_vk> buf);
|
||||
|
||||
void add_sampled_image(uint32_t binding, std::shared_ptr<render_resource> in_texture);
|
||||
|
||||
void add_storage_image(uint32_t binding, std::shared_ptr<render_resource> in_texture);
|
||||
|
||||
void add_input_attachment(uint32_t binding);
|
||||
|
||||
void add_sampler(uint32_t binding, vk::Sampler immutable_samplers = nullptr);
|
||||
|
||||
void add_combined_image(uint32_t binding, std::shared_ptr<render_resource> in_texture);
|
||||
|
||||
virtual void create() = 0;
|
||||
protected:
|
||||
vk::Pipeline pipeline_;
|
||||
vk::PipelineLayout pipeline_layout_;
|
||||
vk::DescriptorSetLayout descriptor_set_layout_;
|
||||
vk::DescriptorSet descriptor_set_;
|
||||
|
||||
vk::DescriptorPool descriptor_pool_;
|
||||
std::vector<vk::DescriptorSetLayoutBinding> descriptor_set_layout_bindings_;
|
||||
|
||||
std::map<uint32_t, std::shared_ptr<buffer_vk>> buffers_;
|
||||
std::map<uint32_t, std::shared_ptr<render_resource>> textures_;
|
||||
protected:
|
||||
void create_pipeline_layout();
|
||||
};
|
@ -1,74 +0,0 @@
|
||||
#pragma once
|
||||
#include "imgui.h"
|
||||
#include "renderer.h"
|
||||
#include "application/application.h"
|
||||
|
||||
class render_resource : public std::enable_shared_from_this<render_resource> {
|
||||
public:
|
||||
render_resource() {
|
||||
format = vk::Format::eUndefined;
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
image = nullptr;
|
||||
image_view = nullptr;
|
||||
sampler = nullptr;
|
||||
memory = nullptr;
|
||||
descriptor_set = nullptr;
|
||||
}
|
||||
virtual ~render_resource() {
|
||||
render_resource::destroy();
|
||||
}
|
||||
|
||||
[[nodiscard]] int get_width() const { return width_; }
|
||||
[[nodiscard]] int get_height() const { return height_; }
|
||||
[[nodiscard]] ImTextureID get_texture_id() const { return descriptor_set; }
|
||||
[[nodiscard]] vk::DescriptorImageInfo get_descriptor_info() const {
|
||||
vk::DescriptorImageInfo info;
|
||||
info.setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
info.setImageView(image_view);
|
||||
info.setSampler(sampler);
|
||||
return info;
|
||||
}
|
||||
|
||||
void init(uint32_t width, uint32_t height, vk::Format in_format) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
format = in_format;
|
||||
on_init();
|
||||
}
|
||||
void resize(uint32_t width, uint32_t height) {
|
||||
if (width_ == width && height_ == height) {
|
||||
return;
|
||||
}
|
||||
destroy();
|
||||
init(width, height, format);
|
||||
}
|
||||
void draw() const {
|
||||
ImGui::Image(get_texture_id(), ImVec2(static_cast<float>(get_width()), static_cast<float>(get_height())));
|
||||
}
|
||||
virtual void destroy() {
|
||||
const auto r = application::get()->get_renderer();
|
||||
const auto& device = r->device;
|
||||
device.destroySampler(sampler);
|
||||
device.destroyImageView(image_view);
|
||||
device.destroyImage(image);
|
||||
device.freeMemory(memory);
|
||||
ImGui_ImplVulkan_RemoveTexture(descriptor_set);
|
||||
sampler = nullptr;
|
||||
image_view = nullptr;
|
||||
image = nullptr;
|
||||
memory = nullptr;
|
||||
descriptor_set = nullptr;
|
||||
}
|
||||
|
||||
vk::Image image;
|
||||
vk::ImageView image_view;
|
||||
vk::Sampler sampler;
|
||||
vk::DescriptorSet descriptor_set;
|
||||
vk::DeviceMemory memory;
|
||||
vk::Format format;
|
||||
protected:
|
||||
virtual void on_init() = 0;
|
||||
uint32_t width_;
|
||||
uint32_t height_;
|
||||
};
|
@ -1,464 +0,0 @@
|
||||
#include "renderer.h"
|
||||
|
||||
#include "imgui_impl_glfw.h"
|
||||
|
||||
#include "application/application.h"
|
||||
#include "texture.h"
|
||||
|
||||
|
||||
|
||||
#ifdef APP_USE_VULKAN_DEBUG_REPORT
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL on_debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData)
|
||||
{
|
||||
(void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments
|
||||
fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage);
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // APP_USE_VULKAN_DEBUG_REPORT
|
||||
|
||||
static bool is_extension_available(const std::vector<vk::ExtensionProperties>& properties, const char* extension) {
|
||||
return std::ranges::any_of(properties, [extension](const vk::ExtensionProperties& p) {
|
||||
return strcmp(p.extensionName, extension) == 0;
|
||||
});
|
||||
}
|
||||
|
||||
vk::CommandPool renderer::get_command_pool() const {
|
||||
return main_window_data.Frames[main_window_data.FrameIndex].CommandPool;
|
||||
}
|
||||
|
||||
vk::CommandBuffer renderer::create_command_buffer(vk::CommandBufferLevel level, bool begin) const {
|
||||
vk::CommandBufferAllocateInfo alloc_info;
|
||||
alloc_info.setCommandPool(get_command_pool());
|
||||
alloc_info.setLevel(level);
|
||||
alloc_info.setCommandBufferCount(1);
|
||||
|
||||
vk::CommandBuffer command_buffer;
|
||||
auto err = device.allocateCommandBuffers(&alloc_info, &command_buffer);
|
||||
check_vk_result(err);
|
||||
|
||||
// If requested, also start the new command buffer
|
||||
if (begin) {
|
||||
vk::CommandBufferBeginInfo begin_info;
|
||||
begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
|
||||
command_buffer.begin(begin_info);
|
||||
}
|
||||
|
||||
return command_buffer;
|
||||
}
|
||||
|
||||
void renderer::end_command_buffer(vk::CommandBuffer command_buffer, bool use_fence) const {
|
||||
command_buffer.end();
|
||||
|
||||
if (use_fence) {
|
||||
vk::FenceCreateInfo fence_create_info = {};
|
||||
vk::Fence fence = device.createFence(fence_create_info);
|
||||
|
||||
vk::SubmitInfo submit_info;
|
||||
submit_info.setCommandBuffers(command_buffer);
|
||||
queue.submit(submit_info, fence);
|
||||
|
||||
const auto err = device.waitForFences(1, &fence, VK_TRUE, 100000000000);
|
||||
check_vk_result(err);
|
||||
|
||||
device.destroyFence(fence);
|
||||
}
|
||||
else {
|
||||
vk::SubmitInfo submit_info;
|
||||
submit_info.setCommandBuffers(command_buffer);
|
||||
queue.submit(submit_info, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void renderer::init_vulkan(GLFWwindow* window_handle) {
|
||||
std::vector<const char*> extensions;
|
||||
uint32_t extensions_count = 0;
|
||||
const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&extensions_count);
|
||||
for (uint32_t i = 0; i < extensions_count; i++)
|
||||
extensions.push_back(glfw_extensions[i]);
|
||||
setup_vulkan(extensions);
|
||||
|
||||
// Create Window Surface
|
||||
VkSurfaceKHR surface;
|
||||
VkResult err = glfwCreateWindowSurface(instance, window_handle, reinterpret_cast<VkAllocationCallbacks*>(allocator), &surface);
|
||||
check_vk_result(err);
|
||||
|
||||
// Create Framebuffers
|
||||
int w, h;
|
||||
glfwGetFramebufferSize(window_handle, &w, &h);
|
||||
setup_vulkan_window(surface, w, h);
|
||||
|
||||
ImGui_ImplGlfw_InitForVulkan(window_handle, true);
|
||||
|
||||
ImGui_ImplVulkan_InitInfo init_info = {};
|
||||
init_info.Instance = instance;
|
||||
init_info.PhysicalDevice = physical_device;
|
||||
init_info.Device = device;
|
||||
init_info.QueueFamily = queue_family;
|
||||
init_info.Queue = queue;
|
||||
init_info.PipelineCache = VK_NULL_HANDLE;
|
||||
init_info.DescriptorPool = descriptor_pool;
|
||||
init_info.RenderPass = main_window_data.RenderPass;
|
||||
init_info.Subpass = 0;
|
||||
init_info.MinImageCount = min_image_count;
|
||||
init_info.ImageCount = main_window_data.ImageCount;
|
||||
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
init_info.Allocator = reinterpret_cast<VkAllocationCallbacks*>(allocator);
|
||||
init_info.CheckVkResultFn = check_vk_result;
|
||||
ImGui_ImplVulkan_Init(&init_info);
|
||||
}
|
||||
|
||||
vk::PhysicalDevice renderer::setup_vulkan_select_physical_device() const {
|
||||
const std::vector<vk::PhysicalDevice> gpus = instance.enumeratePhysicalDevices();
|
||||
IM_ASSERT(!gpus.empty());
|
||||
|
||||
// If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers
|
||||
// most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple
|
||||
// dedicated GPUs) is out of scope of this sample.
|
||||
for (auto& device: gpus) {
|
||||
if (const auto properties = device.getProperties(); properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu)
|
||||
return device;
|
||||
}
|
||||
|
||||
// Use first GPU (Integrated) is a Discrete one is not available.
|
||||
if (!gpus.empty())
|
||||
return gpus[0];
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void renderer::setup_vulkan(std::vector<const char*> instance_extensions) {
|
||||
// Create Vulkan Instance
|
||||
{
|
||||
vk::InstanceCreateInfo create_info;
|
||||
|
||||
// VkInstanceCreateInfo create_info = {};
|
||||
// create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
|
||||
// Enumerate available extensions
|
||||
auto properties = vk::enumerateInstanceExtensionProperties();
|
||||
|
||||
// Enable required extensions
|
||||
if (is_extension_available(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
|
||||
instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
||||
#ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
|
||||
if (is_extension_available(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||
instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||
create_info.flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
|
||||
}
|
||||
#endif
|
||||
// Enabling validation layers
|
||||
#ifdef APP_USE_VULKAN_DEBUG_REPORT
|
||||
const char* layers[] = { "VK_LAYER_KHRONOS_validation" };
|
||||
create_info.enabledLayerCount = 1;
|
||||
create_info.ppEnabledLayerNames = layers;
|
||||
instance_extensions.push_back("VK_EXT_debug_report");
|
||||
#endif
|
||||
|
||||
// Create Vulkan Instance
|
||||
create_info.setPEnabledExtensionNames(instance_extensions);
|
||||
instance = vk::createInstance(create_info, allocator);
|
||||
|
||||
// Setup the debug report callback
|
||||
#ifdef APP_USE_VULKAN_DEBUG_REPORT
|
||||
auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
|
||||
IM_ASSERT(vkCreateDebugReportCallbackEXT != nullptr);
|
||||
VkDebugReportCallbackCreateInfoEXT debug_report_ci = {};
|
||||
debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
|
||||
debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
|
||||
debug_report_ci.pfnCallback = on_debug_report;
|
||||
debug_report_ci.pUserData = nullptr;
|
||||
auto err = vkCreateDebugReportCallbackEXT(instance, &debug_report_ci, (VkAllocationCallbacks*)allocator, &debug_report);
|
||||
check_vk_result(err);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Select Physical Device (GPU)
|
||||
physical_device = setup_vulkan_select_physical_device();
|
||||
|
||||
// Select graphics queue family
|
||||
{
|
||||
const auto queues = physical_device.getQueueFamilyProperties();
|
||||
for (uint32_t i = 0; i < queues.size(); i++) {
|
||||
if (queues[i].queueFlags & vk::QueueFlagBits::eGraphics) {
|
||||
queue_family = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
IM_ASSERT(queue_family != static_cast<uint32_t>(-1));
|
||||
}
|
||||
|
||||
// Create Logical Device (with 1 queue)
|
||||
{
|
||||
std::vector<const char*> device_extensions;
|
||||
device_extensions.emplace_back("VK_KHR_swapchain");
|
||||
|
||||
// Enumerate physical device extension
|
||||
auto properties = physical_device.enumerateDeviceExtensionProperties();
|
||||
|
||||
#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME
|
||||
if (is_extension_available(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME))
|
||||
device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
|
||||
#endif
|
||||
|
||||
std::vector<float> queue_priority = {1.0f};
|
||||
vk::DeviceQueueCreateInfo queue_info;
|
||||
queue_info.setQueueFamilyIndex(queue_family);
|
||||
queue_info.setQueuePriorities(queue_priority);
|
||||
|
||||
vk::DeviceCreateInfo create_info;
|
||||
create_info.setQueueCreateInfos(queue_info);
|
||||
create_info.setPEnabledExtensionNames(device_extensions);
|
||||
device = physical_device.createDevice(create_info, allocator);
|
||||
queue = device.getQueue(queue_family, 0);
|
||||
}
|
||||
|
||||
// Create Descriptor Pool
|
||||
// The example only requires a single combined image sampler descriptor for the font image and only uses one descriptor set (for that)
|
||||
// If you wish to load e.g. additional textures you may need to alter pools sizes.
|
||||
{
|
||||
std::vector<vk::DescriptorPoolSize> pool_sizes;
|
||||
pool_sizes.emplace_back(vk::DescriptorType::eCombinedImageSampler, 1);
|
||||
|
||||
vk::DescriptorPoolCreateInfo descriptor_pool_create_info;
|
||||
descriptor_pool_create_info.setMaxSets(16);
|
||||
descriptor_pool_create_info.setPoolSizes(pool_sizes);
|
||||
|
||||
descriptor_pool = device.createDescriptorPool(descriptor_pool_create_info);
|
||||
}
|
||||
}
|
||||
|
||||
// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo.
|
||||
// Your real engine/app may not use them.
|
||||
void renderer::setup_vulkan_window(VkSurfaceKHR surface, int width,
|
||||
int height) {
|
||||
main_window_data.Surface = surface;
|
||||
|
||||
// Check for WSI support
|
||||
vk::Bool32 res;
|
||||
const auto err = physical_device.getSurfaceSupportKHR(queue_family, main_window_data.Surface, &res);
|
||||
check_vk_result(err);
|
||||
if (res != VK_TRUE) {
|
||||
fprintf(stderr, "Error no WSI support on physical device 0\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Select Surface Format
|
||||
constexpr VkFormat requestSurfaceImageFormat[] = {
|
||||
VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM
|
||||
};
|
||||
constexpr VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
|
||||
main_window_data.SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(physical_device, main_window_data.Surface, requestSurfaceImageFormat,
|
||||
(size_t) IM_ARRAYSIZE(requestSurfaceImageFormat),
|
||||
requestSurfaceColorSpace);
|
||||
|
||||
// Select Present Mode
|
||||
#ifdef APP_USE_UNLIMITED_FRAME_RATE
|
||||
VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
|
||||
#else
|
||||
VkPresentModeKHR present_modes[] = {VK_PRESENT_MODE_FIFO_KHR};
|
||||
#endif
|
||||
main_window_data.PresentMode = ImGui_ImplVulkanH_SelectPresentMode(physical_device, main_window_data.Surface, &present_modes[0],
|
||||
IM_ARRAYSIZE(present_modes));
|
||||
//printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode);
|
||||
|
||||
// Create SwapChain, RenderPass, Framebuffer, etc.
|
||||
IM_ASSERT(min_image_count >= 2);
|
||||
|
||||
ImGui_ImplVulkanH_CreateOrResizeWindow(instance, physical_device, device, &main_window_data, queue_family,
|
||||
reinterpret_cast<VkAllocationCallbacks*>(allocator), width,
|
||||
height, min_image_count);
|
||||
}
|
||||
|
||||
void renderer::cleanup_vulkan() const {
|
||||
device.destroyDescriptorPool(descriptor_pool);
|
||||
#ifdef APP_USE_VULKAN_DEBUG_REPORT
|
||||
// Remove the debug report callback
|
||||
auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
|
||||
vkDestroyDebugReportCallbackEXT(instance, debug_report, (VkAllocationCallbacks*)allocator);
|
||||
#endif // APP_USE_VULKAN_DEBUG_REPORT
|
||||
|
||||
device.destroy();
|
||||
instance.destroy();
|
||||
}
|
||||
|
||||
void renderer::cleanup_vulkan_window() {
|
||||
ImGui_ImplVulkanH_DestroyWindow(instance, device, &main_window_data,
|
||||
reinterpret_cast<VkAllocationCallbacks*>(allocator));
|
||||
}
|
||||
|
||||
void renderer::frame_render(ImDrawData* draw_data) {
|
||||
vk::Semaphore image_acquired_semaphore = main_window_data.FrameSemaphores[main_window_data.SemaphoreIndex].ImageAcquiredSemaphore;
|
||||
vk::Semaphore render_complete_semaphore = main_window_data.FrameSemaphores[main_window_data.SemaphoreIndex].RenderCompleteSemaphore;
|
||||
|
||||
vk::Result err = device.acquireNextImageKHR(main_window_data.Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE,
|
||||
&main_window_data.FrameIndex);
|
||||
if (err == vk::Result::eErrorOutOfDateKHR || err == vk::Result::eSuboptimalKHR) {
|
||||
swap_chain_rebuild_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
check_vk_result(err);
|
||||
|
||||
ImGui_ImplVulkanH_Frame* fd = &main_window_data.Frames[main_window_data.FrameIndex];
|
||||
const vk::CommandBuffer cmd_buf = fd->CommandBuffer;
|
||||
const vk::Fence fence = fd->Fence; {
|
||||
err = device.waitForFences(1, &fence, VK_TRUE, UINT64_MAX);
|
||||
|
||||
// wait indefinitely instead of periodically checking
|
||||
check_vk_result(err);
|
||||
|
||||
err = device.resetFences(1, &fence);
|
||||
check_vk_result(err);
|
||||
} {
|
||||
const vk::CommandPool command_pool = fd->CommandPool;
|
||||
device.resetCommandPool(command_pool);
|
||||
|
||||
vk::CommandBufferBeginInfo info = {};
|
||||
info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
|
||||
|
||||
cmd_buf.begin(info);
|
||||
} {
|
||||
const vk::Framebuffer framebuffer = fd->Framebuffer;
|
||||
const vk::RenderPass render_pass = main_window_data.RenderPass;
|
||||
|
||||
const auto clear_color = main_window_data.ClearValue.color.float32;
|
||||
const auto clear_depth = main_window_data.ClearValue.depthStencil.depth;
|
||||
const auto clear_stencil = main_window_data.ClearValue.depthStencil.stencil;
|
||||
|
||||
vk::ClearValue clear_value;
|
||||
clear_value.color = vk::ClearColorValue(std::array<float, 4>{
|
||||
clear_color[0], clear_color[1], clear_color[2], clear_color[3]
|
||||
});
|
||||
clear_value.depthStencil = vk::ClearDepthStencilValue(clear_depth, clear_stencil);
|
||||
|
||||
std::vector<vk::ClearValue> clear_values;
|
||||
clear_values.emplace_back(clear_value);
|
||||
|
||||
vk::RenderPassBeginInfo info;
|
||||
info.setRenderPass(render_pass);
|
||||
info.setFramebuffer(framebuffer);
|
||||
info.renderArea.extent.width = main_window_data.Width;
|
||||
info.renderArea.extent.height = main_window_data.Height;
|
||||
info.setClearValues(clear_values);
|
||||
|
||||
cmd_buf.beginRenderPass(info, vk::SubpassContents::eInline);
|
||||
}
|
||||
|
||||
// Record dear imgui primitives into command buffer
|
||||
ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
|
||||
|
||||
// Submit command buffer
|
||||
vkCmdEndRenderPass(fd->CommandBuffer); {
|
||||
vk::PipelineStageFlags wait_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||
|
||||
vk::SubmitInfo info;
|
||||
info.setWaitSemaphores(image_acquired_semaphore);
|
||||
info.setWaitDstStageMask(wait_stage);
|
||||
info.setCommandBuffers(cmd_buf);
|
||||
info.setSignalSemaphores(render_complete_semaphore);
|
||||
|
||||
cmd_buf.end();
|
||||
err = queue.submit(1, &info, fence);
|
||||
check_vk_result(err);
|
||||
}
|
||||
}
|
||||
|
||||
void renderer::frame_present() {
|
||||
if (swap_chain_rebuild_)
|
||||
return;
|
||||
vk::Semaphore render_complete_semaphore = main_window_data.FrameSemaphores[main_window_data.SemaphoreIndex].RenderCompleteSemaphore;
|
||||
vk::SwapchainKHR swapchain = main_window_data.Swapchain;
|
||||
uint32_t frame_index = main_window_data.FrameIndex;
|
||||
|
||||
vk::PresentInfoKHR info;
|
||||
info.setWaitSemaphores(render_complete_semaphore);
|
||||
info.setSwapchains(swapchain);
|
||||
info.setImageIndices(frame_index);
|
||||
|
||||
try {
|
||||
(void)queue.presentKHR(info);
|
||||
} catch (const vk::OutOfDateKHRError& e) {
|
||||
swap_chain_rebuild_ = true;
|
||||
return;
|
||||
}
|
||||
main_window_data.SemaphoreIndex = (main_window_data.SemaphoreIndex + 1) % main_window_data.SemaphoreCount; // Now we can use the next set of semaphores
|
||||
}
|
||||
|
||||
void renderer::pre_init() {
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
}
|
||||
|
||||
bool renderer::init(GLFWwindow* window_handle) {
|
||||
if (has_initialized_)
|
||||
return true;
|
||||
|
||||
if (!glfwVulkanSupported()) {
|
||||
throw std::runtime_error("Vulkan not supported");
|
||||
}
|
||||
init_vulkan(window_handle);
|
||||
|
||||
has_initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void renderer::shutdown() {
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
|
||||
cleanup_vulkan_window();
|
||||
cleanup_vulkan();
|
||||
}
|
||||
|
||||
void renderer::new_frame(GLFWwindow* window_handle) {
|
||||
// Resize swap chain?
|
||||
if (swap_chain_rebuild_)
|
||||
{
|
||||
int width, height;
|
||||
glfwGetFramebufferSize(window_handle, &width, &height);
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
ImGui_ImplVulkan_SetMinImageCount(min_image_count);
|
||||
ImGui_ImplVulkanH_CreateOrResizeWindow(instance, physical_device, device, &main_window_data, queue_family, reinterpret_cast<VkAllocationCallbacks*>(allocator), width, height, min_image_count);
|
||||
main_window_data.FrameIndex = 0;
|
||||
swap_chain_rebuild_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void renderer::end_frame(GLFWwindow* window_handle) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
ImDrawData* main_draw_data = ImGui::GetDrawData();
|
||||
const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f);
|
||||
main_window_data.ClearValue.color.float32[0] = clear_color.x * clear_color.w;
|
||||
main_window_data.ClearValue.color.float32[1] = clear_color.y * clear_color.w;
|
||||
main_window_data.ClearValue.color.float32[2] = clear_color.z * clear_color.w;
|
||||
main_window_data.ClearValue.color.float32[3] = clear_color.w;
|
||||
if (!main_is_minimized)
|
||||
frame_render(main_draw_data);
|
||||
|
||||
// Update and Render additional Platform Windows
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||
{
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
}
|
||||
|
||||
// Present Main Platform Window
|
||||
if (!main_is_minimized)
|
||||
frame_present();
|
||||
}
|
||||
|
||||
std::shared_ptr<texture> renderer::create_texture(const uint8_t* data, int width, int height, vk::Format format) {
|
||||
auto out = std::make_shared<texture>();
|
||||
out->init(width, height, format);
|
||||
if (data)
|
||||
out->upload(data, width * height * 4);
|
||||
return out;
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui_impl_vulkan.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
// #ifdef _DEBUG
|
||||
// #define APP_USE_VULKAN_DEBUG_REPORT
|
||||
// #endif
|
||||
|
||||
#define APP_USE_UNLIMITED_FRAME_RATE
|
||||
|
||||
class render_target;
|
||||
class texture;
|
||||
constexpr ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
constexpr float clear_color_with_alpha[4] = {
|
||||
clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w
|
||||
};
|
||||
|
||||
static void check_vk_result(vk::Result err) {
|
||||
if (err == vk::Result::eSuccess)
|
||||
return;
|
||||
|
||||
spdlog::error("[vulkan] Error: VkResult = {}", vk::to_string(err));
|
||||
abort();
|
||||
}
|
||||
|
||||
static void check_vk_result(VkResult err) {
|
||||
if (err == VK_SUCCESS)
|
||||
return;
|
||||
if (err < 0) {
|
||||
spdlog::error("[vulkan] Error: VkResult = {}", (int)err);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
class renderer {
|
||||
public:
|
||||
virtual ~renderer() = default;
|
||||
|
||||
void pre_init();
|
||||
|
||||
bool init(GLFWwindow* window_handle);
|
||||
|
||||
virtual void shutdown();
|
||||
|
||||
void new_frame(GLFWwindow* window_handle);
|
||||
|
||||
void end_frame(GLFWwindow* window_handle);
|
||||
|
||||
static CORE_API std::shared_ptr<texture> create_texture(const uint8_t* data, int width, int height, vk::Format format);
|
||||
|
||||
void set_vsync(const bool vsync) { vsync_ = vsync; }
|
||||
|
||||
vk::AllocationCallbacks* allocator = nullptr;
|
||||
vk::Instance instance = VK_NULL_HANDLE;
|
||||
vk::PhysicalDevice physical_device = VK_NULL_HANDLE;
|
||||
vk::Device device = VK_NULL_HANDLE;
|
||||
uint32_t queue_family = (uint32_t) -1;
|
||||
vk::Queue queue = VK_NULL_HANDLE;
|
||||
vk::DescriptorPool descriptor_pool = VK_NULL_HANDLE;
|
||||
#ifdef APP_USE_VULKAN_DEBUG_REPORT
|
||||
VkDebugReportCallbackEXT debug_report = VK_NULL_HANDLE;
|
||||
#endif
|
||||
[[nodiscard]] vk::CommandPool get_command_pool() const;
|
||||
[[nodiscard]] vk::CommandBuffer create_command_buffer(vk::CommandBufferLevel level, bool begin) const;
|
||||
void end_command_buffer(vk::CommandBuffer command_buffer, bool use_fence = false) const;
|
||||
|
||||
ImGui_ImplVulkanH_Window main_window_data;
|
||||
int min_image_count = 2;
|
||||
protected:
|
||||
void init_vulkan(GLFWwindow* window_handle);
|
||||
[[nodiscard]] vk::PhysicalDevice setup_vulkan_select_physical_device() const;
|
||||
void setup_vulkan(std::vector<const char*> instance_extensions);
|
||||
void setup_vulkan_window(VkSurfaceKHR surface, int width, int height);
|
||||
void cleanup_vulkan() const;
|
||||
void cleanup_vulkan_window();
|
||||
void frame_render(ImDrawData* draw_data);
|
||||
void frame_present();
|
||||
|
||||
bool has_initialized_ = false;
|
||||
bool swap_chain_rebuild_ = false;
|
||||
|
||||
bool vsync_ = true;
|
||||
};
|
@ -1,130 +0,0 @@
|
||||
#include "texture.h"
|
||||
|
||||
|
||||
#include "buffer_vk.h"
|
||||
#include "application/application.h"
|
||||
#include "renderer.h"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
|
||||
texture::texture() {
|
||||
memory = nullptr;
|
||||
}
|
||||
|
||||
void texture::upload(const void* data, size_t size) {
|
||||
renderer* r = application::get()->get_renderer();
|
||||
const vk::Device& device = r->device;
|
||||
|
||||
// Create Upload Buffer
|
||||
vk::BufferCreateInfo buffer_info;
|
||||
buffer_info.setSize(size);
|
||||
buffer_info.setUsage(vk::BufferUsageFlagBits::eTransferSrc);
|
||||
buffer_info.setSharingMode(vk::SharingMode::eExclusive);
|
||||
vk::Buffer upload_buffer = device.createBuffer(buffer_info);
|
||||
|
||||
vk::MemoryRequirements req = device.getBufferMemoryRequirements(upload_buffer);
|
||||
uint32_t upload_memory_type = vk::su::findMemoryType(r->physical_device.getMemoryProperties(), req.memoryTypeBits,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible);
|
||||
vk::MemoryAllocateInfo alloc_info;
|
||||
alloc_info.setAllocationSize(req.size);
|
||||
alloc_info.setMemoryTypeIndex(upload_memory_type);
|
||||
vk::DeviceMemory upload_memory = device.allocateMemory(alloc_info);
|
||||
device.bindBufferMemory(upload_buffer, upload_memory, 0);
|
||||
|
||||
void* p = device.mapMemory(upload_memory, 0, size);
|
||||
memcpy(p, data, size);
|
||||
vk::MappedMemoryRange memory_range;
|
||||
memory_range.setMemory(upload_memory);
|
||||
memory_range.setSize(size);
|
||||
device.flushMappedMemoryRanges(memory_range);
|
||||
device.unmapMemory(upload_memory);
|
||||
|
||||
const vk::CommandBuffer command_buffer = r->create_command_buffer(vk::CommandBufferLevel::ePrimary, true);
|
||||
|
||||
vk::ImageMemoryBarrier copy_barrier;
|
||||
copy_barrier.setDstAccessMask(vk::AccessFlagBits::eTransferWrite);
|
||||
copy_barrier.setOldLayout(vk::ImageLayout::eUndefined);
|
||||
copy_barrier.setNewLayout(vk::ImageLayout::eTransferDstOptimal);
|
||||
copy_barrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
|
||||
copy_barrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
|
||||
copy_barrier.setImage(image);
|
||||
copy_barrier.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eTransfer, {}, {}, {},
|
||||
copy_barrier);
|
||||
|
||||
vk::BufferImageCopy region;
|
||||
region.setImageSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1});
|
||||
region.setImageExtent({static_cast<uint32_t>(width_), static_cast<uint32_t>(height_), 1});
|
||||
command_buffer.copyBufferToImage(upload_buffer, image, vk::ImageLayout::eTransferDstOptimal, region);
|
||||
|
||||
vk::ImageMemoryBarrier use_barrier;
|
||||
use_barrier.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite);
|
||||
use_barrier.setDstAccessMask(vk::AccessFlagBits::eShaderRead);
|
||||
use_barrier.setOldLayout(vk::ImageLayout::eTransferDstOptimal);
|
||||
use_barrier.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
use_barrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
|
||||
use_barrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
|
||||
use_barrier.setImage(image);
|
||||
use_barrier.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {},
|
||||
{}, {}, use_barrier);
|
||||
|
||||
r->end_command_buffer(command_buffer);
|
||||
device.waitIdle();
|
||||
|
||||
device.destroyBuffer(upload_buffer);
|
||||
device.freeMemory(upload_memory);
|
||||
|
||||
// device.freeCommandBuffers(command_pool, command_buffer);
|
||||
}
|
||||
|
||||
void texture::on_init() {
|
||||
const renderer* r = application::get()->get_renderer();
|
||||
const vk::Device& device = r->device;
|
||||
const vk::PhysicalDevice& physical_device = r->physical_device;
|
||||
|
||||
vk::ImageCreateInfo image_info;
|
||||
image_info.setImageType(vk::ImageType::e2D);
|
||||
image_info.setFormat(format);
|
||||
image_info.setExtent({ width_, height_, 1 });
|
||||
image_info.setMipLevels(1);
|
||||
image_info.setArrayLayers(1);
|
||||
image_info.setSamples(vk::SampleCountFlagBits::e1);
|
||||
image_info.setTiling(vk::ImageTiling::eOptimal);
|
||||
image_info.setUsage(vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eStorage);
|
||||
image_info.setSharingMode(vk::SharingMode::eExclusive);
|
||||
image_info.setInitialLayout(vk::ImageLayout::eUndefined);
|
||||
image = device.createImage(image_info);
|
||||
|
||||
const vk::PhysicalDeviceMemoryProperties memory_properties = physical_device.getMemoryProperties();
|
||||
const vk::MemoryRequirements memory_requirements = device.getImageMemoryRequirements(image);
|
||||
const uint32_t memory_type = vk::su::findMemoryType(memory_properties, memory_requirements.memoryTypeBits,
|
||||
vk::MemoryPropertyFlagBits::eDeviceLocal);
|
||||
|
||||
vk::MemoryAllocateInfo memory_allocate_info;
|
||||
memory_allocate_info.setAllocationSize(memory_requirements.size);
|
||||
memory_allocate_info.setMemoryTypeIndex(memory_type);
|
||||
memory = device.allocateMemory(memory_allocate_info);
|
||||
device.bindImageMemory(image, memory, 0);
|
||||
|
||||
vk::ImageViewCreateInfo view_info;
|
||||
view_info.setImage(image);
|
||||
view_info.setViewType(vk::ImageViewType::e2D);
|
||||
view_info.setFormat(format);
|
||||
view_info.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
image_view = device.createImageView(view_info);
|
||||
|
||||
vk::SamplerCreateInfo sampler_info;
|
||||
sampler_info.setMagFilter(vk::Filter::eLinear);
|
||||
sampler_info.setMinFilter(vk::Filter::eLinear);
|
||||
sampler_info.setMipmapMode(vk::SamplerMipmapMode::eLinear);
|
||||
sampler_info.setAddressModeU(vk::SamplerAddressMode::eRepeat);
|
||||
sampler_info.setAddressModeV(vk::SamplerAddressMode::eRepeat);
|
||||
sampler_info.setAddressModeW(vk::SamplerAddressMode::eRepeat);
|
||||
sampler_info.setMinLod(-1000);
|
||||
sampler_info.setMaxLod(1000);
|
||||
sampler_info.setMaxAnisotropy(1);
|
||||
sampler = device.createSampler(sampler_info);
|
||||
|
||||
descriptor_set = ImGui_ImplVulkan_AddTexture(sampler, image_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
#include "imgui.h"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "buffer_vk.h"
|
||||
#include "render_resource.h"
|
||||
|
||||
class texture : public render_resource {
|
||||
public:
|
||||
texture();
|
||||
|
||||
bool is_valid() const { return image_view; }
|
||||
|
||||
void upload(const void* data, size_t size);
|
||||
protected:
|
||||
void on_init() override;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -1,439 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Copyright(c) 2019, NVIDIA CORPORATION. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory> // std::unique_ptr
|
||||
|
||||
namespace vk
|
||||
{
|
||||
namespace su
|
||||
{
|
||||
const uint64_t FenceTimeout = 100000000;
|
||||
|
||||
template <typename Func>
|
||||
void oneTimeSubmit( vk::Device const & device, vk::CommandPool const & commandPool, vk::Queue const & queue, Func const & func )
|
||||
{
|
||||
vk::CommandBuffer commandBuffer =
|
||||
device.allocateCommandBuffers( vk::CommandBufferAllocateInfo( commandPool, vk::CommandBufferLevel::ePrimary, 1 ) ).front();
|
||||
commandBuffer.begin( vk::CommandBufferBeginInfo( vk::CommandBufferUsageFlagBits::eOneTimeSubmit ) );
|
||||
func( commandBuffer );
|
||||
commandBuffer.end();
|
||||
queue.submit( vk::SubmitInfo( 0, nullptr, nullptr, 1, &commandBuffer ), nullptr );
|
||||
queue.waitIdle();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void copyToDevice( vk::Device const & device, vk::DeviceMemory const & deviceMemory, T const * pData, size_t count, vk::DeviceSize stride = sizeof( T ) )
|
||||
{
|
||||
assert( sizeof( T ) <= stride );
|
||||
uint8_t * deviceData = static_cast<uint8_t *>( device.mapMemory( deviceMemory, 0, count * stride ) );
|
||||
if ( stride == sizeof( T ) )
|
||||
{
|
||||
memcpy( deviceData, pData, count * sizeof( T ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
memcpy( deviceData, &pData[i], sizeof( T ) );
|
||||
deviceData += stride;
|
||||
}
|
||||
}
|
||||
device.unmapMemory( deviceMemory );
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void copyToDevice( vk::Device const & device, vk::DeviceMemory const & deviceMemory, T const & data )
|
||||
{
|
||||
copyToDevice<T>( device, deviceMemory, &data, 1 );
|
||||
}
|
||||
|
||||
template <class T>
|
||||
VULKAN_HPP_INLINE constexpr const T & clamp( const T & v, const T & lo, const T & hi )
|
||||
{
|
||||
return v < lo ? lo : hi < v ? hi : v;
|
||||
}
|
||||
|
||||
void setImageLayout(
|
||||
vk::CommandBuffer const & commandBuffer, vk::Image image, vk::Format format, vk::ImageLayout oldImageLayout, vk::ImageLayout newImageLayout );
|
||||
|
||||
struct WindowData
|
||||
{
|
||||
WindowData( GLFWwindow * wnd, std::string const & name, vk::Extent2D const & extent );
|
||||
WindowData( const WindowData & ) = delete;
|
||||
WindowData( WindowData && other );
|
||||
~WindowData() noexcept;
|
||||
|
||||
GLFWwindow * handle;
|
||||
std::string name;
|
||||
vk::Extent2D extent;
|
||||
};
|
||||
|
||||
WindowData createWindow( std::string const & windowName, vk::Extent2D const & extent );
|
||||
|
||||
struct BufferData
|
||||
{
|
||||
BufferData( vk::PhysicalDevice const & physicalDevice,
|
||||
vk::Device const & device,
|
||||
vk::DeviceSize size,
|
||||
vk::BufferUsageFlags usage,
|
||||
vk::MemoryPropertyFlags propertyFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent );
|
||||
|
||||
void clear( vk::Device const & device )
|
||||
{
|
||||
device.destroyBuffer( buffer ); // to prevent some validation layer warning, the Buffer needs to be destroyed before the bound DeviceMemory
|
||||
device.freeMemory( deviceMemory );
|
||||
}
|
||||
|
||||
template <typename DataType>
|
||||
void upload( vk::Device const & device, DataType const & data ) const
|
||||
{
|
||||
assert( ( m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent ) && ( m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible ) );
|
||||
assert( sizeof( DataType ) <= m_size );
|
||||
|
||||
void * dataPtr = device.mapMemory( deviceMemory, 0, sizeof( DataType ) );
|
||||
memcpy( dataPtr, &data, sizeof( DataType ) );
|
||||
device.unmapMemory( deviceMemory );
|
||||
}
|
||||
|
||||
template <typename DataType>
|
||||
void upload( vk::Device const & device, std::vector<DataType> const & data, size_t stride = 0 ) const
|
||||
{
|
||||
assert( m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible );
|
||||
|
||||
size_t elementSize = stride ? stride : sizeof( DataType );
|
||||
assert( sizeof( DataType ) <= elementSize );
|
||||
|
||||
copyToDevice( device, deviceMemory, data.data(), data.size(), elementSize );
|
||||
}
|
||||
|
||||
template <typename DataType>
|
||||
void upload( vk::PhysicalDevice const & physicalDevice,
|
||||
vk::Device const & device,
|
||||
vk::CommandPool const & commandPool,
|
||||
vk::Queue queue,
|
||||
std::vector<DataType> const & data,
|
||||
size_t stride ) const
|
||||
{
|
||||
assert( m_usage & vk::BufferUsageFlagBits::eTransferDst );
|
||||
assert( m_propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal );
|
||||
|
||||
size_t elementSize = stride ? stride : sizeof( DataType );
|
||||
assert( sizeof( DataType ) <= elementSize );
|
||||
|
||||
size_t dataSize = data.size() * elementSize;
|
||||
assert( dataSize <= m_size );
|
||||
|
||||
vk::su::BufferData stagingBuffer( physicalDevice, device, dataSize, vk::BufferUsageFlagBits::eTransferSrc );
|
||||
copyToDevice( device, stagingBuffer.deviceMemory, data.data(), data.size(), elementSize );
|
||||
|
||||
vk::su::oneTimeSubmit( device,
|
||||
commandPool,
|
||||
queue,
|
||||
[&]( vk::CommandBuffer const & commandBuffer )
|
||||
{ commandBuffer.copyBuffer( stagingBuffer.buffer, buffer, vk::BufferCopy( 0, 0, dataSize ) ); } );
|
||||
|
||||
stagingBuffer.clear( device );
|
||||
}
|
||||
|
||||
vk::Buffer buffer;
|
||||
vk::DeviceMemory deviceMemory;
|
||||
#if !defined( NDEBUG )
|
||||
private:
|
||||
vk::DeviceSize m_size;
|
||||
vk::BufferUsageFlags m_usage;
|
||||
vk::MemoryPropertyFlags m_propertyFlags;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ImageData
|
||||
{
|
||||
ImageData( vk::PhysicalDevice const & physicalDevice,
|
||||
vk::Device const & device,
|
||||
vk::Format format,
|
||||
vk::Extent2D const & extent,
|
||||
vk::ImageTiling tiling,
|
||||
vk::ImageUsageFlags usage,
|
||||
vk::ImageLayout initialLayout,
|
||||
vk::MemoryPropertyFlags memoryProperties,
|
||||
vk::ImageAspectFlags aspectMask );
|
||||
|
||||
void clear( vk::Device const & device )
|
||||
{
|
||||
device.destroyImageView( imageView );
|
||||
device.destroyImage( image ); // the Image should to be destroyed before the bound DeviceMemory is freed
|
||||
device.freeMemory( deviceMemory );
|
||||
}
|
||||
|
||||
vk::Format format;
|
||||
vk::Image image;
|
||||
vk::DeviceMemory deviceMemory;
|
||||
vk::ImageView imageView;
|
||||
};
|
||||
|
||||
struct DepthBufferData : public ImageData
|
||||
{
|
||||
DepthBufferData( vk::PhysicalDevice const & physicalDevice, vk::Device const & device, vk::Format format, vk::Extent2D const & extent );
|
||||
};
|
||||
|
||||
struct SurfaceData
|
||||
{
|
||||
SurfaceData( vk::Instance const & instance, std::string const & windowName, vk::Extent2D const & extent );
|
||||
|
||||
vk::Extent2D extent;
|
||||
WindowData window;
|
||||
vk::SurfaceKHR surface;
|
||||
};
|
||||
|
||||
struct SwapChainData
|
||||
{
|
||||
SwapChainData( vk::PhysicalDevice const & physicalDevice,
|
||||
vk::Device const & device,
|
||||
vk::SurfaceKHR const & surface,
|
||||
vk::Extent2D const & extent,
|
||||
vk::ImageUsageFlags usage,
|
||||
vk::SwapchainKHR const & oldSwapChain,
|
||||
uint32_t graphicsFamilyIndex,
|
||||
uint32_t presentFamilyIndex );
|
||||
|
||||
void clear( vk::Device const & device )
|
||||
{
|
||||
for ( auto & imageView : imageViews )
|
||||
{
|
||||
device.destroyImageView( imageView );
|
||||
}
|
||||
imageViews.clear();
|
||||
images.clear();
|
||||
device.destroySwapchainKHR( swapChain );
|
||||
}
|
||||
|
||||
vk::Format colorFormat;
|
||||
vk::SwapchainKHR swapChain;
|
||||
std::vector<vk::Image> images;
|
||||
std::vector<vk::ImageView> imageViews;
|
||||
};
|
||||
|
||||
class CheckerboardImageGenerator
|
||||
{
|
||||
public:
|
||||
CheckerboardImageGenerator( std::array<uint8_t, 3> const & rgb0 = { { 0, 0, 0 } }, std::array<uint8_t, 3> const & rgb1 = { { 255, 255, 255 } } );
|
||||
|
||||
void operator()( void * data, vk::Extent2D & extent ) const;
|
||||
|
||||
private:
|
||||
std::array<uint8_t, 3> const & m_rgb0;
|
||||
std::array<uint8_t, 3> const & m_rgb1;
|
||||
};
|
||||
|
||||
class MonochromeImageGenerator
|
||||
{
|
||||
public:
|
||||
MonochromeImageGenerator( std::array<unsigned char, 3> const & rgb );
|
||||
|
||||
void operator()( void * data, vk::Extent2D const & extent ) const;
|
||||
|
||||
private:
|
||||
std::array<unsigned char, 3> const & m_rgb;
|
||||
};
|
||||
|
||||
class PixelsImageGenerator
|
||||
{
|
||||
public:
|
||||
PixelsImageGenerator( vk::Extent2D const & extent, size_t channels, unsigned char const * pixels );
|
||||
|
||||
void operator()( void * data, vk::Extent2D const & extent ) const;
|
||||
|
||||
private:
|
||||
vk::Extent2D m_extent;
|
||||
size_t m_channels;
|
||||
unsigned char const * m_pixels;
|
||||
};
|
||||
|
||||
struct TextureData
|
||||
{
|
||||
TextureData( vk::PhysicalDevice const & physicalDevice,
|
||||
vk::Device const & device,
|
||||
vk::Extent2D const & extent_ = { 256, 256 },
|
||||
vk::ImageUsageFlags usageFlags = {},
|
||||
vk::FormatFeatureFlags formatFeatureFlags = {},
|
||||
bool anisotropyEnable = false,
|
||||
bool forceStaging = false );
|
||||
|
||||
void clear( vk::Device const & device )
|
||||
{
|
||||
if ( stagingBufferData )
|
||||
{
|
||||
stagingBufferData->clear( device );
|
||||
}
|
||||
imageData->clear( device );
|
||||
device.destroySampler( sampler );
|
||||
}
|
||||
|
||||
template <typename ImageGenerator>
|
||||
void setImage( vk::Device const & device, vk::CommandBuffer const & commandBuffer, ImageGenerator const & imageGenerator )
|
||||
{
|
||||
void * data = needsStaging
|
||||
? device.mapMemory( stagingBufferData->deviceMemory, 0, device.getBufferMemoryRequirements( stagingBufferData->buffer ).size )
|
||||
: device.mapMemory( imageData->deviceMemory, 0, device.getImageMemoryRequirements( imageData->image ).size );
|
||||
imageGenerator( data, extent );
|
||||
device.unmapMemory( needsStaging ? stagingBufferData->deviceMemory : imageData->deviceMemory );
|
||||
|
||||
if ( needsStaging )
|
||||
{
|
||||
// Since we're going to blit to the texture image, set its layout to eTransferDstOptimal
|
||||
vk::su::setImageLayout( commandBuffer, imageData->image, imageData->format, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal );
|
||||
vk::BufferImageCopy copyRegion( 0,
|
||||
extent.width,
|
||||
extent.height,
|
||||
vk::ImageSubresourceLayers( vk::ImageAspectFlagBits::eColor, 0, 0, 1 ),
|
||||
vk::Offset3D( 0, 0, 0 ),
|
||||
vk::Extent3D( extent, 1 ) );
|
||||
commandBuffer.copyBufferToImage( stagingBufferData->buffer, imageData->image, vk::ImageLayout::eTransferDstOptimal, copyRegion );
|
||||
// Set the layout for the texture image from eTransferDstOptimal to SHADER_READ_ONLY
|
||||
vk::su::setImageLayout(
|
||||
commandBuffer, imageData->image, imageData->format, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal );
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we can use the linear tiled image as a texture, just do it
|
||||
vk::su::setImageLayout(
|
||||
commandBuffer, imageData->image, imageData->format, vk::ImageLayout::ePreinitialized, vk::ImageLayout::eShaderReadOnlyOptimal );
|
||||
}
|
||||
}
|
||||
|
||||
vk::Format format;
|
||||
vk::Extent2D extent;
|
||||
bool needsStaging;
|
||||
std::unique_ptr<BufferData> stagingBufferData;
|
||||
std::unique_ptr<ImageData> imageData;
|
||||
vk::Sampler sampler;
|
||||
};
|
||||
|
||||
struct UUID
|
||||
{
|
||||
public:
|
||||
UUID( uint8_t const data[VK_UUID_SIZE] );
|
||||
|
||||
uint8_t m_data[VK_UUID_SIZE];
|
||||
};
|
||||
|
||||
template <typename TargetType, typename SourceType>
|
||||
VULKAN_HPP_INLINE TargetType checked_cast( SourceType value )
|
||||
{
|
||||
static_assert( sizeof( TargetType ) <= sizeof( SourceType ), "No need to cast from smaller to larger type!" );
|
||||
static_assert( std::numeric_limits<SourceType>::is_integer, "Only integer types supported!" );
|
||||
static_assert( !std::numeric_limits<SourceType>::is_signed, "Only unsigned types supported!" );
|
||||
static_assert( std::numeric_limits<TargetType>::is_integer, "Only integer types supported!" );
|
||||
static_assert( !std::numeric_limits<TargetType>::is_signed, "Only unsigned types supported!" );
|
||||
assert( value <= std::numeric_limits<TargetType>::max() );
|
||||
return static_cast<TargetType>( value );
|
||||
}
|
||||
|
||||
vk::DeviceMemory allocateDeviceMemory( vk::Device const & device,
|
||||
vk::PhysicalDeviceMemoryProperties const & memoryProperties,
|
||||
vk::MemoryRequirements const & memoryRequirements,
|
||||
vk::MemoryPropertyFlags memoryPropertyFlags );
|
||||
bool contains( std::vector<vk::ExtensionProperties> const & extensionProperties, std::string const & extensionName );
|
||||
vk::DescriptorPool createDescriptorPool( vk::Device const & device, std::vector<vk::DescriptorPoolSize> const & poolSizes );
|
||||
vk::DescriptorSetLayout createDescriptorSetLayout( vk::Device const & device,
|
||||
std::vector<std::tuple<vk::DescriptorType, uint32_t, vk::ShaderStageFlags>> const & bindingData,
|
||||
vk::DescriptorSetLayoutCreateFlags flags = {} );
|
||||
vk::Device createDevice( vk::PhysicalDevice const & physicalDevice,
|
||||
uint32_t queueFamilyIndex,
|
||||
std::vector<std::string> const & extensions = {},
|
||||
vk::PhysicalDeviceFeatures const * physicalDeviceFeatures = nullptr,
|
||||
void const * pNext = nullptr );
|
||||
std::vector<vk::Framebuffer> createFramebuffers( vk::Device const & device,
|
||||
vk::RenderPass & renderPass,
|
||||
std::vector<vk::ImageView> const & imageViews,
|
||||
vk::ImageView const & depthImageView,
|
||||
vk::Extent2D const & extent );
|
||||
vk::Pipeline createGraphicsPipeline( vk::Device const & device,
|
||||
vk::PipelineCache const & pipelineCache,
|
||||
std::pair<vk::ShaderModule, vk::SpecializationInfo const *> const & vertexShaderData,
|
||||
std::pair<vk::ShaderModule, vk::SpecializationInfo const *> const & fragmentShaderData,
|
||||
uint32_t vertexStride,
|
||||
std::vector<std::pair<vk::Format, uint32_t>> const & vertexInputAttributeFormatOffset,
|
||||
vk::FrontFace frontFace,
|
||||
bool depthBuffered,
|
||||
vk::PipelineLayout const & pipelineLayout,
|
||||
vk::RenderPass const & renderPass );
|
||||
vk::Instance createInstance( std::string const & appName,
|
||||
std::string const & engineName,
|
||||
std::vector<std::string> const & layers = {},
|
||||
std::vector<std::string> const & extensions = {},
|
||||
uint32_t apiVersion = VK_API_VERSION_1_0 );
|
||||
vk::RenderPass createRenderPass( vk::Device const & device,
|
||||
vk::Format colorFormat,
|
||||
vk::Format depthFormat,
|
||||
vk::AttachmentLoadOp loadOp = vk::AttachmentLoadOp::eClear,
|
||||
vk::ImageLayout colorFinalLayout = vk::ImageLayout::ePresentSrcKHR );
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsMessengerCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
|
||||
VkDebugUtilsMessengerCallbackDataEXT const * pCallbackData,
|
||||
void * /*pUserData*/ );
|
||||
uint32_t findGraphicsQueueFamilyIndex( std::vector<vk::QueueFamilyProperties> const & queueFamilyProperties );
|
||||
std::pair<uint32_t, uint32_t> findGraphicsAndPresentQueueFamilyIndex( vk::PhysicalDevice physicalDevice, vk::SurfaceKHR const & surface );
|
||||
uint32_t findMemoryType( vk::PhysicalDeviceMemoryProperties const & memoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirementsMask );
|
||||
std::vector<char const *> gatherExtensions( std::vector<std::string> const & extensions
|
||||
#if !defined( NDEBUG )
|
||||
,
|
||||
std::vector<vk::ExtensionProperties> const & extensionProperties
|
||||
#endif
|
||||
);
|
||||
std::vector<char const *> gatherLayers( std::vector<std::string> const & layers
|
||||
#if !defined( NDEBUG )
|
||||
,
|
||||
std::vector<vk::LayerProperties> const & layerProperties
|
||||
#endif
|
||||
);
|
||||
std::vector<std::string> getDeviceExtensions();
|
||||
std::vector<std::string> getInstanceExtensions();
|
||||
vk::DebugUtilsMessengerCreateInfoEXT makeDebugUtilsMessengerCreateInfoEXT();
|
||||
#if defined( NDEBUG )
|
||||
vk::StructureChain<vk::InstanceCreateInfo>
|
||||
#else
|
||||
vk::StructureChain<vk::InstanceCreateInfo, vk::DebugUtilsMessengerCreateInfoEXT>
|
||||
#endif
|
||||
makeInstanceCreateInfoChain( vk::ApplicationInfo const & applicationInfo,
|
||||
std::vector<char const *> const & layers,
|
||||
std::vector<char const *> const & extensions );
|
||||
vk::Format pickDepthFormat( vk::PhysicalDevice const & physicalDevice );
|
||||
vk::PresentModeKHR pickPresentMode( std::vector<vk::PresentModeKHR> const & presentModes );
|
||||
vk::SurfaceFormatKHR pickSurfaceFormat( std::vector<vk::SurfaceFormatKHR> const & formats );
|
||||
void submitAndWait( vk::Device const & device, vk::Queue const & queue, vk::CommandBuffer const & commandBuffer );
|
||||
void updateDescriptorSets( vk::Device const & device,
|
||||
vk::DescriptorSet const & descriptorSet,
|
||||
std::vector<std::tuple<vk::DescriptorType, vk::Buffer const &, vk::DeviceSize, vk::BufferView const &>> const & bufferData,
|
||||
vk::su::TextureData const & textureData,
|
||||
uint32_t bindingOffset = 0 );
|
||||
void updateDescriptorSets( vk::Device const & device,
|
||||
vk::DescriptorSet const & descriptorSet,
|
||||
std::vector<std::tuple<vk::DescriptorType, vk::Buffer const &, vk::DeviceSize, vk::BufferView const &>> const & bufferData,
|
||||
std::vector<vk::su::TextureData> const & textureData,
|
||||
uint32_t bindingOffset = 0 );
|
||||
|
||||
} // namespace su
|
||||
} // namespace vk
|
||||
|
||||
std::ostream & operator<<( std::ostream & os, vk::su::UUID const & uuid );
|
@ -1,8 +0,0 @@
|
||||
#include "compound_widget.h"
|
||||
|
||||
void compound_widget::set_content(const std::shared_ptr<widget>& content) {
|
||||
child_ = content;
|
||||
}
|
||||
|
||||
void compound_widget::on_paint(ImGuiIO& io) {
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
#include "widget.h"
|
||||
|
||||
class compound_widget : public widget {
|
||||
public:
|
||||
void set_content(const std::shared_ptr<widget>& content);
|
||||
void on_paint(ImGuiIO& io) override;
|
||||
private:
|
||||
std::shared_ptr<widget> child_;
|
||||
};
|
@ -1 +0,0 @@
|
||||
#include "leaf_widget.h"
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
#include "widget.h"
|
||||
|
||||
class leaf_widget : public widget {
|
||||
public:
|
||||
|
||||
};
|
@ -1 +0,0 @@
|
||||
#include "panel_widget.h"
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
#include "widget.h"
|
||||
|
||||
class panel_widget : public widget {
|
||||
public:
|
||||
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
#include "extern.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
extern "C" {
|
||||
void HideTabBar();
|
||||
}
|
||||
|
||||
namespace ImGui {
|
||||
inline void HideTabBar()
|
||||
{
|
||||
ImGuiWindowClass window_class;
|
||||
window_class.DockNodeFlagsOverrideSet = ImGuiDockNodeFlags_NoWindowMenuButton;
|
||||
// window_class.DockNodeFlagsOverrideSet = ImGuiDockNodeFlags_NoTabBar;
|
||||
SetNextWindowClass(&window_class);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#include "widget.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
ImGuiID widget::generate_widget_id() {
|
||||
// random number generator
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<ImGuiID> dis(0, std::numeric_limits<ImGuiID>::max());
|
||||
return dis(gen);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
class geometry;
|
||||
|
||||
class CORE_API widget : public std::enable_shared_from_this<widget> {
|
||||
public:
|
||||
virtual ~widget() = default;
|
||||
|
||||
virtual void tick() {}
|
||||
virtual void on_paint(ImGuiIO& io) = 0;
|
||||
virtual void on_arrange_children(const geometry& allotted_geometry) {}
|
||||
[[nodiscard]] virtual ImVec2 compute_desired_size() const { return {0, 0}; }
|
||||
static ImGuiID generate_widget_id();
|
||||
};
|
1
third_party/glfw
vendored
1
third_party/glfw
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 64b4f0f30c60f11aee715214a9d8faef4372d946
|
40
third_party/imgui/CMakeLists.txt
vendored
40
third_party/imgui/CMakeLists.txt
vendored
@ -1,40 +0,0 @@
|
||||
project(imgui)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
|
||||
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC
|
||||
imgui/imgui.cpp
|
||||
imgui/imgui.h
|
||||
imgui/imgui_draw.cpp
|
||||
imgui/imgui_internal.h
|
||||
imgui/imconfig.h
|
||||
imgui/imgui_widgets.cpp
|
||||
imgui/imstb_rectpack.h
|
||||
imgui/imstb_textedit.h
|
||||
imgui/imstb_truetype.h
|
||||
imgui/imgui_tables.cpp
|
||||
imgui-knobs/imgui-knobs.h
|
||||
imgui-knobs/imgui-knobs.cpp
|
||||
)
|
||||
|
||||
find_package(Vulkan REQUIRED)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
imgui/backends/imgui_impl_glfw.cpp
|
||||
imgui/backends/imgui_impl_glfw.h
|
||||
imgui/backends/imgui_impl_vulkan.cpp
|
||||
imgui/backends/imgui_impl_vulkan.h
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC glfw ${Vulkan_LIBRARIES})
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/imgui)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/imgui/backends)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/imgui-knobs)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} glfw ${Vulkan_INCLUDE_DIRS})
|
||||
|
||||
target_precompile_headers(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/pch.h)
|
||||
|
||||
add_definitions(-DIMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||
add_compile_definitions(-DIMGUI_DEFINE_MATH_OPERATORS)
|
1
third_party/imgui/imgui
vendored
1
third_party/imgui/imgui
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 8048b52498a9bf2a9f87b080d43b0bfd7a5d51d8
|
303
third_party/imgui/imgui-knobs/imgui-knobs.cpp
vendored
303
third_party/imgui/imgui-knobs/imgui-knobs.cpp
vendored
@ -1,303 +0,0 @@
|
||||
#include "imgui-knobs.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
#define IMGUIKNOBS_PI 3.14159265358979323846f
|
||||
|
||||
namespace ImGuiKnobs {
|
||||
namespace detail {
|
||||
void draw_arc1(ImVec2 center, float radius, float start_angle, float end_angle, float thickness, ImColor color, int num_segments) {
|
||||
ImVec2 start = {
|
||||
center[0] + cosf(start_angle) * radius,
|
||||
center[1] + sinf(start_angle) * radius,
|
||||
};
|
||||
|
||||
ImVec2 end = {
|
||||
center[0] + cosf(end_angle) * radius,
|
||||
center[1] + sinf(end_angle) * radius,
|
||||
};
|
||||
|
||||
// Calculate bezier arc points
|
||||
auto ax = start[0] - center[0];
|
||||
auto ay = start[1] - center[1];
|
||||
auto bx = end[0] - center[0];
|
||||
auto by = end[1] - center[1];
|
||||
auto q1 = ax * ax + ay * ay;
|
||||
auto q2 = q1 + ax * bx + ay * by;
|
||||
auto k2 = (4.0f / 3.0f) * (sqrtf((2.0f * q1 * q2)) - q2) / (ax * by - ay * bx);
|
||||
auto arc1 = ImVec2{center[0] + ax - k2 * ay, center[1] + ay + k2 * ax};
|
||||
auto arc2 = ImVec2{center[0] + bx + k2 * by, center[1] + by - k2 * bx};
|
||||
|
||||
auto *draw_list = ImGui::GetWindowDrawList();
|
||||
#if IMGUI_VERSION_NUM <= 18000
|
||||
draw_list->AddBezierCurve(start, arc1, arc2, end, color, thickness, num_segments);
|
||||
#else
|
||||
draw_list->AddBezierCubic(start, arc1, arc2, end, color, thickness, num_segments);
|
||||
#endif
|
||||
}
|
||||
|
||||
void draw_arc(ImVec2 center, float radius, float start_angle, float end_angle, float thickness, ImColor color, int num_segments, int bezier_count) {
|
||||
// Overlap and angle of ends of bezier curves needs work, only looks good when not transperant
|
||||
auto overlap = thickness * radius * 0.00001f * IMGUIKNOBS_PI;
|
||||
auto delta = end_angle - start_angle;
|
||||
auto bez_step = 1.0f / bezier_count;
|
||||
auto mid_angle = start_angle + overlap;
|
||||
|
||||
for (auto i = 0; i < bezier_count - 1; i++) {
|
||||
auto mid_angle2 = delta * bez_step + mid_angle;
|
||||
draw_arc1(center, radius, mid_angle - overlap, mid_angle2 + overlap, thickness, color, num_segments);
|
||||
mid_angle = mid_angle2;
|
||||
}
|
||||
|
||||
draw_arc1(center, radius, mid_angle - overlap, end_angle, thickness, color, num_segments);
|
||||
}
|
||||
|
||||
template<typename DataType>
|
||||
struct knob {
|
||||
float radius;
|
||||
bool value_changed;
|
||||
ImVec2 center;
|
||||
bool is_active;
|
||||
bool is_hovered;
|
||||
float angle_min;
|
||||
float angle_max;
|
||||
float t;
|
||||
float angle;
|
||||
float angle_cos;
|
||||
float angle_sin;
|
||||
|
||||
knob(const char *_label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, float _radius, const char *format, ImGuiKnobFlags flags) {
|
||||
radius = _radius;
|
||||
t = ((float) *p_value - v_min) / (v_max - v_min);
|
||||
auto screen_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
// Handle dragging
|
||||
ImGui::InvisibleButton(_label, {radius * 2.0f, radius * 2.0f});
|
||||
auto gid = ImGui::GetID(_label);
|
||||
ImGuiSliderFlags drag_flags = 0;
|
||||
if (!(flags & ImGuiKnobFlags_DragHorizontal)) {
|
||||
drag_flags |= ImGuiSliderFlags_Vertical;
|
||||
}
|
||||
value_changed = ImGui::DragBehavior(gid, data_type, p_value, speed, &v_min, &v_max, format, drag_flags);
|
||||
|
||||
angle_min = IMGUIKNOBS_PI * 0.75f;
|
||||
angle_max = IMGUIKNOBS_PI * 2.25f;
|
||||
center = {screen_pos[0] + radius, screen_pos[1] + radius};
|
||||
is_active = ImGui::IsItemActive();
|
||||
is_hovered = ImGui::IsItemHovered();
|
||||
angle = angle_min + (angle_max - angle_min) * t;
|
||||
angle_cos = cosf(angle);
|
||||
angle_sin = sinf(angle);
|
||||
}
|
||||
|
||||
void draw_dot(float size, float radius, float angle, color_set color, bool filled, int segments) {
|
||||
auto dot_size = size * this->radius;
|
||||
auto dot_radius = radius * this->radius;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddCircleFilled(
|
||||
{center[0] + cosf(angle) * dot_radius, center[1] + sinf(angle) * dot_radius},
|
||||
dot_size,
|
||||
is_active ? color.active : (is_hovered ? color.hovered : color.base),
|
||||
segments);
|
||||
}
|
||||
|
||||
void draw_tick(float start, float end, float width, float angle, color_set color) {
|
||||
auto tick_start = start * radius;
|
||||
auto tick_end = end * radius;
|
||||
auto angle_cos = cosf(angle);
|
||||
auto angle_sin = sinf(angle);
|
||||
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{center[0] + angle_cos * tick_end, center[1] + angle_sin * tick_end},
|
||||
{center[0] + angle_cos * tick_start, center[1] + angle_sin * tick_start},
|
||||
is_active ? color.active : (is_hovered ? color.hovered : color.base),
|
||||
width * radius);
|
||||
}
|
||||
|
||||
void draw_circle(float size, color_set color, bool filled, int segments) {
|
||||
auto circle_radius = size * radius;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddCircleFilled(
|
||||
center,
|
||||
circle_radius,
|
||||
is_active ? color.active : (is_hovered ? color.hovered : color.base));
|
||||
}
|
||||
|
||||
void draw_arc(float radius, float size, float start_angle, float end_angle, color_set color, int segments, int bezier_count) {
|
||||
auto track_radius = radius * this->radius;
|
||||
auto track_size = size * this->radius * 0.5f + 0.0001f;
|
||||
|
||||
detail::draw_arc(
|
||||
center,
|
||||
track_radius,
|
||||
start_angle,
|
||||
end_angle,
|
||||
track_size,
|
||||
is_active ? color.active : (is_hovered ? color.hovered : color.base),
|
||||
segments,
|
||||
bezier_count);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename DataType>
|
||||
knob<DataType> knob_with_drag(const char *label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float _speed, const char *format, float size, ImGuiKnobFlags flags) {
|
||||
auto speed = _speed == 0 ? (v_max - v_min) / 250.f : _speed;
|
||||
ImGui::PushID(label);
|
||||
auto width = size == 0 ? ImGui::GetTextLineHeight() * 4.0f : size * ImGui::GetIO().FontGlobalScale;
|
||||
ImGui::PushItemWidth(width);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// There's an issue with `SameLine` and Groups, see https://github.com/ocornut/imgui/issues/4190.
|
||||
// This is probably not the best solution, but seems to work for now
|
||||
ImGui::GetCurrentWindow()->DC.CurrLineTextBaseOffset = 0;
|
||||
|
||||
// Draw title
|
||||
if (!(flags & ImGuiKnobFlags_NoTitle)) {
|
||||
auto title_size = ImGui::CalcTextSize(label, NULL, false, width);
|
||||
|
||||
// Center title
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (width - title_size[0]) * 0.5f);
|
||||
|
||||
ImGui::Text("%s", label);
|
||||
}
|
||||
|
||||
// Draw knob
|
||||
knob<DataType> k(label, data_type, p_value, v_min, v_max, speed, width * 0.5f, format, flags);
|
||||
|
||||
// Draw tooltip
|
||||
if (flags & ImGuiKnobFlags_ValueTooltip && (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) || ImGui::IsItemActive())) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text(format, *p_value);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
// Draw input
|
||||
if (!(flags & ImGuiKnobFlags_NoInput)) {
|
||||
ImGuiSliderFlags drag_flags = 0;
|
||||
if (!(flags & ImGuiKnobFlags_DragHorizontal)) {
|
||||
drag_flags |= ImGuiSliderFlags_Vertical;
|
||||
}
|
||||
auto changed = ImGui::DragScalar("###knob_drag", data_type, p_value, speed, &v_min, &v_max, format, drag_flags);
|
||||
if (changed) {
|
||||
k.value_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::PopID();
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
color_set GetPrimaryColorSet() {
|
||||
auto *colors = ImGui::GetStyle().Colors;
|
||||
|
||||
return {colors[ImGuiCol_ButtonActive], colors[ImGuiCol_ButtonHovered], colors[ImGuiCol_ButtonHovered]};
|
||||
}
|
||||
|
||||
color_set GetSecondaryColorSet() {
|
||||
auto *colors = ImGui::GetStyle().Colors;
|
||||
auto active = ImVec4(
|
||||
colors[ImGuiCol_ButtonActive].x * 0.5f,
|
||||
colors[ImGuiCol_ButtonActive].y * 0.5f,
|
||||
colors[ImGuiCol_ButtonActive].z * 0.5f,
|
||||
colors[ImGuiCol_ButtonActive].w);
|
||||
|
||||
auto hovered = ImVec4(
|
||||
colors[ImGuiCol_ButtonHovered].x * 0.5f,
|
||||
colors[ImGuiCol_ButtonHovered].y * 0.5f,
|
||||
colors[ImGuiCol_ButtonHovered].z * 0.5f,
|
||||
colors[ImGuiCol_ButtonHovered].w);
|
||||
|
||||
return {active, hovered, hovered};
|
||||
}
|
||||
|
||||
color_set GetTrackColorSet() {
|
||||
auto *colors = ImGui::GetStyle().Colors;
|
||||
|
||||
return {colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBg]};
|
||||
}
|
||||
}// namespace detail
|
||||
|
||||
|
||||
template<typename DataType>
|
||||
bool BaseKnob(const char *label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps = 10) {
|
||||
auto knob = detail::knob_with_drag(label, data_type, p_value, v_min, v_max, speed, format, size, flags);
|
||||
|
||||
switch (variant) {
|
||||
case ImGuiKnobVariant_Tick: {
|
||||
knob.draw_circle(0.85f, detail::GetSecondaryColorSet(), true, 32);
|
||||
knob.draw_tick(0.5f, 0.85f, 0.08f, knob.angle, detail::GetPrimaryColorSet());
|
||||
break;
|
||||
}
|
||||
case ImGuiKnobVariant_Dot: {
|
||||
knob.draw_circle(0.85f, detail::GetSecondaryColorSet(), true, 32);
|
||||
knob.draw_dot(0.12f, 0.6f, knob.angle, detail::GetPrimaryColorSet(), true, 12);
|
||||
break;
|
||||
}
|
||||
|
||||
case ImGuiKnobVariant_Wiper: {
|
||||
knob.draw_circle(0.7f, detail::GetSecondaryColorSet(), true, 32);
|
||||
knob.draw_arc(0.8f, 0.41f, knob.angle_min, knob.angle_max, detail::GetTrackColorSet(), 16, 2);
|
||||
|
||||
if (knob.t > 0.01f) {
|
||||
knob.draw_arc(0.8f, 0.43f, knob.angle_min, knob.angle, detail::GetPrimaryColorSet(), 16, 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiKnobVariant_WiperOnly: {
|
||||
knob.draw_arc(0.8f, 0.41f, knob.angle_min, knob.angle_max, detail::GetTrackColorSet(), 32, 2);
|
||||
|
||||
if (knob.t > 0.01) {
|
||||
knob.draw_arc(0.8f, 0.43f, knob.angle_min, knob.angle, detail::GetPrimaryColorSet(), 16, 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiKnobVariant_WiperDot: {
|
||||
knob.draw_circle(0.6f, detail::GetSecondaryColorSet(), true, 32);
|
||||
knob.draw_arc(0.85f, 0.41f, knob.angle_min, knob.angle_max, detail::GetTrackColorSet(), 16, 2);
|
||||
knob.draw_dot(0.1f, 0.85f, knob.angle, detail::GetPrimaryColorSet(), true, 12);
|
||||
break;
|
||||
}
|
||||
case ImGuiKnobVariant_Stepped: {
|
||||
for (auto n = 0.f; n < steps; n++) {
|
||||
auto a = n / (steps - 1);
|
||||
auto angle = knob.angle_min + (knob.angle_max - knob.angle_min) * a;
|
||||
knob.draw_tick(0.7f, 0.9f, 0.04f, angle, detail::GetPrimaryColorSet());
|
||||
}
|
||||
|
||||
knob.draw_circle(0.6f, detail::GetSecondaryColorSet(), true, 32);
|
||||
knob.draw_dot(0.12f, 0.4f, knob.angle, detail::GetPrimaryColorSet(), true, 12);
|
||||
break;
|
||||
}
|
||||
case ImGuiKnobVariant_Space: {
|
||||
knob.draw_circle(0.3f - knob.t * 0.1f, detail::GetSecondaryColorSet(), true, 16);
|
||||
|
||||
if (knob.t > 0.01f) {
|
||||
knob.draw_arc(0.4f, 0.15f, knob.angle_min - 1.0f, knob.angle - 1.0f, detail::GetPrimaryColorSet(), 16, 2);
|
||||
knob.draw_arc(0.6f, 0.15f, knob.angle_min + 1.0f, knob.angle + 1.0f, detail::GetPrimaryColorSet(), 16, 2);
|
||||
knob.draw_arc(0.8f, 0.15f, knob.angle_min + 3.0f, knob.angle + 3.0f, detail::GetPrimaryColorSet(), 16, 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return knob.value_changed;
|
||||
}
|
||||
|
||||
bool Knob(const char *label, float *p_value, float v_min, float v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps) {
|
||||
const char *_format = format == NULL ? "%.3f" : format;
|
||||
return BaseKnob(label, ImGuiDataType_Float, p_value, v_min, v_max, speed, _format, variant, size, flags, steps);
|
||||
}
|
||||
|
||||
bool KnobInt(const char *label, int *p_value, int v_min, int v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps) {
|
||||
const char *_format = format == NULL ? "%i" : format;
|
||||
return BaseKnob(label, ImGuiDataType_S32, p_value, v_min, v_max, speed, _format, variant, size, flags, steps);
|
||||
}
|
||||
|
||||
}// namespace ImGuiKnobs
|
45
third_party/imgui/imgui-knobs/imgui-knobs.h
vendored
45
third_party/imgui/imgui-knobs/imgui-knobs.h
vendored
@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <imgui.h>
|
||||
|
||||
typedef int ImGuiKnobFlags;
|
||||
|
||||
enum ImGuiKnobFlags_ {
|
||||
ImGuiKnobFlags_NoTitle = 1 << 0,
|
||||
ImGuiKnobFlags_NoInput = 1 << 1,
|
||||
ImGuiKnobFlags_ValueTooltip = 1 << 2,
|
||||
ImGuiKnobFlags_DragHorizontal = 1 << 3,
|
||||
};
|
||||
|
||||
typedef int ImGuiKnobVariant;
|
||||
|
||||
enum ImGuiKnobVariant_ {
|
||||
ImGuiKnobVariant_Tick = 1 << 0,
|
||||
ImGuiKnobVariant_Dot = 1 << 1,
|
||||
ImGuiKnobVariant_Wiper = 1 << 2,
|
||||
ImGuiKnobVariant_WiperOnly = 1 << 3,
|
||||
ImGuiKnobVariant_WiperDot = 1 << 4,
|
||||
ImGuiKnobVariant_Stepped = 1 << 5,
|
||||
ImGuiKnobVariant_Space = 1 << 6,
|
||||
};
|
||||
|
||||
namespace ImGuiKnobs {
|
||||
|
||||
struct color_set {
|
||||
ImColor base;
|
||||
ImColor hovered;
|
||||
ImColor active;
|
||||
|
||||
color_set(ImColor base, ImColor hovered, ImColor active) : base(base), hovered(hovered), active(active) {}
|
||||
|
||||
color_set(ImColor color) {
|
||||
base = color;
|
||||
hovered = color;
|
||||
active = color;
|
||||
}
|
||||
};
|
||||
|
||||
bool Knob(const char *label, float *p_value, float v_min, float v_max, float speed = 0, const char *format = NULL, ImGuiKnobVariant variant = ImGuiKnobVariant_Tick, float size = 0, ImGuiKnobFlags flags = 0, int steps = 10);
|
||||
bool KnobInt(const char *label, int *p_value, int v_min, int v_max, float speed = 0, const char *format = NULL, ImGuiKnobVariant variant = ImGuiKnobVariant_Tick, float size = 0, ImGuiKnobFlags flags = 0, int steps = 10);
|
||||
}// namespace ImGuiKnobs
|
0
third_party/imgui/pch.h
vendored
0
third_party/imgui/pch.h
vendored
Loading…
x
Reference in New Issue
Block a user