Merge pull request #5 from benkyd/vulkan_rewrite

Vulkan rewrite merge
This commit is contained in:
Benjamin Kyd
2023-12-19 16:15:49 +00:00
committed by GitHub
83 changed files with 13942 additions and 13291 deletions

View File

@@ -57,7 +57,7 @@ BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 0
ColumnLimit: 90
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false

186
.vims Normal file
View File

@@ -0,0 +1,186 @@
let SessionLoad = 1
let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1
let v:this_session=expand("<sfile>:p")
silent only
silent tabonly
cd ~/dprog/active/inferno-hart
if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''
let s:wipebuf = bufnr('%')
endif
let s:shortmess_save = &shortmess
if &shortmess =~ 'A'
set shortmess=aoOA
else
set shortmess=aoO
endif
badd +693 src/graphics/vulkan_tutorial.cpp
badd +316 src/inferno.cpp
badd +45 src/scene/mesh.hpp
badd +75 src/scene/mesh.cpp
badd +33 src/graphics/pipeline.cpp
badd +40 src/graphics/vkrenderer.cpp
badd +14 src/graphics/pipeline.hpp
badd +1 src/graphics/device.cpp
badd +20 src/graphics/device.hpp
badd +54 src/graphics/renderpass.cpp
badd +20 src/graphics/renderpass.hpp
badd +13 src/graphics/swapchain.hpp
badd +56 src/inferno.hpp
badd +1 src/main.cpp
badd +1 ~/dprog/active/inferno-hart/src/graphics/descriptor.cpp
badd +53 src/graphics/vkrenderer.hpp
badd +42 src/renderer/renderer.hpp
badd +28 src/scene/scene.cpp
badd +173 src/graphics/buffer.cpp
badd +5 res/shaders/vulkan_test.vert
badd +46 src/graphics/buffer.hpp
badd +3 src/renderer/renderer.cpp
badd +279 src/graphics/swapchain.cpp
badd +22 res/shaders/basic.vert
badd +181 src/graphics/shader.cpp
badd +51 src/graphics/shader.hpp
badd +2 src/scene/scene.hpp
badd +240 src/scene/camera.cpp
badd +59 libhart/scene/camera.hpp
badd +30 src/graphics.hpp
badd +43 src/gui/gui.hpp
badd +130 src/window.cpp
badd +111 src/thirdparty/imgui/imgui_impl_vulkan.cpp
badd +66 ~/dprog/active/inferno-hart/src/thirdparty/imgui/imgui_impl_vulkan.h
badd +38 src/preview_renderer/renderer.hpp
badd +59 src/preview_renderer/renderer.cpp
argglobal
%argdel
edit src/inferno.cpp
let s:save_splitbelow = &splitbelow
let s:save_splitright = &splitright
set splitbelow splitright
wincmd _ | wincmd |
vsplit
wincmd _ | wincmd |
vsplit
2wincmd h
wincmd w
wincmd w
wincmd _ | wincmd |
split
1wincmd k
wincmd w
let &splitbelow = s:save_splitbelow
let &splitright = s:save_splitright
wincmd t
let s:save_winminheight = &winminheight
let s:save_winminwidth = &winminwidth
set winminheight=0
set winheight=1
set winminwidth=0
set winwidth=1
wincmd =
argglobal
balt src/preview_renderer/renderer.hpp
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 317 - ((60 * winheight(0) + 43) / 86)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 317
normal! 050|
wincmd w
argglobal
if bufexists(fnamemodify("src/preview_renderer/renderer.cpp", ":p")) | buffer src/preview_renderer/renderer.cpp | else | edit src/preview_renderer/renderer.cpp | endif
if &buftype ==# 'terminal'
silent file src/preview_renderer/renderer.cpp
endif
balt src/graphics/vkrenderer.cpp
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 59 - ((56 * winheight(0) + 43) / 86)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 59
normal! 015|
wincmd w
argglobal
if bufexists(fnamemodify("src/graphics/vkrenderer.hpp", ":p")) | buffer src/graphics/vkrenderer.hpp | else | edit src/graphics/vkrenderer.hpp | endif
if &buftype ==# 'terminal'
silent file src/graphics/vkrenderer.hpp
endif
balt src/graphics/vkrenderer.cpp
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 10 - ((4 * winheight(0) + 24) / 49)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 10
normal! 0
wincmd w
argglobal
if bufexists(fnamemodify("src/preview_renderer/renderer.hpp", ":p")) | buffer src/preview_renderer/renderer.hpp | else | edit src/preview_renderer/renderer.hpp | endif
if &buftype ==# 'terminal'
silent file src/preview_renderer/renderer.hpp
endif
balt src/graphics/pipeline.hpp
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 1 - ((0 * winheight(0) + 18) / 36)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 1
normal! 08|
wincmd w
wincmd =
tabnext 1
if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0 && getbufvar(s:wipebuf, '&buftype') isnot# 'terminal'
silent exe 'bwipe ' . s:wipebuf
endif
unlet! s:wipebuf
set winheight=1 winwidth=20
let &shortmess = s:shortmess_save
let &winminheight = s:save_winminheight
let &winminwidth = s:save_winminwidth
let s:sx = expand("<sfile>:p:r")."x.vim"
if filereadable(s:sx)
exe "source " . fnameescape(s:sx)
endif
let &g:so = s:so_save | let &g:siso = s:siso_save
set hlsearch
nohlsearch
doautoall SessionLoadPost
unlet SessionLoad
" vim: set ft=vim :

77
.vscode/settings.json vendored
View File

@@ -1,77 +0,0 @@
{
"files.associations": {
"string": "cpp",
"vector": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"forward_list": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cfenv": "cpp",
"cinttypes": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"valarray": "cpp",
"variant": "cpp",
"complex": "cpp",
"hash_map": "cpp",
"filesystem": "cpp"
},
"cmake.configureOnOpen": false
}

View File

@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.13)
project(inferno)
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
file(GLOB SRC "src/*.cpp" "src/graphics/*.cpp" "src/preview_renderer/*.cpp" "src/scene/*.cpp" "src/thirdparty/imgui/*.cpp" "src/preview_renderer/*.cpp")
add_executable(inferno ${SRC})
target_include_directories(inferno PRIVATE "libhart/")
target_include_directories(inferno PRIVATE "libhart/thirdparty")
target_include_directories(inferno PRIVATE "libhart/")
target_include_directories(inferno PRIVATE "src/")
target_include_directories(inferno PRIVATE "src/thirdparty")
@@ -35,23 +35,49 @@ else()
endif()
endif()
# Compile shaders first..
file(GLOB SHADERS "res/shaders/*.vert" "res/shaders/*.frag")
foreach(SHADER ${SHADERS})
get_filename_component(FILENAME ${SHADER} ABSOLUTE)
set(SPIRV "${FILENAME}.spv")
add_custom_command(
OUTPUT ${SPIRV}
COMMAND glslc ${SHADER} -o ${SPIRV}
DEPENDS ${SHADER}
)
list(APPEND SPIRV_BINARY_FILES ${SPIRV})
endforeach(SHADER)
add_custom_target(
shaders
DEPENDS ${SPIRV_BINARY_FILES}
)
add_dependencies(inferno shaders)
# Copy resources
#invole install_res.sh on compile with make
add_custom_command(TARGET shaders POST_BUILD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/install_res.sh
COMMENT "Copying resources"
)
# "Universal" libraries
set(THREADS_PREFER_PTHREAD_FLAD ON)
find_package(Threads REQUIRED)
set(OpenGL_GL_PREFERENCE GLVND)
find_package(OpenGL REQUIRED)
find_package(OpenMP REQUIRED)
find_package(Vulkan REQUIRED)
# Libraries
if (WIN32)
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake"
find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(inferno PUBLIC glfw)
# OpenGL
target_link_libraries(inferno PRIVATE OpenGL::GL)
target_link_libraries(inferno PRIVATE Vulkan::Vulkan)
else()
find_package(PkgConfig)
@@ -67,7 +93,8 @@ else()
target_link_libraries(inferno PRIVATE
${GLFW3_LIBRARIES}
OpenGL::GL
Vulkan::Vulkan
libvulkan.so
OpenMP::OpenMP_CXX
)
endif()

1
DAP Breakpoints Normal file
View File

@@ -0,0 +1 @@

View File

@@ -1,37 +0,0 @@
[Window][Preview]
Pos=8,34
Size=499,677
Collapsed=0
DockId=0x00000001,0
[Window][Render]
Pos=509,34
Size=763,677
Collapsed=0
DockId=0x00000002,0
[Window][main]
Pos=0,0
Size=1280,720
Collapsed=0
[Window][Debug##Default]
Pos=60,60
Size=400,400
Collapsed=0
[Window][Inferno HART]
Pos=60,60
Size=338,222
Collapsed=0
[Window][Dear ImGui Demo]
Pos=650,20
Size=550,680
Collapsed=0
[Docking][Data]
DockSpace ID=0x9498A894 Window=0xBF28CD64 Pos=8,34 Size=1264,677 Split=X
DockNode ID=0x00000001 Parent=0x9498A894 SizeRef=499,1000 Selected=0x382916D5
DockNode ID=0x00000002 Parent=0x9498A894 SizeRef=499,1000 CentralNode=1 Selected=0x81AED595

1
install_res.sh Executable file
View File

@@ -0,0 +1 @@
rsync -a --progress res/ build/res/

View File

@@ -1,7 +1,4 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
@@ -19,9 +16,9 @@
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"text": "-enable-pretty-printing"
}
],
]
}
]
}
}

View File

@@ -2,18 +2,18 @@
// easy include for graphics shit
// Include OpenGL
extern "C"
{
#include <glad/glad.h>
}
// Include GLFW
// Include GLFW and ImGUI
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
// glm
#include <glm/glm.hpp>
#include <glm/gtx/intersect.hpp>
#include <glm/gtx/component_wise.hpp>
#include <glm/gtx/string_cast.hpp>
#include "imgui/imgui.h"
#include "imgui/imgui_internal.h"
#include "imgui/imgui_impl_vulkan.h"
#include "imgui/imgui_impl_glfw.h"
// glm
#define GLM_FORCE_SWIZZLE
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

View File

@@ -55,8 +55,8 @@ glm::ivec2 camera_raster_get_viewport(Camera* camera);
void camera_ray_set_viewport(Camera* camera, glm::ivec2 viewport);
glm::ivec2 camera_ray_get_viewport(Camera* camera);
void camera_move(Camera* camera, uint8_t movement_delta);
void camera_mouse_move(Camera* camera, glm::vec2 mouse_delta);
void camera_move(Camera* camera, uint8_t movement_delta, float delta_time);
void camera_mouse_move(Camera* camera, glm::vec2 mouse_delta, float delta_time);
void camera_set_position(Camera* camera, glm::vec3 position);
void camera_set_euler_look(Camera* camera, float roll,

File diff suppressed because it is too large Load Diff

View File

@@ -1,282 +0,0 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

View File

@@ -1,41 +1,20 @@
#type vertex
#version 450 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 uv;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
out vec3 vNormal;
out vec3 vFragPos;
void main() {
vFragPos = vec3(model * vec4(position, 1.0));
vNormal = normal;
gl_Position = proj * view * model * vec4(position, 1.0);
}
#type fragment
#version 450 core
layout (location = 0) in vec3 vNormal;
layout (location = 1) in vec3 vFragPos;
layout(location = 0) out vec4 outColour;
in vec3 vNormal;
in vec3 vFragPos;
vec3 lightColour = {1.0, 1.0, 1.0};
vec3 lightPos = {12.0, 2.0, 4.0};
vec3 lightPos = {12.0, 2.0, 4.0};
vec3 objectColour = {1.0, 0.74, 0.21};
void main() {
// ambient
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColour;
// diffuse
// diffuse
vec3 norm = normalize(vNormal);
vec3 lightDir = normalize(lightPos - vFragPos);
float diff = max(dot(norm, lightDir), 0.0);

BIN
res/shaders/basic.frag.spv Normal file

Binary file not shown.

22
res/shaders/basic.vert Normal file
View File

@@ -0,0 +1,22 @@
#version 450 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (binding = 0) uniform SceneUniformBufferObject {
mat4 proj;
mat4 view;
mat4 unused0;
mat4 unused1;
} ubo;
// mat4 model;
layout (location = 0) out vec3 vNormal;
layout (location = 1) out vec3 vFragPos;
void main() {
vFragPos = vec3(vec4(position, 1.0));
vNormal = normal;
gl_Position = ubo.proj * ubo.view * vec4(position, 1.0);
}

BIN
res/shaders/basic.vert.spv Normal file

Binary file not shown.

View File

@@ -0,0 +1,11 @@
#version 450 core
layout (location = 0) in vec3 vFragPos;
layout(location = 0) out vec4 outColour;
void main()
{
outColour = vec4(1., 0., 0., 1.0);
}

Binary file not shown.

View File

@@ -0,0 +1,19 @@
#version 450 core
layout (location = 0) in vec3 position;
layout (binding = 0) uniform SceneUniformBufferObject {
mat4 proj;
mat4 view;
mat4 unused0;
mat4 unused1;
} ubo;
// mat4 model;
layout (location = 1) out vec3 vFragPos;
void main() {
vFragPos = vec3(vec4(position, 1.0));
gl_Position = ubo.proj * ubo.view * vec4(position, 1.0);
}

Binary file not shown.

View File

@@ -0,0 +1,11 @@
#version 450 core
in vec4 vFragPos;
layout(location = 0) out vec4 outColour;
void main()
{
outColour = vec4(vFragPos.xyz, 1.0);
}

View File

@@ -1,4 +1,3 @@
#type vertex
#version 450 core
layout(location = 0) in vec4 position;
@@ -14,16 +13,3 @@ void main()
vFragPos = model * position;
gl_Position = proj * view * model * position;
}
#type fragment
#version 450 core
in vec4 vFragPos;
layout(location = 0) out vec4 outColour;
void main()
{
outColour = vec4(vFragPos.xyz, 1.0);
}

View File

@@ -0,0 +1,9 @@
#version 450 core
layout(location = 0) in vec3 fFragColour;
layout(location = 0) out vec4 outColour;
void main() {
outColour = vec4(fFragColour, 1.0);
}

Binary file not shown.

View File

@@ -0,0 +1,13 @@
#version 450 core
layout(location = 0) in vec3 vPosition;
layout(location = 1) in vec3 vColor;
layout(location = 0) out vec3 fFragColour;
void main() {
gl_Position = vec4(vPosition, 1.0);
fFragColour = vColor;
}

Binary file not shown.

View File

@@ -1,21 +1,33 @@
#pragma once
// easy include for graphics shit
// Include OpenGL
extern "C"
{
#include <glad/glad.h>
}
#define FRAMES_IN_FLIGHT 3
#define SHADER_STAGES 2
// Include GLFW and ImGUI
#ifdef _WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#define GLFW_EXPOSE_NATIVE_WIN32
#include <windows.h>
#else
#define VK_USE_PLATFORM_XLIB_KHR
#define GLFW_EXPOSE_NATIVE_X11
#include <X11/Xlib.h>
#endif
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <vulkan/vulkan_xlib.h>
#include <GLFW/glfw3native.h>
#include "imgui/imgui.h"
#include "imgui/imgui_internal.h"
#include "imgui/imgui_impl_opengl3.h"
#include "imgui/imgui_impl_glfw.h"
#include "imgui/imgui_impl_vulkan.h"
#include "imgui/imgui_internal.h"
// glm
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#define GLM_FORCE_SWIZZLE
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
@@ -23,7 +35,7 @@ extern "C"
#include <vector>
namespace inferno {
namespace graphics::rays {
class Ray;
class Ray;
}
using RayField = std::vector<graphics::rays::Ray*>;
}

203
src/graphics/buffer.cpp Normal file
View File

@@ -0,0 +1,203 @@
#include "buffer.hpp"
#include "device.hpp"
#include "scene/mesh.hpp"
#include "yolo/yolo.hpp"
#include <vulkan/vulkan_core.h>
namespace inferno::graphics {
GenBuffer* generic_buffer_create(GraphicsDevice* device, uint32_t count,
VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties)
{
GenBuffer* buffer = new GenBuffer;
buffer->Device = device;
buffer->Count = count;
buffer->Size = size;
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(device->VulkanDevice, &bufferInfo, nullptr, &buffer->Handle)
!= VK_SUCCESS) {
yolo::error("failed to create vertex buffer!");
}
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(device->VulkanDevice, buffer->Handle, &memRequirements);
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex
= device_find_memory_type(device, memRequirements.memoryTypeBits, properties);
if (vkAllocateMemory(device->VulkanDevice, &allocInfo, nullptr, &buffer->DeviceData)
!= VK_SUCCESS) {
yolo::error("failed to allocate vertex buffer memory!");
}
vkBindBufferMemory(device->VulkanDevice, buffer->Handle, buffer->DeviceData, 0);
return buffer;
}
void generic_buffer_cleanup(GenBuffer* buffer)
{
vkDestroyBuffer(buffer->Device->VulkanDevice, buffer->Handle, nullptr);
vkFreeMemory(buffer->Device->VulkanDevice, buffer->DeviceData, nullptr);
delete buffer;
}
void buffer_copy(Buffer* buffer, GraphicsDevice* device)
{
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
// shit this needs command pool access
allocInfo.commandPool = device->VulkanCommandPool;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(device->VulkanDevice, &allocInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
VkBufferCopy copyRegion = {};
copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0;
copyRegion.size = buffer->StagingBuffer->Size;
vkCmdCopyBuffer(commandBuffer, buffer->StagingBuffer->Handle,
buffer->GenericBuffer->Handle, 1, &copyRegion);
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(device->VulkanGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(device->VulkanGraphicsQueue);
vkFreeCommandBuffers(
device->VulkanDevice, device->VulkanCommandPool, 1, &commandBuffer);
}
Buffer* vertex_buffer_create(GraphicsDevice* device, void* data, uint32_t size, bool bind)
{
VkDeviceSize bufferSize = size * sizeof(scene::Vert);
Buffer* buffer = new Buffer;
buffer->StagingBuffer = generic_buffer_create(device, size, bufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
yolo::debug("Staging buffer size: {}", bufferSize);
buffer->NullableClientData = data;
void* mData;
vkMapMemory(device->VulkanDevice, buffer->StagingBuffer->DeviceData, 0, bufferSize, 0,
&mData);
memcpy(mData, data, bufferSize);
vkUnmapMemory(device->VulkanDevice, buffer->StagingBuffer->DeviceData);
buffer->GenericBuffer = generic_buffer_create(device, size, bufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
buffer_copy(buffer, device);
return buffer;
}
void vertex_buffer_cleanup(Buffer* buffer)
{
generic_buffer_cleanup(buffer->GenericBuffer);
generic_buffer_cleanup(buffer->StagingBuffer);
delete buffer;
}
void vertex_buffer_update(Buffer* buffer, void* data, uint32_t size)
{
VkDeviceSize bufferSize = size * sizeof(scene::Vert);
if (buffer->StagingBuffer->Size < bufferSize) {
generic_buffer_cleanup(buffer->StagingBuffer);
generic_buffer_cleanup(buffer->GenericBuffer);
buffer->StagingBuffer = generic_buffer_create(buffer->GenericBuffer->Device, size,
bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
buffer->GenericBuffer
= generic_buffer_create(buffer->GenericBuffer->Device, size, bufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
void* mData;
vkMapMemory(buffer->GenericBuffer->Device->VulkanDevice,
buffer->StagingBuffer->DeviceData, 0, bufferSize, 0, &mData);
memcpy(mData, data, bufferSize);
vkUnmapMemory(
buffer->GenericBuffer->Device->VulkanDevice, buffer->StagingBuffer->DeviceData);
buffer_copy(buffer, buffer->GenericBuffer->Device);
}
void vertex_buffer_bind(Buffer* buffer, VkCommandBuffer commandBuffer)
{
VkBuffer vertexBuffers[] = { buffer->GenericBuffer->Handle };
VkDeviceSize offsets[] = { 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
}
Buffer* index_buffer_create(GraphicsDevice* device, void* data, uint32_t size, bool bind)
{
VkDeviceSize bufferSize = size * sizeof(scene::Index);
Buffer* buffer = new Buffer;
buffer->StagingBuffer = generic_buffer_create(device, size, bufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
buffer->NullableClientData = data;
void* mData;
vkMapMemory(device->VulkanDevice, buffer->StagingBuffer->DeviceData, 0, bufferSize, 0,
&mData);
memcpy(mData, data, bufferSize);
vkUnmapMemory(device->VulkanDevice, buffer->StagingBuffer->DeviceData);
buffer->GenericBuffer = generic_buffer_create(device, size, bufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
buffer_copy(buffer, device);
return buffer;
}
void index_buffer_cleanup(Buffer* buffer)
{
generic_buffer_cleanup(buffer->GenericBuffer);
generic_buffer_cleanup(buffer->StagingBuffer);
delete buffer;
}
void index_buffer_bind(Buffer* buffer, VkCommandBuffer commandBuffer)
{
vkCmdBindIndexBuffer(
commandBuffer, buffer->GenericBuffer->Handle, 0, VK_INDEX_TYPE_UINT32);
}
}

74
src/graphics/buffer.hpp Normal file
View File

@@ -0,0 +1,74 @@
#pragma once
#include "graphics.hpp"
#include "device.hpp"
namespace inferno::scene {
struct Vert;
}
namespace inferno::graphics {
struct GraphicsDevice;
typedef struct GenBuffer {
GraphicsDevice* Device;
VkBuffer Handle;
VkDeviceMemory DeviceData;
void* MappedData;
uint32_t Count;
VkDeviceSize Size;
} GenBuffer;
// NOTE: Staging Buffer and Buffer are literally the exact same thing
// It's all about the semantics of on-device memory transfer
// TODO: Does the size of the staging buffer matter?
typedef struct Buffer {
GenBuffer* GenericBuffer;
GenBuffer* StagingBuffer;
void* NullableClientData;
} Buffer;
GenBuffer* generic_buffer_create(GraphicsDevice* device, uint32_t count,
VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties);
void generic_buffer_cleanup(GenBuffer* buffer);
void buffer_copy(Buffer* buffer, GraphicsDevice* device);
Buffer* vertex_buffer_create(
GraphicsDevice* device, void* data, uint32_t size, bool bind = false);
void vertex_buffer_cleanup(Buffer* buffer);
void vertex_buffer_update(Buffer* buffer, void* data, uint32_t size);
void vertex_buffer_bind(Buffer* buffer, VkCommandBuffer commandBuffer);
Buffer* index_buffer_create(
GraphicsDevice* device, void* data, uint32_t size, bool bind = false);
void index_buffer_cleanup(Buffer* buffer);
void index_buffer_update(Buffer* buffer, void* data, uint32_t size);
void index_buffer_bind(Buffer* buffer, VkCommandBuffer commandBuffer);
// We *do* want universally mapped memory to act as a "uniform buffer"
template <typename T>
GenBuffer* uniform_buffer_create(GraphicsDevice* device, bool bind = false)
{
GenBuffer* buffer
= generic_buffer_create(device, 0, sizeof(T), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkMapMemory(device->VulkanDevice, buffer->DeviceData, 0, buffer->Size, 0,
&buffer->MappedData);
return buffer;
}
inline void uniform_buffer_cleanup(GenBuffer* buffer) { generic_buffer_cleanup(buffer); }
template <typename T> void uniform_buffer_update(GenBuffer* buffer, T* data)
{
memcpy(buffer->MappedData, (void*)data, sizeof(T));
}
}

426
src/graphics/device.cpp Normal file
View File

@@ -0,0 +1,426 @@
#include "device.hpp"
#include "graphics.hpp"
#include "window.hpp"
#include "yolo/yolo.hpp"
#include <map>
#include <set>
namespace inferno::graphics {
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
{
yolo::warn("[VULKAN] {}", pCallbackData->pMessage);
return VK_FALSE;
}
VkResult createDebugUtilsMessengerEXT(VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
{
auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
instance, "vkCreateDebugUtilsMessengerEXT");
if (func != nullptr) {
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
} else {
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
void destroyDebugUtilsMessengerEXT(VkInstance instance,
VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator)
{
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, debugMessenger, pAllocator);
}
}
void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo)
{
createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = debugCallback;
}
#ifdef VALIDATION_LAYERS_ENABLED
bool checkValidationLayerSupport()
{
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
for (const char* layerName : VALIDATION_LAYERS) {
bool layerFound = false;
for (const auto& layerProperties : availableLayers) {
if (strcmp(layerName, layerProperties.layerName) == 0) {
layerFound = true;
break;
}
}
if (!layerFound) {
return false;
}
}
return true;
}
#endif
std::vector<const char*> getRequiredExtensions()
{
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(
glfwExtensions, glfwExtensions + glfwExtensionCount);
#ifdef VALIDATION_LAYERS_ENABLED
yolo::info("Validation layers enabled");
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
yolo::info("Requested instance extensions:");
for (const auto& extension : extensions) {
yolo::info("\t{}", extension);
}
return extensions;
}
QueueFamilyIndices device_get_queue_families(GraphicsDevice* g, VkPhysicalDevice device)
{
QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(
device, &queueFamilyCount, queueFamilies.data());
uint32_t queueFamIndex = 0;
for (const auto& queueFamily : queueFamilies) {
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = queueFamIndex;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(
device, queueFamIndex, g->VulkanSurface, &presentSupport);
if (presentSupport) {
indices.presentFamily = queueFamIndex;
}
if (indices.isComplete()) {
break;
}
queueFamIndex++;
}
return indices;
}
bool device_evaluate_extensions(
VkPhysicalDevice device, std::vector<const char*> extensions)
{
uint32_t extensionCount;
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(
device, nullptr, &extensionCount, availableExtensions.data());
std::set<std::string> requiredExtensions(extensions.begin(), extensions.end());
for (const auto& extension : availableExtensions) {
requiredExtensions.erase(extension.extensionName);
}
return requiredExtensions.empty();
}
bool device_evaluate(GraphicsDevice* g, VkPhysicalDevice device)
{
int score = 0;
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
yolo::info("Found device {}", deviceProperties.deviceName);
// Discrete GPUs have a significant performance advantage
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
yolo::info("Device {} is a discrete GPU", deviceProperties.deviceName);
score += 1000;
}
// does the device support the extensions we need?
if (!device_evaluate_extensions(device, DEVICE_EXTENSIONS)) {
return 0;
}
// Maximum possible size of textures affects graphics quality
score += deviceProperties.limits.maxImageDimension2D;
// Ensure that the device can process the graphics commands that we need
QueueFamilyIndices indices = device_get_queue_families(g, device);
if (!indices.isComplete())
return 0;
return score;
}
GraphicsDevice* device_create()
{
GraphicsDevice* device = new GraphicsDevice();
device_create_vulkan_instance(device);
device_vulkan_debugger(device);
window_init_device(device, &device_resize_callback);
device->SurfaceSize = window_get_size();
device_create_vulkan_physical_device(device);
device_create_vulkan_logical_device(device);
device_create_command_pool(device);
return device;
}
void device_cleanup(GraphicsDevice* device)
{
vkDestroyDevice(device->VulkanDevice, nullptr);
#ifdef VALIDATION_LAYERS_ENABLED
destroyDebugUtilsMessengerEXT(
device->VulkanInstance, device->VulkanDebugMessenger, nullptr);
#endif
vkDestroySurfaceKHR(device->VulkanInstance, device->VulkanSurface, nullptr);
vkDestroyInstance(device->VulkanInstance, nullptr);
}
void device_create_vulkan_instance(GraphicsDevice* device)
{
#ifdef VALIDATION_LAYERS_ENABLED
if (!checkValidationLayerSupport()) {
yolo::error("validation layers requested, but not available!");
}
#endif
VkApplicationInfo appInfo {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Inferno HART";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "No Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_3;
VkInstanceCreateInfo createInfo {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
auto extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
#ifdef VALIDATION_LAYERS_ENABLED
createInfo.enabledLayerCount = static_cast<uint32_t>(VALIDATION_LAYERS.size());
createInfo.ppEnabledLayerNames = VALIDATION_LAYERS.data();
populateDebugMessengerCreateInfo(debugCreateInfo);
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo;
#else
createInfo.enabledLayerCount = 0;
createInfo.pNext = nullptr;
#endif
if (vkCreateInstance(&createInfo, nullptr, &device->VulkanInstance) != VK_SUCCESS) {
yolo::error("failed to create instance!");
exit(1);
}
}
void device_vulkan_debugger(GraphicsDevice* device)
{
#ifndef VALIDATION_LAYERS_ENABLED
yolo::warn("Validation layers disabled");
return;
#endif
VkDebugUtilsMessengerCreateInfoEXT createInfo;
populateDebugMessengerCreateInfo(createInfo);
if (createDebugUtilsMessengerEXT(
device->VulkanInstance, &createInfo, nullptr, &device->VulkanDebugMessenger)
!= VK_SUCCESS) {
yolo::error("failed to set up debug messenger!");
exit(1);
}
}
void device_create_vulkan_physical_device(GraphicsDevice* device)
{
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(device->VulkanInstance, &deviceCount, nullptr);
if (deviceCount == 0) {
yolo::error("failed to find GPUs with Vulkan support!");
exit(1);
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(device->VulkanInstance, &deviceCount, devices.data());
// std::multimap is sorted list of key-value pairs. Sorted by key.
std::multimap<int, VkPhysicalDevice> candidates;
for (const auto& device_candidate : devices) {
int score = device_evaluate(device, device_candidate);
candidates.insert(std::make_pair(score, device_candidate));
}
// as candidates is a sorted list, the last value will always be the biggest score
// (first element)
if (candidates.rbegin()->first > 0) {
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(candidates.rbegin()->second, &deviceProperties);
yolo::info("Using device: {}", deviceProperties.deviceName);
// established the device with the best score, or the only one in the system.
device->VulkanPhysicalDevice = candidates.rbegin()->second;
} else {
yolo::error("failed to find a suitable GPU!");
exit(1);
}
}
void device_create_vulkan_logical_device(GraphicsDevice* device)
{
QueueFamilyIndices indices
= device_get_queue_families(device, device->VulkanPhysicalDevice);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<uint32_t> uniqueQueueFamilies
= { indices.graphicsFamily.value(), indices.presentFamily.value() };
float queuePriority = 1.0f;
for (uint32_t queueFamily : uniqueQueueFamilies) {
VkDeviceQueueCreateInfo queueCreateInfo {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfo.queueCount = 1;
queueCreateInfos.push_back(queueCreateInfo);
}
VkPhysicalDeviceFeatures deviceFeatures {};
VkDeviceCreateInfo createInfo {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.enabledExtensionCount = static_cast<uint32_t>(DEVICE_EXTENSIONS.size());
createInfo.ppEnabledExtensionNames = DEVICE_EXTENSIONS.data();
yolo::info("Requested device extensions:");
for (const auto& extension : DEVICE_EXTENSIONS) {
yolo::info("\t{}", extension);
}
VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicCreateInfo {};
dynamicCreateInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR;
dynamicCreateInfo.dynamicRendering = VK_TRUE;
createInfo.pNext = &dynamicCreateInfo;
#ifdef VALIDATION_LAYERS_ENABLED
createInfo.enabledLayerCount = static_cast<uint32_t>(VALIDATION_LAYERS.size());
createInfo.ppEnabledLayerNames = VALIDATION_LAYERS.data();
#endif
#ifndef VALIDATION_LAYERS_ENABLED
createInfo.enabledLayerCount = 0;
#endif
if (vkCreateDevice(
device->VulkanPhysicalDevice, &createInfo, nullptr, &device->VulkanDevice)
!= VK_SUCCESS) {
yolo::error("failed to create logical device!");
exit(1);
}
vkGetDeviceQueue(device->VulkanDevice, indices.graphicsFamily.value(), 0,
&device->VulkanGraphicsQueue);
vkGetDeviceQueue(device->VulkanDevice, indices.presentFamily.value(), 0,
&device->VulkanPresentQueue);
}
void device_create_command_pool(GraphicsDevice* device)
{
QueueFamilyIndices queueFamilyIndices
= device_get_queue_families(device, device->VulkanPhysicalDevice);
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT
| VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
if (vkCreateCommandPool(
device->VulkanDevice, &poolInfo, nullptr, &device->VulkanCommandPool)
!= VK_SUCCESS) {
yolo::error("failed to create command pool!");
}
}
void device_resize_callback(GLFWwindow* window, int width, int height)
{
auto device = (GraphicsDevice*)glfwGetWindowUserPointer(window);
device->Resized = true;
device->SurfaceSize = glm::ivec2(width, height);
yolo::info("Window resized to {}x{}", width, height);
}
uint32_t device_find_memory_type(
GraphicsDevice* g, uint32_t typeFilter, VkMemoryPropertyFlags properties)
{
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(g->VulkanPhysicalDevice, &memProperties);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
if ((typeFilter & (1 << i))
&& (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
yolo::error("Failed to find suitable memory type!");
exit(1);
}
}

60
src/graphics/device.hpp Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
#include "graphics.hpp"
#include <optional>
namespace inferno::graphics {
#define VALIDATION_LAYERS_ENABLED 1
#ifdef VALIDATION_LAYERS_ENABLED
const std::vector<const char*> VALIDATION_LAYERS = {
"VK_LAYER_KHRONOS_validation",
};
#endif
const std::vector<const char*> DEVICE_EXTENSIONS = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
};
typedef struct GraphicsDevice {
VkInstance VulkanInstance;
VkDebugUtilsMessengerEXT VulkanDebugMessenger;
VkPhysicalDevice VulkanPhysicalDevice;
VkDevice VulkanDevice;
VkSurfaceKHR VulkanSurface;
VkQueue VulkanGraphicsQueue;
VkQueue VulkanPresentQueue;
VkCommandPool VulkanCommandPool;
glm::ivec2 SurfaceSize;
bool Resized = false;
} GraphicsDevice;
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool isComplete()
{
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
// MUST ONLY BE CALLED AFTER GLFW INIT
GraphicsDevice* device_create();
void device_cleanup(GraphicsDevice* device);
void device_create_vulkan_instance(GraphicsDevice* device);
void device_vulkan_debugger(GraphicsDevice* device);
void device_create_vulkan_physical_device(GraphicsDevice* device);
void device_create_vulkan_logical_device(GraphicsDevice* device);
void device_create_command_pool(GraphicsDevice* device);
void device_resize_callback(GLFWwindow* device, int width, int height);
QueueFamilyIndices device_get_queue_families(GraphicsDevice* g, VkPhysicalDevice device);
uint32_t device_find_memory_type(GraphicsDevice* g, uint32_t typeFilter, VkMemoryPropertyFlags properties);
}

101
src/graphics/image.cpp Normal file
View File

@@ -0,0 +1,101 @@
#include "graphics/image.hpp"
#include "device.hpp"
#include "yolo/yolo.hpp"
namespace inferno::graphics {
void create_image(GraphicsDevice* device, uint32_t width, uint32_t height,
VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage,
VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory)
{
VkImageCreateInfo imageInfo {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = tiling;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usage;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateImage(device->VulkanDevice, &imageInfo, nullptr, &image) != VK_SUCCESS) {
yolo::error("failed to create image!");
}
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(device->VulkanDevice, image, &memRequirements);
VkMemoryAllocateInfo allocInfo {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex
= device_find_memory_type(device, memRequirements.memoryTypeBits, properties);
if (vkAllocateMemory(device->VulkanDevice, &allocInfo, nullptr, &imageMemory)
!= VK_SUCCESS) {
yolo::error("failed to allocate image memory!");
}
vkBindImageMemory(device->VulkanDevice, image, imageMemory, 0);
}
VkImageView create_image_view(GraphicsDevice* device, VkImage image, VkFormat format,
VkImageAspectFlags aspectFlags)
{
VkImageViewCreateInfo viewInfo {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = aspectFlags;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkImageView imageView;
if (vkCreateImageView(device->VulkanDevice, &viewInfo, nullptr, &imageView)
!= VK_SUCCESS) {
yolo::error("failed to create texture image view!");
}
return imageView;
}
VkFormat find_format(GraphicsDevice* device, const std::vector<VkFormat>& candidates,
VkImageTiling tiling, VkFormatFeatureFlags features)
{
for (VkFormat format : candidates) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(device->VulkanPhysicalDevice, format, &props);
if (tiling == VK_IMAGE_TILING_LINEAR
&& (props.linearTilingFeatures & features) == features) {
return format;
} else if (tiling == VK_IMAGE_TILING_OPTIMAL
&& (props.optimalTilingFeatures & features) == features) {
return format;
}
}
throw std::runtime_error("failed to find supported format!");
}
VkFormat find_depth_format(GraphicsDevice* device)
{
return VK_FORMAT_D32_SFLOAT_S8_UINT;
return find_format(device,
{ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
}
}

20
src/graphics/image.hpp Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "graphics.hpp"
namespace inferno::graphics {
struct GraphicsDevice;
void create_image(GraphicsDevice* device, uint32_t width, uint32_t height,
VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage,
VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory);
VkImageView create_image_view(GraphicsDevice* device, VkImage image, VkFormat format,
VkImageAspectFlags aspectFlags);
VkFormat find_format(GraphicsDevice* device, const std::vector<VkFormat>& candidates,
VkImageTiling tiling, VkFormatFeatureFlags features);
VkFormat find_depth_format(GraphicsDevice* device);
}

207
src/graphics/pipeline.cpp Normal file
View File

@@ -0,0 +1,207 @@
#include "pipeline.hpp"
#include "device.hpp"
#include "shader.hpp"
#include "swapchain.hpp"
#include "scene/mesh.hpp"
#include <array>
#include <cstdint>
#include "yolo/yolo.hpp"
namespace inferno::graphics {
Pipeline* pipeline_create(GraphicsDevice* device, SwapChain* swap, Shader* shader,
uint32_t descriptorSetLayoutCount, VkDescriptorSetLayout* layouts, PipelineType type)
{
Pipeline* pipeline = new Pipeline();
pipeline->Device = device;
pipeline->Swap = swap;
pipeline->RelaventShader = shader;
pipeline->Type = type;
pipeline->DescriptorSetLayoutCount = descriptorSetLayoutCount;
pipeline->DescriptorSetLayouts = layouts;
auto bindingDescription
= new VkVertexInputBindingDescription(scene::get_vert_binding_description());
auto attributeDescriptions = new std::array<VkVertexInputAttributeDescription, 2>(
scene::get_vert_attribute_descriptions());
VkPipelineVertexInputStateCreateInfo vertexInputInfo {};
vertexInputInfo.pNext = nullptr;
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.vertexAttributeDescriptionCount
= static_cast<uint32_t>(attributeDescriptions->size());
vertexInputInfo.pVertexBindingDescriptions = bindingDescription;
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions->data();
VkPipelineInputAssemblyStateCreateInfo inputAssembly {};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
if (type == PIPELINE_TYPE_GRAPHICS_LINE) {
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
}
VkPipelineViewportStateCreateInfo viewportState {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo rasterizer {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE; // NOTE: This is for shadow mapping
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.3f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
// rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
if (type == PIPELINE_TYPE_GRAPHICS_LINE) {
rasterizer.polygonMode = VK_POLYGON_MODE_LINE;
rasterizer.cullMode = VK_CULL_MODE_NONE;
}
VkPipelineMultisampleStateCreateInfo multisampling {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT
| VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
// Alpha blending
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor
= VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo colorBlending {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f; // Optional
colorBlending.blendConstants[1] = 0.0f; // Optional
colorBlending.blendConstants[2] = 0.0f; // Optional
colorBlending.blendConstants[3] = 0.0f; // pipeline->Optional
VkPipelineDepthStencilStateCreateInfo depthState {};
depthState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthState.depthTestEnable = VK_TRUE;
depthState.depthWriteEnable = VK_TRUE;
depthState.depthCompareOp = VK_COMPARE_OP_LESS;
depthState.depthBoundsTestEnable = VK_TRUE;
depthState.minDepthBounds = 0.0f;
depthState.maxDepthBounds = 1.0f;
depthState.stencilTestEnable = VK_FALSE;
depthState.front = {};
depthState.back = {};
VkPipelineDynamicStateCreateInfo dynamicStatesCreateInfo {};
std::vector<VkDynamicState> dynamicStates
= { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
dynamicStatesCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStatesCreateInfo.dynamicStateCount
= static_cast<uint32_t>(dynamicStates.size());
dynamicStatesCreateInfo.pDynamicStates = dynamicStates.data();
VkPipelineLayoutCreateInfo pipelineLayoutInfo {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = descriptorSetLayoutCount;
pipelineLayoutInfo.pSetLayouts = layouts;
pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
if (vkCreatePipelineLayout(
device->VulkanDevice, &pipelineLayoutInfo, nullptr, &pipeline->Layout)
!= VK_SUCCESS) {
yolo::error("failed to create pipeline layout!");
return nullptr;
}
VkPipelineRenderingCreateInfo renderingPipelineInfo = {};
renderingPipelineInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
renderingPipelineInfo.colorAttachmentCount = 1;
renderingPipelineInfo.pColorAttachmentFormats = &swap->ImageFormat;
renderingPipelineInfo.depthAttachmentFormat = find_depth_format(device);
renderingPipelineInfo.pNext = nullptr;
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = SHADER_STAGES;
pipelineInfo.pStages = shader->ShaderStages;
pipelineInfo.pNext = &renderingPipelineInfo;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicStatesCreateInfo;
pipelineInfo.pDepthStencilState = &depthState;
pipelineInfo.pTessellationState = VK_NULL_HANDLE; // Optional
pipelineInfo.layout = pipeline->Layout;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
yolo::debug("All Binding Description: {} stride: {}",
vertexInputInfo.pVertexBindingDescriptions->binding,
vertexInputInfo.pVertexBindingDescriptions->stride);
yolo::debug("All Attribute Description0: {} location: {} format: {} offset: {}",
vertexInputInfo.pVertexAttributeDescriptions[0].binding,
vertexInputInfo.pVertexAttributeDescriptions[0].location,
vertexInputInfo.pVertexAttributeDescriptions[0].format,
vertexInputInfo.pVertexAttributeDescriptions[0].offset);
yolo::debug("All Attribute Description1: {} location: {} format: {} offset: {}",
vertexInputInfo.pVertexAttributeDescriptions[1].binding,
vertexInputInfo.pVertexAttributeDescriptions[1].location,
vertexInputInfo.pVertexAttributeDescriptions[1].format,
vertexInputInfo.pVertexAttributeDescriptions[1].offset);
if (vkCreateGraphicsPipelines(device->VulkanDevice, VK_NULL_HANDLE, 1, &pipelineInfo,
nullptr, &pipeline->GraphicsPipeline)
!= VK_SUCCESS) {
yolo::error("failed to create graphics pipeline!");
}
return pipeline;
}
void pipeline_cleanup(Pipeline* pipeline)
{
vkDestroyPipeline(
pipeline->Device->VulkanDevice, pipeline->GraphicsPipeline, nullptr);
vkDestroyPipelineLayout(pipeline->Device->VulkanDevice, pipeline->Layout, nullptr);
delete pipeline;
}
void pipeline_recreate(Pipeline* pipeline)
{
vkDeviceWaitIdle(pipeline->Device->VulkanDevice);
GraphicsDevice* device = pipeline->Device;
SwapChain* swap = pipeline->Swap;
swapchain_recreate(swap);
Shader* shader = pipeline->RelaventShader;
uint32_t descriptorSetLayoutCount = descriptorSetLayoutCount;
VkDescriptorSetLayout* layouts = pipeline->DescriptorSetLayouts;
PipelineType type = pipeline->Type;
pipeline_cleanup(pipeline);
pipeline = pipeline_create(device, swap, shader, descriptorSetLayoutCount, layouts, type);
}
} // namespace inferno::graphics

39
src/graphics/pipeline.hpp Normal file
View File

@@ -0,0 +1,39 @@
#pragma once
#include "graphics.hpp"
namespace inferno::graphics {
struct GraphicsDevice;
struct RenderPass;
struct SwapChain;
struct Shader;
typedef enum PipelineType {
PIPELINE_TYPE_GRAPHICS,
PIPELINE_TYPE_GRAPHICS_LINE,
PIPELINE_TYPE_COMPUTE,
PIPELINE_TYPE_RAYTRACING
} PipelineType;
typedef struct Pipeline {
GraphicsDevice* Device;
SwapChain* Swap;
Shader* RelaventShader;
uint32_t DescriptorSetLayoutCount;
VkDescriptorSetLayout* DescriptorSetLayouts;
VkPipeline GraphicsPipeline;
VkPipelineLayout Layout;
PipelineType Type;
} Pipeline;
Pipeline* pipeline_create(GraphicsDevice* device, SwapChain* swap, Shader* shader,
uint32_t descriptorSetLayoutCount, VkDescriptorSetLayout* layouts, PipelineType type);
void pipeline_cleanup(Pipeline* pipeline);
void pipeline_recreate(Pipeline* pipeline);
}

View File

@@ -0,0 +1,114 @@
#include "rendertarget.hpp"
#include "device.hpp"
#include "graphics.hpp"
#include "image.hpp"
#include "yolo/yolo.hpp"
namespace inferno::graphics {
RenderTarget* rendertarget_create(
GraphicsDevice* device, VkExtent2D extent, VkFormat format, bool depth)
{
RenderTarget* target = new RenderTarget();
target->Device = device;
target->Format = format;
target->Extent = extent;
VkSamplerCreateInfo samplerInfo {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
if (vkCreateSampler(device->VulkanDevice, &samplerInfo, nullptr, &target->Sampler)
!= VK_SUCCESS) {
yolo::error("failed to create texture sampler!");
}
create_image(device, extent.width, extent.height, format, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, target->Image, target->Memory);
target->ImageView
= create_image_view(device, target->Image, format, VK_IMAGE_ASPECT_COLOR_BIT);
target->DescriptorSet = ImGui_ImplVulkan_AddTexture(
target->Sampler, target->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if (depth) {
rendertarget_create_depth(target);
}
return target;
}
void rendertarget_cleanup(RenderTarget* target)
{
vkDestroyImageView(target->Device->VulkanDevice, target->ImageView, nullptr);
vkDestroyImage(target->Device->VulkanDevice, target->Image, nullptr);
vkFreeMemory(target->Device->VulkanDevice, target->Memory, nullptr);
rendertarget_destroy_depth(target);
delete target;
}
void rendertarget_create_depth(RenderTarget* target)
{
VkFormat depthFormat = find_depth_format(target->Device);
target->TargetDepth = new DepthAttachment();
target->TargetDepth->Format = depthFormat;
create_image(target->Device, target->Extent.width, target->Extent.height, depthFormat,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, target->TargetDepth->Image,
target->TargetDepth->Memory);
target->TargetDepth->ImageView = create_image_view(target->Device, target->TargetDepth->Image,
depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
}
void rendertarget_destroy_depth(RenderTarget* target)
{
if (target->TargetDepth != nullptr) {
vkDestroyImageView(
target->Device->VulkanDevice, target->TargetDepth->ImageView, nullptr);
vkDestroyImage(target->Device->VulkanDevice, target->TargetDepth->Image, nullptr);
vkFreeMemory(target->Device->VulkanDevice, target->TargetDepth->Memory, nullptr);
}
}
void rendertarget_recreate(RenderTarget* target, VkExtent2D extent, VkFormat format)
{
vkDeviceWaitIdle(target->Device->VulkanDevice);
bool doDepth = target->TargetDepth != nullptr;
rendertarget_cleanup(target);
target->Extent = extent;
target->Format = format;
rendertarget_create(target->Device, extent, format, doDepth);
// if (doDepth)
// rendertarget_create_depth(target);
}
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include "graphics.hpp"
#include <optional>
namespace inferno::graphics {
struct GraphicsDevice;
// TODO: Should probably be "ImageAttachment" and then the
// target will abstract which sort of attachment it is
typedef struct DepthAttachment {
VkImage Image;
VkDeviceMemory Memory;
VkImageView ImageView;
VkFormat Format;
} DepthAttachment;
// TODO: What about the present mode?
typedef struct RenderTarget {
VkImage Image;
VkDeviceMemory Memory;
VkImageView ImageView;
VkFormat Format;
VkExtent2D Extent;
DepthAttachment* TargetDepth = nullptr;
// NOTE: This is for the ImGui renderer.. it needs a descriptor set of a sampler of an
// image
VkSampler Sampler;
VkDescriptorSet DescriptorSet;
GraphicsDevice* Device;
} RenderTarget;
RenderTarget* rendertarget_create(
GraphicsDevice* device, VkExtent2D extent, VkFormat format, bool depth);
void rendertarget_cleanup(RenderTarget* target);
void rendertarget_create_depth(RenderTarget* target);
void rendertarget_destroy_depth(RenderTarget* target);
void rendertarget_recreate(RenderTarget* target, VkExtent2D extent, VkFormat format);
}

223
src/graphics/shader.cpp Normal file
View File

@@ -0,0 +1,223 @@
#include "shader.hpp"
#include "buffer.hpp"
#include "device.hpp"
#include "pipeline.hpp"
#include "swapchain.hpp"
#include "scene/scene.hpp"
#include <fstream>
#include <iostream>
#include "yolo/yolo.hpp"
namespace inferno::graphics {
std::string textFromFile(const std::filesystem::path& path)
{
std::ifstream input(path);
return std::string(
(std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
}
Shader* shader_create(
GraphicsDevice* device, SwapChain* swapchain, ShaderProgramType type)
{
Shader* shader = new Shader;
shader->Device = device;
shader->GraphicsSwapchain = swapchain;
shader->Type = type;
return shader;
}
void shader_cleanup(Shader* shader)
{
uniform_buffer_cleanup(shader->GlobalUniformBuffer);
pipeline_cleanup(shader->GraphicsPipeline);
vkDestroyDescriptorPool(
shader->Device->VulkanDevice, shader->DescriptorPool, nullptr);
vkDestroyDescriptorSetLayout(
shader->Device->VulkanDevice, shader->DescriptorLayout, nullptr);
vkDestroyShaderModule(shader->Device->VulkanDevice, shader->VertexShader, nullptr);
vkDestroyShaderModule(shader->Device->VulkanDevice, shader->FragmentShader, nullptr);
delete shader;
}
void shader_load(Shader* shader, std::filesystem::path path)
{
std::string shaderPath = path.string();
std::string vertexShaderPath = shaderPath + ".vert.spv";
std::string fragmentShaderPath = shaderPath + ".frag.spv";
assert(std::filesystem::exists(vertexShaderPath));
assert(std::filesystem::exists(fragmentShaderPath));
std::string vertexLoadedShaderCode = textFromFile(vertexShaderPath);
VkShaderModuleCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = vertexLoadedShaderCode.size();
createInfo.pCode = (uint32_t*)(vertexLoadedShaderCode.data());
if (vkCreateShaderModule(
shader->Device->VulkanDevice, &createInfo, nullptr, &shader->VertexShader)
!= VK_SUCCESS) {
yolo::error("failed to create shader module");
}
memset(&shader->ShaderStages, 0,
SHADER_STAGES * sizeof(VkPipelineShaderStageCreateInfo));
shader->ShaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader->ShaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader->ShaderStages[0].module = shader->VertexShader;
shader->ShaderStages[0].pName = "main";
shader->ShaderStages[0].pSpecializationInfo = nullptr; // Optional
shader->ShaderStages[0].flags = 0; // Optional
shader->ShaderStages[0].pNext = nullptr; // Optional
std::string fragmentLoadedShaderCode = textFromFile(fragmentShaderPath);
VkShaderModuleCreateInfo createInfo2 = {};
createInfo2.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo2.codeSize = fragmentLoadedShaderCode.size();
createInfo2.pCode = (uint32_t*)(fragmentLoadedShaderCode.data());
if (vkCreateShaderModule(
shader->Device->VulkanDevice, &createInfo2, nullptr, &shader->FragmentShader)
!= VK_SUCCESS) {
yolo::error("failed to create shader module");
}
shader->ShaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader->ShaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader->ShaderStages[1].module = shader->FragmentShader;
shader->ShaderStages[1].pName = "main";
shader->ShaderStages[0].pSpecializationInfo = nullptr; // Optional
shader->ShaderStages[0].flags = 0; // Optional
shader->ShaderStages[0].pNext = nullptr; // Optional
}
void shader_update_state(Shader* shader, VkCommandBuffer commandBuffer,
scene::GlobalUniformObject object, uint32_t frameIndex)
{
VkDescriptorSet descriptorSet = shader->Descriptors[frameIndex];
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
shader->GraphicsPipeline->Layout, 0, 1, &descriptorSet, 0, nullptr);
uniform_buffer_update(shader->GlobalUniformBuffer, &object);
VkDescriptorBufferInfo bufferInfo {};
bufferInfo.buffer = shader->GlobalUniformBuffer->Handle;
bufferInfo.offset = 0;
bufferInfo.range = sizeof(scene::GlobalUniformObject);
VkWriteDescriptorSet descriptorWrite {};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = descriptorSet;
descriptorWrite.dstBinding = 0;
descriptorWrite.dstArrayElement = 0;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pBufferInfo = &bufferInfo;
vkUpdateDescriptorSets(shader->Device->VulkanDevice, 1, &descriptorWrite, 0, nullptr);
}
// build the desriptor set layout and pipeline layout
void shader_build(Shader* shader)
{
VkDescriptorSetLayoutBinding uboLayoutBinding {};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
uboLayoutBinding.pImmutableSamplers = nullptr; // Optional
VkDescriptorSetLayoutCreateInfo layoutInfo {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = 1;
layoutInfo.pBindings = &uboLayoutBinding;
if (vkCreateDescriptorSetLayout(
shader->Device->VulkanDevice, &layoutInfo, nullptr, &shader->DescriptorLayout)
!= VK_SUCCESS) {
yolo::error("failed to create descriptor set layout!");
}
VkDescriptorPoolSize poolSize {};
poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSize.descriptorCount = static_cast<uint32_t>(FRAMES_IN_FLIGHT);
VkDescriptorPoolCreateInfo poolInfo {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = 1;
poolInfo.pPoolSizes = &poolSize;
poolInfo.maxSets = static_cast<uint32_t>(FRAMES_IN_FLIGHT);
if (vkCreateDescriptorPool(
shader->Device->VulkanDevice, &poolInfo, nullptr, &shader->DescriptorPool)
!= VK_SUCCESS) {
yolo::error("failed to create descriptor pool!");
}
std::vector<VkDescriptorSetLayout> layouts(
FRAMES_IN_FLIGHT, shader->DescriptorLayout);
VkDescriptorSetAllocateInfo allocInfo {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = shader->DescriptorPool;
allocInfo.descriptorSetCount = static_cast<uint32_t>(FRAMES_IN_FLIGHT);
allocInfo.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(
shader->Device->VulkanDevice, &allocInfo, shader->Descriptors)
!= VK_SUCCESS) {
yolo::error("failed to allocate descriptor sets!");
}
PipelineType pipelineType = PIPELINE_TYPE_GRAPHICS;
switch (shader->Type) {
case SHADER_PROGRAM_TYPE_GRAPHICS:
pipelineType = PIPELINE_TYPE_GRAPHICS;
break;
case SHADER_PROGRAM_TYPE_GRAPHICS_LINE:
pipelineType = PIPELINE_TYPE_GRAPHICS_LINE;
break;
default:
yolo::error("Shader type not supported");
break;
}
shader->GraphicsPipeline = pipeline_create(shader->Device, shader->GraphicsSwapchain,
shader, layouts.size(), layouts.data(), pipelineType);
shader->GlobalUniformBuffer
= uniform_buffer_create<scene::GlobalUniformObject>(shader->Device, true);
}
void shader_use(Shader* shader, VkCommandBuffer commandBuffer, VkRect2D renderArea)
{
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
shader->GraphicsPipeline->GraphicsPipeline);
VkViewport viewport {};
viewport.x = 0.0f;
// viewport.y = static_cast<float>(renderArea.extent.height);
viewport.y = 0.0f;
viewport.width = static_cast<float>(renderArea.extent.width);
viewport.height = static_cast<float>(renderArea.extent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor {};
scissor.offset = { 0, 0 };
scissor.extent = renderArea.extent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
}
void shader_unuse(Shader* shader, VkCommandBuffer commandBuffer)
{
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VK_NULL_HANDLE);
}
}

73
src/graphics/shader.hpp Normal file
View File

@@ -0,0 +1,73 @@
#pragma once
#include "../graphics.hpp"
#include <filesystem>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace inferno::scene {
struct GlobalUniformObject;
}
namespace inferno::graphics {
struct GraphicsDevice;
struct SwapChain;
struct Pipeline;
struct GenBuffer;
typedef enum ShaderProgramType {
SHADER_PROGRAM_TYPE_GRAPHICS,
SHADER_PROGRAM_TYPE_GRAPHICS_LINE,
SHADER_PROGRAM_TYPE_COMPUTE,
SHADER_PROGRAM_TYPE_RAYTRACING
} ShaderProgramType;
typedef struct ShaderPushConst {
union {
glm::mat4 mat[2]; // 64 bytes each
float data[16];
};
} ShaderPushConstants;
// TODO: Make general
typedef struct Shader {
GraphicsDevice* Device;
ShaderProgramType Type;
SwapChain* GraphicsSwapchain;
Pipeline* GraphicsPipeline;
VkDescriptorPool DescriptorPool;
VkDescriptorSet Descriptors[FRAMES_IN_FLIGHT];
VkDescriptorSetLayout DescriptorLayout;
GenBuffer* GlobalUniformBuffer;
VkShaderModule VertexShader;
VkShaderModule FragmentShader;
VkPipelineShaderStageCreateInfo ShaderStages[SHADER_STAGES];
VkPipelineVertexInputStateCreateInfo VertexInputInfo;
} Shader;
Shader* shader_create(
GraphicsDevice* device, SwapChain* swapchain, ShaderProgramType type);
void shader_cleanup(Shader* shader);
void shader_load(Shader* shader, std::filesystem::path path);
void shader_update_state(Shader* shader, VkCommandBuffer commandBuffer,
scene::GlobalUniformObject object, uint32_t frameIndex);
void shader_push_const(glm::mat4 mat);
// actually creates the pipeline
void shader_build(Shader* shader);
void shader_use(Shader* shader, VkCommandBuffer commandBuffer, VkRect2D renderArea);
void shader_unuse(Shader* shader, VkCommandBuffer commandBuffer);
}

208
src/graphics/swapchain.cpp Normal file
View File

@@ -0,0 +1,208 @@
#include "swapchain.hpp"
#include "graphics.hpp"
#include "device.hpp"
#include "image.hpp"
#include "yolo/yolo.hpp"
namespace inferno::graphics {
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR Capabilities;
std::vector<VkSurfaceFormatKHR> Formats;
std::vector<VkPresentModeKHR> PresentModes;
};
SwapChainSupportDetails device_get_swapchain_support(
GraphicsDevice* g, VkPhysicalDevice device)
{
SwapChainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
device, g->VulkanSurface, &details.Capabilities);
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, nullptr);
if (formatCount != 0) {
details.Formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(
device, g->VulkanSurface, &formatCount, details.Formats.data());
}
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(
device, g->VulkanSurface, &presentModeCount, nullptr);
if (presentModeCount != 0) {
details.PresentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(
device, g->VulkanSurface, &presentModeCount, details.PresentModes.data());
}
return details;
}
VkSurfaceFormatKHR device_choose_swap_surface_format(
const std::vector<VkSurfaceFormatKHR>& availableFormats)
{
for (const auto& availableFormat : availableFormats) {
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB
&& availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return availableFormat;
}
}
return availableFormats[0];
}
VkPresentModeKHR device_choose_swap_present_mode(
const std::vector<VkPresentModeKHR>& availablePresentModes)
{
for (const auto& availablePresentMode : availablePresentModes) {
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
return availablePresentMode;
}
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D device_choose_swap_extent(
const VkSurfaceCapabilitiesKHR& capabilities, int width, int height)
{
if (capabilities.currentExtent.width != UINT32_MAX) {
yolo::info("Surface size: {}x{}", capabilities.currentExtent.width,
capabilities.currentExtent.height);
return capabilities.currentExtent;
} else {
VkExtent2D actualExtent = { (uint32_t)width, (uint32_t)height };
yolo::info("Surface size: {}x{}", width, height);
actualExtent.width = std::max(capabilities.minImageExtent.width,
std::min(capabilities.maxImageExtent.width, actualExtent.width));
actualExtent.height = std::max(capabilities.minImageExtent.height,
std::min(capabilities.maxImageExtent.height, actualExtent.height));
return actualExtent;
}
}
SwapChain* swapchain_create(GraphicsDevice* device, glm::ivec2 surface_size)
{
SwapChain* swapchain = new SwapChain();
SwapChainSupportDetails swapChainSupport
= device_get_swapchain_support(device, device->VulkanPhysicalDevice);
VkSurfaceFormatKHR surfaceFormat
= device_choose_swap_surface_format(swapChainSupport.Formats);
VkPresentModeKHR presentMode
= device_choose_swap_present_mode(swapChainSupport.PresentModes);
yolo::debug("Surface format: {}", surfaceFormat.format);
VkExtent2D extent = device_choose_swap_extent(
swapChainSupport.Capabilities, surface_size.x, surface_size.y);
swapchain->Extent = extent;
uint32_t imageCount = swapChainSupport.Capabilities.minImageCount + 1;
if (swapChainSupport.Capabilities.maxImageCount > 0
&& imageCount > swapChainSupport.Capabilities.maxImageCount) {
imageCount = swapChainSupport.Capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = device->VulkanSurface;
createInfo.minImageCount = imageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageExtent = extent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
QueueFamilyIndices indices
= device_get_queue_families(device, device->VulkanPhysicalDevice);
uint32_t queueFamilyIndices[]
= { indices.graphicsFamily.value(), indices.presentFamily.value() };
if (indices.graphicsFamily != indices.presentFamily) {
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndices;
} else {
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0; // Optional
createInfo.pQueueFamilyIndices = nullptr; // Optional
}
createInfo.preTransform = swapChainSupport.Capabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = VK_NULL_HANDLE;
if (vkCreateSwapchainKHR(
device->VulkanDevice, &createInfo, nullptr, &swapchain->Handle)
!= VK_SUCCESS) {
yolo::error("failed to create swap chain!");
exit(1);
}
yolo::info("Swap chain created");
vkGetSwapchainImagesKHR(
device->VulkanDevice, swapchain->Handle, &imageCount, nullptr);
swapchain->Images.resize(imageCount);
vkGetSwapchainImagesKHR(
device->VulkanDevice, swapchain->Handle, &imageCount, swapchain->Images.data());
swapchain->ImageFormat = surfaceFormat.format;
swapchain->Device = device;
swapchain_image_view_create(swapchain);
return swapchain;
}
void swapchain_cleanup(SwapChain* swapchain)
{
for (auto imageView : swapchain->ImageViews) {
vkDestroyImageView(swapchain->Device->VulkanDevice, imageView, nullptr);
}
vkDestroySwapchainKHR(swapchain->Device->VulkanDevice, swapchain->Handle, nullptr);
delete swapchain;
}
void swapchain_image_view_create(SwapChain* swapchain)
{
swapchain->ImageViews.resize(swapchain->Images.size());
for (size_t i = 0; i < swapchain->Images.size(); i++) {
swapchain->ImageViews[i] = create_image_view(swapchain->Device,
swapchain->Images[i], swapchain->ImageFormat, VK_IMAGE_ASPECT_COLOR_BIT);
}
VkFormat depthFormat = find_depth_format(swapchain->Device);
swapchain->DepthFormat = depthFormat;
create_image(swapchain->Device, swapchain->Extent.width, swapchain->Extent.height,
depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, swapchain->depthImage,
swapchain->depthImageMemory);
swapchain->DepthImageView = create_image_view(
swapchain->Device, swapchain->depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
}
void swapchain_recreate(SwapChain* swapchain)
{
vkDeviceWaitIdle(swapchain->Device->VulkanDevice);
GraphicsDevice* device = swapchain->Device;
swapchain_cleanup(swapchain);
swapchain = swapchain_create(device, device->SurfaceSize);
}
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include "graphics.hpp"
namespace inferno::graphics {
struct GraphicsDevice;
struct RenderPass;
typedef struct SwapChain {
VkSwapchainKHR Handle;
VkFormat ImageFormat;
VkFormat DepthFormat;
VkExtent2D Extent;
std::vector<VkImage> Images;
std::vector<VkImageView> ImageViews;
VkImage depthImage;
VkDeviceMemory depthImageMemory;
VkImageView DepthImageView;
GraphicsDevice* Device;
} SwapChain;
SwapChain* swapchain_create(GraphicsDevice* device, glm::ivec2 surface_size);
void swapchain_cleanup(SwapChain* swapchain);
void swapchain_image_view_create(SwapChain* swapchain);
void swapchain_framebuffers_create(SwapChain* swapchain);
void swapchain_recreate(SwapChain* swapchain);
VkFormat find_depth_format(GraphicsDevice* device);
}

399
src/graphics/vkrenderer.cpp Normal file
View File

@@ -0,0 +1,399 @@
#include "vkrenderer.hpp"
#include "device.hpp"
#include "graphics.hpp"
#include "pipeline.hpp"
#include "rendertarget.hpp"
#include "swapchain.hpp"
#include "gui/gui.hpp"
#include "yolo/yolo.hpp"
#include <functional>
#include <vulkan/vulkan_core.h>
namespace inferno::graphics {
VulkanRenderer* renderer_create(GraphicsDevice* device)
{
auto renderer = new VulkanRenderer();
renderer->Device = device;
renderer->Swap = swapchain_create(device, device->SurfaceSize);
renderer->InFlight.resize(FRAMES_IN_FLIGHT);
// Creating the synchronization objects
VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; // Start in signaled state
for (size_t i = 0; i < FRAMES_IN_FLIGHT; i++) {
renderer->InFlight[i] = FrameInFlight {};
// Create the Uniform Buffer here
if (vkCreateSemaphore(device->VulkanDevice, &semaphoreInfo, nullptr,
&renderer->InFlight[i].ImageAvailable)
!= VK_SUCCESS
|| vkCreateSemaphore(device->VulkanDevice, &semaphoreInfo, nullptr,
&renderer->InFlight[i].RenderFinished)
!= VK_SUCCESS
|| vkCreateFence(device->VulkanDevice, &fenceInfo, nullptr,
&renderer->InFlight[i].Fence)
!= VK_SUCCESS) {
yolo::error("failed to create synchronization objects for a frame!");
}
}
renderer->CurrentFrameIndex = 0;
renderer->CurrentFrame = &renderer->InFlight[0];
return renderer;
}
void renderer_configure_gui(VulkanRenderer* renderer) { gui::imgui_init(renderer); }
void renderer_cleanup(VulkanRenderer* renderer)
{
vkDestroyCommandPool(
renderer->Device->VulkanDevice, renderer->Device->VulkanCommandPool, nullptr);
for (size_t i = 0; i < FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(renderer->Device->VulkanDevice,
renderer->InFlight[i].ImageAvailable, nullptr);
vkDestroySemaphore(renderer->Device->VulkanDevice,
renderer->InFlight[i].RenderFinished, nullptr);
vkDestroyFence(
renderer->Device->VulkanDevice, renderer->InFlight[i].Fence, nullptr);
}
}
void renderer_configure_command_buffer(VulkanRenderer* renderer)
{
renderer->CommandBuffersInFlight.resize(FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo allocInfo {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = renderer->Device->VulkanCommandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t)FRAMES_IN_FLIGHT;
if (vkAllocateCommandBuffers(renderer->Device->VulkanDevice, &allocInfo,
&renderer->CommandBuffersInFlight[0])
!= VK_SUCCESS) {
yolo::error("failed to allocate command buffers!");
}
yolo::debug("Command buffer created");
}
void renderer_record_command_buffer(VulkanRenderer* renderer, uint32_t imageIndex)
{
VkCommandBufferBeginInfo beginInfo {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0; // Optional
beginInfo.pInheritanceInfo = nullptr; // Optional
if (vkBeginCommandBuffer(
renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex], &beginInfo)
!= VK_SUCCESS) {
yolo::error("failed to begin recording command buffer!");
}
renderer->CommandBufferInFlight
= &renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex];
}
void renderer_submit_oneoff(VulkanRenderer* renderer,
std::function<void(VulkanRenderer*, VkCommandBuffer*)> callback, bool post)
{
if (post) {
renderer->SubmitQueueOneOffPostFrame.push_back(callback);
} else {
renderer->SubmitQueueOneOffPreFrame.push_back(callback);
}
}
void renderer_submit_repeat(VulkanRenderer* renderer,
std::function<void(VulkanRenderer*, VkCommandBuffer*)> callback, bool post)
{
if (post) {
renderer->SubmitQueuePostFrame.push_back(callback);
} else {
renderer->SubmitQueuePreFrame.push_back(callback);
}
}
void work_queue(VulkanRenderer* renderer,
std::vector<std::function<void(VulkanRenderer*, VkCommandBuffer*)>>* queue,
bool clear)
{
for (auto& callback : *queue) {
callback(renderer, renderer->CommandBufferInFlight);
}
if (clear) {
queue->clear();
}
}
void renderer_submit_now(VulkanRenderer* renderer,
std::function<void(VulkanRenderer*, VkCommandBuffer*)> callback)
{
VkCommandBufferAllocateInfo allocInfo {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = renderer->Device->VulkanCommandPool;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(renderer->Device->VulkanDevice, &allocInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
callback(renderer, &commandBuffer);
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(renderer->Device->VulkanGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(renderer->Device->VulkanGraphicsQueue);
vkFreeCommandBuffers(renderer->Device->VulkanDevice,
renderer->Device->VulkanCommandPool, 1, &commandBuffer);
}
bool renderer_begin_frame(VulkanRenderer* renderer)
{
vkWaitForFences(renderer->Device->VulkanDevice, 1, &renderer->CurrentFrame->Fence,
VK_TRUE, UINT64_MAX);
auto swapStatus = vkAcquireNextImageKHR(renderer->Device->VulkanDevice,
renderer->Swap->Handle, UINT64_MAX, renderer->CurrentFrame->ImageAvailable,
VK_NULL_HANDLE, &renderer->ImageIndex);
if (swapStatus == VK_ERROR_OUT_OF_DATE_KHR || renderer->Device->Resized) {
yolo::info("Swapchain out of date");
swapchain_recreate(renderer->Swap);
return false;
} else if (swapStatus != VK_SUCCESS) {
yolo::error("failed to acquire swap chain image!");
}
vkResetFences(renderer->Device->VulkanDevice, 1, &renderer->CurrentFrame->Fence);
vkResetCommandBuffer(
renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex], 0);
renderer_record_command_buffer(renderer, renderer->ImageIndex);
work_queue(renderer, &renderer->SubmitQueueOneOffPreFrame, true);
work_queue(renderer, &renderer->SubmitQueuePreFrame, false);
return true;
}
void renderer_begin_pass(
VulkanRenderer* renderer, RenderTarget* target, VkRect2D renderArea, bool clear)
{
VkImageMemoryBarrier imageMemoryBarrier {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.image = target->Image;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.levelCount = 1;
if (!clear) {
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
vkCmdPipelineBarrier(renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex],
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
VkClearValue clearColor = { { { 0.0f, 0.0f, 0.0f, 1.0f } } };
VkRenderingAttachmentInfo attachmentInfo {};
attachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
attachmentInfo.imageView = target->ImageView;
attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR;
attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachmentInfo.clearValue = clearColor;
if (!clear) {
attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
}
VkRenderingAttachmentInfo depthAttachmentInfo;
if (target->TargetDepth != nullptr) {
depthAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
depthAttachmentInfo.imageView = target->TargetDepth->ImageView;
depthAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR;
depthAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachmentInfo.clearValue.depthStencil = { 1.0f, 0 };
depthAttachmentInfo.resolveMode = VK_RESOLVE_MODE_NONE;
depthAttachmentInfo.resolveImageView = VK_NULL_HANDLE;
depthAttachmentInfo.resolveImageLayout = VK_IMAGE_LAYOUT_GENERAL;
depthAttachmentInfo.pNext = VK_NULL_HANDLE;
if (!clear) {
depthAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
}
}
VkRenderingInfo renderingInfo {};
renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR;
renderingInfo.renderArea = renderArea;
renderingInfo.layerCount = 1;
renderingInfo.colorAttachmentCount = 1;
renderingInfo.pColorAttachments = &attachmentInfo;
if (target->TargetDepth != nullptr)
renderingInfo.pDepthAttachment = &depthAttachmentInfo;
vkCmdBeginRendering(
renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex], &renderingInfo);
}
void renderer_begin_pass(VulkanRenderer* renderer, VkRect2D renderArea)
{
VkImageMemoryBarrier imageMemoryBarrier {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.image = renderer->Swap->Images[renderer->ImageIndex];
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier(renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex],
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
VkClearValue clearColor = { { { 0.0f, 0.0f, 0.0f, 1.0f } } };
VkRenderingAttachmentInfo attachmentInfo {};
attachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
attachmentInfo.imageView = renderer->Swap->ImageViews[renderer->ImageIndex];
attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR;
attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachmentInfo.clearValue = clearColor;
VkRenderingAttachmentInfo depthAttachmentInfo;
depthAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
depthAttachmentInfo.imageView = renderer->Swap->DepthImageView;
depthAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR;
depthAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachmentInfo.clearValue.depthStencil = { 1.0f, 0 };
depthAttachmentInfo.resolveMode = VK_RESOLVE_MODE_NONE;
depthAttachmentInfo.resolveImageView = VK_NULL_HANDLE;
depthAttachmentInfo.resolveImageLayout = VK_IMAGE_LAYOUT_GENERAL;
depthAttachmentInfo.pNext = VK_NULL_HANDLE;
VkRenderingInfo renderingInfo {};
renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR;
renderingInfo.renderArea = renderArea;
renderingInfo.layerCount = 1;
renderingInfo.colorAttachmentCount = 1;
renderingInfo.pColorAttachments = &attachmentInfo;
renderingInfo.pDepthAttachment = &depthAttachmentInfo;
vkCmdBeginRendering(
renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex], &renderingInfo);
}
void renderer_end_pass(VulkanRenderer* renderer)
{
vkCmdEndRendering(*renderer->CommandBufferInFlight);
VkImageMemoryBarrier imageMemoryBarrier {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
imageMemoryBarrier.image = renderer->Swap->Images[renderer->ImageIndex];
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier(renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex],
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1,
&imageMemoryBarrier);
}
bool renderer_draw_frame(VulkanRenderer* renderer)
{
work_queue(renderer, &renderer->SubmitQueueOneOffPostFrame, true);
work_queue(renderer, &renderer->SubmitQueuePostFrame, false);
if (vkEndCommandBuffer(renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex])
!= VK_SUCCESS) {
yolo::error("failed to record command buffer!");
}
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = { renderer->CurrentFrame->ImageAvailable };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask
= waitStages; // TODO: This is a bit of a hack, we should be waiting for the
// renderpass to finish, not the color attachment stage
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers
= &renderer->CommandBuffersInFlight[renderer->CurrentFrameIndex];
VkSemaphore signalSemaphores[] = { renderer->CurrentFrame->RenderFinished };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
if (vkQueueSubmit(renderer->Device->VulkanGraphicsQueue, 1, &submitInfo,
renderer->CurrentFrame->Fence)
!= VK_SUCCESS) {
yolo::error("failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { renderer->Swap->Handle };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &renderer->ImageIndex;
auto swapStatus
= vkQueuePresentKHR(renderer->Device->VulkanPresentQueue, &presentInfo);
if (swapStatus == VK_ERROR_OUT_OF_DATE_KHR || swapStatus == VK_SUBOPTIMAL_KHR
|| renderer->Device->Resized) {
yolo::info("Swapchain out of date");
swapchain_recreate(renderer->Swap);
} else if (swapStatus != VK_SUCCESS) {
yolo::error("failed to present swap chain image!");
}
renderer->CurrentFrameIndex = (renderer->CurrentFrameIndex + 1) % FRAMES_IN_FLIGHT;
renderer->CurrentFrame = &renderer->InFlight[renderer->CurrentFrameIndex];
return true;
}
}

View File

@@ -0,0 +1,79 @@
#pragma once
#include "graphics.hpp"
#include <functional>
#include <map>
#include <vector>
namespace inferno::graphics {
struct GraphicsDevice;
struct Pipeline;
struct SwapChain;
struct RenderTarget;
struct GenBuffer;
struct Shader;
// TODO: Make the inflight frames work better
typedef struct FrameInFlight {
VkSemaphore ImageAvailable;
VkSemaphore RenderFinished;
VkFence Fence;
// Global Uniform Buffer
// TODO: We also need push constants
GenBuffer* UniformBuffer;
} FrameInFlight;
typedef struct VulkanRenderer {
GraphicsDevice* Device;
SwapChain* Swap;
// TODO: This is really fucking annoying, how can we
// not do this? CommandBuffers need to be *sequential*
// in client memory
std::vector<VkCommandBuffer> CommandBuffersInFlight;
VkCommandBuffer* CommandBufferInFlight;
std::vector<FrameInFlight> InFlight;
FrameInFlight* CurrentFrame;
uint32_t CurrentFrameIndex;
uint32_t ImageIndex;
std::vector<std::function<void(VulkanRenderer*, VkCommandBuffer*)>>
SubmitQueueOneOffPreFrame;
std::vector<std::function<void(VulkanRenderer*, VkCommandBuffer*)>>
SubmitQueueOneOffPostFrame;
std::vector<std::function<void(VulkanRenderer*, VkCommandBuffer*)>>
SubmitQueuePreFrame;
std::vector<std::function<void(VulkanRenderer*, VkCommandBuffer*)>>
SubmitQueuePostFrame;
} VulkanRenderer;
VulkanRenderer* renderer_create(GraphicsDevice* device);
void renderer_cleanup(VulkanRenderer* renderer);
void renderer_configure_gui(VulkanRenderer* renderer);
void renderer_configure_command_buffer(VulkanRenderer* renderer);
void renderer_submit_oneoff(VulkanRenderer* renderer,
std::function<void(VulkanRenderer*, VkCommandBuffer*)> callback, bool post = false);
void renderer_submit_repeat(VulkanRenderer* renderer,
std::function<void(VulkanRenderer*, VkCommandBuffer*)> callback, bool post = false);
void renderer_submit_now(VulkanRenderer* renderer,
std::function<void(VulkanRenderer*, VkCommandBuffer*)> callback);
bool renderer_begin_frame(VulkanRenderer* renderer);
void renderer_begin_pass(VulkanRenderer* renderer, RenderTarget* target,
VkRect2D renderArea, bool clear = true);
// this is for rendering to the swapchain / present image
void renderer_begin_pass(VulkanRenderer* renderer, VkRect2D renderArea);
void renderer_end_pass(VulkanRenderer* renderer);
bool renderer_draw_frame(VulkanRenderer* renderer);
}

110
src/gui/gui.hpp Normal file
View File

@@ -0,0 +1,110 @@
#pragma once
#include "graphics.hpp"
#include "imgui/imgui.h"
#include "style.hpp"
#include "graphics/device.hpp"
#include "graphics/swapchain.hpp"
#include "graphics/vkrenderer.hpp"
#include "window.hpp"
#include "yolo/yolo.hpp"
namespace inferno::gui {
#define WINDOW_FLAGS \
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse \
| ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove \
| ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus \
| ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoBackground \
| ImGuiWindowFlags_NoDecoration | ImGuiMouseCursor_Arrow
inline void imgui_init(graphics::VulkanRenderer* renderer)
{
graphics::GraphicsDevice* device = renderer->Device;
VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } };
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
pool_info.maxSets = 1000;
pool_info.poolSizeCount = std::size(pool_sizes);
pool_info.pPoolSizes = pool_sizes;
VkDescriptorPool imguiPool;
vkCreateDescriptorPool(device->VulkanDevice, &pool_info, nullptr, &imguiPool);
// this initializes the core structures of imgui
ImGui::CreateContext();
auto& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
ImGui_ImplGlfw_InitForVulkan(graphics::window_get_glfw_window(), true);
// this initializes imgui for Vulkan
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = device->VulkanInstance;
init_info.PhysicalDevice = device->VulkanPhysicalDevice;
init_info.Device = device->VulkanDevice;
init_info.Queue = device->VulkanGraphicsQueue;
init_info.DescriptorPool = imguiPool;
init_info.MinImageCount = 3;
init_info.ImageCount = 3;
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
init_info.UseDynamicRendering = true;
init_info.ColorAttachmentFormat = renderer->Swap->ImageFormat;
ImGui_ImplVulkan_Init(&init_info, VK_NULL_HANDLE);
SetupImGuiStyle2();
yolo::info("Initialized ImGUI");
}
inline void imgui_new_frame()
{
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGui::Begin("main", nullptr, WINDOW_FLAGS);
ImGui::SetWindowPos({ 0, 0 });
ImGui::SetWindowSize(
{ graphics::window_get_size().x, graphics::window_get_size().y });
ImGuiID dockspace_id = ImGui::GetID("main");
static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode;
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
}
inline void imgui_render_frame(VkCommandBuffer command_buffer)
{
ImGui::End();
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
ImGui_ImplVulkan_RenderDrawData(draw_data, command_buffer);
}
inline void imgui_shutdown()
{
ImGui_ImplVulkan_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
}
}

View File

@@ -2,7 +2,7 @@
#include <graphics.hpp>
namespace inferno {
namespace inferno::gui {
inline void SetupImGuiStyle2()
{

View File

@@ -1,25 +1,32 @@
#include "inferno.hpp"
#include <cstdint>
#include <graphics.hpp>
#include <version.hpp>
// #include "gui/layout.hpp"
#include "imgui/imgui.h"
#include "renderer/renderer.hpp"
#include "scene/scene.hpp"
#include "graphics/rendertarget.hpp"
#include "gui/gui.hpp"
// #include "renderer/renderer.hpp"
// #include "scene/scene.hpp"
#include "graphics/buffer.hpp"
#include "graphics/device.hpp"
#include "graphics/pipeline.hpp"
#include "graphics/shader.hpp"
#include "graphics/swapchain.hpp"
#include "graphics/vkrenderer.hpp"
#include "preview_renderer/debug.hpp"
#include "window.hpp"
#include "preview_renderer/debug.hpp"
#include "preview_renderer/renderer.hpp"
#include "preview_renderer/shader.hpp"
// #include "preview_renderer/debug.hpp"
// #include "preview_renderer/renderer.hpp"
#include "scene/camera.hpp"
#include "scene/material.hpp"
// #include "scene/material.hpp"
#include "scene/mesh.hpp"
#include "scene/scene.hpp"
#include <yolo/yolo.hpp>
#include <chrono>
#include <iostream>
#include <memory>
#include <numeric>
namespace inferno {
@@ -33,16 +40,15 @@ InfernoTimer* inferno_timer_create()
return timer;
}
void inferno_timer_cleanup(InfernoTimer* timer)
{
delete timer;
}
void inferno_timer_cleanup(InfernoTimer* timer) { delete timer; }
void inferno_timer_rolling_average(InfernoTimer* timer, int count)
{
if (timer->Times.size() > count)
timer->Times.erase(timer->Times.begin());
timer->RollingAverage = std::accumulate(timer->Times.begin(), timer->Times.end(), std::chrono::duration<double>(0.0)) / count;
timer->RollingAverage = std::accumulate(timer->Times.begin(), timer->Times.end(),
std::chrono::duration<double>(0.0))
/ count;
}
void inferno_timer_start(InfernoTimer* timer)
@@ -55,7 +61,7 @@ void inferno_timer_end(InfernoTimer* timer)
timer->EndTime = std::chrono::high_resolution_clock::now();
timer->Time = timer->EndTime - timer->StartTime;
timer->Times.push_back(timer->Time);
inferno_timer_rolling_average(timer, 100);
inferno_timer_rolling_average(timer, 1000);
}
void inferno_timer_print(InfernoTimer* timer, bool time)
@@ -88,35 +94,51 @@ InfernoApp* inferno_create()
graphics::camera_set_position(app->Scene->Camera, { 0.0f, 1.0f, 3.1f });
// Create window
graphics::window_create("Inferno v" INFERNO_VERSION, 1280, 720);
graphics::window_create("Inferno v" INFERNO_VERSION, 1920, 1080);
app->Device = graphics::device_create();
app->Renderer = graphics::renderer_create(app->Device);
// setup the scene
scene::Material* basicMaterial = new scene::Material("basic");
graphics::Shader* basicShader = graphics::shader_create();
graphics::shader_load(basicShader, "res/shaders/basic.glsl");
graphics::shader_link(basicShader);
basicMaterial->setGlShader(basicShader);
graphics::renderer_configure_gui(app->Renderer);
graphics::renderer_configure_command_buffer(app->Renderer);
scene::Mesh* mesh = new scene::Mesh;
mesh->loadOBJ("res/cornell.obj");
mesh->ready();
mesh->setMaterial(basicMaterial);
app->PreviewRenderer = graphics::preview_create(app->Renderer);
graphics::debug_do_depth_test(false);
graphics::renderer_submit_repeat(
app->Renderer,
[](graphics::VulkanRenderer* renderer, VkCommandBuffer* commandBuffer) {
gui::imgui_new_frame();
},
false);
graphics::renderer_submit_repeat(
app->Renderer,
[&](graphics::VulkanRenderer* renderer, VkCommandBuffer* commandBuffer) {
graphics::renderer_begin_pass(renderer,
{ 0, 0, (uint32_t)graphics::window_get_size().x,
(uint32_t)graphics::window_get_size().y });
gui::imgui_render_frame(*commandBuffer);
graphics::renderer_end_pass(renderer);
},
true);
scene::Mesh* mesh = scene::mesh_create(app->Device);
scene::mesh_load_obj(mesh, "res/cornell-box.obj");
scene::mesh_ready(mesh);
scene::SceneObject* object = scene::scene_object_create();
scene::scene_object_add_mesh(object, mesh);
scene::scene_add_object(app->Scene, object);
// scene::Mesh* box = new scene::Mesh;
// box->loadOBJ("res/cornell.obj");
// box->ready();
// box->setMaterial(basicMaterial);
// scene::SceneObject* box_object = scene::scene_object_create();
// scene::scene_object_add_mesh(box_object, box);
// scene::scene_add_object(app->Scene, box_object);
scene::Mesh* lucy = scene::mesh_create(app->Device);
scene::mesh_load_obj(lucy, "res/lucy.obj");
scene::mesh_ready(lucy);
scene::SceneObject* lucyObject = scene::scene_object_create();
scene::scene_object_add_mesh(lucyObject, lucy);
scene::scene_add_object(app->Scene, lucyObject);
app->PreviewRenderer = graphics::preview_create();
graphics::preview_set_viewport(app->PreviewRenderer, app->Scene->Camera);
app->RayRenderer = graphics::rayr_create(app->Scene);
graphics::rayr_set_viewport(app->RayRenderer, app->Scene->Camera);
// app->PreviewRenderer = graphics::preview_create();
// graphics::preview_set_viewport(app->PreviewRenderer, app->Scene->Camera);
// app->RayRenderer = graphics::rayr_create(app->Scene);
// graphics::rayr_set_viewport(app->RayRenderer, app->Scene->Camera);
return app;
}
@@ -142,13 +164,13 @@ static void inferno_gui_help_marker(const char* desc)
void inferno_preset_gui(InfernoApp* app)
{
ImGuiID dockspace_id = ImGui::GetID("main");
ImGui::DockBuilderRemoveNode(dockspace_id); // Clear out existing layout
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace); // Add empty node
ImGui::DockBuilderSetNodeSize(dockspace_id, { 1000, 1000 });
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace); // Add empty
// node ImGui::DockBuilderSetNodeSize(dockspace_id, { 1000, 1000 });
ImGuiID dock_main_id = dockspace_id;
ImGuiID dock_left = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.5f, NULL, &dock_main_id);
ImGuiID dock_left = ImGui::DockBuilderSplitNode(
dock_main_id, ImGuiDir_Left, 0.5f, NULL, &dock_main_id);
ImGui::DockBuilderDockWindow("Preview", dock_left);
ImGui::DockBuilderDockWindow("Render", dock_main_id);
ImGui::DockBuilderFinish(dockspace_id);
@@ -165,13 +187,16 @@ void inferno_move_input(InfernoApp* app, std::chrono::duration<double> deltaTime
// pan only get on hold
static glm::dvec2 lastMousePos;
static int firstClick = 0;
if (glfwGetMouseButton(graphics::window_get_glfw_window(), GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) {
if (glfwGetMouseButton(graphics::window_get_glfw_window(), GLFW_MOUSE_BUTTON_1)
== GLFW_PRESS) {
firstClick++;
if (firstClick == 1) {
glfwGetCursorPos(graphics::window_get_glfw_window(), &lastMousePos.x, &lastMousePos.y);
glfwGetCursorPos(
graphics::window_get_glfw_window(), &lastMousePos.x, &lastMousePos.y);
}
glm::dvec2 tempMousePos = { 0.0f, 0.0f };
glfwGetCursorPos(graphics::window_get_glfw_window(), &tempMousePos.x, &tempMousePos.y);
glfwGetCursorPos(
graphics::window_get_glfw_window(), &tempMousePos.x, &tempMousePos.y);
app->Input->MouseDelta = lastMousePos - tempMousePos;
lastMousePos = tempMousePos;
} else {
@@ -203,57 +228,40 @@ void inferno_stop_move_input(InfernoApp* app)
bool inferno_pre(InfernoApp* app)
{
inferno_timer_start(app->MainTimer);
app->FrameCount++;
if (app->FrameCount % 100 == 0) {
yolo::info("Average FPS: {}", 1.0 / inferno_timer_get_time(app->MainTimer).count());
yolo::info(
"Average FPS: {}", 1.0 / inferno_timer_get_time(app->MainTimer).count());
inferno_timer_print(app->MainTimer, false);
}
if (!graphics::window_new_frame())
if (!graphics::renderer_begin_frame(app->Renderer))
return false;
// set the main window to the dockspace and then on the first launch set the preset
ImGuiID dockspace_id = ImGui::GetID("main");
static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode;
if (ImGui::DockBuilderGetNode(dockspace_id) == NULL) {
inferno_preset_gui(app);
}
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
return true;
}
void inferno_end(InfernoApp* app)
{
// clang-format off
GLenum err;
while((err = glGetError()) != GL_NO_ERROR) {
std::string error;
switch (err) {
case GL_INVALID_ENUM: error = "INVALID_ENUM"; break;
case GL_INVALID_VALUE: error = "INVALID_VALUE"; break;
case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break;
case GL_STACK_OVERFLOW: error = "STACK_OVERFLOW"; break;
case GL_STACK_UNDERFLOW: error = "STACK_UNDERFLOW"; break;
case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break;
default: error = std::to_string((uint32_t)err); break;
}
yolo::error("[GL]: {} {}", err, error);
}
graphics::renderer_draw_frame(app->Renderer);
graphics::window_render();
inferno_timer_end(app->MainTimer);
}
int inferno_run(InfernoApp* app)
{
while (true) {
inferno_timer_start(app->MainTimer);
while (graphics::window_new_frame()) {
if (!inferno_pre(app))
break;
continue;
if (glm::length(app->Input->MouseDelta) > 0.0f)
graphics::camera_mouse_move(app->Scene->Camera, app->Input->MouseDelta);
graphics::camera_mouse_move(app->Scene->Camera, app->Input->MouseDelta,
inferno_timer_get_time(app->MainTimer).count());
if (app->Input->MovementDelta != 0b00000000)
graphics::camera_move(app->Scene->Camera, app->Input->MovementDelta);
graphics::camera_move(app->Scene->Camera, app->Input->MovementDelta,
inferno_timer_get_time(app->MainTimer).count());
// Menu Bar
static bool showPreview = true;
@@ -279,8 +287,7 @@ int inferno_run(InfernoApp* app)
if (showRenderSettings && ImGui::Begin("Inferno HART")) {
if (ImGui::TreeNode("Camera")) {
graphics::Camera* camera = scene::scene_get_camera(app->Scene);
graphics::camera_draw_ui(camera);
graphics::camera_draw_ui(scene::scene_get_camera(app->Scene));
ImGui::TreePop();
}
if (ImGui::TreeNode("Preview Render")) {
@@ -293,43 +300,51 @@ int inferno_run(InfernoApp* app)
ImGui::TreePop();
}
if (ImGui::TreeNode("RayTraced Render")) {
graphics::rayr_draw_ui(app->RayRenderer);
// graphics::rayr_draw_ui(app->RayRenderer);
ImGui::TreePop();
}
ImGui::End();
}
if (showPreview && ImGui::Begin("Preview", nullptr, ImGuiWindowFlags_NoScrollbar)) {
if (showPreview
&& ImGui::Begin("Preview", nullptr, ImGuiWindowFlags_NoScrollbar)) {
if (ImGui::IsWindowHovered()) {
inferno_move_input(app, inferno_timer_get_time(app->MainTimer));
} else {
inferno_stop_move_input(app);
}
graphics::camera_raster_set_viewport(scene::scene_get_camera(app->Scene),
{ ImGui::GetWindowSize().x, ImGui::GetWindowSize().y });
graphics::preview_set_viewport(app->PreviewRenderer, app->Scene->Camera);
static ImVec2 lastViewport = { 0, 0 };
ImVec2 currentViewport = ImGui::GetWindowSize();
if (lastViewport.x != currentViewport.x
|| lastViewport.y != currentViewport.y) {
graphics::camera_raster_set_viewport(scene::scene_get_camera(app->Scene),
{ ImGui::GetWindowSize().x, ImGui::GetWindowSize().y });
// if imgui has changed the viewport, we need to recreate the rendertarget
graphics::preview_set_viewport(app->PreviewRenderer, app->Scene->Camera);
}
lastViewport = currentViewport;
graphics::preview_draw(app->PreviewRenderer, app->Scene);
graphics::debug_draw_to_target(app->Scene);
graphics::debug_draw_line({ 0, 0, 0 }, { 1, 1, 0 }, { 1, 1, 0 });
graphics::debug_draw_to_preview(app->Scene);
ImTextureID texture = (ImTextureID)graphics::preview_get_rendered_texture(app->PreviewRenderer);
ImGui::Image(
texture,
{ ImGui::GetWindowSize().x, ImGui::GetWindowSize().y },
ImTextureID texture
= (ImTextureID)graphics::preview_get_target(app->PreviewRenderer)
->DescriptorSet;
ImGui::Image(texture, { ImGui::GetWindowSize().x, ImGui::GetWindowSize().y },
ImVec2(0, 1), ImVec2(1, 0));
ImGui::End();
}
if (ImGui::Begin("Render")) {
graphics::rayr_draw(app->RayRenderer);
// graphics::rayr_draw(app->RayRenderer);
ImTextureID texture = (ImTextureID)graphics::rayr_get_rendered_texture(app->RayRenderer);
ImGui::Image(
texture,
{ ImGui::GetWindowSize().x, ImGui::GetWindowSize().y },
ImVec2(0, 1), ImVec2(1, 0));
// ImTextureID texture
// = (ImTextureID)graphics::rayr_get_rendered_texture(app->RayRenderer);
// ImGui::Image(texture, { ImGui::GetWindowSize().x, ImGui::GetWindowSize().y
// },
// ImVec2(0, 1), ImVec2(1, 0));
ImGui::End();
}
@@ -337,11 +352,8 @@ int inferno_run(InfernoApp* app)
ImGui::ShowDemoWindow();
}
graphics::window_render();
inferno_end(app);
inferno_timer_end(app->MainTimer);
}
return 1;
}

View File

@@ -1,10 +1,12 @@
#pragma once
#include "graphics.hpp"
#include "graphics/device.hpp"
#include "scene/mesh.hpp"
#include "scene/scene.hpp"
#include "scene/camera.hpp"
#include "renderer/renderer.hpp"
// #include "scene/camera.hpp"
#include "preview_renderer/renderer.hpp"
#include "renderer/renderer.hpp"
#include <memory>
@@ -12,10 +14,16 @@ namespace inferno {
namespace graphics {
struct Camera;
struct GraphicsDevice;
struct VulkanRenderer;
struct RenderTarget;
struct Buffer;
struct Shader;
}
namespace scene {
struct Scene;
struct Vert;
}
typedef struct InfernoTimer {
@@ -40,13 +48,17 @@ std::chrono::duration<double> inferno_timer_get_time(InfernoTimer* timer);
std::chrono::duration<double> inferno_timer_get_average(InfernoTimer* timer);
typedef struct InfernoInput {
glm::vec2 MouseDelta = {0.0f, 0.0f};
glm::vec2 MouseDelta = { 0.0f, 0.0f };
uint8_t MovementDelta = 0;
} InfernoInput;
typedef struct InfernoApp {
InfernoInput* Input;
scene::Scene* Scene;
graphics::GraphicsDevice* Device;
graphics::VulkanRenderer* Renderer;
graphics::PreviewRenderer* PreviewRenderer;
graphics::RayRenderer* RayRenderer;
@@ -62,6 +74,7 @@ void inferno_stop_move_input(InfernoApp* app);
bool inferno_pre(InfernoApp* app);
void inferno_end(InfernoApp* app);
int inferno_run(InfernoApp* app);
} // namespace inferno

View File

@@ -1,8 +1,14 @@
#include "debug.hpp"
#include "graphics/buffer.hpp"
#include "graphics/rendertarget.hpp"
#include "graphics/shader.hpp"
#include "graphics/vkrenderer.hpp"
#include "preview_renderer/renderer.hpp"
#include "preview_renderer/shader.hpp"
#include "scene/camera.hpp"
#include "scene/mesh.hpp"
#include "scene/scene.hpp"
#include <graphics.hpp>
@@ -25,42 +31,35 @@ struct DebugTextBillboard {
struct _DebugInternal {
std::mutex DebugMutex;
GLuint VAO;
GLuint VBO;
Buffer* LineBuffer = nullptr;
Shader* LineShader;
};
static DebugDrawer* DebugDrawerInstance = nullptr;
void debug_init()
void debug_init(PreviewRenderer* renderer)
{
DebugDrawerInstance = new DebugDrawer;
DebugDrawerInstance->Renderer = renderer;
DebugDrawerInstance->LineElements = std::vector<DebugLine>();
DebugDrawerInstance->BillboardElements = std::vector<DebugTextBillboard>();
// DebugDrawerInstance->BillboardElements = std::vector<DebugTextBillboard>();
DebugDrawerInstance->_Internal = new _DebugInternal;
DebugDrawerInstance->_Internal->LineShader = shader_create();
shader_load(DebugDrawerInstance->_Internal->LineShader, "res/shaders/lines_debug.glsl");
shader_link(DebugDrawerInstance->_Internal->LineShader);
glGenVertexArrays(1, &DebugDrawerInstance->_Internal->VAO);
glBindVertexArray(DebugDrawerInstance->_Internal->VAO);
glGenBuffers(1, &DebugDrawerInstance->_Internal->VBO);
glBindBuffer(GL_ARRAY_BUFFER, DebugDrawerInstance->_Internal->VBO);
DebugDrawerInstance->_Internal->LineShader = shader_create(renderer->Renderer->Device,
renderer->Renderer->Swap, SHADER_PROGRAM_TYPE_GRAPHICS_LINE);
graphics::shader_load(
DebugDrawerInstance->_Internal->LineShader, "res/shaders/lines_debug");
graphics::shader_build(DebugDrawerInstance->_Internal->LineShader);
yolo::debug("DebugDrawer initialized");
}
void debug_cleanup()
{
delete DebugDrawerInstance;
}
void debug_cleanup() { delete DebugDrawerInstance; }
void debug_attach_renderer(PreviewRenderer* renderer)
void debug_do_depth_test(bool doDepthTest)
{
DebugDrawerInstance->Renderer = renderer;
DebugDrawerInstance->DoDepthTest = doDepthTest;
}
void debug_draw_line(glm::vec3 start, glm::vec3 end, glm::vec3 color)
@@ -69,85 +68,63 @@ void debug_draw_line(glm::vec3 start, glm::vec3 end, glm::vec3 color)
DebugDrawerInstance->LineElements.push_back({ start, end, color });
}
void debug_draw_text_billboard(glm::vec3 position, glm::vec3 color, std::string text)
{
std::lock_guard<std::mutex> lock(DebugDrawerInstance->_Internal->DebugMutex);
DebugDrawerInstance->BillboardElements.push_back({ position, color, text });
}
void debug_draw_ui() { ImGui::Checkbox("Show Overlay", &DebugDrawerInstance->DoShow); }
void debug_draw_ui()
{
ImGui::Checkbox("Show Overlay", &DebugDrawerInstance->DoShow);
}
void debug_draw_to_target(scene::Scene* scene)
void debug_draw_to_preview(scene::Scene* scene)
{
if (!DebugDrawerInstance->DoShow)
return;
auto renderer = DebugDrawerInstance->Renderer;
uint32_t bufferSize
= DebugDrawerInstance->LineElements.size() * sizeof(DebugLine) * 2;
scene::DebugLineVert* bufferData = new scene::DebugLineVert[bufferSize];
glBindFramebuffer(GL_FRAMEBUFFER, renderer->RenderTarget);
glDisable(GL_DEPTH_TEST);
glBindBuffer(GL_ARRAY_BUFFER, DebugDrawerInstance->_Internal->VBO);
glBindVertexArray(DebugDrawerInstance->_Internal->VAO);
GLuint vertex_position_arr_size = DebugDrawerInstance->LineElements.size() * 2 * 3;
GLfloat vertex_position_arr[vertex_position_arr_size];
for (int i = 0; i < DebugDrawerInstance->LineElements.size(); i++) {
auto line = DebugDrawerInstance->LineElements[i];
vertex_position_arr[i * 6 + 0] = line.Start.x;
vertex_position_arr[i * 6 + 1] = line.Start.y;
vertex_position_arr[i * 6 + 2] = line.Start.z;
vertex_position_arr[i * 6 + 3] = line.End.x;
vertex_position_arr[i * 6 + 4] = line.End.y;
vertex_position_arr[i * 6 + 5] = line.End.z;
bufferData[i * 2] = { DebugDrawerInstance->LineElements[i].Start,
DebugDrawerInstance->LineElements[i].Color };
bufferData[i * 2 + 1] = { DebugDrawerInstance->LineElements[i].End,
DebugDrawerInstance->LineElements[i].Color };
}
glBufferData(GL_ARRAY_BUFFER, vertex_position_arr_size * sizeof(GLfloat), vertex_position_arr, GL_STATIC_DRAW);
if (bufferSize > 0 && DebugDrawerInstance->_Internal->LineBuffer == nullptr) {
DebugDrawerInstance->_Internal->LineBuffer = vertex_buffer_create(
DebugDrawerInstance->Renderer->Renderer->Device, bufferData, bufferSize);
}
Shader* shader = DebugDrawerInstance->_Internal->LineShader;
shader_use(shader);
auto backend = DebugDrawerInstance->Renderer->Renderer;
VkCommandBuffer commandBuffer = *backend->CommandBufferInFlight;
GLint posAttrib = glGetAttribLocation(graphics::shader_get_program(shader), "position");
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(posAttrib);
graphics::renderer_begin_pass(backend,
DebugDrawerInstance->Renderer->PreviewRenderTarget,
DebugDrawerInstance->Renderer->Viewport, false);
auto viewMatrix = graphics::camera_get_view(scene::scene_get_camera(scene));
auto projMatrix = graphics::camera_get_projection(scene::scene_get_camera(scene));
graphics::shader_use(DebugDrawerInstance->_Internal->LineShader, commandBuffer,
DebugDrawerInstance->Renderer->Viewport);
GLint uniTrans = glGetUniformLocation(graphics::shader_get_program(shader), "model");
glUniformMatrix4fv(uniTrans, 1, GL_FALSE, glm::value_ptr(glm::mat4(1.0f)));
scene::GlobalUniformObject GlobalUniformObject {
.Projection = graphics::camera_get_projection(scene->Camera),
.View = graphics::camera_get_view(scene->Camera),
};
GLint uniView = glGetUniformLocation(graphics::shader_get_program(shader), "view");
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(viewMatrix));
graphics::shader_update_state(DebugDrawerInstance->_Internal->LineShader,
commandBuffer, GlobalUniformObject, backend->CurrentFrameIndex);
GLint uniProj = glGetUniformLocation(graphics::shader_get_program(shader), "proj");
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(projMatrix));
graphics::vertex_buffer_bind(
DebugDrawerInstance->_Internal->LineBuffer, commandBuffer);
glDrawArrays(GL_LINES, 0, vertex_position_arr_size);
if (DebugDrawerInstance->DoDepthTest)
vkCmdSetDepthTestEnable(commandBuffer, VK_TRUE);
else
vkCmdSetDepthTestEnable(commandBuffer, VK_FALSE);
vkCmdDraw(commandBuffer, DebugDrawerInstance->LineElements.size() * 2, 1, 0, 0);
vkCmdSetDepthTestEnable(commandBuffer, VK_TRUE);
graphics::renderer_end_pass(backend);
DebugDrawerInstance->LineElements.clear();
// glEnable(GL_TEXTURE_2D);
// glEnable(GL_BLEND);
// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//
// for (auto& billboard : DebugDrawerInstance->BillboardElements) {
// glColor3f(billboard.Color.x, billboard.Color.y, billboard.Color.z);
// glRasterPos3f(billboard.Position.x, billboard.Position.y, billboard.Position.z);
//
// for (auto& c : billboard.Text) {
// glutBitmapCharacter(GL_BITMAP, c);
// }
// }
DebugDrawerInstance->BillboardElements.clear();
glEnable(GL_DEPTH_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
delete[] bufferData;
}
}

View File

@@ -21,24 +21,25 @@ struct DebugTextBillboard;
struct _DebugInternal;
struct DebugDrawer {
PreviewRenderer* Renderer = nullptr;
_DebugInternal* _Internal = nullptr;
bool DoShow = true;
PreviewRenderer* Renderer = nullptr;
bool DoDepthTest = true;
std::vector<DebugLine> LineElements;
std::vector<DebugTextBillboard> BillboardElements;
// std::vector<DebugTextBillboard> BillboardElements;
};
void debug_init();
void debug_init(PreviewRenderer* renderer);
void debug_cleanup();
void debug_attach_renderer(PreviewRenderer* renderer);
void debug_do_depth_test(bool doDepthTest);
void debug_draw_line(glm::vec3 start, glm::vec3 end, glm::vec3 color);
void debug_draw_text_billboard(glm::vec3 position, glm::vec3 color, std::string text);
// void debug_draw_text_billboard(glm::vec3 position, glm::vec3 color, std::string text);
void debug_draw_ui();
void debug_draw_to_target(scene::Scene* scene);
void debug_draw_to_preview(scene::Scene* scene);
}

View File

@@ -3,14 +3,20 @@
#include <graphics.hpp>
#include "preview_renderer/debug.hpp"
#include "graphics/buffer.hpp"
#include "graphics/rendertarget.hpp"
#include "graphics/shader.hpp"
#include "graphics/vkrenderer.hpp"
#include "scene/mesh.hpp"
#include "scene/object.hpp"
#include "shader.hpp"
#include "scene/scene.hpp"
#include <yolo/yolo.hpp>
#include <algorithm>
#include <scene/camera.hpp>
#include <scene/material.hpp>
#include <scene/mesh.hpp>
#include <scene/object.hpp>
#include <scene/scene.hpp>
@@ -19,132 +25,79 @@
namespace inferno::graphics {
PreviewRenderer* preview_create()
PreviewRenderer* preview_create(VulkanRenderer* vkrenderer)
{
PreviewRenderer* renderer = new PreviewRenderer;
renderer->Renderer = vkrenderer;
glGenFramebuffers(1, &renderer->RenderTarget);
glBindFramebuffer(GL_FRAMEBUFFER, renderer->RenderTarget);
renderer->DrawShader = graphics::shader_create(
vkrenderer->Device, vkrenderer->Swap, SHADER_PROGRAM_TYPE_GRAPHICS);
glGenTextures(1, &renderer->RenderTargetTexture);
glBindTexture(GL_TEXTURE_2D, renderer->RenderTargetTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
graphics::shader_load(renderer->DrawShader, "res/shaders/basic");
graphics::shader_build(renderer->DrawShader);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenTextures(1, &renderer->RenderTargetDepthTexture);
glBindTexture(GL_TEXTURE_2D, renderer->RenderTargetDepthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 800, 600, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Attach the texture to the framebuffer.
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, renderer->RenderTargetDepthTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderer->RenderTargetTexture, 0);
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
renderer->PreviewRenderTarget = graphics::rendertarget_create(
renderer->Renderer->Device, { 1920, 1080 }, VK_FORMAT_R8G8B8A8_UNORM, true);
// bind preview renderer to debugdraw
debug_init();
debug_attach_renderer(renderer);
debug_init(renderer);
return renderer;
}
void preview_cleanup(PreviewRenderer* renderer)
{
}
void preview_cleanup(PreviewRenderer* renderer) { }
void preview_draw_ui(PreviewRenderer* renderer)
{
}
void preview_draw_ui(PreviewRenderer* renderer) { }
void preview_set_viewport(PreviewRenderer* renderer, Camera* camera)
{
auto viewport = camera_raster_get_viewport(camera);
renderer->Viewport = viewport;
glBindFramebuffer(GL_FRAMEBUFFER, renderer->RenderTarget);
glBindTexture(GL_TEXTURE_2D, renderer->RenderTargetTexture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGB,
renderer->Viewport.x,
renderer->Viewport.y,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
NULL);
glBindTexture(GL_TEXTURE_2D, renderer->RenderTargetDepthTexture);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_DEPTH24_STENCIL8,
renderer->Viewport.x,
renderer->Viewport.y,
0,
GL_DEPTH_COMPONENT,
GL_FLOAT,
NULL);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
renderer->Viewport.offset.x = 0;
renderer->Viewport.offset.y = 0;
renderer->Viewport.extent.width = viewport.x;
renderer->Viewport.extent.height = viewport.y;
renderer->HasViewportChanged = true;
}
GLuint preview_get_rendered_texture(PreviewRenderer* renderer)
RenderTarget* preview_get_target(PreviewRenderer* renderer)
{
glBindFramebuffer(GL_FRAMEBUFFER, renderer->RenderTarget);
return renderer->RenderTargetTexture;
return renderer->PreviewRenderTarget;
}
void preview_draw(PreviewRenderer* renderer, scene::Scene* scene)
{
// clear
glBindFramebuffer(GL_FRAMEBUFFER, renderer->RenderTarget);
glClearColor(0.1, 0.1, 0.1, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
VkCommandBuffer commandBuffer = *renderer->Renderer->CommandBufferInFlight;
// if changed
if (renderer->HasViewportChanged) {
graphics::rendertarget_recreate(renderer->PreviewRenderTarget,
renderer->Viewport.extent, VK_FORMAT_R8G8B8A8_UNORM);
}
// draw
glBindFramebuffer(GL_FRAMEBUFFER, renderer->RenderTarget);
glViewport(0,
0,
renderer->Viewport.x,
renderer->Viewport.y);
graphics::renderer_begin_pass(
renderer->Renderer, renderer->PreviewRenderTarget, renderer->Viewport);
glEnable(GL_DEPTH_TEST);
graphics::shader_use(renderer->DrawShader, commandBuffer, renderer->Viewport);
scene::GlobalUniformObject globalUniformObject {
.Projection = graphics::camera_get_projection(scene->Camera),
.View = graphics::camera_get_view(scene->Camera),
};
graphics::shader_update_state(renderer->DrawShader, commandBuffer,
globalUniformObject, renderer->Renderer->CurrentFrameIndex);
for (scene::SceneObject* o : scene::scene_get_renderables(scene)) {
for (scene::Mesh* m : scene::scene_object_get_meshs(o)) {
graphics::Shader* shader = m->getMaterial()->getGlShader();
graphics::shader_use(shader);
auto viewMatrix = graphics::camera_get_view(scene::scene_get_camera(scene));
auto projMatrix = graphics::camera_get_projection(scene::scene_get_camera(scene));
graphics::vertex_buffer_bind(m->VertexBuffer, commandBuffer);
graphics::index_buffer_bind(m->IndexBuffer, commandBuffer);
GLint uniTrans = glGetUniformLocation(graphics::shader_get_program(shader), "model");
glUniformMatrix4fv(uniTrans, 1, GL_FALSE, glm::value_ptr(glm::mat4(1.0f)));
GLint uniView = glGetUniformLocation(graphics::shader_get_program(shader), "view");
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(viewMatrix));
GLint uniProj = glGetUniformLocation(graphics::shader_get_program(shader), "proj");
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(projMatrix));
glBindVertexArray(m->getVAO());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->getEBO());
glDrawElements(GL_TRIANGLES, m->getIndexCount() * sizeof(uint32_t), GL_UNSIGNED_INT, 0);
vkCmdDrawIndexed(
commandBuffer, m->IndexBuffer->GenericBuffer->Count, 1, 0, 0, 0);
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
graphics::renderer_end_pass(renderer->Renderer);
renderer->HasViewportChanged = false;
}
}

View File

@@ -10,19 +10,25 @@ struct Scene;
namespace inferno::graphics {
struct VulkanRenderer;
struct Viewport;
struct Camera;
struct DebugDraw;
struct RenderTarget;
struct Shader;
typedef struct PreviewRenderer {
glm::ivec2 Viewport;
VulkanRenderer* Renderer;
GLuint RenderTarget = 0;
GLuint RenderTargetTexture = 0;
GLuint RenderTargetDepthTexture = 0;
RenderTarget* PreviewRenderTarget;
Shader* DrawShader;
VkRect2D Viewport;
bool HasViewportChanged = false;
} PreviewRenderer;
PreviewRenderer* preview_create();
PreviewRenderer* preview_create(VulkanRenderer* renderer);
void preview_cleanup(PreviewRenderer* renderer);
void preview_draw_debug_ui(PreviewRenderer* renderer);
@@ -30,7 +36,7 @@ void preview_draw_debug_ui(PreviewRenderer* renderer);
void preview_draw_ui(PreviewRenderer* renderer);
void preview_set_viewport(PreviewRenderer* renderer, Camera* camera);
GLuint preview_get_rendered_texture(PreviewRenderer* renderer);
RenderTarget* preview_get_target(PreviewRenderer* renderer);
void preview_draw(PreviewRenderer* renderer, scene::Scene* scene);

View File

@@ -1,188 +0,0 @@
#include "shader.hpp"
#include <fstream>
#include <iostream>
namespace inferno::graphics {
static std::unordered_map<GLuint, int> shader2Index = {
{ GL_VERTEX_SHADER, 0 },
{ GL_GEOMETRY_SHADER, 1 },
{ GL_FRAGMENT_SHADER, 2 }
};
inline std::string trim(std::string& str)
{
str.erase(str.find_last_not_of(' ') + 1); // suffixing spaces
str.erase(0, str.find_first_not_of(' ')); // prefixing spaces
return str;
}
std::string textFromFile(const std::filesystem::path& path)
{
std::ifstream input(path);
return std::string((std::istreambuf_iterator<char>(input)),
std::istreambuf_iterator<char>());
}
std::vector<const ShaderPreprocessorDefinition*> getKeys(Shader* shader, std::string key)
{
std::vector<const ShaderPreprocessorDefinition*> ret;
for (const auto& p : shader->PreprocessorDefinitions)
if (p.key == key)
ret.push_back(&p);
return ret;
}
bool checkShader(GLuint uid)
{
GLint isCompiled = 0;
glGetShaderiv(uid, GL_COMPILE_STATUS, &isCompiled);
if (isCompiled == GL_FALSE) {
GLint maxLength = 0;
glGetShaderiv(uid, GL_INFO_LOG_LENGTH, &maxLength);
std::vector<GLchar> errorLog(maxLength);
glGetShaderInfoLog(uid, maxLength, &maxLength, &errorLog[0]);
for (int i = 0; i < errorLog.size(); i++) {
std::cout << errorLog[i];
}
glDeleteShader(uid);
return false;
}
return true;
}
Shader* shader_create()
{
Shader* shader = new Shader;
shader->Program = 0;
shader->Shaders[0] = GL_NONE;
shader->Shaders[1] = GL_NONE;
shader->Shaders[2] = GL_NONE;
return shader;
}
void shader_cleanup(Shader* shader)
{
for (int i = 0; i < 3; i++) {
if (shader->Shaders[i] == GL_NONE)
continue;
glDeleteShader(shader->Shaders[i]);
}
glDeleteProgram(shader->Program);
}
void shader_load(Shader* shader, std::filesystem::path path)
{
assert(std::filesystem::exists(path));
std::string loadedShaderSource = textFromFile(path);
for (int i = 0; i < loadedShaderSource.length(); i++) {
const char& c = loadedShaderSource[i];
if (c == '#') {
ShaderPreprocessorDefinition def = { .start = i };
int j;
for (j = ++i; loadedShaderSource[j] != ' '; j++) {
def.key += loadedShaderSource[j];
}
for (j++; loadedShaderSource[j] != '\n'; j++) {
def.def += loadedShaderSource[j];
}
def.end = j;
i = j; // advance i
def.def = trim(def.def);
def.key = trim(def.key);
shader->PreprocessorDefinitions.push_back(def);
}
}
// now we have all of the key/value definitions
// we can extract the relavent ones, for example
// "type"
std::vector<const ShaderPreprocessorDefinition*> types = getKeys(shader, "type");
int i = 0;
for (const ShaderPreprocessorDefinition* type : types) {
GLuint glType = GL_NONE;
if (type->def == "vertex")
glType = GL_VERTEX_SHADER;
if (type->def == "geometry")
glType = GL_GEOMETRY_SHADER;
if (type->def == "fragment")
glType = GL_FRAGMENT_SHADER;
assert(glType != GL_NONE);
shader->Shaders[shader2Index[glType]] = glCreateShader(glType);
const char* source = loadedShaderSource.c_str() + type->end;
int end = types.size() - 1 == i ? types.size() : types[i + 1]->start;
int length = end - type->end;
glShaderSource(shader->Shaders[shader2Index[glType]], 1, &source, &length);
i++;
}
}
void shader_link(Shader* shader)
{
shader->Program = glCreateProgram();
for (int i = 0; i < 3; i++) {
if (shader->Shaders[i] == GL_NONE)
continue;
glCompileShader(shader->Shaders[i]);
if (!checkShader(shader->Shaders[i]))
continue;
glAttachShader(shader->Program, shader->Shaders[i]);
}
glLinkProgram(shader->Program);
}
GLuint shader_get_program(Shader* shader)
{
return shader->Program;
}
void shader_add_attribute(Shader* shader, const std::string& attribute)
{
shader->Attributes[attribute] = glGetAttribLocation(shader->Program, attribute.c_str());
}
void shader_add_uniform(Shader* shader, const std::string& uniform)
{
shader->Uniforms[uniform] = glGetUniformLocation(shader->Program, uniform.c_str());
}
GLuint shader_get_attribute(Shader* shader, const std::string& attribute)
{
return shader->Attributes[attribute];
}
GLuint shader_get_uniform(Shader* shader, const std::string& uniform)
{
return shader->Uniforms[uniform];
}
void shader_use(Shader* shader)
{
glUseProgram(shader->Program);
}
void shader_unuse(Shader* shader)
{
glUseProgram(0);
}
}

View File

@@ -1,44 +0,0 @@
#pragma once
#include "../graphics.hpp"
#include <filesystem>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace inferno::graphics {
typedef struct ShaderPreprocessorDefinition {
int start, end;
std::string key;
std::string def;
} shaderpreprocessordefinition;
typedef struct Shader {
GLuint Shaders[3];
GLuint Program;
std::unordered_map<std::string, GLuint> Attributes;
std::unordered_map<std::string, GLuint> Uniforms;
std::vector<ShaderPreprocessorDefinition> PreprocessorDefinitions;
} Shader;
Shader* shader_create();
void shader_cleanup(Shader* shader);
void shader_load(Shader* shader, std::filesystem::path path);
void shader_link(Shader* shader);
GLuint shader_get_program(Shader* shader);
// TODO: Implement shader_reload
void shader_add_attribute(Shader* shader, const std::string& attribute);
void shader_add_uniform(Shader* shader, const std::string& uniform);
GLuint shader_get_attribute(Shader* shader, const std::string& attribute);
GLuint shader_get_uniform(Shader* shader, const std::string& uniform);
void shader_use(Shader* shader);
void shader_unuse(Shader* shader);
}

View File

@@ -177,7 +177,7 @@ glm::ivec2 camera_ray_get_viewport(Camera* camera)
return camera->Views.Ray;
}
void camera_move(Camera* camera, uint8_t movement_delta)
void camera_move(Camera* camera, uint8_t movement_delta, float delta_time)
{
if (movement_delta == 0)
return;
@@ -223,16 +223,17 @@ void camera_move(Camera* camera, uint8_t movement_delta)
// forward vector must be negative to look forward.
// read :http://in2gpu.com/2015/05/17/view-matrix/
camera->Position += delta * camera->Speed;
camera->Position += delta * camera->Speed * (delta_time * 100);
// update the view matrix
camera_update(camera);
}
void camera_mouse_move(Camera* camera, glm::vec2 mouse_delta)
void camera_mouse_move(Camera* camera, glm::vec2 mouse_delta, float delta_time)
{
if (glm::length(mouse_delta) == 0)
return;
// note that yaw and pitch must be converted to radians.
// this is done in update() by glm::rotate
camera->Yaw += camera->MouseSensitivity * (mouse_delta.x / 100);

View File

@@ -1,27 +0,0 @@
#include "material.hpp"
#include "preview_renderer/shader.hpp"
namespace inferno::scene {
Material::Material(std::string name)
: mName(name)
{
}
Material::~Material()
{
}
void Material::setGlShader(graphics::Shader* shader)
{
mGlShader = shader;
}
graphics::Shader* Material::getGlShader()
{
return mGlShader;
}
}

View File

@@ -1,31 +0,0 @@
#pragma once
#include <string>
#include <hart_graphics.hpp>
namespace inferno::graphics {
class Shader;
};
namespace inferno::scene {
class HitInfo;
class Material {
public:
Material(std::string name);
~Material();
std::string getName();
void setGlShader(graphics::Shader* shader);
graphics::Shader* getGlShader();
glm::vec3 sample(HitInfo* hit);
private:
std::string mName;
graphics::Shader* mGlShader;
};
}

View File

@@ -1,120 +1,114 @@
#include "mesh.hpp"
#include <yolo/yolo.hpp>
#include <scene/objloader.hpp>
#include "graphics/buffer.hpp"
#include "graphics/device.hpp"
#include <yolo/yolo.hpp>
#include <iostream>
namespace inferno::scene {
Mesh::Mesh()
VkVertexInputBindingDescription get_vert_binding_description()
{
VkVertexInputBindingDescription bindingDescription = {};
bindingDescription.binding = 0;
bindingDescription.stride = sizeof(Vert);
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return bindingDescription;
}
Mesh::~Mesh()
std::array<VkVertexInputAttributeDescription, 2> get_vert_attribute_descriptions()
{
delete mObjLoader;
std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions = {};
// Position
attributeDescriptions[0].binding = 0;
attributeDescriptions[0].location = 0;
attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[0].offset = offsetof(Vert, Position);
// Normal
attributeDescriptions[1].binding = 0;
attributeDescriptions[1].location = 1;
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[1].offset = offsetof(Vert, Normal);
return attributeDescriptions;
}
void Mesh::loadOBJ(std::filesystem::path file)
Mesh* mesh_create(graphics::GraphicsDevice* device)
{
mObjLoader = new ObjLoader();
mObjLoader->load(file);
Mesh* mesh = new Mesh();
mesh->Device = device;
int vertCount = mObjLoader->getVertCount();
for (int i = 0; i < vertCount * 3; i += 3)
{
return mesh;
}
void mesh_cleanup(Mesh* mesh)
{
delete mesh->MeshObjLoader;
delete mesh;
}
void mesh_load_obj(Mesh* mesh, std::filesystem::path file)
{
mesh->MeshObjLoader = new ObjLoader();
mesh->MeshObjLoader->load(file);
int vertCount = mesh->MeshObjLoader->getVertCount();
for (int i = 0; i < vertCount * 3; i += 3) {
Vert vert;
vert.Position = {
mObjLoader->getPositions()[i],
mObjLoader->getPositions()[i+1],
mObjLoader->getPositions()[i+2],
mesh->MeshObjLoader->getPositions()[i],
mesh->MeshObjLoader->getPositions()[i + 1],
mesh->MeshObjLoader->getPositions()[i + 2],
};
vert.Normal = {
mObjLoader->getNormals()[i],
mObjLoader->getNormals()[i+1],
mObjLoader->getNormals()[i+2],
mesh->MeshObjLoader->getNormals()[i],
mesh->MeshObjLoader->getNormals()[i + 1],
mesh->MeshObjLoader->getNormals()[i + 2],
};
mVerticies.push_back(vert);
mesh->Verticies.push_back(vert);
}
}
void Mesh::ready()
void mesh_ready(Mesh* mesh)
{
// TODO: ready check
glGenVertexArrays(1, &mVAO);
glGenBuffers(1, &mVBO);
glGenBuffers(1, &mEBO);
void* data = mesh->Verticies.data();
void* indexData = (void*)mesh->MeshObjLoader->getFaces();
glBindVertexArray(mVAO);
// load data into vertex buffers
uint32_t size = mesh->Verticies.size();
// yolo::debug("Mesh size: {}", size);
glBindBuffer(GL_ARRAY_BUFFER, mVBO);
glBufferData(GL_ARRAY_BUFFER, mVerticies.size() * sizeof(Vert), &mVerticies[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mObjLoader->getIndexCount() * sizeof(uint32_t), &mObjLoader->getFaces()[0], GL_STATIC_DRAW);
// set the vertex attribute pointers
// vertex Positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (void*)0);
// vertex normals
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (void*)offsetof(Vert, Normal));
// vertex UV
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vert), (void*)offsetof(Vert, UV));
glBindVertexArray(0);
mesh->VertexBuffer = graphics::vertex_buffer_create(mesh->Device, data, size);
mesh->IndexBuffer = graphics::index_buffer_create(
mesh->Device, indexData, mesh->MeshObjLoader->getIndexCount());
yolo::debug("Mesh for preview ready...");
}
int Mesh::getVerticies(const float** v, const float** n)
uint32_t mesh_get_verticies(Mesh* mesh, const float** v, const float** n)
{
*v = &mObjLoader->getPositions()[0];
*n = &mObjLoader->getNormals()[0];
return mObjLoader->getVertCount();
*v = &mesh->MeshObjLoader->getPositions()[0];
*n = &mesh->MeshObjLoader->getNormals()[0];
return mesh->MeshObjLoader->getVertCount();
}
int Mesh::getIndicies(const uint32_t** i)
uint32_t mesh_get_indicies(Mesh* mesh, const uint32_t** i)
{
*i = &mObjLoader->getFaces()[0];
return mObjLoader->getIndexCount();
*i = &mesh->MeshObjLoader->getFaces()[0];
return mesh->MeshObjLoader->getIndexCount();
}
int Mesh::getIndexCount()
{
return mObjLoader->getIndexCount();
}
uint32_t mesh_get_index_count(Mesh* mesh) { return mesh->MeshObjLoader->getIndexCount(); }
void Mesh::setMaterial(Material* mat)
{
mMaterial = mat;
}
void mesh_set_model_matrix(Mesh* mesh, glm::mat4 model) { mesh->ModelMatrix = model; }
Material* Mesh::getMaterial()
{
return mMaterial;
}
GLuint Mesh::getVAO()
{
return mVAO;
}
GLuint Mesh::getVBO()
{
return mVBO;
}
GLuint Mesh::getEBO()
{
return mEBO;
}
glm::mat4 mesh_get_model_matrix(Mesh* mesh) { return mesh->ModelMatrix; }
}

View File

@@ -1,56 +1,61 @@
#pragma once
#include <vector>
#include <filesystem>
#include <hart_graphics.hpp>
#include <array>
#include <filesystem>
#include <vector>
namespace inferno::graphics {
struct GraphicsDevice;
struct Buffer;
}
namespace inferno::scene {
class ObjLoader;
class Material;
// TODO: This should be procedural like everything else
struct Vert
{
struct Vert {
glm::vec3 Position;
glm::vec3 Normal;
glm::vec2 UV;
};
class Mesh
{
public:
Mesh();
~Mesh();
void loadOBJ(std::filesystem::path file);
void ready();
int getVerticies(const float** v, const float** n);
int getIndicies(const uint32_t** i);
int getIndexCount();
void setMaterial(Material* mat);
Material* getMaterial();
// Raster
public:
GLuint getVAO();
GLuint getVBO();
GLuint getEBO();
private:
GLuint mVAO;
GLuint mVBO;
GLuint mEBO;
glm::mat4 mModel;
private:
ObjLoader* mObjLoader;
Material* mMaterial;
std::vector<Vert> mVerticies;
struct DebugLineVert {
glm::vec3 Position;
glm::vec3 Color;
};
using Index = uint32_t;
// NOTE: Out of coincidence, this is the same for both vert and debug line vert
// TODO: Make this a template // somehow
VkVertexInputBindingDescription get_vert_binding_description();
std::array<VkVertexInputAttributeDescription, 2> get_vert_attribute_descriptions();
typedef struct Mesh {
graphics::GraphicsDevice* Device;
glm::mat4 ModelMatrix;
ObjLoader* MeshObjLoader;
std::vector<Vert> Verticies;
graphics::Buffer* VertexBuffer;
graphics::Buffer* IndexBuffer;
} Mesh;
Mesh* mesh_create(graphics::GraphicsDevice* device);
void mesh_cleanup(Mesh* mesh);
void mesh_load_obj(Mesh* mesh, std::filesystem::path file);
void mesh_ready(Mesh* mesh);
uint32_t mesh_get_verticies(Mesh* mesh, const float** v, const float** n);
uint32_t mesh_get_indicies(Mesh* mesh, const uint32_t** i);
uint32_t mesh_get_index_count(Mesh* mesh);
void mesh_set_model_matrix(Mesh* mesh, glm::mat4 model);
glm::mat4 mesh_get_model_matrix(Mesh* mesh);
}

View File

@@ -1,5 +1,7 @@
#pragma once
#include "hart_graphics.hpp"
#include "scene/object.hpp"
#include <memory>
@@ -14,6 +16,13 @@ namespace inferno::scene {
class SceneObject;
class Mesh;
typedef struct GlobalUniformObject {
glm::mat4 Projection;
glm::mat4 View;
glm::mat4 _padding0; // Fuck you nvidia!
glm::mat4 _padding1;
} GlobalUniformObject;
typedef struct Scene {
std::vector<SceneObject*> Objects;
graphics::Camera* Camera;

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// DEAR IMGUI COMPILE-TIME OPTIONS
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
@@ -9,7 +9,7 @@
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
@@ -26,21 +26,21 @@
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
@@ -62,9 +62,10 @@
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
@@ -75,6 +76,12 @@
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
// Only works in combination with IMGUI_ENABLE_FREETYPE.
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
//---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
//#define IMGUI_ENABLE_STB_TRUETYPE
@@ -90,6 +97,8 @@
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- ...Or use Dear ImGui's own very basic math operators.
//#define IMGUI_DEFINE_MATH_OPERATORS
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
@@ -103,23 +112,18 @@
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
// This adds a small runtime cost which is why it is not enabled by default.
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
void MyFunction(const char* name, MyMatrix44* mtx);
}
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.89 WIP
// dear imgui, v1.90.1 WIP
// (drawing and font code)
/*
@@ -26,13 +26,12 @@ Index of this file:
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "imgui.h"
#ifndef IMGUI_DISABLE
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include "imgui.h"
#ifndef IMGUI_DISABLE
#include "imgui_internal.h"
#ifdef IMGUI_ENABLE_FREETYPE
#include "misc/freetype/imgui_freetype.h"
@@ -64,6 +63,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
@@ -135,7 +135,7 @@ namespace IMGUI_STB_NAMESPACE
#define STBTT_sqrt(x) ImSqrt(x)
#define STBTT_pow(x,y) ImPow(x,y)
#define STBTT_fabs(x) ImFabs(x)
#define STBTT_ifloor(x) ((int)ImFloorSigned(x))
#define STBTT_ifloor(x) ((int)ImFloor(x))
#define STBTT_iceil(x) ((int)ImCeil(x))
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
@@ -392,9 +392,9 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
void ImDrawList::_ResetForNewFrame()
{
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0);
IM_STATIC_ASSERT(offsetof(ImDrawCmd, TextureId) == sizeof(ImVec4));
IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
if (_Splitter._Count > 1)
_Splitter.Merge(this);
@@ -455,11 +455,13 @@ void ImDrawList::AddDrawCmd()
// Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL
void ImDrawList::_PopUnusedDrawCmd()
{
if (CmdBuffer.Size == 0)
return;
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
if (curr_cmd->ElemCount == 0 && curr_cmd->UserCallback == NULL)
while (CmdBuffer.Size > 0)
{
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
if (curr_cmd->ElemCount != 0 || curr_cmd->UserCallback != NULL)
return;// break;
CmdBuffer.pop_back();
}
}
void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
@@ -479,7 +481,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
}
// Compare ClipRect, TextureId and VtxOffset with a single memcmp()
#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
#define ImDrawCmd_HeaderSize (offsetof(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
@@ -565,7 +567,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
{
// Automatic segment count
const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy
if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
if (radius_idx >= 0 && radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
return _Data->CircleSegmentCounts[radius_idx]; // Use cached value
else
return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);
@@ -712,7 +714,7 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness)
{
if (points_count < 2)
if (points_count < 2 || (col & IM_COL32_A_MASK) == 0)
return;
const bool closed = (flags & ImDrawFlags_Closed) != 0;
@@ -970,7 +972,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col)
{
if (points_count < 3)
if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)
return;
const ImVec2 uv = _Data->TexUvWhitePixel;
@@ -1195,8 +1197,8 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);
const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);
const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f);
const int a_min_sample = a_is_reverse ? (int)ImFloor(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloor(a_max_sample_f);
const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);
const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
@@ -1221,6 +1223,27 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
}
}
void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments)
{
if (num_segments <= 0)
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
_Path.reserve(_Path.Size + (num_segments + 1));
const float cos_rot = ImCos(rot);
const float sin_rot = ImSin(rot);
for (int i = 0; i <= num_segments; i++)
{
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
ImVec2 point(ImCos(a) * radius_x, ImSin(a) * radius_y);
const float rel_x = (point.x * cos_rot) - (point.y * sin_rot);
const float rel_y = (point.x * sin_rot) + (point.y * cos_rot);
point.x = rel_x + center.x;
point.y = rel_y + center.y;
_Path.push_back(point);
}
}
ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t)
{
float u = 1.0f - t;
@@ -1316,33 +1339,22 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
}
}
IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
{
/*
IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Obsoleted in 1.82 (from February 2021)
// Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
// ~0 --> ImDrawFlags_RoundCornersAll or 0
if (flags == ~0)
return ImDrawFlags_RoundCornersAll;
// Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations)
// 0x01 --> ImDrawFlags_RoundCornersTopLeft (VALUE 0x01 OVERLAPS ImDrawFlags_Closed but ImDrawFlags_Closed is never valid in this path!)
// 0x02 --> ImDrawFlags_RoundCornersTopRight
// 0x03 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight
// 0x04 --> ImDrawFlags_RoundCornersBotLeft
// 0x05 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBotLeft
// ...
// 0x0F --> ImDrawFlags_RoundCornersAll or 0
// (See all values in ImDrawCornerFlags_)
if (flags >= 0x01 && flags <= 0x0F)
return (flags << 4);
// Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023)
// - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
if (flags == ~0) { return ImDrawFlags_RoundCornersAll; }
// - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.
if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); }
// We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'
#endif
// If this triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
// Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc...
*/
// If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
// Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway.
// See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section.
IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!");
if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
@@ -1353,10 +1365,12 @@ static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags)
{
flags = FixRectCornerFlags(flags);
rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f);
rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f);
if (rounding >= 0.5f)
{
flags = FixRectCornerFlags(flags);
rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f);
rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f);
}
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
{
PathLineTo(a);
@@ -1549,6 +1563,35 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in
PathFillConvex(col);
}
// Ellipse
void ImDrawList::AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments, float thickness)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
if (num_segments <= 0)
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);
PathStroke(col, true, thickness);
}
void ImDrawList::AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
if (num_segments <= 0)
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);
PathFillConvex(col);
}
// Cubic Bezier takes 4 controls points
void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments)
{
@@ -1813,6 +1856,63 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)
// [SECTION] ImDrawData
//-----------------------------------------------------------------------------
void ImDrawData::Clear()
{
Valid = false;
CmdListsCount = TotalIdxCount = TotalVtxCount = 0;
CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them.
DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f);
OwnerViewport = NULL;
}
// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list
// as long at it is expected that the result will be later merged into draw_data->CmdLists[].
void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
{
if (draw_list->CmdBuffer.Size == 0)
return;
if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL)
return;
// Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
// May trigger for you if you are using PrimXXX functions incorrectly.
IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
// Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
// If this assert triggers because you are drawing lots of stuff manually:
// - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
// Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
// - If you want large meshes with more than 64K vertices, you can either:
// (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
// Most example backends already support this from 1.71. Pre-1.71 backends won't.
// Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
// (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
// Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
// glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
// Your own engine or render API may use different parameters or function calls to specify index sizes.
// 2 and 4 bytes indices are generally supported by most graphics API.
// - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
// the 64K limit to split your draw commands in multiple draw lists.
if (sizeof(ImDrawIdx) == 2)
IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
// Add to output list + records state in ImDrawData
out_list->push_back(draw_list);
draw_data->CmdListsCount++;
draw_data->TotalVtxCount += draw_list->VtxBuffer.Size;
draw_data->TotalIdxCount += draw_list->IdxBuffer.Size;
}
void ImDrawData::AddDrawList(ImDrawList* draw_list)
{
IM_ASSERT(CmdLists.Size == CmdListsCount);
draw_list->_PopUnusedDrawCmd();
ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list);
}
// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
void ImDrawData::DeIndexAllBuffers()
{
@@ -1837,15 +1937,9 @@ void ImDrawData::DeIndexAllBuffers()
// or if there is a difference between your window resolution and framebuffer resolution.
void ImDrawData::ScaleClipRects(const ImVec2& fb_scale)
{
for (int i = 0; i < CmdListsCount; i++)
{
ImDrawList* cmd_list = CmdLists[i];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i];
cmd->ClipRect = ImVec4(cmd->ClipRect.x * fb_scale.x, cmd->ClipRect.y * fb_scale.y, cmd->ClipRect.z * fb_scale.x, cmd->ClipRect.w * fb_scale.y);
}
}
for (ImDrawList* draw_list : CmdLists)
for (ImDrawCmd& cmd : draw_list->CmdBuffer)
cmd.ClipRect = ImVec4(cmd.ClipRect.x * fb_scale.x, cmd.ClipRect.y * fb_scale.y, cmd.ClipRect.z * fb_scale.x, cmd.ClipRect.w * fb_scale.y);
}
//-----------------------------------------------------------------------------
@@ -1901,6 +1995,14 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve
}
}
void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out)
{
ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out;
}
//-----------------------------------------------------------------------------
// [SECTION] ImFontConfig
//-----------------------------------------------------------------------------
@@ -1909,10 +2011,11 @@ ImFontConfig::ImFontConfig()
{
memset(this, 0, sizeof(*this));
FontDataOwnedByAtlas = true;
OversampleH = 3; // FIXME: 2 may be a better default?
OversampleH = 2;
OversampleV = 1;
GlyphMaxAdvanceX = FLT_MAX;
RasterizerMultiply = 1.0f;
RasterizerDensity = 1.0f;
EllipsisChar = (ImWchar)-1;
}
@@ -1986,19 +2089,19 @@ ImFontAtlas::~ImFontAtlas()
void ImFontAtlas::ClearInputData()
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
for (int i = 0; i < ConfigData.Size; i++)
if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas)
for (ImFontConfig& font_cfg : ConfigData)
if (font_cfg.FontData && font_cfg.FontDataOwnedByAtlas)
{
IM_FREE(ConfigData[i].FontData);
ConfigData[i].FontData = NULL;
IM_FREE(font_cfg.FontData);
font_cfg.FontData = NULL;
}
// When clearing this we lose access to the font name and other information used to build the font.
for (int i = 0; i < Fonts.Size; i++)
if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size)
for (ImFont* font : Fonts)
if (font->ConfigData >= ConfigData.Data && font->ConfigData < ConfigData.Data + ConfigData.Size)
{
Fonts[i]->ConfigData = NULL;
Fonts[i]->ConfigDataCount = 0;
font->ConfigData = NULL;
font->ConfigDataCount = 0;
}
ConfigData.clear();
CustomRects.clear();
@@ -2095,6 +2198,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
ImFontAtlasUpdateConfigDataPointers(this);
// Invalidate texture
TexReady = false;
ClearTexData();
@@ -2131,7 +2236,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
if (font_cfg.Name[0] == '\0')
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
font_cfg.EllipsisChar = (ImWchar)0x0085;
font_cfg.GlyphOffset.y = 1.0f * IM_FLOOR(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
font_cfg.GlyphOffset.y = 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
@@ -2161,13 +2266,14 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels,
}
// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().
ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
IM_ASSERT(font_cfg.FontData == NULL);
font_cfg.FontData = ttf_data;
font_cfg.FontDataSize = ttf_size;
IM_ASSERT(font_data_size > 100 && "Incorrect value for font_data_size!"); // Heuristic to prevent accidentally passing a wrong value to font_data_size.
font_cfg.FontData = font_data;
font_cfg.FontDataSize = font_data_size;
font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;
if (glyph_ranges)
font_cfg.GlyphRanges = glyph_ranges;
@@ -2382,13 +2488,21 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found.");
if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
{
IM_ASSERT(0 && "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.");
return false;
}
// Measure highest codepoints
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
{
// Check for valid range. This may also help detect *some* dangling pointers, because a common
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
IM_ASSERT(src_range[0] <= src_range[1]);
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
}
dst_tmp.SrcCount++;
dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
}
@@ -2459,7 +2573,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
// Convert our ranges in the format stb_truetype wants
ImFontConfig& cfg = atlas->ConfigData[src_i];
src_tmp.PackRange.font_size = cfg.SizePixels;
src_tmp.PackRange.font_size = cfg.SizePixels * cfg.RasterizerDensity;
src_tmp.PackRange.first_unicode_codepoint_in_range = 0;
src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data;
src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size;
@@ -2468,7 +2582,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV;
// Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels);
const float scale = (cfg.SizePixels > 0.0f) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels * cfg.RasterizerDensity) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels * cfg.RasterizerDensity);
const int padding = atlas->TexGlyphPadding;
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
{
@@ -2553,13 +2667,10 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
// 9. Setup ImFont and glyphs for runtime
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
if (src_tmp.GlyphsCount == 0)
continue;
// When merging fonts with MergeMode=true:
// - We can have multiple input fonts writing into a same destination font.
// - dst_font->ConfigData is != from cfg which is our source configuration.
ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
ImFontConfig& cfg = atlas->ConfigData[src_i];
ImFont* dst_font = cfg.DstFont;
@@ -2567,12 +2678,14 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
const float descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
const float font_off_x = cfg.GlyphOffset.x;
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
const float inv_rasterization_scale = 1.0f / cfg.RasterizerDensity;
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
{
// Register glyph
@@ -2581,7 +2694,11 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
stbtt_aligned_quad q;
float unused_x = 0.0f, unused_y = 0.0f;
stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0);
dst_font->AddGlyph(&cfg, (ImWchar)codepoint, q.x0 + font_off_x, q.y0 + font_off_y, q.x1 + font_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, pc.xadvance);
float x0 = q.x0 * inv_rasterization_scale + font_off_x;
float y0 = q.y0 * inv_rasterization_scale + font_off_y;
float x1 = q.x1 * inv_rasterization_scale + font_off_x;
float y1 = q.y1 * inv_rasterization_scale + font_off_y;
dst_font->AddGlyph(&cfg, (ImWchar)codepoint, x0, y0, x1, y1, q.s0, q.t0, q.s1, q.t1, pc.xadvance * inv_rasterization_scale);
}
}
@@ -2601,19 +2718,31 @@ const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype()
#endif // IMGUI_ENABLE_STB_TRUETYPE
void ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas)
{
for (ImFontConfig& font_cfg : atlas->ConfigData)
{
ImFont* font = font_cfg.DstFont;
if (!font_cfg.MergeMode)
{
font->ConfigData = &font_cfg;
font->ConfigDataCount = 0;
}
font->ConfigDataCount++;
}
}
void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)
{
if (!font_config->MergeMode)
{
font->ClearOutputData();
font->FontSize = font_config->SizePixels;
font->ConfigData = font_config;
font->ConfigDataCount = 0;
IM_ASSERT(font->ConfigData == font_config);
font->ContainerAtlas = atlas;
font->Ascent = ascent;
font->Descent = descent;
}
font->ConfigDataCount++;
}
void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque)
@@ -2623,6 +2752,9 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa
ImVector<ImFontAtlasCustomRect>& user_rects = atlas->CustomRects;
IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong.
#ifdef __GNUC__
if (user_rects.Size < 1) { __builtin_unreachable(); } // Workaround for GCC bug if IM_ASSERT() is defined to conditionally throw (see #5343)
#endif
ImVector<stbrp_rect> pack_rects;
pack_rects.resize(user_rects.Size);
@@ -2757,6 +2889,13 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
// Note: this is called / shared by both the stb_truetype and the FreeType builder
void ImFontAtlasBuildInit(ImFontAtlas* atlas)
{
// Round font size
// - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
// - Note that using io.FontGlobalScale or SetWindowFontScale(), with are legacy-ish, partially supported features, can still lead to unrounded sizes.
// - We may support it better later and remove this rounding.
for (ImFontConfig& cfg : atlas->ConfigData)
cfg.SizePixels = ImTrunc(cfg.SizePixels);
// Register texture region for mouse cursors or standard white pixels
if (atlas->PackIdMouseCursors < 0)
{
@@ -2798,9 +2937,9 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
}
// Build all fonts lookup tables
for (int i = 0; i < atlas->Fonts.Size; i++)
if (atlas->Fonts[i]->DirtyLookupTables)
atlas->Fonts[i]->BuildLookupTable();
for (ImFont* font : atlas->Fonts)
if (font->DirtyLookupTables)
font->BuildLookupTable();
atlas->TexReady = true;
}
@@ -2943,19 +3082,19 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese()
// 2999 ideograms code points for Japanese
// - 2136 Joyo (meaning "for regular use" or "for common use") Kanji code points
// - 863 Jinmeiyo (meaning "for personal name") Kanji code points
// - Sourced from the character information database of the Information-technology Promotion Agency, Japan
// - https://mojikiban.ipa.go.jp/mji/
// - Available under the terms of the Creative Commons Attribution-ShareAlike 2.1 Japan (CC BY-SA 2.1 JP).
// - https://creativecommons.org/licenses/by-sa/2.1/jp/deed.en
// - https://creativecommons.org/licenses/by-sa/2.1/jp/legalcode
// - You can generate this code by the script at:
// - https://github.com/vaiorabbit/everyday_use_kanji
// - Sourced from official information provided by the government agencies of Japan:
// - List of Joyo Kanji by the Agency for Cultural Affairs
// - https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/
// - List of Jinmeiyo Kanji by the Ministry of Justice
// - http://www.moj.go.jp/MINJI/minji86.html
// - Available under the terms of the Creative Commons Attribution 4.0 International (CC BY 4.0).
// - https://creativecommons.org/licenses/by/4.0/legalcode
// - You can generate this code by the script at:
// - https://github.com/vaiorabbit/everyday_use_kanji
// - References:
// - List of Joyo Kanji
// - (Official list by the Agency for Cultural Affairs) https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kakuki/14/tosin02/index.html
// - (Wikipedia) https://en.wikipedia.org/wiki/List_of_j%C5%8Dy%C5%8D_kanji
// - List of Jinmeiyo Kanji
// - (Official list by the Ministry of Justice) http://www.moj.go.jp/MINJI/minji86.html
// - (Wikipedia) https://en.wikipedia.org/wiki/Jinmeiy%C5%8D_kanji
// - Missing 1 Joyo Kanji: U+20B9F (Kun'yomi: Shikaru, On'yomi: Shitsu,shichi), see https://github.com/ocornut/imgui/pull/3627 for details.
// You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
@@ -3118,7 +3257,8 @@ ImFont::ImFont()
FallbackAdvanceX = 0.0f;
FallbackChar = (ImWchar)-1;
EllipsisChar = (ImWchar)-1;
DotChar = (ImWchar)-1;
EllipsisWidth = EllipsisCharStep = 0.0f;
EllipsisCharCount = 0;
FallbackGlyph = NULL;
ContainerAtlas = NULL;
ConfigData = NULL;
@@ -3164,6 +3304,7 @@ void ImFont::BuildLookupTable()
max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);
// Build lookup table
IM_ASSERT(Glyphs.Size > 0 && "Font has not loaded glyph!");
IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved
IndexAdvanceX.clear();
IndexLookup.clear();
@@ -3199,17 +3340,7 @@ void ImFont::BuildLookupTable()
SetGlyphVisible((ImWchar)' ', false);
SetGlyphVisible((ImWchar)'\t', false);
// Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
// However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
// FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 };
const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
if (EllipsisChar == (ImWchar)-1)
EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars));
if (DotChar == (ImWchar)-1)
DotChar = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars));
// Setup fallback character
// Setup Fallback character
const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' };
FallbackGlyph = FindGlyphNoFallback(FallbackChar);
if (FallbackGlyph == NULL)
@@ -3222,11 +3353,32 @@ void ImFont::BuildLookupTable()
FallbackChar = (ImWchar)FallbackGlyph->Codepoint;
}
}
FallbackAdvanceX = FallbackGlyph->AdvanceX;
for (int i = 0; i < max_codepoint + 1; i++)
if (IndexAdvanceX[i] < 0.0f)
IndexAdvanceX[i] = FallbackAdvanceX;
// Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
// However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
// FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 };
const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
if (EllipsisChar == (ImWchar)-1)
EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars));
const ImWchar dot_char = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars));
if (EllipsisChar != (ImWchar)-1)
{
EllipsisCharCount = 1;
EllipsisWidth = EllipsisCharStep = FindGlyph(EllipsisChar)->X1;
}
else if (dot_char != (ImWchar)-1)
{
const ImFontGlyph* glyph = FindGlyph(dot_char);
EllipsisChar = dot_char;
EllipsisCharCount = 3;
EllipsisCharStep = (glyph->X1 - glyph->X0) + 1.0f;
EllipsisWidth = EllipsisCharStep * 3.0f - 1.0f;
}
}
// API is designed this way to avoid exposing the 4K page size
@@ -3269,7 +3421,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa
advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX);
if (advance_x != advance_x_original)
{
float char_off_x = cfg->PixelSnapH ? ImFloor((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f;
float char_off_x = cfg->PixelSnapH ? ImTrunc((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f;
x0 += char_off_x;
x1 += char_off_x;
}
@@ -3375,6 +3527,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
bool inside_word = true;
const char* s = text;
IM_ASSERT(text_end != NULL);
while (s < text_end)
{
unsigned int c = (unsigned int)*s;
@@ -3383,8 +3536,6 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
next_s = s + 1;
else
next_s = s + ImTextCharFromUtf8(&c, s, text_end);
if (c == 0)
break;
if (c < 32)
{
@@ -3490,15 +3641,9 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
const char* prev_s = s;
unsigned int c = (unsigned int)*s;
if (c < 0x80)
{
s += 1;
}
else
{
s += ImTextCharFromUtf8(&c, s, text_end);
if (c == 0) // Malformed UTF-8?
break;
}
if (c < 32)
{
@@ -3544,8 +3689,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
if (glyph->Colored)
col |= ~IM_COL32_A_MASK;
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
float x = IM_FLOOR(pos.x);
float y = IM_FLOOR(pos.y);
float x = IM_TRUNC(pos.x);
float y = IM_TRUNC(pos.y);
draw_list->PrimReserve(6, 4);
draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
}
@@ -3557,8 +3702,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
// Align to be pixel perfect
float x = IM_FLOOR(pos.x);
float y = IM_FLOOR(pos.y);
float x = IM_TRUNC(pos.x);
float y = IM_TRUNC(pos.y);
if (y > clip_rect.w)
return;
@@ -3578,7 +3723,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
// FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA().
// If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both.
// However it is still better than nothing performing the fast-forward!
s = CalcWordWrapPositionA(scale, s, line_end, wrap_width);
s = CalcWordWrapPositionA(scale, s, line_end ? line_end : text_end, wrap_width);
s = CalcWordWrapNextLineStartA(s, text_end);
}
else
@@ -3610,10 +3755,9 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
const int idx_count_max = (int)(text_end - s) * 6;
const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max;
draw_list->PrimReserve(idx_count_max, vtx_count_max);
ImDrawVert* vtx_write = draw_list->_VtxWritePtr;
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;
ImDrawVert* vtx_write = draw_list->_VtxWritePtr;
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
unsigned int vtx_index = draw_list->_VtxCurrentIdx;
const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
const char* word_wrap_eol = NULL;
@@ -3639,15 +3783,9 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
// Decode and advance source
unsigned int c = (unsigned int)*s;
if (c < 0x80)
{
s += 1;
}
else
{
s += ImTextCharFromUtf8(&c, s, text_end);
if (c == 0) // Malformed UTF-8?
break;
}
if (c < 32)
{
@@ -3718,14 +3856,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
// We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
{
idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2);
idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3);
vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;
vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;
vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;
vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;
idx_write[0] = (ImDrawIdx)(vtx_index); idx_write[1] = (ImDrawIdx)(vtx_index + 1); idx_write[2] = (ImDrawIdx)(vtx_index + 2);
idx_write[3] = (ImDrawIdx)(vtx_index); idx_write[4] = (ImDrawIdx)(vtx_index + 2); idx_write[5] = (ImDrawIdx)(vtx_index + 3);
vtx_write += 4;
vtx_current_idx += 4;
vtx_index += 4;
idx_write += 6;
}
}
@@ -3739,7 +3877,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size);
draw_list->_VtxWritePtr = vtx_write;
draw_list->_IdxWritePtr = idx_write;
draw_list->_VtxCurrentIdx = vtx_current_idx;
draw_list->_VtxCurrentIdx = vtx_index;
}
//-----------------------------------------------------------------------------
@@ -3792,6 +3930,7 @@ void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir d
void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
{
// FIXME-OPT: This should be baked in font.
draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
}

View File

@@ -1,10 +1,11 @@
// dear imgui: Platform Backend for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.)
// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ or GLFW 3.4+ for full feature support.)
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
@@ -15,15 +16,29 @@
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2023-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys.
// 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609)
// 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491)
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702)
// 2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034)
// 2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240)
// 2023-02-03: Emscripten: Registering custom low-level mouse wheel handler to get more accurate scrolling impulses on Emscripten. (#4019, #6096)
// 2023-01-18: Handle unsupported glfwGetVideoMode() call on e.g. Emscripten.
// 2023-01-04: Inputs: Fixed mods state on Linux when using Alt-GR text input (e.g. German keyboard layout), could lead to broken text input. Revert a 2022/01/17 change were we resumed using mods provided by GLFW, turns out they were faulty.
// 2022-11-22: Perform a dummy glfwGetError() read to cancel missing names with glfwGetKeyName(). (#5908)
// 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785)
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position.
// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position *EDIT* Reverted 2023-07-18.
// 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX.
// 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11.
// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend.
@@ -60,6 +75,7 @@
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#ifndef IMGUI_DISABLE
#include "imgui_impl_glfw.h"
// Clang warnings with -Weverything
@@ -82,27 +98,39 @@
#include <GLFW/glfw3native.h> // for glfwGetCocoaWindow()
#endif
#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING
#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED
#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity
#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale
#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface
#define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwFocusWindow
#define GLFW_HAS_FOCUS_ON_SHOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_FOCUS_ON_SHOW
#define GLFW_HAS_MONITOR_WORK_AREA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorWorkarea
#define GLFW_HAS_OSX_WINDOW_POS_FIX (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION * 10 >= 3310) // 3.3.1+ Fixed: Resizing window repositions it on MacOS #1553
#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released?
#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR
#else
#define GLFW_HAS_NEW_CURSORS (0)
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/html5.h>
#endif
#ifdef GLFW_MOUSE_PASSTHROUGH // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2020-07-17 (passthrough)
#define GLFW_HAS_MOUSE_PASSTHROUGH (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_MOUSE_PASSTHROUGH
// We gather version tests as define in order to easily see which features are version-dependent.
#define GLFW_VERSION_COMBINED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION)
#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_COMBINED >= 3200) // 3.2+ GLFW_FLOATING
#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_HOVERED
#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwSetWindowOpacity
#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorContentScale
#if defined(__EMSCRIPTEN__) || defined(__SWITCH__) // no Vulkan support in GLFW for Emscripten or homebrew Nintendo Switch
#define GLFW_HAS_VULKAN (0)
#else
#define GLFW_HAS_MOUSE_PASSTHROUGH (0)
#define GLFW_HAS_VULKAN (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwCreateWindowSurface
#endif
#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetGamepadState() new api
#define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName()
#define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwFocusWindow
#define GLFW_HAS_FOCUS_ON_SHOW (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_FOCUS_ON_SHOW
#define GLFW_HAS_MONITOR_WORK_AREA (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorWorkarea
#define GLFW_HAS_OSX_WINDOW_POS_FIX (GLFW_VERSION_COMBINED >= 3301) // 3.3.1+ Fixed: Resizing window repositions it on MacOS #1553
#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released?
#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR
#else
#define GLFW_HAS_NEW_CURSORS (0)
#endif
#ifdef GLFW_MOUSE_PASSTHROUGH // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2020-07-17 (passthrough)
#define GLFW_HAS_MOUSE_PASSTHROUGH (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_MOUSE_PASSTHROUGH
#else
#define GLFW_HAS_MOUSE_PASSTHROUGH (0)
#endif
#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api
#define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName()
#define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError()
// GLFW data
enum GlfwClientApi
@@ -122,10 +150,8 @@ struct ImGui_ImplGlfw_Data
ImVec2 LastValidMousePos;
GLFWwindow* KeyOwnerWindows[GLFW_KEY_LAST];
bool InstalledCallbacks;
bool CallbacksChainForAllWindows;
bool WantUpdateMonitors;
#ifdef _WIN32
WNDPROC GlfwWndProc;
#endif
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
GLFWwindowfocusfun PrevUserCallbackWindowFocus;
@@ -136,6 +162,9 @@ struct ImGui_ImplGlfw_Data
GLFWkeyfun PrevUserCallbackKey;
GLFWcharfun PrevUserCallbackChar;
GLFWmonitorfun PrevUserCallbackMonitor;
#ifdef _WIN32
WNDPROC PrevWndProc;
#endif
ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); }
};
@@ -277,39 +306,46 @@ static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key)
case GLFW_KEY_F10: return ImGuiKey_F10;
case GLFW_KEY_F11: return ImGuiKey_F11;
case GLFW_KEY_F12: return ImGuiKey_F12;
case GLFW_KEY_F13: return ImGuiKey_F13;
case GLFW_KEY_F14: return ImGuiKey_F14;
case GLFW_KEY_F15: return ImGuiKey_F15;
case GLFW_KEY_F16: return ImGuiKey_F16;
case GLFW_KEY_F17: return ImGuiKey_F17;
case GLFW_KEY_F18: return ImGuiKey_F18;
case GLFW_KEY_F19: return ImGuiKey_F19;
case GLFW_KEY_F20: return ImGuiKey_F20;
case GLFW_KEY_F21: return ImGuiKey_F21;
case GLFW_KEY_F22: return ImGuiKey_F22;
case GLFW_KEY_F23: return ImGuiKey_F23;
case GLFW_KEY_F24: return ImGuiKey_F24;
default: return ImGuiKey_None;
}
}
static int ImGui_ImplGlfw_KeyToModifier(int key)
{
if (key == GLFW_KEY_LEFT_CONTROL || key == GLFW_KEY_RIGHT_CONTROL)
return GLFW_MOD_CONTROL;
if (key == GLFW_KEY_LEFT_SHIFT || key == GLFW_KEY_RIGHT_SHIFT)
return GLFW_MOD_SHIFT;
if (key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT)
return GLFW_MOD_ALT;
if (key == GLFW_KEY_LEFT_SUPER || key == GLFW_KEY_RIGHT_SUPER)
return GLFW_MOD_SUPER;
return 0;
}
static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods)
// X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW
// See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630
static void ImGui_ImplGlfw_UpdateKeyModifiers(GLFWwindow* window)
{
ImGuiIO& io = ImGui::GetIO();
io.AddKeyEvent(ImGuiMod_Ctrl, (mods & GLFW_MOD_CONTROL) != 0);
io.AddKeyEvent(ImGuiMod_Shift, (mods & GLFW_MOD_SHIFT) != 0);
io.AddKeyEvent(ImGuiMod_Alt, (mods & GLFW_MOD_ALT) != 0);
io.AddKeyEvent(ImGuiMod_Super, (mods & GLFW_MOD_SUPER) != 0);
io.AddKeyEvent(ImGuiMod_Ctrl, (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS));
io.AddKeyEvent(ImGuiMod_Shift, (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS));
io.AddKeyEvent(ImGuiMod_Alt, (glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS));
io.AddKeyEvent(ImGuiMod_Super, (glfwGetKey(window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS));
}
static bool ImGui_ImplGlfw_ShouldChainCallback(GLFWwindow* window)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
return bd->CallbacksChainForAllWindows ? true : (window == bd->Window);
}
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackMousebutton != nullptr && window == bd->Window)
if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackMousebutton(window, button, action, mods);
ImGui_ImplGlfw_UpdateKeyModifiers(mods);
ImGui_ImplGlfw_UpdateKeyModifiers(window);
ImGuiIO& io = ImGui::GetIO();
if (button >= 0 && button < ImGuiMouseButton_COUNT)
@@ -319,16 +355,21 @@ void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int acti
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackScroll != nullptr && window == bd->Window)
if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackScroll(window, xoffset, yoffset);
#ifdef __EMSCRIPTEN__
// Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback().
return;
#endif
ImGuiIO& io = ImGui::GetIO();
io.AddMouseWheelEvent((float)xoffset, (float)yoffset);
}
static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
{
#if GLFW_HAS_GET_KEY_NAME && !defined(__EMSCRIPTEN__)
#if GLFW_HAS_GETKEYNAME && !defined(__EMSCRIPTEN__)
// GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult.
// (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently)
// See https://github.com/glfw/glfw/issues/1502 for details.
@@ -336,7 +377,12 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
// This won't cover edge cases but this is at least going to cover common cases.
if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL)
return key;
GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr);
const char* key_name = glfwGetKeyName(key, scancode);
glfwSetErrorCallback(prev_error_callback);
#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908)
(void)glfwGetError(nullptr);
#endif
if (key_name && key_name[0] != 0 && key_name[1] == 0)
{
const char char_names[] = "`-=[]\\,;\'./";
@@ -357,16 +403,13 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackKey != nullptr && window == bd->Window)
if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackKey(window, keycode, scancode, action, mods);
if (action != GLFW_PRESS && action != GLFW_RELEASE)
return;
// Workaround: X11 does not include current pressed/released modifier key in 'mods' flags. https://github.com/glfw/glfw/issues/1630
if (int keycode_to_mod = ImGui_ImplGlfw_KeyToModifier(keycode))
mods = (action == GLFW_PRESS) ? (mods | keycode_to_mod) : (mods & ~keycode_to_mod);
ImGui_ImplGlfw_UpdateKeyModifiers(mods);
ImGui_ImplGlfw_UpdateKeyModifiers(window);
if (keycode >= 0 && keycode < IM_ARRAYSIZE(bd->KeyOwnerWindows))
bd->KeyOwnerWindows[keycode] = (action == GLFW_PRESS) ? window : nullptr;
@@ -382,7 +425,7 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i
void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackWindowFocus != nullptr && window == bd->Window)
if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackWindowFocus(window, focused);
ImGuiIO& io = ImGui::GetIO();
@@ -392,10 +435,8 @@ void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)
void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorPos != nullptr && window == bd->Window)
if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackCursorPos(window, x, y);
if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
return;
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
@@ -414,10 +455,8 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorEnter != nullptr && window == bd->Window)
if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackCursorEnter(window, entered);
if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
return;
ImGuiIO& io = ImGui::GetIO();
if (entered)
@@ -436,7 +475,7 @@ void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)
void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackChar != nullptr && window == bd->Window)
if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackChar(window, c);
ImGuiIO& io = ImGui::GetIO();
@@ -449,6 +488,28 @@ void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int)
bd->WantUpdateMonitors = true;
}
#ifdef __EMSCRIPTEN__
static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*)
{
// Mimic Emscripten_HandleWheel() in SDL.
// Corresponding equivalent in GLFW JS emulation layer has incorrect quantizing preventing small values. See #6096
float multiplier = 0.0f;
if (ev->deltaMode == DOM_DELTA_PIXEL) { multiplier = 1.0f / 100.0f; } // 100 pixels make up a step.
else if (ev->deltaMode == DOM_DELTA_LINE) { multiplier = 1.0f / 3.0f; } // 3 lines make up a step.
else if (ev->deltaMode == DOM_DELTA_PAGE) { multiplier = 80.0f; } // A page makes up 80 steps.
float wheel_x = ev->deltaX * -multiplier;
float wheel_y = ev->deltaY * -multiplier;
ImGuiIO& io = ImGui::GetIO();
io.AddMouseWheelEvent(wheel_x, wheel_y);
//IMGUI_DEBUG_LOG("[Emsc] mode %d dx: %.2f, dy: %.2f, dz: %.2f --> feed %.2f %.2f\n", (int)ev->deltaMode, ev->deltaX, ev->deltaY, ev->deltaZ, wheel_x, wheel_y);
return EM_TRUE;
}
#endif
#ifdef _WIN32
static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
#endif
void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
@@ -491,10 +552,21 @@ void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window)
bd->PrevUserCallbackMonitor = nullptr;
}
// Set to 'true' to enable chaining installed callbacks for all windows (including secondary viewports created by backends or by user.
// This is 'false' by default meaning we only chain callbacks for the main viewport.
// We cannot set this to 'true' by default because user callbacks code may be not testing the 'window' parameter of their callback.
// If you set this to 'true' your user callback code will need to make sure you are testing the 'window' parameter.
void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
bd->CallbacksChainForAllWindows = chain_for_all_windows;
}
static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
//printf("GLFW_VERSION: %d.%d.%d (%d)", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED);
// Setup backend capabilities flags
ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)();
@@ -502,7 +574,9 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
io.BackendPlatformName = "imgui_impl_glfw";
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
#ifndef __EMSCRIPTEN__
io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
#endif
#if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32))
io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional)
#endif
@@ -537,26 +611,44 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
#endif
glfwSetErrorCallback(prev_error_callback);
#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908)
(void)glfwGetError(nullptr);
#endif
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
if (install_callbacks)
ImGui_ImplGlfw_InstallCallbacks(window);
// Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096)
// We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves.
// FIXME: May break chaining in case user registered their own Emscripten callback?
#ifdef __EMSCRIPTEN__
emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, ImGui_ImplEmscripten_WheelCallback);
#endif
// Update monitors the first time (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784)
ImGui_ImplGlfw_UpdateMonitors();
glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback);
// Our mouse update function expect PlatformHandle to be filled for the main viewport
// Set platform dependent data in viewport
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
main_viewport->PlatformHandle = (void*)bd->Window;
#ifdef _WIN32
main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window);
#elif defined(__APPLE__)
main_viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(bd->Window);
#else
IM_UNUSED(main_viewport);
#endif
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplGlfw_InitPlatformInterface();
// Windows: register a WndProc hook so we can intercept some messages.
#ifdef _WIN32
bd->PrevWndProc = (WNDPROC)::GetWindowLongPtr((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC);
IM_ASSERT(bd->PrevWndProc != nullptr);
::SetWindowLongPtr((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);
#endif
bd->ClientApi = client_api;
return true;
}
@@ -586,12 +678,23 @@ void ImGui_ImplGlfw_Shutdown()
if (bd->InstalledCallbacks)
ImGui_ImplGlfw_RestoreCallbacks(bd->Window);
#ifdef __EMSCRIPTEN__
emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, nullptr);
#endif
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
glfwDestroyCursor(bd->MouseCursors[cursor_n]);
// Windows: restore our WndProc hook
#ifdef _WIN32
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
::SetWindowLongPtr((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->PrevWndProc);
bd->PrevWndProc = nullptr;
#endif
io.BackendPlatformName = nullptr;
io.BackendPlatformUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport);
IM_DELETE(bd);
}
@@ -601,11 +704,6 @@ static void ImGui_ImplGlfw_UpdateMouseData()
ImGuiIO& io = ImGui::GetIO();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
if (glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
{
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
return;
}
ImGuiID mouse_viewport_id = 0;
const ImVec2 mouse_pos_prev = io.MousePos;
@@ -707,7 +805,7 @@ static void ImGui_ImplGlfw_UpdateGamepads()
return;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
#if GLFW_HAS_GAMEPAD_API
#if GLFW_HAS_GAMEPAD_API && !defined(__EMSCRIPTEN__)
GLFWgamepadstate gamepad;
if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad))
return;
@@ -755,8 +853,13 @@ static void ImGui_ImplGlfw_UpdateMonitors()
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
bd->WantUpdateMonitors = false;
int monitors_count = 0;
GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count);
if (monitors_count == 0) // Preserve existing monitor list if there are none. Happens on macOS sleeping (#5683)
return;
platform_io.Monitors.resize(0);
for (int n = 0; n < monitors_count; n++)
{
@@ -764,6 +867,8 @@ static void ImGui_ImplGlfw_UpdateMonitors()
int x, y;
glfwGetMonitorPos(glfw_monitors[n], &x, &y);
const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]);
if (vid_mode == nullptr)
continue; // Failed to get Video mode (e.g. Emscripten does not support this function)
monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y);
monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height);
#if GLFW_HAS_MONITOR_WORK_AREA
@@ -781,9 +886,9 @@ static void ImGui_ImplGlfw_UpdateMonitors()
glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale);
monitor.DpiScale = x_scale;
#endif
monitor.PlatformHandle = (void*)glfw_monitors[n]; // [...] GLFW doc states: "guaranteed to be valid only until the monitor configuration changes"
platform_io.Monitors.push_back(monitor);
}
bd->WantUpdateMonitors = false;
}
void ImGui_ImplGlfw_NewFrame()
@@ -804,7 +909,10 @@ void ImGui_ImplGlfw_NewFrame()
ImGui_ImplGlfw_UpdateMonitors();
// Setup time step
// (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644)
double current_time = glfwGetTime();
if (current_time <= bd->Time)
current_time = bd->Time + 0.00001f;
io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
bd->Time = current_time;
@@ -821,15 +929,18 @@ void ImGui_ImplGlfw_NewFrame()
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data.
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
struct ImGui_ImplGlfw_ViewportData
{
GLFWwindow* Window;
bool WindowOwned;
int IgnoreWindowPosEventFrame;
int IgnoreWindowSizeEventFrame;
#ifdef _WIN32
WNDPROC PrevWndProc;
#endif
ImGui_ImplGlfw_ViewportData() { Window = nullptr; WindowOwned = false; IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; }
ImGui_ImplGlfw_ViewportData() { memset(this, 0, sizeof(*this)); IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; }
~ImGui_ImplGlfw_ViewportData() { IM_ASSERT(Window == nullptr); }
};
@@ -947,26 +1058,6 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport)
viewport->PlatformUserData = viewport->PlatformHandle = nullptr;
}
// We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs".
// In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!)
#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)
static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (msg == WM_NCHITTEST)
{
// Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL).
// The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging.
// If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in
// your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT");
if (viewport->Flags & ImGuiViewportFlags_NoInputs)
return HTTRANSPARENT;
}
return ::CallWindowProc(bd->GlfwWndProc, hWnd, msg, wParam, lParam);
}
#endif
static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport)
{
ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
@@ -984,11 +1075,9 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport)
// GLFW hack: install hook for WM_NCHITTEST message handler
#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport);
if (bd->GlfwWndProc == nullptr)
bd->GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC);
::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WndProcNoInputs);
vd->PrevWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC);
::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);
#endif
#if !GLFW_HAS_FOCUS_ON_SHOW
@@ -1173,6 +1262,63 @@ static void ImGui_ImplGlfw_ShutdownPlatformInterface()
ImGui::DestroyPlatformWindows();
}
//-----------------------------------------------------------------------------
// WndProc hook (declared here because we will need access to ImGui_ImplGlfw_ViewportData)
#ifdef _WIN32
static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()
{
LPARAM extra_info = ::GetMessageExtraInfo();
if ((extra_info & 0xFFFFFF80) == 0xFF515700)
return ImGuiMouseSource_Pen;
if ((extra_info & 0xFFFFFF80) == 0xFF515780)
return ImGuiMouseSource_TouchScreen;
return ImGuiMouseSource_Mouse;
}
static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
WNDPROC prev_wndproc = bd->PrevWndProc;
ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT");
if (viewport != NULL)
if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData)
prev_wndproc = vd->PrevWndProc;
switch (msg)
{
// GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen.
// Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently.
case WM_MOUSEMOVE: case WM_NCMOUSEMOVE:
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP:
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP:
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP:
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP:
ImGui::GetIO().AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo());
break;
// We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs".
// In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!)
#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED
case WM_NCHITTEST:
{
// Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL).
// The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging.
// If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in
// your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
if (viewport && (viewport->Flags & ImGuiViewportFlags_NoInputs))
return HTTRANSPARENT;
break;
}
#endif
}
return ::CallWindowProc(prev_wndproc, hWnd, msg, wParam, lParam);
}
#endif // #ifdef _WIN32
//-----------------------------------------------------------------------------
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif // #ifndef IMGUI_DISABLE

View File

@@ -5,6 +5,7 @@
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
@@ -15,11 +16,15 @@
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE
struct GLFWwindow;
struct GLFWmonitor;
@@ -30,13 +35,17 @@ IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool ins
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
// GLFW callbacks (installer)
// GLFW callbacks install
// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any.
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks.
IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window);
IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window);
// GLFW callbacks (individual callbacks to call if you didn't install callbacks)
// GFLW callbacks options:
// - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user)
IMGUI_IMPL_API void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows);
// GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks)
IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87
@@ -45,3 +54,5 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event);
#endif // #ifndef IMGUI_DISABLE

View File

@@ -1,921 +0,0 @@
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
// 2022-09-27: OpenGL: Added ability to '#define IMGUI_IMPL_OPENGL_DEBUG'.
// 2022-05-23: OpenGL: Reworking 2021-12-15 "Using buffer orphaning" so it only happens on Intel GPU, seems to cause problems otherwise. (#4468, #4825, #4832, #5127).
// 2022-05-13: OpenGL: Fix state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states.
// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.
// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions.
// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state.
// 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader.
// 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version.
// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater.
// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer.
// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.
// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.
// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x)
// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader.
// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader.
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a nullptr pointer.
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
//----------------------------------------
// OpenGL GLSL GLSL
// version version string
//----------------------------------------
// 2.0 110 "#version 110"
// 2.1 120 "#version 120"
// 3.0 130 "#version 130"
// 3.1 140 "#version 140"
// 3.2 150 "#version 150"
// 3.3 330 "#version 330 core"
// 4.0 400 "#version 400 core"
// 4.1 410 "#version 410 core"
// 4.2 420 "#version 410 core"
// 4.3 430 "#version 430 core"
// ES 2.0 100 "#version 100" = WebGL 1.0
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
// Clang warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#endif
// GL includes
#if defined(IMGUI_IMPL_OPENGL_ES2)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES2/gl.h> // Use GL ES 2
#else
#include <GLES2/gl2.h> // Use GL ES 2
#endif
#if defined(__EMSCRIPTEN__)
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#endif
#include <GLES2/gl2ext.h>
#endif
#elif defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
#else
#include <GLES3/gl3.h> // Use GL ES 3
#endif
#elif !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are using our own minimal custom loader based on gl3w.
// In the rest of your app/engine, you can use another loader of your choice (gl3w, glew, glad, glbinding, glext, glLoadGen, etc.).
// If you happen to be developing a new feature for this backend (imgui_impl_opengl3.cpp):
// - You may need to regenerate imgui_impl_opengl3_loader.h to add new symbols. See https://github.com/dearimgui/gl3w_stripped
// - You can temporarily use an unstripped version. See https://github.com/dearimgui/gl3w_stripped/releases
// Changes to this backend using new APIs should be accompanied by a regenerated stripped loader version.
#define IMGL3W_IMPL
#include "imgui_impl_opengl3_loader.h"
#endif
// Vertex arrays are not supported on ES2/WebGL1 unless Emscripten which uses an extension
#ifndef IMGUI_IMPL_OPENGL_ES2
#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
#elif defined(__EMSCRIPTEN__)
#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
#define glBindVertexArray glBindVertexArrayOES
#define glGenVertexArrays glGenVertexArraysOES
#define glDeleteVertexArrays glDeleteVertexArraysOES
#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES
#endif
// Desktop GL 2.0+ has glPolygonMode() which GL ES and WebGL don't have.
#ifdef GL_POLYGON_MODE
#define IMGUI_IMPL_HAS_POLYGON_MODE
#endif
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
#endif
// Desktop GL 3.3+ has glBindSampler()
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_3)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
#endif
// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
#endif
// Desktop GL use extension detection
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
#endif
// [Debugging]
//#define IMGUI_IMPL_OPENGL_DEBUG
#ifdef IMGUI_IMPL_OPENGL_DEBUG
#include <stdio.h>
#define GL_CALL(_CALL) do { _CALL; GLenum gl_err = glGetError(); if (gl_err != 0) fprintf(stderr, "GL error 0x%x returned from '%s'.\n", gl_err, #_CALL); } while (0) // Call with error check
#else
#define GL_CALL(_CALL) _CALL // Call without error check
#endif
// OpenGL Data
struct ImGui_ImplOpenGL3_Data
{
GLuint GlVersion; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
char GlslVersionString[32]; // Specified by user or detected based on compile time GL settings.
GLuint FontTexture;
GLuint ShaderHandle;
GLint AttribLocationTex; // Uniforms location
GLint AttribLocationProjMtx;
GLuint AttribLocationVtxPos; // Vertex attributes location
GLuint AttribLocationVtxUV;
GLuint AttribLocationVtxColor;
unsigned int VboHandle, ElementsHandle;
GLsizeiptr VertexBufferSize;
GLsizeiptr IndexBufferSize;
bool HasClipOrigin;
bool UseBufferSubData;
ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
}
// Forward Declarations
static void ImGui_ImplOpenGL3_InitPlatformInterface();
static void ImGui_ImplOpenGL3_ShutdownPlatformInterface();
// OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only)
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
struct ImGui_ImplOpenGL3_VtxAttribState
{
GLint Enabled, Size, Type, Normalized, Stride;
GLvoid* Ptr;
void GetState(GLint index)
{
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &Enabled);
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_SIZE, &Size);
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_TYPE, &Type);
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &Normalized);
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &Stride);
glGetVertexAttribPointerv(index, GL_VERTEX_ATTRIB_ARRAY_POINTER, &Ptr);
}
void SetState(GLint index)
{
glVertexAttribPointer(index, Size, Type, (GLboolean)Normalized, Stride, Ptr);
if (Enabled) glEnableVertexAttribArray(index); else glDisableVertexAttribArray(index);
}
};
#endif
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
// Initialize our loader
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
if (imgl3wInit() != 0)
{
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
return false;
}
#endif
// Setup backend capabilities flags
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_opengl3";
// Query for GL version (e.g. 320 for GL 3.2)
#if !defined(IMGUI_IMPL_OPENGL_ES2)
GLint major = 0;
GLint minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
if (major == 0 && minor == 0)
{
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
const char* gl_version = (const char*)glGetString(GL_VERSION);
sscanf(gl_version, "%d.%d", &major, &minor);
}
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
// Query vendor to enable glBufferSubData kludge
#ifdef _WIN32
if (const char* vendor = (const char*)glGetString(GL_VENDOR))
if (strncmp(vendor, "Intel", 5) == 0)
bd->UseBufferSubData = true;
#endif
#else
bd->GlVersion = 200; // GLES 2
#endif
#ifdef IMGUI_IMPL_OPENGL_DEBUG
printf("GL_MAJOR_VERSION = %d\nGL_MINOR_VERSION = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", major, minor, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG]
#endif
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (bd->GlVersion >= 320)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure.
if (glsl_version == nullptr)
{
#if defined(IMGUI_IMPL_OPENGL_ES2)
glsl_version = "#version 100";
#elif defined(IMGUI_IMPL_OPENGL_ES3)
glsl_version = "#version 300 es";
#elif defined(__APPLE__)
glsl_version = "#version 150";
#else
glsl_version = "#version 130";
#endif
}
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString));
strcpy(bd->GlslVersionString, glsl_version);
strcat(bd->GlslVersionString, "\n");
// Make an arbitrary GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know!
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
// Detect extensions we support
bd->HasClipOrigin = (bd->GlVersion >= 450);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
GLint num_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
for (GLint i = 0; i < num_extensions; i++)
{
const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i);
if (extension != nullptr && strcmp(extension, "GL_ARB_clip_control") == 0)
bd->HasClipOrigin = true;
}
#endif
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplOpenGL3_InitPlatformInterface();
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_ShutdownPlatformInterface();
ImGui_ImplOpenGL3_DestroyDeviceObjects();
io.BackendRendererName = nullptr;
io.BackendRendererUserData = nullptr;
IM_DELETE(bd);
}
void ImGui_ImplOpenGL3_NewFrame()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplOpenGL3_Init()?");
if (!bd->ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
if (bd->GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART);
#endif
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
#if defined(GL_CLIP_ORIGIN)
bool clip_origin_lower_left = true;
if (bd->HasClipOrigin)
{
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&current_clip_origin);
if (current_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
}
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
GL_CALL(glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height));
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
#if defined(GL_CLIP_ORIGIN)
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
#endif
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(bd->ShaderHandle);
glUniform1i(bd->AttribLocationTex, 0);
glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
if (bd->GlVersion >= 330)
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
(void)vertex_array_object;
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle));
GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle));
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos));
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV));
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor));
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)));
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)));
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)));
}
// OpenGL3 Render function.
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
// This is in order to be able to run within an OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);
GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
GLuint last_sampler; if (bd->GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; }
#endif
GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
// This is part of VAO on OpenGL 3.0+ and OpenGL ES 3.0+.
GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);
ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_pos; last_vtx_attrib_state_pos.GetState(bd->AttribLocationVtxPos);
ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_uv; last_vtx_attrib_state_uv.GetState(bd->AttribLocationVtxUV);
ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_color; last_vtx_attrib_state_color.GetState(bd->AttribLocationVtxColor);
#endif
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
#endif
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
GLboolean last_enable_primitive_restart = (bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;
#endif
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GL_CALL(glGenVertexArrays(1, &vertex_array_object));
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
// - On Intel windows drivers we got reports that regular glBufferData() led to accumulating leaks when using multi-viewports, so we started using orphaning + glBufferSubData(). (See https://github.com/ocornut/imgui/issues/4468)
// - On NVIDIA drivers we got reports that using orphaning + glBufferSubData() led to glitches when using multi-viewports.
// - OpenGL drivers are in a very sorry state in 2022, for now we are switching code path based on vendors.
const GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
const GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
if (bd->UseBufferSubData)
{
if (bd->VertexBufferSize < vtx_buffer_size)
{
bd->VertexBufferSize = vtx_buffer_size;
GL_CALL(glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, nullptr, GL_STREAM_DRAW));
}
if (bd->IndexBufferSize < idx_buffer_size)
{
bd->IndexBufferSize = idx_buffer_size;
GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, nullptr, GL_STREAM_DRAW));
}
GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data));
GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data));
}
else
{
GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW));
GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW));
}
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != nullptr)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
GL_CALL(glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)));
// Bind texture, Draw
GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()));
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (bd->GlVersion >= 320)
GL_CALL(glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset));
else
#endif
GL_CALL(glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))));
}
}
}
// Destroy the temporary VAO
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GL_CALL(glDeleteVertexArrays(1, &vertex_array_object));
#endif
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
if (bd->GlVersion >= 330)
glBindSampler(0, last_sampler);
#endif
glActiveTexture(last_active_texture);
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
last_vtx_attrib_state_pos.SetState(bd->AttribLocationVtxPos);
last_vtx_attrib_state_uv.SetState(bd->AttribLocationVtxUV);
last_vtx_attrib_state_color.SetState(bd->AttribLocationVtxColor);
#endif
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
#endif
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
(void)bd; // Not all compilation paths use this
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Build texture atlas
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
GLint last_texture;
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
GL_CALL(glGenTextures(1, &bd->FontTexture));
GL_CALL(glBindTexture(GL_TEXTURE_2D, bd->FontTexture));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
#ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
#endif
GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
// Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
// Restore state
GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture));
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->FontTexture)
{
glDeleteTextures(1, &bd->FontTexture);
io.Fonts->SetTexID(0);
bd->FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\n", desc, bd->GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\n", desc, bd->GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
// Parse GLSL version string
int glsl_version = 130;
sscanf(bd->GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision highp float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = nullptr;
const GLchar* fragment_shader = nullptr;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader };
GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr);
glCompileShader(vert_handle);
CheckShader(vert_handle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader };
GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr);
glCompileShader(frag_handle);
CheckShader(frag_handle, "fragment shader");
// Link
bd->ShaderHandle = glCreateProgram();
glAttachShader(bd->ShaderHandle, vert_handle);
glAttachShader(bd->ShaderHandle, frag_handle);
glLinkProgram(bd->ShaderHandle);
CheckProgram(bd->ShaderHandle, "shader program");
glDetachShader(bd->ShaderHandle, vert_handle);
glDetachShader(bd->ShaderHandle, frag_handle);
glDeleteShader(vert_handle);
glDeleteShader(frag_handle);
bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture");
bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx");
bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position");
bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV");
bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &bd->VboHandle);
glGenBuffers(1, &bd->ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindVertexArray(last_vertex_array);
#endif
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }
if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
}
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*)
{
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
{
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
}
ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData);
}
static void ImGui_ImplOpenGL3_InitPlatformInterface()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow;
}
static void ImGui_ImplOpenGL3_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif

View File

@@ -1,56 +0,0 @@
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// About GLSL version:
// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string.
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// Specific OpenGL ES versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
&& !defined(IMGUI_IMPL_OPENGL_ES3)
// Try to detect GLES on matching platforms
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__)
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
#else
// Otherwise imgui_impl_opengl3_loader.h will be used.
#endif
#endif

View File

@@ -1,794 +0,0 @@
//-----------------------------------------------------------------------------
// About imgui_impl_opengl3_loader.h:
//
// We embed our own OpenGL loader to not require user to provide their own or to have to use ours,
// which proved to be endless problems for users.
// Our loader is custom-generated, based on gl3w but automatically filtered to only include
// enums/functions that we use in our imgui_impl_opengl3.cpp source file in order to be small.
//
// YOU SHOULD NOT NEED TO INCLUDE/USE THIS DIRECTLY. THIS IS USED BY imgui_impl_opengl3.cpp ONLY.
// THE REST OF YOUR APP SHOULD USE A DIFFERENT GL LOADER: ANY GL LOADER OF YOUR CHOICE.
//
// IF YOU GET BUILD ERRORS IN THIS FILE (commonly macro redefinitions or function redefinitions):
// IT LIKELY MEANS THAT YOU ARE BUILDING 'imgui_impl_opengl3.cpp' OR INCUDING 'imgui_impl_opengl3_loader.h'
// IN THE SAME COMPILATION UNIT AS ONE OF YOUR FILE WHICH IS USING A THIRD-PARTY OPENGL LOADER.
// (e.g. COULD HAPPEN IF YOU ARE DOING A UNITY/JUMBO BUILD, OR INCLUDING .CPP FILES FROM OTHERS)
// YOU SHOULD NOT BUILD BOTH IN THE SAME COMPILATION UNIT.
// BUT IF YOU REALLY WANT TO, you can '#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM' and imgui_impl_opengl3.cpp
// WILL NOT BE USING OUR LOADER, AND INSTEAD EXPECT ANOTHER/YOUR LOADER TO BE AVAILABLE IN THE COMPILATION UNIT.
//
// Regenerate with:
// python gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
//
// More info:
// https://github.com/dearimgui/gl3w_stripped
// https://github.com/ocornut/imgui/issues/4445
//-----------------------------------------------------------------------------
/*
* This file was generated with gl3w_gen.py, part of imgl3w
* (hosted at https://github.com/dearimgui/gl3w_stripped)
*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __gl3w_h_
#define __gl3w_h_
// Adapted from KHR/khrplatform.h to avoid including entire file.
#ifndef __khrplatform_h_
typedef float khronos_float_t;
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef signed long long int khronos_ssize_t;
#else
typedef signed long int khronos_intptr_t;
typedef signed long int khronos_ssize_t;
#endif
#if defined(_MSC_VER) && !defined(__clang__)
typedef signed __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100)
#include <stdint.h>
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#else
typedef signed long long khronos_int64_t;
typedef unsigned long long khronos_uint64_t;
#endif
#endif // __khrplatform_h_
#ifndef __gl_glcorearb_h_
#define __gl_glcorearb_h_ 1
#ifdef __cplusplus
extern "C" {
#endif
/*
** Copyright 2013-2020 The Khronos Group Inc.
** SPDX-License-Identifier: MIT
**
** This header is generated from the Khronos OpenGL / OpenGL ES XML
** API Registry. The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** https://github.com/KhronosGroup/OpenGL-Registry
*/
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
#endif
#ifndef APIENTRY
#define APIENTRY
#endif
#ifndef APIENTRYP
#define APIENTRYP APIENTRY *
#endif
#ifndef GLAPI
#define GLAPI extern
#endif
/* glcorearb.h is for use with OpenGL core profile implementations.
** It should should be placed in the same directory as gl.h and
** included as <GL/glcorearb.h>.
**
** glcorearb.h includes only APIs in the latest OpenGL core profile
** implementation together with APIs in newer ARB extensions which
** can be supported by the core profile. It does not, and never will
** include functionality removed from the core profile, such as
** fixed-function vertex and fragment processing.
**
** Do not #include both <GL/glcorearb.h> and either of <GL/gl.h> or
** <GL/glext.h> in the same source file.
*/
/* Generated C header for:
* API: gl
* Profile: core
* Versions considered: .*
* Versions emitted: .*
* Default extensions included: glcore
* Additional extensions included: _nomatch_^
* Extensions removed: _nomatch_^
*/
#ifndef GL_VERSION_1_0
typedef void GLvoid;
typedef unsigned int GLenum;
typedef khronos_float_t GLfloat;
typedef int GLint;
typedef int GLsizei;
typedef unsigned int GLbitfield;
typedef double GLdouble;
typedef unsigned int GLuint;
typedef unsigned char GLboolean;
typedef khronos_uint8_t GLubyte;
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_FALSE 0
#define GL_TRUE 1
#define GL_TRIANGLES 0x0004
#define GL_ONE 1
#define GL_SRC_ALPHA 0x0302
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_FRONT_AND_BACK 0x0408
#define GL_POLYGON_MODE 0x0B40
#define GL_CULL_FACE 0x0B44
#define GL_DEPTH_TEST 0x0B71
#define GL_STENCIL_TEST 0x0B90
#define GL_VIEWPORT 0x0BA2
#define GL_BLEND 0x0BE2
#define GL_SCISSOR_BOX 0x0C10
#define GL_SCISSOR_TEST 0x0C11
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#define GL_PACK_ALIGNMENT 0x0D05
#define GL_TEXTURE_2D 0x0DE1
#define GL_UNSIGNED_BYTE 0x1401
#define GL_UNSIGNED_SHORT 0x1403
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406
#define GL_RGBA 0x1908
#define GL_FILL 0x1B02
#define GL_VENDOR 0x1F00
#define GL_RENDERER 0x1F01
#define GL_VERSION 0x1F02
#define GL_EXTENSIONS 0x1F03
#define GL_LINEAR 0x2601
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode);
typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask);
typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap);
typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap);
typedef void (APIENTRYP PFNGLFLUSHPROC) (void);
typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param);
typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void);
typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data);
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name);
typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap);
typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode);
GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param);
GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
GLAPI void APIENTRY glClear (GLbitfield mask);
GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
GLAPI void APIENTRY glDisable (GLenum cap);
GLAPI void APIENTRY glEnable (GLenum cap);
GLAPI void APIENTRY glFlush (void);
GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param);
GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
GLAPI GLenum APIENTRY glGetError (void);
GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data);
GLAPI const GLubyte *APIENTRY glGetString (GLenum name);
GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap);
GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
#endif
#endif /* GL_VERSION_1_0 */
#ifndef GL_VERSION_1_1
typedef khronos_float_t GLclampf;
typedef double GLclampd;
#define GL_TEXTURE_BINDING_2D 0x8069
typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices);
typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures);
typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);
GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture);
GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures);
GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
#endif
#endif /* GL_VERSION_1_1 */
#ifndef GL_VERSION_1_3
#define GL_TEXTURE0 0x84C0
#define GL_ACTIVE_TEXTURE 0x84E0
typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glActiveTexture (GLenum texture);
#endif
#endif /* GL_VERSION_1_3 */
#ifndef GL_VERSION_1_4
#define GL_BLEND_DST_RGB 0x80C8
#define GL_BLEND_SRC_RGB 0x80C9
#define GL_BLEND_DST_ALPHA 0x80CA
#define GL_BLEND_SRC_ALPHA 0x80CB
#define GL_FUNC_ADD 0x8006
typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
GLAPI void APIENTRY glBlendEquation (GLenum mode);
#endif
#endif /* GL_VERSION_1_4 */
#ifndef GL_VERSION_1_5
typedef khronos_ssize_t GLsizeiptr;
typedef khronos_intptr_t GLintptr;
#define GL_ARRAY_BUFFER 0x8892
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
#define GL_ARRAY_BUFFER_BINDING 0x8894
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
#define GL_STREAM_DRAW 0x88E0
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer);
GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers);
GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers);
GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
#endif
#endif /* GL_VERSION_1_5 */
#ifndef GL_VERSION_2_0
typedef char GLchar;
typedef khronos_int16_t GLshort;
typedef khronos_int8_t GLbyte;
typedef khronos_uint16_t GLushort;
#define GL_BLEND_EQUATION_RGB 0x8009
#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
#define GL_BLEND_EQUATION_ALPHA 0x883D
#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPILE_STATUS 0x8B81
#define GL_LINK_STATUS 0x8B82
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_CURRENT_PROGRAM 0x8B8D
#define GL_UPPER_LEFT 0x8CA2
typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha);
typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader);
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void);
typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type);
typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader);
typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer);
typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha);
GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader);
GLAPI void APIENTRY glCompileShader (GLuint shader);
GLAPI GLuint APIENTRY glCreateProgram (void);
GLAPI GLuint APIENTRY glCreateShader (GLenum type);
GLAPI void APIENTRY glDeleteProgram (GLuint program);
GLAPI void APIENTRY glDeleteShader (GLuint shader);
GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader);
GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index);
GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index);
GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name);
GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name);
GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer);
GLAPI void APIENTRY glLinkProgram (GLuint program);
GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
GLAPI void APIENTRY glUseProgram (GLuint program);
GLAPI void APIENTRY glUniform1i (GLint location, GLint v0);
GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
#endif
#endif /* GL_VERSION_2_0 */
#ifndef GL_VERSION_3_0
typedef khronos_uint16_t GLhalf;
#define GL_MAJOR_VERSION 0x821B
#define GL_MINOR_VERSION 0x821C
#define GL_NUM_EXTENSIONS 0x821D
#define GL_FRAMEBUFFER_SRGB 0x8DB9
#define GL_VERTEX_ARRAY_BINDING 0x85B5
typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data);
typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data);
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index);
typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array);
typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);
typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index);
GLAPI void APIENTRY glBindVertexArray (GLuint array);
GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays);
GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays);
#endif
#endif /* GL_VERSION_3_0 */
#ifndef GL_VERSION_3_1
#define GL_VERSION_3_1 1
#define GL_PRIMITIVE_RESTART 0x8F9D
#endif /* GL_VERSION_3_1 */
#ifndef GL_VERSION_3_2
#define GL_VERSION_3_2 1
typedef struct __GLsync *GLsync;
typedef khronos_uint64_t GLuint64;
typedef khronos_int64_t GLint64;
typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
#endif
#endif /* GL_VERSION_3_2 */
#ifndef GL_VERSION_3_3
#define GL_VERSION_3_3 1
#define GL_SAMPLER_BINDING 0x8919
typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler);
#endif
#endif /* GL_VERSION_3_3 */
#ifndef GL_VERSION_4_1
typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data);
typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data);
#endif /* GL_VERSION_4_1 */
#ifndef GL_VERSION_4_3
typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
#endif /* GL_VERSION_4_3 */
#ifndef GL_VERSION_4_5
#define GL_CLIP_ORIGIN 0x935C
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param);
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param);
#endif /* GL_VERSION_4_5 */
#ifndef GL_ARB_bindless_texture
typedef khronos_uint64_t GLuint64EXT;
#endif /* GL_ARB_bindless_texture */
#ifndef GL_ARB_cl_event
struct _cl_context;
struct _cl_event;
#endif /* GL_ARB_cl_event */
#ifndef GL_ARB_clip_control
#define GL_ARB_clip_control 1
#endif /* GL_ARB_clip_control */
#ifndef GL_ARB_debug_output
typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
#endif /* GL_ARB_debug_output */
#ifndef GL_EXT_EGL_image_storage
typedef void *GLeglImageOES;
#endif /* GL_EXT_EGL_image_storage */
#ifndef GL_EXT_direct_state_access
typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params);
typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params);
typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params);
typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param);
typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param);
#endif /* GL_EXT_direct_state_access */
#ifndef GL_NV_draw_vulkan_image
typedef void (APIENTRY *GLVULKANPROCNV)(void);
#endif /* GL_NV_draw_vulkan_image */
#ifndef GL_NV_gpu_shader5
typedef khronos_int64_t GLint64EXT;
#endif /* GL_NV_gpu_shader5 */
#ifndef GL_NV_vertex_buffer_unified_memory
typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result);
#endif /* GL_NV_vertex_buffer_unified_memory */
#ifdef __cplusplus
}
#endif
#endif
#ifndef GL3W_API
#define GL3W_API
#endif
#ifndef __gl_h_
#define __gl_h_
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define GL3W_OK 0
#define GL3W_ERROR_INIT -1
#define GL3W_ERROR_LIBRARY_OPEN -2
#define GL3W_ERROR_OPENGL_VERSION -3
typedef void (*GL3WglProc)(void);
typedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc);
/* gl3w api */
GL3W_API int imgl3wInit(void);
GL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc);
GL3W_API int imgl3wIsSupported(int major, int minor);
GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
/* gl3w internal state */
union GL3WProcs {
GL3WglProc ptr[58];
struct {
PFNGLACTIVETEXTUREPROC ActiveTexture;
PFNGLATTACHSHADERPROC AttachShader;
PFNGLBINDBUFFERPROC BindBuffer;
PFNGLBINDSAMPLERPROC BindSampler;
PFNGLBINDTEXTUREPROC BindTexture;
PFNGLBINDVERTEXARRAYPROC BindVertexArray;
PFNGLBLENDEQUATIONPROC BlendEquation;
PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate;
PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate;
PFNGLBUFFERDATAPROC BufferData;
PFNGLBUFFERSUBDATAPROC BufferSubData;
PFNGLCLEARPROC Clear;
PFNGLCLEARCOLORPROC ClearColor;
PFNGLCOMPILESHADERPROC CompileShader;
PFNGLCREATEPROGRAMPROC CreateProgram;
PFNGLCREATESHADERPROC CreateShader;
PFNGLDELETEBUFFERSPROC DeleteBuffers;
PFNGLDELETEPROGRAMPROC DeleteProgram;
PFNGLDELETESHADERPROC DeleteShader;
PFNGLDELETETEXTURESPROC DeleteTextures;
PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays;
PFNGLDETACHSHADERPROC DetachShader;
PFNGLDISABLEPROC Disable;
PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray;
PFNGLDRAWELEMENTSPROC DrawElements;
PFNGLDRAWELEMENTSBASEVERTEXPROC DrawElementsBaseVertex;
PFNGLENABLEPROC Enable;
PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray;
PFNGLFLUSHPROC Flush;
PFNGLGENBUFFERSPROC GenBuffers;
PFNGLGENTEXTURESPROC GenTextures;
PFNGLGENVERTEXARRAYSPROC GenVertexArrays;
PFNGLGETATTRIBLOCATIONPROC GetAttribLocation;
PFNGLGETERRORPROC GetError;
PFNGLGETINTEGERVPROC GetIntegerv;
PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog;
PFNGLGETPROGRAMIVPROC GetProgramiv;
PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog;
PFNGLGETSHADERIVPROC GetShaderiv;
PFNGLGETSTRINGPROC GetString;
PFNGLGETSTRINGIPROC GetStringi;
PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation;
PFNGLGETVERTEXATTRIBPOINTERVPROC GetVertexAttribPointerv;
PFNGLGETVERTEXATTRIBIVPROC GetVertexAttribiv;
PFNGLISENABLEDPROC IsEnabled;
PFNGLLINKPROGRAMPROC LinkProgram;
PFNGLPIXELSTOREIPROC PixelStorei;
PFNGLPOLYGONMODEPROC PolygonMode;
PFNGLREADPIXELSPROC ReadPixels;
PFNGLSCISSORPROC Scissor;
PFNGLSHADERSOURCEPROC ShaderSource;
PFNGLTEXIMAGE2DPROC TexImage2D;
PFNGLTEXPARAMETERIPROC TexParameteri;
PFNGLUNIFORM1IPROC Uniform1i;
PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv;
PFNGLUSEPROGRAMPROC UseProgram;
PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer;
PFNGLVIEWPORTPROC Viewport;
} gl;
};
GL3W_API extern union GL3WProcs imgl3wProcs;
/* OpenGL functions */
#define glActiveTexture imgl3wProcs.gl.ActiveTexture
#define glAttachShader imgl3wProcs.gl.AttachShader
#define glBindBuffer imgl3wProcs.gl.BindBuffer
#define glBindSampler imgl3wProcs.gl.BindSampler
#define glBindTexture imgl3wProcs.gl.BindTexture
#define glBindVertexArray imgl3wProcs.gl.BindVertexArray
#define glBlendEquation imgl3wProcs.gl.BlendEquation
#define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate
#define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate
#define glBufferData imgl3wProcs.gl.BufferData
#define glBufferSubData imgl3wProcs.gl.BufferSubData
#define glClear imgl3wProcs.gl.Clear
#define glClearColor imgl3wProcs.gl.ClearColor
#define glCompileShader imgl3wProcs.gl.CompileShader
#define glCreateProgram imgl3wProcs.gl.CreateProgram
#define glCreateShader imgl3wProcs.gl.CreateShader
#define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers
#define glDeleteProgram imgl3wProcs.gl.DeleteProgram
#define glDeleteShader imgl3wProcs.gl.DeleteShader
#define glDeleteTextures imgl3wProcs.gl.DeleteTextures
#define glDeleteVertexArrays imgl3wProcs.gl.DeleteVertexArrays
#define glDetachShader imgl3wProcs.gl.DetachShader
#define glDisable imgl3wProcs.gl.Disable
#define glDisableVertexAttribArray imgl3wProcs.gl.DisableVertexAttribArray
#define glDrawElements imgl3wProcs.gl.DrawElements
#define glDrawElementsBaseVertex imgl3wProcs.gl.DrawElementsBaseVertex
#define glEnable imgl3wProcs.gl.Enable
#define glEnableVertexAttribArray imgl3wProcs.gl.EnableVertexAttribArray
#define glFlush imgl3wProcs.gl.Flush
#define glGenBuffers imgl3wProcs.gl.GenBuffers
#define glGenTextures imgl3wProcs.gl.GenTextures
#define glGenVertexArrays imgl3wProcs.gl.GenVertexArrays
#define glGetAttribLocation imgl3wProcs.gl.GetAttribLocation
#define glGetError imgl3wProcs.gl.GetError
#define glGetIntegerv imgl3wProcs.gl.GetIntegerv
#define glGetProgramInfoLog imgl3wProcs.gl.GetProgramInfoLog
#define glGetProgramiv imgl3wProcs.gl.GetProgramiv
#define glGetShaderInfoLog imgl3wProcs.gl.GetShaderInfoLog
#define glGetShaderiv imgl3wProcs.gl.GetShaderiv
#define glGetString imgl3wProcs.gl.GetString
#define glGetStringi imgl3wProcs.gl.GetStringi
#define glGetUniformLocation imgl3wProcs.gl.GetUniformLocation
#define glGetVertexAttribPointerv imgl3wProcs.gl.GetVertexAttribPointerv
#define glGetVertexAttribiv imgl3wProcs.gl.GetVertexAttribiv
#define glIsEnabled imgl3wProcs.gl.IsEnabled
#define glLinkProgram imgl3wProcs.gl.LinkProgram
#define glPixelStorei imgl3wProcs.gl.PixelStorei
#define glPolygonMode imgl3wProcs.gl.PolygonMode
#define glReadPixels imgl3wProcs.gl.ReadPixels
#define glScissor imgl3wProcs.gl.Scissor
#define glShaderSource imgl3wProcs.gl.ShaderSource
#define glTexImage2D imgl3wProcs.gl.TexImage2D
#define glTexParameteri imgl3wProcs.gl.TexParameteri
#define glUniform1i imgl3wProcs.gl.Uniform1i
#define glUniformMatrix4fv imgl3wProcs.gl.UniformMatrix4fv
#define glUseProgram imgl3wProcs.gl.UseProgram
#define glVertexAttribPointer imgl3wProcs.gl.VertexAttribPointer
#define glViewport imgl3wProcs.gl.Viewport
#ifdef __cplusplus
}
#endif
#endif
#ifdef IMGL3W_IMPL
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
static HMODULE libgl;
typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR);
static GL3WglGetProcAddr wgl_get_proc_address;
static int open_libgl(void)
{
libgl = LoadLibraryA("opengl32.dll");
if (!libgl)
return GL3W_ERROR_LIBRARY_OPEN;
wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, "wglGetProcAddress");
return GL3W_OK;
}
static void close_libgl(void) { FreeLibrary(libgl); }
static GL3WglProc get_proc(const char *proc)
{
GL3WglProc res;
res = (GL3WglProc)wgl_get_proc_address(proc);
if (!res)
res = (GL3WglProc)GetProcAddress(libgl, proc);
return res;
}
#elif defined(__APPLE__)
#include <dlfcn.h>
static void *libgl;
static int open_libgl(void)
{
libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL);
if (!libgl)
return GL3W_ERROR_LIBRARY_OPEN;
return GL3W_OK;
}
static void close_libgl(void) { dlclose(libgl); }
static GL3WglProc get_proc(const char *proc)
{
GL3WglProc res;
*(void **)(&res) = dlsym(libgl, proc);
return res;
}
#else
#include <dlfcn.h>
static void *libgl;
static GL3WglProc (*glx_get_proc_address)(const GLubyte *);
static int open_libgl(void)
{
libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL);
if (!libgl)
return GL3W_ERROR_LIBRARY_OPEN;
*(void **)(&glx_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB");
return GL3W_OK;
}
static void close_libgl(void) { dlclose(libgl); }
static GL3WglProc get_proc(const char *proc)
{
GL3WglProc res;
res = glx_get_proc_address((const GLubyte *)proc);
if (!res)
*(void **)(&res) = dlsym(libgl, proc);
return res;
}
#endif
static struct { int major, minor; } version;
static int parse_version(void)
{
if (!glGetIntegerv)
return GL3W_ERROR_INIT;
glGetIntegerv(GL_MAJOR_VERSION, &version.major);
glGetIntegerv(GL_MINOR_VERSION, &version.minor);
if (version.major < 3)
return GL3W_ERROR_OPENGL_VERSION;
return GL3W_OK;
}
static void load_procs(GL3WGetProcAddressProc proc);
int imgl3wInit(void)
{
int res = open_libgl();
if (res)
return res;
atexit(close_libgl);
return imgl3wInit2(get_proc);
}
int imgl3wInit2(GL3WGetProcAddressProc proc)
{
load_procs(proc);
return parse_version();
}
int imgl3wIsSupported(int major, int minor)
{
if (major < 3)
return 0;
if (version.major == major)
return version.minor >= minor;
return version.major >= major;
}
GL3WglProc imgl3wGetProcAddress(const char *proc) { return get_proc(proc); }
static const char *proc_names[] = {
"glActiveTexture",
"glAttachShader",
"glBindBuffer",
"glBindSampler",
"glBindTexture",
"glBindVertexArray",
"glBlendEquation",
"glBlendEquationSeparate",
"glBlendFuncSeparate",
"glBufferData",
"glBufferSubData",
"glClear",
"glClearColor",
"glCompileShader",
"glCreateProgram",
"glCreateShader",
"glDeleteBuffers",
"glDeleteProgram",
"glDeleteShader",
"glDeleteTextures",
"glDeleteVertexArrays",
"glDetachShader",
"glDisable",
"glDisableVertexAttribArray",
"glDrawElements",
"glDrawElementsBaseVertex",
"glEnable",
"glEnableVertexAttribArray",
"glFlush",
"glGenBuffers",
"glGenTextures",
"glGenVertexArrays",
"glGetAttribLocation",
"glGetError",
"glGetIntegerv",
"glGetProgramInfoLog",
"glGetProgramiv",
"glGetShaderInfoLog",
"glGetShaderiv",
"glGetString",
"glGetStringi",
"glGetUniformLocation",
"glGetVertexAttribPointerv",
"glGetVertexAttribiv",
"glIsEnabled",
"glLinkProgram",
"glPixelStorei",
"glPolygonMode",
"glReadPixels",
"glScissor",
"glShaderSource",
"glTexImage2D",
"glTexParameteri",
"glUniform1i",
"glUniformMatrix4fv",
"glUseProgram",
"glVertexAttribPointer",
"glViewport",
};
GL3W_API union GL3WProcs imgl3wProcs;
static void load_procs(GL3WGetProcAddressProc proc)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(proc_names); i++)
imgl3wProcs.ptr[i] = proc(proc_names[i]);
}
#ifdef __cplusplus
}
#endif
#endif

1883
src/thirdparty/imgui/imgui_impl_vulkan.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

170
src/thirdparty/imgui/imgui_impl_vulkan.h vendored Normal file
View File

@@ -0,0 +1,170 @@
// dear imgui: Renderer Backend for Vulkan
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [x] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport).
// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
// See imgui_impl_vulkan.cpp file for details.
// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
// You will use those if you want to use this rendering backend in your engine/app.
// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
// Read comments in imgui_impl_vulkan.h.
#pragma once
#ifndef IMGUI_DISABLE
#include "imgui.h" // IMGUI_IMPL_API
// [Configuration] in order to use a custom Vulkan function loader:
// (1) You'll need to disable default Vulkan function prototypes.
// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag.
// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit:
// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file
// - Or as a compilation flag in your build system
// - Or uncomment here (not recommended because you'd be modifying imgui sources!)
// - Do not simply add it in a .cpp file!
// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function.
// If you have no idea what this is, leave it alone!
//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES
// Vulkan includes
#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES)
#define VK_NO_PROTOTYPES
#endif
#include <vulkan/vulkan.h>
// Initialization data, for ImGui_ImplVulkan_Init()
// [Please zero-clear before use!]
struct ImGui_ImplVulkan_InitInfo
{
VkInstance Instance;
VkPhysicalDevice PhysicalDevice;
VkDevice Device;
uint32_t QueueFamily;
VkQueue Queue;
VkPipelineCache PipelineCache;
VkDescriptorPool DescriptorPool;
uint32_t Subpass;
uint32_t MinImageCount; // >= 2
uint32_t ImageCount; // >= MinImageCount
VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT (0 -> default to VK_SAMPLE_COUNT_1_BIT)
// Dynamic Rendering (Optional)
bool UseDynamicRendering; // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3.
VkFormat ColorAttachmentFormat; // Required for dynamic rendering
// Allocation, Debugging
const VkAllocationCallbacks* Allocator;
void (*CheckVkResultFn)(VkResult err);
};
// Called by user code
IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass);
IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown();
IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame();
IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE);
IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontsTexture();
IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated)
// Register a texture (VkDescriptorSet == ImTextureID)
// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem
// Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions.
IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout);
IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);
// Optional: load Vulkan functions with a custom function loader
// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES
IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr);
//-------------------------------------------------------------------------
// Internal / Miscellaneous Vulkan Helpers
// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
//-------------------------------------------------------------------------
// You probably do NOT need to use or care about those functions.
// Those functions only exist because:
// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files.
// 2) the multi-viewport / platform window implementation needs them internally.
// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings,
// but it is too much code to duplicate everywhere so we exceptionally expose them.
//
// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.).
// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work.
// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions)
//-------------------------------------------------------------------------
struct ImGui_ImplVulkanH_Frame;
struct ImGui_ImplVulkanH_Window;
// Helpers
IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count);
IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator);
IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space);
IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count);
IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode);
// Helper structure to hold the data needed by one rendering frame
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
// [Please zero-clear before use!]
struct ImGui_ImplVulkanH_Frame
{
VkCommandPool CommandPool;
VkCommandBuffer CommandBuffer;
VkFence Fence;
VkImage Backbuffer;
VkImageView BackbufferView;
VkFramebuffer Framebuffer;
};
struct ImGui_ImplVulkanH_FrameSemaphores
{
VkSemaphore ImageAcquiredSemaphore;
VkSemaphore RenderCompleteSemaphore;
};
// Helper structure to hold the data needed by one rendering context into one OS window
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
struct ImGui_ImplVulkanH_Window
{
int Width;
int Height;
VkSwapchainKHR Swapchain;
VkSurfaceKHR Surface;
VkSurfaceFormatKHR SurfaceFormat;
VkPresentModeKHR PresentMode;
VkRenderPass RenderPass;
VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo
bool UseDynamicRendering;
bool ClearEnable;
VkClearValue ClearValue;
uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount)
uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count)
uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data)
ImGui_ImplVulkanH_Frame* Frames;
ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores;
ImGui_ImplVulkanH_Window()
{
memset((void*)this, 0, sizeof(*this));
PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this.
ClearEnable = true;
}
};
#endif // #ifndef IMGUI_DISABLE

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,9 @@
// This is a slightly modified version of stb_textedit.h 1.14.
// Those changes would need to be pushed into nothings/stb:
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
// Grep for [DEAR IMGUI] to find the changes.
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
// stb_textedit.h - v1.14 - public domain - Sean Barrett
// Development of this library was sponsored by RAD Game Tools
@@ -29,7 +31,7 @@
// DEPENDENCIES
//
// Uses the C runtime function 'memmove', which you can override
// by defining STB_TEXTEDIT_memmove before the implementation.
// by defining IMSTB_TEXTEDIT_memmove before the implementation.
// Uses no other functions. Performs no runtime allocations.
//
//
@@ -273,8 +275,8 @@
////
////
#ifndef INCLUDE_STB_TEXTEDIT_H
#define INCLUDE_STB_TEXTEDIT_H
#ifndef INCLUDE_IMSTB_TEXTEDIT_H
#define INCLUDE_IMSTB_TEXTEDIT_H
////////////////////////////////////////////////////////////////////////
//
@@ -285,33 +287,33 @@
// and undo state.
//
#ifndef STB_TEXTEDIT_UNDOSTATECOUNT
#define STB_TEXTEDIT_UNDOSTATECOUNT 99
#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
#endif
#ifndef STB_TEXTEDIT_UNDOCHARCOUNT
#define STB_TEXTEDIT_UNDOCHARCOUNT 999
#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
#endif
#ifndef STB_TEXTEDIT_CHARTYPE
#define STB_TEXTEDIT_CHARTYPE int
#ifndef IMSTB_TEXTEDIT_CHARTYPE
#define IMSTB_TEXTEDIT_CHARTYPE int
#endif
#ifndef STB_TEXTEDIT_POSITIONTYPE
#define STB_TEXTEDIT_POSITIONTYPE int
#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
#define IMSTB_TEXTEDIT_POSITIONTYPE int
#endif
typedef struct
{
// private data
STB_TEXTEDIT_POSITIONTYPE where;
STB_TEXTEDIT_POSITIONTYPE insert_length;
STB_TEXTEDIT_POSITIONTYPE delete_length;
IMSTB_TEXTEDIT_POSITIONTYPE where;
IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
int char_storage;
} StbUndoRecord;
typedef struct
{
// private data
StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
short undo_point, redo_point;
int undo_char_point, redo_char_point;
} StbUndoState;
@@ -370,7 +372,7 @@ typedef struct
float ymin,ymax; // height of row above and below baseline
int num_chars;
} StbTexteditRow;
#endif //INCLUDE_STB_TEXTEDIT_H
#endif //INCLUDE_IMSTB_TEXTEDIT_H
////////////////////////////////////////////////////////////////////////////
@@ -383,11 +385,11 @@ typedef struct
// implementation isn't include-guarded, since it might have indirectly
// included just the "header" portion
#ifdef STB_TEXTEDIT_IMPLEMENTATION
#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
#ifndef STB_TEXTEDIT_memmove
#ifndef IMSTB_TEXTEDIT_memmove
#include <string.h>
#define STB_TEXTEDIT_memmove memmove
#define IMSTB_TEXTEDIT_memmove memmove
#endif
@@ -397,7 +399,7 @@ typedef struct
//
// traverse the layout to locate the nearest character to a display position
static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
{
StbTexteditRow r;
int n = STB_TEXTEDIT_STRINGLEN(str);
@@ -457,7 +459,7 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
}
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
// goes off the top or bottom of the text
@@ -475,7 +477,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
}
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
int p = 0;
@@ -501,11 +503,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
//
// forward declarations
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
typedef struct
{
@@ -517,36 +519,21 @@ typedef struct
// find the x/y location of a character, and remember info about the previous row in
// case we get a move-up event (for page up, we'll have to rescan)
static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line)
static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
{
StbTexteditRow r;
int prev_start = 0;
int z = STB_TEXTEDIT_STRINGLEN(str);
int i=0, first;
if (n == z) {
// if it's at the end, then find the last line -- simpler than trying to
// explicitly handle this case in the regular code
if (single_line) {
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
find->y = 0;
find->first_char = 0;
find->length = z;
find->height = r.ymax - r.ymin;
find->x = r.x1;
} else {
find->y = 0;
find->x = 0;
find->height = 1;
while (i < z) {
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
prev_start = i;
i += r.num_chars;
}
find->first_char = i;
find->length = 0;
find->prev_first = prev_start;
}
if (n == z && single_line) {
// special case if it's at the end (may not be needed?)
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
find->y = 0;
find->first_char = 0;
find->length = z;
find->height = r.ymax - r.ymin;
find->x = r.x1;
return;
}
@@ -557,9 +544,16 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
if (n < i + r.num_chars)
break;
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
break; // [DEAR IMGUI]
prev_start = i;
i += r.num_chars;
find->y += r.baseline_y_delta;
if (i == z) // [DEAR IMGUI]
{
r.num_chars = 0; // [DEAR IMGUI]
break; // [DEAR IMGUI]
}
}
find->first_char = first = i;
@@ -576,7 +570,7 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
// make the selection/cursor state valid if client altered the string
static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
int n = STB_TEXTEDIT_STRINGLEN(str);
if (STB_TEXT_HAS_SELECTION(state)) {
@@ -590,7 +584,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
}
// delete characters while updating undo
static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
{
stb_text_makeundo_delete(str, state, where, len);
STB_TEXTEDIT_DELETECHARS(str, where, len);
@@ -598,7 +592,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
}
// delete the section
static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
stb_textedit_clamp(str, state);
if (STB_TEXT_HAS_SELECTION(state)) {
@@ -635,7 +629,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state)
}
// move cursor to last character of selection
static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_sortselection(state);
@@ -647,13 +641,13 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
}
#ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
}
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{
--c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) )
@@ -668,7 +662,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
#endif
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{
const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character
@@ -695,7 +689,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
}
// API cut: delete selection
static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_delete_selection(str,state); // implicitly clamps
@@ -706,7 +700,7 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
}
// API paste: replace existing selection with passed-in text
static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
{
// if there's a selection, the paste should delete it
stb_textedit_clamp(str, state);
@@ -727,14 +721,14 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
#endif
// API key: process a keyboard input
static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
{
retry:
switch (key) {
default: {
int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) {
STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
// can't add newline in single-line mode
if (c == '\n' && state->single_line)
@@ -899,8 +893,8 @@ retry:
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
#endif
x += dx;
@@ -961,8 +955,8 @@ retry:
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
#endif
x += dx;
@@ -1119,8 +1113,8 @@ retry:
static void stb_textedit_flush_redo(StbUndoState *state)
{
state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
}
// discard the oldest entry in the undo list
@@ -1132,13 +1126,13 @@ static void stb_textedit_discard_undo(StbUndoState *state)
int n = state->undo_rec[0].insert_length, i;
// delete n characters from all other records
state->undo_char_point -= n;
STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
for (i=0; i < state->undo_point; ++i)
if (state->undo_rec[i].char_storage >= 0)
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
}
--state->undo_point;
STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
}
}
@@ -1148,7 +1142,7 @@ static void stb_textedit_discard_undo(StbUndoState *state)
// fill up even though the undo buffer didn't
static void stb_textedit_discard_redo(StbUndoState *state)
{
int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;
int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
if (state->redo_point <= k) {
// if the k'th undo state has characters, clean those up
@@ -1156,7 +1150,7 @@ static void stb_textedit_discard_redo(StbUndoState *state)
int n = state->undo_rec[k].insert_length, i;
// move the remaining redo character data to the end of the buffer
state->redo_char_point += n;
STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
// adjust the position of all the other records to account for above memmove
for (i=state->redo_point; i < k; ++i)
if (state->undo_rec[i].char_storage >= 0)
@@ -1164,12 +1158,12 @@ static void stb_textedit_discard_redo(StbUndoState *state)
}
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
// [DEAR IMGUI]
size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
// now move redo_point to point to the new one
++state->redo_point;
@@ -1183,32 +1177,32 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch
// if we have no free records, we have to make room, by sliding the
// existing records down
if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
stb_textedit_discard_undo(state);
// if the characters to store won't possibly fit in the buffer, we can't undo
if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) {
if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
state->undo_point = 0;
state->undo_char_point = 0;
return NULL;
}
// if we don't have enough free characters in the buffer, we have to make room
while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT)
while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
stb_textedit_discard_undo(state);
return &state->undo_rec[state->undo_point++];
}
static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
{
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
if (r == NULL)
return NULL;
r->where = pos;
r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;
r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;
r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
if (insert_len == 0) {
r->char_storage = -1;
@@ -1220,7 +1214,7 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos,
}
}
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
StbUndoState *s = &state->undostate;
StbUndoRecord u, *r;
@@ -1247,7 +1241,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// characters stored for *undoing* don't leave room for redo
// if the last is true, we have to bail
if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) {
if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
// the undo records take up too much character space; there's no space to store the redo characters
r->insert_length = 0;
} else {
@@ -1256,7 +1250,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// there's definitely room to store the characters eventually
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
// should never happen:
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return;
// there's currently not enough room, so discard a redo record
stb_textedit_discard_redo(s);
@@ -1288,11 +1282,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
s->redo_point--;
}
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
StbUndoState *s = &state->undostate;
StbUndoRecord *u, r;
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return;
// we need to do two things: apply the redo record, and create an undo record
@@ -1344,20 +1338,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le
stb_text_createundo(&state->undostate, where, 0, length);
}
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
{
int i;
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
if (p) {
for (i=0; i < length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
}
}
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
{
int i;
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
if (p) {
for (i=0; i < old_length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
@@ -1369,8 +1363,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
{
state->undostate.undo_point = 0;
state->undostate.undo_char_point = 0;
state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
state->select_end = state->select_start = 0;
state->cursor = 0;
state->has_preferred_x = 0;
@@ -1393,16 +1387,16 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
#pragma GCC diagnostic ignored "-Wcast-qual"
#endif
static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
{
return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);
return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
}
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#endif//STB_TEXTEDIT_IMPLEMENTATION
#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
/*
------------------------------------------------------------------------------

View File

@@ -2008,7 +2008,7 @@ static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int gly
start = end;
}
}
if (fdselector == -1) stbtt__new_buf(NULL, 0);
if (fdselector == -1) return stbtt__new_buf(NULL, 0); // [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422
return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));
}

View File

@@ -0,0 +1,6 @@
#!/bin/bash
## -V: create SPIR-V binary
## -x: save binary output as text-based 32-bit hexadecimal numbers
## -o: output file
glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag
glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert

View File

@@ -0,0 +1,14 @@
#version 450 core
layout(location = 0) out vec4 fColor;
layout(set=0, binding=0) uniform sampler2D sTexture;
layout(location = 0) in struct {
vec4 Color;
vec2 UV;
} In;
void main()
{
fColor = In.Color * texture(sTexture, In.UV.st);
}

View File

@@ -0,0 +1,25 @@
#version 450 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aUV;
layout(location = 2) in vec4 aColor;
layout(push_constant) uniform uPushConstant {
vec2 uScale;
vec2 uTranslate;
} pc;
out gl_PerVertex {
vec4 gl_Position;
};
layout(location = 0) out struct {
vec4 Color;
vec2 UV;
} Out;
void main()
{
Out.Color = aColor;
Out.UV = aUV;
gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1);
}

View File

@@ -1,18 +1,27 @@
#include "window.hpp"
#include "graphics/device.hpp"
#include "gui/style.hpp"
#include <graphics.hpp>
#include <version.hpp>
#include "yolo/yolo.hpp"
#include <map>
#include <optional>
#include <set>
#include <vector>
#include <vulkan/vulkan_core.h>
namespace inferno::graphics {
static GLFWwindow* Window;
static WINDOW_MODE WinMode = WINDOW_MODE::WIN_MODE_DEFAULT;
static KeyCallback UserKeyCallback = nullptr;
static int Width, Height;
static const char* GlslVersion;
static GLFWwindow* Window;
void glfwKeyCallback(GLFWwindow* window, int key, int scancode,
int action, int mods)
void glfwKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (UserKeyCallback != nullptr) {
UserKeyCallback(key, scancode, action, mods);
@@ -24,79 +33,14 @@ void glfwErrorCallback(int error, const char* description)
yolo::error("[GLFW {}] {}", error, description);
}
void setupGLFW(std::string title)
void setupWindow(std::string title)
{
glfwSetErrorCallback(glfwErrorCallback);
if (!glfwInit())
throw std::runtime_error("Failed to initialize GLFW");
glfwInit();
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100
GlslVersion = "#version 100";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#elif defined(__APPLE__)
// GL 3.2 + GLSL 150
GlslVersion = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#else
// GL 4.5 + GLSL 450
GlslVersion = "#version 450";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
#endif
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
// Create window with graphics context
Window = glfwCreateWindow(1280, 720, title.c_str(), NULL, NULL);
if (Window == NULL)
throw std::runtime_error("Could not create window");
glfwMakeContextCurrent(Window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
glfwSwapInterval(1); // Enable vsync
yolo::info("GLFW {} initialized", glfwGetVersionString());
yolo::info("OpenGL {} initialized", glGetString(GL_VERSION));
yolo::info("GLSL {} initialized", glGetString(GL_SHADING_LANGUAGE_VERSION));
yolo::info("INFERNO HART Running on ", glGetString(GL_RENDERER));
}
void setupImGui()
{
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
// Setup Dear ImGui style
ImGui::StyleColorsDark();
ImGuiIO& io = ImGui::GetIO();
(void)io;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME: THIS CURRENTLY DOESN'T
// WORK AS EXPECTED. DON'T USE IN
// USER APP!
io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME:
// io.ConfigDockingWithShift
// = true;
// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForOpenGL(Window, true);
ImGui_ImplOpenGL3_Init(GlslVersion);
inferno::SetupImGuiStyle2();
}
void shutdownImGui()
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
Window = glfwCreateWindow(Width, Height, title.c_str(), nullptr, nullptr);
}
void shutdownGLFW()
@@ -109,21 +53,26 @@ void window_create(std::string title, int width, int height)
{
Width = width;
Height = height;
setupGLFW(title);
setupWindow(title);
glfwSetKeyCallback(Window, glfwKeyCallback);
setupImGui();
}
void window_cleanup()
void window_init_device(GraphicsDevice* device, GLFWframebuffersizefun resizeCallback)
{
shutdownImGui();
shutdownGLFW();
if (glfwCreateWindowSurface(
device->VulkanInstance, Window, nullptr, &device->VulkanSurface)
!= VK_SUCCESS) {
yolo::error("failed to create window surface!");
exit(1);
}
glfwSetWindowUserPointer(Window, device);
glfwSetFramebufferSizeCallback(Window, (GLFWframebuffersizefun)resizeCallback);
}
void window_set_title(std::string title)
{
glfwSetWindowTitle(Window, title.c_str());
}
void window_cleanup() { shutdownGLFW(); }
void window_set_title(std::string title) { glfwSetWindowTitle(Window, title.c_str()); }
void window_set_size(int w, int h)
{
@@ -143,9 +92,9 @@ GLFWwindow* window_get_glfw_window() { return Window; }
void window_set_mode(WINDOW_MODE mode)
{
WinMode = mode;
if (mode == WINDOW_MODE::WIN_MODE_FPS) {
glfwSetInputMode(Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
}
// if (mode == WINDOW_MODE::WIN_MODE_FPS) {
// glfwSetInputMode(Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
// }
}
void window_set_key_callback(KeyCallback callback) { UserKeyCallback = callback; }
@@ -155,39 +104,23 @@ KeyCallback window_get_key_callback() { return UserKeyCallback; }
bool window_new_frame()
{
glfwPollEvents();
if (WinMode == WIN_MODE_FPS) {
glfwSetInputMode(Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
glfwSetCursorPos(Window, (double)Width / 2, (double)Height / 2);
}
// if (WinMode == WIN_MODE_FPS) {
// glfwSetInputMode(Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
// glfwSetCursorPos(Window, (double)Width / 2, (double)Height / 2);
// }
if (glfwWindowShouldClose(Window)) {
return false;
}
glfwSetInputMode(Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glfwGetWindowSize(Window, &Width, &Height);
glClearColor(0.1, 0.1, 0.1, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGui::Begin("main", nullptr, WINDOW_FLAGS);
ImGui::SetWindowPos(ImVec2(0, 0));
ImGui::SetWindowSize(ImVec2(Width, Height));
return true;
}
void window_render()
{
ImGui::End();
ImGui::Render();
auto io = ImGui::GetIO();
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(Window);
ImGui::UpdatePlatformWindows();
// ImGui::UpdatePlatformWindows();
}
}

View File

@@ -1,13 +1,14 @@
#pragma once
#include <string>
#include "graphics.hpp"
#define WINDOW_FLAGS ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoCollapse
#include <optional>
#include <string>
namespace inferno::graphics {
struct GraphicsDevice;
typedef void (*KeyCallback)(int key, int scan, int action, int mod);
typedef void (*MouseCallback)(double x, double y);
@@ -19,6 +20,8 @@ enum WINDOW_MODE {
void window_create(std::string title, int width, int height);
void window_cleanup();
void window_init_device(GraphicsDevice* device, GLFWframebuffersizefun resizeCallback);
void window_set_title(std::string title);
void window_set_size(int w, int h);

31
vk_layer_settings.txt Normal file
View File

@@ -0,0 +1,31 @@
# The main, heavy-duty validation checks. This may be valuable early in the
# development cycle to reduce validation output while correcting
# parameter/object usage errors.
khronos_validation.validate_core = true
# Enable synchronization validation during command buffers recording. This
# feature reports resource access conflicts due to missing or incorrect
# synchronization operations between actions (Draw, Copy, Dispatch, Blit)
# reading or writing the same regions of memory.
khronos_validation.validate_sync = true
# Thread checks. In order to not degrade performance, it might be best to run
# your program with thread-checking disabled most of the time, enabling it
# occasionally for a quick sanity check or when debugging difficult application
# behaviors.
khronos_validation.thread_safety = true
# Specifies what action is to be taken when a layer reports information
khronos_validation.debug_action = VK_DBG_LAYER_ACTION_LOG_MSG
# Comma-delineated list of options specifying the types of messages to be reported
khronos_validation.report_flags = debug,error,perf,info,warn
# Enable limiting of duplicate messages.
khronos_validation.enable_message_limit = true
# Maximum number of times any single validation message should be reported.
khronos_validation.duplicate_message_limit = 3
khronos_validation.printf_to_stdout = true
khronos_validation.printf_verbose = true