diff --git a/.github/ISSUE_TEMPLATE/issue_template.yml b/.github/ISSUE_TEMPLATE/issue_template.yml index 792d8f63e..6ed62493a 100644 --- a/.github/ISSUE_TEMPLATE/issue_template.yml +++ b/.github/ISSUE_TEMPLATE/issue_template.yml @@ -4,10 +4,12 @@ body: - type: markdown attributes: value: | - FOR FIRST-TIME USERS ISSUES COMPILING/LINKING/RUNNING or LOADING FONTS, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions) - For anything else: we are happy to use 'GitHub Issues' for many types of open-ended questions. We are encouraging 'Issues' becoming a large, centralized and cross-referenced database of Dear ImGui contents. + FOR FIRST-TIME USERS ISSUES COMPILING/LINKING/RUNNING, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions) + For anything else: **we are happy to use 'GitHub Issues' for many types of open-ended questions**. We are encouraging 'Issues' becoming a large, centralized, tagged, cross-referenced database of Dear ImGui contents. Be mindful that messages are being sent to the e-mail box of "Watching" users. Try to proof-read your messages before sending them. Edits are not seen by those users. + + **If you are using Dear ImGui as part of a job that you are being well-paid for** and your company is not a sponsor. Please be mindful that this is a Free Software and you might be about to ask volunteers to help you doing your job. Please put extra effort describing your issue or question properly. If your company is wealthy, please read [Funding](https://github.com/ocornut/imgui/wiki/Funding) and consider getting in touch. - type: markdown attributes: value: | diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 638545bd6..d40b14ccf 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,5 +2,9 @@ 1. PLEASE CAREFULLY READ: [Contributing Guidelines](https://github.com/ocornut/imgui/blob/master/docs/CONTRIBUTING.md) -2. Clear this template before submitting your PR. +2. **Make sure you're using a special branch just for this pull request**. (In git, 1 PR = 1 branch. If you update the branch the PR will be updated.) + +3. Consider running the [imgui_test_suite](https://github.com/ocornut/imgui_test_engine) or adding new tests to test for expected behaviors. + +4. Clear this template before submitting your PR. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f95ff43c8..9bc81bf81 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,32 +16,46 @@ on: - requested jobs: - Windows: - runs-on: windows-2019 - env: - VS_PATH: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\ - MSBUILD_PATH: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\ - steps: - - uses: actions/checkout@v4 + Build-Windows: + runs-on: windows-2025 + name: Build - Windows + defaults: + run: + working-directory: ${{ github.workspace }}/imgui + + env: + VS_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\ + MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\ + steps: + - uses: actions/checkout@v6 + with: + path: ${{ github.workspace }}/imgui + + # The VulkanSDK libs for Windows is manually generated using build_windows_vulkan_libs.ps1 + attached to issue #8925. + # (we have a .yml workflow in commit history if it becomes ever useful to create this on CI too) - name: Install Dependencies shell: powershell run: | - Invoke-WebRequest -Uri "https://www.libsdl.org/release/SDL2-devel-2.26.3-VC.zip" -OutFile "SDL2-devel-2.26.3-VC.zip" - Expand-Archive -Path SDL2-devel-2.26.3-VC.zip - echo "SDL2_DIR=$(pwd)\SDL2-devel-2.26.3-VC\SDL2-2.26.3\" >>${env:GITHUB_ENV} + Invoke-WebRequest -Uri "https://www.libsdl.org/release/SDL2-devel-2.32.8-VC.zip" -OutFile "SDL2-devel-2.32.8-VC.zip" + Expand-Archive -Path SDL2-devel-2.32.8-VC.zip + echo "SDL2_DIR=$(pwd)\SDL2-devel-2.32.8-VC\SDL2-2.32.8\" >>${env:GITHUB_ENV} - Invoke-WebRequest -Uri "https://github.com/ocornut/imgui/files/3789205/vulkan-sdk-1.1.121.2.zip" -OutFile vulkan-sdk-1.1.121.2.zip - Expand-Archive -Path vulkan-sdk-1.1.121.2.zip - echo "VULKAN_SDK=$(pwd)\vulkan-sdk-1.1.121.2\" >>${env:GITHUB_ENV} + Invoke-WebRequest -Uri "https://www.libsdl.org/release/SDL3-devel-3.2.18-VC.zip" -OutFile "SDL3-devel-3.2.18-VC.zip" + Expand-Archive -Path SDL3-devel-3.2.18-VC.zip + echo "SDL3_DIR=$(pwd)\SDL3-devel-3.2.18-VC\SDL3-3.2.18\" >>${env:GITHUB_ENV} + + Invoke-WebRequest -Uri "https://github.com/user-attachments/files/22464296/vulkan_windows_libs_1.4.326.zip" -OutFile vulkan_windows_libs_1.4.326.zip + Expand-Archive -Path vulkan_windows_libs_1.4.326.zip + echo "VULKAN_SDK=$(pwd)\vulkan_windows_libs_1.4.326\" >>${env:GITHUB_ENV} - name: Fix Projects shell: powershell run: | # CI workers do not supporter older Visual Studio versions. Fix projects to target newer available version. gci -recurse -filter "*.vcxproj" | ForEach-Object { - (Get-Content $_.FullName) -Replace "v\d{3}","v142" | Set-Content -Path $_.FullName - (Get-Content $_.FullName) -Replace "[\d\.]+","10.0.18362.0" | Set-Content -Path $_.FullName + (Get-Content $_.FullName) -Replace "v\d{3}","v143" | Set-Content -Path $_.FullName + (Get-Content $_.FullName) -Replace "[\d\.]+",'$(LatestTargetPlatformVersion)' | Set-Content -Path $_.FullName } # Not using matrix here because it would inflate job count too much. Check out and setup is done for every job and that makes build times way too long. @@ -51,15 +65,11 @@ jobs: - name: Build example_null (mingw 64-bit, as DLL) shell: bash run: | - echo '#ifdef _EXPORT' > example_single_file.cpp - echo '# define IMGUI_API __declspec(dllexport)' >> example_single_file.cpp - echo '#else' >> example_single_file.cpp - echo '# define IMGUI_API __declspec(dllimport)' >> example_single_file.cpp - echo '#endif' >> example_single_file.cpp + echo '#define IMGUI_API __declspec(dllexport)' > example_single_file.cpp echo '#define IMGUI_IMPLEMENTATION' >> example_single_file.cpp echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp - g++ -I. -Wall -Wformat -D_EXPORT -shared -o libimgui.dll -Wl,--out-implib,libimgui.a example_single_file.cpp -limm32 - g++ -I. -Wall -Wformat -o example_null.exe examples/example_null/main.cpp -L. -limgui + g++ -I. -Wall -Wformat -shared -o libimgui.dll -Wl,--out-implib,libimgui.a example_single_file.cpp -limm32 + g++ -I. -Wall -Wformat -DIMGUI_API='__declspec(dllimport)' -DIMGUI_IMPL_API= -o example_null.exe examples/example_null/main.cpp -L. -limgui rm -f example_null.exe libimgui.* example_single_file.* - name: Build example_null (extra warnings, msvc 64-bit) @@ -99,20 +109,19 @@ jobs: run: | call "%VS_PATH%\VC\Auxiliary\Build\vcvars64.bat" - echo #ifdef _EXPORT > example_single_file.cpp - echo # define IMGUI_API __declspec(dllexport) >> example_single_file.cpp - echo #else >> example_single_file.cpp - echo # define IMGUI_API __declspec(dllimport) >> example_single_file.cpp - echo #endif >> example_single_file.cpp + echo #define IMGUI_API __declspec(dllexport) > example_single_file.cpp echo #define IMGUI_IMPLEMENTATION >> example_single_file.cpp echo #include "misc/single_file/imgui_single_file.h" >> example_single_file.cpp - cl.exe /D_USRDLL /D_WINDLL /D_EXPORT /I. example_single_file.cpp /LD /FeImGui.dll /link - cl.exe /I. ImGui.lib /Feexample_null.exe examples/example_null/main.cpp + cl.exe /D_USRDLL /D_WINDLL /I. example_single_file.cpp /LD /FeImGui.dll /link + cl.exe /DIMGUI_API=__declspec(dllimport) -DIMGUI_IMPL_API= /I. ImGui.lib /Feexample_null.exe examples/example_null/main.cpp + # Win64 examples are more frequently compilted than the Win32 examples. + # More of the Win32 examples requires 'workflow_run' to reduce waste. - name: Build Win32 example_glfw_opengl2 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj /p:Platform=Win32 /p:Configuration=Release' + if: github.event_name == 'workflow_run' - name: Build Win32 example_glfw_opengl3 shell: cmd @@ -148,79 +157,126 @@ jobs: run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_directx11/example_sdl2_directx11.vcxproj /p:Platform=Win32 /p:Configuration=Release' if: github.event_name == 'workflow_run' + - name: Build Win32 example_sdl3_opengl3 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_opengl3/example_sdl3_opengl3.vcxproj /p:Platform=Win32 /p:Configuration=Release' + + - name: Build Win32 example_sdl3_sdlgpu3 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj /p:Platform=Win32 /p:Configuration=Release' + if: github.event_name == 'workflow_run' + + - name: Build Win32 example_sdl3_sdlrenderer3 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3.vcxproj /p:Platform=Win32 /p:Configuration=Release' + if: github.event_name == 'workflow_run' + + - name: Build Win32 example_sdl3_vulkan + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release' + if: github.event_name == 'workflow_run' + - name: Build Win32 example_win32_directx9 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx9/example_win32_directx9.vcxproj /p:Platform=Win32 /p:Configuration=Release' + if: github.event_name == 'workflow_run' - name: Build Win32 example_win32_directx10 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx10/example_win32_directx10.vcxproj /p:Platform=Win32 /p:Configuration=Release' + if: github.event_name == 'workflow_run' - name: Build Win32 example_win32_directx11 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx11/example_win32_directx11.vcxproj /p:Platform=Win32 /p:Configuration=Release' if: github.event_name == 'workflow_run' - - name: Build x64 example_glfw_opengl2 + # Windows 64-bits + - name: Build Win64 example_glfw_opengl2 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj /p:Platform=x64 /p:Configuration=Release' if: github.event_name == 'workflow_run' - - name: Build x64 example_glfw_opengl3 + - name: Build Win64 example_glfw_opengl3 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj /p:Platform=x64 /p:Configuration=Release' - - name: Build x64 example_glfw_vulkan + - name: Build Win64 example_glfw_vulkan shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release' - - name: Build x64 example_sdl2_sdlrenderer2 + - name: Build Win64 example_sdl2_sdlrenderer2 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj /p:Platform=x64 /p:Configuration=Release' if: github.event_name == 'workflow_run' - - name: Build x64 example_sdl2_vulkan + - name: Build Win64 example_sdl2_vulkan shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release' if: github.event_name == 'workflow_run' - - name: Build x64 example_sdl2_opengl2 + - name: Build Win64 example_sdl2_opengl2 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_opengl2/example_sdl2_opengl2.vcxproj /p:Platform=x64 /p:Configuration=Release' if: github.event_name == 'workflow_run' - - name: Build x64 example_sdl2_opengl3 + - name: Build Win64 example_sdl2_opengl3 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_opengl3/example_sdl2_opengl3.vcxproj /p:Platform=x64 /p:Configuration=Release' if: github.event_name == 'workflow_run' - - name: Build x64 example_sdl2_directx11 + - name: Build Win64 example_sdl2_directx11 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_directx11/example_sdl2_directx11.vcxproj /p:Platform=x64 /p:Configuration=Release' - - name: Build x64 example_win32_directx9 + - name: Build Win64 example_sdl3_opengl3 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_opengl3/example_sdl3_opengl3.vcxproj /p:Platform=x64 /p:Configuration=Release' + if: github.event_name == 'workflow_run' + + - name: Build Win64 example_sdl3_sdlgpu3 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj /p:Platform=x64 /p:Configuration=Release' + + - name: Build Win64 example_sdl3_sdlrenderer3 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3.vcxproj /p:Platform=x64 /p:Configuration=Release' + + - name: Build Win64 example_sdl3_vulkan + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release' + if: github.event_name == 'workflow_run' + + - name: Build Win64 example_win32_directx9 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx9/example_win32_directx9.vcxproj /p:Platform=x64 /p:Configuration=Release' if: github.event_name == 'workflow_run' - - name: Build x64 example_win32_directx10 + - name: Build Win64 example_win32_directx10 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx10/example_win32_directx10.vcxproj /p:Platform=x64 /p:Configuration=Release' if: github.event_name == 'workflow_run' - - name: Build x64 example_win32_directx11 + - name: Build Win64 example_win32_directx11 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx11/example_win32_directx11.vcxproj /p:Platform=x64 /p:Configuration=Release' - if: github.event_name == 'workflow_run' - - name: Build x64 example_win32_directx12 + - name: Build Win64 example_win32_directx12 shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx12/example_win32_directx12.vcxproj /p:Platform=x64 /p:Configuration=Release' - Linux: - runs-on: ubuntu-24.04 + Build-Linux: + runs-on: ubuntu-latest + name: Build - Linux + + defaults: + run: + working-directory: ${{ github.workspace }}/imgui + steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 + with: + path: ${{ github.workspace }}/imgui - name: Install Dependencies run: | @@ -287,7 +343,7 @@ jobs: EOF g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp - - name: Build example_null (with large ImDrawIdx + pointer ImTextureID) + - name: Build example_null (with large ImDrawIdx + custom ImTextureID) run: | cat > example_single_file.cpp <<'EOF' @@ -300,6 +356,21 @@ jobs: EOF g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp + cat > example_single_file.cpp <<'EOF' + + // Test build ImTextureID defined as a struct + struct SomeType { int a = 0; int b = 0; }; + #define ImTextureID SomeType + #define ImTextureID_Invalid SomeType() + inline bool operator==(const SomeType& lhs, const SomeType& rhs) { return lhs.a == rhs.a && lhs.b == rhs.b; } + inline bool operator!=(const SomeType& lhs, const SomeType& rhs) { return lhs.a != rhs.a || lhs.b != rhs.b; } + #define IMGUI_IMPLEMENTATION + #include "misc/single_file/imgui_single_file.h" + #include "examples/example_null/main.cpp" + + EOF + g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp + - name: Build example_null (with IMGUI_DISABLE_OBSOLETE_FUNCTIONS) run: | cat > example_single_file.cpp <<'EOF' @@ -434,19 +505,27 @@ jobs: - name: Build with IMGUI_IMPL_VULKAN_NO_PROTOTYPES run: g++ -c -I. -std=c++11 -DIMGUI_IMPL_VULKAN_NO_PROTOTYPES=1 backends/imgui_impl_vulkan.cpp - MacOS: + Build-MacOS: runs-on: macos-latest + name: Build - MacOS + + defaults: + run: + working-directory: ${{ github.workspace }}/imgui + steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 + with: + path: ${{ github.workspace }}/imgui - name: Install Dependencies run: | - brew install glfw3 sdl2 + brew install glfw3 sdl2 sdl3 - name: Build example_null (extra warnings, clang 64-bit) run: make -C examples/example_null WITH_EXTRA_WARNINGS=1 - - name: Build example_null (single file build) + - name: Build macOS example_null (single file build) run: | cat > example_single_file.cpp <<'EOF' @@ -457,7 +536,7 @@ jobs: EOF clang++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp - - name: Build example_null (single file build, c++20) + - name: Build macOS example_null (single file build, c++20) run: | cat > example_single_file.cpp <<'EOF' @@ -468,7 +547,7 @@ jobs: EOF clang++ -I. -std=c++20 -Wall -Wformat -o example_single_file example_single_file.cpp - - name: Build example_null (without c++ runtime) + - name: Build macOS example_null (without c++ runtime) run: | cat > example_single_file.cpp <<'EOF' @@ -479,46 +558,57 @@ jobs: EOF clang++ -I. -std=c++11 -Wall -Wformat -nodefaultlibs -fno-rtti -fno-exceptions -fno-threadsafe-statics -lc -lm -o example_single_file example_single_file.cpp - - name: Build example_glfw_opengl2 + - name: Build macOS example_glfw_opengl2 run: make -C examples/example_glfw_opengl2 + if: github.event_name == 'workflow_run' - - name: Build example_glfw_opengl3 + - name: Build macOS example_glfw_opengl3 run: make -C examples/example_glfw_opengl3 if: github.event_name == 'workflow_run' - - name: Build example_glfw_metal + - name: Build macOS example_glfw_metal run: make -C examples/example_glfw_metal - - name: Build example_sdl2_metal + - name: Build macOS example_sdl2_metal run: make -C examples/example_sdl2_metal - - name: Build example_sdl2_opengl2 + - name: Build macOS example_sdl2_opengl2 run: make -C examples/example_sdl2_opengl2 if: github.event_name == 'workflow_run' - - name: Build example_sdl2_opengl3 + - name: Build macOS example_sdl2_opengl3 run: make -C examples/example_sdl2_opengl3 + if: github.event_name == 'workflow_run' - - name: Build example_apple_metal + - name: Build macOS example_sdl3_opengl3 + run: make -C examples/example_sdl3_opengl3 + + - name: Build macOS example_apple_metal run: xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_macos + if: github.event_name == 'workflow_run' - - name: Build example_apple_opengl2 + - name: Build macOS example_apple_opengl2 run: xcodebuild -project examples/example_apple_opengl2/example_apple_opengl2.xcodeproj -target example_osx_opengl2 + if: github.event_name == 'workflow_run' + + Build-iOS: + runs-on: macos-14 + name: Build - iOS - iOS: - runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build example_apple_metal run: | # Code signing is required, but we disable it because it is irrelevant for CI builds. xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_ios CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - Emscripten: - runs-on: ubuntu-24.04 + Build-Emscripten: + runs-on: ubuntu-latest + name: Build - Emscripten + steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Dependencies run: | @@ -556,12 +646,148 @@ jobs: emcmake cmake -B build -DCMAKE_BUILD_TYPE=Release examples/example_glfw_wgpu cmake --build build - Android: - runs-on: ubuntu-24.04 + Build-Android: + runs-on: ubuntu-latest + name: Build - Android + steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build example_android_opengl3 run: | cd examples/example_android_opengl3/android gradle assembleDebug --stacktrace + + Test-Windows: + runs-on: windows-2025 + name: Test - Windows + + defaults: + run: + working-directory: ${{ github.workspace }}/imgui + + env: + MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\ + + steps: + - uses: actions/checkout@v6 + with: + path: ${{ github.workspace }}/imgui + + - uses: actions/checkout@v6 + continue-on-error: true + with: + fetch-depth: 1 + repository: ocornut/imgui_test_engine + path: ${{ github.workspace }}/imgui_test_engine + submodules: true + + - name: Fix Tests Projects + shell: powershell + working-directory: ${{ github.workspace }}/imgui_test_engine + run: | + # WARNING: This will need updating if toolset/sdk change in project files! + gci -recurse -filter "*.vcxproj" | ForEach-Object { + # Fix SDK and toolset for most samples. + (Get-Content $_.FullName) -Replace "v110","v142" | Set-Content -Path $_.FullName + (Get-Content $_.FullName) -Replace "8.1","10.0.20348.0" | Set-Content -Path $_.FullName + # Fix SDK and toolset for samples that require newer SDK/toolset. At the moment it is only dx12. + (Get-Content $_.FullName) -Replace "v140","v142" | Set-Content -Path $_.FullName + (Get-Content $_.FullName) -Replace "10.0.14393.0","10.0.20348.0" | Set-Content -Path $_.FullName + } + + - name: Build Tests + shell: cmd + working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite + run: '"%MSBUILD_PATH%\MSBuild.exe" imgui_test_suite.vcxproj /p:Platform=x64 /p:Configuration=Release /p:ClFlags=/WX -maxcpucount:%NUMBER_OF_PROCESSORS%' + + - name: Run Tests + working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite + run: Release/imgui_test_suite.exe -nogui -nopause -v2 -ve4 tests + + - name: Check for Docking + id: check_docking + shell: bash + working-directory: ${{ github.workspace }}/imgui + run: echo "has_dock=$(grep -q "#define IMGUI_HAS_DOCK" imgui.h && echo true || echo false)" >> $GITHUB_OUTPUT + + - name: Run Viewport Tests + if: steps.check_docking.outputs.has_dock == 'true' + working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite + run: Release/imgui_test_suite.exe -nogui -nopause -v2 -ve4 -viewport-mock viewport + + Test-Linux: + runs-on: ubuntu-latest + name: Test - Linux + + defaults: + run: + working-directory: ${{ github.workspace }}/imgui + + steps: + - uses: actions/checkout@v6 + with: + path: ${{ github.workspace }}/imgui + + - uses: actions/checkout@v6 + with: + fetch-depth: 1 + repository: ocornut/imgui_test_engine + path: ${{ github.workspace }}/imgui_test_engine + submodules: true + + - name: Build Tests + working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite + run: make -j$(nproc) + + - name: Run Tests + working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite + run: ./imgui_test_suite -nogui -nopause -v2 -ve4 tests + + - name: Check for Docking + id: check_docking + working-directory: ${{ github.workspace }}/imgui + run: echo "has_dock=$(grep -q "#define IMGUI_HAS_DOCK" imgui.h && echo true || echo false)" >> $GITHUB_OUTPUT + + - name: Run Viewport Tests + if: steps.check_docking.outputs.has_dock == 'true' + working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite + run: ./imgui_test_suite -nogui -nopause -v2 -ve4 -viewport-mock viewport + +# Test-MacOS: +# runs-on: macos-latest +# name: Test - MacOS +# +# defaults: +# run: +# working-directory: ${{ github.workspace }}/imgui +# +# steps: +# - uses: actions/checkout@v6 +# with: +# path: ${{ github.workspace }}/imgui +# +# - uses: actions/checkout@v6 +# with: +# fetch-depth: 1 +# repository: ocornut/imgui_test_engine +# path: ${{ github.workspace }}/imgui_test_engine +# submodules: true +# +# - name: Build Tests +# working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite +# run: make -j$(nproc) +# +# - name: Run Tests +# working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite +# run: ./imgui_test_suite -nogui -nopause -v2 -ve4 tests +# +# - name: Check for Docking +# id: check_docking +# working-directory: ${{ github.workspace }}/imgui +# run: echo "has_dock=$(grep -q "#define IMGUI_HAS_DOCK" imgui.h && echo true || echo false)" >> $GITHUB_OUTPUT +# +# - name: Run Viewport Tests +# if: steps.check_docking.outputs.has_dock == 'true' +# working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite +# run: ./imgui_test_suite -nogui -nopause -v2 -ve4 -viewport-mock viewport diff --git a/.github/workflows/build_windows_vulkan_libs.ps1 b/.github/workflows/build_windows_vulkan_libs.ps1 new file mode 100644 index 000000000..6658f5b5b --- /dev/null +++ b/.github/workflows/build_windows_vulkan_libs.ps1 @@ -0,0 +1,38 @@ +# This is current meant to be run manually, occasionally: +# - Run then zip the contents of vulkanArtifact/ into e.g. vulkan_windows_libs_1.4.326 +# - Upload as an attachment to https://github.com/ocornut/imgui/pull/8925 then change filename in build.yml +# - There is a build_windows_vulkan_libs.yml in commit history that we removed thinking this is run so rarely we don't need to pollute CI UI with it. + +# Set default vulkan version if none provided +if (-not $env:VULKAN_TAG) { $env:VULKAN_TAG = "1.4.326" } + +# Create output folder +mkdir vulkanArtifact + +# Download Vulkan Headers + +Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v$($env:VULKAN_TAG).zip" -OutFile Vulkan-Headers-$($env:VULKAN_TAG).zip +Expand-Archive -Path Vulkan-Headers-$($env:VULKAN_TAG).zip + +# Copy Vulkan Headers to artifact folder + +cp -R Vulkan-Headers-$($env:VULKAN_TAG)\Vulkan-Headers-$($env:VULKAN_TAG)\include vulkanArtifact\Include + +# Download Vulkan Loader + +Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/v$($env:VULKAN_TAG).zip" -OutFile Vulkan-Loader-$($env:VULKAN_TAG).zip +Expand-Archive -Path Vulkan-Loader-$($env:VULKAN_TAG).zip + +# Build Vulkan Loader x64 + +cmake -S Vulkan-Loader-$($env:VULKAN_TAG)\Vulkan-Loader-$($env:VULKAN_TAG) -B VulkanLoader-build64 -D UPDATE_DEPS=On -A x64 +cmake --build VulkanLoader-build64 +mkdir vulkanArtifact\Lib +copy VulkanLoader-build64\loader\Debug\vulkan-1.lib vulkanArtifact\Lib + +# Build Vulkan Loader win32 + +cmake -S Vulkan-Loader-$($env:VULKAN_TAG)\Vulkan-Loader-$($env:VULKAN_TAG) -B VulkanLoader-build32 -D UPDATE_DEPS=On -A Win32 +cmake --build VulkanLoader-build32 +mkdir vulkanArtifact\Lib32 +copy VulkanLoader-build32\loader\Debug\vulkan-1.lib vulkanArtifact\Lib32 diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 69df5cdf8..295628368 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -10,9 +10,9 @@ on: jobs: PVS-Studio: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 1 diff --git a/.gitignore b/.gitignore index f632636e0..1aedcf779 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ imgui*.ini examples/*/Debug/* examples/*/Release/* examples/*/x64/* +examples/*.tmp ## Visual Studio artifacts .vs @@ -29,12 +30,14 @@ ipch ## Getting files created in JSON/Schemas/Catalog/ from a VS2022 update JSON/ -## Commonly used CMake directories +## Commonly used CMake directories & CMake CPM cache build*/ +.cache -## Xcode artifacts +## Xcode & macOS artifacts project.xcworkspace xcuserdata +examples/*/*.dSYM ## Emscripten artifacts examples/*.o.tmp @@ -44,18 +47,33 @@ examples/example_glfw_opengl3/web/* examples/example_glfw_wgpu/web/* examples/example_glfw_wgpu/external/* examples/example_sdl2_opengl3/web/* +examples/example_sdl2_wgpu/web/* +examples/example_sdl3_opengl3/web/* +examples/example_sdl3_wgpu/web/* ## JetBrains IDE artifacts .idea cmake-build-* +## VS code artifacts +.vscode + ## Unix executables from our example Makefiles +examples/example_apple_metal/example_apple_metal +examples/example_apple_opengl2/example_apple_opengl2 examples/example_glfw_metal/example_glfw_metal examples/example_glfw_opengl2/example_glfw_opengl2 examples/example_glfw_opengl3/example_glfw_opengl3 +examples/example_glfw_vulkan/example_glfw_vulkan examples/example_glut_opengl2/example_glut_opengl2 examples/example_null/example_null examples/example_sdl2_metal/example_sdl2_metal examples/example_sdl2_opengl2/example_sdl2_opengl2 examples/example_sdl2_opengl3/example_sdl2_opengl3 -examples/example_sdl2_sdlrenderer/example_sdl2_sdlrenderer +examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2 +examples/example_sdl2_vulkan/example_sdl2_vulkan +examples/example_sdl3_metal/example_sdl3_metal +examples/example_sdl3_opengl3/example_sdl3_opengl3 +examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3 +examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3 +examples/example_sdl3_vulkan/example_sdl3_vulkan diff --git a/LICENSE.txt b/LICENSE.txt index 3282f5b5b..d5ba3155f 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2024 Omar Cornut +Copyright (c) 2014-2026 Omar Cornut Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 3e6d76647..6ee88a184 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -2,12 +2,13 @@ // (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.) // Implemented features: -// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [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 ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: Clipboard support (from Allegro 5.1.12) -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// Issues: -// [ ] Renderer: The renderer is suboptimal as we need to convert vertices manually. +// [X] Platform: Clipboard support (from Allegro 5.1.12). +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// Missing features or Issues: +// [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually. // [ ] Platform: Missing gamepad support. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -20,6 +21,13 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-09-18: Call platform_io.ClearRendererHandlers() and platform_io.ClearPlatformHandlers() on shutdown. +// 2025-08-12: Inputs: fixed missing support for ImGuiKey_PrintScreen under Windows, as raw Allegro 5 does not receive it. +// 2025-08-12: Added ImGui_ImplAllegro5_SetDisplay() function to change current ALLEGRO_DISPLAY, as Allegro applications often need to do that. +// 2025-07-07: Fixed texture update broken on some platforms where ALLEGRO_LOCK_WRITEONLY needed all texels to be rewritten. +// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLGPU3_CreateFontsTexture() and ImGui_ImplSDLGPU3_DestroyFontsTexture(). +// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support. +// 2025-01-06: Avoid calling al_set_mouse_cursor() repeatedly since it appears to leak on on X11 (#8256). // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn // - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn @@ -95,6 +103,7 @@ struct ImGui_ImplAllegro5_Data ALLEGRO_MOUSE_CURSOR* MouseCursorInvisible; ALLEGRO_VERTEX_DECL* VertexDecl; char* ClipboardTextData; + ImGuiMouseCursor LastCursor; ImVector BufVertices; ImVector BufIndices; @@ -134,6 +143,13 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplAllegro5_UpdateTexture(tex); + // Backup Allegro state that will be modified ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); ALLEGRO_TRANSFORM last_transform = *al_get_current_transform(); @@ -147,10 +163,8 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) ImGui_ImplAllegro5_SetupRenderState(draw_data); // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; - ImVector& vertices = bd->BufVertices; #if ALLEGRO_HAS_DRAW_INDEXED_PRIM vertices.resize(draw_list->VtxBuffer.Size); @@ -229,43 +243,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) bool ImGui_ImplAllegro5_CreateDeviceObjects() { - // Build texture atlas ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - // Create texture - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) - int flags = al_get_new_bitmap_flags(); - int fmt = al_get_new_bitmap_format(); - al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP | ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR); - al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE); - ALLEGRO_BITMAP* img = al_create_bitmap(width, height); - al_set_new_bitmap_flags(flags); - al_set_new_bitmap_format(fmt); - if (!img) - return false; - - ALLEGRO_LOCKED_REGION* locked_img = al_lock_bitmap(img, al_get_bitmap_format(img), ALLEGRO_LOCK_WRITEONLY); - if (!locked_img) - { - al_destroy_bitmap(img); - return false; - } - memcpy(locked_img->data, pixels, sizeof(int) * width * height); - al_unlock_bitmap(img); - - // Convert software texture to hardware texture. - ALLEGRO_BITMAP* cloned_img = al_clone_bitmap(img); - al_destroy_bitmap(img); - if (!cloned_img) - return false; - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)cloned_img); - bd->Texture = cloned_img; // Create an invisible mouse cursor // Because al_hide_mouse_cursor() seems to mess up with the actual inputs.. @@ -276,16 +254,82 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects() return true; } +void ImGui_ImplAllegro5_UpdateTexture(ImTextureData* tex) +{ + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + + // Create texture + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + const int new_bitmap_flags = al_get_new_bitmap_flags(); + int new_bitmap_format = al_get_new_bitmap_format(); + al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP | ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR); + al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE); + ALLEGRO_BITMAP* cpu_bitmap = al_create_bitmap(tex->Width, tex->Height); + IM_ASSERT(cpu_bitmap != nullptr && "Backend failed to create texture!"); + + // Upload pixels + ALLEGRO_LOCKED_REGION* locked_region = al_lock_bitmap(cpu_bitmap, al_get_bitmap_format(cpu_bitmap), ALLEGRO_LOCK_WRITEONLY); + IM_ASSERT(locked_region != nullptr && "Backend failed to create texture!"); + memcpy(locked_region->data, tex->GetPixels(), tex->GetSizeInBytes()); + al_unlock_bitmap(cpu_bitmap); + + // Convert software texture to hardware texture. + al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP); + al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ANY_32_WITH_ALPHA); + ALLEGRO_BITMAP* gpu_bitmap = al_clone_bitmap(cpu_bitmap); + al_destroy_bitmap(cpu_bitmap); + IM_ASSERT(gpu_bitmap != nullptr && "Backend failed to create texture!"); + + al_set_new_bitmap_flags(new_bitmap_flags); + al_set_new_bitmap_format(new_bitmap_format); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)gpu_bitmap); + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantUpdates) + { + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + ImTextureRect r = tex->UpdateRect; // Bounding box encompassing all individual updates + ALLEGRO_BITMAP* gpu_bitmap = (ALLEGRO_BITMAP*)(intptr_t)tex->TexID; + ALLEGRO_LOCKED_REGION* locked_region = al_lock_bitmap_region(gpu_bitmap, r.x, r.y, r.w, r.h, al_get_bitmap_format(gpu_bitmap), ALLEGRO_LOCK_WRITEONLY); + IM_ASSERT(locked_region && "Backend failed to update texture!"); + for (int y = 0; y < r.h; y++) + memcpy((unsigned char*)locked_region->data + locked_region->pitch * y, tex->GetPixelsAt(r.x, r.y + y), r.w * tex->BytesPerPixel); // dst, src, block pitch + al_unlock_bitmap(gpu_bitmap); + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantDestroy) + { + ALLEGRO_BITMAP* backend_tex = (ALLEGRO_BITMAP*)(intptr_t)tex->TexID; + if (backend_tex) + al_destroy_bitmap(backend_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); + } +} + void ImGui_ImplAllegro5_InvalidateDeviceObjects() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); - if (bd->Texture) - { - io.Fonts->SetTexID(0); - al_destroy_bitmap(bd->Texture); - bd->Texture = nullptr; - } + + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + { + tex->SetStatus(ImTextureStatus_WantDestroy); + ImGui_ImplAllegro5_UpdateTexture(tex); + } + + // Destroy mouse cursor if (bd->MouseCursorInvisible) { al_destroy_mouse_cursor(bd->MouseCursorInvisible); @@ -436,20 +480,11 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) io.BackendPlatformUserData = (void*)bd; io.BackendPlatformName = io.BackendRendererName = "imgui_impl_allegro5"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. - bd->Display = display; + bd->LastCursor = ALLEGRO_SYSTEM_MOUSE_CURSOR_NONE; - // Create custom vertex declaration. - // Unfortunately Allegro doesn't support 32-bit packed colors so we have to convert them to 4 floats. - // We still use a custom declaration to use 'ALLEGRO_PRIM_TEX_COORD' instead of 'ALLEGRO_PRIM_TEX_COORD_PIXEL' else we can't do a reliable conversion. - ALLEGRO_VERTEX_ELEMENT elems[] = - { - { ALLEGRO_PRIM_POSITION, ALLEGRO_PRIM_FLOAT_2, offsetof(ImDrawVertAllegro, pos) }, - { ALLEGRO_PRIM_TEX_COORD, ALLEGRO_PRIM_FLOAT_2, offsetof(ImDrawVertAllegro, uv) }, - { ALLEGRO_PRIM_COLOR_ATTR, 0, offsetof(ImDrawVertAllegro, col) }, - { 0, 0, 0 } - }; - bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro)); + ImGui_ImplAllegro5_SetDisplay(display); #if ALLEGRO_HAS_CLIPBOARD ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); @@ -465,6 +500,7 @@ void ImGui_ImplAllegro5_Shutdown() ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplAllegro5_InvalidateDeviceObjects(); if (bd->VertexDecl) @@ -474,10 +510,39 @@ void ImGui_ImplAllegro5_Shutdown() io.BackendPlatformName = io.BackendRendererName = nullptr; io.BackendPlatformUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_HasMouseCursors; + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); + platform_io.ClearPlatformHandlers(); IM_DELETE(bd); } +void ImGui_ImplAllegro5_SetDisplay(ALLEGRO_DISPLAY* display) +{ + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); + bd->Display = display; + + if (bd->VertexDecl) + { + al_destroy_vertex_decl(bd->VertexDecl); + bd->VertexDecl = nullptr; + } + + if (bd->Display && !bd->VertexDecl) + { + // Create custom vertex declaration. + // Unfortunately Allegro doesn't support 32-bits packed colors so we have to convert them to 4 floats. + // We still use a custom declaration to use 'ALLEGRO_PRIM_TEX_COORD' instead of 'ALLEGRO_PRIM_TEX_COORD_PIXEL' else we can't do a reliable conversion. + ALLEGRO_VERTEX_ELEMENT elems[] = + { + { ALLEGRO_PRIM_POSITION, ALLEGRO_PRIM_FLOAT_2, offsetof(ImDrawVertAllegro, pos) }, + { ALLEGRO_PRIM_TEX_COORD, ALLEGRO_PRIM_FLOAT_2, offsetof(ImDrawVertAllegro, uv) }, + { ALLEGRO_PRIM_COLOR_ATTR, 0, offsetof(ImDrawVertAllegro, col) }, + { 0, 0, 0 } + }; + bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro)); + } +} + // ev->keyboard.modifiers seems always zero so using that... static void ImGui_ImplAllegro5_UpdateKeyModifiers() { @@ -568,9 +633,16 @@ static void ImGui_ImplAllegro5_UpdateMouseCursor() ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) + + // Hide OS mouse cursor if imgui is drawing it + if (io.MouseDrawCursor) + imgui_cursor = ImGuiMouseCursor_None; + + if (bd->LastCursor == imgui_cursor) + return; + bd->LastCursor = imgui_cursor; + if (imgui_cursor == ImGuiMouseCursor_None) { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor al_set_mouse_cursor(bd->Display, bd->MouseCursorInvisible); } else @@ -584,6 +656,8 @@ static void ImGui_ImplAllegro5_UpdateMouseCursor() case ImGuiMouseCursor_ResizeEW: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_E; break; case ImGuiMouseCursor_ResizeNESW: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NE; break; case ImGuiMouseCursor_ResizeNWSE: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NW; break; + case ImGuiMouseCursor_Wait: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_BUSY; break; + case ImGuiMouseCursor_Progress: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_PROGRESS; break; case ImGuiMouseCursor_NotAllowed: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_UNAVAILABLE; break; } al_set_system_mouse_cursor(bd->Display, cursor_id); @@ -595,12 +669,11 @@ void ImGui_ImplAllegro5_NewFrame() ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplAllegro5_Init()?"); - if (!bd->Texture) + if (!bd->MouseCursorInvisible) ImGui_ImplAllegro5_CreateDeviceObjects(); - ImGuiIO& io = ImGui::GetIO(); - // Setup display size (every frame to accommodate for window resizing) + ImGuiIO& io = ImGui::GetIO(); int w, h; w = al_get_display_width(bd->Display); h = al_get_display_height(bd->Display); @@ -611,6 +684,11 @@ void ImGui_ImplAllegro5_NewFrame() io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; + // Allegro 5 doesn't receive PrintScreen under Windows +#ifdef _WIN32 + io.AddKeyEvent(ImGuiKey_PrintScreen, (::GetAsyncKeyState(VK_SNAPSHOT) & 0x8000) != 0); +#endif + // Setup mouse cursor shape ImGui_ImplAllegro5_UpdateMouseCursor(); } diff --git a/backends/imgui_impl_allegro5.h b/backends/imgui_impl_allegro5.h index aba5c4770..135c4ff8a 100644 --- a/backends/imgui_impl_allegro5.h +++ b/backends/imgui_impl_allegro5.h @@ -2,11 +2,12 @@ // (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.) // Implemented features: -// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [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 ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: Clipboard support (from Allegro 5.1.12) -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// Issues: +// [X] Platform: Clipboard support (from Allegro 5.1.12). +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// Missing features or Issues: // [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually. // [ ] Platform: Missing gamepad support. @@ -31,9 +32,13 @@ IMGUI_IMPL_API void ImGui_ImplAllegro5_Shutdown(); IMGUI_IMPL_API void ImGui_ImplAllegro5_NewFrame(); IMGUI_IMPL_API void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data); IMGUI_IMPL_API bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* event); +IMGUI_IMPL_API void ImGui_ImplAllegro5_SetDisplay(ALLEGRO_DISPLAY* display); // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API bool ImGui_ImplAllegro5_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplAllegro5_InvalidateDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplAllegro5_UpdateTexture(ImTextureData* tex); + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 05939293c..a76de1c26 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -4,10 +4,10 @@ // Implemented features: // [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 AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. -// Missing features: +// Missing features or Issues: // [ ] Platform: Clipboard support. -// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. +// [ ] Platform: Gamepad support. +// [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. // Important: // - Consider using SDL or GLFW backend on Android, which will be more full-featured than this. // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) diff --git a/backends/imgui_impl_android.h b/backends/imgui_impl_android.h index efe27dcb9..f6e41039a 100644 --- a/backends/imgui_impl_android.h +++ b/backends/imgui_impl_android.h @@ -4,10 +4,10 @@ // Implemented features: // [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 AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. -// Missing features: +// Missing features or Issues: // [ ] Platform: Clipboard support. -// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. +// [ ] Platform: Gamepad support. +// [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. // Important: // - Consider using SDL or GLFW backend on Android, which will be more full-featured than this. // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 2638caf9c..118798325 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // 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. @@ -15,6 +16,11 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-01-19: DirectX11: Added 'SamplerNearest' in ImGui_ImplDX11_RenderState. Renamed 'SamplerDefault' to 'SamplerLinear'. +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-11: DirectX10: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. +// 2025-05-07: DirectX10: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows). +// 2025-01-06: DirectX10: Expose selected render state in ImGui_ImplDX10_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2024-10-07: DirectX10: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 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). @@ -46,7 +52,19 @@ #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif -// DirectX data +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + +// DirectX10 data +struct ImGui_ImplDX10_Texture +{ + ID3D10Texture2D* pTexture; + ID3D10ShaderResourceView* pTextureView; +}; + struct ImGui_ImplDX10_Data { ID3D10Device* pd3dDevice; @@ -57,8 +75,8 @@ struct ImGui_ImplDX10_Data ID3D10InputLayout* pInputLayout; ID3D10Buffer* pVertexConstantBuffer; ID3D10PixelShader* pPixelShader; - ID3D10SamplerState* pFontSampler; - ID3D10ShaderResourceView* pFontTextureView; + ID3D10SamplerState* pTexSamplerLinear; + ID3D10SamplerState* pTexSamplerNearest; ID3D10RasterizerState* pRasterizerState; ID3D10BlendState* pBlendState; ID3D10DepthStencilState* pDepthStencilState; @@ -86,16 +104,36 @@ static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); // Setup viewport - D3D10_VIEWPORT vp; - memset(&vp, 0, sizeof(D3D10_VIEWPORT)); - vp.Width = (UINT)draw_data->DisplaySize.x; - vp.Height = (UINT)draw_data->DisplaySize.y; + D3D10_VIEWPORT vp = {}; + vp.Width = (UINT)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + vp.Height = (UINT)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0; device->RSSetViewports(1, &vp); - // Bind shader and vertex buffers + // Setup orthographic projection matrix into our constant buffer + // 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. + void* mapped_resource; + if (bd->pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) == S_OK) + { + VERTEX_CONSTANT_BUFFER_DX10* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX10*)mapped_resource; + 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; + float mvp[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, 0.5f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, + }; + memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); + bd->pVertexConstantBuffer->Unmap(); + } + + // Setup shader and vertex buffers unsigned int stride = sizeof(ImDrawVert); unsigned int offset = 0; device->IASetInputLayout(bd->pInputLayout); @@ -105,7 +143,7 @@ static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* device->VSSetShader(bd->pVertexShader); device->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer); device->PSSetShader(bd->pPixelShader); - device->PSSetSamplers(0, 1, &bd->pFontSampler); + device->PSSetSamplers(0, 1, &bd->pTexSamplerLinear); device->GSSetShader(nullptr); // Setup render state @@ -125,13 +163,19 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); ID3D10Device* device = bd->pd3dDevice; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplDX10_UpdateTexture(tex); + // Create and grow vertex/index buffers if needed if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; - D3D10_BUFFER_DESC desc; - memset(&desc, 0, sizeof(D3D10_BUFFER_DESC)); + D3D10_BUFFER_DESC desc = {}; desc.Usage = D3D10_USAGE_DYNAMIC; desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; @@ -145,8 +189,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) { if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; - D3D10_BUFFER_DESC desc; - memset(&desc, 0, sizeof(D3D10_BUFFER_DESC)); + D3D10_BUFFER_DESC desc = {}; desc.Usage = D3D10_USAGE_DYNAMIC; desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); desc.BindFlags = D3D10_BIND_INDEX_BUFFER; @@ -160,9 +203,8 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) ImDrawIdx* idx_dst = nullptr; bd->pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst); bd->pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst); - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); vtx_dst += draw_list->VtxBuffer.Size; @@ -171,28 +213,6 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) bd->pVB->Unmap(); bd->pIB->Unmap(); - // Setup orthographic projection matrix into our constant buffer - // 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. - { - void* mapped_resource; - if (bd->pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) - return; - VERTEX_CONSTANT_BUFFER_DX10* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX10*)mapped_resource; - 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; - float mvp[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, 0.5f, 0.0f }, - { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, - }; - memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); - bd->pVertexConstantBuffer->Unmap(); - } - // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) struct BACKUP_DX10_STATE { @@ -236,19 +256,27 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) // Setup desired DX state ImGui_ImplDX10_SetupRenderState(draw_data, device); + // Setup render state structure (for callbacks and custom texture bindings) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImGui_ImplDX10_RenderState render_state; + render_state.Device = bd->pd3dDevice; + render_state.SamplerLinear = bd->pTexSamplerLinear; + render_state.SamplerNearest = bd->pTexSamplerNearest; + render_state.VertexConstantBuffer = bd->pVertexConstantBuffer; + platform_io.Renderer_RenderState = &render_state; // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) int global_vtx_offset = 0; int global_idx_offset = 0; ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) + ImVec2 clip_scale = draw_data->FramebufferScale; + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) + 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.) @@ -260,8 +288,8 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) else { // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); - ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + 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; @@ -278,6 +306,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) global_idx_offset += draw_list->IdxBuffer.Size; global_vtx_offset += draw_list->VtxBuffer.Size; } + platform_io.Renderer_RenderState = nullptr; // Restore modified DX state device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); @@ -297,21 +326,39 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) device->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); } -static void ImGui_ImplDX10_CreateFontsTexture() +static void ImGui_ImplDX10_DestroyTexture(ImTextureData* tex) { - // Build texture atlas - ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - // Upload texture to graphics system + if (ImGui_ImplDX10_Texture* backend_tex = (ImGui_ImplDX10_Texture*)tex->BackendUserData) { + IM_ASSERT(backend_tex->pTextureView == (ID3D10ShaderResourceView*)(intptr_t)tex->TexID); + backend_tex->pTextureView->Release(); + backend_tex->pTexture->Release(); + IM_DELETE(backend_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->BackendUserData = nullptr; + } + tex->SetStatus(ImTextureStatus_Destroyed); +} + +void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex) +{ + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + unsigned int* pixels = (unsigned int*)tex->GetPixels(); + ImGui_ImplDX10_Texture* backend_tex = IM_NEW(ImGui_ImplDX10_Texture)(); + + // Create texture D3D10_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.Width = width; - desc.Height = height; + desc.Width = (UINT)tex->Width; + desc.Height = (UINT)tex->Height; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -320,13 +367,12 @@ static void ImGui_ImplDX10_CreateFontsTexture() desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = 0; - ID3D10Texture2D* pTexture = nullptr; D3D10_SUBRESOURCE_DATA subResource; subResource.pSysMem = pixels; subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; - bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); - IM_ASSERT(pTexture != nullptr); + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &backend_tex->pTexture); + IM_ASSERT(backend_tex->pTexture != nullptr && "Backend failed to create texture!"); // Create texture view D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc; @@ -335,28 +381,29 @@ static void ImGui_ImplDX10_CreateFontsTexture() srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; srv_desc.Texture2D.MipLevels = desc.MipLevels; srv_desc.Texture2D.MostDetailedMip = 0; - bd->pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &bd->pFontTextureView); - pTexture->Release(); + bd->pd3dDevice->CreateShaderResourceView(backend_tex->pTexture, &srv_desc, &backend_tex->pTextureView); + IM_ASSERT(backend_tex->pTextureView != nullptr && "Backend failed to create texture!"); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)backend_tex->pTextureView); + tex->SetStatus(ImTextureStatus_OK); + tex->BackendUserData = backend_tex; } - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView); - - // Create texture sampler - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + else if (tex->Status == ImTextureStatus_WantUpdates) { - D3D10_SAMPLER_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR; - desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP; - desc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP; - desc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP; - desc.MipLODBias = 0.f; - desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS; - desc.MinLOD = 0.f; - desc.MaxLOD = 0.f; - bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler); + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + ImGui_ImplDX10_Texture* backend_tex = (ImGui_ImplDX10_Texture*)tex->BackendUserData; + IM_ASSERT(backend_tex->pTextureView == (ID3D10ShaderResourceView*)(intptr_t)tex->TexID); + for (ImTextureRect& r : tex->Updates) + { + D3D10_BOX box = { (UINT)r.x, (UINT)r.y, (UINT)0, (UINT)(r.x + r.w), (UINT)(r.y + r.h), (UINT)1 }; + bd->pd3dDevice->UpdateSubresource(backend_tex->pTexture, 0, &box, tex->GetPixelsAt(r.x, r.y), (UINT)tex->GetPitch(), 0); + } + tex->SetStatus(ImTextureStatus_OK); } + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + ImGui_ImplDX10_DestroyTexture(tex); } bool ImGui_ImplDX10_CreateDeviceObjects() @@ -364,8 +411,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); if (!bd->pd3dDevice) return false; - if (bd->pFontSampler) - ImGui_ImplDX10_InvalidateDeviceObjects(); + ImGui_ImplDX10_InvalidateDeviceObjects(); // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) // If you would like to use this DX10 sample code but remove this dependency you can: @@ -428,7 +474,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() // Create the constant buffer { - D3D10_BUFFER_DESC desc; + D3D10_BUFFER_DESC desc = {}; desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX10); desc.Usage = D3D10_USAGE_DYNAMIC; desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER; @@ -508,7 +554,23 @@ bool ImGui_ImplDX10_CreateDeviceObjects() bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } - ImGui_ImplDX10_CreateFontsTexture(); + // Create texture sampler + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + { + D3D10_SAMPLER_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR; + desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP; + desc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP; + desc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP; + desc.MipLODBias = 0.f; + desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS; + desc.MinLOD = 0.f; + desc.MaxLOD = 0.f; + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerLinear); + desc.Filter = D3D10_FILTER_MIN_MAG_MIP_POINT; + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerNearest); + } return true; } @@ -519,8 +581,12 @@ void ImGui_ImplDX10_InvalidateDeviceObjects() if (!bd->pd3dDevice) return; - if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; } - if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplDX10_DestroyTexture(tex); + if (bd->pTexSamplerLinear) { bd->pTexSamplerLinear->Release(); bd->pTexSamplerLinear = nullptr; } + if (bd->pTexSamplerNearest) { bd->pTexSamplerNearest->Release(); bd->pTexSamplerNearest = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = nullptr; } @@ -543,6 +609,10 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx10"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; @@ -567,13 +637,16 @@ void ImGui_ImplDX10_Shutdown() ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplDX10_InvalidateDeviceObjects(); if (bd->pFactory) { bd->pFactory->Release(); } if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } @@ -582,8 +655,9 @@ void ImGui_ImplDX10_NewFrame() ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX10_Init()?"); - if (!bd->pFontSampler) - ImGui_ImplDX10_CreateDeviceObjects(); + if (!bd->pVertexShader) + if (!ImGui_ImplDX10_CreateDeviceObjects()) + IM_ASSERT(0 && "ImGui_ImplDX10_CreateDeviceObjects() failed!"); } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_dx10.h b/backends/imgui_impl_dx10.h index 667f296f1..87945d64e 100644 --- a/backends/imgui_impl_dx10.h +++ b/backends/imgui_impl_dx10.h @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // 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. @@ -18,6 +19,8 @@ #ifndef IMGUI_DISABLE struct ID3D10Device; +struct ID3D10SamplerState; +struct ID3D10Buffer; // Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplDX10_Init(ID3D10Device* device); @@ -29,4 +32,18 @@ IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data); IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex); + +// [BETA] Selected render state data shared with callbacks. +// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX10_RenderDrawData() call. +// (Please open an issue if you feel you need access to more data) +struct ImGui_ImplDX10_RenderState +{ + ID3D10Device* Device; + ID3D10SamplerState* SamplerLinear; + ID3D10SamplerState* SamplerNearest; + ID3D10Buffer* VertexConstantBuffer; +}; + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index b928bb86f..193127b4e 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -16,6 +17,11 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-01-19: DirectX11: Added 'SamplerNearest' in ImGui_ImplDX11_RenderState. Renamed 'SamplerDefault' to 'SamplerLinear'. +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-11: DirectX11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. +// 2025-05-07: DirectX11: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows). +// 2025-01-06: DirectX11: Expose VertexConstantBuffer in ImGui_ImplDX11_RenderState. Reset projection matrix in ImDrawCallback_ResetRenderState handler. // 2024-10-07: DirectX11: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-10-07: DirectX11: Expose selected render state in ImGui_ImplDX11_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -48,7 +54,19 @@ #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + // DirectX11 data +struct ImGui_ImplDX11_Texture +{ + ID3D11Texture2D* pTexture; + ID3D11ShaderResourceView* pTextureView; +}; + struct ImGui_ImplDX11_Data { ID3D11Device* pd3dDevice; @@ -60,8 +78,8 @@ struct ImGui_ImplDX11_Data ID3D11InputLayout* pInputLayout; ID3D11Buffer* pVertexConstantBuffer; ID3D11PixelShader* pPixelShader; - ID3D11SamplerState* pFontSampler; - ID3D11ShaderResourceView* pFontTextureView; + ID3D11SamplerState* pTexSamplerLinear; + ID3D11SamplerState* pTexSamplerNearest; ID3D11RasterizerState* pRasterizerState; ID3D11BlendState* pBlendState; ID3D11DepthStencilState* pDepthStencilState; @@ -84,20 +102,40 @@ static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData() } // Functions -static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* device_ctx) +static void ImGui_ImplDX11_SetupRenderState(const ImDrawData* draw_data, ID3D11DeviceContext* device_ctx) { ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); // Setup viewport - D3D11_VIEWPORT vp; - memset(&vp, 0, sizeof(D3D11_VIEWPORT)); - vp.Width = draw_data->DisplaySize.x; - vp.Height = draw_data->DisplaySize.y; + D3D11_VIEWPORT vp = {}; + vp.Width = draw_data->DisplaySize.x * draw_data->FramebufferScale.x; + vp.Height = draw_data->DisplaySize.y * draw_data->FramebufferScale.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0; device_ctx->RSSetViewports(1, &vp); + // Setup orthographic projection matrix into our constant buffer + // 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. + D3D11_MAPPED_SUBRESOURCE mapped_resource; + if (device_ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) == S_OK) + { + VERTEX_CONSTANT_BUFFER_DX11* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX11*)mapped_resource.pData; + 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; + float mvp[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, 0.5f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, + }; + memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); + device_ctx->Unmap(bd->pVertexConstantBuffer, 0); + } + // Setup shader and vertex buffers unsigned int stride = sizeof(ImDrawVert); unsigned int offset = 0; @@ -108,13 +146,13 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC device_ctx->VSSetShader(bd->pVertexShader, nullptr, 0); device_ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer); device_ctx->PSSetShader(bd->pPixelShader, nullptr, 0); - device_ctx->PSSetSamplers(0, 1, &bd->pFontSampler); + device_ctx->PSSetSamplers(0, 1, &bd->pTexSamplerLinear); device_ctx->GSSetShader(nullptr, nullptr, 0); device_ctx->HSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. device_ctx->DSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. device_ctx->CSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. - // Setup blend state + // Setup render state const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; device_ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); device_ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0); @@ -131,13 +169,19 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); ID3D11DeviceContext* device = bd->pd3dDeviceContext; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplDX11_UpdateTexture(tex); + // Create and grow vertex/index buffers if needed if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; - D3D11_BUFFER_DESC desc; - memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); + D3D11_BUFFER_DESC desc = {}; desc.Usage = D3D11_USAGE_DYNAMIC; desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; @@ -150,8 +194,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) { if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; - D3D11_BUFFER_DESC desc; - memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); + D3D11_BUFFER_DESC desc = {}; desc.Usage = D3D11_USAGE_DYNAMIC; desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); desc.BindFlags = D3D11_BIND_INDEX_BUFFER; @@ -168,9 +211,8 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) return; ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); vtx_dst += draw_list->VtxBuffer.Size; @@ -179,28 +221,6 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) device->Unmap(bd->pVB, 0); device->Unmap(bd->pIB, 0); - // Setup orthographic projection matrix into our constant buffer - // 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. - { - D3D11_MAPPED_SUBRESOURCE mapped_resource; - if (device->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) - return; - VERTEX_CONSTANT_BUFFER_DX11* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX11*)mapped_resource.pData; - 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; - float mvp[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, 0.5f, 0.0f }, - { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, - }; - memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); - device->Unmap(bd->pVertexConstantBuffer, 0); - } - // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) struct BACKUP_DX11_STATE { @@ -254,7 +274,9 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ImGui_ImplDX11_RenderState render_state; render_state.Device = bd->pd3dDevice; render_state.DeviceContext = bd->pd3dDeviceContext; - render_state.SamplerDefault = bd->pFontSampler; + render_state.SamplerLinear = bd->pTexSamplerLinear; + render_state.SamplerNearest = bd->pTexSamplerNearest; + render_state.VertexConstantBuffer = bd->pVertexConstantBuffer; platform_io.Renderer_RenderState = &render_state; // Render command lists @@ -262,9 +284,9 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) int global_idx_offset = 0; int global_vtx_offset = 0; ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) + ImVec2 clip_scale = draw_data->FramebufferScale; + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; @@ -280,8 +302,8 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) else { // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); - ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + 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; @@ -298,7 +320,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) global_idx_offset += draw_list->IdxBuffer.Size; global_vtx_offset += draw_list->VtxBuffer.Size; } - platform_io.Renderer_RenderState = NULL; + platform_io.Renderer_RenderState = nullptr; // Restore modified DX state device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); @@ -320,21 +342,39 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) device->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); } -static void ImGui_ImplDX11_CreateFontsTexture() +static void ImGui_ImplDX11_DestroyTexture(ImTextureData* tex) { - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - // Upload texture to graphics system + if (ImGui_ImplDX11_Texture* backend_tex = (ImGui_ImplDX11_Texture*)tex->BackendUserData) { + IM_ASSERT(backend_tex->pTextureView == (ID3D11ShaderResourceView*)(intptr_t)tex->TexID); + backend_tex->pTextureView->Release(); + backend_tex->pTexture->Release(); + IM_DELETE(backend_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->BackendUserData = nullptr; + } + tex->SetStatus(ImTextureStatus_Destroyed); +} + +void ImGui_ImplDX11_UpdateTexture(ImTextureData* tex) +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + unsigned int* pixels = (unsigned int*)tex->GetPixels(); + ImGui_ImplDX11_Texture* backend_tex = IM_NEW(ImGui_ImplDX11_Texture)(); + + // Create texture D3D11_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.Width = width; - desc.Height = height; + desc.Width = (UINT)tex->Width; + desc.Height = (UINT)tex->Height; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -342,14 +382,12 @@ static void ImGui_ImplDX11_CreateFontsTexture() desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = 0; - - ID3D11Texture2D* pTexture = nullptr; D3D11_SUBRESOURCE_DATA subResource; subResource.pSysMem = pixels; subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; - bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); - IM_ASSERT(pTexture != nullptr); + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &backend_tex->pTexture); + IM_ASSERT(backend_tex->pTexture != nullptr && "Backend failed to create texture!"); // Create texture view D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; @@ -358,28 +396,29 @@ static void ImGui_ImplDX11_CreateFontsTexture() srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; - bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView); - pTexture->Release(); + bd->pd3dDevice->CreateShaderResourceView(backend_tex->pTexture, &srvDesc, &backend_tex->pTextureView); + IM_ASSERT(backend_tex->pTextureView != nullptr && "Backend failed to create texture!"); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)backend_tex->pTextureView); + tex->SetStatus(ImTextureStatus_OK); + tex->BackendUserData = backend_tex; } - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView); - - // Create texture sampler - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + else if (tex->Status == ImTextureStatus_WantUpdates) { - D3D11_SAMPLER_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; - desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; - desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; - desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; - desc.MipLODBias = 0.f; - desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; - desc.MinLOD = 0.f; - desc.MaxLOD = 0.f; - bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler); + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + ImGui_ImplDX11_Texture* backend_tex = (ImGui_ImplDX11_Texture*)tex->BackendUserData; + IM_ASSERT(backend_tex->pTextureView == (ID3D11ShaderResourceView*)(intptr_t)tex->TexID); + for (ImTextureRect& r : tex->Updates) + { + D3D11_BOX box = { (UINT)r.x, (UINT)r.y, (UINT)0, (UINT)(r.x + r.w), (UINT)(r.y + r .h), (UINT)1 }; + bd->pd3dDeviceContext->UpdateSubresource(backend_tex->pTexture, 0, &box, tex->GetPixelsAt(r.x, r.y), (UINT)tex->GetPitch(), 0); + } + tex->SetStatus(ImTextureStatus_OK); } + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + ImGui_ImplDX11_DestroyTexture(tex); } bool ImGui_ImplDX11_CreateDeviceObjects() @@ -387,8 +426,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); if (!bd->pd3dDevice) return false; - if (bd->pFontSampler) - ImGui_ImplDX11_InvalidateDeviceObjects(); + ImGui_ImplDX11_InvalidateDeviceObjects(); // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) // If you would like to use this DX11 sample code but remove this dependency you can: @@ -451,7 +489,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() // Create the constant buffer { - D3D11_BUFFER_DESC desc; + D3D11_BUFFER_DESC desc = {}; desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX11); desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; @@ -531,7 +569,23 @@ bool ImGui_ImplDX11_CreateDeviceObjects() bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } - ImGui_ImplDX11_CreateFontsTexture(); + // Create texture sampler + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + { + D3D11_SAMPLER_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.MipLODBias = 0.f; + desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; + desc.MinLOD = 0.f; + desc.MaxLOD = 0.f; + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerLinear); + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerNearest); + } return true; } @@ -542,8 +596,13 @@ void ImGui_ImplDX11_InvalidateDeviceObjects() if (!bd->pd3dDevice) return; - if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; } - if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well. + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplDX11_DestroyTexture(tex); + + if (bd->pTexSamplerLinear) { bd->pTexSamplerLinear->Release(); bd->pTexSamplerLinear = nullptr; } + if (bd->pTexSamplerNearest) { bd->pTexSamplerNearest->Release(); bd->pTexSamplerNearest = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = nullptr; } @@ -566,6 +625,10 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx11"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; @@ -593,14 +656,17 @@ void ImGui_ImplDX11_Shutdown() ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplDX11_InvalidateDeviceObjects(); if (bd->pFactory) { bd->pFactory->Release(); } if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); } + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } @@ -609,8 +675,9 @@ void ImGui_ImplDX11_NewFrame() ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX11_Init()?"); - if (!bd->pFontSampler) - ImGui_ImplDX11_CreateDeviceObjects(); + if (!bd->pVertexShader) + if (!ImGui_ImplDX11_CreateDeviceObjects()) + IM_ASSERT(0 && "ImGui_ImplDX11_CreateDeviceObjects() failed!"); } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h index c3b8379b1..338e0093f 100644 --- a/backends/imgui_impl_dx11.h +++ b/backends/imgui_impl_dx11.h @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -21,6 +22,7 @@ struct ID3D11Device; struct ID3D11DeviceContext; struct ID3D11SamplerState; +struct ID3D11Buffer; // Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); @@ -32,6 +34,9 @@ IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplDX11_UpdateTexture(ImTextureData* tex); + // [BETA] Selected render state data shared with callbacks. // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX11_RenderDrawData() call. // (Please open an issue if you feel you need access to more data) @@ -39,7 +44,9 @@ struct ImGui_ImplDX11_RenderState { ID3D11Device* Device; ID3D11DeviceContext* DeviceContext; - ID3D11SamplerState* SamplerDefault; + ID3D11SamplerState* SamplerLinear; + ID3D11SamplerState* SamplerNearest; + ID3D11Buffer* VertexConstantBuffer; }; #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index acf98858b..a5420782a 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification. @@ -19,6 +20,17 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002) +// 2025-09-29: DirectX12: Rework synchronization logic. (#8961) +// 2025-09-29: DirectX12: Enable swapchain tearing to eliminate viewports framerate throttling. (#8965) +// 2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time. (#8963) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-19: Fixed build on MinGW. (#8702, #4594) +// 2025-06-11: DirectX12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. +// 2025-05-07: DirectX12: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows). +// 2025-02-24: DirectX12: Fixed an issue where ImGui_ImplDX12_Init() signature change from 2024-11-15 combined with change from 2025-01-15 made legacy ImGui_ImplDX12_Init() crash. (#8429) +// 2025-01-15: DirectX12: Texture upload use the command queue provided in ImGui_ImplDX12_InitInfo instead of creating its own. +// 2024-12-09: DirectX12: Let user specifies the DepthStencilView format by setting ImGui_ImplDX12_InitInfo::DSVFormat. // 2024-11-15: DirectX12: *BREAKING CHANGE* Changed ImGui_ImplDX12_Init() signature to take a ImGui_ImplDX12_InitInfo struct. Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete). // 2024-11-15: DirectX12: *BREAKING CHANGE* User is now required to pass function pointers to allocate/free SRV Descriptors. We provide convenience legacy fields to pass a single descriptor, matching the old API, but upcoming features will want multiple. // 2024-10-23: DirectX12: Unmap() call specify written range. The range is informational and may be used by debug tools. @@ -49,12 +61,21 @@ // DirectX #include -#include +#include #include #ifdef _MSC_VER #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + +// MinGW workaround, see #4594 +typedef decltype(D3D12SerializeRootSignature) *_PFN_D3D12_SERIALIZE_ROOT_SIGNATURE; + // DirectX12 data struct ImGui_ImplDX12_RenderBuffers; @@ -63,25 +84,41 @@ struct ImGui_ImplDX12_Texture ID3D12Resource* pTextureResource; D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle; D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle; + + ImGui_ImplDX12_Texture() { memset((void*)this, 0, sizeof(*this)); } }; struct ImGui_ImplDX12_Data { ImGui_ImplDX12_InitInfo InitInfo; + IDXGIFactory5* pdxgiFactory; ID3D12Device* pd3dDevice; - ID3D12RootSignature* pRootSignature; - ID3D12PipelineState* pPipelineState; + ID3D12RootSignature* pRootSignatureLinear; + ID3D12RootSignature* pRootSignatureNearest; + ID3D12PipelineState* pPipelineStateLinear; + ID3D12PipelineState* pPipelineStateNearest; + ID3D12CommandQueue* pCommandQueue; + bool commandQueueOwned; DXGI_FORMAT RTVFormat; + DXGI_FORMAT DSVFormat; ID3D12DescriptorHeap* pd3dSrvDescHeap; + ID3D12Fence* Fence; + UINT64 FenceLastSignaledValue; + HANDLE FenceEvent; UINT numFramesInFlight; + bool tearingSupport; + bool LegacySingleDescriptorUsed; + + ID3D12CommandAllocator* pTexCmdAllocator; + ID3D12GraphicsCommandList* pTexCmdList; + ID3D12Resource* pTexUploadBuffer; + UINT pTexUploadBufferSize; + void* pTexUploadBufferMapped; ImGui_ImplDX12_RenderBuffers* pFrameResources; UINT frameIndex; - ImGui_ImplDX12_Texture FontTexture; - bool LegacySingleDescriptorUsed; - - ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); frameIndex = UINT_MAX; } + ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); } }; // Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts @@ -105,11 +142,27 @@ struct VERTEX_CONSTANT_BUFFER_DX12 float mvp[4][4]; }; +// FIXME-WIP: Allow user to forward declare those two, for until we come up with a backend agnostic API to do this. (#9173) +void ImGui_ImplDX12_SetupSamplerLinear(ID3D12GraphicsCommandList* command_list); +void ImGui_ImplDX12_SetupSamplerNearest(ID3D12GraphicsCommandList* command_list); + // Functions -static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list, ImGui_ImplDX12_RenderBuffers* fr) +void ImGui_ImplDX12_SetupSamplerLinear(ID3D12GraphicsCommandList* command_list) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + command_list->SetPipelineState(bd->pPipelineStateLinear); + command_list->SetGraphicsRootSignature(bd->pRootSignatureLinear); +} +void ImGui_ImplDX12_SetupSamplerNearest(ID3D12GraphicsCommandList* command_list) +{ + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + command_list->SetPipelineState(bd->pPipelineStateNearest); + command_list->SetGraphicsRootSignature(bd->pRootSignatureNearest); +} + +static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list, ImGui_ImplDX12_RenderBuffers* fr) +{ // Setup orthographic projection matrix into our constant buffer // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). VERTEX_CONSTANT_BUFFER_DX12 vertex_constant_buffer; @@ -129,10 +182,9 @@ static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12Graphic } // Setup viewport - D3D12_VIEWPORT vp; - memset(&vp, 0, sizeof(D3D12_VIEWPORT)); - vp.Width = draw_data->DisplaySize.x; - vp.Height = draw_data->DisplaySize.y; + D3D12_VIEWPORT vp = {}; + vp.Width = draw_data->DisplaySize.x * draw_data->FramebufferScale.x; + vp.Height = draw_data->DisplaySize.y * draw_data->FramebufferScale.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0.0f; @@ -141,21 +193,18 @@ static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12Graphic // Bind shader and vertex buffers unsigned int stride = sizeof(ImDrawVert); unsigned int offset = 0; - D3D12_VERTEX_BUFFER_VIEW vbv; - memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW)); + D3D12_VERTEX_BUFFER_VIEW vbv = {}; vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset; vbv.SizeInBytes = fr->VertexBufferSize * stride; vbv.StrideInBytes = stride; command_list->IASetVertexBuffers(0, 1, &vbv); - D3D12_INDEX_BUFFER_VIEW ibv; - memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW)); + D3D12_INDEX_BUFFER_VIEW ibv = {}; ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress(); ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx); ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; command_list->IASetIndexBuffer(&ibv); command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - command_list->SetPipelineState(bd->pPipelineState); - command_list->SetGraphicsRootSignature(bd->pRootSignature); + ImGui_ImplDX12_SetupSamplerLinear(command_list); command_list->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0); // Setup blend factor @@ -178,8 +227,14 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - // FIXME: I'm assuming that this only gets called once per frame! - // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplDX12_UpdateTexture(tex); + + // FIXME: We are assuming that this only gets called once per frame! ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); bd->frameIndex = bd->frameIndex + 1; ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight]; @@ -189,13 +244,11 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL { SafeRelease(fr->VertexBuffer); fr->VertexBufferSize = draw_data->TotalVtxCount + 5000; - D3D12_HEAP_PROPERTIES props; - memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); + D3D12_HEAP_PROPERTIES props = {}; props.Type = D3D12_HEAP_TYPE_UPLOAD; props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - D3D12_RESOURCE_DESC desc; - memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC)); + D3D12_RESOURCE_DESC desc = {}; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Width = fr->VertexBufferSize * sizeof(ImDrawVert); desc.Height = 1; @@ -212,13 +265,11 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL { SafeRelease(fr->IndexBuffer); fr->IndexBufferSize = draw_data->TotalIdxCount + 10000; - D3D12_HEAP_PROPERTIES props; - memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); + D3D12_HEAP_PROPERTIES props = {}; props.Type = D3D12_HEAP_TYPE_UPLOAD; props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - D3D12_RESOURCE_DESC desc; - memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC)); + D3D12_RESOURCE_DESC desc = {}; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Width = fr->IndexBufferSize * sizeof(ImDrawIdx); desc.Height = 1; @@ -242,9 +293,8 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL return; ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource; ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource; - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); vtx_dst += draw_list->VtxBuffer.Size; @@ -274,9 +324,9 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL int global_vtx_offset = 0; int global_idx_offset = 0; ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) + ImVec2 clip_scale = draw_data->FramebufferScale; + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; @@ -292,8 +342,8 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL else { // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); - ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + 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; @@ -311,23 +361,43 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL global_idx_offset += draw_list->IdxBuffer.Size; global_vtx_offset += draw_list->VtxBuffer.Size; } - platform_io.Renderer_RenderState = NULL; + platform_io.Renderer_RenderState = nullptr; } -static void ImGui_ImplDX12_CreateFontsTexture() +static void ImGui_ImplDX12_DestroyTexture(ImTextureData* tex) { - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - // Upload texture to graphics system - ImGui_ImplDX12_Texture* font_tex = &bd->FontTexture; + if (ImGui_ImplDX12_Texture* backend_tex = (ImGui_ImplDX12_Texture*)tex->BackendUserData) { - D3D12_HEAP_PROPERTIES props; - memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); + IM_ASSERT(backend_tex->hFontSrvGpuDescHandle.ptr == (UINT64)tex->TexID); + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, backend_tex->hFontSrvCpuDescHandle, backend_tex->hFontSrvGpuDescHandle); + SafeRelease(backend_tex->pTextureResource); + backend_tex->hFontSrvCpuDescHandle.ptr = 0; + backend_tex->hFontSrvGpuDescHandle.ptr = 0; + IM_DELETE(backend_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->BackendUserData = nullptr; + } + tex->SetStatus(ImTextureStatus_Destroyed); +} + +void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) +{ + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + bool need_barrier_before_copy = true; // Do we need a resource barrier before we copy new data in? + + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + ImGui_ImplDX12_Texture* backend_tex = IM_NEW(ImGui_ImplDX12_Texture)(); + bd->InitInfo.SrvDescriptorAllocFn(&bd->InitInfo, &backend_tex->hFontSrvCpuDescHandle, &backend_tex->hFontSrvGpuDescHandle); // Allocate a desctriptor handle + + D3D12_HEAP_PROPERTIES props = {}; props.Type = D3D12_HEAP_TYPE_DEFAULT; props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; @@ -336,8 +406,8 @@ static void ImGui_ImplDX12_CreateFontsTexture() ZeroMemory(&desc, sizeof(desc)); desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; desc.Alignment = 0; - desc.Width = width; - desc.Height = height; + desc.Width = tex->Width; + desc.Height = tex->Height; desc.DepthOrArraySize = 1; desc.MipLevels = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -350,104 +420,7 @@ static void ImGui_ImplDX12_CreateFontsTexture() bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pTexture)); - UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); - UINT uploadSize = height * uploadPitch; - desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - desc.Alignment = 0; - desc.Width = uploadSize; - desc.Height = 1; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - desc.Flags = D3D12_RESOURCE_FLAG_NONE; - - props.Type = D3D12_HEAP_TYPE_UPLOAD; - props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - - ID3D12Resource* uploadBuffer = nullptr; - HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, - D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)); - IM_ASSERT(SUCCEEDED(hr)); - - void* mapped = nullptr; - D3D12_RANGE range = { 0, uploadSize }; - hr = uploadBuffer->Map(0, &range, &mapped); - IM_ASSERT(SUCCEEDED(hr)); - for (int y = 0; y < height; y++) - memcpy((void*) ((uintptr_t) mapped + y * uploadPitch), pixels + y * width * 4, width * 4); - uploadBuffer->Unmap(0, &range); - - D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; - srcLocation.pResource = uploadBuffer; - srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - srcLocation.PlacedFootprint.Footprint.Width = width; - srcLocation.PlacedFootprint.Footprint.Height = height; - srcLocation.PlacedFootprint.Footprint.Depth = 1; - srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch; - - D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; - dstLocation.pResource = pTexture; - dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dstLocation.SubresourceIndex = 0; - - D3D12_RESOURCE_BARRIER barrier = {}; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = pTexture; - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - - ID3D12Fence* fence = nullptr; - hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); - IM_ASSERT(SUCCEEDED(hr)); - - HANDLE event = CreateEvent(0, 0, 0, 0); - IM_ASSERT(event != nullptr); - - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.NodeMask = 1; - - ID3D12CommandQueue* cmdQueue = nullptr; - hr = bd->pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue)); - IM_ASSERT(SUCCEEDED(hr)); - - ID3D12CommandAllocator* cmdAlloc = nullptr; - hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc)); - IM_ASSERT(SUCCEEDED(hr)); - - ID3D12GraphicsCommandList* cmdList = nullptr; - hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList)); - IM_ASSERT(SUCCEEDED(hr)); - - cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr); - cmdList->ResourceBarrier(1, &barrier); - - hr = cmdList->Close(); - IM_ASSERT(SUCCEEDED(hr)); - - cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList); - hr = cmdQueue->Signal(fence, 1); - IM_ASSERT(SUCCEEDED(hr)); - - fence->SetEventOnCompletion(1, event); - WaitForSingleObject(event, INFINITE); - - cmdList->Release(); - cmdAlloc->Release(); - cmdQueue->Release(); - CloseHandle(event); - fence->Release(); - uploadBuffer->Release(); - - // Create texture view + // Create SRV D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; ZeroMemory(&srvDesc, sizeof(srvDesc)); srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -455,13 +428,143 @@ static void ImGui_ImplDX12_CreateFontsTexture() srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, font_tex->hFontSrvCpuDescHandle); - SafeRelease(font_tex->pTextureResource); - font_tex->pTextureResource = pTexture; + bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, backend_tex->hFontSrvCpuDescHandle); + SafeRelease(backend_tex->pTextureResource); + backend_tex->pTextureResource = pTexture; + + // Store identifiers + tex->SetTexID((ImTextureID)backend_tex->hFontSrvGpuDescHandle.ptr); + tex->BackendUserData = backend_tex; + need_barrier_before_copy = false; // Because this is a newly-created texture it will be in D3D12_RESOURCE_STATE_COMMON and thus we don't need a barrier + // We don't set tex->Status to ImTextureStatus_OK to let the code fallthrough below. } - // Store our identifier - io.Fonts->SetTexID((ImTextureID)font_tex->hFontSrvGpuDescHandle.ptr); + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) + { + ImGui_ImplDX12_Texture* backend_tex = (ImGui_ImplDX12_Texture*)tex->BackendUserData; + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + + // We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture. + // FIXME-OPT: Uploading single box even when using ImTextureStatus_WantUpdates. Could use tex->Updates[] + // - Copy all blocks contiguously in upload buffer. + // - Barrier before copy, submit all CopyTextureRegion(), barrier after copy. + const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x; + const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y; + const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w; + const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h; + + // Update full texture or selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions. + UINT upload_pitch_src = upload_w * tex->BytesPerPixel; + UINT upload_pitch_dst = (upload_pitch_src + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); + UINT upload_size = upload_pitch_dst * upload_h; + + if (bd->pTexUploadBuffer == nullptr || upload_size > bd->pTexUploadBufferSize) + { + if (bd->pTexUploadBufferMapped) + { + D3D12_RANGE range = { 0, bd->pTexUploadBufferSize }; + bd->pTexUploadBuffer->Unmap(0, &range); + bd->pTexUploadBufferMapped = nullptr; + } + SafeRelease(bd->pTexUploadBuffer); + + D3D12_RESOURCE_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + desc.Alignment = 0; + desc.Width = upload_size; + desc.Height = 1; + desc.DepthOrArraySize = 1; + desc.MipLevels = 1; + desc.Format = DXGI_FORMAT_UNKNOWN; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + desc.Flags = D3D12_RESOURCE_FLAG_NONE; + + D3D12_HEAP_PROPERTIES props; + memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); + props.Type = D3D12_HEAP_TYPE_UPLOAD; + props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&bd->pTexUploadBuffer)); + IM_ASSERT(SUCCEEDED(hr)); + + D3D12_RANGE range = {0, upload_size}; + hr = bd->pTexUploadBuffer->Map(0, &range, &bd->pTexUploadBufferMapped); + IM_ASSERT(SUCCEEDED(hr)); + bd->pTexUploadBufferSize = upload_size; + } + + bd->pTexCmdAllocator->Reset(); + bd->pTexCmdList->Reset(bd->pTexCmdAllocator, nullptr); + ID3D12GraphicsCommandList* cmdList = bd->pTexCmdList; + + // Copy to upload buffer + for (int y = 0; y < upload_h; y++) + memcpy((void*)((uintptr_t)bd->pTexUploadBufferMapped + y * upload_pitch_dst), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch_src); + + if (need_barrier_before_copy) + { + D3D12_RESOURCE_BARRIER barrier = {}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = backend_tex->pTextureResource; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + cmdList->ResourceBarrier(1, &barrier); + } + + D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; + D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; + { + srcLocation.pResource = bd->pTexUploadBuffer; + srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srcLocation.PlacedFootprint.Footprint.Width = upload_w; + srcLocation.PlacedFootprint.Footprint.Height = upload_h; + srcLocation.PlacedFootprint.Footprint.Depth = 1; + srcLocation.PlacedFootprint.Footprint.RowPitch = upload_pitch_dst; + dstLocation.pResource = backend_tex->pTextureResource; + dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dstLocation.SubresourceIndex = 0; + } + cmdList->CopyTextureRegion(&dstLocation, upload_x, upload_y, 0, &srcLocation, nullptr); + + { + D3D12_RESOURCE_BARRIER barrier = {}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = backend_tex->pTextureResource; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + cmdList->ResourceBarrier(1, &barrier); + } + + HRESULT hr = cmdList->Close(); + IM_ASSERT(SUCCEEDED(hr)); + ID3D12CommandQueue* cmdQueue = bd->pCommandQueue; + cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList); + hr = cmdQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue); + IM_ASSERT(SUCCEEDED(hr)); + + // FIXME-OPT: Suboptimal? + // - To remove this may need to create NumFramesInFlight x ImGui_ImplDX12_FrameContext in backend data (mimick docking version) + // - Store per-frame in flight: upload buffer? + // - Where do cmdList and cmdAlloc fit? + bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent); + ::WaitForSingleObject(bd->FenceEvent, INFINITE); + + tex->SetStatus(ImTextureStatus_OK); + } + + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames >= (int)bd->numFramesInFlight) + ImGui_ImplDX12_DestroyTexture(tex); } bool ImGui_ImplDX12_CreateDeviceObjects() @@ -469,9 +572,16 @@ bool ImGui_ImplDX12_CreateDeviceObjects() ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); if (!bd || !bd->pd3dDevice) return false; - if (bd->pPipelineState) + if (bd->pPipelineStateLinear) ImGui_ImplDX12_InvalidateDeviceObjects(); + HRESULT hr = ::CreateDXGIFactory1(IID_PPV_ARGS(&bd->pdxgiFactory)); + IM_ASSERT(hr == S_OK); + + BOOL allow_tearing = FALSE; + bd->pdxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing)); + bd->tearingSupport = (allow_tearing == TRUE); + // Create the root signature { D3D12_DESCRIPTOR_RANGE descRange = {}; @@ -495,26 +605,26 @@ bool ImGui_ImplDX12_CreateDeviceObjects() param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. - D3D12_STATIC_SAMPLER_DESC staticSampler = {}; - staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; - staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSampler.MipLODBias = 0.f; - staticSampler.MaxAnisotropy = 0; - staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; - staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; - staticSampler.MinLOD = 0.f; - staticSampler.MaxLOD = 0.f; - staticSampler.ShaderRegister = 0; - staticSampler.RegisterSpace = 0; - staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + D3D12_STATIC_SAMPLER_DESC staticSampler[1] = {}; + staticSampler[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + staticSampler[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + staticSampler[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + staticSampler[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + staticSampler[0].MipLODBias = 0.f; + staticSampler[0].MaxAnisotropy = 0; + staticSampler[0].ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; + staticSampler[0].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + staticSampler[0].MinLOD = 0.f; + staticSampler[0].MaxLOD = D3D12_FLOAT32_MAX; + staticSampler[0].ShaderRegister = 0; + staticSampler[0].RegisterSpace = 0; + staticSampler[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; D3D12_ROOT_SIGNATURE_DESC desc = {}; desc.NumParameters = _countof(param); desc.pParameters = param; desc.NumStaticSamplers = 1; - desc.pStaticSamplers = &staticSampler; + desc.pStaticSamplers = &staticSampler[0]; desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | @@ -531,7 +641,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects() // (2) there exists a version of d3d12.dll for Windows 7 (D3D12On7) in one of the following directories. // See https://github.com/ocornut/imgui/pull/3696 for details. const char* localD3d12Paths[] = { ".\\d3d12.dll", ".\\d3d12on7\\d3d12.dll", ".\\12on7\\d3d12.dll" }; // A. current directory, B. used by some games, C. used in Microsoft D3D12On7 sample - for (int i = 0; i < IM_ARRAYSIZE(localD3d12Paths); i++) + for (int i = 0; i < IM_COUNTOF(localD3d12Paths); i++) if ((d3d12_dll = ::LoadLibraryA(localD3d12Paths[i])) != nullptr) break; @@ -543,7 +653,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects() return false; } - PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature"); + _PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (_PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)(void*)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature"); if (D3D12SerializeRootSignatureFn == nullptr) return false; @@ -551,7 +661,15 @@ bool ImGui_ImplDX12_CreateDeviceObjects() if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK) return false; - bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature)); + bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignatureLinear)); + blob->Release(); + + // Root Signature for ImDrawCallback_SetSamplerNearest + staticSampler[0].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK) + return false; + + bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignatureNearest)); blob->Release(); } @@ -561,14 +679,14 @@ bool ImGui_ImplDX12_CreateDeviceObjects() // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. // See https://github.com/ocornut/imgui/pull/638 for sources and details. - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc; - memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC)); + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; psoDesc.NodeMask = 1; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - psoDesc.pRootSignature = bd->pRootSignature; + psoDesc.pRootSignature = bd->pRootSignatureLinear; psoDesc.SampleMask = UINT_MAX; psoDesc.NumRenderTargets = 1; psoDesc.RTVFormats[0] = bd->RTVFormat; + psoDesc.DSVFormat = bd->DSVFormat; psoDesc.SampleDesc.Count = 1; psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; @@ -687,13 +805,36 @@ bool ImGui_ImplDX12_CreateDeviceObjects() desc.BackFace = desc.FrontFace; } - HRESULT result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState)); + HRESULT result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineStateLinear)); + if (result_pipeline_state != S_OK) + { + vertexShaderBlob->Release(); + pixelShaderBlob->Release(); + return false; + } + + // Pipeline State for ImDrawCallback_SetSamplerNearest + psoDesc.pRootSignature = bd->pRootSignatureNearest; + + result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineStateNearest)); vertexShaderBlob->Release(); pixelShaderBlob->Release(); if (result_pipeline_state != S_OK) return false; - ImGui_ImplDX12_CreateFontsTexture(); + // Create command allocator and command list for ImGui_ImplDX12_UpdateTexture() + hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&bd->pTexCmdAllocator)); + IM_ASSERT(SUCCEEDED(hr)); + hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, bd->pTexCmdAllocator, nullptr, IID_PPV_ARGS(&bd->pTexCmdList)); + IM_ASSERT(SUCCEEDED(hr)); + hr = bd->pTexCmdList->Close(); + IM_ASSERT(SUCCEEDED(hr)); + + // Create fence. + hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&bd->Fence)); + IM_ASSERT(hr == S_OK); + bd->FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + IM_ASSERT(bd->FenceEvent != nullptr); return true; } @@ -704,15 +845,32 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() if (!bd || !bd->pd3dDevice) return; - ImGuiIO& io = ImGui::GetIO(); - SafeRelease(bd->pRootSignature); - SafeRelease(bd->pPipelineState); + SafeRelease(bd->pdxgiFactory); + if (bd->commandQueueOwned) + SafeRelease(bd->pCommandQueue); + bd->commandQueueOwned = false; + SafeRelease(bd->pRootSignatureLinear); + SafeRelease(bd->pRootSignatureNearest); + SafeRelease(bd->pPipelineStateLinear); + SafeRelease(bd->pPipelineStateNearest); - // Free SRV descriptor used by texture - ImGui_ImplDX12_Texture* font_tex = &bd->FontTexture; - bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, font_tex->hFontSrvCpuDescHandle, font_tex->hFontSrvGpuDescHandle); - SafeRelease(font_tex->pTextureResource); - io.Fonts->SetTexID(0); // We copied bd->hFontSrvGpuDescHandle to io.Fonts->TexID so let's clear that as well. + if (bd->pTexUploadBufferMapped) + { + D3D12_RANGE range = { 0, bd->pTexUploadBufferSize }; + bd->pTexUploadBuffer->Unmap(0, &range); + bd->pTexUploadBufferMapped = nullptr; + } + SafeRelease(bd->pTexUploadBuffer); + SafeRelease(bd->pTexCmdList); + SafeRelease(bd->pTexCmdAllocator); + SafeRelease(bd->Fence); + CloseHandle(bd->FenceEvent); + bd->FenceEvent = nullptr; + + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplDX12_DestroyTexture(tex); for (UINT i = 0; i < bd->numFramesInFlight; i++) { @@ -722,6 +880,28 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() } } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +static void ImGui_ImplDX12_InitLegacySingleDescriptorMode(ImGui_ImplDX12_InitInfo* init_info) +{ + // Wrap legacy behavior of passing space for a single descriptor + IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0); + init_info->SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle) + { + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + IM_ASSERT(bd->LegacySingleDescriptorUsed == false && "Only 1 simultaneous texture allowed with legacy ImGui_ImplDX12_Init() signature!"); + *out_cpu_handle = bd->InitInfo.LegacySingleSrvCpuDescriptor; + *out_gpu_handle = bd->InitInfo.LegacySingleSrvGpuDescriptor; + bd->LegacySingleDescriptorUsed = true; + }; + init_info->SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE) + { + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + IM_ASSERT(bd->LegacySingleDescriptorUsed == true); + bd->LegacySingleDescriptorUsed = false; + }; +} +#endif + bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info) { ImGuiIO& io = ImGui::GetIO(); @@ -734,39 +914,24 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info) init_info = &bd->InitInfo; bd->pd3dDevice = init_info->Device; + IM_ASSERT(init_info->CommandQueue != nullptr); + bd->pCommandQueue = init_info->CommandQueue; bd->RTVFormat = init_info->RTVFormat; + bd->DSVFormat = init_info->DSVFormat; bd->numFramesInFlight = init_info->NumFramesInFlight; bd->pd3dSrvDescHeap = init_info->SrvDescriptorHeap; + bd->tearingSupport = false; io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx12"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (init_info->SrvDescriptorAllocFn == NULL) - { - // Wrap legacy behavior of passing space for a single descriptor - IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0); - init_info->SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle) - { - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - IM_ASSERT(bd->LegacySingleDescriptorUsed == false); - *out_cpu_handle = bd->InitInfo.LegacySingleSrvCpuDescriptor; - *out_gpu_handle = bd->InitInfo.LegacySingleSrvGpuDescriptor; - bd->LegacySingleDescriptorUsed = true; - }; - init_info->SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE) - { - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - IM_ASSERT(bd->LegacySingleDescriptorUsed == true); - bd->LegacySingleDescriptorUsed = false; - }; - } + if (init_info->SrvDescriptorAllocFn == nullptr) + ImGui_ImplDX12_InitLegacySingleDescriptorMode(init_info); #endif - - // Allocate 1 SRV descriptor for the font texture - IM_ASSERT(init_info->SrvDescriptorAllocFn != NULL && init_info->SrvDescriptorFreeFn != NULL); - init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->FontTexture.hFontSrvCpuDescHandle, &bd->FontTexture.hFontSrvGpuDescHandle); + IM_ASSERT(init_info->SrvDescriptorAllocFn != nullptr && init_info->SrvDescriptorFreeFn != nullptr); // Create buffers with a default size (they will later be grown as needed) bd->frameIndex = UINT_MAX; @@ -794,8 +959,22 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO init_info.RTVFormat = rtv_format; init_info.SrvDescriptorHeap = srv_descriptor_heap; init_info.LegacySingleSrvCpuDescriptor = font_srv_cpu_desc_handle; - init_info.LegacySingleSrvGpuDescriptor = font_srv_gpu_desc_handle;; - return ImGui_ImplDX12_Init(&init_info); + init_info.LegacySingleSrvGpuDescriptor = font_srv_gpu_desc_handle; + + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.NodeMask = 1; + HRESULT hr = device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&init_info.CommandQueue)); + IM_ASSERT(SUCCEEDED(hr)); + + bool ret = ImGui_ImplDX12_Init(&init_info); + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + bd->commandQueueOwned = true; + ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasTextures; // Using legacy ImGui_ImplDX12_Init() call with 1 SRV descriptor we cannot support multiple textures. + + return ret; } #endif @@ -804,14 +983,15 @@ void ImGui_ImplDX12_Shutdown() ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - // Clean up windows and device objects ImGui_ImplDX12_InvalidateDeviceObjects(); delete[] bd->pFrameResources; io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } @@ -820,8 +1000,9 @@ void ImGui_ImplDX12_NewFrame() ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX12_Init()?"); - if (!bd->pPipelineState) - ImGui_ImplDX12_CreateDeviceObjects(); + if (!bd->pPipelineStateLinear) + if (!ImGui_ImplDX12_CreateDeviceObjects()) + IM_ASSERT(0 && "ImGui_ImplDX12_CreateDeviceObjects() failed!"); } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index 34938fb56..b8be8db22 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification. @@ -23,17 +24,24 @@ #include // DXGI_FORMAT #include // D3D12_CPU_DESCRIPTOR_HANDLE +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#endif + // Initialization data, for ImGui_ImplDX12_Init() struct ImGui_ImplDX12_InitInfo { ID3D12Device* Device; - ID3D12CommandQueue* CommandQueue; + ID3D12CommandQueue* CommandQueue; // Command queue used for queuing texture uploads. int NumFramesInFlight; - DXGI_FORMAT RTVFormat; + DXGI_FORMAT RTVFormat; // RenderTarget format. + DXGI_FORMAT DSVFormat; // DepthStencilView format. void* UserData; // Allocating SRV descriptors for textures is up to the application, so we provide callbacks. - // (current version of the backend will only allocate one descriptor, future versions will need to allocate more) + // (current version of the backend will only allocate one descriptor, from 1.92 the backend will need to allocate more) ID3D12DescriptorHeap* SrvDescriptorHeap; void (*SrvDescriptorAllocFn)(ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle); void (*SrvDescriptorFreeFn)(ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_desc_handle); @@ -42,7 +50,7 @@ struct ImGui_ImplDX12_InitInfo D3D12_GPU_DESCRIPTOR_HANDLE LegacySingleSrvGpuDescriptor; #endif - ImGui_ImplDX12_InitInfo() { memset(this, 0, sizeof(*this)); } + ImGui_ImplDX12_InitInfo() { memset((void*)this, 0, sizeof(*this)); } }; // Follow "Getting Started" link and check examples/ folder to learn about using backends! @@ -53,7 +61,8 @@ IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Legacy initialization API Obsoleted in 1.91.5 -// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap' +// - font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap' +// - When we introduced the ImGui_ImplDX12_InitInfo struct we also added a 'ID3D12CommandQueue* CommandQueue' field. IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); #endif @@ -61,6 +70,9 @@ IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex); + // [BETA] Selected render state data shared with callbacks. // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX12_RenderDrawData() call. // (Please open an issue if you feel you need access to more data) @@ -70,4 +82,8 @@ struct ImGui_ImplDX12_RenderState ID3D12GraphicsCommandList* CommandList; }; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index c97d3c740..24911fadd 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -2,8 +2,10 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9. // 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. @@ -15,6 +17,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-19: Fixed issue in ImGui_ImplDX9_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-11: DirectX9: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. // 2024-10-07: DirectX9: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-02-12: DirectX9: Using RGBA format when supported by the driver to avoid CPU side conversion. (#6575) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -43,13 +48,18 @@ // DirectX #include +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + // DirectX data struct ImGui_ImplDX9_Data { LPDIRECT3DDEVICE9 pd3dDevice; LPDIRECT3DVERTEXBUFFER9 pVB; LPDIRECT3DINDEXBUFFER9 pIB; - LPDIRECT3DTEXTURE9 FontTexture; int VertexBufferSize; int IndexBufferSize; bool HasRgbaSupport; @@ -162,6 +172,13 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); LPDIRECT3DDEVICE9 device = bd->pd3dDevice; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplDX9_UpdateTexture(tex); + // Create and grow buffers if needed if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { @@ -213,9 +230,8 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) // FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and // 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR // 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; } - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_src = draw_list->VtxBuffer.Data; for (int i = 0; i < draw_list->VtxBuffer.Size; i++) { @@ -245,9 +261,8 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) int global_vtx_offset = 0; int global_idx_offset = 0; ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; @@ -321,6 +336,10 @@ bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx9"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = 4096; bd->pd3dDevice = device; bd->pd3dDevice->AddRef(); @@ -334,17 +353,20 @@ void ImGui_ImplDX9_Shutdown() ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplDX9_InvalidateDeviceObjects(); if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } // Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices) -static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, ImU32* src, int src_pitch, ImU32* dst, int dst_pitch, int w, int h) +static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, const ImU32* src, int src_pitch, ImU32* dst, int dst_pitch, int w, int h) { #ifndef IMGUI_USE_BGRA_PACKED_COLOR ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); @@ -355,8 +377,8 @@ static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, ImU32* src, int #endif for (int y = 0; y < h; y++) { - ImU32* src_p = (ImU32*)((unsigned char*)src + src_pitch * y); - ImU32* dst_p = (ImU32*)((unsigned char*)dst + dst_pitch * y); + const ImU32* src_p = (const ImU32*)(const void*)((const unsigned char*)src + src_pitch * y); + ImU32* dst_p = (ImU32*)(void*)((unsigned char*)dst + dst_pitch * y); if (convert_rgba_to_bgra) for (int x = w; x > 0; x--, src_p++, dst_p++) // Convert copy *dst_p = IMGUI_COL_TO_DX9_ARGB(*src_p); @@ -365,28 +387,62 @@ static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, ImU32* src, int } } -static bool ImGui_ImplDX9_CreateFontsTexture() +void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex) { - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); - unsigned char* pixels; - int width, height, bytes_per_pixel; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel); - // Upload texture to graphics system - bd->FontTexture = nullptr; - if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, bd->HasRgbaSupport ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &bd->FontTexture, nullptr) < 0) - return false; - D3DLOCKED_RECT tex_locked_rect; - if (bd->FontTexture->LockRect(0, &tex_locked_rect, nullptr, 0) != D3D_OK) - return false; - ImGui_ImplDX9_CopyTextureRegion(io.Fonts->TexPixelsUseColors, (ImU32*)pixels, width * bytes_per_pixel, (ImU32*)tex_locked_rect.pBits, (int)tex_locked_rect.Pitch, width, height); - bd->FontTexture->UnlockRect(0); + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + LPDIRECT3DTEXTURE9 dx_tex = nullptr; + HRESULT hr = bd->pd3dDevice->CreateTexture(tex->Width, tex->Height, 1, D3DUSAGE_DYNAMIC, bd->HasRgbaSupport ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &dx_tex, nullptr); + if (hr < 0) + { + IM_ASSERT(hr >= 0 && "Backend failed to create texture!"); + return; + } - // Store our identifier - io.Fonts->SetTexID((ImTextureID)bd->FontTexture); - return true; + D3DLOCKED_RECT locked_rect; + if (dx_tex->LockRect(0, &locked_rect, nullptr, 0) == D3D_OK) + { + ImGui_ImplDX9_CopyTextureRegion(tex->UseColors, (ImU32*)tex->GetPixels(), tex->Width * 4, (ImU32*)locked_rect.pBits, (ImU32)locked_rect.Pitch, tex->Width, tex->Height); + dx_tex->UnlockRect(0); + } + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)dx_tex); + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantUpdates) + { + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + LPDIRECT3DTEXTURE9 backend_tex = (LPDIRECT3DTEXTURE9)(intptr_t)tex->TexID; + RECT update_rect = { (LONG)tex->UpdateRect.x, (LONG)tex->UpdateRect.y, (LONG)(tex->UpdateRect.x + tex->UpdateRect.w), (LONG)(tex->UpdateRect.y + tex->UpdateRect.h) }; + D3DLOCKED_RECT locked_rect; + if (backend_tex->LockRect(0, &locked_rect, &update_rect, 0) == D3D_OK) + for (ImTextureRect& r : tex->Updates) + ImGui_ImplDX9_CopyTextureRegion(tex->UseColors, (ImU32*)tex->GetPixelsAt(r.x, r.y), tex->Width * 4, + (ImU32*)locked_rect.pBits + (r.x - update_rect.left) + (r.y - update_rect.top) * (locked_rect.Pitch / 4), (int)locked_rect.Pitch, r.w, r.h); + backend_tex->UnlockRect(0); + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantDestroy) + { + if (tex->TexID != ImTextureID_Invalid) + if (LPDIRECT3DTEXTURE9 backend_tex = (LPDIRECT3DTEXTURE9)tex->TexID) + { + IM_ASSERT(tex->TexID == (ImTextureID)(intptr_t)backend_tex); + backend_tex->Release(); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + } + tex->SetStatus(ImTextureStatus_Destroyed); + } } bool ImGui_ImplDX9_CreateDeviceObjects() @@ -394,8 +450,6 @@ bool ImGui_ImplDX9_CreateDeviceObjects() ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); if (!bd || !bd->pd3dDevice) return false; - if (!ImGui_ImplDX9_CreateFontsTexture()) - return false; return true; } @@ -404,18 +458,23 @@ void ImGui_ImplDX9_InvalidateDeviceObjects() ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); if (!bd || !bd->pd3dDevice) return; + + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + { + tex->SetStatus(ImTextureStatus_WantDestroy); + ImGui_ImplDX9_UpdateTexture(tex); + } if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } - if (bd->FontTexture) { bd->FontTexture->Release(); bd->FontTexture = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. } void ImGui_ImplDX9_NewFrame() { ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX9_Init()?"); - - if (!bd->FontTexture) - ImGui_ImplDX9_CreateDeviceObjects(); + IM_UNUSED(bd); } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_dx9.h b/backends/imgui_impl_dx9.h index df6a0a012..021439b9a 100644 --- a/backends/imgui_impl_dx9.h +++ b/backends/imgui_impl_dx9.h @@ -2,8 +2,10 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9. // 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. @@ -29,4 +31,7 @@ IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data); IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex); + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index afa2185a3..27f5fe90a 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -1,14 +1,18 @@ // 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+ or GLFW 3.4+ for full feature support.) +// (Requires: GLFW 3.0+. Prefer GLFW 3.3+/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 are obsolete since 1.87 and not supported since 1.91.5] // [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+). +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors) with GLFW 3.1+. Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Multiple Dear ImGui contexts support. +// Missing features or Issues: +// [ ] Platform: Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. +// [ ] Platform: Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. // 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. @@ -25,7 +29,19 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// 2026-03-25: Mouse cursor is properly restored if changed by user app/code while using glfwSetInputMode(..., GLFW_CURSOR_DISABLED) or ImGuiConfigFlags_NoMouseCursorChange. Amend change from 2025-12-10. +// 2026-02-10: Try to set IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND automatically if corresponding headers are not accessible. (#9225) +// 2025-12-12: Added IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND to forcefully disable either. +// 2025-12-10: Avoid repeated glfwSetCursor()/glfwSetInputMode() calls when unnecessary. Lowers overhead for very high framerates (e.g. 10k+ FPS). +// 2025-11-06: Lower minimum requirement to GLFW 3.0. Though a recent version e.g GLFW 3.4 is highly recommended. +// 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. +// 2025-09-15: Content Scales are always reported as 1.0 on Wayland. FramebufferScale are always reported as 1.0 on X11. (#8920, #8921) +// 2025-07-08: Made ImGui_ImplGlfw_GetContentScaleForWindow(), ImGui_ImplGlfw_GetContentScaleForMonitor() helpers return 1.0f on Emscripten and Android platforms, matching macOS logic. (#8742, #8733) +// 2025-06-18: Added support for multiple Dear ImGui contexts. (#8676, #8239, #8069) +// 2025-06-11: Added ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) and ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor) helper to facilitate making DPI-aware apps. +// 2025-03-10: Map GLFW_KEY_WORLD_1 and GLFW_KEY_WORLD_2 into ImGuiKey_Oem102. +// 2025-03-03: Fixed clipboard handler assertion when using GLFW <= 3.2.1 compiled with asserts enabled. +// 2024-08-22: Moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn // - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn // - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn @@ -89,29 +105,55 @@ // 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 +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning: declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is. +#endif + +#if defined(__has_include) +#if !__has_include() || !__has_include() +#define IMGUI_IMPL_GLFW_DISABLE_X11 +#endif +#if !__has_include() +#define IMGUI_IMPL_GLFW_DISABLE_WAYLAND +#endif #endif // GLFW +#if !defined(IMGUI_IMPL_GLFW_DISABLE_X11) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)) +#define GLFW_HAS_X11 1 +#else +#define GLFW_HAS_X11 0 +#endif +#if !defined(IMGUI_IMPL_GLFW_DISABLE_WAYLAND) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)) +#define GLFW_HAS_WAYLAND 1 +#else +#define GLFW_HAS_WAYLAND 0 +#endif #include - #ifdef _WIN32 #undef APIENTRY -#ifndef GLFW_EXPOSE_NATIVE_WIN32 +#ifndef GLFW_EXPOSE_NATIVE_WIN32 // for glfwGetWin32Window() #define GLFW_EXPOSE_NATIVE_WIN32 #endif -#include // for glfwGetWin32Window() -#endif -#ifdef __APPLE__ -#ifndef GLFW_EXPOSE_NATIVE_COCOA +#include +#elif defined(__APPLE__) +#ifndef GLFW_EXPOSE_NATIVE_COCOA // for glfwGetCocoaWindow() #define GLFW_EXPOSE_NATIVE_COCOA #endif -#include // for glfwGetCocoaWindow() +#include +#elif GLFW_HAS_X11 +#ifndef GLFW_EXPOSE_NATIVE_X11 // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.) +#define GLFW_EXPOSE_NATIVE_X11 #endif +#include +#endif +#undef Status // X11 headers are leaking this. #ifndef _WIN32 #include // for usleep() #endif +#include // for snprintf() #ifdef __EMSCRIPTEN__ #include @@ -125,6 +167,8 @@ // 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_CREATECURSOR (GLFW_VERSION_COMBINED >= 3100) // 3.1+ glfwCreateCursor() +#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorContentScale #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 @@ -133,25 +177,42 @@ #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() +#define GLFW_HAS_GETPLATFORM (GLFW_VERSION_COMBINED >= 3400) // 3.4+ glfwGetPlatform() + +// Map GLFWWindow* to ImGuiContext*. +// - Would be simpler if we could use glfwSetWindowUserPointer()/glfwGetWindowUserPointer(), but this is a single and shared resource. +// - Would be simpler if we could use e.g. std::map<> as well. But we don't. +// - This is not particularly optimized as we expect size to be small and queries to be rare. +struct ImGui_ImplGlfw_WindowToContext { GLFWwindow* Window; ImGuiContext* Context; }; +static ImVector g_ContextMap; +static void ImGui_ImplGlfw_ContextMap_Add(GLFWwindow* window, ImGuiContext* ctx) { g_ContextMap.push_back(ImGui_ImplGlfw_WindowToContext{ window, ctx }); } +static void ImGui_ImplGlfw_ContextMap_Remove(GLFWwindow* window) { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) { g_ContextMap.erase_unsorted(&entry); if (g_ContextMap.empty()) g_ContextMap.clear(); return; } } +static ImGuiContext* ImGui_ImplGlfw_ContextMap_Get(GLFWwindow* window) { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) return entry.Context; return nullptr; } -// GLFW data enum GlfwClientApi { - GlfwClientApi_Unknown, GlfwClientApi_OpenGL, GlfwClientApi_Vulkan, + GlfwClientApi_Unknown, // Anything else fits here. }; +// GLFW data struct ImGui_ImplGlfw_Data { + ImGuiContext* Context; GLFWwindow* Window; GlfwClientApi ClientApi; double Time; GLFWwindow* MouseWindow; +#if GLFW_HAS_CREATECURSOR GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; + GLFWcursor* LastMouseCursor; +#endif ImVec2 LastValidMousePos; + bool IsWayland; bool InstalledCallbacks; bool CallbacksChainForAllWindows; + char BackendPlatformName[32]; #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 const char* CanvasSelector; #endif @@ -179,12 +240,37 @@ struct ImGui_ImplGlfw_Data // (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks. // - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it. // FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +namespace ImGui { extern ImGuiIO& GetIO(ImGuiContext*); } static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() { + // Get data for current context return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } +static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData(GLFWwindow* window) +{ + // Get data for a given GLFW window, regardless of current context (since GLFW events are sent together) + ImGuiContext* ctx = ImGui_ImplGlfw_ContextMap_Get(window); + return (ImGui_ImplGlfw_Data*)ImGui::GetIO(ctx).BackendPlatformUserData; +} // Functions +static bool ImGui_ImplGlfw_IsWayland() +{ +#if !GLFW_HAS_WAYLAND + return false; +#elif GLFW_HAS_GETPLATFORM + return glfwGetPlatform() == GLFW_PLATFORM_WAYLAND; +#else + const char* version = glfwGetVersionString(); + if (strstr(version, "Wayland") == nullptr) // e.g. Ubuntu 22.04 ships with GLFW 3.3.6 compiled without Wayland + return false; +#ifdef GLFW_EXPOSE_NATIVE_X11 + if (glfwGetX11Display() != nullptr) + return false; +#endif + return true; +#endif +} // Not static to allow third-party code to use that if they want to (but undocumented) ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode); @@ -217,6 +303,8 @@ ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode) case GLFW_KEY_EQUAL: return ImGuiKey_Equal; case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; + case GLFW_KEY_WORLD_1: return ImGuiKey_Oem102; + case GLFW_KEY_WORLD_2: return ImGuiKey_Oem102; case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; @@ -316,38 +404,35 @@ ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode) // 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) +static void ImGui_ImplGlfw_UpdateKeyModifiers(ImGuiIO& io, GLFWwindow* window) { - ImGuiIO& io = ImGui::GetIO(); 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) +static bool ImGui_ImplGlfw_ShouldChainCallback(ImGui_ImplGlfw_Data* bd, 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 && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackMousebutton(window, button, action, mods); - ImGui_ImplGlfw_UpdateKeyModifiers(window); - - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); + ImGui_ImplGlfw_UpdateKeyModifiers(io, window); if (button >= 0 && button < ImGuiMouseButton_COUNT) io.AddMouseButtonEvent(button, action == GLFW_PRESS); } void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackScroll(window, xoffset, yoffset); #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 @@ -355,7 +440,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo return; #endif - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddMouseWheelEvent((float)xoffset, (float)yoffset); } @@ -380,7 +465,7 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) { const char char_names[] = "`-=[]\\,;\'./"; const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; - IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys)); + IM_ASSERT(IM_COUNTOF(char_names) == IM_COUNTOF(char_keys)); if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); } else if (key_name[0] >= 'a' && key_name[0] <= 'z') { key = GLFW_KEY_A + (key_name[0] - 'a'); } @@ -395,18 +480,18 @@ 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 && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); if (action != GLFW_PRESS && action != GLFW_RELEASE) return; - ImGui_ImplGlfw_UpdateKeyModifiers(window); + ImGuiIO& io = ImGui::GetIO(bd->Context); + ImGui_ImplGlfw_UpdateKeyModifiers(io, window); keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); - ImGuiIO& io = ImGui::GetIO(); ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode); io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) @@ -414,21 +499,21 @@ 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 && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackWindowFocus(window, focused); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddFocusEvent(focused != 0); } void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackCursorPos(window, x, y); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddMousePosEvent((float)x, (float)y); bd->LastValidMousePos = ImVec2((float)x, (float)y); } @@ -437,11 +522,11 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) // so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackCursorEnter(window, entered); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); if (entered) { bd->MouseWindow = window; @@ -457,31 +542,32 @@ 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 && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackChar(window, c); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddInputCharacter(c); } void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) { - // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. + // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. } #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 -static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*) +static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void* user_data) { // Mimic Emscripten_HandleWheel() in SDL. // Corresponding equivalent in GLFW JS emulation layer has incorrect quantizing preventing small values. See #6096 + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; 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(); + ImGuiIO& io = ImGui::GetIO(bd->Context); 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; @@ -502,7 +588,9 @@ static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() } static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)::GetPropA(hWnd, "IMGUI_BACKEND_DATA"); + ImGuiIO& io = ImGui::GetIO(bd->Context); + switch (msg) { case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: @@ -510,8 +598,9 @@ static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wPara 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()); + io.AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo()); break; + default: break; } return ::CallWindowProcW(bd->PrevWndProc, hWnd, msg, wParam, lParam); } @@ -519,7 +608,7 @@ static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wPara void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); IM_ASSERT(bd->Window == window); @@ -536,7 +625,7 @@ void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); IM_ASSERT(bd->Window == window); @@ -559,7 +648,7 @@ 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. +// 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. @@ -586,17 +675,29 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Setup backend capabilities flags ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)(); + snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_glfw (%d)", GLFW_VERSION_COMBINED); io.BackendPlatformUserData = (void*)bd; - io.BackendPlatformName = "imgui_impl_glfw"; + io.BackendPlatformName = bd->BackendPlatformName; +#if GLFW_HAS_CREATECURSOR io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) +#endif io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + bd->Context = ImGui::GetCurrentContext(); bd->Window = window; bd->Time = 0.0; + bd->IsWayland = ImGui_ImplGlfw_IsWayland(); + ImGui_ImplGlfw_ContextMap_Add(window, bd->Context); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(NULL, text); }; - platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(NULL); }; +#if GLFW_VERSION_COMBINED < 3300 + platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(ImGui_ImplGlfw_GetBackendData()->Window, text); }; + platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(ImGui_ImplGlfw_GetBackendData()->Window); }; +#else + platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(nullptr, text); }; + platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(nullptr); }; +#endif + #ifdef __EMSCRIPTEN__ platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; #endif @@ -605,6 +706,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. // Missing cursors will return nullptr and our _UpdateMouseCursor() function will use the Arrow cursor instead.) +#if GLFW_HAS_CREATECURSOR GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); @@ -623,6 +725,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); #endif glfwSetErrorCallback(prev_error_callback); +#endif #if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) (void)glfwGetError(nullptr); #endif @@ -644,7 +747,9 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Windows: register a WndProc hook so we can intercept some messages. #ifdef _WIN32 - bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC); + HWND hwnd = (HWND)main_viewport->PlatformHandleRaw; + ::SetPropA(hwnd, "IMGUI_BACKEND_DATA", bd); + bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW(hwnd, GWLP_WNDPROC); IM_ASSERT(bd->PrevWndProc != nullptr); ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); #endif @@ -655,7 +760,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw #if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817 if (emscripten::glfw3::IsRuntimePlatformApple()) { - ImGui::GetIO().ConfigMacOSXBehaviors = true; + io.ConfigMacOSXBehaviors = true; // Due to how the browser (poorly) handles the Meta Key, this line essentially disables repeats when used. // This means that Meta + V only registers a single key-press, even if the keys are held. @@ -689,7 +794,9 @@ void ImGui_ImplGlfw_Shutdown() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); if (bd->InstalledCallbacks) ImGui_ImplGlfw_RestoreCallbacks(bd->Window); @@ -697,13 +804,14 @@ void ImGui_ImplGlfw_Shutdown() if (bd->CanvasSelector) emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr); #endif - +#if GLFW_HAS_CREATECURSOR for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) glfwDestroyCursor(bd->MouseCursors[cursor_n]); - +#endif // Windows: restore our WndProc hook #ifdef _WIN32 ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ::SetPropA((HWND)main_viewport->PlatformHandleRaw, "IMGUI_BACKEND_DATA", nullptr); ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->PrevWndProc); bd->PrevWndProc = nullptr; #endif @@ -711,6 +819,8 @@ void ImGui_ImplGlfw_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + platform_io.ClearPlatformHandlers(); + ImGui_ImplGlfw_ContextMap_Remove(bd->Window); IM_DELETE(bd); } @@ -750,7 +860,10 @@ static void ImGui_ImplGlfw_UpdateMouseCursor() ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + { + bd->LastMouseCursor = nullptr; // Invalidate so that if user changes underlying cursor we will update it next time we can. return; + } ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) @@ -758,14 +871,25 @@ static void ImGui_ImplGlfw_UpdateMouseCursor() GLFWwindow* window = bd->Window; if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + if (bd->LastMouseCursor != nullptr) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + bd->LastMouseCursor = nullptr; + } } else { // Show OS mouse cursor // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. - glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); +#if GLFW_HAS_CREATECURSOR + GLFWcursor* cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]; + if (bd->LastMouseCursor != cursor) + { + glfwSetCursor(window, cursor); + bd->LastMouseCursor = cursor; + } +#endif glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } } @@ -776,7 +900,7 @@ static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0 static void ImGui_ImplGlfw_UpdateGamepads() { ImGuiIO& io = ImGui::GetIO(); - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs, but see #8075 return; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; @@ -824,20 +948,69 @@ static void ImGui_ImplGlfw_UpdateGamepads() #undef MAP_ANALOG } +// - On Windows the process needs to be marked DPI-aware!! SDL2 doesn't do it by default. You can call ::SetProcessDPIAware() or call ImGui_ImplWin32_EnableDpiAwareness() from Win32 backend. +// - Apple platforms use FramebufferScale so we always return 1.0f. +// - Some accessibility applications are declaring virtual monitors with a DPI of 0.0f, see #7902. We preserve this value for caller to handle. +float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) +{ +#if GLFW_HAS_WAYLAND + if (ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window)) + if (bd->IsWayland) + return 1.0f; +#endif +#if GLFW_HAS_PER_MONITOR_DPI && !(defined(__APPLE__) || defined(__EMSCRIPTEN__) || defined(__ANDROID__)) + float x_scale, y_scale; + glfwGetWindowContentScale(window, &x_scale, &y_scale); + return x_scale; +#else + IM_UNUSED(window); + return 1.0f; +#endif +} + +float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor) +{ +#if GLFW_HAS_WAYLAND + if (ImGui_ImplGlfw_IsWayland()) // We can't access our bd->IsWayland cache for a monitor. + return 1.0f; +#endif +#if GLFW_HAS_PER_MONITOR_DPI && !(defined(__APPLE__) || defined(__EMSCRIPTEN__) || defined(__ANDROID__)) + float x_scale, y_scale; + glfwGetMonitorContentScale(monitor, &x_scale, &y_scale); + return x_scale; +#else + IM_UNUSED(monitor); + return 1.0f; +#endif +} + +static void ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(GLFWwindow* window, ImVec2* out_size, ImVec2* out_framebuffer_scale) +{ + int w, h; + int display_w, display_h; + glfwGetWindowSize(window, &w, &h); + glfwGetFramebufferSize(window, &display_w, &display_h); + float fb_scale_x = (w > 0) ? (float)display_w / (float)w : 1.0f; + float fb_scale_y = (h > 0) ? (float)display_h / (float)h : 1.0f; +#if GLFW_HAS_WAYLAND + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (!bd->IsWayland) + fb_scale_x = fb_scale_y = 1.0f; +#endif + if (out_size != nullptr) + *out_size = ImVec2((float)w, (float)h); + if (out_framebuffer_scale != nullptr) + *out_framebuffer_scale = ImVec2(fb_scale_x, fb_scale_y); +} + void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?"); - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - glfwGetWindowSize(bd->Window, &w, &h); - glfwGetFramebufferSize(bd->Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - if (w > 0 && h > 0) - io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); + // Setup main viewport size (every frame to accommodate for window resizing) + ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale); // 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) @@ -901,7 +1074,7 @@ void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow*, const char* canvas_s // 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? - emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, ImGui_ImplEmscripten_WheelCallback); + emscripten_set_wheel_callback(bd->CanvasSelector, bd, false, ImGui_ImplEmscripten_WheelCallback); } #elif defined(EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3) // When using --use-port=contrib.glfw3 for the GLFW implementation, you can override the behavior of this call diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 5d8940a0c..7dc8cb5e1 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -1,13 +1,18 @@ // 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.0+. Prefer GLFW 3.3+/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 are obsolete since 1.87 and not supported since 1.91.5] // [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+). +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors) with GLFW 3.1+. Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Multiple Dear ImGui contexts support. +// Missing features or Issues: +// [ ] Platform: Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. +// [ ] Platform: Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. // 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. @@ -43,7 +48,7 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* wi IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window); IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window); -// GFLW callbacks options: +// GLFW 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); @@ -59,5 +64,8 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int // GLFW helpers IMGUI_IMPL_API void ImGui_ImplGlfw_Sleep(int milliseconds); +IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window); +IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor); + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index 6bb9eae7e..a68f9dbc8 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -7,7 +7,7 @@ // Implemented features: // [X] Platform: Partial 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 GLUT values are obsolete since 1.87 and not supported since 1.91.5] -// Issues: +// Missing features or Issues: // [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I // [ ] Platform: Missing horizontal mouse wheel support. // [ ] Platform: Missing mouse cursor shape/visibility support. @@ -50,7 +50,7 @@ static int g_Time = 0; // Current time, in milliseconds -// Glut has 1 function for characters and one for "special keys". We map the characters in the 0..255 range and the keys above. +// Glut has one function for characters and one for "special keys". We map the characters in the 0..255 range and the keys above. static ImGuiKey ImGui_ImplGLUT_KeyToImGuiKey(int key) { switch (key) diff --git a/backends/imgui_impl_glut.h b/backends/imgui_impl_glut.h index feeca8b3f..20e77dbce 100644 --- a/backends/imgui_impl_glut.h +++ b/backends/imgui_impl_glut.h @@ -7,7 +7,7 @@ // Implemented features: // [X] Platform: Partial 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 GLUT values are obsolete since 1.87 and not supported since 1.91.5] -// Issues: +// Missing features or Issues: // [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I // [ ] Platform: Missing horizontal mouse wheel support. // [ ] Platform: Missing mouse cursor shape/visibility support. diff --git a/backends/imgui_impl_metal.h b/backends/imgui_impl_metal.h index 2402c02c6..8af2c8b6b 100644 --- a/backends/imgui_impl_metal.h +++ b/backends/imgui_impl_metal.h @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. OSX) // Implemented features: -// [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'MTLTexture' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // 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. @@ -13,6 +14,7 @@ // - 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 @@ -34,11 +36,12 @@ IMGUI_IMPL_API void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id commandEncoder); // Called by Init/NewFrame/Shutdown -IMGUI_IMPL_API bool ImGui_ImplMetal_CreateFontsTexture(id device); -IMGUI_IMPL_API void ImGui_ImplMetal_DestroyFontsTexture(); IMGUI_IMPL_API bool ImGui_ImplMetal_CreateDeviceObjects(id device); IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplMetal_UpdateTexture(ImTextureData* tex); + #endif //----------------------------------------------------------------------------- @@ -61,11 +64,12 @@ IMGUI_IMPL_API void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, MTL::RenderCommandEncoder* commandEncoder); // Called by Init/NewFrame/Shutdown -IMGUI_IMPL_API bool ImGui_ImplMetal_CreateFontsTexture(MTL::Device* device); -IMGUI_IMPL_API void ImGui_ImplMetal_DestroyFontsTexture(); IMGUI_IMPL_API bool ImGui_ImplMetal_CreateDeviceObjects(MTL::Device* device); IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplMetal_UpdateTexture(ImTextureData* tex); + #endif #endif diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 12dc3d50f..0ab182a82 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. OSX) // Implemented features: -// [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'MTLTexture' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // 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. @@ -15,6 +16,11 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-19: Fixed issue in ImGui_ImplMetal_RenderDrawData() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplMetal_CreateFontsTexture() and ImGui_ImplMetal_DestroyFontsTexture(). +// 2025-02-03: Metal: Crash fix. (#8367) +// 2025-01-08: Metal: Fixed memory leaks when using metal-cpp (#8276, #8166) or when using multiple contexts (#7419). // 2022-08-23: Metal: Update deprecated property 'sampleCount'->'rasterSampleCount'. // 2022-07-05: Metal: Add dispatch synchronization. // 2022-06-30: Metal: Use __bridge for ARC based systems. @@ -57,6 +63,11 @@ - (instancetype)initWithRenderPassDescriptor:(MTLRenderPassDescriptor*)renderPassDescriptor; @end +@interface MetalTexture : NSObject +@property (nonatomic, strong) id metalTexture; +- (instancetype)initWithTexture:(id)metalTexture; +@end + // A singleton that stores long-lived objects that are needed by the Metal // renderer backend. Stores the render pipeline state cache and the default // font texture, and manages the reusable buffer cache. @@ -65,7 +76,6 @@ @property (nonatomic, strong) id depthStencilState; @property (nonatomic, strong) FramebufferDescriptor* framebufferDescriptor; // framebuffer descriptor for current frame; transient @property (nonatomic, strong) NSMutableDictionary* renderPipelineStateCache; // pipeline cache; keyed on framebuffer descriptors -@property (nonatomic, strong, nullable) id fontTexture; @property (nonatomic, strong) NSMutableArray* bufferCache; @property (nonatomic, assign) double lastBufferCachePurge; - (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id)device; @@ -108,11 +118,6 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, } -bool ImGui_ImplMetal_CreateFontsTexture(MTL::Device* device) -{ - return ImGui_ImplMetal_CreateFontsTexture((__bridge id)(device)); -} - bool ImGui_ImplMetal_CreateDeviceObjects(MTL::Device* device) { return ImGui_ImplMetal_CreateDeviceObjects((__bridge id)(device)); @@ -132,6 +137,7 @@ bool ImGui_ImplMetal_Init(id device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_metal"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. bd->SharedMetalContext = [[MetalContext alloc] init]; bd->SharedMetalContext.device = device; @@ -142,27 +148,34 @@ bool ImGui_ImplMetal_Init(id device) void ImGui_ImplMetal_Shutdown() { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); + IM_UNUSED(bd); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImGui_ImplMetal_DestroyDeviceObjects(); ImGui_ImplMetal_DestroyBackendData(); - ImGuiIO& io = ImGui::GetIO(); io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); } void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor) { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); IM_ASSERT(bd != nil && "Context or backend not initialized! Did you call ImGui_ImplMetal_Init()?"); +#ifdef IMGUI_IMPL_METAL_CPP + bd->SharedMetalContext.framebufferDescriptor = [[[FramebufferDescriptor alloc] initWithRenderPassDescriptor:renderPassDescriptor]autorelease]; +#else bd->SharedMetalContext.framebufferDescriptor = [[FramebufferDescriptor alloc] initWithRenderPassDescriptor:renderPassDescriptor]; - +#endif if (bd->SharedMetalContext.depthStencilState == nil) ImGui_ImplMetal_CreateDeviceObjects(bd->SharedMetalContext.device); } -static void ImGui_ImplMetal_SetupRenderState(ImDrawData* drawData, id commandBuffer, +static void ImGui_ImplMetal_SetupRenderState(ImDrawData* draw_data, id commandBuffer, id commandEncoder, id renderPipelineState, MetalBuffer* vertexBuffer, size_t vertexBufferOffset) { @@ -178,17 +191,17 @@ static void ImGui_ImplMetal_SetupRenderState(ImDrawData* drawData, idDisplaySize.x * drawData->FramebufferScale.x), - .height = (double)(drawData->DisplaySize.y * drawData->FramebufferScale.y), + .width = (double)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x), + .height = (double)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y), .znear = 0.0, .zfar = 1.0 }; [commandEncoder setViewport:viewport]; - float L = drawData->DisplayPos.x; - float R = drawData->DisplayPos.x + drawData->DisplaySize.x; - float T = drawData->DisplayPos.y; - float B = drawData->DisplayPos.y + drawData->DisplaySize.y; + 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; float N = (float)viewport.znear; float F = (float)viewport.zfar; const float ortho_projection[4][4] = @@ -207,17 +220,24 @@ static void ImGui_ImplMetal_SetupRenderState(ImDrawData* drawData, id commandBuffer, id commandEncoder) +void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, id commandBuffer, id commandEncoder) { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); MetalContext* ctx = bd->SharedMetalContext; // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - int fb_width = (int)(drawData->DisplaySize.x * drawData->FramebufferScale.x); - int fb_height = (int)(drawData->DisplaySize.y * drawData->FramebufferScale.y); - if (fb_width <= 0 || fb_height <= 0 || drawData->CmdListsCount == 0) + 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 || draw_data->CmdLists.Size == 0) return; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplMetal_UpdateTexture(tex); + // Try to retrieve a render pipeline state that is compatible with the framebuffer config for this frame // The hit rate for this cache should be very near 100%. id renderPipelineState = ctx.renderPipelineStateCache[ctx.framebufferDescriptor]; @@ -230,24 +250,22 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id c ctx.renderPipelineStateCache[ctx.framebufferDescriptor] = renderPipelineState; } - size_t vertexBufferLength = (size_t)drawData->TotalVtxCount * sizeof(ImDrawVert); - size_t indexBufferLength = (size_t)drawData->TotalIdxCount * sizeof(ImDrawIdx); + size_t vertexBufferLength = (size_t)draw_data->TotalVtxCount * sizeof(ImDrawVert); + size_t indexBufferLength = (size_t)draw_data->TotalIdxCount * sizeof(ImDrawIdx); MetalBuffer* vertexBuffer = [ctx dequeueReusableBufferOfLength:vertexBufferLength device:commandBuffer.device]; MetalBuffer* indexBuffer = [ctx dequeueReusableBufferOfLength:indexBufferLength device:commandBuffer.device]; - ImGui_ImplMetal_SetupRenderState(drawData, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, 0); + ImGui_ImplMetal_SetupRenderState(draw_data, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, 0); // Will project scissor/clipping rectangles into framebuffer space - ImVec2 clip_off = drawData->DisplayPos; // (0,0) unless using multi-viewports - ImVec2 clip_scale = drawData->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + 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 size_t vertexBufferOffset = 0; size_t indexBufferOffset = 0; - for (int n = 0; n < drawData->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = drawData->CmdLists[n]; - memcpy((char*)vertexBuffer.buffer.contents + vertexBufferOffset, draw_list->VtxBuffer.Data, (size_t)draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy((char*)indexBuffer.buffer.contents + indexBufferOffset, draw_list->IdxBuffer.Data, (size_t)draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); @@ -259,7 +277,7 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id c // 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_ImplMetal_SetupRenderState(drawData, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, vertexBufferOffset); + ImGui_ImplMetal_SetupRenderState(draw_data, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, vertexBufferOffset); else pcmd->UserCallback(draw_list, pcmd); } @@ -290,7 +308,8 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id c [commandEncoder setScissorRect:scissorRect]; // Bind texture, Draw - if (ImTextureID tex_id = pcmd->GetTexID()) + ImTextureID tex_id = pcmd->GetTexID(); + if (tex_id != ImTextureID_Invalid) [commandEncoder setFragmentTexture:(__bridge id)(void*)(intptr_t)(tex_id) atIndex:0]; [commandEncoder setVertexBufferOffset:(vertexBufferOffset + pcmd->VtxOffset * sizeof(ImDrawVert)) atIndex:0]; @@ -306,58 +325,84 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id c indexBufferOffset += (size_t)draw_list->IdxBuffer.Size * sizeof(ImDrawIdx); } + MetalContext* sharedMetalContext = bd->SharedMetalContext; [commandBuffer addCompletedHandler:^(id) { dispatch_async(dispatch_get_main_queue(), ^{ - ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); - if (bd != nullptr) + @synchronized(sharedMetalContext.bufferCache) { - @synchronized(bd->SharedMetalContext.bufferCache) - { - [bd->SharedMetalContext.bufferCache addObject:vertexBuffer]; - [bd->SharedMetalContext.bufferCache addObject:indexBuffer]; - } + [sharedMetalContext.bufferCache addObject:vertexBuffer]; + [sharedMetalContext.bufferCache addObject:indexBuffer]; } }); }]; } -bool ImGui_ImplMetal_CreateFontsTexture(id device) +static void ImGui_ImplMetal_DestroyTexture(ImTextureData* tex) { - ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); + if (MetalTexture* backend_tex = (__bridge_transfer MetalTexture*)(tex->BackendUserData)) + { + IM_ASSERT(backend_tex.metalTexture == (__bridge id)(void*)(intptr_t)tex->TexID); + backend_tex.metalTexture = nil; - // We are retrieving and uploading the font atlas as a 4-channels RGBA texture here. - // In theory we could call GetTexDataAsAlpha8() and upload a 1-channel texture to save on memory access bandwidth. - // However, using a shader designed for 1-channel texture would make it less obvious to use the ImTextureID facility to render users own textures. - // You can make that change in your implementation. - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm - width:(NSUInteger)width - height:(NSUInteger)height - mipmapped:NO]; - textureDescriptor.usage = MTLTextureUsageShaderRead; -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST - textureDescriptor.storageMode = MTLStorageModeManaged; -#else - textureDescriptor.storageMode = MTLStorageModeShared; -#endif - id texture = [device newTextureWithDescriptor:textureDescriptor]; - [texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)width, (NSUInteger)height) mipmapLevel:0 withBytes:pixels bytesPerRow:(NSUInteger)width * 4]; - bd->SharedMetalContext.fontTexture = texture; - io.Fonts->SetTexID((ImTextureID)(intptr_t)(__bridge void*)bd->SharedMetalContext.fontTexture); // ImTextureID == ImU64 - - return (bd->SharedMetalContext.fontTexture != nil); + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->BackendUserData = nullptr; + } + tex->SetStatus(ImTextureStatus_Destroyed); } -void ImGui_ImplMetal_DestroyFontsTexture() +void ImGui_ImplMetal_UpdateTexture(ImTextureData* tex) { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); - bd->SharedMetalContext.fontTexture = nil; - io.Fonts->SetTexID(0); + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + + // We are retrieving and uploading the font atlas as a 4-channels RGBA texture here. + // In theory we could call GetTexDataAsAlpha8() and upload a 1-channel texture to save on memory access bandwidth. + // However, using a shader designed for 1-channel texture would make it less obvious to use the ImTextureID facility to render users own textures. + // You can make that change in your implementation. + MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm + width:(NSUInteger)tex->Width + height:(NSUInteger)tex->Height + mipmapped:NO]; + textureDescriptor.usage = MTLTextureUsageShaderRead; + #if TARGET_OS_OSX || TARGET_OS_MACCATALYST + textureDescriptor.storageMode = MTLStorageModeManaged; + #else + textureDescriptor.storageMode = MTLStorageModeShared; + #endif + id texture = [bd->SharedMetalContext.device newTextureWithDescriptor:textureDescriptor]; + [texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)tex->Width, (NSUInteger)tex->Height) mipmapLevel:0 withBytes:tex->Pixels bytesPerRow:(NSUInteger)tex->Width * 4]; + MetalTexture* backend_tex = [[MetalTexture alloc] initWithTexture:texture]; + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)texture); + tex->SetStatus(ImTextureStatus_OK); + tex->BackendUserData = (__bridge_retained void*)(backend_tex); + } + else if (tex->Status == ImTextureStatus_WantUpdates) + { + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + MetalTexture* backend_tex = (__bridge MetalTexture*)(tex->BackendUserData); + for (ImTextureRect& r : tex->Updates) + { + [backend_tex.metalTexture replaceRegion:MTLRegionMake2D((NSUInteger)r.x, (NSUInteger)r.y, (NSUInteger)r.w, (NSUInteger)r.h) + mipmapLevel:0 + withBytes:tex->GetPixelsAt(r.x, r.y) + bytesPerRow:(NSUInteger)tex->Width * 4]; + } + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + { + ImGui_ImplMetal_DestroyTexture(tex); + } } bool ImGui_ImplMetal_CreateDeviceObjects(id device) @@ -367,7 +412,9 @@ bool ImGui_ImplMetal_CreateDeviceObjects(id device) depthStencilDescriptor.depthWriteEnabled = NO; depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways; bd->SharedMetalContext.depthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDescriptor]; - ImGui_ImplMetal_CreateFontsTexture(device); +#ifdef IMGUI_IMPL_METAL_CPP + [depthStencilDescriptor release]; +#endif return true; } @@ -375,7 +422,12 @@ bool ImGui_ImplMetal_CreateDeviceObjects(id device) void ImGui_ImplMetal_DestroyDeviceObjects() { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); - ImGui_ImplMetal_DestroyFontsTexture(); + + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplMetal_DestroyTexture(tex); + [bd->SharedMetalContext.renderPipelineStateCache removeAllObjects]; } @@ -441,6 +493,18 @@ void ImGui_ImplMetal_DestroyDeviceObjects() @end +#pragma mark - MetalTexture implementation + +@implementation MetalTexture +- (instancetype)initWithTexture:(id)metalTexture +{ + if ((self = [super init])) + self.metalTexture = metalTexture; + return self; +} + +@end + #pragma mark - MetalContext implementation @implementation MetalContext diff --git a/backends/imgui_impl_null.cpp b/backends/imgui_impl_null.cpp new file mode 100644 index 000000000..6bf3ddb22 --- /dev/null +++ b/backends/imgui_impl_null.cpp @@ -0,0 +1,102 @@ +// dear imgui: Null Platform+Renderer Backends +// This is designed if you need to use a blind Dear Imgui context with no input and no output. + +// 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 + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2025-11-17: Initial version. + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_null.h" + +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. +#endif + +IMGUI_IMPL_API bool ImGui_ImplNull_Init() +{ + ImGui_ImplNullPlatform_Init(); + ImGui_ImplNullRender_Init(); + return true; +} + +IMGUI_IMPL_API void ImGui_ImplNull_Shutdown() +{ + ImGui_ImplNullRender_Shutdown(); + ImGui_ImplNullPlatform_Shutdown(); +} + +IMGUI_IMPL_API void ImGui_ImplNull_NewFrame() +{ + ImGui_ImplNullPlatform_NewFrame(); + ImGui_ImplNullRender_NewFrame(); +} + +IMGUI_IMPL_API bool ImGui_ImplNullPlatform_Init() +{ + ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; + return true; +} + +IMGUI_IMPL_API void ImGui_ImplNullPlatform_Shutdown() +{ + ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags &= ~ImGuiBackendFlags_HasMouseCursors; +} + +IMGUI_IMPL_API void ImGui_ImplNullPlatform_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize = ImVec2(1920, 1080); + io.DeltaTime = 1.0f / 60.0f; +} + +IMGUI_IMPL_API bool ImGui_ImplNullRender_Init() +{ + ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; + return true; +} + +IMGUI_IMPL_API void ImGui_ImplNullRender_Shutdown() +{ + ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasTextures; +} + +IMGUI_IMPL_API void ImGui_ImplNullRender_NewFrame() +{ +} + +static void ImGui_ImplNullRender_UpdateTexture(ImTextureData* tex) +{ + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantDestroy) + tex->SetStatus(ImTextureStatus_OK); + if (tex->Status == ImTextureStatus_WantDestroy) + { + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); + } +} + +IMGUI_IMPL_API void ImGui_ImplNullRender_RenderDrawData(ImDrawData* draw_data) +{ + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplNullRender_UpdateTexture(tex); +} + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_null.h b/backends/imgui_impl_null.h new file mode 100644 index 000000000..b5cfb5a2c --- /dev/null +++ b/backends/imgui_impl_null.h @@ -0,0 +1,34 @@ +// dear imgui: Null Platform+Renderer Backends +// This is designed if you need to use a blind Dear Imgui context with no input and no output. + +// 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 + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! + +// Null = NullPlatform + NullRender +IMGUI_IMPL_API bool ImGui_ImplNull_Init(); +IMGUI_IMPL_API void ImGui_ImplNull_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplNull_NewFrame(); + +// Null platform only (single screen, fixed timestep, no inputs) +IMGUI_IMPL_API bool ImGui_ImplNullPlatform_Init(); +IMGUI_IMPL_API void ImGui_ImplNullPlatform_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplNullPlatform_NewFrame(); + +// Null renderer only (no output) +IMGUI_IMPL_API bool ImGui_ImplNullRender_Init(); +IMGUI_IMPL_API void ImGui_ImplNullRender_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplNullRender_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplNullRender_RenderDrawData(ImDrawData* draw_data); + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 789d74afb..4fffc5774 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -2,7 +2,10 @@ // 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: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// Missing features or Issues: +// [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // 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. @@ -22,6 +25,10 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-12: OpenGL: Fixed invalid assert in ImGui_ImplOpenGL3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-07-15: OpenGL: Set GL_UNPACK_ALIGNMENT to 1 before updating textures. (#8802) +// 2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL2_CreateFontsTexture() and ImGui_ImplOpenGL2_DestroyFontsTexture(). // 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-06-28: OpenGL: ImGui_ImplOpenGL2_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL2_DestroyFontsTexture(). (#7748) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -67,10 +74,18 @@ #include #endif +// [Debugging] +//#define IMGUI_IMPL_OPENGL_DEBUG +#ifdef IMGUI_IMPL_OPENGL_DEBUG +#include +#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_ImplOpenGL2_Data { - GLuint FontTexture; - ImGui_ImplOpenGL2_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -92,6 +107,7 @@ bool ImGui_ImplOpenGL2_Init() ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)(); io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_opengl2"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. return true; } @@ -101,10 +117,14 @@ void ImGui_ImplOpenGL2_Shutdown() ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplOpenGL2_DestroyDeviceObjects(); + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } @@ -112,11 +132,7 @@ void ImGui_ImplOpenGL2_NewFrame() { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL2_Init()?"); - - if (!bd->FontTexture) - ImGui_ImplOpenGL2_CreateDeviceObjects(); - if (!bd->FontTexture) - ImGui_ImplOpenGL2_CreateFontsTexture(); + IM_UNUSED(bd); } static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height) @@ -153,7 +169,7 @@ static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_wid // 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. - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + GL_CALL(glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height)); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); @@ -174,6 +190,13 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) if (fb_width == 0 || fb_height == 0) return; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplOpenGL2_UpdateTexture(tex); + // Backup GL state GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); @@ -191,9 +214,8 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) 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++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data; glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, pos))); @@ -247,57 +269,79 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, last_tex_env_mode); } -bool ImGui_ImplOpenGL2_CreateFontsTexture() +void ImGui_ImplOpenGL2_UpdateTexture(ImTextureData* tex) { - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); - 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; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &bd->FontTexture); - glBindTexture(GL_TEXTURE_2D, bd->FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - 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 - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGui_ImplOpenGL2_DestroyFontsTexture() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); - if (bd->FontTexture) + if (tex->Status == ImTextureStatus_WantCreate) { - glDeleteTextures(1, &bd->FontTexture); - io.Fonts->SetTexID(0); - bd->FontTexture = 0; + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + const void* pixels = tex->GetPixels(); + GLuint gl_texture_id = 0; + + // 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, &gl_texture_id)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_texture_id)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)); + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)gl_texture_id); + tex->SetStatus(ImTextureStatus_OK); + + // Restore state + GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); + } + else if (tex->Status == ImTextureStatus_WantUpdates) + { + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + GLint last_texture; + GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); + + GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID; + GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_tex_id)); + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width)); + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + for (ImTextureRect& r : tex->Updates) + GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixelsAt(r.x, r.y))); + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); // Restore state + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantDestroy) + { + GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID; + glDeleteTextures(1, &gl_tex_id); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); } } bool ImGui_ImplOpenGL2_CreateDeviceObjects() { - return ImGui_ImplOpenGL2_CreateFontsTexture(); + return true; } void ImGui_ImplOpenGL2_DestroyDeviceObjects() { - ImGui_ImplOpenGL2_DestroyFontsTexture(); + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + { + tex->SetStatus(ImTextureStatus_WantDestroy); + ImGui_ImplOpenGL2_UpdateTexture(tex); + } } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_opengl2.h b/backends/imgui_impl_opengl2.h index e7f7a58c1..65b0fe0b4 100644 --- a/backends/imgui_impl_opengl2.h +++ b/backends/imgui_impl_opengl2.h @@ -2,7 +2,10 @@ // 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: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// Missing features or Issues: +// [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // 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. @@ -31,9 +34,10 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL2_NewFrame(); IMGUI_IMPL_API void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data); // Called by Init/NewFrame/Shutdown -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture(); IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplOpenGL2_UpdateTexture(ImTextureData* tex); + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 57c699421..d1894a135 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -4,8 +4,9 @@ // 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: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only). +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // About WebGL/ES: // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. @@ -22,6 +23,14 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-12: OpenGL: Fixed invalid assert in ImGui_ImplOpenGL3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) +// 2025-12-11: OpenGL: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-07-22: OpenGL: Add and call embedded loader shutdown during ImGui_ImplOpenGL3_Shutdown() to facilitate multiple init/shutdown cycles in same process. (#8792) +// 2025-07-15: OpenGL: Set GL_UNPACK_ALIGNMENT to 1 before updating textures (#8802) + restore non-WebGL/ES update path that doesn't require a CPU-side copy. +// 2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL3_CreateFontsTexture() and ImGui_ImplOpenGL3_DestroyFontsTexture(). +// 2025-06-04: OpenGL: Made GLES 3.20 contexts not access GL_CONTEXT_PROFILE_MASK nor GL_PRIMITIVE_RESTART. (#8664) +// 2025-02-18: OpenGL: Lazily reinitialize embedded GL loader for when calling backend from e.g. other DLL boundaries. (#8406) // 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-06-28: OpenGL: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748) // 2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562) @@ -33,7 +42,7 @@ // 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375) // 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333) // 2023-03-23: OpenGL: Properly restoring "no shader program bound" if it was the case prior to running the rendering function. (#6267, #6220, #6224) -// 2023-03-15: OpenGL: Fixed GL loader crash when GL_VERSION returns NULL. (#6154, #4445, #3530) +// 2023-03-15: OpenGL: Fixed GL loader crash when GL_VERSION returns nullptr. (#6154, #4445, #3530) // 2023-03-06: OpenGL: Fixed restoration of a potentially deleted OpenGL program, by calling glIsProgram(). (#6220, #6224) // 2022-11-09: OpenGL: Reverted use of glBufferSubData(), too many corruptions issues + old issues seemingly can't be reproed with Intel drivers nowadays (revert 2021-12-15 and 2022-05-23 changes). // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -53,7 +62,7 @@ // 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-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. @@ -137,6 +146,7 @@ #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' #pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) +#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1 #endif // GL includes @@ -168,6 +178,7 @@ // - 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 +#define IMGUI_IMPL_OPENGL_LOADER_IMGL3W #include "imgui_impl_opengl3_loader.h" #endif @@ -227,7 +238,7 @@ struct ImGui_ImplOpenGL3_Data bool GlProfileIsES3; bool GlProfileIsCompat; GLint GlProfileMask; - GLuint FontTexture; + GLint MaxTextureSize; GLuint ShaderHandle; GLint AttribLocationTex; // Uniforms location GLint AttribLocationProjMtx; @@ -238,8 +249,10 @@ struct ImGui_ImplOpenGL3_Data GLsizeiptr VertexBufferSize; GLsizeiptr IndexBufferSize; bool HasPolygonMode; + bool HasBindSampler; bool HasClipOrigin; bool UseBufferSubData; + ImVector TempBuffer; ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -275,6 +288,29 @@ struct ImGui_ImplOpenGL3_VtxAttribState }; #endif +// Not static to allow third-party code to use that if they want to (but undocumented) +bool ImGui_ImplOpenGL3_InitLoader(); +bool ImGui_ImplOpenGL3_InitLoader() +{ + // Lazily initialize our loader if not already done + // (to facilitate handling multiple DLL boundaries and multiple context shutdowns we call this from all main entry points) +#ifdef IMGUI_IMPL_OPENGL_LOADER_IMGL3W + if (glGetIntegerv == nullptr && imgl3wInit() != 0) + { + fprintf(stderr, "Failed to initialize OpenGL loader!\n"); + return false; + } +#endif + return true; +} + +static void ImGui_ImplOpenGL3_ShutdownLoader() +{ +#ifdef IMGUI_IMPL_OPENGL_LOADER_IMGL3W + imgl3wShutdown(); +#endif +} + // Functions bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { @@ -282,14 +318,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) IMGUI_CHECKVERSION(); 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"); + // Initialize loader + if (!ImGui_ImplOpenGL3_InitLoader()) return false; - } -#endif // Setup backend capabilities flags ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)(); @@ -297,13 +328,14 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) io.BackendRendererName = "imgui_impl_opengl3"; // Query for GL version (e.g. 320 for GL 3.2) + const char* gl_version_str = (const char*)glGetString(GL_VERSION); #if defined(IMGUI_IMPL_OPENGL_ES2) // GLES 2 bd->GlVersion = 200; bd->GlProfileIsES2 = true; + IM_UNUSED(gl_version_str); #else // Desktop or GLES 3 - const char* gl_version_str = (const char*)glGetString(GL_VERSION); GLint major = 0; GLint minor = 0; glGetIntegerv(GL_MAJOR_VERSION, &major); @@ -311,11 +343,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) if (major == 0 && minor == 0) sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "." bd->GlVersion = (GLuint)(major * 100 + minor * 10); -#if defined(GL_CONTEXT_PROFILE_MASK) - if (bd->GlVersion >= 320) - glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask); - bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0; -#endif + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &bd->MaxTextureSize); #if defined(IMGUI_IMPL_OPENGL_ES3) bd->GlProfileIsES3 = true; @@ -324,6 +352,12 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) bd->GlProfileIsES3 = true; #endif +#if defined(GL_CONTEXT_PROFILE_MASK) + if (!bd->GlProfileIsES3 && bd->GlVersion >= 320) + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask); + bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0; +#endif + bd->UseBufferSubData = false; /* // Query vendor to enable glBufferSubData kludge @@ -336,13 +370,17 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) #endif #ifdef IMGUI_IMPL_OPENGL_DEBUG - printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2 = %d, GlProfileIsES3 = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] + printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2/IsEs3 = %d/%d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (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_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = (int)bd->MaxTextureSize; // 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. @@ -358,7 +396,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) glsl_version = "#version 130"; #endif } - IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString)); + IM_ASSERT((int)strlen(glsl_version) + 2 < IM_COUNTOF(bd->GlslVersionString)); strcpy(bd->GlslVersionString, glsl_version); strcat(bd->GlslVersionString, "\n"); @@ -370,6 +408,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) // Detect extensions we support #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3); +#endif +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER + bd->HasBindSampler = (bd->GlVersion >= 330 || bd->GlProfileIsES3); #endif bd->HasClipOrigin = (bd->GlVersion >= 450); #ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS @@ -391,12 +432,17 @@ 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(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplOpenGL3_DestroyDeviceObjects(); + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); + + ImGui_ImplOpenGL3_ShutdownLoader(); } void ImGui_ImplOpenGL3_NewFrame() @@ -404,10 +450,10 @@ void ImGui_ImplOpenGL3_NewFrame() ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL3_Init()?"); + ImGui_ImplOpenGL3_InitLoader(); if (!bd->ShaderHandle) - ImGui_ImplOpenGL3_CreateDeviceObjects(); - if (!bd->FontTexture) - ImGui_ImplOpenGL3_CreateFontsTexture(); + if (!ImGui_ImplOpenGL3_CreateDeviceObjects()) + IM_ASSERT(0 && "ImGui_ImplOpenGL3_CreateDeviceObjects() failed!"); } static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) @@ -423,7 +469,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid glDisable(GL_STENCIL_TEST); glEnable(GL_SCISSOR_TEST); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART - if (bd->GlVersion >= 310) + if (!bd->GlProfileIsES3 && bd->GlVersion >= 310) glDisable(GL_PRIMITIVE_RESTART); #endif #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE @@ -464,7 +510,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER - if (bd->GlVersion >= 330 || bd->GlProfileIsES3) + if (bd->HasBindSampler) glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise. #endif @@ -495,15 +541,24 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) if (fb_width <= 0 || fb_height <= 0) return; + ImGui_ImplOpenGL3_InitLoader(); + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplOpenGL3_UpdateTexture(tex); + // 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 || bd->GlProfileIsES3) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; } + GLuint last_sampler; if (bd->HasBindSampler) { 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 @@ -533,7 +588,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) 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; + GLboolean last_enable_primitive_restart = (!bd->GlProfileIsES3 && bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE; #endif // Setup desired GL state @@ -550,10 +605,8 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) 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++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; - // Upload vertex/index buffers // - OpenGL drivers are in a very sorry state nowadays.... // During 2021 we attempted to switch from glBufferData() to orphaning+glBufferSubData() following reports @@ -630,7 +683,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) if (last_program == 0 || glIsProgram(last_program)) glUseProgram(last_program); glBindTexture(GL_TEXTURE_2D, last_texture); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER - if (bd->GlVersion >= 330 || bd->GlProfileIsES3) + if (bd->HasBindSampler) glBindSampler(0, last_sampler); #endif glActiveTexture(last_active_texture); @@ -652,7 +705,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) 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); } + if (!bd->GlProfileIsES3 && bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); } #endif #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE @@ -665,50 +718,90 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) (void)bd; // Not all compilation paths use this } -bool ImGui_ImplOpenGL3_CreateFontsTexture() +static void ImGui_ImplOpenGL3_DestroyTexture(ImTextureData* tex) { - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID; + glDeleteTextures(1, &gl_tex_id); - // 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)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); -#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 identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); - - // Restore state - GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); - - return true; + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); } -void ImGui_ImplOpenGL3_DestroyFontsTexture() +void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) { - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); - if (bd->FontTexture) + // FIXME: Consider backing up and restoring + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) { - glDeleteTextures(1, &bd->FontTexture); - io.Fonts->SetTexID(0); - bd->FontTexture = 0; +#ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); +#endif +#ifdef GL_UNPACK_ALIGNMENT + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); +#endif } + + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + const void* pixels = tex->GetPixels(); + GLuint gl_texture_id = 0; + + // 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, &gl_texture_id)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_texture_id)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)gl_texture_id); + tex->SetStatus(ImTextureStatus_OK); + + // Restore state + GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); + } + else if (tex->Status == ImTextureStatus_WantUpdates) + { + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + GLint last_texture; + GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); + + GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID; + GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_tex_id)); +#if GL_UNPACK_ROW_LENGTH // Not on WebGL/ES + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width)); + for (ImTextureRect& r : tex->Updates) + GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixelsAt(r.x, r.y))); + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); +#else + // GL ES doesn't have GL_UNPACK_ROW_LENGTH, so we need to (A) copy to a contiguous buffer or (B) upload line by line. + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + for (ImTextureRect& r : tex->Updates) + { + const int src_pitch = r.w * tex->BytesPerPixel; + bd->TempBuffer.resize(r.h * src_pitch); + char* out_p = bd->TempBuffer.Data; + for (int y = 0; y < r.h; y++, out_p += src_pitch) + memcpy(out_p, tex->GetPixelsAt(r.x, r.y + y), src_pitch); + IM_ASSERT(out_p == bd->TempBuffer.end()); + GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, bd->TempBuffer.Data)); + } +#endif + tex->SetStatus(ImTextureStatus_OK); + GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); // Restore state + } + else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + ImGui_ImplOpenGL3_DestroyTexture(tex); } // 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. @@ -751,6 +844,7 @@ static bool CheckProgram(GLuint handle, const char* desc) bool ImGui_ImplOpenGL3_CreateDeviceObjects() { + ImGui_ImplOpenGL3_InitLoader(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); // Backup GL state @@ -900,21 +994,24 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() GL_CALL(vert_handle = glCreateShader(GL_VERTEX_SHADER)); glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr); glCompileShader(vert_handle); - CheckShader(vert_handle, "vertex shader"); + if (!CheckShader(vert_handle, "vertex shader")) + return false; const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader }; GLuint frag_handle; GL_CALL(frag_handle = glCreateShader(GL_FRAGMENT_SHADER)); glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr); glCompileShader(frag_handle); - CheckShader(frag_handle, "fragment shader"); + if (!CheckShader(frag_handle, "fragment shader")) + return false; // Link bd->ShaderHandle = glCreateProgram(); glAttachShader(bd->ShaderHandle, vert_handle); glAttachShader(bd->ShaderHandle, frag_handle); glLinkProgram(bd->ShaderHandle); - CheckProgram(bd->ShaderHandle, "shader program"); + if (!CheckProgram(bd->ShaderHandle, "shader program")) + return false; glDetachShader(bd->ShaderHandle, vert_handle); glDetachShader(bd->ShaderHandle, frag_handle); @@ -931,8 +1028,6 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() 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); @@ -948,11 +1043,16 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() void ImGui_ImplOpenGL3_DestroyDeviceObjects() { + ImGui_ImplOpenGL3_InitLoader(); 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(); + + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplOpenGL3_DestroyTexture(tex); } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_opengl3.h b/backends/imgui_impl_opengl3.h index 54545f957..aabe04b07 100644 --- a/backends/imgui_impl_opengl3.h +++ b/backends/imgui_impl_opengl3.h @@ -4,8 +4,9 @@ // 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: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only). +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // About WebGL/ES: // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. @@ -36,11 +37,12 @@ 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(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex); + // Configuration flags to add in your imconfig file: //#define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten) //#define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android) diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h index d6ffa5a2d..2c485841a 100644 --- a/backends/imgui_impl_opengl3_loader.h +++ b/backends/imgui_impl_opengl3_loader.h @@ -166,7 +166,9 @@ typedef khronos_uint8_t GLubyte; #define GL_SCISSOR_BOX 0x0C10 #define GL_SCISSOR_TEST 0x0C11 #define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_ALIGNMENT 0x0CF5 #define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 #define GL_TEXTURE_2D 0x0DE1 #define GL_UNSIGNED_BYTE 0x1401 #define GL_UNSIGNED_SHORT 0x1403 @@ -178,6 +180,7 @@ typedef khronos_uint8_t GLubyte; #define GL_RENDERER 0x1F01 #define GL_VERSION 0x1F02 #define GL_EXTENSIONS 0x1F03 +#define GL_NEAREST 0x2600 #define GL_LINEAR 0x2601 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MIN_FILTER 0x2801 @@ -224,11 +227,13 @@ 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 PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); 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 glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); 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); @@ -396,9 +401,15 @@ GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum #ifndef GL_VERSION_3_3 #define GL_VERSION_3_3 1 #define GL_SAMPLER_BINDING 0x8919 +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); #ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); +GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); #endif #endif /* GL_VERSION_3_3 */ #ifndef GL_VERSION_4_1 @@ -473,12 +484,13 @@ typedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc); /* gl3w api */ GL3W_API int imgl3wInit(void); GL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc); +GL3W_API void imgl3wShutdown(void); GL3W_API int imgl3wIsSupported(int major, int minor); GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); /* gl3w internal state */ union ImGL3WProcs { - GL3WglProc ptr[59]; + GL3WglProc ptr[63]; struct { PFNGLACTIVETEXTUREPROC ActiveTexture; PFNGLATTACHSHADERPROC AttachShader; @@ -498,6 +510,7 @@ union ImGL3WProcs { PFNGLCREATESHADERPROC CreateShader; PFNGLDELETEBUFFERSPROC DeleteBuffers; PFNGLDELETEPROGRAMPROC DeleteProgram; + PFNGLDELETESAMPLERSPROC DeleteSamplers; PFNGLDELETESHADERPROC DeleteShader; PFNGLDELETETEXTURESPROC DeleteTextures; PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays; @@ -510,6 +523,7 @@ union ImGL3WProcs { PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray; PFNGLFLUSHPROC Flush; PFNGLGENBUFFERSPROC GenBuffers; + PFNGLGENSAMPLERSPROC GenSamplers; PFNGLGENTEXTURESPROC GenTextures; PFNGLGENVERTEXARRAYSPROC GenVertexArrays; PFNGLGETATTRIBLOCATIONPROC GetAttribLocation; @@ -530,10 +544,12 @@ union ImGL3WProcs { PFNGLPIXELSTOREIPROC PixelStorei; PFNGLPOLYGONMODEPROC PolygonMode; PFNGLREADPIXELSPROC ReadPixels; + PFNGLSAMPLERPARAMETERIPROC SamplerParameteri; PFNGLSCISSORPROC Scissor; PFNGLSHADERSOURCEPROC ShaderSource; PFNGLTEXIMAGE2DPROC TexImage2D; PFNGLTEXPARAMETERIPROC TexParameteri; + PFNGLTEXSUBIMAGE2DPROC TexSubImage2D; PFNGLUNIFORM1IPROC Uniform1i; PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv; PFNGLUSEPROGRAMPROC UseProgram; @@ -563,6 +579,7 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs; #define glCreateShader imgl3wProcs.gl.CreateShader #define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers #define glDeleteProgram imgl3wProcs.gl.DeleteProgram +#define glDeleteSamplers imgl3wProcs.gl.DeleteSamplers #define glDeleteShader imgl3wProcs.gl.DeleteShader #define glDeleteTextures imgl3wProcs.gl.DeleteTextures #define glDeleteVertexArrays imgl3wProcs.gl.DeleteVertexArrays @@ -575,6 +592,7 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs; #define glEnableVertexAttribArray imgl3wProcs.gl.EnableVertexAttribArray #define glFlush imgl3wProcs.gl.Flush #define glGenBuffers imgl3wProcs.gl.GenBuffers +#define glGenSamplers imgl3wProcs.gl.GenSamplers #define glGenTextures imgl3wProcs.gl.GenTextures #define glGenVertexArrays imgl3wProcs.gl.GenVertexArrays #define glGetAttribLocation imgl3wProcs.gl.GetAttribLocation @@ -595,10 +613,12 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs; #define glPixelStorei imgl3wProcs.gl.PixelStorei #define glPolygonMode imgl3wProcs.gl.PolygonMode #define glReadPixels imgl3wProcs.gl.ReadPixels +#define glSamplerParameteri imgl3wProcs.gl.SamplerParameteri #define glScissor imgl3wProcs.gl.Scissor #define glShaderSource imgl3wProcs.gl.ShaderSource #define glTexImage2D imgl3wProcs.gl.TexImage2D #define glTexParameteri imgl3wProcs.gl.TexParameteri +#define glTexSubImage2D imgl3wProcs.gl.TexSubImage2D #define glUniform1i imgl3wProcs.gl.Uniform1i #define glUniformMatrix4fv imgl3wProcs.gl.UniformMatrix4fv #define glUseProgram imgl3wProcs.gl.UseProgram @@ -626,7 +646,7 @@ extern "C" { #endif #include -static HMODULE libgl; +static HMODULE libgl = NULL; typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR); static GL3WglGetProcAddr wgl_get_proc_address; @@ -639,7 +659,7 @@ static int open_libgl(void) return GL3W_OK; } -static void close_libgl(void) { FreeLibrary(libgl); } +static void close_libgl(void) { FreeLibrary(libgl); libgl = NULL; } static GL3WglProc get_proc(const char *proc) { GL3WglProc res; @@ -651,7 +671,7 @@ static GL3WglProc get_proc(const char *proc) #elif defined(__APPLE__) #include -static void *libgl; +static void *libgl = NULL; static int open_libgl(void) { libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL); @@ -660,7 +680,7 @@ static int open_libgl(void) return GL3W_OK; } -static void close_libgl(void) { dlclose(libgl); } +static void close_libgl(void) { dlclose(libgl); libgl = NULL; } static GL3WglProc get_proc(const char *proc) { @@ -694,7 +714,11 @@ static void close_libgl(void) static int is_library_loaded(const char* name, void** lib) { +#if defined(__HAIKU__) + *lib = NULL; // no support for RTLD_NOLOAD on Haiku. +#else *lib = dlopen(name, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); +#endif return *lib != NULL; } @@ -812,6 +836,7 @@ static int parse_version(void) } static void load_procs(GL3WGetProcAddressProc proc); +static void clear_procs(); int imgl3wInit(void) { @@ -828,6 +853,12 @@ int imgl3wInit2(GL3WGetProcAddressProc proc) return parse_version(); } +void imgl3wShutdown(void) +{ + close_libgl(); + clear_procs(); +} + int imgl3wIsSupported(int major, int minor) { if (major < 2) @@ -858,6 +889,7 @@ static const char *proc_names[] = { "glCreateShader", "glDeleteBuffers", "glDeleteProgram", + "glDeleteSamplers", "glDeleteShader", "glDeleteTextures", "glDeleteVertexArrays", @@ -870,6 +902,7 @@ static const char *proc_names[] = { "glEnableVertexAttribArray", "glFlush", "glGenBuffers", + "glGenSamplers", "glGenTextures", "glGenVertexArrays", "glGetAttribLocation", @@ -890,10 +923,12 @@ static const char *proc_names[] = { "glPixelStorei", "glPolygonMode", "glReadPixels", + "glSamplerParameteri", "glScissor", "glShaderSource", "glTexImage2D", "glTexParameteri", + "glTexSubImage2D", "glUniform1i", "glUniformMatrix4fv", "glUseProgram", @@ -910,6 +945,13 @@ static void load_procs(GL3WGetProcAddressProc proc) imgl3wProcs.ptr[i] = proc(proc_names[i]); } +static void clear_procs() +{ + size_t i; + for (i = 0; i < GL3W_ARRAY_SIZE(proc_names); i++) + imgl3wProcs.ptr[i] = nullptr; +} + #ifdef __cplusplus } #endif diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h index 4caab8ff5..ea2b4f492 100644 --- a/backends/imgui_impl_osx.h +++ b/backends/imgui_impl_osx.h @@ -4,11 +4,11 @@ // - Requires linking with the GameController framework ("-framework GameController"). // Implemented features: -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Clipboard support is part of core Dear ImGui (no specific code in this backend). // [X] Platform: Mouse support. Can discriminate Mouse/Pen. // [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 kVK_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Gamepad support. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: IME support. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -19,6 +19,7 @@ // - 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 diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index c99ba1f06..0dfdd8e90 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -4,11 +4,11 @@ // - Requires linking with the GameController framework ("-framework GameController"). // Implemented features: -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Clipboard support is part of core Dear ImGui (no specific code in this backend). // [X] Platform: Mouse support. Can discriminate Mouse/Pen. // [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 kVK_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Gamepad support. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: IME support. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -29,6 +29,11 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. +// 2025-06-27: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support. +// 2025-06-12: ImGui_ImplOSX_HandleEvent() only process event for window containing our view. (#8644) +// 2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set. +// 2025-01-20: Removed notification observer when shutting down. (#8331) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn // - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn @@ -36,7 +41,7 @@ // 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F20 function keys. Stopped mapping F13 into PrintScreen. // 2023-04-09: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_Pen. -// 2023-02-01: Fixed scroll wheel scaling for devices emitting events with hasPreciseScrollingDeltas==false (e.g. non-Apple mices). +// 2023-02-01: Fixed scroll wheel scaling for devices emitting events with hasPreciseScrollingDeltas==false (e.g. non-Apple mice). // 2022-11-02: Fixed mouse coordinates before clicking the host window. // 2022-10-06: Fixed mouse inputs on flipped views. // 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). @@ -105,6 +110,7 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view); + (id)_windowResizeNorthEastSouthWestCursor; + (id)_windowResizeNorthSouthCursor; + (id)_windowResizeEastWestCursor; ++ (id)busyButClickableCursor; @end /** @@ -142,9 +148,11 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view); NSWindow* window = view.window; if (!window) return; - NSRect contentRect = [window contentRectForFrameRect:window.frame]; - NSRect rect = NSMakeRect(_posX, contentRect.size.height - _posY, 0, 0); - _imeRect = [window convertRectToScreen:rect]; + { + NSRect contentRect = [window contentRectForFrameRect:window.frame]; + NSRect rect = NSMakeRect(_posX, contentRect.size.height - _posY, 0, 0); + _imeRect = [window convertRectToScreen:rect]; + } } - (void)viewDidMoveToWindow @@ -421,12 +429,13 @@ bool ImGui_ImplOSX_Init(NSView* view) bd->MouseCursors[ImGuiMouseCursor_Arrow] = [NSCursor arrowCursor]; bd->MouseCursors[ImGuiMouseCursor_TextInput] = [NSCursor IBeamCursor]; bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = [NSCursor closedHandCursor]; - bd->MouseCursors[ImGuiMouseCursor_Hand] = [NSCursor pointingHandCursor]; - bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = [NSCursor operationNotAllowedCursor]; bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor]; bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor]; bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor]; bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor]; + bd->MouseCursors[ImGuiMouseCursor_Hand] = [NSCursor pointingHandCursor]; + bd->MouseCursors[ImGuiMouseCursor_Wait] = bd->MouseCursors[ImGuiMouseCursor_Progress] = [NSCursor respondsToSelector:@selector(busyButClickableCursor)] ? [NSCursor busyButClickableCursor] : [NSCursor arrowCursor]; + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = [NSCursor operationNotAllowedCursor]; // Note that imgui.cpp also include default OSX clipboard handlers which can be enabled // by adding '#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS' in imconfig.h and adding '-framework ApplicationServices' to your linker command-line. @@ -473,7 +482,7 @@ bool ImGui_ImplOSX_Init(NSView* view) [view addSubview:bd->KeyEventResponder]; ImGui_ImplOSX_AddTrackingArea(view); - platform_io.Platform_SetImeDataFn = [](ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void + platform_io.Platform_SetImeDataFn = [](ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData* data) -> void { ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); if (data->WantVisible) @@ -497,6 +506,7 @@ void ImGui_ImplOSX_Shutdown() ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + [[NSNotificationCenter defaultCenter] removeObserver:bd->Observer]; bd->Observer = nullptr; if (bd->Monitor != nullptr) { @@ -507,9 +517,12 @@ void ImGui_ImplOSX_Shutdown() ImGui_ImplOSX_DestroyBackendData(); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasGamepad); + platform_io.ClearPlatformHandlers(); } static void ImGui_ImplOSX_UpdateMouseCursor() @@ -532,7 +545,7 @@ static void ImGui_ImplOSX_UpdateMouseCursor() else { NSCursor* desired = bd->MouseCursors[imgui_cursor] ?: bd->MouseCursors[ImGuiMouseCursor_Arrow]; - // -[NSCursor set] generates measureable overhead if called unconditionally. + // -[NSCursor set] generates measurable overhead if called unconditionally. if (desired != NSCursor.currentCursor) { [desired set]; @@ -548,8 +561,6 @@ static void ImGui_ImplOSX_UpdateMouseCursor() static void ImGui_ImplOSX_UpdateGamepads() { ImGuiIO& io = ImGui::GetIO(); - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. - return; #if APPLE_HAS_CONTROLLER GCController* controller = GCController.current; @@ -620,9 +631,9 @@ void ImGui_ImplOSX_NewFrame(NSView* view) // Setup display size if (view) { - const float dpi = (float)[view.window backingScaleFactor]; + const float fb_scale = (float)[view.window backingScaleFactor]; io.DisplaySize = ImVec2((float)view.bounds.size.width, (float)view.bounds.size.height); - io.DisplayFramebufferScale = ImVec2(dpi, dpi); + io.DisplayFramebufferScale = ImVec2(fb_scale, fb_scale); } // Setup time step @@ -660,6 +671,9 @@ static ImGuiMouseSource GetMouseSource(NSEvent* event) static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) { + // Only process events from the window containing ImGui view + if (event.window != view.window) + return false; ImGuiIO& io = ImGui::GetIO(); if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown) @@ -785,8 +799,6 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) default: return io.WantCaptureKeyboard; } - - NSEventModifierFlags modifier_flags = [event modifierFlags]; io.AddKeyEvent(key, (modifier_flags & mask) != 0); io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) } diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 14022159f..f578977c4 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -7,8 +7,8 @@ // [X] Platform: Clipboard support. // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. // [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 SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Gamepad support. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -21,6 +21,21 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-02-13: Inputs: systems other than X11 are back to starting mouse capture on mouse down (reverts 2025-02-26 change). Only X11 requires waiting for a drag by default (not ideal, but a better default for X11 users). Added ImGui_ImplSDL2_SetMouseCaptureMode() for X11 debugger users. (#3650, #6410, #9235) +// 2026-01-15: Changed GetClipboardText() handler to return nullptr on error aka clipboard contents is not text. Consistent with other backends. (#9168) +// 2025-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_MOUSEMOTION data is reliable. Fix macOS notch mouse coordinates issue in fullscreen mode + better perf on X11. (#7919, #7786) +// 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. +// 2025-09-15: Content Scales are always reported as 1.0 on Wayland. (#8921) +// 2025-07-08: Made ImGui_ImplSDL2_GetContentScaleForWindow(), ImGui_ImplSDL2_GetContentScaleForDisplay() helpers return 1.0f on Emscripten and Android platforms, matching macOS logic. (#8742, #8733) +// 2025-06-11: Added ImGui_ImplSDL2_GetContentScaleForWindow(SDL_Window* window) and ImGui_ImplSDL2_GetContentScaleForDisplay(int display_index) helper to facilitate making DPI-aware apps. +// 2025-04-09: Don't attempt to call SDL_CaptureMouse() on drivers where we don't call SDL_GetGlobalMouseState(). (#8561) +// 2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set. +// 2025-03-10: When dealing with OEM keys, use scancodes instead of translated keycodes to choose ImGuiKey values. (#7136, #7201, #7206, #7306, #7670, #7672, #8468) +// 2025-02-26: Only start SDL_CaptureMouse() when mouse is being dragged, to mitigate issues with e.g. Linux debuggers not claiming capture back. (#6410, #3650) +// 2025-02-24: Avoid calling SDL_GetGlobalMouseState() when mouse is in relative mode. +// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support. +// 2025-02-10: Using SDL_OpenURL() in platform_io.Platform_OpenInShellFn handler. +// 2025-01-20: Made ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode_Manual) accept an empty array. // 2024-10-24: Emscripten: from SDL 2.30.9, SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f. // 2024-09-09: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: @@ -95,25 +110,30 @@ // 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 "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #endif // SDL #include #include +#include // for snprintf() #ifdef __APPLE__ #include #endif #ifdef __EMSCRIPTEN__ #include #endif +#undef Status // X11 headers are leaking this. #if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__) #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1 #else #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0 #endif +#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#define SDL_HAS_OPEN_URL SDL_VERSION_ATLEAST(2,0,14) #if SDL_HAS_VULKAN #include #endif @@ -122,10 +142,11 @@ struct ImGui_ImplSDL2_Data { SDL_Window* Window; - Uint32 WindowID; + Uint32 WindowID; // Stored in ImGuiViewport::PlatformHandle. Use SDL_GetWindowFromID() to get SDL_Window* from Uint32 WindowID. SDL_Renderer* Renderer; Uint64 Time; char* ClipboardTextData; + char BackendPlatformName[48]; // Mouse handling Uint32 MouseWindowID; @@ -134,6 +155,7 @@ struct ImGui_ImplSDL2_Data SDL_Cursor* MouseLastCursor; int MouseLastLeaveFrame; bool MouseCanUseGlobalState; + ImGui_ImplSDL2_MouseCaptureMode MouseCaptureMode; // Gamepad handling ImVector Gamepads; @@ -158,7 +180,10 @@ static const char* ImGui_ImplSDL2_GetClipboardText(ImGuiContext*) ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); - bd->ClipboardTextData = SDL_GetClipboardText(); + if (SDL_HasClipboardText()) + bd->ClipboardTextData = SDL_GetClipboardText(); + else + bd->ClipboardTextData = nullptr; return bd->ClipboardTextData; } @@ -185,7 +210,6 @@ static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImG ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode); ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) { - IM_UNUSED(scancode); switch (keycode) { case SDLK_TAB: return ImGuiKey_Tab; @@ -203,17 +227,17 @@ ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode sca case SDLK_SPACE: return ImGuiKey_Space; case SDLK_RETURN: return ImGuiKey_Enter; case SDLK_ESCAPE: return ImGuiKey_Escape; - case SDLK_QUOTE: return ImGuiKey_Apostrophe; + //case SDLK_QUOTE: return ImGuiKey_Apostrophe; case SDLK_COMMA: return ImGuiKey_Comma; - case SDLK_MINUS: return ImGuiKey_Minus; + //case SDLK_MINUS: return ImGuiKey_Minus; case SDLK_PERIOD: return ImGuiKey_Period; - case SDLK_SLASH: return ImGuiKey_Slash; + //case SDLK_SLASH: return ImGuiKey_Slash; case SDLK_SEMICOLON: return ImGuiKey_Semicolon; - case SDLK_EQUALS: return ImGuiKey_Equal; - case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket; - case SDLK_BACKSLASH: return ImGuiKey_Backslash; - case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket; - case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent; + //case SDLK_EQUALS: return ImGuiKey_Equal; + //case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket; + //case SDLK_BACKSLASH: return ImGuiKey_Backslash; + //case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket; + //case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent; case SDLK_CAPSLOCK: return ImGuiKey_CapsLock; case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock; case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock; @@ -309,6 +333,24 @@ ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode sca case SDLK_AC_FORWARD: return ImGuiKey_AppForward; default: break; } + + // Fallback to scancode + switch (scancode) + { + case SDL_SCANCODE_GRAVE: return ImGuiKey_GraveAccent; + case SDL_SCANCODE_MINUS: return ImGuiKey_Minus; + case SDL_SCANCODE_EQUALS: return ImGuiKey_Equal; + case SDL_SCANCODE_LEFTBRACKET: return ImGuiKey_LeftBracket; + case SDL_SCANCODE_RIGHTBRACKET: return ImGuiKey_RightBracket; + case SDL_SCANCODE_NONUSBACKSLASH: return ImGuiKey_Oem102; + case SDL_SCANCODE_BACKSLASH: return ImGuiKey_Backslash; + case SDL_SCANCODE_SEMICOLON: return ImGuiKey_Semicolon; + case SDL_SCANCODE_APOSTROPHE: return ImGuiKey_Apostrophe; + case SDL_SCANCODE_COMMA: return ImGuiKey_Comma; + case SDL_SCANCODE_PERIOD: return ImGuiKey_Period; + case SDL_SCANCODE_SLASH: return ImGuiKey_Slash; + default: break; + } return ImGuiKey_None; } @@ -324,14 +366,13 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id) { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); - return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL; + return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr; } // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -342,7 +383,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { case SDL_MOUSEMOTION: { - if (ImGui_ImplSDL2_GetViewportForWindowID(event->motion.windowID) == NULL) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->motion.windowID) == nullptr) return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); @@ -351,7 +392,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_MOUSEWHEEL: { - if (ImGui_ImplSDL2_GetViewportForWindowID(event->wheel.windowID) == NULL) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->wheel.windowID) == nullptr) return false; //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); #if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten! @@ -371,7 +412,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { - if (ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID) == NULL) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID) == nullptr) return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } @@ -388,7 +429,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_TEXTINPUT: { - if (ImGui_ImplSDL2_GetViewportForWindowID(event->text.windowID) == NULL) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->text.windowID) == nullptr) return false; io.AddInputCharactersUTF8(event->text.text); return true; @@ -396,17 +437,19 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_KEYDOWN: case SDL_KEYUP: { - if (ImGui_ImplSDL2_GetViewportForWindowID(event->key.windowID) == NULL) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->key.windowID) == nullptr) return false; ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod); + //IMGUI_DEBUG_LOG("SDL_KEY_%s : key=%d ('%s'), scancode=%d ('%s'), mod=%X\n", + // (event->type == SDL_KEYDOWN) ? "DOWN" : "UP ", event->key.keysym.sym, SDL_GetKeyName(event->key.keysym.sym), event->key.keysym.scancode, SDL_GetScancodeName(event->key.keysym.scancode), event->key.keysym.mod); ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode); io.AddKeyEvent(key, (event->type == SDL_KEYDOWN)); - io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. + io.SetKeyEventNativeData(key, (int)event->key.keysym.sym, (int)event->key.keysym.scancode, (int)event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. return true; } case SDL_WINDOWEVENT: { - if (ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID) == NULL) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID) == nullptr) return false; // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. // - However we won't get a correct LEAVE event for a captured window. @@ -423,7 +466,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) bd->MouseLastLeaveFrame = ImGui::GetFrameCount() + 1; if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); - else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) + if (window_event == SDL_WINDOWEVENT_FOCUS_LOST) io.AddFocusEvent(false); return true; } @@ -433,6 +476,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) bd->WantUpdateGamepadsList = true; return true; } + default: + break; } return false; } @@ -446,29 +491,41 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void ImGuiIO& io = ImGui::GetIO(); IMGUI_CHECKVERSION(); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + //SDL_SetHint(SDL_HINT_EVENT_LOGGING, "2"); - // Check and store if we are on a SDL backend that supports global mouse position - // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) - bool mouse_can_use_global_state = false; -#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE - const char* sdl_backend = SDL_GetCurrentVideoDriver(); - const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; - for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++) - if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0) - mouse_can_use_global_state = true; -#endif + // Obtain compiled and runtime versions + SDL_version ver_compiled; + SDL_version ver_runtime; + SDL_VERSION(&ver_compiled); + SDL_GetVersion(&ver_runtime); // Setup backend capabilities flags ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)(); + snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl2 (%u.%u.%u, %u.%u.%u)", + ver_compiled.major, ver_compiled.minor, ver_compiled.patch, ver_runtime.major, ver_runtime.minor, ver_runtime.patch); io.BackendPlatformUserData = (void*)bd; - io.BackendPlatformName = "imgui_impl_sdl2"; - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendPlatformName = bd->BackendPlatformName; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) bd->Window = window; bd->WindowID = SDL_GetWindowID(window); bd->Renderer = renderer; - bd->MouseCanUseGlobalState = mouse_can_use_global_state; + + // Check and store if we are on a SDL backend that supports SDL_GetGlobalMouseState() and SDL_CaptureMouse() + // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) + bd->MouseCanUseGlobalState = false; + bd->MouseCaptureMode = ImGui_ImplSDL2_MouseCaptureMode_Disabled; +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + const char* sdl_backend = SDL_GetCurrentVideoDriver(); + const char* capture_and_global_state_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; + for (const char* item : capture_and_global_state_whitelist) + if (strncmp(sdl_backend, item, strlen(item)) == 0) + { + bd->MouseCanUseGlobalState = true; + bd->MouseCaptureMode = (strcmp(item, "x11") == 0) ? ImGui_ImplSDL2_MouseCaptureMode_EnabledAfterDrag : ImGui_ImplSDL2_MouseCaptureMode_Enabled; + } +#endif ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; @@ -477,6 +534,8 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void platform_io.Platform_SetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData; #ifdef __EMSCRIPTEN__ platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; }; +#elif SDL_HAS_OPEN_URL + platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { return SDL_OpenURL(url) == 0; }; #endif // Gamepad handling @@ -492,6 +551,8 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); + bd->MouseCursors[ImGuiMouseCursor_Wait] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT); + bd->MouseCursors[ImGuiMouseCursor_Progress] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); // Set platform dependent data in viewport @@ -578,6 +639,7 @@ void ImGui_ImplSDL2_Shutdown() ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); @@ -588,9 +650,18 @@ void ImGui_ImplSDL2_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + platform_io.ClearPlatformHandlers(); IM_DELETE(bd); } +void ImGui_ImplSDL2_SetMouseCaptureMode(ImGui_ImplSDL2_MouseCaptureMode mode) +{ + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + if (mode == ImGui_ImplSDL2_MouseCaptureMode_Disabled && bd->MouseCaptureMode != ImGui_ImplSDL2_MouseCaptureMode_Disabled) + SDL_CaptureMouse(SDL_FALSE); + bd->MouseCaptureMode = mode; +} + static void ImGui_ImplSDL2_UpdateMouseData() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -598,11 +669,25 @@ static void ImGui_ImplSDL2_UpdateMouseData() // We forward mouse input when hovered or captured (via SDL_MOUSEMOTION) or when focused (below) #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE - // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside - SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE); + // - SDL_CaptureMouse() let the OS know e.g. that our drags can extend outside of parent boundaries (we want updated position) and shouldn't trigger other operations outside. + // - Debuggers under Linux tends to leave captured mouse on break, which may be very inconvenient, so to mitigate the issue on X11 we we wait until mouse has moved to begin capture. + if (bd->MouseCaptureMode == ImGui_ImplSDL2_MouseCaptureMode_Enabled) + { + SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE); + } + else if (bd->MouseCaptureMode == ImGui_ImplSDL2_MouseCaptureMode_EnabledAfterDrag) + { + bool want_capture = false; + for (int button_n = 0; button_n < ImGuiMouseButton_COUNT && !want_capture; button_n++) + if (ImGui::IsMouseDragging(button_n, 1.0f)) + want_capture = true; + SDL_CaptureMouse(want_capture ? SDL_TRUE : SDL_FALSE); + } + SDL_Window* focused_window = SDL_GetKeyboardFocus(); const bool is_app_focused = (bd->Window == focused_window); #else + SDL_Window* focused_window = bd->Window; const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only #endif if (is_app_focused) @@ -611,13 +696,20 @@ static void ImGui_ImplSDL2_UpdateMouseData() if (io.WantSetMousePos) SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y); - // (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured) - if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0) + // (Optional) Fallback to provide unclamped mouse position when focused but not hovered (SDL_MOUSEMOTION already provides this when hovered or captured) + // Note that SDL_GetGlobalMouseState() is in theory slow on X11, but this only runs on rather specific cases. If a problem we may provide a way to opt-out this feature. + SDL_Window* hovered_window = SDL_GetMouseFocus(); + const bool is_relative_mouse_mode = SDL_GetRelativeMouseMode() != 0; + if (hovered_window == nullptr && bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode) { - int window_x, window_y, mouse_x_global, mouse_y_global; - SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); - SDL_GetWindowPosition(bd->Window, &window_x, &window_y); - io.AddMousePosEvent((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); + // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + int mouse_x, mouse_y; + int window_x, window_y; + SDL_GetGlobalMouseState(&mouse_x, &mouse_y); + SDL_GetWindowPosition(focused_window, &window_x, &window_y); + mouse_x -= window_x; + mouse_y -= window_y; + io.AddMousePosEvent((float)mouse_x, (float)mouse_y); } } } @@ -648,6 +740,30 @@ static void ImGui_ImplSDL2_UpdateMouseCursor() } } +// - On Windows the process needs to be marked DPI-aware!! SDL2 doesn't do it by default. You can call ::SetProcessDPIAware() or call ImGui_ImplWin32_EnableDpiAwareness() from Win32 backend. +// - Apple platforms use FramebufferScale so we always return 1.0f. +// - Some accessibility applications are declaring virtual monitors with a DPI of 0.0f, see #7902. We preserve this value for caller to handle. +float ImGui_ImplSDL2_GetContentScaleForWindow(SDL_Window* window) +{ + return ImGui_ImplSDL2_GetContentScaleForDisplay(SDL_GetWindowDisplayIndex(window)); +} + +float ImGui_ImplSDL2_GetContentScaleForDisplay(int display_index) +{ + const char* sdl_driver = SDL_GetCurrentVideoDriver(); + if (sdl_driver && strcmp(sdl_driver, "wayland") == 0) + return 1.0f; +#if SDL_HAS_PER_MONITOR_DPI +#if !defined(__APPLE__) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) + float dpi = 0.0f; + if (SDL_GetDisplayDPI(display_index, &dpi, nullptr, nullptr) == 0) + return dpi / 96.0f; +#endif +#endif + IM_UNUSED(display_index); + return 1.0f; +} + static void ImGui_ImplSDL2_CloseGamepads() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -663,7 +779,7 @@ void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_ ImGui_ImplSDL2_CloseGamepads(); if (mode == ImGui_ImplSDL2_GamepadMode_Manual) { - IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0); + IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0); for (int n = 0; n < manual_gamepads_count; n++) bd->Gamepads.push_back(manual_gamepads_array[n]); } @@ -717,9 +833,6 @@ static void ImGui_ImplSDL2_UpdateGamepads() bd->WantUpdateGamepadsList = false; } - // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) - return; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; if (bd->Gamepads.Size == 0) return; @@ -753,29 +866,35 @@ static void ImGui_ImplSDL2_UpdateGamepads() ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_CONTROLLER_AXIS_RIGHTY, +thumb_dead_zone, +32767); } +static void ImGui_ImplSDL2_GetWindowSizeAndFramebufferScale(SDL_Window* window, SDL_Renderer* renderer, ImVec2* out_size, ImVec2* out_framebuffer_scale) +{ + int w, h; + int display_w, display_h; + SDL_GetWindowSize(window, &w, &h); + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + w = h = 0; + if (renderer != nullptr) + SDL_GetRendererOutputSize(renderer, &display_w, &display_h); +#if SDL_HAS_VULKAN + else if (SDL_GetWindowFlags(window) & SDL_WINDOW_VULKAN) + SDL_Vulkan_GetDrawableSize(window, &display_w, &display_h); +#endif + else + SDL_GL_GetDrawableSize(window, &display_w, &display_h); + if (out_size != nullptr) + *out_size = ImVec2((float)w, (float)h); + if (out_framebuffer_scale != nullptr) + *out_framebuffer_scale = (w > 0 && h > 0) ? ImVec2((float)display_w / w, (float)display_h / h) : ImVec2(1.0f, 1.0f); +} + void ImGui_ImplSDL2_NewFrame() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL2_Init()?"); ImGuiIO& io = ImGui::GetIO(); - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - SDL_GetWindowSize(bd->Window, &w, &h); - if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED) - w = h = 0; - if (bd->Renderer != nullptr) - SDL_GetRendererOutputSize(bd->Renderer, &display_w, &display_h); -#if SDL_HAS_VULKAN - else if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_VULKAN) - SDL_Vulkan_GetDrawableSize(bd->Window, &display_w, &display_h); -#endif - else - SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - if (w > 0 && h > 0) - io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); + // Setup main viewport size (every frame to accommodate for window resizing) + ImGui_ImplSDL2_GetWindowSizeAndFramebufferScale(bd->Window, bd->Renderer, &io.DisplaySize, &io.DisplayFramebufferScale); // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644) diff --git a/backends/imgui_impl_sdl2.h b/backends/imgui_impl_sdl2.h index 8ccc6fdc7..63ef7071a 100644 --- a/backends/imgui_impl_sdl2.h +++ b/backends/imgui_impl_sdl2.h @@ -6,8 +6,8 @@ // [X] Platform: Clipboard support. // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. // [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 SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Gamepad support. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -38,9 +38,20 @@ IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(); IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event); +// DPI-related helpers (optional) +IMGUI_IMPL_API float ImGui_ImplSDL2_GetContentScaleForWindow(SDL_Window* window); +IMGUI_IMPL_API float ImGui_ImplSDL2_GetContentScaleForDisplay(int display_index); + // Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this. // When using manual mode, caller is responsible for opening/closing gamepad. enum ImGui_ImplSDL2_GamepadMode { ImGui_ImplSDL2_GamepadMode_AutoFirst, ImGui_ImplSDL2_GamepadMode_AutoAll, ImGui_ImplSDL2_GamepadMode_Manual }; -IMGUI_IMPL_API void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array = NULL, int manual_gamepads_count = -1); +IMGUI_IMPL_API void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array = nullptr, int manual_gamepads_count = -1); + +// (Advanced, for X11 users) Override Mouse Capture mode. Mouse capture allows receiving updated mouse position after clicking inside our window and dragging outside it. +// Having this 'Enabled' is in theory always better. But, on X11 if you crash/break to debugger while capture is active you may temporarily lose access to your mouse. +// The best solution is to setup your debugger to automatically release capture, e.g. 'setxkbmap -option grab:break_actions && xdotool key XF86Ungrab' or via a GDB script. See #3650. +// But you may independently decide on X11, when a debugger is attached, to set this value to ImGui_ImplSDL2_MouseCaptureMode_Disabled. +enum ImGui_ImplSDL2_MouseCaptureMode { ImGui_ImplSDL2_MouseCaptureMode_Enabled, ImGui_ImplSDL2_MouseCaptureMode_EnabledAfterDrag, ImGui_ImplSDL2_MouseCaptureMode_Disabled }; +IMGUI_IMPL_API void ImGui_ImplSDL2_SetMouseCaptureMode(ImGui_ImplSDL2_MouseCaptureMode mode); #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 3cd56a756..a2bd606a6 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -1,15 +1,14 @@ -// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) -// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// dear imgui: Platform Backend for SDL3 +// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) - // Implemented features: // [X] Platform: Clipboard support. // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. // [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 SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Gamepad support. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: IME support. // 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. @@ -21,6 +20,24 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-02-13: Inputs: systems other than X11 are back to starting mouse capture on mouse down (reverts 2025-02-26 change). Only X11 requires waiting for a drag by default (not ideal, but a better default for X11 users). Added ImGui_ImplSDL3_SetMouseCaptureMode() for X11 debugger users. (#3650, #6410, #9235) +// 2026-01-15: Changed GetClipboardText() handler to return nullptr on error aka clipboard contents is not text. Consistent with other backends. (#9168) +// 2025-11-05: Fixed an issue with missing characters events when an already active text field changes viewports. (#9054) +// 2025-10-22: Fixed Platform_OpenInShellFn() return value (unused in core). +// 2025-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_EVENT_MOUSE_MOTION data is reliable. Fix macOS notch mouse coordinates issue in fullscreen mode + better perf on X11. (#7919, #7786) +// 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. +// 2025-09-15: Use SDL_GetWindowDisplayScale() on Mac to output DisplayFrameBufferScale. The function is more reliable during resolution changes e.g. going fullscreen. (#8703, #4414) +// 2025-06-27: IME: avoid calling SDL_StartTextInput() again if already active. (#8727) +// 2025-04-22: IME: honor ImGuiPlatformImeData->WantTextInput as an alternative way to call SDL_StartTextInput(), without IME being necessarily visible. +// 2025-04-09: Don't attempt to call SDL_CaptureMouse() on drivers where we don't call SDL_GetGlobalMouseState(). (#8561) +// 2025-03-30: Update for SDL3 api changes: Revert SDL_GetClipboardText() memory ownership change. (#8530, #7801) +// 2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set. +// 2025-03-10: When dealing with OEM keys, use scancodes instead of translated keycodes to choose ImGuiKey values. (#7136, #7201, #7206, #7306, #7670, #7672, #8468) +// 2025-02-26: Only start SDL_CaptureMouse() when mouse is being dragged, to mitigate issues with e.g. Linux debuggers not claiming capture back. (#6410, #3650) +// 2025-02-24: Avoid calling SDL_GetGlobalMouseState() when mouse is in relative mode. +// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support. +// 2025-02-10: Using SDL_OpenURL() in platform_io.Platform_OpenInShellFn handler. +// 2025-01-20: Made ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode_Manual) accept an empty array. // 2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten. // 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: @@ -55,11 +72,13 @@ // 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 "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #endif // SDL #include +#include // for snprintf() #if defined(__APPLE__) #include #endif @@ -92,9 +111,12 @@ struct ImGui_ImplSDL3_Data SDL_Renderer* Renderer; Uint64 Time; char* ClipboardTextData; + char BackendPlatformName[48]; // IME handling SDL_Window* ImeWindow; + ImGuiPlatformImeData ImeData; + bool ImeDirty; // Mouse handling Uint32 MouseWindowID; @@ -103,6 +125,7 @@ struct ImGui_ImplSDL3_Data SDL_Cursor* MouseLastCursor; int MousePendingLeaveFrame; bool MouseCanUseGlobalState; + ImGui_ImplSDL3_MouseCaptureMode MouseCaptureMode; // Gamepad handling ImVector Gamepads; @@ -121,14 +144,19 @@ static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } +// Forward Declarations +static void ImGui_ImplSDL3_UpdateIme(); + // Functions static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); - const char* sdl_clipboard_text = SDL_GetClipboardText(); - bd->ClipboardTextData = sdl_clipboard_text ? SDL_strdup(sdl_clipboard_text) : NULL; + if (SDL_HasClipboardText()) + bd->ClipboardTextData = SDL_GetClipboardText(); + else + bd->ClipboardTextData = nullptr; return bd->ClipboardTextData; } @@ -137,16 +165,38 @@ static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text) SDL_SetClipboardText(text); } -static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) +static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); - SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; - SDL_Window* window = SDL_GetWindowFromID(window_id); - if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != NULL) + return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr; +} + +static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData* data) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + bd->ImeData = *data; + bd->ImeDirty = true; + ImGui_ImplSDL3_UpdateIme(); +} + +// We discard viewport passed via ImGuiPlatformImeData and always call SDL_StartTextInput() on SDL_GetKeyboardFocus(). +static void ImGui_ImplSDL3_UpdateIme() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + ImGuiPlatformImeData* data = &bd->ImeData; + SDL_Window* window = SDL_GetKeyboardFocus(); + + // Stop previous input + if ((!(data->WantVisible || data->WantTextInput) || bd->ImeWindow != window) && bd->ImeWindow != nullptr) { SDL_StopTextInput(bd->ImeWindow); bd->ImeWindow = nullptr; } + if ((!bd->ImeDirty && bd->ImeWindow == window) || (window == nullptr)) + return; + + // Start/update current input + bd->ImeDirty = false; if (data->WantVisible) { SDL_Rect r; @@ -155,9 +205,10 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view r.w = 1; r.h = (int)data->InputLineHeight; SDL_SetTextInputArea(window, &r, 0); - SDL_StartTextInput(window); bd->ImeWindow = window; } + if (!SDL_TextInputActive(window) && (data->WantVisible || data->WantTextInput)) + SDL_StartTextInput(window); } // Not static to allow third-party code to use that if they want to (but undocumented) @@ -203,17 +254,17 @@ ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode sca case SDLK_SPACE: return ImGuiKey_Space; case SDLK_RETURN: return ImGuiKey_Enter; case SDLK_ESCAPE: return ImGuiKey_Escape; - case SDLK_APOSTROPHE: return ImGuiKey_Apostrophe; + //case SDLK_APOSTROPHE: return ImGuiKey_Apostrophe; case SDLK_COMMA: return ImGuiKey_Comma; - case SDLK_MINUS: return ImGuiKey_Minus; + //case SDLK_MINUS: return ImGuiKey_Minus; case SDLK_PERIOD: return ImGuiKey_Period; - case SDLK_SLASH: return ImGuiKey_Slash; + //case SDLK_SLASH: return ImGuiKey_Slash; case SDLK_SEMICOLON: return ImGuiKey_Semicolon; - case SDLK_EQUALS: return ImGuiKey_Equal; - case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket; - case SDLK_BACKSLASH: return ImGuiKey_Backslash; - case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket; - case SDLK_GRAVE: return ImGuiKey_GraveAccent; + //case SDLK_EQUALS: return ImGuiKey_Equal; + //case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket; + //case SDLK_BACKSLASH: return ImGuiKey_Backslash; + //case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket; + //case SDLK_GRAVE: return ImGuiKey_GraveAccent; case SDLK_CAPSLOCK: return ImGuiKey_CapsLock; case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock; case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock; @@ -292,6 +343,24 @@ ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode sca case SDLK_AC_FORWARD: return ImGuiKey_AppForward; default: break; } + + // Fallback to scancode + switch (scancode) + { + case SDL_SCANCODE_GRAVE: return ImGuiKey_GraveAccent; + case SDL_SCANCODE_MINUS: return ImGuiKey_Minus; + case SDL_SCANCODE_EQUALS: return ImGuiKey_Equal; + case SDL_SCANCODE_LEFTBRACKET: return ImGuiKey_LeftBracket; + case SDL_SCANCODE_RIGHTBRACKET: return ImGuiKey_RightBracket; + case SDL_SCANCODE_NONUSBACKSLASH: return ImGuiKey_Oem102; + case SDL_SCANCODE_BACKSLASH: return ImGuiKey_Backslash; + case SDL_SCANCODE_SEMICOLON: return ImGuiKey_Semicolon; + case SDL_SCANCODE_APOSTROPHE: return ImGuiKey_Apostrophe; + case SDL_SCANCODE_COMMA: return ImGuiKey_Comma; + case SDL_SCANCODE_PERIOD: return ImGuiKey_Period; + case SDL_SCANCODE_SLASH: return ImGuiKey_Slash; + default: break; + } return ImGuiKey_None; } @@ -304,18 +373,10 @@ static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0); } - -static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id) -{ - ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); - return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL; -} - // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); @@ -326,7 +387,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) { case SDL_EVENT_MOUSE_MOTION: { - if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == NULL) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr) return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); @@ -335,7 +396,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_MOUSE_WHEEL: { - if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == NULL) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr) return false; //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); float wheel_x = -event->wheel.x; @@ -347,7 +408,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_UP: { - if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == NULL) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr) return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } @@ -364,7 +425,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_TEXT_INPUT: { - if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == NULL) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr) return false; io.AddInputCharactersUTF8(event->text.text); return true; @@ -372,18 +433,19 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: { - if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == NULL) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == nullptr) return false; - //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod); ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); + //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%s : key=%d ('%s'), scancode=%d ('%s'), mod=%X\n", + // (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP ", event->key.key, SDL_GetKeyName(event->key.key), event->key.scancode, SDL_GetScancodeName(event->key.scancode), event->key.mod); ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode); io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN)); - io.SetKeyEventNativeData(key, event->key.key, event->key.scancode, event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. + io.SetKeyEventNativeData(key, (int)event->key.key, (int)event->key.scancode, (int)event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. return true; } case SDL_EVENT_WINDOW_MOUSE_ENTER: { - if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) return false; bd->MouseWindowID = event->window.windowID; bd->MousePendingLeaveFrame = 0; @@ -395,7 +457,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) // FIXME: Unconfirmed whether this is still needed with SDL3. case SDL_EVENT_WINDOW_MOUSE_LEAVE: { - if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) return false; bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1; return true; @@ -403,7 +465,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_WINDOW_FOCUS_GAINED: case SDL_EVENT_WINDOW_FOCUS_LOST: { - if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) return false; io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED); return true; @@ -414,6 +476,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) bd->WantUpdateGamepadsList = true; return true; } + default: + break; } return false; } @@ -424,7 +488,7 @@ static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Win viewport->PlatformHandleRaw = nullptr; #if defined(_WIN32) && !defined(__WINRT__) viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); -#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) +#elif defined(__APPLE__) viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); #endif } @@ -435,34 +499,43 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void IMGUI_CHECKVERSION(); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); IM_UNUSED(sdl_gl_context); // Unused in this branch + //SDL_SetHint(SDL_HINT_EVENT_LOGGING, "2"); - // Check and store if we are on a SDL backend that supports global mouse position - // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) - bool mouse_can_use_global_state = false; -#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE - const char* sdl_backend = SDL_GetCurrentVideoDriver(); - const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; - for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++) - if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0) - mouse_can_use_global_state = true; -#endif + const int ver_linked = SDL_GetVersion(); // Setup backend capabilities flags ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)(); + snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl3 (%d.%d.%d; %d.%d.%d)", + SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked)); io.BackendPlatformUserData = (void*)bd; - io.BackendPlatformName = "imgui_impl_sdl3"; + io.BackendPlatformName = bd->BackendPlatformName; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) bd->Window = window; bd->WindowID = SDL_GetWindowID(window); bd->Renderer = renderer; - bd->MouseCanUseGlobalState = mouse_can_use_global_state; + + // Check and store if we are on a SDL backend that supports SDL_GetGlobalMouseState() and SDL_CaptureMouse() + // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) + bd->MouseCanUseGlobalState = false; + bd->MouseCaptureMode = ImGui_ImplSDL3_MouseCaptureMode_Disabled; +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + const char* sdl_backend = SDL_GetCurrentVideoDriver(); + const char* capture_and_global_state_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; + for (const char* item : capture_and_global_state_whitelist) + if (strncmp(sdl_backend, item, strlen(item)) == 0) + { + bd->MouseCanUseGlobalState = true; + bd->MouseCaptureMode = (strcmp(item, "x11") == 0) ? ImGui_ImplSDL3_MouseCaptureMode_EnabledAfterDrag : ImGui_ImplSDL3_MouseCaptureMode_Enabled; + } +#endif ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; + platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { return SDL_OpenURL(url); }; // Gamepad handling bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst; @@ -477,6 +550,8 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE); bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER); + bd->MouseCursors[ImGuiMouseCursor_Wait] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT); + bd->MouseCursors[ImGuiMouseCursor_Progress] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_PROGRESS); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED); // Set platform dependent data in viewport @@ -488,22 +563,18 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered. // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application. // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click: - // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED) -#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH + // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_EVENT_WINDOW_FOCUS_GAINED) SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); -#endif // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710) -#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); -#endif return true; } +// Should technically be a SDL_GLContext but due to typedef it is sane to keep it void* in public interface. bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) { - IM_UNUSED(sdl_gl_context); // Viewport branch will need this. return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context); } @@ -530,6 +601,11 @@ bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* rendere return ImGui_ImplSDL3_Init(window, renderer, nullptr); } +bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window) +{ + return ImGui_ImplSDL3_Init(window, nullptr, nullptr); +} + bool ImGui_ImplSDL3_InitForOther(SDL_Window* window) { return ImGui_ImplSDL3_Init(window, nullptr, nullptr); @@ -542,6 +618,7 @@ void ImGui_ImplSDL3_Shutdown() ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); @@ -552,9 +629,18 @@ void ImGui_ImplSDL3_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + platform_io.ClearPlatformHandlers(); IM_DELETE(bd); } +void ImGui_ImplSDL3_SetMouseCaptureMode(ImGui_ImplSDL3_MouseCaptureMode mode) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + if (mode == ImGui_ImplSDL3_MouseCaptureMode_Disabled && bd->MouseCaptureMode != ImGui_ImplSDL3_MouseCaptureMode_Disabled) + SDL_CaptureMouse(false); + bd->MouseCaptureMode = mode; +} + static void ImGui_ImplSDL3_UpdateMouseData() { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); @@ -562,8 +648,21 @@ static void ImGui_ImplSDL3_UpdateMouseData() // We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below) #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE - // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside - SDL_CaptureMouse(bd->MouseButtonsDown != 0); + // - SDL_CaptureMouse() let the OS know e.g. that our drags can extend outside of parent boundaries (we want updated position) and shouldn't trigger other operations outside. + // - Debuggers under Linux tends to leave captured mouse on break, which may be very inconvenient, so to mitigate the issue on X11 we we wait until mouse has moved to begin capture. + if (bd->MouseCaptureMode == ImGui_ImplSDL3_MouseCaptureMode_Enabled) + { + SDL_CaptureMouse(bd->MouseButtonsDown != 0); + } + else if (bd->MouseCaptureMode == ImGui_ImplSDL3_MouseCaptureMode_EnabledAfterDrag) + { + bool want_capture = false; + for (int button_n = 0; button_n < ImGuiMouseButton_COUNT && !want_capture; button_n++) + if (ImGui::IsMouseDragging(button_n, 1.0f)) + want_capture = true; + SDL_CaptureMouse(want_capture); + } + SDL_Window* focused_window = SDL_GetKeyboardFocus(); const bool is_app_focused = (bd->Window == focused_window); #else @@ -576,15 +675,20 @@ static void ImGui_ImplSDL3_UpdateMouseData() if (io.WantSetMousePos) SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y); - // (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured) - if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0) + // (Optional) Fallback to provide unclamped mouse position when focused but not hovered (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured) + // Note that SDL_GetGlobalMouseState() is in theory slow on X11, but this only runs on rather specific cases. If a problem we may provide a way to opt-out this feature. + SDL_Window* hovered_window = SDL_GetMouseFocus(); + const bool is_relative_mouse_mode = SDL_GetWindowRelativeMouseMode(bd->Window); + if (hovered_window == nullptr && bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode) { // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) - float mouse_x_global, mouse_y_global; + float mouse_x, mouse_y; int window_x, window_y; - SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); + SDL_GetGlobalMouseState(&mouse_x, &mouse_y); SDL_GetWindowPosition(focused_window, &window_x, &window_y); - io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y); + mouse_x -= window_x; + mouse_y -= window_y; + io.AddMousePosEvent(mouse_x, mouse_y); } } } @@ -630,7 +734,7 @@ void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad* ImGui_ImplSDL3_CloseGamepads(); if (mode == ImGui_ImplSDL3_GamepadMode_Manual) { - IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0); + IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0); for (int n = 0; n < manual_gamepads_count; n++) bd->Gamepads.push_back(manual_gamepads_array[n]); } @@ -685,9 +789,6 @@ static void ImGui_ImplSDL3_UpdateGamepads() SDL_free(sdl_gamepads); } - // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) - return; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; if (bd->Gamepads.Size == 0) return; @@ -721,24 +822,39 @@ static void ImGui_ImplSDL3_UpdateGamepads() ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767); } +static void ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(SDL_Window* window, ImVec2* out_size, ImVec2* out_framebuffer_scale) +{ + int w, h; + SDL_GetWindowSize(window, &w, &h); + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + w = h = 0; + +#if defined(__APPLE__) + float fb_scale_x = SDL_GetWindowDisplayScale(window); // Seems more reliable during resolution change (#8703) + float fb_scale_y = fb_scale_x; +#else + int display_w, display_h; + SDL_GetWindowSizeInPixels(window, &display_w, &display_h); + float fb_scale_x = (w > 0) ? (float)display_w / (float)w : 1.0f; + float fb_scale_y = (h > 0) ? (float)display_h / (float)h : 1.0f; +#endif + + if (out_size != nullptr) + *out_size = ImVec2((float)w, (float)h); + if (out_framebuffer_scale != nullptr) + *out_framebuffer_scale = ImVec2(fb_scale_x, fb_scale_y); +} + void ImGui_ImplSDL3_NewFrame() { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?"); ImGuiIO& io = ImGui::GetIO(); - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - SDL_GetWindowSize(bd->Window, &w, &h); - if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED) - w = h = 0; - SDL_GetWindowSizeInPixels(bd->Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - if (w > 0 && h > 0) - io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); + // Setup main viewport size (every frame to accommodate for window resizing) + ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale); - // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + // Setup time step (we could also use SDL_GetTicksNS() available since SDL3) // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644) static Uint64 frequency = SDL_GetPerformanceFrequency(); Uint64 current_time = SDL_GetPerformanceCounter(); @@ -756,6 +872,7 @@ void ImGui_ImplSDL3_NewFrame() ImGui_ImplSDL3_UpdateMouseData(); ImGui_ImplSDL3_UpdateMouseCursor(); + ImGui_ImplSDL3_UpdateIme(); // Update game controllers (if enabled and available) ImGui_ImplSDL3_UpdateGamepads(); diff --git a/backends/imgui_impl_sdl3.h b/backends/imgui_impl_sdl3.h index c4a5a1230..0720283c0 100644 --- a/backends/imgui_impl_sdl3.h +++ b/backends/imgui_impl_sdl3.h @@ -1,15 +1,14 @@ -// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) -// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// dear imgui: Platform Backend for SDL3 +// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) - // Implemented features: // [X] Platform: Clipboard support. // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. // [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 SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Gamepad support. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: IME support. // 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. @@ -34,6 +33,7 @@ IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer); +IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOther(SDL_Window* window); IMGUI_IMPL_API void ImGui_ImplSDL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL3_NewFrame(); @@ -42,6 +42,13 @@ IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event); // Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this. // When using manual mode, caller is responsible for opening/closing gamepad. enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual }; -IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = NULL, int manual_gamepads_count = -1); +IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1); + +// (Advanced, for X11 users) Override Mouse Capture mode. Mouse capture allows receiving updated mouse position after clicking inside our window and dragging outside it. +// Having this 'Enabled' is in theory always better. But, on X11 if you crash/break to debugger while capture is active you may temporarily lose access to your mouse. +// The best solution is to setup your debugger to automatically release capture, e.g. 'setxkbmap -option grab:break_actions && xdotool key XF86Ungrab' or via a GDB script. See #3650. +// But you may independently decide on X11, when a debugger is attached, to set this value to ImGui_ImplSDL3_MouseCaptureMode_Disabled. +enum ImGui_ImplSDL3_MouseCaptureMode { ImGui_ImplSDL3_MouseCaptureMode_Enabled, ImGui_ImplSDL3_MouseCaptureMode_EnabledAfterDrag, ImGui_ImplSDL3_MouseCaptureMode_Disabled }; +IMGUI_IMPL_API void ImGui_ImplSDL3_SetMouseCaptureMode(ImGui_ImplSDL3_MouseCaptureMode mode); #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_sdlgpu3.cpp b/backends/imgui_impl_sdlgpu3.cpp new file mode 100644 index 000000000..54a527a3c --- /dev/null +++ b/backends/imgui_impl_sdlgpu3.cpp @@ -0,0 +1,692 @@ +// dear imgui: Renderer Backend for SDL_GPU +// This needs to be used along with the SDL3 Platform Backend + +// Implemented features: +// [X] Renderer: User texture binding. Use 'SDL_GPUTexture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! **IMPORTANT** Before 2025/08/08, ImTextureID was a reference to a SDL_GPUTextureSamplerBinding struct. +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). + +// The aim of imgui_impl_sdlgpu3.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_sdlgpu3.cpp/.h in their own engine/app. +// - Unlike other backends, the user must call the function ImGui_ImplSDLGPU3_PrepareDrawData() BEFORE issuing a SDL_GPURenderPass containing ImGui_ImplSDLGPU3_RenderDrawData. +// Calling the function is MANDATORY, otherwise the ImGui will not upload neither the vertex nor the index buffer for the GPU. See imgui_impl_sdlgpu3.cpp for more info. + +// CHANGELOG +// 2026-03-19: Fixed issue in ImGui_ImplSDLGPU3_DestroyTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) +// 2026-02-25: Removed unnecessary call to SDL_WaitForGPUIdle when releasing vertex/index buffers. (#9262) +// 2025-11-26: macOS version can use MSL shaders in order to support macOS 10.14+ (vs Metallib shaders requiring macOS 14+). Requires calling SDL_CreateGPUDevice() with SDL_GPU_SHADERFORMAT_MSL. +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-08-20: Added ImGui_ImplSDLGPU3_InitInfo::SwapchainComposition and ImGui_ImplSDLGPU3_InitInfo::PresentMode to configure how secondary viewports are created. +// 2025-08-08: *BREAKING* Changed ImTextureID type from SDL_GPUTextureSamplerBinding* to SDL_GPUTexture*, which is more natural and easier for user to manage. If you need to change the current sampler, you can access the ImGui_ImplSDLGPU3_RenderState struct. (#8866, #8163, #7998, #7988) +// 2025-08-08: Expose SamplerDefault and SamplerCurrent in ImGui_ImplSDLGPU3_RenderState. Allow callback to change sampler. +// 2025-06-25: Mapping transfer buffer for texture update use cycle=true. Fixes artifacts e.g. on Metal backend. +// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLGPU3_CreateFontsTexture() and ImGui_ImplSDLGPU3_DestroyFontsTexture(). +// 2025-04-28: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2025-03-30: Made ImGui_ImplSDLGPU3_PrepareDrawData() reuse GPU Transfer Buffers which were unusually slow to recreate every frame. Much faster now. +// 2025-03-21: Fixed typo in function name Imgui_ImplSDLGPU3_PrepareDrawData() -> ImGui_ImplSDLGPU3_PrepareDrawData(). +// 2025-01-16: Renamed ImGui_ImplSDLGPU3_InitInfo::GpuDevice to Device. +// 2025-01-09: SDL_GPU: Added the SDL_GPU3 backend. + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_sdlgpu3.h" +#include "imgui_impl_sdlgpu3_shaders.h" + +// SDL_GPU Data + +// Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplSDLGPU3_RenderDrawData() +struct ImGui_ImplSDLGPU3_FrameData +{ + SDL_GPUBuffer* VertexBuffer = nullptr; + SDL_GPUTransferBuffer* VertexTransferBuffer = nullptr; + uint32_t VertexBufferSize = 0; + SDL_GPUBuffer* IndexBuffer = nullptr; + SDL_GPUTransferBuffer* IndexTransferBuffer = nullptr; + uint32_t IndexBufferSize = 0; +}; + +struct ImGui_ImplSDLGPU3_Data +{ + ImGui_ImplSDLGPU3_InitInfo InitInfo; + + // Graphics pipeline & shaders + SDL_GPUShader* VertexShader = nullptr; + SDL_GPUShader* FragmentShader = nullptr; + SDL_GPUGraphicsPipeline* Pipeline = nullptr; + SDL_GPUSampler* TexSamplerLinear = nullptr; + SDL_GPUSampler* TexSamplerNearest = nullptr; + SDL_GPUTransferBuffer* TexTransferBuffer = nullptr; + uint32_t TexTransferBufferSize = 0; + + // Frame data for main window + ImGui_ImplSDLGPU3_FrameData MainWindowFrameData; +}; + +// Forward Declarations +static void ImGui_ImplSDLGPU3_DestroyFrameData(); + +//----------------------------------------------------------------------------- +// FUNCTIONS +//----------------------------------------------------------------------------- + +// 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. +// FIXME: multi-context support has never been tested. +static ImGui_ImplSDLGPU3_Data* ImGui_ImplSDLGPU3_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplSDLGPU3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; +} + +static void ImGui_ImplSDLGPU3_SetupRenderState(ImDrawData* draw_data, ImGui_ImplSDLGPU3_RenderState* render_state, SDL_GPUGraphicsPipeline* pipeline, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, ImGui_ImplSDLGPU3_FrameData* fd, uint32_t fb_width, uint32_t fb_height) +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + render_state->SamplerCurrent = bd->TexSamplerLinear; + + // Bind graphics pipeline + SDL_BindGPUGraphicsPipeline(render_pass, pipeline); + + // Bind Vertex And Index Buffers + if (draw_data->TotalVtxCount > 0) + { + SDL_GPUBufferBinding vertex_buffer_binding = {}; + vertex_buffer_binding.buffer = fd->VertexBuffer; + vertex_buffer_binding.offset = 0; + SDL_GPUBufferBinding index_buffer_binding = {}; + index_buffer_binding.buffer = fd->IndexBuffer; + index_buffer_binding.offset = 0; + SDL_BindGPUVertexBuffers(render_pass,0, &vertex_buffer_binding, 1); + SDL_BindGPUIndexBuffer(render_pass, &index_buffer_binding, sizeof(ImDrawIdx) == 2 ? SDL_GPU_INDEXELEMENTSIZE_16BIT : SDL_GPU_INDEXELEMENTSIZE_32BIT); + } + + // Setup viewport + SDL_GPUViewport viewport = {}; + viewport.x = 0; + viewport.y = 0; + viewport.w = (float)fb_width; + viewport.h = (float)fb_height; + viewport.min_depth = 0.0f; + viewport.max_depth = 1.0f; + SDL_SetGPUViewport(render_pass, &viewport); + + // Setup scale and translation + // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + struct UBO { float scale[2]; float translation[2]; } ubo; + ubo.scale[0] = 2.0f / draw_data->DisplaySize.x; + ubo.scale[1] = 2.0f / draw_data->DisplaySize.y; + ubo.translation[0] = -1.0f - draw_data->DisplayPos.x * ubo.scale[0]; + ubo.translation[1] = -1.0f - draw_data->DisplayPos.y * ubo.scale[1]; + SDL_PushGPUVertexUniformData(command_buffer, 0, &ubo, sizeof(UBO)); +} + +static void CreateOrResizeBuffers(SDL_GPUBuffer** buffer, SDL_GPUTransferBuffer** transferbuffer, uint32_t* old_size, uint32_t new_size, SDL_GPUBufferUsageFlags usage) +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; + + // There is no need for calling SDL_WaitForGPUIdle here, as SDL3 will handle deferred buffer deletion automatically. + SDL_ReleaseGPUBuffer(v->Device, *buffer); + SDL_ReleaseGPUTransferBuffer(v->Device, *transferbuffer); + + SDL_GPUBufferCreateInfo buffer_info = {}; + buffer_info.usage = usage; + buffer_info.size = new_size; + buffer_info.props = 0; + *buffer = SDL_CreateGPUBuffer(v->Device, &buffer_info); + *old_size = new_size; + IM_ASSERT(*buffer != nullptr && "Failed to create GPU Buffer, call SDL_GetError() for more information"); + + SDL_GPUTransferBufferCreateInfo transferbuffer_info = {}; + transferbuffer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + transferbuffer_info.size = new_size; + *transferbuffer = SDL_CreateGPUTransferBuffer(v->Device, &transferbuffer_info); + IM_ASSERT(*transferbuffer != nullptr && "Failed to create GPU Transfer Buffer, call SDL_GetError() for more information"); +} + +// SDL_GPU doesn't allow copy passes to occur while a render or compute pass is bound! +// The only way to allow a user to supply their own RenderPass (to render to a texture instead of the window for example), +// is to split the upload part of ImGui_ImplSDLGPU3_RenderDrawData() to another function that needs to be called by the user before rendering. +void ImGui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer) +{ + // 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 || draw_data->TotalVtxCount <= 0) + return; + + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplSDLGPU3_UpdateTexture(tex); + + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; + ImGui_ImplSDLGPU3_FrameData* fd = &bd->MainWindowFrameData; + + uint32_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); + uint32_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); + if (fd->VertexBuffer == nullptr || fd->VertexBufferSize < vertex_size) + CreateOrResizeBuffers(&fd->VertexBuffer, &fd->VertexTransferBuffer, &fd->VertexBufferSize, vertex_size, SDL_GPU_BUFFERUSAGE_VERTEX); + if (fd->IndexBuffer == nullptr || fd->IndexBufferSize < index_size) + CreateOrResizeBuffers(&fd->IndexBuffer, &fd->IndexTransferBuffer, &fd->IndexBufferSize, index_size, SDL_GPU_BUFFERUSAGE_INDEX); + + ImDrawVert* vtx_dst = (ImDrawVert*)SDL_MapGPUTransferBuffer(v->Device, fd->VertexTransferBuffer, true); + ImDrawIdx* idx_dst = (ImDrawIdx*)SDL_MapGPUTransferBuffer(v->Device, fd->IndexTransferBuffer, true); + for (const ImDrawList* draw_list : draw_data->CmdLists) + { + memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + vtx_dst += draw_list->VtxBuffer.Size; + idx_dst += draw_list->IdxBuffer.Size; + } + SDL_UnmapGPUTransferBuffer(v->Device, fd->VertexTransferBuffer); + SDL_UnmapGPUTransferBuffer(v->Device, fd->IndexTransferBuffer); + + SDL_GPUTransferBufferLocation vertex_buffer_location = {}; + vertex_buffer_location.offset = 0; + vertex_buffer_location.transfer_buffer = fd->VertexTransferBuffer; + SDL_GPUTransferBufferLocation index_buffer_location = {}; + index_buffer_location.offset = 0; + index_buffer_location.transfer_buffer = fd->IndexTransferBuffer; + + SDL_GPUBufferRegion vertex_buffer_region = {}; + vertex_buffer_region.buffer = fd->VertexBuffer; + vertex_buffer_region.offset = 0; + vertex_buffer_region.size = vertex_size; + + SDL_GPUBufferRegion index_buffer_region = {}; + index_buffer_region.buffer = fd->IndexBuffer; + index_buffer_region.offset = 0; + index_buffer_region.size = index_size; + + SDL_GPUCopyPass* copy_pass = SDL_BeginGPUCopyPass(command_buffer); + SDL_UploadToGPUBuffer(copy_pass, &vertex_buffer_location, &vertex_buffer_region, true); + SDL_UploadToGPUBuffer(copy_pass, &index_buffer_location, &index_buffer_region, true); + SDL_EndGPUCopyPass(copy_pass); +} + +void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, SDL_GPUGraphicsPipeline* pipeline) +{ + // 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_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + ImGui_ImplSDLGPU3_FrameData* fd = &bd->MainWindowFrameData; + + if (pipeline == nullptr) + pipeline = bd->Pipeline; + + // 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) + + // Setup render state structure (for callbacks and custom texture bindings) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImGui_ImplSDLGPU3_RenderState render_state; + render_state.Device = bd->InitInfo.Device; + render_state.SamplerLinear = render_state.SamplerCurrent = bd->TexSamplerLinear; + render_state.SamplerNearest = bd->TexSamplerNearest; + platform_io.Renderer_RenderState = &render_state; + + ImGui_ImplSDLGPU3_SetupRenderState(draw_data, &render_state, pipeline, command_buffer, render_pass, fd, fb_width, fb_height); + + // Render command lists + // (Because we merged all buffers into a single one, we maintain our own offset into them) + int global_vtx_offset = 0; + int global_idx_offset = 0; + for (const ImDrawList* draw_list : draw_data->CmdLists) + { + for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &draw_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_ImplSDLGPU3_SetupRenderState(draw_data, &render_state, pipeline, command_buffer, render_pass, fd, fb_width, fb_height); + else + pcmd->UserCallback(draw_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); + + // Clamp to viewport as SDL_SetGPUScissor() won't accept values that are off bounds + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + // Apply scissor/clipping rectangle + SDL_Rect scissor_rect = {}; + scissor_rect.x = (int)clip_min.x; + scissor_rect.y = (int)clip_min.y; + scissor_rect.w = (int)(clip_max.x - clip_min.x); + scissor_rect.h = (int)(clip_max.y - clip_min.y); + SDL_SetGPUScissor(render_pass,&scissor_rect); + + // Bind DescriptorSet with font or user texture + SDL_GPUTextureSamplerBinding texture_sampler_binding; + texture_sampler_binding.texture = (SDL_GPUTexture*)(intptr_t)pcmd->GetTexID(); + texture_sampler_binding.sampler = render_state.SamplerCurrent; + SDL_BindGPUFragmentSamplers(render_pass, 0, &texture_sampler_binding, 1); + + // Draw + // **IF YOU GET A CRASH HERE** In 1.92.2 on 2025/08/08 we have changed ImTextureID to store 'SDL_GPUTexture*' instead of storing 'SDL_GPUTextureSamplerBinding'. + // Any code loading custom texture using this backend needs to be updated. + SDL_DrawGPUIndexedPrimitives(render_pass, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); + } + } + global_idx_offset += draw_list->IdxBuffer.Size; + global_vtx_offset += draw_list->VtxBuffer.Size; + } + + // Note: at this point both SDL_SetGPUViewport() and SDL_SetGPUScissor() have been called. + // Our last values will leak into user/application rendering if you forgot to call SDL_SetGPUViewport() and SDL_SetGPUScissor() yourself to explicitly set that state + // In theory we should aim to backup/restore those values but I am not sure this is possible. + // We perform a call to SDL_SetGPUScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644) + SDL_Rect scissor_rect { 0, 0, fb_width, fb_height }; + SDL_SetGPUScissor(render_pass, &scissor_rect); +} + +static void ImGui_ImplSDLGPU3_DestroyTexture(ImTextureData* tex) +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + if (tex->GetTexID() != ImTextureID_Invalid) + if (SDL_GPUTexture* raw_tex = (SDL_GPUTexture*)(intptr_t)tex->GetTexID()) + SDL_ReleaseGPUTexture(bd->InitInfo.Device, raw_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); +} + +void ImGui_ImplSDLGPU3_UpdateTexture(ImTextureData* tex) +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; + + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + + // Create texture + SDL_GPUTextureCreateInfo texture_info = {}; + texture_info.type = SDL_GPU_TEXTURETYPE_2D; + texture_info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; + texture_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; + texture_info.width = tex->Width; + texture_info.height = tex->Height; + texture_info.layer_count_or_depth = 1; + texture_info.num_levels = 1; + texture_info.sample_count = SDL_GPU_SAMPLECOUNT_1; + + SDL_GPUTexture* raw_tex = SDL_CreateGPUTexture(v->Device, &texture_info); + IM_ASSERT(raw_tex != nullptr && "Failed to create texture, call SDL_GetError() for more info"); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)raw_tex); + } + + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) + { + SDL_GPUTexture* raw_tex = (SDL_GPUTexture*)(intptr_t)tex->GetTexID(); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + + // Update full texture or selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions. + // We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture. + const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x; + const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y; + const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w; + const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h; + uint32_t upload_pitch = upload_w * tex->BytesPerPixel; + uint32_t upload_size = upload_w * upload_h * tex->BytesPerPixel; + + // Create transfer buffer + if (bd->TexTransferBufferSize < upload_size) + { + SDL_ReleaseGPUTransferBuffer(v->Device, bd->TexTransferBuffer); + SDL_GPUTransferBufferCreateInfo transferbuffer_info = {}; + transferbuffer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + transferbuffer_info.size = upload_size + 1024; + bd->TexTransferBufferSize = upload_size + 1024; + bd->TexTransferBuffer = SDL_CreateGPUTransferBuffer(v->Device, &transferbuffer_info); + IM_ASSERT(bd->TexTransferBuffer != nullptr && "Failed to create transfer buffer, call SDL_GetError() for more information"); + } + + // Copy to transfer buffer + { + void* texture_ptr = SDL_MapGPUTransferBuffer(v->Device, bd->TexTransferBuffer, true); + for (int y = 0; y < upload_h; y++) + memcpy((void*)((uintptr_t)texture_ptr + y * upload_pitch), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch); + SDL_UnmapGPUTransferBuffer(v->Device, bd->TexTransferBuffer); + } + + SDL_GPUTextureTransferInfo transfer_info = {}; + transfer_info.offset = 0; + transfer_info.transfer_buffer = bd->TexTransferBuffer; + + SDL_GPUTextureRegion texture_region = {}; + texture_region.texture = raw_tex; + texture_region.x = (Uint32)upload_x; + texture_region.y = (Uint32)upload_y; + texture_region.w = (Uint32)upload_w; + texture_region.h = (Uint32)upload_h; + texture_region.d = 1; + + // Upload + { + SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(v->Device); + SDL_GPUCopyPass* copy_pass = SDL_BeginGPUCopyPass(cmd); + SDL_UploadToGPUTexture(copy_pass, &transfer_info, &texture_region, false); + SDL_EndGPUCopyPass(copy_pass); + SDL_SubmitGPUCommandBuffer(cmd); + } + + tex->SetStatus(ImTextureStatus_OK); + } + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + ImGui_ImplSDLGPU3_DestroyTexture(tex); +} + +static void ImGui_ImplSDLGPU3_CreateShaders() +{ + // Create the shader modules + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; + + const char* driver = SDL_GetGPUDeviceDriver(v->Device); + + SDL_GPUShaderCreateInfo vertex_shader_info = {}; + vertex_shader_info.entrypoint = "main"; + vertex_shader_info.stage = SDL_GPU_SHADERSTAGE_VERTEX; + vertex_shader_info.num_uniform_buffers = 1; + vertex_shader_info.num_storage_buffers = 0; + vertex_shader_info.num_storage_textures = 0; + vertex_shader_info.num_samplers = 0; + + SDL_GPUShaderCreateInfo fragment_shader_info = {}; + fragment_shader_info.entrypoint = "main"; + fragment_shader_info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; + fragment_shader_info.num_samplers = 1; + fragment_shader_info.num_storage_buffers = 0; + fragment_shader_info.num_storage_textures = 0; + fragment_shader_info.num_uniform_buffers = 0; + + if (strcmp(driver, "vulkan") == 0) + { + vertex_shader_info.format = SDL_GPU_SHADERFORMAT_SPIRV; + vertex_shader_info.code = spirv_vertex; + vertex_shader_info.code_size = sizeof(spirv_vertex); + fragment_shader_info.format = SDL_GPU_SHADERFORMAT_SPIRV; + fragment_shader_info.code = spirv_fragment; + fragment_shader_info.code_size = sizeof(spirv_fragment); + } + else if (strcmp(driver, "direct3d12") == 0) + { + vertex_shader_info.format = SDL_GPU_SHADERFORMAT_DXBC; + vertex_shader_info.code = dxbc_vertex; + vertex_shader_info.code_size = sizeof(dxbc_vertex); + fragment_shader_info.format = SDL_GPU_SHADERFORMAT_DXBC; + fragment_shader_info.code = dxbc_fragment; + fragment_shader_info.code_size = sizeof(dxbc_fragment); + } +#ifdef __APPLE__ + else + { + SDL_GPUShaderFormat supported_formats = SDL_GetGPUShaderFormats(v->Device); + if (supported_formats & SDL_GPU_SHADERFORMAT_METALLIB) + { + // Using metallib blobs (macOS 14+, iOS) + vertex_shader_info.entrypoint = "main0"; + vertex_shader_info.format = SDL_GPU_SHADERFORMAT_METALLIB; + vertex_shader_info.code = metallib_vertex; + vertex_shader_info.code_size = sizeof(metallib_vertex); + fragment_shader_info.entrypoint = "main0"; + fragment_shader_info.format = SDL_GPU_SHADERFORMAT_METALLIB; + fragment_shader_info.code = metallib_fragment; + fragment_shader_info.code_size = sizeof(metallib_fragment); + } + else if (supported_formats & SDL_GPU_SHADERFORMAT_MSL) + { + // macOS: using MSL source + vertex_shader_info.entrypoint = "main0"; + vertex_shader_info.format = SDL_GPU_SHADERFORMAT_MSL; + vertex_shader_info.code = msl_vertex; + vertex_shader_info.code_size = sizeof(msl_vertex); + fragment_shader_info.entrypoint = "main0"; + fragment_shader_info.format = SDL_GPU_SHADERFORMAT_MSL; + fragment_shader_info.code = msl_fragment; + fragment_shader_info.code_size = sizeof(msl_fragment); + } + } +#endif + bd->VertexShader = SDL_CreateGPUShader(v->Device, &vertex_shader_info); + bd->FragmentShader = SDL_CreateGPUShader(v->Device, &fragment_shader_info); + IM_ASSERT(bd->VertexShader != nullptr && "Failed to create vertex shader, call SDL_GetError() for more information"); + IM_ASSERT(bd->FragmentShader != nullptr && "Failed to create fragment shader, call SDL_GetError() for more information"); +} + +static void ImGui_ImplSDLGPU3_CreateGraphicsPipeline() +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; + ImGui_ImplSDLGPU3_CreateShaders(); + + SDL_GPUVertexBufferDescription vertex_buffer_desc[1]; + vertex_buffer_desc[0].slot = 0; + vertex_buffer_desc[0].input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; + vertex_buffer_desc[0].instance_step_rate = 0; + vertex_buffer_desc[0].pitch = sizeof(ImDrawVert); + + SDL_GPUVertexAttribute vertex_attributes[3]; + vertex_attributes[0].buffer_slot = 0; + vertex_attributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2; + vertex_attributes[0].location = 0; + vertex_attributes[0].offset = offsetof(ImDrawVert,pos); + + vertex_attributes[1].buffer_slot = 0; + vertex_attributes[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2; + vertex_attributes[1].location = 1; + vertex_attributes[1].offset = offsetof(ImDrawVert, uv); + + vertex_attributes[2].buffer_slot = 0; + vertex_attributes[2].format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM; + vertex_attributes[2].location = 2; + vertex_attributes[2].offset = offsetof(ImDrawVert, col); + + SDL_GPUVertexInputState vertex_input_state = {}; + vertex_input_state.num_vertex_attributes = 3; + vertex_input_state.vertex_attributes = vertex_attributes; + vertex_input_state.num_vertex_buffers = 1; + vertex_input_state.vertex_buffer_descriptions = vertex_buffer_desc; + + SDL_GPURasterizerState rasterizer_state = {}; + rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL; + rasterizer_state.cull_mode = SDL_GPU_CULLMODE_NONE; + rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE; + rasterizer_state.enable_depth_bias = false; + rasterizer_state.enable_depth_clip = false; + + SDL_GPUMultisampleState multisample_state = {}; + multisample_state.sample_count = v->MSAASamples; + multisample_state.enable_mask = false; + + SDL_GPUDepthStencilState depth_stencil_state = {}; + depth_stencil_state.enable_depth_test = false; + depth_stencil_state.enable_depth_write = false; + depth_stencil_state.enable_stencil_test = false; + + SDL_GPUColorTargetBlendState blend_state = {}; + blend_state.enable_blend = true; + blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA; + blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD; + blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE; + blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD; + blend_state.color_write_mask = SDL_GPU_COLORCOMPONENT_R | SDL_GPU_COLORCOMPONENT_G | SDL_GPU_COLORCOMPONENT_B | SDL_GPU_COLORCOMPONENT_A; + + SDL_GPUColorTargetDescription color_target_desc[1]; + color_target_desc[0].format = v->ColorTargetFormat; + color_target_desc[0].blend_state = blend_state; + + SDL_GPUGraphicsPipelineTargetInfo target_info = {}; + target_info.num_color_targets = 1; + target_info.color_target_descriptions = color_target_desc; + target_info.has_depth_stencil_target = false; + + SDL_GPUGraphicsPipelineCreateInfo pipeline_info = {}; + pipeline_info.vertex_shader = bd->VertexShader; + pipeline_info.fragment_shader = bd->FragmentShader; + pipeline_info.vertex_input_state = vertex_input_state; + pipeline_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; + pipeline_info.rasterizer_state = rasterizer_state; + pipeline_info.multisample_state = multisample_state; + pipeline_info.depth_stencil_state = depth_stencil_state; + pipeline_info.target_info = target_info; + + bd->Pipeline = SDL_CreateGPUGraphicsPipeline(v->Device, &pipeline_info); + IM_ASSERT(bd->Pipeline != nullptr && "Failed to create graphics pipeline, call SDL_GetError() for more information"); +} + +void ImGui_ImplSDLGPU3_CreateDeviceObjects() +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; + + ImGui_ImplSDLGPU3_DestroyDeviceObjects(); + + if (bd->TexSamplerLinear == nullptr) + { + // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. + SDL_GPUSamplerCreateInfo sampler_info = {}; + sampler_info.min_filter = SDL_GPU_FILTER_LINEAR; + sampler_info.mag_filter = SDL_GPU_FILTER_LINEAR; + sampler_info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; + sampler_info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; + sampler_info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; + sampler_info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; + sampler_info.mip_lod_bias = 0.0f; + sampler_info.min_lod = -1000.0f; + sampler_info.max_lod = 1000.0f; + sampler_info.enable_anisotropy = false; + sampler_info.max_anisotropy = 1.0f; + sampler_info.enable_compare = false; + bd->TexSamplerLinear = SDL_CreateGPUSampler(v->Device, &sampler_info); + IM_ASSERT(bd->TexSamplerLinear != nullptr && "Failed to create sampler, call SDL_GetError() for more information"); + + sampler_info.min_filter = SDL_GPU_FILTER_NEAREST; + sampler_info.mag_filter = SDL_GPU_FILTER_NEAREST; + sampler_info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST; + bd->TexSamplerNearest = SDL_CreateGPUSampler(v->Device, &sampler_info); + IM_ASSERT(bd->TexSamplerNearest != nullptr && "Failed to create sampler, call SDL_GetError() for more information"); + } + + ImGui_ImplSDLGPU3_CreateGraphicsPipeline(); +} + +void ImGui_ImplSDLGPU3_DestroyFrameData() +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; + + ImGui_ImplSDLGPU3_FrameData* fd = &bd->MainWindowFrameData; + SDL_ReleaseGPUBuffer(v->Device, fd->VertexBuffer); + SDL_ReleaseGPUBuffer(v->Device, fd->IndexBuffer); + SDL_ReleaseGPUTransferBuffer(v->Device, fd->VertexTransferBuffer); + SDL_ReleaseGPUTransferBuffer(v->Device, fd->IndexTransferBuffer); + fd->VertexBuffer = fd->IndexBuffer = nullptr; + fd->VertexTransferBuffer = fd->IndexTransferBuffer = nullptr; + fd->VertexBufferSize = fd->IndexBufferSize = 0; +} + +void ImGui_ImplSDLGPU3_DestroyDeviceObjects() +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; + + ImGui_ImplSDLGPU3_DestroyFrameData(); + + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplSDLGPU3_DestroyTexture(tex); + if (bd->TexTransferBuffer) { SDL_ReleaseGPUTransferBuffer(v->Device, bd->TexTransferBuffer); bd->TexTransferBuffer = nullptr; } + if (bd->VertexShader) { SDL_ReleaseGPUShader(v->Device, bd->VertexShader); bd->VertexShader = nullptr; } + if (bd->FragmentShader) { SDL_ReleaseGPUShader(v->Device, bd->FragmentShader); bd->FragmentShader = nullptr; } + if (bd->TexSamplerLinear) { SDL_ReleaseGPUSampler(v->Device, bd->TexSamplerLinear); bd->TexSamplerLinear = nullptr; } + if (bd->TexSamplerNearest) { SDL_ReleaseGPUSampler(v->Device, bd->TexSamplerNearest); bd->TexSamplerNearest = nullptr; } + if (bd->Pipeline) { SDL_ReleaseGPUGraphicsPipeline(v->Device, bd->Pipeline); bd->Pipeline = nullptr; } +} + +bool ImGui_ImplSDLGPU3_Init(ImGui_ImplSDLGPU3_InitInfo* info) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplSDLGPU3_Data* bd = IM_NEW(ImGui_ImplSDLGPU3_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_sdlgpu3"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + IM_ASSERT(info->Device != nullptr); + IM_ASSERT(info->ColorTargetFormat != SDL_GPU_TEXTUREFORMAT_INVALID); + + bd->InitInfo = *info; + + return true; +} + +void ImGui_ImplSDLGPU3_Shutdown() +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + ImGui_ImplSDLGPU3_DestroyDeviceObjects(); + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); + IM_DELETE(bd); +} + +void ImGui_ImplSDLGPU3_NewFrame() +{ + ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLGPU3_Init()?"); + + if (!bd->TexSamplerLinear) + ImGui_ImplSDLGPU3_CreateDeviceObjects(); +} + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_sdlgpu3.h b/backends/imgui_impl_sdlgpu3.h new file mode 100644 index 000000000..1c73dfd71 --- /dev/null +++ b/backends/imgui_impl_sdlgpu3.h @@ -0,0 +1,65 @@ +// dear imgui: Renderer Backend for SDL_GPU +// This needs to be used along with the SDL3 Platform Backend + +// Implemented features: +// [X] Renderer: User texture binding. Use 'SDL_GPUTexture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! **IMPORTANT** Before 2025/08/08, ImTextureID was a reference to a SDL_GPUTextureSamplerBinding struct. +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). + +// The aim of imgui_impl_sdlgpu3.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_sdlgpu3.cpp/.h in their own engine/app. +// - Unlike other backends, the user must call the function ImGui_ImplSDLGPU_PrepareDrawData BEFORE issuing a SDL_GPURenderPass containing ImGui_ImplSDLGPU_RenderDrawData. +// Calling the function is MANDATORY, otherwise the ImGui will not upload neither the vertex nor the index buffer for the GPU. See imgui_impl_sdlgpu3.cpp for more info. + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE +#include + +// Initialization data, for ImGui_ImplSDLGPU_Init() +// - Remember to set ColorTargetFormat to the correct format. If you're rendering to the swapchain, call SDL_GetGPUSwapchainTextureFormat() to query the right value +struct ImGui_ImplSDLGPU3_InitInfo +{ + SDL_GPUDevice* Device = nullptr; + SDL_GPUTextureFormat ColorTargetFormat = SDL_GPU_TEXTUREFORMAT_INVALID; + SDL_GPUSampleCount MSAASamples = SDL_GPU_SAMPLECOUNT_1; + SDL_GPUSwapchainComposition SwapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR; // Only used in multi-viewports mode. + SDL_GPUPresentMode PresentMode = SDL_GPU_PRESENTMODE_VSYNC; // Only used in multi-viewports mode. +}; + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplSDLGPU3_Init(ImGui_ImplSDLGPU3_InitInfo* info); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, SDL_GPUGraphicsPipeline* pipeline = nullptr); + +// Use if you want to reset your rendering device without losing Dear ImGui state. +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_DestroyDeviceObjects(); + +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_UpdateTexture(ImTextureData* tex); + +// [BETA] Selected render state data shared with callbacks. +// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplSDLGPU3_RenderDrawData() call. +// (Please open an issue if you feel you need access to more data) +struct ImGui_ImplSDLGPU3_RenderState +{ + SDL_GPUDevice* Device; + SDL_GPUSampler* SamplerLinear; // Bilinear filtering sampler + SDL_GPUSampler* SamplerNearest; // Nearest/point filtering sampler + SDL_GPUSampler* SamplerCurrent; // Current sampler (may be changed by callback) +}; + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_sdlgpu3_shaders.h b/backends/imgui_impl_sdlgpu3_shaders.h new file mode 100644 index 000000000..ca94075f1 --- /dev/null +++ b/backends/imgui_impl_sdlgpu3_shaders.h @@ -0,0 +1,406 @@ +#pragma once +#ifndef IMGUI_DISABLE +#include + +// Data exported using +// misc/fonts/binary_to_compressed_c.exe -u8 -nocompress filename symbolname >filename.h +// With some manual pasting. + +// Check sdlgpu3/ folder for the shaders' source code and instruction on how to build them +const uint8_t spirv_vertex[1732] = { + 3,2,35,7,0,0,1,0,11,0,13,0,55,0,0,0,0,0,0,0,17,0,2,0,1,0,0,0,11,0,6,0,1,0,0,0,71,76,83,76,46,115,116,100,46,52,53,48,0,0,0,0,14,0,3,0,0,0,0,0,1,0,0,0,15,0,10,0,0,0,0,0,4,0,0,0,109, + 97,105,110,0,0,0,0,11,0,0,0,15,0,0,0,21,0,0,0,30,0,0,0,31,0,0,0,3,0,3,0,2,0,0,0,194,1,0,0,4,0,10,0,71,76,95,71,79,79,71,76,69,95,99,112,112,95,115,116,121,108,101,95,108,105,110,101, + 95,100,105,114,101,99,116,105,118,101,0,0,4,0,8,0,71,76,95,71,79,79,71,76,69,95,105,110,99,108,117,100,101,95,100,105,114,101,99,116,105,118,101,0,5,0,4,0,4,0,0,0,109,97,105,110,0, + 0,0,0,5,0,3,0,9,0,0,0,0,0,0,0,6,0,5,0,9,0,0,0,0,0,0,0,67,111,108,111,114,0,0,0,6,0,4,0,9,0,0,0,1,0,0,0,85,86,0,0,5,0,3,0,11,0,0,0,79,117,116,0,5,0,4,0,15,0,0,0,97,67,111,108,111,114, + 0,0,5,0,3,0,21,0,0,0,97,85,86,0,5,0,6,0,28,0,0,0,103,108,95,80,101,114,86,101,114,116,101,120,0,0,0,0,6,0,6,0,28,0,0,0,0,0,0,0,103,108,95,80,111,115,105,116,105,111,110,0,6,0,7,0,28, + 0,0,0,1,0,0,0,103,108,95,80,111,105,110,116,83,105,122,101,0,0,0,0,6,0,7,0,28,0,0,0,2,0,0,0,103,108,95,67,108,105,112,68,105,115,116,97,110,99,101,0,6,0,7,0,28,0,0,0,3,0,0,0,103,108, + 95,67,117,108,108,68,105,115,116,97,110,99,101,0,5,0,3,0,30,0,0,0,0,0,0,0,5,0,4,0,31,0,0,0,97,80,111,115,0,0,0,0,5,0,6,0,33,0,0,0,117,80,117,115,104,67,111,110,115,116,97,110,116,0, + 0,0,6,0,5,0,33,0,0,0,0,0,0,0,117,83,99,97,108,101,0,0,6,0,6,0,33,0,0,0,1,0,0,0,117,84,114,97,110,115,108,97,116,101,0,0,5,0,3,0,35,0,0,0,112,99,0,0,71,0,4,0,11,0,0,0,30,0,0,0,0,0,0, + 0,71,0,4,0,15,0,0,0,30,0,0,0,2,0,0,0,71,0,4,0,21,0,0,0,30,0,0,0,1,0,0,0,71,0,3,0,28,0,0,0,2,0,0,0,72,0,5,0,28,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,72,0,5,0,28,0,0,0,1,0,0,0,11,0,0,0,1,0, + 0,0,72,0,5,0,28,0,0,0,2,0,0,0,11,0,0,0,3,0,0,0,72,0,5,0,28,0,0,0,3,0,0,0,11,0,0,0,4,0,0,0,71,0,4,0,31,0,0,0,30,0,0,0,0,0,0,0,71,0,3,0,33,0,0,0,2,0,0,0,72,0,5,0,33,0,0,0,0,0,0,0,35, + 0,0,0,0,0,0,0,72,0,5,0,33,0,0,0,1,0,0,0,35,0,0,0,8,0,0,0,71,0,4,0,35,0,0,0,33,0,0,0,0,0,0,0,71,0,4,0,35,0,0,0,34,0,0,0,1,0,0,0,19,0,2,0,2,0,0,0,33,0,3,0,3,0,0,0,2,0,0,0,22,0,3,0,6, + 0,0,0,32,0,0,0,23,0,4,0,7,0,0,0,6,0,0,0,4,0,0,0,23,0,4,0,8,0,0,0,6,0,0,0,2,0,0,0,30,0,4,0,9,0,0,0,7,0,0,0,8,0,0,0,32,0,4,0,10,0,0,0,3,0,0,0,9,0,0,0,59,0,4,0,10,0,0,0,11,0,0,0,3,0,0, + 0,21,0,4,0,12,0,0,0,32,0,0,0,1,0,0,0,43,0,4,0,12,0,0,0,13,0,0,0,0,0,0,0,32,0,4,0,14,0,0,0,1,0,0,0,7,0,0,0,59,0,4,0,14,0,0,0,15,0,0,0,1,0,0,0,32,0,4,0,17,0,0,0,3,0,0,0,7,0,0,0,43,0, + 4,0,12,0,0,0,19,0,0,0,1,0,0,0,32,0,4,0,20,0,0,0,1,0,0,0,8,0,0,0,59,0,4,0,20,0,0,0,21,0,0,0,1,0,0,0,32,0,4,0,23,0,0,0,3,0,0,0,8,0,0,0,21,0,4,0,25,0,0,0,32,0,0,0,0,0,0,0,43,0,4,0,25, + 0,0,0,26,0,0,0,1,0,0,0,28,0,4,0,27,0,0,0,6,0,0,0,26,0,0,0,30,0,6,0,28,0,0,0,7,0,0,0,6,0,0,0,27,0,0,0,27,0,0,0,32,0,4,0,29,0,0,0,3,0,0,0,28,0,0,0,59,0,4,0,29,0,0,0,30,0,0,0,3,0,0,0, + 59,0,4,0,20,0,0,0,31,0,0,0,1,0,0,0,30,0,4,0,33,0,0,0,8,0,0,0,8,0,0,0,32,0,4,0,34,0,0,0,2,0,0,0,33,0,0,0,59,0,4,0,34,0,0,0,35,0,0,0,2,0,0,0,32,0,4,0,36,0,0,0,2,0,0,0,8,0,0,0,43,0,4, + 0,6,0,0,0,43,0,0,0,0,0,0,0,43,0,4,0,6,0,0,0,44,0,0,0,0,0,128,63,43,0,4,0,6,0,0,0,49,0,0,0,0,0,128,191,32,0,4,0,50,0,0,0,3,0,0,0,6,0,0,0,54,0,5,0,2,0,0,0,4,0,0,0,0,0,0,0,3,0,0,0,248, + 0,2,0,5,0,0,0,61,0,4,0,7,0,0,0,16,0,0,0,15,0,0,0,65,0,5,0,17,0,0,0,18,0,0,0,11,0,0,0,13,0,0,0,62,0,3,0,18,0,0,0,16,0,0,0,61,0,4,0,8,0,0,0,22,0,0,0,21,0,0,0,65,0,5,0,23,0,0,0,24,0,0, + 0,11,0,0,0,19,0,0,0,62,0,3,0,24,0,0,0,22,0,0,0,61,0,4,0,8,0,0,0,32,0,0,0,31,0,0,0,65,0,5,0,36,0,0,0,37,0,0,0,35,0,0,0,13,0,0,0,61,0,4,0,8,0,0,0,38,0,0,0,37,0,0,0,133,0,5,0,8,0,0,0, + 39,0,0,0,32,0,0,0,38,0,0,0,65,0,5,0,36,0,0,0,40,0,0,0,35,0,0,0,19,0,0,0,61,0,4,0,8,0,0,0,41,0,0,0,40,0,0,0,129,0,5,0,8,0,0,0,42,0,0,0,39,0,0,0,41,0,0,0,81,0,5,0,6,0,0,0,45,0,0,0,42, + 0,0,0,0,0,0,0,81,0,5,0,6,0,0,0,46,0,0,0,42,0,0,0,1,0,0,0,80,0,7,0,7,0,0,0,47,0,0,0,45,0,0,0,46,0,0,0,43,0,0,0,44,0,0,0,65,0,5,0,17,0,0,0,48,0,0,0,30,0,0,0,13,0,0,0,62,0,3,0,48,0,0, + 0,47,0,0,0,65,0,6,0,50,0,0,0,51,0,0,0,30,0,0,0,13,0,0,0,26,0,0,0,61,0,4,0,6,0,0,0,52,0,0,0,51,0,0,0,133,0,5,0,6,0,0,0,53,0,0,0,52,0,0,0,49,0,0,0,65,0,6,0,50,0,0,0,54,0,0,0,30,0,0,0, + 13,0,0,0,26,0,0,0,62,0,3,0,54,0,0,0,53,0,0,0,253,0,1,0,56,0,1,0, +}; +const uint8_t spirv_fragment[844] = { + 3,2,35,7,0,0,1,0,11,0,13,0,30,0,0,0,0,0,0,0,17,0,2,0,1,0,0,0,11,0,6,0,1,0,0,0,71,76,83,76,46,115,116,100,46,52,53,48,0,0,0,0,14,0,3,0,0,0,0,0,1,0,0,0,15,0,7,0,4,0,0,0,4,0,0,0,109,97, + 105,110,0,0,0,0,9,0,0,0,13,0,0,0,16,0,3,0,4,0,0,0,7,0,0,0,3,0,3,0,2,0,0,0,194,1,0,0,4,0,10,0,71,76,95,71,79,79,71,76,69,95,99,112,112,95,115,116,121,108,101,95,108,105,110,101,95,100, + 105,114,101,99,116,105,118,101,0,0,4,0,8,0,71,76,95,71,79,79,71,76,69,95,105,110,99,108,117,100,101,95,100,105,114,101,99,116,105,118,101,0,5,0,4,0,4,0,0,0,109,97,105,110,0,0,0,0,5, + 0,4,0,9,0,0,0,102,67,111,108,111,114,0,0,5,0,3,0,11,0,0,0,0,0,0,0,6,0,5,0,11,0,0,0,0,0,0,0,67,111,108,111,114,0,0,0,6,0,4,0,11,0,0,0,1,0,0,0,85,86,0,0,5,0,3,0,13,0,0,0,73,110,0,0,5, + 0,5,0,22,0,0,0,115,84,101,120,116,117,114,101,0,0,0,0,71,0,4,0,9,0,0,0,30,0,0,0,0,0,0,0,71,0,4,0,13,0,0,0,30,0,0,0,0,0,0,0,71,0,4,0,22,0,0,0,33,0,0,0,0,0,0,0,71,0,4,0,22,0,0,0,34,0, + 0,0,2,0,0,0,19,0,2,0,2,0,0,0,33,0,3,0,3,0,0,0,2,0,0,0,22,0,3,0,6,0,0,0,32,0,0,0,23,0,4,0,7,0,0,0,6,0,0,0,4,0,0,0,32,0,4,0,8,0,0,0,3,0,0,0,7,0,0,0,59,0,4,0,8,0,0,0,9,0,0,0,3,0,0,0,23, + 0,4,0,10,0,0,0,6,0,0,0,2,0,0,0,30,0,4,0,11,0,0,0,7,0,0,0,10,0,0,0,32,0,4,0,12,0,0,0,1,0,0,0,11,0,0,0,59,0,4,0,12,0,0,0,13,0,0,0,1,0,0,0,21,0,4,0,14,0,0,0,32,0,0,0,1,0,0,0,43,0,4,0, + 14,0,0,0,15,0,0,0,0,0,0,0,32,0,4,0,16,0,0,0,1,0,0,0,7,0,0,0,25,0,9,0,19,0,0,0,6,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,27,0,3,0,20,0,0,0,19,0,0,0,32,0,4,0,21,0,0,0,0, + 0,0,0,20,0,0,0,59,0,4,0,21,0,0,0,22,0,0,0,0,0,0,0,43,0,4,0,14,0,0,0,24,0,0,0,1,0,0,0,32,0,4,0,25,0,0,0,1,0,0,0,10,0,0,0,54,0,5,0,2,0,0,0,4,0,0,0,0,0,0,0,3,0,0,0,248,0,2,0,5,0,0,0,65, + 0,5,0,16,0,0,0,17,0,0,0,13,0,0,0,15,0,0,0,61,0,4,0,7,0,0,0,18,0,0,0,17,0,0,0,61,0,4,0,20,0,0,0,23,0,0,0,22,0,0,0,65,0,5,0,25,0,0,0,26,0,0,0,13,0,0,0,24,0,0,0,61,0,4,0,10,0,0,0,27,0, + 0,0,26,0,0,0,87,0,5,0,7,0,0,0,28,0,0,0,23,0,0,0,27,0,0,0,133,0,5,0,7,0,0,0,29,0,0,0,18,0,0,0,28,0,0,0,62,0,3,0,9,0,0,0,29,0,0,0,253,0,1,0,56,0,1,0, +}; + +const uint8_t dxbc_vertex[1064] = { + 68,88,66,67,32,50,127,204,241,196,165,104,216,114,216,116,220,164,29,45,1,0,0,0,40,4,0,0,5,0,0,0,52,0,0,0,136,1,0,0,236,1,0,0,92,2,0,0,140,3,0,0,82,68,69,70,76,1,0,0,1,0,0,0,116,0, + 0,0,1,0,0,0,60,0,0,0,1,5,254,255,0,5,0,0,34,1,0,0,19,19,68,37,60,0,0,0,24,0,0,0,40,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, + 0,1,0,0,0,1,0,0,0,0,0,0,0,117,80,117,115,104,67,111,110,115,116,97,110,116,0,171,171,100,0,0,0,2,0,0,0,140,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,220,0,0,0,0,0,0,0,8,0,0,0,2,0,0,0,240,0,0, + 0,0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,20,1,0,0,8,0,0,0,8,0,0,0,2,0,0,0,240,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,112,99,95,117,83,99,97, + 108,101,0,102,108,111,97,116,50,0,171,171,171,1,0,3,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,230,0,0,0,112,99,95,117,84,114,97,110,115,108,97,116,101,0,77,105,99,114, + 111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,49,48,46,49,0,171,171,73,83,71,78,92,0,0,0,3,0,0,0,8,0,0,0,80,0,0,0,0,0, + 0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,3,0,0,80,0,0,0,1,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,3,3,0,0,80,0,0,0,2,0,0,0,0,0,0,0,3,0,0,0,2,0,0,0,15,15,0,0,84,69,88,67,79,79,82,68,0,171,171,171,79,83, + 71,78,104,0,0,0,3,0,0,0,8,0,0,0,80,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,15,0,0,0,80,0,0,0,1,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,3,12,0,0,89,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,2,0,0,0,15,0,0, + 0,84,69,88,67,79,79,82,68,0,83,86,95,80,111,115,105,116,105,111,110,0,171,171,171,83,72,69,88,40,1,0,0,81,0,1,0,74,0,0,0,106,8,0,1,89,0,0,7,70,142,48,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, + 0,0,1,0,0,0,95,0,0,3,50,16,16,0,0,0,0,0,95,0,0,3,50,16,16,0,1,0,0,0,95,0,0,3,242,16,16,0,2,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,101,0,0,3,50,32,16,0,1,0,0,0,103,0,0,4,242,32,16,0,2, + 0,0,0,1,0,0,0,104,0,0,2,1,0,0,0,50,0,0,13,50,0,16,0,0,0,0,0,70,16,16,0,0,0,0,0,70,128,48,0,0,0,0,0,0,0,0,0,0,0,0,0,230,138,48,0,0,0,0,0,0,0,0,0,0,0,0,0,54,0,0,6,34,32,16,0,2,0,0,0, + 26,0,16,128,65,0,0,0,0,0,0,0,54,0,0,5,242,32,16,0,0,0,0,0,70,30,16,0,2,0,0,0,54,0,0,5,18,32,16,0,2,0,0,0,10,0,16,0,0,0,0,0,54,0,0,8,194,32,16,0,2,0,0,0,2,64,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,128,63,54,0,0,5,50,32,16,0,1,0,0,0,70,16,16,0,1,0,0,0,62,0,0,1,83,84,65,84,148,0,0,0,7,0,0,0,1,0,0,0,0,0,0,0,6,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; +const uint8_t dxbc_fragment[744] = { + 68,88,66,67,235,219,43,109,38,151,39,223,98,41,193,28,215,98,67,110,1,0,0,0,232,2,0,0,5,0,0,0,52,0,0,0,12,1,0,0,88,1,0,0,140,1,0,0,76,2,0,0,82,68,69,70,208,0,0,0,0,0,0,0,0,0,0,0,2, + 0,0,0,60,0,0,0,1,5,255,255,0,5,0,0,167,0,0,0,19,19,68,37,60,0,0,0,24,0,0,0,40,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,140,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0, + 0,0,2,0,0,0,0,0,0,0,158,0,0,0,2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,0,0,0,0,1,0,0,0,12,0,0,0,2,0,0,0,0,0,0,0,95,115,84,101,120,116,117,114,101,95,115,97,109,112,108,101,114,0,115, + 84,101,120,116,117,114,101,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,49,48,46,49,0,171,73,83,71,78, + 68,0,0,0,2,0,0,0,8,0,0,0,56,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,15,15,0,0,56,0,0,0,1,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,3,3,0,0,84,69,88,67,79,79,82,68,0,171,171,171,79,83,71,78,44,0, + 0,0,1,0,0,0,8,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,15,0,0,0,83,86,95,84,97,114,103,101,116,0,171,171,83,72,69,88,184,0,0,0,81,0,0,0,46,0,0,0,106,8,0,1,90,0,0,6,70,110,48, + 0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,88,24,0,7,70,126,48,0,0,0,0,0,0,0,0,0,0,0,0,0,85,85,0,0,2,0,0,0,98,16,0,3,242,16,16,0,0,0,0,0,98,16,0,3,50,16,16,0,1,0,0,0,101,0,0,3,242,32,16,0,0, + 0,0,0,104,0,0,2,1,0,0,0,69,0,0,11,242,0,16,0,0,0,0,0,70,16,16,0,1,0,0,0,70,126,32,0,0,0,0,0,0,0,0,0,0,96,32,0,0,0,0,0,0,0,0,0,56,0,0,7,242,32,16,0,0,0,0,0,70,14,16,0,0,0,0,0,70,30, + 16,0,0,0,0,0,62,0,0,1,83,84,65,84,148,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +#ifdef __APPLE__ +#include +#if TARGET_OS_MAC +const uint8_t metallib_vertex[3892] = { + 77,84,76,66,1,128,2,0,7,0,0,129,14,0,0,0,52,15,0,0,0,0,0,0,88,0,0,0,0,0,0,0,123,0,0,0,0,0,0,0,219,0,0,0,0,0,0,0,49,0,0,0,0,0,0,0,12,1,0,0,0,0,0,0,8,0,0,0,0,0,0,0,20,1,0,0,0,0,0,0,32, + 14,0,0,0,0,0,0,1,0,0,0,123,0,0,0,78,65,77,69,6,0,109,97,105,110,48,0,84,89,80,69,1,0,0,72,65,83,72,32,0,62,81,190,157,8,240,236,158,164,213,65,62,170,226,96,136,231,243,238,160,100, + 26,13,254,254,64,19,129,180,3,149,75,77,68,83,90,8,0,32,14,0,0,0,0,0,0,79,70,70,84,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,69,82,83,8,0,2,0,6,0,3,0,1,0,69,78,68,84, + 69,78,68,84,45,0,0,0,86,65,84,84,24,0,3,0,97,80,111,115,0,0,128,97,85,86,0,1,128,97,67,111,108,111,114,0,2,128,86,65,84,89,5,0,3,0,4,4,6,69,78,68,84,4,0,0,0,69,78,68,84,222,192,23, + 11,0,0,0,0,20,0,0,0,8,14,0,0,255,255,255,255,66,67,192,222,53,20,0,0,3,0,0,0,98,12,48,36,128,16,5,200,20,0,0,0,33,12,0,0,74,3,0,0,11,2,33,0,2,0,0,0,22,0,0,0,7,129,35,145,65,200,4,73, + 6,16,50,57,146,1,132,12,37,5,8,25,30,4,139,98,128,16,69,2,66,146,11,66,132,16,50,20,56,8,24,75,10,50,66,136,72,112,196,33,35,68,18,135,140,16,65,146,2,100,200,8,177,20,32,67,70,136, + 32,201,1,50,66,132,24,42,40,42,144,49,124,176,92,145,32,196,200,0,0,0,137,32,0,0,24,0,0,0,50,34,8,9,32,98,70,0,33,43,36,152,16,33,37,36,152,16,25,39,12,133,164,144,96,66,100,92,32, + 36,100,130,224,153,1,24,70,32,128,97,4,1,184,67,72,32,37,77,17,37,76,62,149,82,210,193,57,141,52,1,205,148,4,145,65,4,66,48,197,136,68,145,13,4,204,17,128,129,10,228,28,1,40,12,34, + 8,194,48,2,145,140,0,0,0,0,0,81,24,0,0,100,0,0,0,27,246,35,248,255,255,255,255,1,48,5,192,15,0,56,0,254,0,144,0,10,232,3,34,28,224,1,30,228,225,29,240,161,13,204,161,30,220,97,28,218, + 192,28,224,161,13,218,33,28,232,1,29,0,122,144,135,122,40,7,128,48,7,121,8,135,118,40,135,54,128,135,119,72,7,119,160,135,114,144,7,32,28,216,129,29,0,162,29,210,193,29,218,128,29, + 202,225,28,194,129,29,218,192,30,202,97,28,232,225,29,228,161,13,238,33,29,200,129,30,208,1,136,3,57,192,3,96,112,135,119,104,3,113,168,135,116,96,7,122,72,7,119,152,7,128,112,135, + 119,104,131,116,112,7,115,152,135,54,48,7,120,104,131,118,8,7,122,64,7,128,30,228,161,30,202,1,32,220,225,29,218,192,29,194,193,29,230,161,13,204,1,30,218,160,29,194,129,30,208,1,160, + 7,121,168,135,114,0,8,119,120,135,54,152,135,116,56,7,119,40,7,114,104,3,125,40,7,121,120,135,121,104,3,115,128,135,54,104,135,112,160,7,116,0,232,65,30,234,161,28,0,194,29,222,161, + 13,232,65,30,194,1,30,224,33,29,220,225,28,218,160,29,194,129,30,208,1,160,7,121,168,135,114,0,136,121,160,135,112,24,135,117,104,3,120,144,135,119,160,135,114,24,7,122,120,7,121,104, + 3,113,168,7,115,48,135,114,144,135,54,152,135,116,208,135,114,0,240,0,32,234,193,29,230,33,28,204,161,28,218,192,28,224,161,13,218,33,28,232,1,29,0,122,144,135,122,40,7,96,195,24,8, + 4,176,0,164,0,84,65,128,4,105,0,13,225,144,14,242,208,6,226,80,15,230,96,14,229,32,15,109,224,14,239,208,6,225,192,14,233,16,14,243,0,0,0,73,24,0,0,1,0,0,0,19,132,64,0,19,176,112,72, + 7,121,176,3,58,104,131,112,128,7,120,96,135,114,104,131,118,8,135,113,120,135,121,192,135,56,160,3,55,128,3,55,128,131,13,183,81,14,109,0,15,122,96,7,116,160,7,118,64,7,122,96,7,116, + 208,6,233,16,7,122,128,7,122,128,7,109,144,14,120,160,7,120,160,7,120,208,6,233,16,7,118,160,7,113,96,7,122,16,7,118,208,6,233,48,7,114,160,7,115,32,7,122,48,7,114,208,6,233,96,7,116, + 160,7,118,64,7,122,96,7,116,208,6,230,48,7,114,160,7,115,32,7,122,48,7,114,208,6,230,96,7,116,160,7,118,64,7,122,96,7,116,208,6,246,16,7,118,160,7,113,96,7,122,16,7,118,208,6,246,32, + 7,116,160,7,115,32,7,122,48,7,114,208,6,246,48,7,114,160,7,115,32,7,122,48,7,114,208,6,246,64,7,120,160,7,118,64,7,122,96,7,116,208,6,246,96,7,116,160,7,118,64,7,122,96,7,116,208,6, + 246,144,7,118,160,7,113,32,7,120,160,7,113,32,7,120,208,6,246,16,7,114,128,7,122,16,7,114,128,7,122,16,7,114,128,7,109,96,15,113,144,7,114,160,7,114,80,7,118,160,7,114,80,7,118,208, + 6,246,32,7,117,96,7,122,32,7,117,96,7,122,32,7,117,96,7,109,96,15,117,16,7,114,160,7,117,16,7,114,160,7,117,16,7,114,208,6,246,16,7,112,32,7,116,160,7,113,0,7,114,64,7,122,16,7,112, + 32,7,116,208,6,238,128,7,122,16,7,118,160,7,115,32,7,26,33,12,89,48,0,210,208,67,42,160,48,0,0,8,0,0,0,4,0,0,0,0,0,10,64,98,131,64,81,166,1,0,128,44,16,0,11,0,0,0,50,30,152,16,25,17, + 76,144,140,9,38,71,198,4,67,202,34,40,129,66,40,135,242,41,64,129,130,40,144,98,24,1,40,3,218,17,0,210,177,132,39,0,0,0,177,24,0,0,165,0,0,0,51,8,128,28,196,225,28,102,20,1,61,136, + 67,56,132,195,140,66,128,7,121,120,7,115,152,113,12,230,0,15,237,16,14,244,128,14,51,12,66,30,194,193,29,206,161,28,102,48,5,61,136,67,56,132,131,27,204,3,61,200,67,61,140,3,61,204, + 120,140,116,112,7,123,8,7,121,72,135,112,112,7,122,112,3,118,120,135,112,32,135,25,204,17,14,236,144,14,225,48,15,110,48,15,227,240,14,240,80,14,51,16,196,29,222,33,28,216,33,29,194, + 97,30,102,48,137,59,188,131,59,208,67,57,180,3,60,188,131,60,132,3,59,204,240,20,118,96,7,123,104,7,55,104,135,114,104,7,55,128,135,112,144,135,112,96,7,118,40,7,118,248,5,118,120, + 135,119,128,135,95,8,135,113,24,135,114,152,135,121,152,129,44,238,240,14,238,224,14,245,192,14,236,48,3,98,200,161,28,228,161,28,204,161,28,228,161,28,220,97,28,202,33,28,196,129, + 29,202,97,6,214,144,67,57,200,67,57,152,67,57,200,67,57,184,195,56,148,67,56,136,3,59,148,195,47,188,131,60,252,130,59,212,3,59,176,195,12,199,105,135,112,88,135,114,112,131,116,104, + 7,120,96,135,116,24,135,116,160,135,25,206,83,15,238,0,15,242,80,14,228,144,14,227,64,15,225,32,14,236,80,14,51,32,40,29,220,193,30,194,65,30,210,33,28,220,129,30,220,224,28,228,225, + 29,234,1,30,102,24,81,56,176,67,58,156,131,59,204,80,36,118,96,7,123,104,7,55,96,135,119,120,7,120,152,81,76,244,144,15,240,80,14,51,30,106,30,202,97,28,232,33,29,222,193,29,126,1, + 30,228,161,28,204,33,29,240,97,6,84,133,131,56,204,195,59,176,67,61,208,67,57,252,194,60,228,67,59,136,195,59,176,195,140,197,10,135,121,152,135,119,24,135,116,8,7,122,40,7,114,152, + 129,92,227,16,14,236,192,14,229,80,14,243,48,35,193,210,65,30,228,225,23,216,225,29,222,1,30,102,72,25,59,176,131,61,180,131,27,132,195,56,140,67,57,204,195,60,184,193,57,200,195,59, + 212,3,60,204,72,180,113,8,7,118,96,7,113,8,135,113,88,135,25,219,198,14,236,96,15,237,224,6,240,32,15,229,48,15,229,32,15,246,80,14,110,16,14,227,48,14,229,48,15,243,224,6,233,224, + 14,228,80,14,248,48,35,226,236,97,28,194,129,29,216,225,23,236,33,29,230,33,29,196,33,29,216,33,29,232,33,31,102,32,157,59,188,67,61,184,3,57,148,131,57,204,88,188,112,112,7,119,120, + 7,122,8,7,122,72,135,119,112,135,25,206,135,14,229,16,14,240,16,14,236,192,14,239,48,14,243,144,14,244,80,14,51,40,48,8,135,116,144,7,55,48,135,122,112,135,113,160,135,116,120,7,119, + 248,133,115,144,135,119,168,7,120,152,7,0,0,0,0,121,32,0,0,26,1,0,0,114,30,72,32,67,136,12,25,9,114,50,72,32,35,129,140,145,145,209,68,160,16,40,100,60,49,50,66,142,144,33,163,152, + 6,100,208,82,0,0,0,139,210,88,216,6,109,80,28,20,27,71,6,209,18,25,76,178,24,6,179,64,18,49,24,202,131,68,148,161,68,87,35,0,0,0,0,83,68,75,32,86,101,114,115,105,111,110,119,99,104, + 97,114,95,115,105,122,101,102,114,97,109,101,45,112,111,105,110,116,101,114,97,105,114,46,109,97,120,95,100,101,118,105,99,101,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120, + 95,99,111,110,115,116,97,110,116,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120,95,116,104,114,101,97,100,103,114,111,117,112,95,98,117,102,102,101,114,115,97,105,114,46,109, + 97,120,95,116,101,120,116,117,114,101,115,97,105,114,46,109,97,120,95,114,101,97,100,95,119,114,105,116,101,95,116,101,120,116,117,114,101,115,97,105,114,46,109,97,120,95,115,97,109, + 112,108,101,114,115,65,112,112,108,101,32,109,101,116,97,108,32,118,101,114,115,105,111,110,32,51,50,48,50,51,46,51,54,56,32,40,109,101,116,97,108,102,101,45,51,50,48,50,51,46,51,54, + 56,41,77,101,116,97,108,97,105,114,46,99,111,109,112,105,108,101,46,100,101,110,111,114,109,115,95,100,105,115,97,98,108,101,97,105,114,46,99,111,109,112,105,108,101,46,102,97,115, + 116,95,109,97,116,104,95,101,110,97,98,108,101,97,105,114,46,99,111,109,112,105,108,101,46,102,114,97,109,101,98,117,102,102,101,114,95,102,101,116,99,104,95,101,110,97,98,108,101, + 97,105,114,46,118,101,114,116,101,120,95,111,117,116,112,117,116,117,115,101,114,40,108,111,99,110,48,41,97,105,114,46,97,114,103,95,116,121,112,101,95,110,97,109,101,102,108,111,97, + 116,52,97,105,114,46,97,114,103,95,110,97,109,101,79,117,116,95,67,111,108,111,114,117,115,101,114,40,108,111,99,110,49,41,102,108,111,97,116,50,79,117,116,95,85,86,97,105,114,46,112, + 111,115,105,116,105,111,110,103,108,95,80,111,115,105,116,105,111,110,97,105,114,46,118,101,114,116,101,120,95,105,110,112,117,116,97,105,114,46,108,111,99,97,116,105,111,110,95,105, + 110,100,101,120,97,80,111,115,97,85,86,97,67,111,108,111,114,97,105,114,46,98,117,102,102,101,114,97,105,114,46,98,117,102,102,101,114,95,115,105,122,101,97,105,114,46,114,101,97,100, + 97,105,114,46,97,100,100,114,101,115,115,95,115,112,97,99,101,97,105,114,46,115,116,114,117,99,116,95,116,121,112,101,95,105,110,102,111,117,83,99,97,108,101,117,84,114,97,110,115, + 108,97,116,101,97,105,114,46,97,114,103,95,116,121,112,101,95,115,105,122,101,97,105,114,46,97,114,103,95,116,121,112,101,95,97,108,105,103,110,95,115,105,122,101,117,80,117,115,104, + 67,111,110,115,116,97,110,116,112,99,0,0,0,166,119,0,0,0,0,0,0,48,130,144,4,35,8,74,51,130,144,8,35,8,201,48,130,144,16,35,8,73,49,130,144,24,35,8,201,49,130,144,32,35,8,73,50,130, + 144,40,35,8,201,50,130,112,0,51,12,106,16,172,193,12,3,27,8,109,48,195,224,6,131,26,204,48,184,1,241,6,51,12,110,80,188,193,12,131,27,24,111,48,195,224,6,7,28,204,48,184,1,18,7,51, + 12,110,144,200,193,12,129,50,195,160,6,115,64,7,51,16,75,29,176,1,29,204,16,48,51,4,205,12,129,51,131,241,64,145,52,81,51,24,79,21,89,211,53,67,129,69,210,148,205,48,152,194,41,160, + 194,12,9,29,104,27,29,176,65,100,77,220,12,9,27,104,27,27,176,65,100,77,221,12,137,26,104,155,26,176,65,36,77,222,12,10,29,196,1,29,88,100,16,7,113,64,7,86,25,204,64,213,193,7,6,114, + 176,209,1,27,132,129,24,168,193,24,180,130,25,200,193,25,196,65,132,6,83,26,204,64,168,194,42,176,130,43,204,48,216,65,42,188,194,157,1,192,113,28,199,113,28,199,113,28,199,185,129, + 27,184,129,27,184,129,27,184,129,27,184,129,69,7,122,96,89,22,29,208,129,27,208,1,46,224,2,46,240,3,122,128,130,140,4,38,40,35,54,54,187,54,151,182,55,178,58,182,50,23,51,182,176,179, + 185,81,18,59,184,3,60,200,3,61,216,3,62,232,3,63,72,133,141,205,174,205,37,141,172,204,141,110,148,224,15,114,9,75,147,115,177,43,147,155,75,123,115,27,37,0,133,164,194,210,228,92, + 216,194,220,206,234,194,206,202,190,236,202,228,230,210,222,220,70,9,66,33,167,176,52,57,151,177,183,54,184,52,182,178,175,55,56,186,180,55,183,185,81,6,81,24,5,82,72,37,44,77,206, + 197,174,76,142,174,12,111,148,224,21,0,0,0,169,24,0,0,37,0,0,0,11,10,114,40,135,119,128,7,122,88,112,152,67,61,184,195,56,176,67,57,208,195,130,230,28,198,161,13,232,65,30,194,193, + 29,230,33,29,232,33,29,222,193,29,22,52,227,96,14,231,80,15,225,32,15,228,64,15,225,32,15,231,80,14,244,176,128,129,7,121,40,135,112,96,7,118,120,135,113,8,7,122,40,7,114,88,112,156, + 195,56,180,1,59,164,131,61,148,195,2,107,28,216,33,28,220,225,28,220,32,28,228,97,28,220,32,28,232,129,30,194,97,28,208,161,28,200,97,28,194,129,29,216,97,193,1,15,244,32,15,225,80, + 15,244,128,14,0,0,0,0,209,16,0,0,6,0,0,0,7,204,60,164,131,59,156,3,59,148,3,61,160,131,60,148,67,56,144,195,1,0,0,0,97,32,0,0,68,0,0,0,19,4,65,44,16,0,0,0,11,0,0,0,148,51,0,180,37, + 64,61,7,161,72,146,52,7,161,72,9,65,48,26,48,2,48,70,0,130,32,136,127,20,115,16,150,117,97,36,163,1,52,51,0,0,0,0,0,241,48,0,0,32,0,0,0,34,71,200,144,81,18,196,43,0,0,0,0,207,115,89, + 0,111,109,110,105,112,111,116,101,110,116,32,99,104,97,114,83,105,109,112,108,101,32,67,43,43,32,84,66,65,65,97,105,114,45,97,108,105,97,115,45,115,99,111,112,101,115,40,109,97,105, + 110,48,41,97,105,114,45,97,108,105,97,115,45,115,99,111,112,101,45,97,114,103,40,51,41,0,19,132,133,89,33,216,194,44,172,24,110,193,22,104,97,67,32,11,27,134,88,192,133,90,216,48,228, + 66,46,212,194,134,224,22,0,0,157,134,5,146,40,16,130,1,36,254,157,6,103,234,40,16,130,67,0,254,131,12,1,226,12,50,4,138,51,134,48,68,22,128,255,28,195,16,76,179,13,204,5,204,54,4,89, + 48,219,16,12,194,6,1,49,0,4,0,0,0,91,138,32,200,133,67,23,182,20,68,144,11,135,46,0,0,0,0,0,0,113,32,0,0,3,0,0,0,50,14,16,34,132,0,134,6,0,0,0,0,0,0,0,0,101,12,0,0,31,0,0,0,18,3,148, + 240,0,0,0,0,3,0,0,0,5,0,0,0,9,0,0,0,76,0,0,0,1,0,0,0,88,0,0,0,0,0,0,0,88,0,0,0,1,0,0,0,112,0,0,0,0,0,0,0,14,0,0,0,24,0,0,0,0,0,0,0,5,0,0,0,5,0,0,0,0,0,0,0,112,0,0,0,0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,0,0,0,255,255,255,255,0,36,0,0,0,0,0,0,93,12,0,0,13,0,0,0,18,3,148,102,0,0,0,0,109,97,105,110,48,51,50,48,50,51,46,51,54,56,97,105,114,54, + 52,45,97,112,112,108,101,45,109,97,99,111,115,120,49,52,46,48,46,48,0,0,0,0,0,0,0,0,0,0, +}; +const uint8_t metallib_fragment[3787] = { + 77,84,76,66,1,128,2,0,7,0,0,129,14,0,0,0,203,14,0,0,0,0,0,0,88,0,0,0,0,0,0,0,123,0,0,0,0,0,0,0,219,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,227,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,235,0,0,0,0,0,0,0, + 224,13,0,0,0,0,0,0,1,0,0,0,123,0,0,0,78,65,77,69,6,0,109,97,105,110,48,0,84,89,80,69,1,0,1,72,65,83,72,32,0,201,103,233,140,10,95,185,107,79,93,85,82,78,218,248,8,95,184,8,139,191, + 155,174,56,51,95,203,135,255,117,44,62,77,68,83,90,8,0,224,13,0,0,0,0,0,0,79,70,70,84,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,69,82,83,8,0,2,0,6,0,3,0,1,0,69,78,68, + 84,69,78,68,84,4,0,0,0,69,78,68,84,4,0,0,0,69,78,68,84,222,192,23,11,0,0,0,0,20,0,0,0,196,13,0,0,255,255,255,255,66,67,192,222,53,20,0,0,3,0,0,0,98,12,48,36,128,16,5,200,20,0,0,0,33, + 12,0,0,44,3,0,0,11,2,33,0,2,0,0,0,22,0,0,0,7,129,35,145,65,200,4,73,6,16,50,57,146,1,132,12,37,5,8,25,30,4,139,98,128,20,69,2,66,146,11,66,164,16,50,20,56,8,24,75,10,50,82,136,72,112, + 196,33,35,68,18,135,140,16,65,146,2,100,200,8,177,20,32,67,70,136,32,201,1,50,82,132,24,42,40,42,144,49,124,176,92,145,32,197,200,0,0,0,137,32,0,0,31,0,0,0,50,34,72,9,32,98,70,0,33, + 43,36,152,20,33,37,36,152,20,25,39,12,133,164,144,96,82,100,92,32,36,101,130,128,154,1,24,70,32,128,27,132,97,4,1,64,74,154,34,74,152,252,127,34,174,137,138,136,223,30,254,105,140, + 0,24,68,32,2,140,164,41,162,132,201,255,37,128,121,22,34,250,167,49,2,96,16,193,16,76,33,194,40,135,208,28,1,114,132,160,230,8,130,57,2,48,24,70,16,26,163,172,114,6,115,12,128,70,111, + 32,64,5,218,8,0,0,81,24,0,0,105,0,0,0,27,246,35,248,255,255,255,255,1,104,3,96,13,0,83,0,252,0,144,128,10,232,3,34,28,224,1,30,228,225,29,240,161,13,204,161,30,220,97,28,218,192,28, + 224,161,13,218,33,28,232,1,29,0,122,144,135,122,40,7,128,48,7,121,8,135,118,40,135,54,128,135,119,72,7,119,160,135,114,144,7,32,28,216,129,29,0,162,29,210,193,29,218,128,29,202,225, + 28,194,129,29,218,192,30,202,97,28,232,225,29,228,161,13,238,33,29,200,129,30,208,1,136,3,57,192,3,96,112,135,119,104,3,113,168,135,116,96,7,122,72,7,119,152,7,128,112,135,119,104, + 131,116,112,7,115,152,135,54,48,7,120,104,131,118,8,7,122,64,7,128,30,228,161,30,202,1,32,220,225,29,218,192,29,194,193,29,230,161,13,204,1,30,218,160,29,194,129,30,208,1,160,7,121, + 168,135,114,0,8,119,120,135,54,152,135,116,56,7,119,40,7,114,104,3,125,40,7,121,120,135,121,104,3,115,128,135,54,104,135,112,160,7,116,0,232,65,30,234,161,28,0,194,29,222,161,13,232, + 65,30,194,1,30,224,33,29,220,225,28,218,160,29,194,129,30,208,1,160,7,121,168,135,114,0,136,121,160,135,112,24,135,117,104,3,120,144,135,119,160,135,114,24,7,122,120,7,121,104,3,113, + 168,7,115,48,135,114,144,135,54,152,135,116,208,135,114,0,240,0,32,234,193,29,230,33,28,204,161,28,218,192,28,224,161,13,218,33,28,232,1,29,0,122,144,135,122,40,7,96,131,33,12,192, + 2,84,27,140,129,0,22,160,218,0,17,255,255,255,255,63,0,109,0,172,1,96,10,128,31,0,18,80,1,125,176,193,40,2,96,1,170,13,134,33,0,11,80,109,96,142,255,255,255,255,31,128,54,0,214,0,144, + 128,10,232,3,0,73,24,0,0,4,0,0,0,19,134,64,24,38,12,68,97,76,24,142,194,0,0,0,0,19,176,112,72,7,121,176,3,58,104,131,112,128,7,120,96,135,114,104,131,118,8,135,113,120,135,121,192, + 135,56,160,3,55,128,3,55,128,131,13,183,81,14,109,0,15,122,96,7,116,160,7,118,64,7,122,96,7,116,208,6,233,16,7,122,128,7,122,128,7,109,144,14,120,160,7,120,160,7,120,208,6,233,16,7, + 118,160,7,113,96,7,122,16,7,118,208,6,233,48,7,114,160,7,115,32,7,122,48,7,114,208,6,233,96,7,116,160,7,118,64,7,122,96,7,116,208,6,230,48,7,114,160,7,115,32,7,122,48,7,114,208,6,230, + 96,7,116,160,7,118,64,7,122,96,7,116,208,6,246,16,7,118,160,7,113,96,7,122,16,7,118,208,6,246,32,7,116,160,7,115,32,7,122,48,7,114,208,6,246,48,7,114,160,7,115,32,7,122,48,7,114,208, + 6,246,64,7,120,160,7,118,64,7,122,96,7,116,208,6,246,96,7,116,160,7,118,64,7,122,96,7,116,208,6,246,144,7,118,160,7,113,32,7,120,160,7,113,32,7,120,208,6,246,16,7,114,128,7,122,16, + 7,114,128,7,122,16,7,114,128,7,109,96,15,113,144,7,114,160,7,114,80,7,118,160,7,114,80,7,118,208,6,246,32,7,117,96,7,122,32,7,117,96,7,122,32,7,117,96,7,109,96,15,117,16,7,114,160, + 7,117,16,7,114,160,7,117,16,7,114,208,6,246,16,7,112,32,7,116,160,7,113,0,7,114,64,7,122,16,7,112,32,7,116,208,6,238,128,7,122,16,7,118,160,7,115,32,7,26,33,12,89,48,0,210,208,67,42, + 160,64,0,0,8,0,0,0,4,0,0,0,0,0,10,96,72,85,108,15,16,0,2,0,0,128,0,0,0,0,0,64,1,72,108,16,40,234,50,0,0,144,5,2,0,0,0,10,0,0,0,50,30,152,16,25,17,76,144,140,9,38,71,198,4,67,106,69, + 80,2,133,80,14,229,83,128,2,5,81,32,197,48,2,80,6,36,199,18,158,0,0,177,24,0,0,165,0,0,0,51,8,128,28,196,225,28,102,20,1,61,136,67,56,132,195,140,66,128,7,121,120,7,115,152,113,12, + 230,0,15,237,16,14,244,128,14,51,12,66,30,194,193,29,206,161,28,102,48,5,61,136,67,56,132,131,27,204,3,61,200,67,61,140,3,61,204,120,140,116,112,7,123,8,7,121,72,135,112,112,7,122, + 112,3,118,120,135,112,32,135,25,204,17,14,236,144,14,225,48,15,110,48,15,227,240,14,240,80,14,51,16,196,29,222,33,28,216,33,29,194,97,30,102,48,137,59,188,131,59,208,67,57,180,3,60, + 188,131,60,132,3,59,204,240,20,118,96,7,123,104,7,55,104,135,114,104,7,55,128,135,112,144,135,112,96,7,118,40,7,118,248,5,118,120,135,119,128,135,95,8,135,113,24,135,114,152,135,121, + 152,129,44,238,240,14,238,224,14,245,192,14,236,48,3,98,200,161,28,228,161,28,204,161,28,228,161,28,220,97,28,202,33,28,196,129,29,202,97,6,214,144,67,57,200,67,57,152,67,57,200,67, + 57,184,195,56,148,67,56,136,3,59,148,195,47,188,131,60,252,130,59,212,3,59,176,195,12,199,105,135,112,88,135,114,112,131,116,104,7,120,96,135,116,24,135,116,160,135,25,206,83,15,238, + 0,15,242,80,14,228,144,14,227,64,15,225,32,14,236,80,14,51,32,40,29,220,193,30,194,65,30,210,33,28,220,129,30,220,224,28,228,225,29,234,1,30,102,24,81,56,176,67,58,156,131,59,204,80, + 36,118,96,7,123,104,7,55,96,135,119,120,7,120,152,81,76,244,144,15,240,80,14,51,30,106,30,202,97,28,232,33,29,222,193,29,126,1,30,228,161,28,204,33,29,240,97,6,84,133,131,56,204,195, + 59,176,67,61,208,67,57,252,194,60,228,67,59,136,195,59,176,195,140,197,10,135,121,152,135,119,24,135,116,8,7,122,40,7,114,152,129,92,227,16,14,236,192,14,229,80,14,243,48,35,193,210, + 65,30,228,225,23,216,225,29,222,1,30,102,72,25,59,176,131,61,180,131,27,132,195,56,140,67,57,204,195,60,184,193,57,200,195,59,212,3,60,204,72,180,113,8,7,118,96,7,113,8,135,113,88, + 135,25,219,198,14,236,96,15,237,224,6,240,32,15,229,48,15,229,32,15,246,80,14,110,16,14,227,48,14,229,48,15,243,224,6,233,224,14,228,80,14,248,48,35,226,236,97,28,194,129,29,216,225, + 23,236,33,29,230,33,29,196,33,29,216,33,29,232,33,31,102,32,157,59,188,67,61,184,3,57,148,131,57,204,88,188,112,112,7,119,120,7,122,8,7,122,72,135,119,112,135,25,206,135,14,229,16, + 14,240,16,14,236,192,14,239,48,14,243,144,14,244,80,14,51,40,48,8,135,116,144,7,55,48,135,122,112,135,113,160,135,116,120,7,119,248,133,115,144,135,119,168,7,120,152,7,0,0,0,0,121, + 32,0,0,252,0,0,0,114,30,72,32,67,136,12,25,9,114,50,72,32,35,129,140,145,145,209,68,160,16,40,100,60,49,50,66,142,144,33,163,56,6,220,41,1,0,0,0,139,210,88,216,6,109,80,28,20,27,71, + 6,81,100,48,134,180,40,15,178,24,197,34,41,24,178,28,13,83,68,75,32,86,101,114,115,105,111,110,119,99,104,97,114,95,115,105,122,101,102,114,97,109,101,45,112,111,105,110,116,101,114, + 97,105,114,46,109,97,120,95,100,101,118,105,99,101,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120,95,99,111,110,115,116,97,110,116,95,98,117,102,102,101,114,115,97,105,114, + 46,109,97,120,95,116,104,114,101,97,100,103,114,111,117,112,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120,95,116,101,120,116,117,114,101,115,97,105,114,46,109,97,120,95,114, + 101,97,100,95,119,114,105,116,101,95,116,101,120,116,117,114,101,115,97,105,114,46,109,97,120,95,115,97,109,112,108,101,114,115,65,112,112,108,101,32,109,101,116,97,108,32,118,101, + 114,115,105,111,110,32,51,50,48,50,51,46,51,54,56,32,40,109,101,116,97,108,102,101,45,51,50,48,50,51,46,51,54,56,41,77,101,116,97,108,97,105,114,46,99,111,109,112,105,108,101,46,100, + 101,110,111,114,109,115,95,100,105,115,97,98,108,101,97,105,114,46,99,111,109,112,105,108,101,46,102,97,115,116,95,109,97,116,104,95,101,110,97,98,108,101,97,105,114,46,99,111,109, + 112,105,108,101,46,102,114,97,109,101,98,117,102,102,101,114,95,102,101,116,99,104,95,101,110,97,98,108,101,97,105,114,46,114,101,110,100,101,114,95,116,97,114,103,101,116,97,105,114, + 46,97,114,103,95,116,121,112,101,95,110,97,109,101,102,108,111,97,116,52,97,105,114,46,97,114,103,95,110,97,109,101,102,67,111,108,111,114,97,105,114,46,102,114,97,103,109,101,110, + 116,95,105,110,112,117,116,117,115,101,114,40,108,111,99,110,48,41,97,105,114,46,99,101,110,116,101,114,97,105,114,46,112,101,114,115,112,101,99,116,105,118,101,73,110,95,67,111,108, + 111,114,117,115,101,114,40,108,111,99,110,49,41,102,108,111,97,116,50,73,110,95,85,86,97,105,114,46,116,101,120,116,117,114,101,97,105,114,46,108,111,99,97,116,105,111,110,95,105,110, + 100,101,120,97,105,114,46,115,97,109,112,108,101,116,101,120,116,117,114,101,50,100,60,102,108,111,97,116,44,32,115,97,109,112,108,101,62,115,84,101,120,116,117,114,101,97,105,114, + 46,115,97,109,112,108,101,114,115,97,109,112,108,101,114,115,84,101,120,116,117,114,101,83,109,112,108,114,0,198,96,0,0,0,0,0,0,48,130,208,8,35,8,82,51,130,208,12,35,8,13,49,130,208, + 20,35,8,141,49,130,208,28,35,8,13,50,130,208,36,35,8,141,50,130,208,44,35,8,13,51,130,144,0,51,12,100,16,148,193,12,131,25,8,103,48,195,128,6,3,25,204,48,160,1,145,6,51,12,104,80,164, + 193,12,3,26,24,105,48,195,128,6,135,26,204,48,160,1,178,6,51,12,104,144,176,193,12,129,50,195,64,6,109,224,6,51,16,203,27,152,129,27,204,16,48,51,4,205,12,129,51,195,241,184,129,27, + 64,145,52,205,16,128,194,12,137,27,80,149,117,65,145,132,205,144,152,1,149,89,23,164,73,219,12,10,25,112,157,27,152,129,7,125,18,24,204,144,188,65,24,116,110,96,6,144,24,72,99,48,3, + 33,10,163,64,10,165,48,195,0,7,161,96,10,71,6,0,199,113,28,199,113,28,199,113,28,231,6,110,224,6,110,224,6,110,224,6,110,224,6,22,29,232,129,101,89,166,192,177,2,43,144,131,58,128, + 130,140,4,38,40,35,54,54,187,54,151,182,55,178,58,182,50,23,51,182,176,179,185,81,18,56,136,3,57,152,3,58,168,3,59,184,3,60,72,133,141,205,174,205,37,141,172,204,141,110,148,32,15, + 114,9,75,147,115,177,43,147,155,75,123,115,27,37,208,131,164,194,210,228,92,216,194,220,206,234,194,206,202,190,236,202,228,230,210,222,220,70,9,246,32,167,176,52,57,151,177,183,54, + 184,52,182,178,175,55,56,186,180,55,183,185,81,6,62,232,3,63,72,38,44,77,206,197,76,46,236,172,173,204,141,110,148,192,20,0,0,0,0,169,24,0,0,37,0,0,0,11,10,114,40,135,119,128,7,122, + 88,112,152,67,61,184,195,56,176,67,57,208,195,130,230,28,198,161,13,232,65,30,194,193,29,230,33,29,232,33,29,222,193,29,22,52,227,96,14,231,80,15,225,32,15,228,64,15,225,32,15,231, + 80,14,244,176,128,129,7,121,40,135,112,96,7,118,120,135,113,8,7,122,40,7,114,88,112,156,195,56,180,1,59,164,131,61,148,195,2,107,28,216,33,28,220,225,28,220,32,28,228,97,28,220,32, + 28,232,129,30,194,97,28,208,161,28,200,97,28,194,129,29,216,97,193,1,15,244,32,15,225,80,15,244,128,14,0,0,0,0,209,16,0,0,6,0,0,0,7,204,60,164,131,59,156,3,59,148,3,61,160,131,60,148, + 67,56,144,195,1,0,0,0,97,32,0,0,49,0,0,0,19,4,65,44,16,0,0,0,4,0,0,0,196,106,96,4,128,220,8,0,129,17,0,18,51,0,0,0,241,48,0,0,28,0,0,0,34,71,200,144,81,14,196,42,0,0,0,0,23,134,1,0, + 97,105,114,45,97,108,105,97,115,45,115,99,111,112,101,115,40,109,97,105,110,48,41,97,105,114,45,97,108,105,97,115,45,115,99,111,112,101,45,115,97,109,112,108,101,114,115,97,105,114, + 45,97,108,105,97,115,45,115,99,111,112,101,45,116,101,120,116,117,114,101,115,0,43,132,85,64,133,21,3,43,172,66,42,172,24,90,97,21,84,97,131,208,10,172,0,0,35,6,205,16,130,96,240,88, + 135,129,20,3,33,8,204,104,66,0,96,176,136,255,108,3,17,0,27,4,196,0,0,0,2,0,0,0,91,6,224,104,5,0,0,0,0,0,0,0,113,32,0,0,3,0,0,0,50,14,16,34,132,0,251,5,0,0,0,0,0,0,0,0,101,12,0,0,37, + 0,0,0,18,3,148,40,1,0,0,0,3,0,0,0,32,0,0,0,9,0,0,0,76,0,0,0,1,0,0,0,88,0,0,0,0,0,0,0,88,0,0,0,2,0,0,0,136,0,0,0,0,0,0,0,41,0,0,0,24,0,0,0,0,0,0,0,5,0,0,0,5,0,0,0,0,0,0,0,136,0,0,0, + 0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,0,0,0,255,255,255,255,0,36,0,0,5,0,0,0,27,0,0,0,5,0,0,0,27,0,0,0,255,255,255,255,8,36,0,0,0,0,0,0,93,12,0,0,20,0,0,0,18,3, + 148,161,0,0,0,0,109,97,105,110,48,97,105,114,46,115,97,109,112,108,101,95,116,101,120,116,117,114,101,95,50,100,46,118,52,102,51,50,51,50,48,50,51,46,51,54,56,97,105,114,54,52,45,97, + 112,112,108,101,45,109,97,99,111,115,120,49,52,46,48,46,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; +static uint8_t msl_vertex[800] = +{ + 35,105,110,99,108,117,100,101,32,60,109,101,116,97,108,95,115,116,100,108,105,98,62,10,35,105,110,99,108,117,100,101,32,60,115,105,109,100,47,115,105,109,100,46,104,62,10,10,117,115, + 105,110,103,32,110,97,109,101,115,112,97,99,101,32,109,101,116,97,108,59,10,10,115,116,114,117,99,116,32,95,57,10,123,10,32,32,32,32,102,108,111,97,116,52,32,67,111,108,111,114,59, + 10,32,32,32,32,102,108,111,97,116,50,32,85,86,59,10,125,59,10,10,115,116,114,117,99,116,32,85,66,79,10,123,10,32,32,32,32,102,108,111,97,116,50,32,117,83,99,97,108,101,59,10,32,32, + 32,32,102,108,111,97,116,50,32,117,84,114,97,110,115,108,97,116,101,59,10,125,59,10,10,115,116,114,117,99,116,32,109,97,105,110,48,95,111,117,116,10,123,10,32,32,32,32,102,108,111, + 97,116,52,32,79,117,116,95,67,111,108,111,114,32,91,91,117,115,101,114,40,108,111,99,110,48,41,93,93,59,10,32,32,32,32,102,108,111,97,116,50,32,79,117,116,95,85,86,32,91,91,117,115, + 101,114,40,108,111,99,110,49,41,93,93,59,10,32,32,32,32,102,108,111,97,116,52,32,103,108,95,80,111,115,105,116,105,111,110,32,91,91,112,111,115,105,116,105,111,110,93,93,59,10,125, + 59,10,10,115,116,114,117,99,116,32,109,97,105,110,48,95,105,110,10,123,10,32,32,32,32,102,108,111,97,116,50,32,97,80,111,115,32,91,91,97,116,116,114,105,98,117,116,101,40,48,41,93, + 93,59,10,32,32,32,32,102,108,111,97,116,50,32,97,85,86,32,91,91,97,116,116,114,105,98,117,116,101,40,49,41,93,93,59,10,32,32,32,32,102,108,111,97,116,52,32,97,67,111,108,111,114,32, + 91,91,97,116,116,114,105,98,117,116,101,40,50,41,93,93,59,10,125,59,10,10,118,101,114,116,101,120,32,109,97,105,110,48,95,111,117,116,32,109,97,105,110,48,40,109,97,105,110,48,95,105, + 110,32,105,110,32,91,91,115,116,97,103,101,95,105,110,93,93,44,32,99,111,110,115,116,97,110,116,32,85,66,79,38,32,117,98,111,32,91,91,98,117,102,102,101,114,40,48,41,93,93,41,10,123, + 10,32,32,32,32,109,97,105,110,48,95,111,117,116,32,111,117,116,32,61,32,123,125,59,10,32,32,32,32,95,57,32,79,117,116,32,61,32,123,125,59,10,32,32,32,32,79,117,116,46,67,111,108,111, + 114,32,61,32,105,110,46,97,67,111,108,111,114,59,10,32,32,32,32,79,117,116,46,85,86,32,61,32,105,110,46,97,85,86,59,10,32,32,32,32,111,117,116,46,103,108,95,80,111,115,105,116,105, + 111,110,32,61,32,102,108,111,97,116,52,40,40,105,110,46,97,80,111,115,32,42,32,117,98,111,46,117,83,99,97,108,101,41,32,43,32,117,98,111,46,117,84,114,97,110,115,108,97,116,101,44, + 32,48,46,48,44,32,49,46,48,41,59,10,32,32,32,32,111,117,116,46,103,108,95,80,111,115,105,116,105,111,110,46,121,32,42,61,32,40,45,49,46,48,41,59,10,32,32,32,32,111,117,116,46,79,117, + 116,95,67,111,108,111,114,32,61,32,79,117,116,46,67,111,108,111,114,59,10,32,32,32,32,111,117,116,46,79,117,116,95,85,86,32,61,32,79,117,116,46,85,86,59,10,32,32,32,32,114,101,116, + 117,114,110,32,111,117,116,59,10,125,10,10, +}; +static uint8_t msl_fragment[580] = +{ + 35,105,110,99,108,117,100,101,32,60,109,101,116,97,108,95,115,116,100,108,105,98,62,10,35,105,110,99,108,117,100,101,32,60,115,105,109,100,47,115,105,109,100,46,104,62,10,10,117,115, + 105,110,103,32,110,97,109,101,115,112,97,99,101,32,109,101,116,97,108,59,10,10,115,116,114,117,99,116,32,95,49,49,10,123,10,32,32,32,32,102,108,111,97,116,52,32,67,111,108,111,114, + 59,10,32,32,32,32,102,108,111,97,116,50,32,85,86,59,10,125,59,10,10,115,116,114,117,99,116,32,109,97,105,110,48,95,111,117,116,10,123,10,32,32,32,32,102,108,111,97,116,52,32,102,67, + 111,108,111,114,32,91,91,99,111,108,111,114,40,48,41,93,93,59,10,125,59,10,10,115,116,114,117,99,116,32,109,97,105,110,48,95,105,110,10,123,10,32,32,32,32,102,108,111,97,116,52,32, + 73,110,95,67,111,108,111,114,32,91,91,117,115,101,114,40,108,111,99,110,48,41,93,93,59,10,32,32,32,32,102,108,111,97,116,50,32,73,110,95,85,86,32,91,91,117,115,101,114,40,108,111,99, + 110,49,41,93,93,59,10,125,59,10,10,102,114,97,103,109,101,110,116,32,109,97,105,110,48,95,111,117,116,32,109,97,105,110,48,40,109,97,105,110,48,95,105,110,32,105,110,32,91,91,115,116, + 97,103,101,95,105,110,93,93,44,32,116,101,120,116,117,114,101,50,100,60,102,108,111,97,116,62,32,115,84,101,120,116,117,114,101,32,91,91,116,101,120,116,117,114,101,40,48,41,93,93, + 44,32,115,97,109,112,108,101,114,32,115,84,101,120,116,117,114,101,83,109,112,108,114,32,91,91,115,97,109,112,108,101,114,40,48,41,93,93,41,10,123,10,32,32,32,32,109,97,105,110,48, + 95,111,117,116,32,111,117,116,32,61,32,123,125,59,10,32,32,32,32,95,49,49,32,73,110,32,61,32,123,125,59,10,32,32,32,32,73,110,46,67,111,108,111,114,32,61,32,105,110,46,73,110,95,67, + 111,108,111,114,59,10,32,32,32,32,73,110,46,85,86,32,61,32,105,110,46,73,110,95,85,86,59,10,32,32,32,32,111,117,116,46,102,67,111,108,111,114,32,61,32,73,110,46,67,111,108,111,114, + 32,42,32,115,84,101,120,116,117,114,101,46,115,97,109,112,108,101,40,115,84,101,120,116,117,114,101,83,109,112,108,114,44,32,73,110,46,85,86,41,59,10,32,32,32,32,114,101,116,117,114, + 110,32,111,117,116,59,10,125,10,10, +}; +#elif TARGET_OS_IPHONE +const uint8_t metallib_vertex[3876] = { + 77,84,76,66,1,0,2,0,7,0,0,130,18,0,1,0,36,15,0,0,0,0,0,0,88,0,0,0,0,0,0,0,123,0,0,0,0,0,0,0,219,0,0,0,0,0,0,0,49,0,0,0,0,0,0,0,12,1,0,0,0,0,0,0,8,0,0,0,0,0,0,0,20,1,0,0,0,0,0,0,16, + 14,0,0,0,0,0,0,1,0,0,0,123,0,0,0,78,65,77,69,6,0,109,97,105,110,48,0,84,89,80,69,1,0,0,72,65,83,72,32,0,240,54,230,217,232,66,102,78,35,5,77,235,101,252,229,192,148,96,126,162,111, + 77,253,247,211,52,17,198,182,137,68,244,77,68,83,90,8,0,16,14,0,0,0,0,0,0,79,70,70,84,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,69,82,83,8,0,2,0,7,0,3,0,2,0,69,78,68, + 84,69,78,68,84,45,0,0,0,86,65,84,84,24,0,3,0,97,80,111,115,0,0,128,97,85,86,0,1,128,97,67,111,108,111,114,0,2,128,86,65,84,89,5,0,3,0,4,4,6,69,78,68,84,4,0,0,0,69,78,68,84,222,192, + 23,11,0,0,0,0,20,0,0,0,252,13,0,0,255,255,255,255,66,67,192,222,53,20,0,0,3,0,0,0,98,12,48,36,128,16,5,200,20,0,0,0,33,12,0,0,72,3,0,0,11,2,33,0,2,0,0,0,22,0,0,0,7,129,35,145,65,200, + 4,73,6,16,50,57,146,1,132,12,37,5,8,25,30,4,139,98,128,16,69,2,66,146,11,66,132,16,50,20,56,8,24,75,10,50,66,136,72,112,196,33,35,68,18,135,140,16,65,146,2,100,200,8,177,20,32,67,70, + 136,32,201,1,50,66,132,24,42,40,42,144,49,124,176,92,145,32,196,200,0,0,0,137,32,0,0,24,0,0,0,50,34,8,9,32,98,70,0,33,43,36,152,16,33,37,36,152,16,25,39,12,133,164,144,96,66,100,92, + 32,36,100,130,224,153,1,24,70,32,128,97,4,1,184,67,72,32,37,77,17,37,76,62,149,82,210,193,57,141,52,1,205,148,4,145,65,4,66,48,197,136,68,145,13,4,204,17,128,129,10,228,28,1,40,12, + 34,8,194,48,2,145,140,0,0,0,0,0,81,24,0,0,100,0,0,0,27,246,35,248,255,255,255,255,1,48,5,192,15,0,56,0,254,0,144,0,10,232,3,34,28,224,1,30,228,225,29,240,161,13,204,161,30,220,97,28, + 218,192,28,224,161,13,218,33,28,232,1,29,0,122,144,135,122,40,7,128,48,7,121,8,135,118,40,135,54,128,135,119,72,7,119,160,135,114,144,7,32,28,216,129,29,0,162,29,210,193,29,218,128, + 29,202,225,28,194,129,29,218,192,30,202,97,28,232,225,29,228,161,13,238,33,29,200,129,30,208,1,136,3,57,192,3,96,112,135,119,104,3,113,168,135,116,96,7,122,72,7,119,152,7,128,112,135, + 119,104,131,116,112,7,115,152,135,54,48,7,120,104,131,118,8,7,122,64,7,128,30,228,161,30,202,1,32,220,225,29,218,192,29,194,193,29,230,161,13,204,1,30,218,160,29,194,129,30,208,1,160, + 7,121,168,135,114,0,8,119,120,135,54,152,135,116,56,7,119,40,7,114,104,3,125,40,7,121,120,135,121,104,3,115,128,135,54,104,135,112,160,7,116,0,232,65,30,234,161,28,0,194,29,222,161, + 13,232,65,30,194,1,30,224,33,29,220,225,28,218,160,29,194,129,30,208,1,160,7,121,168,135,114,0,136,121,160,135,112,24,135,117,104,3,120,144,135,119,160,135,114,24,7,122,120,7,121,104, + 3,113,168,7,115,48,135,114,144,135,54,152,135,116,208,135,114,0,240,0,32,234,193,29,230,33,28,204,161,28,218,192,28,224,161,13,218,33,28,232,1,29,0,122,144,135,122,40,7,96,195,24,8, + 4,176,0,164,0,84,65,128,4,105,0,13,225,144,14,242,208,6,226,80,15,230,96,14,229,32,15,109,224,14,239,208,6,225,192,14,233,16,14,243,0,0,0,73,24,0,0,1,0,0,0,19,132,64,0,19,170,112,72, + 7,121,176,3,58,104,131,112,128,7,120,96,135,114,104,131,116,120,135,121,136,3,60,112,131,56,112,3,56,216,112,27,229,208,6,240,160,7,118,64,7,122,96,7,116,160,7,118,64,7,109,144,14, + 113,160,7,120,160,7,120,208,6,233,128,7,122,128,7,122,128,7,109,144,14,113,96,7,122,16,7,118,160,7,113,96,7,109,144,14,115,32,7,122,48,7,114,160,7,115,32,7,109,144,14,118,64,7,122, + 96,7,116,160,7,118,64,7,109,96,14,115,32,7,122,48,7,114,160,7,115,32,7,109,96,14,118,64,7,122,96,7,116,160,7,118,64,7,109,96,15,113,96,7,122,16,7,118,160,7,113,96,7,109,96,15,114,64, + 7,122,48,7,114,160,7,115,32,7,109,96,15,115,32,7,122,48,7,114,160,7,115,32,7,109,96,15,116,128,7,122,96,7,116,160,7,118,64,7,109,96,15,118,64,7,122,96,7,116,160,7,118,64,7,109,96,15, + 121,96,7,122,16,7,114,128,7,122,16,7,114,128,7,109,96,15,113,32,7,120,160,7,113,32,7,120,160,7,113,32,7,120,208,6,246,16,7,121,32,7,122,32,7,117,96,7,122,32,7,117,96,7,109,96,15,114, + 80,7,118,160,7,114,80,7,118,160,7,114,80,7,118,208,6,246,80,7,113,32,7,122,80,7,113,32,7,122,80,7,113,32,7,109,96,15,113,0,7,114,64,7,122,16,7,112,32,7,116,160,7,113,0,7,114,64,7,109, + 224,14,120,160,7,113,96,7,122,48,7,114,160,17,194,144,5,3,32,13,61,164,2,10,3,0,128,0,0,0,64,0,0,0,0,0,160,0,36,54,8,20,85,26,0,0,200,2,1,0,11,0,0,0,50,30,152,16,25,17,76,144,140,9, + 38,71,198,4,67,202,34,40,129,66,40,135,242,41,64,129,130,40,144,17,128,50,160,29,1,32,29,75,144,2,0,0,0,0,177,24,0,0,165,0,0,0,51,8,128,28,196,225,28,102,20,1,61,136,67,56,132,195, + 140,66,128,7,121,120,7,115,152,113,12,230,0,15,237,16,14,244,128,14,51,12,66,30,194,193,29,206,161,28,102,48,5,61,136,67,56,132,131,27,204,3,61,200,67,61,140,3,61,204,120,140,116,112, + 7,123,8,7,121,72,135,112,112,7,122,112,3,118,120,135,112,32,135,25,204,17,14,236,144,14,225,48,15,110,48,15,227,240,14,240,80,14,51,16,196,29,222,33,28,216,33,29,194,97,30,102,48,137, + 59,188,131,59,208,67,57,180,3,60,188,131,60,132,3,59,204,240,20,118,96,7,123,104,7,55,104,135,114,104,7,55,128,135,112,144,135,112,96,7,118,40,7,118,248,5,118,120,135,119,128,135,95, + 8,135,113,24,135,114,152,135,121,152,129,44,238,240,14,238,224,14,245,192,14,236,48,3,98,200,161,28,228,161,28,204,161,28,228,161,28,220,97,28,202,33,28,196,129,29,202,97,6,214,144, + 67,57,200,67,57,152,67,57,200,67,57,184,195,56,148,67,56,136,3,59,148,195,47,188,131,60,252,130,59,212,3,59,176,195,12,199,105,135,112,88,135,114,112,131,116,104,7,120,96,135,116,24, + 135,116,160,135,25,206,83,15,238,0,15,242,80,14,228,144,14,227,64,15,225,32,14,236,80,14,51,32,40,29,220,193,30,194,65,30,210,33,28,220,129,30,220,224,28,228,225,29,234,1,30,102,24, + 81,56,176,67,58,156,131,59,204,80,36,118,96,7,123,104,7,55,96,135,119,120,7,120,152,81,76,244,144,15,240,80,14,51,30,106,30,202,97,28,232,33,29,222,193,29,126,1,30,228,161,28,204,33, + 29,240,97,6,84,133,131,56,204,195,59,176,67,61,208,67,57,252,194,60,228,67,59,136,195,59,176,195,140,197,10,135,121,152,135,119,24,135,116,8,7,122,40,7,114,152,129,92,227,16,14,236, + 192,14,229,80,14,243,48,35,193,210,65,30,228,225,23,216,225,29,222,1,30,102,72,25,59,176,131,61,180,131,27,132,195,56,140,67,57,204,195,60,184,193,57,200,195,59,212,3,60,204,72,180, + 113,8,7,118,96,7,113,8,135,113,88,135,25,219,198,14,236,96,15,237,224,6,240,32,15,229,48,15,229,32,15,246,80,14,110,16,14,227,48,14,229,48,15,243,224,6,233,224,14,228,80,14,248,48, + 35,226,236,97,28,194,129,29,216,225,23,236,33,29,230,33,29,196,33,29,216,33,29,232,33,31,102,32,157,59,188,67,61,184,3,57,148,131,57,204,88,188,112,112,7,119,120,7,122,8,7,122,72,135, + 119,112,135,25,206,135,14,229,16,14,240,16,14,236,192,14,239,48,14,243,144,14,244,80,14,51,40,48,8,135,116,144,7,55,48,135,122,112,135,113,160,135,116,120,7,119,248,133,115,144,135, + 119,168,7,120,152,7,0,0,0,0,121,32,0,0,25,1,0,0,114,30,72,32,67,136,12,25,9,114,50,72,32,35,129,140,145,145,209,68,160,16,40,100,60,49,50,66,142,144,33,163,152,6,100,208,82,0,0,0,139, + 210,88,216,6,109,80,28,20,27,71,6,209,18,25,76,178,24,6,179,64,18,49,24,202,131,68,148,161,68,87,35,0,0,0,0,83,68,75,32,86,101,114,115,105,111,110,119,99,104,97,114,95,115,105,122, + 101,102,114,97,109,101,45,112,111,105,110,116,101,114,97,105,114,46,109,97,120,95,100,101,118,105,99,101,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120,95,99,111,110,115,116, + 97,110,116,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120,95,116,104,114,101,97,100,103,114,111,117,112,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120,95,116,101,120, + 116,117,114,101,115,97,105,114,46,109,97,120,95,114,101,97,100,95,119,114,105,116,101,95,116,101,120,116,117,114,101,115,97,105,114,46,109,97,120,95,115,97,109,112,108,101,114,115, + 65,112,112,108,101,32,109,101,116,97,108,32,118,101,114,115,105,111,110,32,51,50,48,50,51,46,51,54,56,32,40,109,101,116,97,108,102,101,45,51,50,48,50,51,46,51,54,56,41,77,101,116,97, + 108,97,105,114,46,99,111,109,112,105,108,101,46,100,101,110,111,114,109,115,95,100,105,115,97,98,108,101,97,105,114,46,99,111,109,112,105,108,101,46,102,97,115,116,95,109,97,116,104, + 95,101,110,97,98,108,101,97,105,114,46,99,111,109,112,105,108,101,46,102,114,97,109,101,98,117,102,102,101,114,95,102,101,116,99,104,95,101,110,97,98,108,101,97,105,114,46,118,101, + 114,116,101,120,95,111,117,116,112,117,116,117,115,101,114,40,108,111,99,110,48,41,97,105,114,46,97,114,103,95,116,121,112,101,95,110,97,109,101,102,108,111,97,116,52,97,105,114,46, + 97,114,103,95,110,97,109,101,79,117,116,95,67,111,108,111,114,117,115,101,114,40,108,111,99,110,49,41,102,108,111,97,116,50,79,117,116,95,85,86,97,105,114,46,112,111,115,105,116,105, + 111,110,103,108,95,80,111,115,105,116,105,111,110,97,105,114,46,118,101,114,116,101,120,95,105,110,112,117,116,97,105,114,46,108,111,99,97,116,105,111,110,95,105,110,100,101,120,97, + 80,111,115,97,85,86,97,67,111,108,111,114,97,105,114,46,98,117,102,102,101,114,97,105,114,46,98,117,102,102,101,114,95,115,105,122,101,97,105,114,46,114,101,97,100,97,105,114,46,97, + 100,100,114,101,115,115,95,115,112,97,99,101,97,105,114,46,115,116,114,117,99,116,95,116,121,112,101,95,105,110,102,111,117,83,99,97,108,101,117,84,114,97,110,115,108,97,116,101,97, + 105,114,46,97,114,103,95,116,121,112,101,95,115,105,122,101,97,105,114,46,97,114,103,95,116,121,112,101,95,97,108,105,103,110,95,115,105,122,101,117,80,117,115,104,67,111,110,115,116, + 97,110,116,112,99,0,0,0,230,117,0,0,0,0,0,0,48,130,144,4,35,8,10,51,130,144,8,35,8,201,48,130,144,16,35,8,73,49,130,144,24,35,8,201,49,130,144,32,35,8,73,50,130,144,40,35,8,7,48,195, + 160,6,193,26,204,48,176,129,208,6,51,12,110,48,168,193,12,131,27,16,111,48,195,224,6,197,27,204,48,184,129,241,6,51,12,110,112,192,193,12,131,27,32,113,48,195,224,6,137,28,204,16,40, + 51,12,106,224,6,115,48,3,177,208,129,26,204,193,12,1,51,67,208,204,16,56,51,24,15,20,73,19,53,131,241,84,145,53,93,51,20,88,36,77,217,12,67,41,152,194,41,204,144,204,129,182,205,1, + 27,68,214,196,205,144,176,129,182,177,1,27,68,214,212,205,144,168,129,182,169,1,27,68,210,228,205,160,204,65,28,204,129,69,6,113,16,7,115,96,149,193,12,20,29,124,96,32,7,219,28,176, + 65,24,136,129,26,140,1,43,152,129,28,156,65,28,68,104,48,165,193,12,68,42,168,194,42,180,194,12,67,29,160,130,43,156,25,0,28,199,113,28,199,113,28,199,113,110,224,6,110,224,6,110,224, + 6,110,224,6,110,96,209,129,30,88,150,69,7,116,224,6,116,128,11,184,128,11,252,128,30,160,32,35,129,9,202,136,141,205,174,205,165,237,141,172,142,173,204,197,140,45,236,108,110,148, + 164,14,236,224,14,240,32,15,244,96,15,248,160,15,82,97,99,179,107,115,73,35,43,115,163,27,37,240,131,92,194,210,228,92,236,202,228,230,210,222,220,70,9,254,32,169,176,52,57,23,182, + 48,183,179,186,176,179,178,47,187,50,185,185,180,55,183,81,2,80,200,41,44,77,206,101,236,173,13,46,141,173,236,235,13,142,46,237,205,109,110,148,33,20,68,97,20,82,9,75,147,115,177, + 43,147,163,43,195,27,37,112,5,0,0,0,169,24,0,0,37,0,0,0,11,10,114,40,135,119,128,7,122,88,112,152,67,61,184,195,56,176,67,57,208,195,130,230,28,198,161,13,232,65,30,194,193,29,230, + 33,29,232,33,29,222,193,29,22,52,227,96,14,231,80,15,225,32,15,228,64,15,225,32,15,231,80,14,244,176,128,129,7,121,40,135,112,96,7,118,120,135,113,8,7,122,40,7,114,88,112,156,195,56, + 180,1,59,164,131,61,148,195,2,107,28,216,33,28,220,225,28,220,32,28,228,97,28,220,32,28,232,129,30,194,97,28,208,161,28,200,97,28,194,129,29,216,97,193,1,15,244,32,15,225,80,15,244, + 128,14,0,0,0,0,209,16,0,0,6,0,0,0,7,204,60,164,131,59,156,3,59,148,3,61,160,131,60,148,67,56,144,195,1,0,0,0,97,32,0,0,68,0,0,0,19,4,65,44,16,0,0,0,11,0,0,0,148,51,0,197,64,91,2,212, + 115,16,73,20,69,115,16,73,36,17,4,163,1,35,0,99,4,32,8,130,248,71,49,7,97,89,23,70,50,26,64,51,3,0,0,0,241,48,0,0,32,0,0,0,34,71,200,144,81,18,196,43,0,0,0,0,207,115,89,0,111,109,110, + 105,112,111,116,101,110,116,32,99,104,97,114,83,105,109,112,108,101,32,67,43,43,32,84,66,65,65,97,105,114,45,97,108,105,97,115,45,115,99,111,112,101,115,40,109,97,105,110,48,41,97, + 105,114,45,97,108,105,97,115,45,115,99,111,112,101,45,97,114,103,40,51,41,0,19,132,101,89,33,212,130,44,172,24,108,161,22,102,97,67,16,11,27,6,88,184,5,90,216,48,224,2,46,208,194,134, + 192,22,0,0,157,6,38,154,40,16,130,65,36,254,157,134,135,234,40,16,130,67,0,254,131,12,1,226,12,50,4,138,51,134,48,68,22,128,255,28,195,16,76,179,13,12,6,204,54,4,90,48,219,16,12,194, + 6,1,49,0,4,0,0,0,91,138,32,192,133,35,23,182,20,68,128,11,71,46,0,0,0,0,0,0,113,32,0,0,3,0,0,0,50,14,16,34,132,0,132,6,0,0,0,0,0,0,0,0,101,12,0,0,31,0,0,0,18,3,148,240,0,0,0,0,3,0, + 0,0,5,0,0,0,9,0,0,0,76,0,0,0,1,0,0,0,88,0,0,0,0,0,0,0,88,0,0,0,1,0,0,0,112,0,0,0,0,0,0,0,14,0,0,0,21,0,0,0,0,0,0,0,5,0,0,0,5,0,0,0,0,0,0,0,112,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, + 0,0,0,0,5,0,0,0,0,0,0,0,5,0,0,0,255,255,255,255,0,36,0,0,0,0,0,0,93,12,0,0,12,0,0,0,18,3,148,99,0,0,0,0,109,97,105,110,48,51,50,48,50,51,46,51,54,56,97,105,114,54,52,45,97,112,112, + 108,101,45,105,111,115,49,56,46,49,46,48,0,0,0,0,0, +}; +const uint8_t metallib_fragment[3771] = { + 77,84,76,66,1,0,2,0,7,0,0,130,18,0,1,0,187,14,0,0,0,0,0,0,88,0,0,0,0,0,0,0,123,0,0,0,0,0,0,0,219,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,227,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,235,0,0,0,0,0,0,0,208, + 13,0,0,0,0,0,0,1,0,0,0,123,0,0,0,78,65,77,69,6,0,109,97,105,110,48,0,84,89,80,69,1,0,1,72,65,83,72,32,0,167,26,51,31,140,153,203,226,66,149,243,47,185,58,96,202,28,176,71,121,86,159, + 244,234,235,69,155,58,121,67,241,212,77,68,83,90,8,0,208,13,0,0,0,0,0,0,79,70,70,84,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,69,82,83,8,0,2,0,7,0,3,0,2,0,69,78,68,84, + 69,78,68,84,4,0,0,0,69,78,68,84,4,0,0,0,69,78,68,84,222,192,23,11,0,0,0,0,20,0,0,0,180,13,0,0,255,255,255,255,66,67,192,222,53,20,0,0,3,0,0,0,98,12,48,36,128,16,5,200,20,0,0,0,33,12, + 0,0,41,3,0,0,11,2,33,0,2,0,0,0,22,0,0,0,7,129,35,145,65,200,4,73,6,16,50,57,146,1,132,12,37,5,8,25,30,4,139,98,128,20,69,2,66,146,11,66,164,16,50,20,56,8,24,75,10,50,82,136,72,112, + 196,33,35,68,18,135,140,16,65,146,2,100,200,8,177,20,32,67,70,136,32,201,1,50,82,132,24,42,40,42,144,49,124,176,92,145,32,197,200,0,0,0,137,32,0,0,31,0,0,0,50,34,72,9,32,98,70,0,33, + 43,36,152,20,33,37,36,152,20,25,39,12,133,164,144,96,82,100,92,32,36,101,130,128,154,1,24,70,32,128,27,132,97,4,1,64,74,154,34,74,152,252,127,34,174,137,138,136,223,30,254,105,140, + 0,24,68,32,2,140,164,41,162,132,201,255,37,128,121,22,34,250,167,49,2,96,16,193,16,76,33,194,40,135,208,28,1,114,132,160,230,8,130,57,2,48,24,70,16,26,163,172,114,6,115,12,128,70,111, + 32,64,5,218,8,0,0,81,24,0,0,105,0,0,0,27,246,35,248,255,255,255,255,1,104,3,96,13,0,83,0,252,0,144,128,10,232,3,34,28,224,1,30,228,225,29,240,161,13,204,161,30,220,97,28,218,192,28, + 224,161,13,218,33,28,232,1,29,0,122,144,135,122,40,7,128,48,7,121,8,135,118,40,135,54,128,135,119,72,7,119,160,135,114,144,7,32,28,216,129,29,0,162,29,210,193,29,218,128,29,202,225, + 28,194,129,29,218,192,30,202,97,28,232,225,29,228,161,13,238,33,29,200,129,30,208,1,136,3,57,192,3,96,112,135,119,104,3,113,168,135,116,96,7,122,72,7,119,152,7,128,112,135,119,104, + 131,116,112,7,115,152,135,54,48,7,120,104,131,118,8,7,122,64,7,128,30,228,161,30,202,1,32,220,225,29,218,192,29,194,193,29,230,161,13,204,1,30,218,160,29,194,129,30,208,1,160,7,121, + 168,135,114,0,8,119,120,135,54,152,135,116,56,7,119,40,7,114,104,3,125,40,7,121,120,135,121,104,3,115,128,135,54,104,135,112,160,7,116,0,232,65,30,234,161,28,0,194,29,222,161,13,232, + 65,30,194,1,30,224,33,29,220,225,28,218,160,29,194,129,30,208,1,160,7,121,168,135,114,0,136,121,160,135,112,24,135,117,104,3,120,144,135,119,160,135,114,24,7,122,120,7,121,104,3,113, + 168,7,115,48,135,114,144,135,54,152,135,116,208,135,114,0,240,0,32,234,193,29,230,33,28,204,161,28,218,192,28,224,161,13,218,33,28,232,1,29,0,122,144,135,122,40,7,96,131,33,12,192, + 2,84,27,140,129,0,22,160,218,0,17,255,255,255,255,63,0,109,0,172,1,96,10,128,31,0,18,80,1,125,176,193,40,2,96,1,170,13,134,33,0,11,80,109,96,142,255,255,255,255,31,128,54,0,214,0,144, + 128,10,232,3,0,73,24,0,0,4,0,0,0,19,134,64,24,38,12,68,97,76,24,142,194,0,0,0,0,19,170,112,72,7,121,176,3,58,104,131,112,128,7,120,96,135,114,104,131,116,120,135,121,136,3,60,112,131, + 56,112,3,56,216,112,27,229,208,6,240,160,7,118,64,7,122,96,7,116,160,7,118,64,7,109,144,14,113,160,7,120,160,7,120,208,6,233,128,7,122,128,7,122,128,7,109,144,14,113,96,7,122,16,7, + 118,160,7,113,96,7,109,144,14,115,32,7,122,48,7,114,160,7,115,32,7,109,144,14,118,64,7,122,96,7,116,160,7,118,64,7,109,96,14,115,32,7,122,48,7,114,160,7,115,32,7,109,96,14,118,64,7, + 122,96,7,116,160,7,118,64,7,109,96,15,113,96,7,122,16,7,118,160,7,113,96,7,109,96,15,114,64,7,122,48,7,114,160,7,115,32,7,109,96,15,115,32,7,122,48,7,114,160,7,115,32,7,109,96,15,116, + 128,7,122,96,7,116,160,7,118,64,7,109,96,15,118,64,7,122,96,7,116,160,7,118,64,7,109,96,15,121,96,7,122,16,7,114,128,7,122,16,7,114,128,7,109,96,15,113,32,7,120,160,7,113,32,7,120, + 160,7,113,32,7,120,208,6,246,16,7,121,32,7,122,32,7,117,96,7,122,32,7,117,96,7,109,96,15,114,80,7,118,160,7,114,80,7,118,160,7,114,80,7,118,208,6,246,80,7,113,32,7,122,80,7,113,32, + 7,122,80,7,113,32,7,109,96,15,113,0,7,114,64,7,122,16,7,112,32,7,116,160,7,113,0,7,114,64,7,109,224,14,120,160,7,113,96,7,122,48,7,114,160,17,194,144,5,3,32,13,61,164,2,10,4,0,128, + 0,0,0,64,0,0,0,0,0,160,0,134,84,197,246,0,1,32,0,0,0,8,0,0,0,0,0,20,128,196,6,129,162,43,3,0,0,89,32,10,0,0,0,50,30,152,16,25,17,76,144,140,9,38,71,198,4,67,106,69,80,2,133,80,14,229, + 83,128,2,5,81,32,35,0,101,64,114,44,65,10,0,0,0,177,24,0,0,165,0,0,0,51,8,128,28,196,225,28,102,20,1,61,136,67,56,132,195,140,66,128,7,121,120,7,115,152,113,12,230,0,15,237,16,14,244, + 128,14,51,12,66,30,194,193,29,206,161,28,102,48,5,61,136,67,56,132,131,27,204,3,61,200,67,61,140,3,61,204,120,140,116,112,7,123,8,7,121,72,135,112,112,7,122,112,3,118,120,135,112,32, + 135,25,204,17,14,236,144,14,225,48,15,110,48,15,227,240,14,240,80,14,51,16,196,29,222,33,28,216,33,29,194,97,30,102,48,137,59,188,131,59,208,67,57,180,3,60,188,131,60,132,3,59,204, + 240,20,118,96,7,123,104,7,55,104,135,114,104,7,55,128,135,112,144,135,112,96,7,118,40,7,118,248,5,118,120,135,119,128,135,95,8,135,113,24,135,114,152,135,121,152,129,44,238,240,14, + 238,224,14,245,192,14,236,48,3,98,200,161,28,228,161,28,204,161,28,228,161,28,220,97,28,202,33,28,196,129,29,202,97,6,214,144,67,57,200,67,57,152,67,57,200,67,57,184,195,56,148,67, + 56,136,3,59,148,195,47,188,131,60,252,130,59,212,3,59,176,195,12,199,105,135,112,88,135,114,112,131,116,104,7,120,96,135,116,24,135,116,160,135,25,206,83,15,238,0,15,242,80,14,228, + 144,14,227,64,15,225,32,14,236,80,14,51,32,40,29,220,193,30,194,65,30,210,33,28,220,129,30,220,224,28,228,225,29,234,1,30,102,24,81,56,176,67,58,156,131,59,204,80,36,118,96,7,123,104, + 7,55,96,135,119,120,7,120,152,81,76,244,144,15,240,80,14,51,30,106,30,202,97,28,232,33,29,222,193,29,126,1,30,228,161,28,204,33,29,240,97,6,84,133,131,56,204,195,59,176,67,61,208,67, + 57,252,194,60,228,67,59,136,195,59,176,195,140,197,10,135,121,152,135,119,24,135,116,8,7,122,40,7,114,152,129,92,227,16,14,236,192,14,229,80,14,243,48,35,193,210,65,30,228,225,23,216, + 225,29,222,1,30,102,72,25,59,176,131,61,180,131,27,132,195,56,140,67,57,204,195,60,184,193,57,200,195,59,212,3,60,204,72,180,113,8,7,118,96,7,113,8,135,113,88,135,25,219,198,14,236, + 96,15,237,224,6,240,32,15,229,48,15,229,32,15,246,80,14,110,16,14,227,48,14,229,48,15,243,224,6,233,224,14,228,80,14,248,48,35,226,236,97,28,194,129,29,216,225,23,236,33,29,230,33, + 29,196,33,29,216,33,29,232,33,31,102,32,157,59,188,67,61,184,3,57,148,131,57,204,88,188,112,112,7,119,120,7,122,8,7,122,72,135,119,112,135,25,206,135,14,229,16,14,240,16,14,236,192, + 14,239,48,14,243,144,14,244,80,14,51,40,48,8,135,116,144,7,55,48,135,122,112,135,113,160,135,116,120,7,119,248,133,115,144,135,119,168,7,120,152,7,0,0,0,0,121,32,0,0,251,0,0,0,114, + 30,72,32,67,136,12,25,9,114,50,72,32,35,129,140,145,145,209,68,160,16,40,100,60,49,50,66,142,144,33,163,56,6,220,41,1,0,0,0,139,210,88,216,6,109,80,28,20,27,71,6,81,100,48,134,180, + 40,15,178,24,197,34,41,24,178,28,13,83,68,75,32,86,101,114,115,105,111,110,119,99,104,97,114,95,115,105,122,101,102,114,97,109,101,45,112,111,105,110,116,101,114,97,105,114,46,109, + 97,120,95,100,101,118,105,99,101,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120,95,99,111,110,115,116,97,110,116,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120,95,116, + 104,114,101,97,100,103,114,111,117,112,95,98,117,102,102,101,114,115,97,105,114,46,109,97,120,95,116,101,120,116,117,114,101,115,97,105,114,46,109,97,120,95,114,101,97,100,95,119,114, + 105,116,101,95,116,101,120,116,117,114,101,115,97,105,114,46,109,97,120,95,115,97,109,112,108,101,114,115,65,112,112,108,101,32,109,101,116,97,108,32,118,101,114,115,105,111,110,32, + 51,50,48,50,51,46,51,54,56,32,40,109,101,116,97,108,102,101,45,51,50,48,50,51,46,51,54,56,41,77,101,116,97,108,97,105,114,46,99,111,109,112,105,108,101,46,100,101,110,111,114,109,115, + 95,100,105,115,97,98,108,101,97,105,114,46,99,111,109,112,105,108,101,46,102,97,115,116,95,109,97,116,104,95,101,110,97,98,108,101,97,105,114,46,99,111,109,112,105,108,101,46,102,114, + 97,109,101,98,117,102,102,101,114,95,102,101,116,99,104,95,101,110,97,98,108,101,97,105,114,46,114,101,110,100,101,114,95,116,97,114,103,101,116,97,105,114,46,97,114,103,95,116,121, + 112,101,95,110,97,109,101,102,108,111,97,116,52,97,105,114,46,97,114,103,95,110,97,109,101,102,67,111,108,111,114,97,105,114,46,102,114,97,103,109,101,110,116,95,105,110,112,117,116, + 117,115,101,114,40,108,111,99,110,48,41,97,105,114,46,99,101,110,116,101,114,97,105,114,46,112,101,114,115,112,101,99,116,105,118,101,73,110,95,67,111,108,111,114,117,115,101,114,40, + 108,111,99,110,49,41,102,108,111,97,116,50,73,110,95,85,86,97,105,114,46,116,101,120,116,117,114,101,97,105,114,46,108,111,99,97,116,105,111,110,95,105,110,100,101,120,97,105,114,46, + 115,97,109,112,108,101,116,101,120,116,117,114,101,50,100,60,102,108,111,97,116,44,32,115,97,109,112,108,101,62,115,84,101,120,116,117,114,101,97,105,114,46,115,97,109,112,108,101, + 114,115,97,109,112,108,101,114,115,84,101,120,116,117,114,101,83,109,112,108,114,0,6,95,0,0,0,0,0,0,48,130,208,8,35,8,18,51,130,208,12,35,8,13,49,130,208,20,35,8,141,49,130,208,28, + 35,8,13,50,130,208,36,35,8,141,50,130,208,44,35,8,9,48,195,64,6,65,25,204,48,152,129,112,6,51,12,104,48,144,193,12,3,26,16,105,48,195,128,6,69,26,204,48,160,129,145,6,51,12,104,112, + 168,193,12,3,26,32,107,48,195,128,6,9,27,204,16,40,51,12,100,128,6,109,48,3,177,184,1,25,180,193,12,1,51,67,208,204,16,56,51,28,79,27,180,1,20,73,211,12,193,31,204,144,180,1,85,89, + 23,20,73,216,12,137,25,80,153,117,65,154,180,205,160,144,1,215,181,129,25,120,208,39,129,193,12,137,27,132,65,215,6,102,0,137,129,52,6,51,16,161,32,10,163,64,10,51,12,111,0,10,165, + 112,99,0,112,28,199,113,28,199,113,28,199,185,129,27,184,129,27,184,129,27,184,129,27,184,129,69,7,122,96,89,150,41,112,172,192,10,228,160,14,160,32,35,129,9,202,136,141,205,174,205, + 165,237,141,172,142,173,204,197,140,45,236,108,110,148,228,13,224,32,14,228,96,14,232,160,14,236,224,14,82,97,99,179,107,115,73,35,43,115,163,27,37,192,131,92,194,210,228,92,236,202, + 228,230,210,222,220,70,9,242,32,169,176,52,57,23,182,48,183,179,186,176,179,178,47,187,50,185,185,180,55,183,81,2,61,200,41,44,77,206,101,236,173,13,46,141,173,236,235,13,142,46,237, + 205,109,110,148,97,15,248,160,15,146,9,75,147,115,49,147,11,59,107,43,115,163,27,37,40,5,0,0,0,0,169,24,0,0,37,0,0,0,11,10,114,40,135,119,128,7,122,88,112,152,67,61,184,195,56,176, + 67,57,208,195,130,230,28,198,161,13,232,65,30,194,193,29,230,33,29,232,33,29,222,193,29,22,52,227,96,14,231,80,15,225,32,15,228,64,15,225,32,15,231,80,14,244,176,128,129,7,121,40,135, + 112,96,7,118,120,135,113,8,7,122,40,7,114,88,112,156,195,56,180,1,59,164,131,61,148,195,2,107,28,216,33,28,220,225,28,220,32,28,228,97,28,220,32,28,232,129,30,194,97,28,208,161,28, + 200,97,28,194,129,29,216,97,193,1,15,244,32,15,225,80,15,244,128,14,0,0,0,0,209,16,0,0,6,0,0,0,7,204,60,164,131,59,156,3,59,148,3,61,160,131,60,148,67,56,144,195,1,0,0,0,97,32,0,0, + 49,0,0,0,19,4,65,44,16,0,0,0,4,0,0,0,196,106,96,4,128,220,8,0,129,17,0,18,51,0,0,0,241,48,0,0,28,0,0,0,34,71,200,144,81,14,196,42,0,0,0,0,23,134,1,0,97,105,114,45,97,108,105,97,115, + 45,115,99,111,112,101,115,40,109,97,105,110,48,41,97,105,114,45,97,108,105,97,115,45,115,99,111,112,101,45,115,97,109,112,108,101,114,115,97,105,114,45,97,108,105,97,115,45,115,99, + 111,112,101,45,116,101,120,116,117,114,101,115,0,43,4,85,56,133,21,195,42,168,2,42,172,24,88,65,21,82,97,131,192,10,171,0,0,35,6,205,16,130,96,240,84,135,129,20,3,33,8,204,104,66,0, + 96,176,136,255,108,3,17,0,27,4,196,0,0,0,2,0,0,0,91,6,224,96,5,0,0,0,0,0,0,0,113,32,0,0,3,0,0,0,50,14,16,34,132,0,248,5,0,0,0,0,0,0,0,0,101,12,0,0,37,0,0,0,18,3,148,40,1,0,0,0,3,0, + 0,0,32,0,0,0,9,0,0,0,76,0,0,0,1,0,0,0,88,0,0,0,0,0,0,0,88,0,0,0,2,0,0,0,136,0,0,0,0,0,0,0,41,0,0,0,21,0,0,0,0,0,0,0,5,0,0,0,5,0,0,0,0,0,0,0,136,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0, + 0,0,0,0,0,5,0,0,0,0,0,0,0,5,0,0,0,255,255,255,255,0,36,0,0,5,0,0,0,27,0,0,0,5,0,0,0,27,0,0,0,255,255,255,255,8,36,0,0,0,0,0,0,93,12,0,0,19,0,0,0,18,3,148,126,0,0,0,0,109,97,105,110, + 48,97,105,114,46,115,97,109,112,108,101,95,116,101,120,116,117,114,101,95,50,100,46,118,52,102,51,50,51,50,48,50,51,46,51,54,56,97,105,114,54,52,45,97,112,112,108,101,45,105,111,115, + 49,56,46,49,46,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; +#elif TARGET_IPHONE_SIMULATOR +#error "SDL_GPU does not support the iphone simulator" +#endif +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp index 27c952269..dc86f6c26 100644 --- a/backends/imgui_impl_sdlrenderer2.cpp +++ b/backends/imgui_impl_sdlrenderer2.cpp @@ -1,15 +1,18 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL2 // (Requires: SDL 2.0.17+) -// Note how SDL_Renderer is an _optional_ component of SDL2. -// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. -// If your application will want to render any non trivial amount of graphics other than UI, -// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and -// it might be difficult to step out of those boundaries. +// Note that SDL_Renderer is an _optional_ component of SDL2, which IMHO is now largely obsolete. +// For a multi-platform app consider using other technologies: +// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API. You will need to update to SDL3. +// - SDL2+DirectX, SDL2+OpenGL, SDL2+Vulkan: combine SDL with dedicated renderers. +// If your application wants to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user +// and it might be difficult to step out of those boundaries. // Implemented features: -// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -21,6 +24,10 @@ // - Introduction, links and more at the top of imgui.cpp // CHANGELOG +// 2026-03-12: Fixed invalid assert in ImGui_ImplSDLRenderer2_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer2_CreateFontsTexture() and ImGui_ImplSDLRenderer2_DestroyFontsTexture(). +// 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color. // 2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer2_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter. // 2023-05-30: Renamed imgui_impl_sdlrenderer.h/.cpp to imgui_impl_sdlrenderer2.h/.cpp to accommodate for upcoming SDL3. @@ -39,6 +46,8 @@ #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #endif // SDL @@ -51,7 +60,7 @@ struct ImGui_ImplSDLRenderer2_Data { SDL_Renderer* Renderer; // Main viewport's renderer - SDL_Texture* FontTexture; + ImGui_ImplSDLRenderer2_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -75,6 +84,7 @@ bool ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_sdlrenderer2"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. bd->Renderer = renderer; @@ -86,49 +96,56 @@ void ImGui_ImplSDLRenderer2_Shutdown() ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplSDLRenderer2_DestroyDeviceObjects(); io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } static void ImGui_ImplSDLRenderer2_SetupRenderState(SDL_Renderer* renderer) { - // Clear out any viewports and cliprect set by the user + // Clear out any viewports and cliprect set by the user // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. - SDL_RenderSetViewport(renderer, nullptr); - SDL_RenderSetClipRect(renderer, nullptr); + SDL_RenderSetViewport(renderer, nullptr); + SDL_RenderSetClipRect(renderer, nullptr); } void ImGui_ImplSDLRenderer2_NewFrame() { ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLRenderer2_Init()?"); - - if (!bd->FontTexture) - ImGui_ImplSDLRenderer2_CreateDeviceObjects(); + IM_UNUSED(bd); } void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer) { - // If there's a scale factor set by the user, use that instead + // If there's a scale factor set by the user, use that instead // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. float rsx = 1.0f; - float rsy = 1.0f; - SDL_RenderGetScale(renderer, &rsx, &rsy); + float rsy = 1.0f; + SDL_RenderGetScale(renderer, &rsx, &rsy); ImVec2 render_scale; - render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; - render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; + render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; + render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x); - int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y); - if (fb_width == 0 || fb_height == 0) - return; + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x); + int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y); + if (fb_width == 0 || fb_height == 0) + return; + + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplSDLRenderer2_UpdateTexture(tex); // Backup SDL_Renderer state that will be modified to restore it afterwards struct BackupSDLRendererState @@ -151,14 +168,13 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* render_state.Renderer = renderer; platform_io.Renderer_RenderState = &render_state; - // Will project scissor/clipping rectangles into framebuffer space - ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports - ImVec2 clip_scale = render_scale; + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = render_scale; // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data; @@ -198,7 +214,7 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* #endif // Bind texture, Draw - SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); + SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); SDL_RenderGeometryRaw(renderer, tex, xy, (int)sizeof(ImDrawVert), color, (int)sizeof(ImDrawVert), @@ -208,62 +224,73 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* } } } - platform_io.Renderer_RenderState = NULL; + platform_io.Renderer_RenderState = nullptr; // Restore modified SDL_Renderer state SDL_RenderSetViewport(renderer, &old.Viewport); SDL_RenderSetClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr); } -// Called by Init/NewFrame/Shutdown -bool ImGui_ImplSDLRenderer2_CreateFontsTexture() +void ImGui_ImplSDLRenderer2_UpdateTexture(ImTextureData* tex) { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_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) - bd->FontTexture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); - if (bd->FontTexture == nullptr) + if (tex->Status == ImTextureStatus_WantCreate) { - SDL_Log("error creating texture"); - return false; + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + + // Create texture + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + SDL_Texture* sdl_texture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, tex->Width, tex->Height); + IM_ASSERT(sdl_texture != nullptr && "Backend failed to create texture!"); + SDL_UpdateTexture(sdl_texture, nullptr, tex->GetPixels(), tex->GetPitch()); + SDL_SetTextureBlendMode(sdl_texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureScaleMode(sdl_texture, SDL_ScaleModeLinear); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)sdl_texture); + tex->SetStatus(ImTextureStatus_OK); } - SDL_UpdateTexture(bd->FontTexture, nullptr, pixels, 4 * width); - SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND); - SDL_SetTextureScaleMode(bd->FontTexture, SDL_ScaleModeLinear); - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); - - return true; -} - -void ImGui_ImplSDLRenderer2_DestroyFontsTexture() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData(); - if (bd->FontTexture) + else if (tex->Status == ImTextureStatus_WantUpdates) { - io.Fonts->SetTexID(0); - SDL_DestroyTexture(bd->FontTexture); - bd->FontTexture = nullptr; + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID; + for (ImTextureRect& r : tex->Updates) + { + SDL_Rect sdl_r = { r.x, r.y, r.w, r.h }; + SDL_UpdateTexture(sdl_texture, &sdl_r, tex->GetPixelsAt(r.x, r.y), tex->GetPitch()); + } + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantDestroy) + { + if (tex->TexID != ImTextureID_Invalid) + if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID) + SDL_DestroyTexture(sdl_texture); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); } } -bool ImGui_ImplSDLRenderer2_CreateDeviceObjects() +void ImGui_ImplSDLRenderer2_CreateDeviceObjects() { - return ImGui_ImplSDLRenderer2_CreateFontsTexture(); } void ImGui_ImplSDLRenderer2_DestroyDeviceObjects() { - ImGui_ImplSDLRenderer2_DestroyFontsTexture(); + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + { + tex->SetStatus(ImTextureStatus_WantDestroy); + ImGui_ImplSDLRenderer2_UpdateTexture(tex); + } } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_sdlrenderer2.h b/backends/imgui_impl_sdlrenderer2.h index 804ca183d..860e95507 100644 --- a/backends/imgui_impl_sdlrenderer2.h +++ b/backends/imgui_impl_sdlrenderer2.h @@ -1,15 +1,18 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL2 // (Requires: SDL 2.0.17+) -// Note how SDL_Renderer is an _optional_ component of SDL2. -// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. -// If your application will want to render any non trivial amount of graphics other than UI, -// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and -// it might be difficult to step out of those boundaries. +// Note that SDL_Renderer is an _optional_ component of SDL2, which IMHO is now largely obsolete. +// For a multi-platform app consider using other technologies: +// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API. You will need to update to SDL3. +// - SDL2+DirectX, SDL2+OpenGL, SDL2+Vulkan: combine SDL with dedicated renderers. +// If your application wants to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user +// and it might be difficult to step out of those boundaries. // Implemented features: -// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -33,11 +36,12 @@ IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_NewFrame(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer); // Called by Init/NewFrame/Shutdown -IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_DestroyFontsTexture(); -IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_DestroyDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_UpdateTexture(ImTextureData* tex); + // [BETA] Selected render state data shared with callbacks. // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplSDLRenderer2_RenderDrawData() call. // (Please open an issue if you feel you need access to more data) diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index 5dabea199..be6c97d8c 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -1,17 +1,18 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL3 -// (Requires: SDL 3.0.0+) +// (Requires: SDL 3.1.8+) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) - -// Note how SDL_Renderer is an _optional_ component of SDL3. -// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. -// If your application will want to render any non trivial amount of graphics other than UI, -// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and -// it might be difficult to step out of those boundaries. +// Note that SDL_Renderer is an _optional_ component of SDL3, which IMHO is now largely obsolete. +// For a multi-platform app consider using other technologies: +// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API. +// - SDL3+DirectX, SDL3+OpenGL, SDL3+Vulkan: combine SDL with dedicated renderers. +// If your application wants to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user +// and it might be difficult to step out of those boundaries. // Implemented features: -// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -23,6 +24,10 @@ // - Introduction, links and more at the top of imgui.cpp // CHANGELOG +// 2026-03-12: Fixed invalid assert in ImGui_ImplSDLRenderer3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer3_CreateFontsTexture() and ImGui_ImplSDLRenderer3_DestroyFontsTexture(). +// 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color. // 2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer3_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009). // 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter. @@ -38,6 +43,8 @@ #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #endif // SDL @@ -50,7 +57,6 @@ struct ImGui_ImplSDLRenderer3_Data { SDL_Renderer* Renderer; // Main viewport's renderer - SDL_Texture* FontTexture; ImVector ColorBuffer; ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); } @@ -76,6 +82,7 @@ bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_sdlrenderer3"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. bd->Renderer = renderer; @@ -87,30 +94,30 @@ void ImGui_ImplSDLRenderer3_Shutdown() ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplSDLRenderer3_DestroyDeviceObjects(); io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer) { - // Clear out any viewports and cliprect set by the user + // Clear out any viewports and cliprect set by the user // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. - SDL_SetRenderViewport(renderer, nullptr); - SDL_SetRenderClipRect(renderer, nullptr); + SDL_SetRenderViewport(renderer, nullptr); + SDL_SetRenderClipRect(renderer, nullptr); } void ImGui_ImplSDLRenderer3_NewFrame() { ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLRenderer3_Init()?"); - - if (!bd->FontTexture) - ImGui_ImplSDLRenderer3_CreateDeviceObjects(); + IM_UNUSED(bd); } // https://github.com/libsdl-org/SDL/issues/9009 @@ -135,21 +142,28 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* { ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); - // If there's a scale factor set by the user, use that instead + // If there's a scale factor set by the user, use that instead // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. float rsx = 1.0f; - float rsy = 1.0f; - SDL_GetRenderScale(renderer, &rsx, &rsy); + float rsy = 1.0f; + SDL_GetRenderScale(renderer, &rsx, &rsy); ImVec2 render_scale; - render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; - render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; + render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; + render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x); - int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y); - if (fb_width == 0 || fb_height == 0) - return; + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x); + int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y); + if (fb_width == 0 || fb_height == 0) + return; + + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplSDLRenderer3_UpdateTexture(tex); // Backup SDL_Renderer state that will be modified to restore it afterwards struct BackupSDLRendererState @@ -174,14 +188,13 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* render_state.Renderer = renderer; platform_io.Renderer_RenderState = &render_state; - // Will project scissor/clipping rectangles into framebuffer space - ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports - ImVec2 clip_scale = render_scale; + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = render_scale; // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data; @@ -217,7 +230,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+ // Bind texture, Draw - SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); + SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex, xy, (int)sizeof(ImDrawVert), color, (int)sizeof(ImDrawVert), @@ -227,62 +240,73 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* } } } - platform_io.Renderer_RenderState = NULL; + platform_io.Renderer_RenderState = nullptr; // Restore modified SDL_Renderer state SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr); SDL_SetRenderClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr); } -// Called by Init/NewFrame/Shutdown -bool ImGui_ImplSDLRenderer3_CreateFontsTexture() +void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_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) - bd->FontTexture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); - if (bd->FontTexture == nullptr) + if (tex->Status == ImTextureStatus_WantCreate) { - SDL_Log("error creating texture"); - return false; + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + + // Create texture + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + SDL_Texture* sdl_texture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, tex->Width, tex->Height); + IM_ASSERT(sdl_texture != nullptr && "Backend failed to create texture!"); + SDL_UpdateTexture(sdl_texture, nullptr, tex->GetPixels(), tex->GetPitch()); + SDL_SetTextureBlendMode(sdl_texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_LINEAR); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)sdl_texture); + tex->SetStatus(ImTextureStatus_OK); } - SDL_UpdateTexture(bd->FontTexture, nullptr, pixels, 4 * width); - SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND); - SDL_SetTextureScaleMode(bd->FontTexture, SDL_SCALEMODE_LINEAR); - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); - - return true; -} - -void ImGui_ImplSDLRenderer3_DestroyFontsTexture() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); - if (bd->FontTexture) + else if (tex->Status == ImTextureStatus_WantUpdates) { - io.Fonts->SetTexID(0); - SDL_DestroyTexture(bd->FontTexture); - bd->FontTexture = nullptr; + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID; + for (ImTextureRect& r : tex->Updates) + { + SDL_Rect sdl_r = { r.x, r.y, r.w, r.h }; + SDL_UpdateTexture(sdl_texture, &sdl_r, tex->GetPixelsAt(r.x, r.y), tex->GetPitch()); + } + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantDestroy) + { + if (tex->TexID != ImTextureID_Invalid) + if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID) + SDL_DestroyTexture(sdl_texture); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); } } -bool ImGui_ImplSDLRenderer3_CreateDeviceObjects() +void ImGui_ImplSDLRenderer3_CreateDeviceObjects() { - return ImGui_ImplSDLRenderer3_CreateFontsTexture(); } void ImGui_ImplSDLRenderer3_DestroyDeviceObjects() { - ImGui_ImplSDLRenderer3_DestroyFontsTexture(); + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + { + tex->SetStatus(ImTextureStatus_WantDestroy); + ImGui_ImplSDLRenderer3_UpdateTexture(tex); + } } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index 7d4c609e8..f5f53b9d2 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -1,17 +1,18 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL3 -// (Requires: SDL 3.0.0+) +// (Requires: SDL 3.1.8+) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) - -// Note how SDL_Renderer is an _optional_ component of SDL3. -// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. -// If your application will want to render any non trivial amount of graphics other than UI, -// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and -// it might be difficult to step out of those boundaries. +// Note that SDL_Renderer is an _optional_ component of SDL3, which IMHO is now largely obsolete. +// For a multi-platform app consider using other technologies: +// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API. +// - SDL3+DirectX, SDL3+OpenGL, SDL3+Vulkan: combine SDL with dedicated renderers. +// If your application wants to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user +// and it might be difficult to step out of those boundaries. // Implemented features: -// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -35,11 +36,12 @@ IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_NewFrame(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer); // Called by Init/NewFrame/Shutdown -IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyFontsTexture(); -IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex); + // [BETA] Selected render state data shared with callbacks. // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplSDLRenderer3_RenderDrawData() call. // (Please open an issue if you feel you need access to more data) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index d499ecb92..1b4e81c2f 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [!] 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. +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. @@ -26,13 +27,37 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-11: Vulkan: Added ImGui_ImplVulkan_PipelineInfo::ExtraDynamicStates[] to allow specifying extra dynamic states to add when creating the VkPipeline. (#9211) +// 2025-09-26: [Helpers] *BREAKING CHANGE*: Vulkan: Helper ImGui_ImplVulkanH_DestroyWindow() does not call vkDestroySurfaceKHR(): as surface is created by caller of ImGui_ImplVulkanH_CreateOrResizeWindow(), it is more consistent that we don't destroy it. (#9163) +// 2026-01-05: [Helpers] *BREAKING CHANGE*: Vulkan: Helper for creating render pass uses ImGui_ImplVulkanH_Window::AttachmentDesc to create render pass. Removed ClearEnabled. (#9152) +// 2025-11-24: [Helpers] Vulkan: Helper for creating a swap-chain (used by examples and multi-viewports) selects VkSwapchainCreateInfoKHR's compositeAlpha based on cap.supportedCompositeAlpha. (#8784) +// 2025-10-15: Vulkan: Added IMGUI_IMPL_VULKAN_VOLK_FILENAME to configure path to volk.h header. (#9008) +// 2025-09-26: *BREAKING CHANGE*: moved some fields in ImGui_ImplVulkan_InitInfo: init_info.RenderPass --> init_info.PipelineInfoMain.RenderPass, init_info.Subpass --> init_info.PipelineInfoMain.Subpass, init_info.MSAASamples --> init_info.PipelineInfoMain.MSAASamples, init_info.PipelineRenderingCreateInfo --> init_info.PipelineInfoMain.PipelineRenderingCreateInfo. +// 2025-09-26: *BREAKING CHANGE*: renamed ImGui_ImplVulkan_MainPipelineCreateInfo to ImGui_ImplVulkan_PipelineInfo. Introduced very recently so shouldn't affect many users. +// 2025-09-26: [Helpers] *BREAKING CHANGE*: Helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a VkImageUsageFlags image_usage` argument, default to VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT if 0. +// 2025-09-26: Vulkan: Added a way to customize shaders by filling ImGui_ImplVulkan_InitInfo::CustomShaderVertCreateInfo/CustomShaderFragCreateInfo. (#8585) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-09-04: Vulkan: Added ImGui_ImplVulkan_CreateMainPipeline(). (#8110, #8111) +// 2025-07-27: Vulkan: Fixed texture update corruption introduced on 2025-06-11. (#8801, #8755, #8840) +// 2025-07-07: Vulkan: Fixed texture synchronization issue introduced on 2025-06-11. (#8772) +// 2025-06-27: Vulkan: Fixed validation errors during texture upload/update by aligning upload size to 'nonCoherentAtomSize'. (#8743, #8744) +// 2025-06-11: Vulkan: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplVulkan_CreateFontsTexture() and ImGui_ImplVulkan_DestroyFontsTexture(). +// 2025-05-07: Vulkan: Fixed validation errors during window detach in multi-viewport mode. (#8600, #8176) +// 2025-05-07: Vulkan: Load dynamic rendering functions using vkGetDeviceProcAddr() + try both non-KHR and KHR versions. (#8600, #8326, #8365) +// 2025-04-07: Vulkan: Deep-copy ImGui_ImplVulkan_InitInfo::PipelineRenderingCreateInfo's pColorAttachmentFormats buffer when set, in order to reduce common user-error of specifying a pointer to data that gets out of scope. (#8282) +// 2025-02-14: *BREAKING CHANGE*: Added uint32_t api_version to ImGui_ImplVulkan_LoadFunctions(). +// 2025-02-13: Vulkan: Added ApiVersion field in ImGui_ImplVulkan_InitInfo. Default to header version if unspecified. Dynamic rendering path loads "vkCmdBeginRendering/vkCmdEndRendering" (without -KHR suffix) on API 1.3. (#8326) +// 2025-01-09: Vulkan: Added IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE to clarify how many image sampler descriptors are expected to be available in descriptor pool. (#6642) +// 2025-01-06: [Helpers] Vulkan: Added more ImGui_ImplVulkanH_XXXX helper functions to simplify our examples. +// 2024-12-11: [Helpers] Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) +// 2024-11-27: Vulkan: Make user-provided descriptor pool optional. As a convenience, when setting init_info->DescriptorPoolSize the backend will create one itself. (#8172, #4867) // 2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2024-10-07: Vulkan: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*. // 2024-04-19: Vulkan: Added convenience support for Volk via IMGUI_IMPL_VULKAN_USE_VOLK define (you can also use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().) // 2024-02-14: *BREAKING CHANGE*: Moved RenderPass parameter from ImGui_ImplVulkan_Init() function to ImGui_ImplVulkan_InitInfo structure. Not required when using dynamic rendering. // 2024-02-12: *BREAKING CHANGE*: Dynamic rendering now require filling PipelineRenderingCreateInfo structure. -// 2024-01-19: Vulkan: Fixed vkAcquireNextImageKHR() validation errors in VulkanSDK 1.3.275 by allocating one extra semaphore than in-flight frames. (#7236) +// 2024-01-19: [Helpers] Vulkan: Fixed vkAcquireNextImageKHR() validation errors in VulkanSDK 1.3.275 by allocating one extra semaphore than in-flight frames. (#7236) // 2024-01-11: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size (#3957). Fixed MinAllocationSize handing (#7189). // 2024-01-03: Vulkan: Added MinAllocationSize field in ImGui_ImplVulkan_InitInfo to workaround zealous "best practice" validation layer. (#7189, #4238) // 2024-01-03: Vulkan: Stopped creating command pools with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT as we don't reset them. @@ -47,7 +72,7 @@ // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-10-04: Vulkan: Added experimental ImGui_ImplVulkan_RemoveTexture() for api symmetry. (#914, #5738). // 2022-01-20: Vulkan: Added support for ImTextureID as VkDescriptorSet. User need to call ImGui_ImplVulkan_AddTexture(). Building for 32-bit targets requires '#define ImTextureID ImU64'. (#914). -// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. +// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likelihood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. // 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-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. // 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. @@ -60,16 +85,16 @@ // 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. // 2019-04-04: *BREAKING CHANGE*: Vulkan: Added ImageCount/MinImageCount fields in ImGui_ImplVulkan_InitInfo, required for initialization (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). Added ImGui_ImplVulkan_SetMinImageCount(). -// 2019-04-04: Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper. +// 2019-04-04: [Helpers] Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper. // 2019-04-04: Vulkan: Avoid passing negative coordinates to vkCmdSetScissor, which debug validation layers do not like. // 2019-04-01: Vulkan: Support for 32-bit index buffer (#define ImDrawIdx unsigned int). // 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display. // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. -// 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. +// 2018-08-25: [Helpers] Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. // 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends. // 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example. // 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. -// 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. +// 2018-03-03: [Helpers] Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. // 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. @@ -85,12 +110,21 @@ #ifndef IM_MAX #define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B)) #endif +#undef Status // X11 headers are leaking this. // Visual Studio warnings #ifdef _MSC_VER #pragma warning (disable: 4127) // condition expression is constant #endif +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) +#endif + // Forward Declarations struct ImGui_ImplVulkan_FrameRenderBuffers; struct ImGui_ImplVulkan_WindowRenderBuffers; @@ -100,11 +134,11 @@ void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulka void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); -void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); // Vulkan prototypes for use with custom loaders -// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h +// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h) #if defined(VK_NO_PROTOTYPES) && !defined(VOLK_H_) #define IMGUI_IMPL_VULKAN_USE_LOADER static bool g_FunctionsLoaded = false; @@ -131,6 +165,7 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorPool) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorSetLayout) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFence) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFramebuffer) \ @@ -145,6 +180,7 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSwapchainKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyBuffer) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorPool) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorSetLayout) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFence) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFramebuffer) \ @@ -159,14 +195,18 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySurfaceKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySwapchainKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDeviceWaitIdle) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkEnumeratePhysicalDevices) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkEndCommandBuffer) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkFlushMappedMemoryRanges) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeCommandBuffers) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeDescriptorSets) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeMemory) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetBufferMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetDeviceQueue) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetImageMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceProperties) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceQueueFamilyProperties) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \ @@ -175,8 +215,10 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueSubmit) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueWaitIdle) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetFences) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkWaitForFences) // Define function pointers #define IMGUI_VULKAN_FUNC_DEF(func) static PFN_##func func; @@ -207,7 +249,17 @@ struct ImGui_ImplVulkan_WindowRenderBuffers { uint32_t Index; uint32_t Count; - ImGui_ImplVulkan_FrameRenderBuffers* FrameRenderBuffers; + ImVector FrameRenderBuffers; +}; + +struct ImGui_ImplVulkan_Texture +{ + VkDeviceMemory Memory; + VkImage Image; + VkImageView ImageView; + VkDescriptorSet DescriptorSet; + + ImGui_ImplVulkan_Texture() { memset((void*)this, 0, sizeof(*this)); } }; // Vulkan data @@ -215,21 +267,20 @@ struct ImGui_ImplVulkan_Data { ImGui_ImplVulkan_InitInfo VulkanInitInfo; VkDeviceSize BufferMemoryAlignment; + VkDeviceSize NonCoherentAtomSize; VkPipelineCreateFlags PipelineCreateFlags; VkDescriptorSetLayout DescriptorSetLayout; VkPipelineLayout PipelineLayout; - VkPipeline Pipeline; + VkPipeline Pipeline; // pipeline for main render pass (created by app) VkShaderModule ShaderModuleVert; VkShaderModule ShaderModuleFrag; + VkDescriptorPool DescriptorPool; + ImVector PipelineRenderingCreateInfoColorAttachmentFormats; // Deep copy of format array - // Font data - VkSampler FontSampler; - VkDeviceMemory FontMemory; - VkImage FontImage; - VkImageView FontView; - VkDescriptorSet FontDescriptorSet; - VkCommandPool FontCommandPool; - VkCommandBuffer FontCommandBuffer; + // Texture management + VkSampler TexSamplerLinear; + VkCommandPool TexCommandPool; + VkCommandBuffer TexCommandBuffer; // Render buffers for main window ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers; @@ -238,6 +289,7 @@ struct ImGui_ImplVulkan_Data { memset((void*)this, 0, sizeof(*this)); BufferMemoryAlignment = 256; + NonCoherentAtomSize = 64; } }; @@ -477,6 +529,13 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm if (fb_width <= 0 || fb_height <= 0) return; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplVulkan_UpdateTexture(tex); + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; if (pipeline == VK_NULL_HANDLE) @@ -484,12 +543,12 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Allocate array to store enough vertex/index buffers ImGui_ImplVulkan_WindowRenderBuffers* wrb = &bd->MainWindowRenderBuffers; - if (wrb->FrameRenderBuffers == nullptr) + if (wrb->FrameRenderBuffers.Size == 0) { wrb->Index = 0; wrb->Count = v->ImageCount; - wrb->FrameRenderBuffers = (ImGui_ImplVulkan_FrameRenderBuffers*)IM_ALLOC(sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); - memset((void*)wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); + wrb->FrameRenderBuffers.resize(wrb->Count); + memset((void*)wrb->FrameRenderBuffers.Data, 0, wrb->FrameRenderBuffers.size_in_bytes()); } IM_ASSERT(wrb->Count == v->ImageCount); wrb->Index = (wrb->Index + 1) % wrb->Count; @@ -512,9 +571,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm check_vk_result(err); err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, index_size, 0, (void**)&idx_dst); check_vk_result(err); - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); vtx_dst += draw_list->VtxBuffer.Size; @@ -550,11 +608,11 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) + VkDescriptorSet last_desc_set = VK_NULL_HANDLE; int global_vtx_offset = 0; int global_idx_offset = 0; - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; @@ -566,6 +624,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); else pcmd->UserCallback(draw_list, pcmd); + last_desc_set = VK_NULL_HANDLE; } else { @@ -590,14 +649,10 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm vkCmdSetScissor(command_buffer, 0, 1, &scissor); // Bind DescriptorSet with font or user texture - VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->GetTexID() }; - if (sizeof(ImTextureID) < sizeof(ImU64)) - { - // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used. - IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontDescriptorSet); - desc_set[0] = bd->FontDescriptorSet; - } - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr); + VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID(); + if (desc_set != last_desc_set) + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr); + last_desc_set = desc_set; // Draw vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); @@ -606,7 +661,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm global_idx_offset += draw_list->IdxBuffer.Size; global_vtx_offset += draw_list->VtxBuffer.Size; } - platform_io.Renderer_RenderState = NULL; + platform_io.Renderer_RenderState = nullptr; // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called. // Our last values will leak into user/application rendering IF: @@ -619,245 +674,265 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm vkCmdSetScissor(command_buffer, 0, 1, &scissor); } -bool ImGui_ImplVulkan_CreateFontsTexture() +static void ImGui_ImplVulkan_DestroyTexture(ImTextureData* tex) { - ImGuiIO& io = ImGui::GetIO(); + if (ImGui_ImplVulkan_Texture* backend_tex = (ImGui_ImplVulkan_Texture*)tex->BackendUserData) + { + IM_ASSERT(backend_tex->DescriptorSet == (VkDescriptorSet)tex->TexID); + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet); + vkDestroyImageView(v->Device, backend_tex->ImageView, v->Allocator); + vkDestroyImage(v->Device, backend_tex->Image, v->Allocator); + vkFreeMemory(v->Device, backend_tex->Memory, v->Allocator); + IM_DELETE(backend_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->BackendUserData = nullptr; + } + tex->SetStatus(ImTextureStatus_Destroyed); +} + +void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex) +{ + if (tex->Status == ImTextureStatus_OK) + return; ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err; - // Destroy existing texture (if any) - if (bd->FontView || bd->FontImage || bd->FontMemory || bd->FontDescriptorSet) + if (tex->Status == ImTextureStatus_WantCreate) { - vkQueueWaitIdle(v->Queue); - ImGui_ImplVulkan_DestroyFontsTexture(); + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + ImGui_ImplVulkan_Texture* backend_tex = IM_NEW(ImGui_ImplVulkan_Texture)(); + + // Create the Image: + { + VkImageCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.imageType = VK_IMAGE_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.extent.width = tex->Width; + info.extent.height = tex->Height; + info.extent.depth = 1; + info.mipLevels = 1; + info.arrayLayers = 1; + info.samples = VK_SAMPLE_COUNT_1_BIT; + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + err = vkCreateImage(v->Device, &info, v->Allocator, &backend_tex->Image); + check_vk_result(err); + VkMemoryRequirements req; + vkGetImageMemoryRequirements(v->Device, backend_tex->Image, &req); + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &backend_tex->Memory); + check_vk_result(err); + err = vkBindImageMemory(v->Device, backend_tex->Image, backend_tex->Memory, 0); + check_vk_result(err); + } + + // Create the Image View: + { + VkImageViewCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.image = backend_tex->Image; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + info.subresourceRange.levelCount = 1; + info.subresourceRange.layerCount = 1; + err = vkCreateImageView(v->Device, &info, v->Allocator, &backend_tex->ImageView); + check_vk_result(err); + } + + // Create the Descriptor Set + backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(bd->TexSamplerLinear, backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + // Store identifiers + tex->SetTexID((ImTextureID)backend_tex->DescriptorSet); + tex->BackendUserData = backend_tex; } - // Create command pool/buffer - if (bd->FontCommandPool == VK_NULL_HANDLE) + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) { - VkCommandPoolCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - info.flags = 0; - info.queueFamilyIndex = v->QueueFamily; - vkCreateCommandPool(v->Device, &info, v->Allocator, &bd->FontCommandPool); - } - if (bd->FontCommandBuffer == VK_NULL_HANDLE) - { - VkCommandBufferAllocateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - info.commandPool = bd->FontCommandPool; - info.commandBufferCount = 1; - err = vkAllocateCommandBuffers(v->Device, &info, &bd->FontCommandBuffer); + ImGui_ImplVulkan_Texture* backend_tex = (ImGui_ImplVulkan_Texture*)tex->BackendUserData; + + // Update full texture or selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions. + // We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture. + const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x; + const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y; + const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w; + const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h; + + // Create the Upload Buffer: + VkDeviceMemory upload_buffer_memory; + + VkBuffer upload_buffer; + VkDeviceSize upload_pitch = upload_w * tex->BytesPerPixel; + VkDeviceSize upload_size = AlignBufferSize(upload_h * upload_pitch, bd->NonCoherentAtomSize); + { + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = upload_size; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &upload_buffer); + check_vk_result(err); + VkMemoryRequirements req; + vkGetBufferMemoryRequirements(v->Device, upload_buffer, &req); + bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &upload_buffer_memory); + check_vk_result(err); + err = vkBindBufferMemory(v->Device, upload_buffer, upload_buffer_memory, 0); + check_vk_result(err); + } + + // Upload to Buffer: + { + char* map = nullptr; + err = vkMapMemory(v->Device, upload_buffer_memory, 0, upload_size, 0, (void**)(&map)); + check_vk_result(err); + for (int y = 0; y < upload_h; y++) + memcpy(map + upload_pitch * y, tex->GetPixelsAt(upload_x, upload_y + y), (size_t)upload_pitch); + VkMappedMemoryRange range[1] = {}; + range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range[0].memory = upload_buffer_memory; + range[0].size = upload_size; + err = vkFlushMappedMemoryRanges(v->Device, 1, range); + check_vk_result(err); + vkUnmapMemory(v->Device, upload_buffer_memory); + } + + // Start command buffer + { + err = vkResetCommandPool(v->Device, bd->TexCommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(bd->TexCommandBuffer, &begin_info); + check_vk_result(err); + } + + // Copy to Image: + { + VkBufferMemoryBarrier upload_barrier[1] = {}; + upload_barrier[0].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + upload_barrier[0].srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + upload_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + upload_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + upload_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + upload_barrier[0].buffer = upload_buffer; + upload_barrier[0].offset = 0; + upload_barrier[0].size = upload_size; + + VkImageMemoryBarrier copy_barrier[1] = {}; + copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + copy_barrier[0].oldLayout = (tex->Status == ImTextureStatus_WantCreate) ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].image = backend_tex->Image; + copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_barrier[0].subresourceRange.levelCount = 1; + copy_barrier[0].subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(bd->TexCommandBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, upload_barrier, 1, copy_barrier); + + VkBufferImageCopy region = {}; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + region.imageExtent.width = upload_w; + region.imageExtent.height = upload_h; + region.imageExtent.depth = 1; + region.imageOffset.x = upload_x; + region.imageOffset.y = upload_y; + vkCmdCopyBufferToImage(bd->TexCommandBuffer, upload_buffer, backend_tex->Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + VkImageMemoryBarrier use_barrier[1] = {}; + use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + use_barrier[0].image = backend_tex->Image; + use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + use_barrier[0].subresourceRange.levelCount = 1; + use_barrier[0].subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(bd->TexCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, use_barrier); + } + + // End command buffer + { + VkSubmitInfo end_info = {}; + end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + end_info.commandBufferCount = 1; + end_info.pCommandBuffers = &bd->TexCommandBuffer; + err = vkEndCommandBuffer(bd->TexCommandBuffer); + check_vk_result(err); + err = vkQueueSubmit(v->Queue, 1, &end_info, VK_NULL_HANDLE); + check_vk_result(err); + } + + err = vkQueueWaitIdle(v->Queue); // FIXME-OPT: Suboptimal! check_vk_result(err); + vkDestroyBuffer(v->Device, upload_buffer, v->Allocator); + vkFreeMemory(v->Device, upload_buffer_memory, v->Allocator); + + tex->SetStatus(ImTextureStatus_OK); } - // Start command buffer - { - err = vkResetCommandPool(v->Device, bd->FontCommandPool, 0); - check_vk_result(err); - VkCommandBufferBeginInfo begin_info = {}; - begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - err = vkBeginCommandBuffer(bd->FontCommandBuffer, &begin_info); - check_vk_result(err); - } - - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - size_t upload_size = width * height * 4 * sizeof(char); - - // Create the Image: - { - VkImageCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - info.imageType = VK_IMAGE_TYPE_2D; - info.format = VK_FORMAT_R8G8B8A8_UNORM; - info.extent.width = width; - info.extent.height = height; - info.extent.depth = 1; - info.mipLevels = 1; - info.arrayLayers = 1; - info.samples = VK_SAMPLE_COUNT_1_BIT; - info.tiling = VK_IMAGE_TILING_OPTIMAL; - info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - err = vkCreateImage(v->Device, &info, v->Allocator, &bd->FontImage); - check_vk_result(err); - VkMemoryRequirements req; - vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req); - VkMemoryAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); - alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); - err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->FontMemory); - check_vk_result(err); - err = vkBindImageMemory(v->Device, bd->FontImage, bd->FontMemory, 0); - check_vk_result(err); - } - - // Create the Image View: - { - VkImageViewCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - info.image = bd->FontImage; - info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = VK_FORMAT_R8G8B8A8_UNORM; - info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - info.subresourceRange.levelCount = 1; - info.subresourceRange.layerCount = 1; - err = vkCreateImageView(v->Device, &info, v->Allocator, &bd->FontView); - check_vk_result(err); - } - - // Create the Descriptor Set: - bd->FontDescriptorSet = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(bd->FontSampler, bd->FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - // Create the Upload Buffer: - VkDeviceMemory upload_buffer_memory; - VkBuffer upload_buffer; - { - VkBufferCreateInfo buffer_info = {}; - buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - buffer_info.size = upload_size; - buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &upload_buffer); - check_vk_result(err); - VkMemoryRequirements req; - vkGetBufferMemoryRequirements(v->Device, upload_buffer, &req); - bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; - VkMemoryAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); - alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); - err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &upload_buffer_memory); - check_vk_result(err); - err = vkBindBufferMemory(v->Device, upload_buffer, upload_buffer_memory, 0); - check_vk_result(err); - } - - // Upload to Buffer: - { - char* map = nullptr; - err = vkMapMemory(v->Device, upload_buffer_memory, 0, upload_size, 0, (void**)(&map)); - check_vk_result(err); - memcpy(map, pixels, upload_size); - VkMappedMemoryRange range[1] = {}; - range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - range[0].memory = upload_buffer_memory; - range[0].size = upload_size; - err = vkFlushMappedMemoryRanges(v->Device, 1, range); - check_vk_result(err); - vkUnmapMemory(v->Device, upload_buffer_memory); - } - - // Copy to Image: - { - VkImageMemoryBarrier copy_barrier[1] = {}; - copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barrier[0].image = bd->FontImage; - copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_barrier[0].subresourceRange.levelCount = 1; - copy_barrier[0].subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(bd->FontCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, copy_barrier); - - VkBufferImageCopy region = {}; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.layerCount = 1; - region.imageExtent.width = width; - region.imageExtent.height = height; - region.imageExtent.depth = 1; - vkCmdCopyBufferToImage(bd->FontCommandBuffer, upload_buffer, bd->FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - - VkImageMemoryBarrier use_barrier[1] = {}; - use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - use_barrier[0].image = bd->FontImage; - use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - use_barrier[0].subresourceRange.levelCount = 1; - use_barrier[0].subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(bd->FontCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, use_barrier); - } - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)bd->FontDescriptorSet); - - // End command buffer - VkSubmitInfo end_info = {}; - end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - end_info.commandBufferCount = 1; - end_info.pCommandBuffers = &bd->FontCommandBuffer; - err = vkEndCommandBuffer(bd->FontCommandBuffer); - check_vk_result(err); - err = vkQueueSubmit(v->Queue, 1, &end_info, VK_NULL_HANDLE); - check_vk_result(err); - - err = vkQueueWaitIdle(v->Queue); - check_vk_result(err); - - vkDestroyBuffer(v->Device, upload_buffer, v->Allocator); - vkFreeMemory(v->Device, upload_buffer_memory, v->Allocator); - - return true; -} - -// You probably never need to call this, as it is called by ImGui_ImplVulkan_CreateFontsTexture() and ImGui_ImplVulkan_Shutdown(). -void ImGui_ImplVulkan_DestroyFontsTexture() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - - if (bd->FontDescriptorSet) - { - ImGui_ImplVulkan_RemoveTexture(bd->FontDescriptorSet); - bd->FontDescriptorSet = VK_NULL_HANDLE; - io.Fonts->SetTexID(0); - } - - if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; } - if (bd->FontImage) { vkDestroyImage(v->Device, bd->FontImage, v->Allocator); bd->FontImage = VK_NULL_HANDLE; } - if (bd->FontMemory) { vkFreeMemory(v->Device, bd->FontMemory, v->Allocator); bd->FontMemory = VK_NULL_HANDLE; } + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames >= (int)bd->VulkanInitInfo.ImageCount) + ImGui_ImplVulkan_DestroyTexture(tex); } static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator) { - // Create the shader modules ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; if (bd->ShaderModuleVert == VK_NULL_HANDLE) { - VkShaderModuleCreateInfo vert_info = {}; - vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - vert_info.codeSize = sizeof(__glsl_shader_vert_spv); - vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; - VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &bd->ShaderModuleVert); + VkShaderModuleCreateInfo default_vert_info = {}; + default_vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + default_vert_info.codeSize = sizeof(__glsl_shader_vert_spv); + default_vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; + VkShaderModuleCreateInfo* p_vert_info = (v->CustomShaderVertCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) ? &v->CustomShaderVertCreateInfo : &default_vert_info; + VkResult err = vkCreateShaderModule(device, p_vert_info, allocator, &bd->ShaderModuleVert); check_vk_result(err); } if (bd->ShaderModuleFrag == VK_NULL_HANDLE) { - VkShaderModuleCreateInfo frag_info = {}; - frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - frag_info.codeSize = sizeof(__glsl_shader_frag_spv); - frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; - VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &bd->ShaderModuleFrag); + VkShaderModuleCreateInfo default_frag_info = {}; + default_frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + default_frag_info.codeSize = sizeof(__glsl_shader_frag_spv); + default_frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; + VkShaderModuleCreateInfo* p_frag_info = (v->CustomShaderFragCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) ? &v->CustomShaderFragCreateInfo : &default_frag_info; + VkResult err = vkCreateShaderModule(device, p_frag_info, allocator, &bd->ShaderModuleFrag); check_vk_result(err); } } -static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) +#if !defined(IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING) && !(defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering)) +typedef void VkPipelineRenderingCreateInfoKHR; +#endif + +static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, const ImGui_ImplVulkan_PipelineInfo* info) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_CreateShaderModules(device, allocator); @@ -915,7 +990,7 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC VkPipelineMultisampleStateCreateInfo ms_info = {}; ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - ms_info.rasterizationSamples = (MSAASamples != 0) ? MSAASamples : VK_SAMPLE_COUNT_1_BIT; + ms_info.rasterizationSamples = (info->MSAASamples != 0) ? info->MSAASamples : VK_SAMPLE_COUNT_1_BIT; VkPipelineColorBlendAttachmentState color_attachment[1] = {}; color_attachment[0].blendEnable = VK_TRUE; @@ -935,41 +1010,44 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC blend_info.attachmentCount = 1; blend_info.pAttachments = color_attachment; - VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + ImVector dynamic_states = info->ExtraDynamicStates; + dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); + dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); VkPipelineDynamicStateCreateInfo dynamic_state = {}; dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); - dynamic_state.pDynamicStates = dynamic_states; + dynamic_state.dynamicStateCount = dynamic_states.Size; + dynamic_state.pDynamicStates = dynamic_states.Data; - VkGraphicsPipelineCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - info.flags = bd->PipelineCreateFlags; - info.stageCount = 2; - info.pStages = stage; - info.pVertexInputState = &vertex_info; - info.pInputAssemblyState = &ia_info; - info.pViewportState = &viewport_info; - info.pRasterizationState = &raster_info; - info.pMultisampleState = &ms_info; - info.pDepthStencilState = &depth_info; - info.pColorBlendState = &blend_info; - info.pDynamicState = &dynamic_state; - info.layout = bd->PipelineLayout; - info.renderPass = renderPass; - info.subpass = subpass; + VkGraphicsPipelineCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + create_info.flags = bd->PipelineCreateFlags; + create_info.stageCount = 2; + create_info.pStages = stage; + create_info.pVertexInputState = &vertex_info; + create_info.pInputAssemblyState = &ia_info; + create_info.pViewportState = &viewport_info; + create_info.pRasterizationState = &raster_info; + create_info.pMultisampleState = &ms_info; + create_info.pDepthStencilState = &depth_info; + create_info.pColorBlendState = &blend_info; + create_info.pDynamicState = &dynamic_state; + create_info.layout = bd->PipelineLayout; + create_info.renderPass = info->RenderPass; + create_info.subpass = info->Subpass; #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING if (bd->VulkanInitInfo.UseDynamicRendering) { - IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR"); - IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo pNext must be NULL"); - info.pNext = &bd->VulkanInitInfo.PipelineRenderingCreateInfo; - info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr. + IM_ASSERT(info->PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo::sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR"); + IM_ASSERT(info->PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo::pNext must be nullptr"); + create_info.pNext = &info->PipelineRenderingCreateInfo; + create_info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr. } #endif - - VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); + VkPipeline pipeline; + VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &create_info, allocator, &pipeline); check_vk_result(err); + return pipeline; } bool ImGui_ImplVulkan_CreateDeviceObjects() @@ -978,7 +1056,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err; - if (!bd->FontSampler) + if (!bd->TexSamplerLinear) { // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. VkSamplerCreateInfo info = {}; @@ -992,7 +1070,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() info.minLod = -1000; info.maxLod = 1000; info.maxAnisotropy = 1.0f; - err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->FontSampler); + err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->TexSamplerLinear); check_vk_result(err); } @@ -1010,6 +1088,21 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } + if (v->DescriptorPoolSize != 0) + { + IM_ASSERT(v->DescriptorPoolSize >= IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE); + VkDescriptorPoolSize pool_size = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, v->DescriptorPoolSize }; + 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 = v->DescriptorPoolSize; + pool_info.poolSizeCount = 1; + pool_info.pPoolSizes = &pool_size; + + err = vkCreateDescriptorPool(v->Device, &pool_info, v->Allocator, &bd->DescriptorPool); + check_vk_result(err); + } + if (!bd->PipelineLayout) { // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix @@ -1028,34 +1121,126 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } - ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, v->RenderPass, v->MSAASamples, &bd->Pipeline, v->Subpass); + // Create pipeline + bool create_main_pipeline = (v->PipelineInfoMain.RenderPass != VK_NULL_HANDLE); +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + create_main_pipeline |= (v->UseDynamicRendering && v->PipelineInfoMain.PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR); +#endif + if (create_main_pipeline) + ImGui_ImplVulkan_CreateMainPipeline(&v->PipelineInfoMain); + + // Create command pool/buffer for texture upload + if (!bd->TexCommandPool) + { + VkCommandPoolCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info.flags = 0; + info.queueFamilyIndex = v->QueueFamily; + err = vkCreateCommandPool(v->Device, &info, v->Allocator, &bd->TexCommandPool); + check_vk_result(err); + } + if (!bd->TexCommandBuffer) + { + VkCommandBufferAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = bd->TexCommandPool; + info.commandBufferCount = 1; + err = vkAllocateCommandBuffers(v->Device, &info, &bd->TexCommandBuffer); + check_vk_result(err); + } return true; } +void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_PipelineInfo* pipeline_info_in) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (bd->Pipeline) + { + vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); + bd->Pipeline = VK_NULL_HANDLE; + } + ImGui_ImplVulkan_PipelineInfo* pipeline_info = &v->PipelineInfoMain; + if (pipeline_info != pipeline_info_in) + *pipeline_info = *pipeline_info_in; + +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + VkPipelineRenderingCreateInfoKHR* pipeline_rendering_create_info = &pipeline_info->PipelineRenderingCreateInfo; + if (v->UseDynamicRendering && pipeline_rendering_create_info->pColorAttachmentFormats != nullptr) + { + // Deep copy buffer to reduce error-rate for end user (#8282) + ImVector formats; + formats.resize((int)pipeline_rendering_create_info->colorAttachmentCount); + memcpy(formats.Data, pipeline_rendering_create_info->pColorAttachmentFormats, (size_t)formats.size_in_bytes()); + formats.swap(bd->PipelineRenderingCreateInfoColorAttachmentFormats); + pipeline_rendering_create_info->pColorAttachmentFormats = bd->PipelineRenderingCreateInfoColorAttachmentFormats.Data; + } +#endif + bd->Pipeline = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, pipeline_info); +} + void ImGui_ImplVulkan_DestroyDeviceObjects() { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); - ImGui_ImplVulkan_DestroyFontsTexture(); - if (bd->FontCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->FontCommandPool, 1, &bd->FontCommandBuffer); bd->FontCommandBuffer = VK_NULL_HANDLE; } - if (bd->FontCommandPool) { vkDestroyCommandPool(v->Device, bd->FontCommandPool, v->Allocator); bd->FontCommandPool = VK_NULL_HANDLE; } + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplVulkan_DestroyTexture(tex); + + if (bd->TexCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->TexCommandPool, 1, &bd->TexCommandBuffer); bd->TexCommandBuffer = VK_NULL_HANDLE; } + if (bd->TexCommandPool) { vkDestroyCommandPool(v->Device, bd->TexCommandPool, v->Allocator); bd->TexCommandPool = VK_NULL_HANDLE; } + if (bd->TexSamplerLinear) { vkDestroySampler(v->Device, bd->TexSamplerLinear, v->Allocator); bd->TexSamplerLinear = VK_NULL_HANDLE; } if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; } if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; } - if (bd->FontSampler) { vkDestroySampler(v->Device, bd->FontSampler, v->Allocator); bd->FontSampler = VK_NULL_HANDLE; } if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } + if (bd->DescriptorPool) { vkDestroyDescriptorPool(v->Device, bd->DescriptorPool, v->Allocator); bd->DescriptorPool = VK_NULL_HANDLE; } } -bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING +static void ImGui_ImplVulkan_LoadDynamicRenderingFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) +{ + IM_UNUSED(api_version); + + // Manually load those two (see #5446, #8326, #8365, #8600) + // - Try loading core (non-KHR) versions first (this will work for Vulkan 1.3+ and the device supports dynamic rendering) + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRendering", user_data)); + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRendering", user_data)); + + // - Fallback to KHR versions if core not available (this will work if KHR extension is available and enabled and also the device supports dynamic rendering) + if (ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR == nullptr || ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR == nullptr) + { + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRenderingKHR", user_data)); + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRenderingKHR", user_data)); + } +} +#endif + +// If unspecified by user, assume that ApiVersion == HeaderVersion + // We don't care about other versions than 1.3 for our checks, so don't need to make this exhaustive (e.g. with all #ifdef VK_VERSION_1_X checks) +static uint32_t ImGui_ImplVulkan_GetDefaultApiVersion() +{ +#ifdef VK_HEADER_VERSION_COMPLETE + return VK_HEADER_VERSION_COMPLETE; +#else + return VK_API_VERSION_1_0; +#endif +} + +bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) { // Load function pointers // You can use the default Vulkan loader using: - // ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void*) { return vkGetInstanceProcAddr(your_vk_isntance, function_name); }); + // ImGui_ImplVulkan_LoadFunctions(VK_API_VERSION_1_3, [](const char* function_name, void*) { return vkGetInstanceProcAddr(your_vk_isntance, function_name); }); // But this would be roughly equivalent to not setting VK_NO_PROTOTYPES. + if (api_version == 0) + api_version = ImGui_ImplVulkan_GetDefaultApiVersion(); + #ifdef IMGUI_IMPL_VULKAN_USE_LOADER #define IMGUI_VULKAN_FUNC_LOAD(func) \ func = reinterpret_cast(loader_func(#func, user_data)); \ @@ -1065,9 +1250,7 @@ bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const ch #undef IMGUI_VULKAN_FUNC_LOAD #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - // Manually load those two (see #5446) - ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRenderingKHR", user_data)); - ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRenderingKHR", user_data)); + ImGui_ImplVulkan_LoadDynamicRenderingFunctions(api_version, loader_func, user_data); #endif #else IM_UNUSED(loader_func); @@ -1082,12 +1265,14 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) { IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + if (info->ApiVersion == 0) + info->ApiVersion = ImGui_ImplVulkan_GetDefaultApiVersion(); + if (info->UseDynamicRendering) { #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING #ifndef IMGUI_IMPL_VULKAN_USE_LOADER - ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdBeginRenderingKHR")); - ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdEndRenderingKHR")); + ImGui_ImplVulkan_LoadDynamicRenderingFunctions(info->ApiVersion, [](const char* function_name, void* user_data) { return vkGetDeviceProcAddr((VkDevice)user_data, function_name); }, (void*)info->Device); #endif IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR != nullptr); IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR != nullptr); @@ -1105,20 +1290,30 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_vulkan"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + // Sanity checks IM_ASSERT(info->Instance != VK_NULL_HANDLE); IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); IM_ASSERT(info->Device != VK_NULL_HANDLE); IM_ASSERT(info->Queue != VK_NULL_HANDLE); - IM_ASSERT(info->DescriptorPool != VK_NULL_HANDLE); IM_ASSERT(info->MinImageCount >= 2); IM_ASSERT(info->ImageCount >= info->MinImageCount); - if (info->UseDynamicRendering == false) - IM_ASSERT(info->RenderPass != VK_NULL_HANDLE); + if (info->DescriptorPool != VK_NULL_HANDLE) // Either DescriptorPool or DescriptorPoolSize must be set, not both! + IM_ASSERT(info->DescriptorPoolSize == 0); + else + IM_ASSERT(info->DescriptorPoolSize > 0); + if (info->UseDynamicRendering) + IM_ASSERT(info->PipelineInfoMain.RenderPass == VK_NULL_HANDLE); bd->VulkanInitInfo = *info; - ImGui_ImplVulkan_CreateDeviceObjects(); + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(info->PhysicalDevice, &properties); + bd->NonCoherentAtomSize = properties.limits.nonCoherentAtomSize; + + if (!ImGui_ImplVulkan_CreateDeviceObjects()) + IM_ASSERT(0 && "ImGui_ImplVulkan_CreateDeviceObjects() failed!"); // <- Can't be hit yet. return true; } @@ -1128,11 +1323,14 @@ void ImGui_ImplVulkan_Shutdown() ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplVulkan_DestroyDeviceObjects(); + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } @@ -1140,9 +1338,7 @@ void ImGui_ImplVulkan_NewFrame() { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplVulkan_Init()?"); - - if (!bd->FontDescriptorSet) - ImGui_ImplVulkan_CreateFontsTexture(); + IM_UNUSED(bd); } void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) @@ -1159,19 +1355,20 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) bd->VulkanInitInfo.MinImageCount = min_image_count; } -// Register a texture +// Register a texture by creating a descriptor // 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. VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool; // Create Descriptor Set: VkDescriptorSet descriptor_set; { VkDescriptorSetAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - alloc_info.descriptorPool = v->DescriptorPool; + alloc_info.descriptorPool = pool; alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &bd->DescriptorSetLayout; VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set); @@ -1179,6 +1376,7 @@ VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image } // Update the Descriptor Set: + if (descriptor_set != VK_NULL_HANDLE) { VkDescriptorImageInfo desc_image[1] = {}; desc_image[0].sampler = sampler; @@ -1199,7 +1397,8 @@ void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - vkFreeDescriptorSets(v->Device, v->DescriptorPool, 1, &descriptor_set); + VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool; + vkFreeDescriptorSets(v->Device, pool, 1, &descriptor_set); } void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator) @@ -1216,15 +1415,14 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk { for (uint32_t n = 0; n < buffers->Count; n++) ImGui_ImplVulkan_DestroyFrameRenderBuffers(device, &buffers->FrameRenderBuffers[n], allocator); - IM_FREE(buffers->FrameRenderBuffers); - buffers->FrameRenderBuffers = nullptr; + buffers->FrameRenderBuffers.clear(); buffers->Index = 0; buffers->Count = 0; } //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers -// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own app.) +// (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: @@ -1234,7 +1432,7 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk // 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. +// You may read this code to learn about Vulkan, but it is recommended you use your 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) //------------------------------------------------------------------------- @@ -1306,6 +1504,49 @@ VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_d return VK_PRESENT_MODE_FIFO_KHR; // Always available } +VkPhysicalDevice ImGui_ImplVulkanH_SelectPhysicalDevice(VkInstance instance) +{ + uint32_t gpu_count; + VkResult err = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr); + check_vk_result(err); + IM_ASSERT(gpu_count > 0); + + ImVector gpus; + gpus.resize(gpu_count); + err = vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.Data); + check_vk_result(err); + + // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers + // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple + // dedicated GPUs) is out of scope of this sample. + for (VkPhysicalDevice& device : gpus) + { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(device, &properties); + if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + return device; + } + + // Use first GPU (Integrated) is a Discrete one is not available. + if (gpu_count > 0) + return gpus[0]; + return VK_NULL_HANDLE; +} + + +uint32_t ImGui_ImplVulkanH_SelectQueueFamilyIndex(VkPhysicalDevice physical_device) +{ + uint32_t count; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, nullptr); + ImVector queues_properties; + queues_properties.resize((int)count); + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, queues_properties.Data); + for (uint32_t i = 0; i < count; i++) + if (queues_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + return i; + return (uint32_t)-1; +} + void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator) { IM_ASSERT(physical_device != VK_NULL_HANDLE && device != VK_NULL_HANDLE); @@ -1369,7 +1610,7 @@ int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_m } // Also destroy old swap chain and in-flight frames data, if any. -void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage) { VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; @@ -1383,10 +1624,8 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); for (uint32_t i = 0; i < wd->SemaphoreCount; i++) ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); - IM_FREE(wd->Frames); - IM_FREE(wd->FrameSemaphores); - wd->Frames = nullptr; - wd->FrameSemaphores = nullptr; + wd->Frames.clear(); + wd->FrameSemaphores.clear(); wd->ImageCount = 0; if (wd->RenderPass) vkDestroyRenderPass(device, wd->RenderPass, allocator); @@ -1399,6 +1638,10 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V // Create Swapchain { + VkSurfaceCapabilitiesKHR cap; + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap); + check_vk_result(err); + VkSwapchainCreateInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; info.surface = wd->Surface; @@ -1406,21 +1649,22 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V info.imageFormat = wd->SurfaceFormat.format; info.imageColorSpace = wd->SurfaceFormat.colorSpace; info.imageArrayLayers = 1; - info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | image_usage; info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family - info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + info.preTransform = (cap.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : cap.currentTransform; + if (cap.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) + info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + else if (cap.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) + info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + else + IM_ASSERT(false && "No supported composite alpha mode found!"); info.presentMode = wd->PresentMode; info.clipped = VK_TRUE; info.oldSwapchain = old_swapchain; - VkSurfaceCapabilitiesKHR cap; - err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap); - check_vk_result(err); if (info.minImageCount < cap.minImageCount) info.minImageCount = cap.minImageCount; else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount) info.minImageCount = cap.maxImageCount; - if (cap.currentExtent.width == 0xffffffff) { info.imageExtent.width = wd->Width = w; @@ -1437,16 +1681,15 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V check_vk_result(err); VkImage backbuffers[16] = {}; IM_ASSERT(wd->ImageCount >= min_image_count); - IM_ASSERT(wd->ImageCount < IM_ARRAYSIZE(backbuffers)); + IM_ASSERT(wd->ImageCount < IM_COUNTOF(backbuffers)); err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers); check_vk_result(err); - IM_ASSERT(wd->Frames == nullptr && wd->FrameSemaphores == nullptr); wd->SemaphoreCount = wd->ImageCount + 1; - wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount); - wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->SemaphoreCount); - memset((void*)wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount); - memset((void*)wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->SemaphoreCount); + wd->Frames.resize(wd->ImageCount); + wd->FrameSemaphores.resize(wd->SemaphoreCount); + memset(wd->Frames.Data, 0, wd->Frames.size_in_bytes()); + memset(wd->FrameSemaphores.Data, 0, wd->FrameSemaphores.size_in_bytes()); for (uint32_t i = 0; i < wd->ImageCount; i++) wd->Frames[i].Backbuffer = backbuffers[i]; } @@ -1456,15 +1699,9 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V // Create the Render Pass if (wd->UseDynamicRendering == false) { - VkAttachmentDescription attachment = {}; - attachment.format = wd->SurfaceFormat.format; - attachment.samples = VK_SAMPLE_COUNT_1_BIT; - attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentDescription attachment = wd->AttachmentDesc; + if (attachment.format == VK_FORMAT_UNDEFINED) + attachment.format = wd->SurfaceFormat.format; VkAttachmentReference color_attachment = {}; color_attachment.attachment = 0; color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -1539,16 +1776,96 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V } // Create or resize window -void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count) +// - 2025/09/26: v1.92.4 added a trailing 'VkImageUsageFlags image_usage' parameter which is usually VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT. +void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count, VkImageUsageFlags image_usage) { IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); - (void)instance; - ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); + IM_ASSERT(wd->Surface != VK_NULL_HANDLE); + IM_UNUSED(instance); + + ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count, image_usage); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); + + // FIXME: to submit the command buffer, we need a queue. In the examples folder, the ImGui_ImplVulkanH_CreateOrResizeWindow function is called + // before the ImGui_ImplVulkan_Init function, so we don't have access to the queue yet. Here we have the queue_family that we can use to grab + // a queue from the device and submit the command buffer. It would be better to have access to the queue as suggested in the FIXME below. + VkCommandPool command_pool; + VkCommandPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.queueFamilyIndex = queue_family; + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VkResult err = vkCreateCommandPool(device, &pool_info, allocator, &command_pool); + check_vk_result(err); + + VkFenceCreateInfo fence_info = {}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VkFence fence; + err = vkCreateFence(device, &fence_info, allocator, &fence); + check_vk_result(err); + + VkCommandBufferAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = 1; + VkCommandBuffer command_buffer; + err = vkAllocateCommandBuffers(device, &alloc_info, &command_buffer); + check_vk_result(err); + + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(command_buffer, &begin_info); + check_vk_result(err); + + // Transition the images to the correct layout for rendering + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = wd->Frames[i].Backbuffer; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + + err = vkEndCommandBuffer(command_buffer); + check_vk_result(err); + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + VkQueue queue; + vkGetDeviceQueue(device, queue_family, 0, &queue); + err = vkQueueSubmit(queue, 1, &submit_info, fence); + check_vk_result(err); + err = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX); + check_vk_result(err); + err = vkResetFences(device, 1, &fence); + check_vk_result(err); + + err = vkResetCommandPool(device, command_pool, 0); + check_vk_result(err); + + // Destroy command buffer and fence and command pool + vkFreeCommandBuffers(device, command_pool, 1, &command_buffer); + vkDestroyCommandPool(device, command_pool, allocator); + vkDestroyFence(device, fence, allocator); + command_pool = VK_NULL_HANDLE; + command_buffer = VK_NULL_HANDLE; + fence = VK_NULL_HANDLE; + queue = VK_NULL_HANDLE; } void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator) { + IM_UNUSED(instance); vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals) //vkQueueWaitIdle(bd->Queue); @@ -1556,16 +1873,16 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); for (uint32_t i = 0; i < wd->SemaphoreCount; i++) ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); - IM_FREE(wd->Frames); - IM_FREE(wd->FrameSemaphores); - wd->Frames = nullptr; - wd->FrameSemaphores = nullptr; + wd->Frames.clear(); + wd->FrameSemaphores.clear(); vkDestroyPipeline(device, wd->Pipeline, allocator); vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); - vkDestroySurfaceKHR(instance, wd->Surface, allocator); - - *wd = ImGui_ImplVulkanH_Window(); + wd->RenderPass = VK_NULL_HANDLE; + wd->Swapchain = VK_NULL_HANDLE; + wd->Width = wd->Height = 0; + wd->FrameIndex = wd->ImageCount = wd->SemaphoreCount = wd->SemaphoreIndex = 0; + //vkDestroySurfaceKHR(instance, wd->Surface, allocator); // v1.92.6 (~2026-01-16): because wd->Surface is user provided we don't attempt to destroy it ourself. } void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator) diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index ca5b4db64..c3fef5105 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [!] 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. +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. @@ -40,9 +41,20 @@ // If you have no idea what this is, leave it alone! //#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES -// Convenience support for Volk +// [Configuration] Convenience support for Volk // (you can also technically use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().) +// (When using Volk from directory outside your include directories list you can specify full path to the volk.h header, +// for example when using Volk from VulkanSDK and using include_directories(${Vulkan_INCLUDE_DIRS})' from 'find_package(Vulkan REQUIRED)') //#define IMGUI_IMPL_VULKAN_USE_VOLK +//#define IMGUI_IMPL_VULKAN_VOLK_FILENAME +//#define IMGUI_IMPL_VULKAN_VOLK_FILENAME // Default +// Reminder: make those changes in your imconfig.h file, not here! + +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#endif #if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES) #define VK_NO_PROTOTYPES @@ -53,7 +65,11 @@ // Vulkan includes #ifdef IMGUI_IMPL_VULKAN_USE_VOLK +#ifdef IMGUI_IMPL_VULKAN_VOLK_FILENAME +#include IMGUI_IMPL_VULKAN_VOLK_FILENAME +#else #include +#endif #else #include #endif @@ -61,39 +77,64 @@ #define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING #endif +// Backend uses a small number of descriptors per font atlas + as many as additional calls done to ImGui_ImplVulkan_AddTexture(). +#define IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE (8) // Minimum per atlas + +// Specify settings to create pipeline and swapchain +struct ImGui_ImplVulkan_PipelineInfo +{ + VkRenderPass RenderPass; // Ignored if using dynamic rendering + uint32_t Subpass; // + VkSampleCountFlagBits MSAASamples = {}; // 0 defaults to VK_SAMPLE_COUNT_1_BIT + ImVector ExtraDynamicStates; // Optional, allows to insert more dynamic states into our VkPipeline +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Optional, valid if .sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR +#endif +}; + // Initialization data, for ImGui_ImplVulkan_Init() -// - VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, -// and must contain a pool size large enough to hold an ImGui VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor. -// - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure. // [Please zero-clear before use!] +// - About descriptor pool: +// - A VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, +// and must contain a pool size large enough to hold a small number of VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptors. +// - As an convenience, by setting DescriptorPoolSize > 0 the backend will create one for you. +// - About dynamic rendering: +// - When using dynamic rendering, set UseDynamicRendering=true + fill PipelineInfoMain.PipelineRenderingCreateInfo structure. struct ImGui_ImplVulkan_InitInfo { + uint32_t ApiVersion; // Fill with API version of Instance, e.g. VK_API_VERSION_1_3 or your value of VkApplicationInfo::apiVersion. May be lower than header version (VK_HEADER_VERSION_COMPLETE) VkInstance Instance; VkPhysicalDevice PhysicalDevice; VkDevice Device; uint32_t QueueFamily; VkQueue Queue; - VkDescriptorPool DescriptorPool; // See requirements in note above - VkRenderPass RenderPass; // Ignored if using dynamic rendering - uint32_t MinImageCount; // >= 2 - uint32_t ImageCount; // >= MinImageCount - VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT + VkDescriptorPool DescriptorPool; // See requirements in note above; ignored if using DescriptorPoolSize > 0 + uint32_t DescriptorPoolSize; // Optional: set to create internal descriptor pool automatically instead of using DescriptorPool. + uint32_t MinImageCount; // >= 2 + uint32_t ImageCount; // >= MinImageCount + VkPipelineCache PipelineCache; // Optional - // (Optional) - VkPipelineCache PipelineCache; - uint32_t Subpass; + // Pipeline + ImGui_ImplVulkan_PipelineInfo PipelineInfoMain; // Infos for Main Viewport (created by app/user) + //VkRenderPass RenderPass; // --> Since 2025/09/26: set 'PipelineInfoMain.RenderPass' instead + //uint32_t Subpass; // --> Since 2025/09/26: set 'PipelineInfoMain.Subpass' instead + //VkSampleCountFlagBits MSAASamples; // --> Since 2025/09/26: set 'PipelineInfoMain.MSAASamples' instead + //VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Since 2025/09/26: set 'PipelineInfoMain.PipelineRenderingCreateInfo' instead // (Optional) Dynamic Rendering - // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3. + // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3 + setup PipelineInfoMain.PipelineRenderingCreateInfo. bool UseDynamicRendering; -#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; -#endif // (Optional) Allocation, Debugging const VkAllocationCallbacks* Allocator; void (*CheckVkResultFn)(VkResult err); - VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory. + VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory. + + // (Optional) Customize default vertex/fragment shaders. + // - if .sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO we use specified structs, otherwise we use defaults. + // - Shader inputs/outputs need to match ours. Code/data pointed to by the structure needs to survive for whole during of backend usage. + VkShaderModuleCreateInfo CustomShaderVertCreateInfo; + VkShaderModuleCreateInfo CustomShaderFragCreateInfo; }; // Follow "Getting Started" link and check examples/ folder to learn about using backends! @@ -101,10 +142,16 @@ IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* 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) +// (Advanced) Use e.g. if you need to recreate pipeline without reinitializing the backend (see #8110, #8111) +// The main window pipeline will be created by ImGui_ImplVulkan_Init() if possible (== RenderPass xor (UseDynamicRendering && PipelineRenderingCreateInfo->sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR)) +// Else, the pipeline can be created, or re-created, using ImGui_ImplVulkan_CreateMainPipeline() before rendering. +IMGUI_IMPL_API void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_PipelineInfo* info); + +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex); + // 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. @@ -113,7 +160,7 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet d // 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); +IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr); // [BETA] Selected render state data shared with callbacks. // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplVulkan_RenderDrawData() call. @@ -127,28 +174,35 @@ struct ImGui_ImplVulkan_RenderState //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers -// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.) //------------------------------------------------------------------------- +// 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. +// WE DO NOT PROVIDE STRONG GUARANTEES OF BACKWARD/FORWARD COMPATIBILITY. // 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, +// Generally we avoid exposing any kind of superfluous high-level helpers in the backends, // 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) +// 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 if you are curious, but +// it is recommended you use your own custom tailored code to do equivalent work. +// +// The ImGui_ImplVulkanH_XXX functions should 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 void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage); +IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, 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 VkPhysicalDevice ImGui_ImplVulkanH_SelectPhysicalDevice(VkInstance instance); +IMGUI_IMPL_API uint32_t ImGui_ImplVulkanH_SelectQueueFamilyIndex(VkPhysicalDevice physical_device); IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode); // Helper structure to hold the data needed by one rendering frame @@ -174,30 +228,48 @@ struct ImGui_ImplVulkanH_FrameSemaphores // (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 SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR - 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; + // Input + bool UseDynamicRendering; + VkSurfaceKHR Surface; // Surface created and destroyed by caller. + VkSurfaceFormatKHR SurfaceFormat; + VkPresentModeKHR PresentMode; + VkAttachmentDescription AttachmentDesc; // RenderPass creation: main attachment description. + VkClearValue ClearValue; // RenderPass creation: clear value when using VK_ATTACHMENT_LOAD_OP_CLEAR. + + // Internal + int Width; // Generally same as passed to ImGui_ImplVulkanH_CreateOrResizeWindow() + int Height; + VkSwapchainKHR Swapchain; + VkRenderPass RenderPass; + VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo + 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 SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR + uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data) + ImVector Frames; + ImVector 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; + + // Parameters to create SwapChain + PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this. + + // Parameters to create RenderPass + AttachmentDesc.format = VK_FORMAT_UNDEFINED; // Will automatically use wd->SurfaceFormat.format. + AttachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT; + AttachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + AttachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + AttachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + AttachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + AttachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + AttachmentDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; } }; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 075f74734..86bb36cc3 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -1,11 +1,14 @@ // dear imgui: Renderer for WebGPU -// This needs to be used along with a Platform Binding (e.g. GLFW) -// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL2, SDL3) +// (Please note that WebGPU is a recent API, may not be supported by all browser, and its ecosystem is generally a mess) // Implemented features: -// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures). + +// Read imgui_impl_wgpu.h about how to use the IMGUI_IMPL_WEBGPU_BACKEND_WGPU or IMGUI_IMPL_WEBGPU_BACKEND_DAWN flags. // 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. @@ -17,6 +20,12 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-25: Added support for WGVK native backend via IMGUI_IMPL_WEBGPU_BACKEND_WGVK define, with SPIRV shaders if WGSL is not available. (#9316, #9246, #9257) +// 2026-03-09: Removed support for Emscripten < 4.0.10. (#9281) +// 2025-10-16: Update to compile with Dawn and Emscripten's 4.0.10+ '--use-port=emdawnwebgpu' ports. (#8381, #8898) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. (#8465) +// 2025-02-26: Recreate image bind groups during render. (#8426, #8046, #7765, #8027) + Update for latest webgpu-native changes. // 2024-10-14: Update Dawn support for change of string usages. (#8082, #8083) // 2024-10-07: Expose selected render state in ImGui_ImplWGPU_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2024-10-07: Changed default texture sampler to Clamp instead of Repeat/Wrap. @@ -40,38 +49,44 @@ // 2021-02-18: Change blending equation to preserve alpha in output buffer. // 2021-01-28: Initial version. -// When targeting native platforms (i.e. NOT emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN -// or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details. -#ifndef __EMSCRIPTEN__ - #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) - #error exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined! - #endif -#else - #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) - #error neither IMGUI_IMPL_WEBGPU_BACKEND_DAWN nor IMGUI_IMPL_WEBGPU_BACKEND_WGPU may be defined if targeting emscripten! - #endif -#endif - #include "imgui.h" + #ifndef IMGUI_DISABLE #include "imgui_impl_wgpu.h" #include -#include +#include + +// One of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details. +#if !defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) && !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) && !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) +#error Exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN, IMGUI_IMPL_WEBGPU_BACKEND_WGPU or IMGUI_IMPL_WEBGPU_BACKEND_WGVK must be defined! +#endif +#if defined(__EMSCRIPTEN__) && defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) +#error Emscripten <4.0.10 with '-sUSE_WEBGPU=1' is not supported anymore. +#endif + +#if defined IMGUI_IMPL_WEBGPU_BACKEND_DAWN || defined IMGUI_IMPL_WEBGPU_BACKEND_WGVK +// Dawn renamed WGPUProgrammableStageDescriptor to WGPUComputeState (see: https://github.com/webgpu-native/webgpu-headers/pull/413) +// Using type alias until WGPU adopts the same naming convention (#8369) +using WGPUProgrammableStageDescriptor = WGPUComputeState; +#endif // Dear ImGui prototypes from imgui_internal.h -extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0); +extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed); #define MEMALIGN(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align (copied from IM_ALIGN() macro). // WebGPU data +struct ImGui_ImplWGPU_Texture +{ + WGPUTexture Texture = nullptr; + WGPUTextureView TextureView = nullptr; +}; + struct RenderResources { - WGPUTexture FontTexture = nullptr; // Font texture - WGPUTextureView FontTextureView = nullptr; // Texture view for font texture - WGPUSampler Sampler = nullptr; // Sampler for the font texture + WGPUSampler Sampler = nullptr; // Sampler for textures WGPUBuffer Uniforms = nullptr; // Shader uniforms WGPUBindGroup CommonBindGroup = nullptr; // Resources bind-group to bind the common resources to pipeline ImGuiStorage ImageBindGroups; // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map) - WGPUBindGroup ImageBindGroup = nullptr; // Default font-resource of Dear ImGui WGPUBindGroupLayout ImageBindGroupLayout = nullptr; // Cache layout used for the image bind group. Avoids allocating unnecessary JS objects when working with WebASM }; @@ -171,6 +186,60 @@ fn main(in: VertexOutput) -> @location(0) vec4 { } )"; +// Same shader as __shader_vert_wgsl[] but compiled as SPIRV. +// 'wgslc -o vert.spv vert.wgsl' + 'binary_to_compressed_c -u8 -nocompress vert.spv' +static const unsigned char __shader_vert_spirv[1996] = +{ + 3,2,35,7,0,3,1,0,1,0,23,0,90,0,0,0,0,0,0,0,17,0,2,0,1,0,0,0,14,0,3,0,0,0,0,0,1,0,0,0,15,0,12,0,0,0,0,0,78,0,0,0,109,97,105,110,0,0,0,0,8,0,0,0,12,0,0,0,13,0,0,0,16,0,0,0,18,0,0,0,19, + 0,0,0,21,0,0,0,71,0,4,0,4,0,0,0,6,0,0,0,16,0,0,0,72,0,5,0,3,0,0,0,0,0,0,0,35,0,0,0,0,0,0,0,71,0,3,0,3,0,0,0,2,0,0,0,71,0,4,0,1,0,0,0,34,0,0,0,0,0,0,0,71,0,4,0,1,0,0,0,33,0,0,0,0,0, + 0,0,71,0,3,0,1,0,0,0,24,0,0,0,71,0,4,0,8,0,0,0,30,0,0,0,0,0,0,0,71,0,4,0,12,0,0,0,30,0,0,0,1,0,0,0,71,0,4,0,13,0,0,0,30,0,0,0,2,0,0,0,71,0,4,0,16,0,0,0,11,0,0,0,0,0,0,0,71,0,4,0,18, + 0,0,0,30,0,0,0,0,0,0,0,71,0,4,0,19,0,0,0,30,0,0,0,1,0,0,0,71,0,4,0,21,0,0,0,11,0,0,0,1,0,0,0,21,0,4,0,6,0,0,0,32,0,0,0,0,0,0,0,23,0,4,0,5,0,0,0,6,0,0,0,4,0,0,0,43,0,4,0,6,0,0,0,7,0, + 0,0,5,0,0,0,28,0,4,0,4,0,0,0,5,0,0,0,7,0,0,0,30,0,3,0,3,0,0,0,4,0,0,0,32,0,4,0,2,0,0,0,2,0,0,0,3,0,0,0,59,0,4,0,2,0,0,0,1,0,0,0,2,0,0,0,22,0,3,0,11,0,0,0,32,0,0,0,23,0,4,0,10,0,0,0, + 11,0,0,0,2,0,0,0,32,0,4,0,9,0,0,0,1,0,0,0,10,0,0,0,59,0,4,0,9,0,0,0,8,0,0,0,1,0,0,0,59,0,4,0,9,0,0,0,12,0,0,0,1,0,0,0,23,0,4,0,15,0,0,0,11,0,0,0,4,0,0,0,32,0,4,0,14,0,0,0,1,0,0,0,15, + 0,0,0,59,0,4,0,14,0,0,0,13,0,0,0,1,0,0,0,32,0,4,0,17,0,0,0,3,0,0,0,15,0,0,0,59,0,4,0,17,0,0,0,16,0,0,0,3,0,0,0,59,0,4,0,17,0,0,0,18,0,0,0,3,0,0,0,32,0,4,0,20,0,0,0,3,0,0,0,10,0,0,0, + 59,0,4,0,20,0,0,0,19,0,0,0,3,0,0,0,32,0,4,0,22,0,0,0,3,0,0,0,11,0,0,0,59,0,4,0,22,0,0,0,21,0,0,0,3,0,0,0,30,0,5,0,24,0,0,0,15,0,0,0,15,0,0,0,10,0,0,0,30,0,5,0,25,0,0,0,10,0,0,0,10, + 0,0,0,15,0,0,0,33,0,4,0,27,0,0,0,24,0,0,0,25,0,0,0,32,0,4,0,30,0,0,0,7,0,0,0,24,0,0,0,46,0,3,0,24,0,0,0,31,0,0,0,32,0,4,0,33,0,0,0,7,0,0,0,15,0,0,0,43,0,4,0,6,0,0,0,34,0,0,0,0,0,0, + 0,24,0,4,0,36,0,0,0,15,0,0,0,4,0,0,0,43,0,4,0,11,0,0,0,40,0,0,0,0,0,0,0,43,0,4,0,11,0,0,0,41,0,0,0,0,0,128,63,43,0,4,0,6,0,0,0,44,0,0,0,1,0,0,0,32,0,4,0,47,0,0,0,7,0,0,0,10,0,0,0,43, + 0,4,0,6,0,0,0,48,0,0,0,2,0,0,0,33,0,4,0,52,0,0,0,36,0,0,0,6,0,0,0,43,0,4,0,6,0,0,0,55,0,0,0,16,0,0,0,32,0,4,0,57,0,0,0,2,0,0,0,5,0,0,0,43,0,4,0,6,0,0,0,66,0,0,0,32,0,0,0,43,0,4,0,6, + 0,0,0,72,0,0,0,48,0,0,0,19,0,2,0,79,0,0,0,33,0,3,0,80,0,0,0,79,0,0,0,54,0,5,0,24,0,0,0,23,0,0,0,0,0,0,0,27,0,0,0,55,0,3,0,25,0,0,0,26,0,0,0,248,0,2,0,28,0,0,0,59,0,5,0,30,0,0,0,29, + 0,0,0,7,0,0,0,31,0,0,0,65,0,5,0,33,0,0,0,32,0,0,0,29,0,0,0,34,0,0,0,57,0,5,0,36,0,0,0,35,0,0,0,37,0,0,0,34,0,0,0,81,0,5,0,10,0,0,0,38,0,0,0,26,0,0,0,0,0,0,0,80,0,6,0,15,0,0,0,39,0, + 0,0,38,0,0,0,40,0,0,0,41,0,0,0,145,0,5,0,15,0,0,0,42,0,0,0,35,0,0,0,39,0,0,0,62,0,4,0,32,0,0,0,42,0,0,0,0,0,0,0,65,0,5,0,33,0,0,0,43,0,0,0,29,0,0,0,44,0,0,0,81,0,5,0,15,0,0,0,45,0, + 0,0,26,0,0,0,2,0,0,0,62,0,4,0,43,0,0,0,45,0,0,0,0,0,0,0,65,0,5,0,47,0,0,0,46,0,0,0,29,0,0,0,48,0,0,0,81,0,5,0,10,0,0,0,49,0,0,0,26,0,0,0,1,0,0,0,62,0,4,0,46,0,0,0,49,0,0,0,0,0,0,0, + 61,0,5,0,24,0,0,0,50,0,0,0,29,0,0,0,0,0,0,0,254,0,2,0,50,0,0,0,56,0,1,0,54,0,5,0,36,0,0,0,37,0,0,0,0,0,0,0,52,0,0,0,55,0,3,0,6,0,0,0,51,0,0,0,248,0,2,0,53,0,0,0,134,0,5,0,6,0,0,0,54, + 0,0,0,51,0,0,0,55,0,0,0,65,0,6,0,57,0,0,0,56,0,0,0,1,0,0,0,34,0,0,0,54,0,0,0,61,0,5,0,5,0,0,0,58,0,0,0,56,0,0,0,0,0,0,0,124,0,4,0,15,0,0,0,59,0,0,0,58,0,0,0,128,0,5,0,6,0,0,0,60,0, + 0,0,55,0,0,0,51,0,0,0,134,0,5,0,6,0,0,0,61,0,0,0,60,0,0,0,55,0,0,0,65,0,6,0,57,0,0,0,62,0,0,0,1,0,0,0,34,0,0,0,61,0,0,0,61,0,5,0,5,0,0,0,63,0,0,0,62,0,0,0,0,0,0,0,124,0,4,0,15,0,0, + 0,64,0,0,0,63,0,0,0,128,0,5,0,6,0,0,0,65,0,0,0,66,0,0,0,51,0,0,0,134,0,5,0,6,0,0,0,67,0,0,0,65,0,0,0,55,0,0,0,65,0,6,0,57,0,0,0,68,0,0,0,1,0,0,0,34,0,0,0,67,0,0,0,61,0,5,0,5,0,0,0, + 69,0,0,0,68,0,0,0,0,0,0,0,124,0,4,0,15,0,0,0,70,0,0,0,69,0,0,0,128,0,5,0,6,0,0,0,71,0,0,0,72,0,0,0,51,0,0,0,134,0,5,0,6,0,0,0,73,0,0,0,71,0,0,0,55,0,0,0,65,0,6,0,57,0,0,0,74,0,0,0, + 1,0,0,0,34,0,0,0,73,0,0,0,61,0,5,0,5,0,0,0,75,0,0,0,74,0,0,0,0,0,0,0,124,0,4,0,15,0,0,0,76,0,0,0,75,0,0,0,80,0,7,0,36,0,0,0,77,0,0,0,59,0,0,0,64,0,0,0,70,0,0,0,76,0,0,0,254,0,2,0,77, + 0,0,0,56,0,1,0,54,0,5,0,79,0,0,0,78,0,0,0,0,0,0,0,80,0,0,0,248,0,2,0,81,0,0,0,61,0,5,0,10,0,0,0,82,0,0,0,8,0,0,0,0,0,0,0,61,0,5,0,10,0,0,0,83,0,0,0,12,0,0,0,0,0,0,0,61,0,5,0,15,0,0, + 0,84,0,0,0,13,0,0,0,0,0,0,0,80,0,6,0,25,0,0,0,85,0,0,0,82,0,0,0,83,0,0,0,84,0,0,0,57,0,5,0,24,0,0,0,86,0,0,0,23,0,0,0,85,0,0,0,81,0,5,0,15,0,0,0,87,0,0,0,86,0,0,0,0,0,0,0,62,0,4,0, + 16,0,0,0,87,0,0,0,0,0,0,0,81,0,5,0,15,0,0,0,88,0,0,0,86,0,0,0,1,0,0,0,62,0,4,0,18,0,0,0,88,0,0,0,0,0,0,0,81,0,5,0,10,0,0,0,89,0,0,0,86,0,0,0,2,0,0,0,62,0,4,0,19,0,0,0,89,0,0,0,0,0, + 0,0,62,0,4,0,21,0,0,0,41,0,0,0,0,0,0,0,253,0,1,0,56,0,1,0, +}; + +// Same shader as __shader_frag_wgsl[] but compiled as SPIRV. +// 'wgslc -o frag.spv frag.wgsl' + 'binary_to_compressed_c -u8 -nocompress frag.spv' +static const unsigned char __shader_frag_spirv[1392] = +{ + 3,2,35,7,0,3,1,0,1,0,23,0,60,0,0,0,0,0,0,0,17,0,2,0,1,0,0,0,11,0,6,0,48,0,0,0,71,76,83,76,46,115,116,100,46,52,53,48,0,0,0,0,14,0,3,0,0,0,0,0,1,0,0,0,15,0,9,0,4,0,0,0,51,0,0,0,109, + 97,105,110,0,0,0,0,15,0,0,0,18,0,0,0,19,0,0,0,22,0,0,0,16,0,3,0,51,0,0,0,7,0,0,0,71,0,4,0,4,0,0,0,6,0,0,0,16,0,0,0,72,0,5,0,3,0,0,0,0,0,0,0,35,0,0,0,0,0,0,0,71,0,3,0,3,0,0,0,2,0,0, + 0,71,0,4,0,1,0,0,0,34,0,0,0,0,0,0,0,71,0,4,0,1,0,0,0,33,0,0,0,0,0,0,0,71,0,3,0,1,0,0,0,24,0,0,0,71,0,4,0,8,0,0,0,34,0,0,0,0,0,0,0,71,0,4,0,8,0,0,0,33,0,0,0,1,0,0,0,71,0,4,0,11,0,0, + 0,34,0,0,0,1,0,0,0,71,0,4,0,11,0,0,0,33,0,0,0,0,0,0,0,71,0,4,0,15,0,0,0,11,0,0,0,15,0,0,0,71,0,4,0,18,0,0,0,30,0,0,0,0,0,0,0,71,0,4,0,19,0,0,0,30,0,0,0,1,0,0,0,71,0,4,0,22,0,0,0,30, + 0,0,0,0,0,0,0,21,0,4,0,6,0,0,0,32,0,0,0,0,0,0,0,23,0,4,0,5,0,0,0,6,0,0,0,4,0,0,0,43,0,4,0,6,0,0,0,7,0,0,0,5,0,0,0,28,0,4,0,4,0,0,0,5,0,0,0,7,0,0,0,30,0,3,0,3,0,0,0,4,0,0,0,32,0,4,0, + 2,0,0,0,2,0,0,0,3,0,0,0,59,0,4,0,2,0,0,0,1,0,0,0,2,0,0,0,26,0,2,0,10,0,0,0,32,0,4,0,9,0,0,0,0,0,0,0,10,0,0,0,59,0,4,0,9,0,0,0,8,0,0,0,0,0,0,0,22,0,3,0,14,0,0,0,32,0,0,0,25,0,9,0,13, + 0,0,0,14,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,32,0,4,0,12,0,0,0,0,0,0,0,13,0,0,0,59,0,4,0,12,0,0,0,11,0,0,0,0,0,0,0,23,0,4,0,17,0,0,0,14,0,0,0,4,0,0,0,32,0,4,0,16, + 0,0,0,1,0,0,0,17,0,0,0,59,0,4,0,16,0,0,0,15,0,0,0,1,0,0,0,59,0,4,0,16,0,0,0,18,0,0,0,1,0,0,0,23,0,4,0,21,0,0,0,14,0,0,0,2,0,0,0,32,0,4,0,20,0,0,0,1,0,0,0,21,0,0,0,59,0,4,0,20,0,0,0, + 19,0,0,0,1,0,0,0,32,0,4,0,23,0,0,0,3,0,0,0,17,0,0,0,59,0,4,0,23,0,0,0,22,0,0,0,3,0,0,0,30,0,5,0,25,0,0,0,17,0,0,0,17,0,0,0,21,0,0,0,33,0,4,0,27,0,0,0,17,0,0,0,25,0,0,0,27,0,3,0,34, + 0,0,0,13,0,0,0,23,0,4,0,38,0,0,0,14,0,0,0,3,0,0,0,32,0,4,0,40,0,0,0,2,0,0,0,5,0,0,0,43,0,4,0,6,0,0,0,41,0,0,0,0,0,0,0,43,0,4,0,6,0,0,0,42,0,0,0,4,0,0,0,19,0,2,0,52,0,0,0,33,0,3,0,53, + 0,0,0,52,0,0,0,54,0,5,0,17,0,0,0,24,0,0,0,0,0,0,0,27,0,0,0,55,0,3,0,25,0,0,0,26,0,0,0,248,0,2,0,28,0,0,0,81,0,5,0,17,0,0,0,29,0,0,0,26,0,0,0,1,0,0,0,61,0,5,0,13,0,0,0,30,0,0,0,11,0, + 0,0,0,0,0,0,61,0,5,0,10,0,0,0,31,0,0,0,8,0,0,0,0,0,0,0,81,0,5,0,21,0,0,0,32,0,0,0,26,0,0,0,2,0,0,0,86,0,5,0,34,0,0,0,33,0,0,0,30,0,0,0,31,0,0,0,87,0,6,0,17,0,0,0,35,0,0,0,33,0,0,0, + 32,0,0,0,0,0,0,0,133,0,5,0,17,0,0,0,36,0,0,0,29,0,0,0,35,0,0,0,79,0,8,0,38,0,0,0,37,0,0,0,36,0,0,0,36,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,65,0,6,0,40,0,0,0,39,0,0,0,1,0,0,0,41,0,0,0,42,0, + 0,0,61,0,5,0,5,0,0,0,43,0,0,0,39,0,0,0,0,0,0,0,81,0,5,0,6,0,0,0,44,0,0,0,43,0,0,0,0,0,0,0,124,0,4,0,14,0,0,0,45,0,0,0,44,0,0,0,80,0,6,0,38,0,0,0,46,0,0,0,45,0,0,0,45,0,0,0,45,0,0,0, + 12,0,7,0,38,0,0,0,47,0,0,0,48,0,0,0,26,0,0,0,37,0,0,0,46,0,0,0,81,0,5,0,14,0,0,0,49,0,0,0,36,0,0,0,3,0,0,0,80,0,5,0,17,0,0,0,50,0,0,0,47,0,0,0,49,0,0,0,254,0,2,0,50,0,0,0,56,0,1,0, + 54,0,5,0,52,0,0,0,51,0,0,0,0,0,0,0,53,0,0,0,248,0,2,0,54,0,0,0,61,0,5,0,17,0,0,0,55,0,0,0,15,0,0,0,0,0,0,0,61,0,5,0,17,0,0,0,56,0,0,0,18,0,0,0,0,0,0,0,61,0,5,0,21,0,0,0,57,0,0,0,19, + 0,0,0,0,0,0,0,80,0,6,0,25,0,0,0,58,0,0,0,55,0,0,0,56,0,0,0,57,0,0,0,57,0,5,0,17,0,0,0,59,0,0,0,24,0,0,0,58,0,0,0,62,0,4,0,22,0,0,0,59,0,0,0,0,0,0,0,253,0,1,0,56,0,1,0, +}; + static void SafeRelease(ImDrawIdx*& res) { if (res) @@ -225,27 +294,11 @@ static void SafeRelease(WGPUShaderModule& res) wgpuShaderModuleRelease(res); res = nullptr; } -static void SafeRelease(WGPUTextureView& res) -{ - if (res) - wgpuTextureViewRelease(res); - res = nullptr; -} -static void SafeRelease(WGPUTexture& res) -{ - if (res) - wgpuTextureRelease(res); - res = nullptr; -} - static void SafeRelease(RenderResources& res) { - SafeRelease(res.FontTexture); - SafeRelease(res.FontTextureView); SafeRelease(res.Sampler); SafeRelease(res.Uniforms); SafeRelease(res.CommonBindGroup); - SafeRelease(res.ImageBindGroup); SafeRelease(res.ImageBindGroupLayout); }; @@ -257,30 +310,40 @@ static void SafeRelease(FrameResources& res) SafeRelease(res.VertexBufferHost); } -static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const char* wgsl_source) +static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModuleWGSL(const char* wgsl_source) { ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); -#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN - WGPUShaderSourceWGSL wgsl_desc = {}; + WGPUShaderSourceWGSL wgsl_desc = {}; wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_desc.code = { wgsl_source, WGPU_STRLEN }; -#else - WGPUShaderModuleWGSLDescriptor wgsl_desc = {}; - wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; - wgsl_desc.code = wgsl_source; -#endif + wgsl_desc.code = { wgsl_source, WGPU_STRLEN }; WGPUShaderModuleDescriptor desc = {}; - desc.nextInChain = reinterpret_cast(&wgsl_desc); + desc.nextInChain = (WGPUChainedStruct*)&wgsl_desc; WGPUProgrammableStageDescriptor stage_desc = {}; stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); -#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN + + stage_desc.entryPoint = { "main", WGPU_STRLEN }; + return stage_desc; +} + +static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModuleSPIRV(const void* spirv_binary, size_t spirv_length) +{ + ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); + + WGPUShaderSourceSPIRV spirv_desc = {}; + spirv_desc.chain.sType = WGPUSType_ShaderSourceSPIRV; + spirv_desc.code = (const uint32_t *)spirv_binary; + spirv_desc.codeSize = ((uint32_t)(spirv_length / 4)); + + WGPUShaderModuleDescriptor desc = {}; + desc.nextInChain = (WGPUChainedStruct*)&spirv_desc; + + WGPUProgrammableStageDescriptor stage_desc = {}; + stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); + stage_desc.entryPoint = { "main", WGPU_STRLEN }; -#else - stage_desc.entryPoint = "main"; -#endif return stage_desc; } @@ -369,9 +432,16 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder // Avoid rendering when minimized 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 || draw_data->CmdListsCount == 0) + if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdLists.Size == 0) return; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplWGPU_UpdateTexture(tex); + // FIXME: Assuming that this only gets called once per frame! // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); @@ -392,10 +462,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder WGPUBufferDescriptor vb_desc = { nullptr, - "Dear ImGui Vertex buffer", -#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN - WGPU_STRLEN, -#endif + { "Dear ImGui Vertex buffer", WGPU_STRLEN, }, WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, MEMALIGN(fr->VertexBufferSize * sizeof(ImDrawVert), 4), false @@ -419,10 +486,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder WGPUBufferDescriptor ib_desc = { nullptr, - "Dear ImGui Index buffer", -#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN - WGPU_STRLEN, -#endif + { "Dear ImGui Index buffer", WGPU_STRLEN, }, WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, MEMALIGN(fr->IndexBufferSize * sizeof(ImDrawIdx), 4), false @@ -437,9 +501,8 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder // Upload vertex/index data into a single contiguous GPU buffer ImDrawVert* vtx_dst = (ImDrawVert*)fr->VertexBufferHost; ImDrawIdx* idx_dst = (ImDrawIdx*)fr->IndexBufferHost; - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); vtx_dst += draw_list->VtxBuffer.Size; @@ -447,8 +510,8 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder } int64_t vb_write_size = MEMALIGN((char*)vtx_dst - (char*)fr->VertexBufferHost, 4); int64_t ib_write_size = MEMALIGN((char*)idx_dst - (char*)fr->IndexBufferHost, 4); - wgpuQueueWriteBuffer(bd->defaultQueue, fr->VertexBuffer, 0, fr->VertexBufferHost, vb_write_size); - wgpuQueueWriteBuffer(bd->defaultQueue, fr->IndexBuffer, 0, fr->IndexBufferHost, ib_write_size); + wgpuQueueWriteBuffer(bd->defaultQueue, fr->VertexBuffer, 0, fr->VertexBufferHost, (size_t)vb_write_size); + wgpuQueueWriteBuffer(bd->defaultQueue, fr->IndexBuffer, 0, fr->IndexBufferHost, (size_t)ib_write_size); // Setup desired render state ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr); @@ -466,9 +529,8 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder int global_idx_offset = 0; ImVec2 clip_scale = draw_data->FramebufferScale; ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* draw_list = draw_data->CmdLists[n]; for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; @@ -485,18 +547,14 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder { // Bind custom texture ImTextureID tex_id = pcmd->GetTexID(); - ImGuiID tex_id_hash = ImHashData(&tex_id, sizeof(tex_id)); - auto bind_group = bd->renderResources.ImageBindGroups.GetVoidPtr(tex_id_hash); - if (bind_group) + ImGuiID tex_id_hash = ImHashData(&tex_id, sizeof(tex_id), 0); + WGPUBindGroup bind_group = (WGPUBindGroup)bd->renderResources.ImageBindGroups.GetVoidPtr(tex_id_hash); + if (!bind_group) { - wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, (WGPUBindGroup)bind_group, 0, nullptr); - } - else - { - WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bd->renderResources.ImageBindGroupLayout, (WGPUTextureView)tex_id); - bd->renderResources.ImageBindGroups.SetVoidPtr(tex_id_hash, image_bind_group); - wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, image_bind_group, 0, nullptr); + bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bd->renderResources.ImageBindGroupLayout, (WGPUTextureView)tex_id); + bd->renderResources.ImageBindGroups.SetVoidPtr(tex_id_hash, bind_group); } + wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, (WGPUBindGroup)bind_group, 0, nullptr); // 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); @@ -518,36 +576,60 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder global_idx_offset += draw_list->IdxBuffer.Size; global_vtx_offset += draw_list->VtxBuffer.Size; } - platform_io.Renderer_RenderState = NULL; + + // Remove all ImageBindGroups + ImGuiStorage& image_bind_groups = bd->renderResources.ImageBindGroups; + for (int i = 0; i < image_bind_groups.Data.Size; i++) + { + WGPUBindGroup bind_group = (WGPUBindGroup)image_bind_groups.Data[i].val_p; + SafeRelease(bind_group); + } + image_bind_groups.Data.resize(0); + + platform_io.Renderer_RenderState = nullptr; } -static void ImGui_ImplWGPU_CreateFontsTexture() +static void ImGui_ImplWGPU_DestroyTexture(ImTextureData* tex) { - // Build texture atlas - ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height, size_pp; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &size_pp); - - // Upload texture to graphics system + if (ImGui_ImplWGPU_Texture* backend_tex = (ImGui_ImplWGPU_Texture*)tex->BackendUserData) { + IM_ASSERT(backend_tex->TextureView == (WGPUTextureView)(intptr_t)tex->TexID); + wgpuTextureViewRelease(backend_tex->TextureView); + wgpuTextureRelease(backend_tex->Texture); + IM_DELETE(backend_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->BackendUserData = nullptr; + } + tex->SetStatus(ImTextureStatus_Destroyed); +} + +void ImGui_ImplWGPU_UpdateTexture(ImTextureData* tex) +{ + ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + ImGui_ImplWGPU_Texture* backend_tex = IM_NEW(ImGui_ImplWGPU_Texture)(); + + // Create texture WGPUTextureDescriptor tex_desc = {}; -#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN - tex_desc.label = { "Dear ImGui Font Texture", WGPU_STRLEN }; -#else - tex_desc.label = "Dear ImGui Font Texture"; -#endif + tex_desc.label = { "Dear ImGui Texture", WGPU_STRLEN }; tex_desc.dimension = WGPUTextureDimension_2D; - tex_desc.size.width = width; - tex_desc.size.height = height; + tex_desc.size.width = tex->Width; + tex_desc.size.height = tex->Height; tex_desc.size.depthOrArrayLayers = 1; tex_desc.sampleCount = 1; tex_desc.format = WGPUTextureFormat_RGBA8Unorm; tex_desc.mipLevelCount = 1; tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding; - bd->renderResources.FontTexture = wgpuDeviceCreateTexture(bd->wgpuDevice, &tex_desc); + backend_tex->Texture = wgpuDeviceCreateTexture(bd->wgpuDevice, &tex_desc); + // Create texture view WGPUTextureViewDescriptor tex_view_desc = {}; tex_view_desc.format = WGPUTextureFormat_RGBA8Unorm; tex_view_desc.dimension = WGPUTextureViewDimension_2D; @@ -556,41 +638,42 @@ static void ImGui_ImplWGPU_CreateFontsTexture() tex_view_desc.baseArrayLayer = 0; tex_view_desc.arrayLayerCount = 1; tex_view_desc.aspect = WGPUTextureAspect_All; - bd->renderResources.FontTextureView = wgpuTextureCreateView(bd->renderResources.FontTexture, &tex_view_desc); + backend_tex->TextureView = wgpuTextureCreateView(backend_tex->Texture, &tex_view_desc); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)backend_tex->TextureView); + tex->BackendUserData = backend_tex; + // We don't set tex->Status to ImTextureStatus_OK to let the code fallthrough below. } - // Upload texture data + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) { - WGPUImageCopyTexture dst_view = {}; - dst_view.texture = bd->renderResources.FontTexture; + ImGui_ImplWGPU_Texture* backend_tex = (ImGui_ImplWGPU_Texture*)tex->BackendUserData; + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + + // We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture. + const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x; + const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y; + const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w; + const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h; + + // Update full texture or selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions. + WGPUTexelCopyTextureInfo dst_view = {}; + dst_view.texture = backend_tex->Texture; dst_view.mipLevel = 0; - dst_view.origin = { 0, 0, 0 }; + dst_view.origin = { (uint32_t)upload_x, (uint32_t)upload_y, 0 }; dst_view.aspect = WGPUTextureAspect_All; - WGPUTextureDataLayout layout = {}; + WGPUTexelCopyBufferLayout layout = {}; layout.offset = 0; - layout.bytesPerRow = width * size_pp; - layout.rowsPerImage = height; - WGPUExtent3D size = { (uint32_t)width, (uint32_t)height, 1 }; - wgpuQueueWriteTexture(bd->defaultQueue, &dst_view, pixels, (uint32_t)(width * size_pp * height), &layout, &size); + layout.bytesPerRow = tex->Width * tex->BytesPerPixel; + layout.rowsPerImage = upload_h; + WGPUExtent3D write_size = { (uint32_t)upload_w, (uint32_t)upload_h, 1 }; + wgpuQueueWriteTexture(bd->defaultQueue, &dst_view, tex->GetPixelsAt(upload_x, upload_y), (uint32_t)(tex->Width * upload_h * tex->BytesPerPixel), &layout, &write_size); + tex->SetStatus(ImTextureStatus_OK); } - - // Create the associated sampler - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) - { - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; - sampler_desc.maxAnisotropy = 1; - bd->renderResources.Sampler = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc); - } - - // Store our identifier - static_assert(sizeof(ImTextureID) >= sizeof(bd->renderResources.FontTexture), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); - io.Fonts->SetTexID((ImTextureID)bd->renderResources.FontTextureView); + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + ImGui_ImplWGPU_DestroyTexture(tex); } static void ImGui_ImplWGPU_CreateUniformBuffer() @@ -599,10 +682,7 @@ static void ImGui_ImplWGPU_CreateUniformBuffer() WGPUBufferDescriptor ub_desc = { nullptr, - "Dear ImGui Uniform buffer", -#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN - WGPU_STRLEN, -#endif + { "Dear ImGui Uniform buffer", WGPU_STRLEN, }, WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, MEMALIGN(sizeof(Uniforms), 16), false @@ -659,16 +739,23 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() graphics_pipeline_desc.layout = wgpuDeviceCreatePipelineLayout(bd->wgpuDevice, &layout_desc); // Create the vertex shader - WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_vert_wgsl); + WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModuleWGSL(__shader_vert_wgsl); + if (!vertex_shader_desc.module) vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModuleSPIRV(__shader_vert_spirv, sizeof(__shader_vert_spirv)); graphics_pipeline_desc.vertex.module = vertex_shader_desc.module; graphics_pipeline_desc.vertex.entryPoint = vertex_shader_desc.entryPoint; // Vertex input configuration WGPUVertexAttribute attribute_desc[] = { +#if defined IMGUI_IMPL_WEBGPU_BACKEND_DAWN || defined IMGUI_IMPL_WEBGPU_BACKEND_WGVK + { nullptr, WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 }, + { nullptr, WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 }, + { nullptr, WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 }, +#else { WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 }, { WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 }, { WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 }, +#endif }; WGPUVertexBufferLayout buffer_layouts[1]; @@ -681,7 +768,8 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() graphics_pipeline_desc.vertex.buffers = buffer_layouts; // Create the pixel shader - WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_frag_wgsl); + WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModuleWGSL(__shader_frag_wgsl); + if (!pixel_shader_desc.module) pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModuleSPIRV(__shader_frag_spirv, sizeof(__shader_frag_spirv)); // Create the blending setup WGPUBlendState blend_state = {}; @@ -708,11 +796,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() // Create depth-stencil State WGPUDepthStencilState depth_stencil_state = {}; depth_stencil_state.format = bd->depthStencilFormat; -#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN depth_stencil_state.depthWriteEnabled = WGPUOptionalBool_False; -#else - depth_stencil_state.depthWriteEnabled = false; -#endif depth_stencil_state.depthCompare = WGPUCompareFunction_Always; depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always; depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep; @@ -728,26 +812,32 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() bd->pipelineState = wgpuDeviceCreateRenderPipeline(bd->wgpuDevice, &graphics_pipeline_desc); - ImGui_ImplWGPU_CreateFontsTexture(); ImGui_ImplWGPU_CreateUniformBuffer(); + // Create sampler + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + WGPUSamplerDescriptor sampler_desc = {}; + sampler_desc.minFilter = WGPUFilterMode_Linear; + sampler_desc.magFilter = WGPUFilterMode_Linear; + sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; + sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; + sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; + sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; + sampler_desc.maxAnisotropy = 1; + bd->renderResources.Sampler = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc); + // Create resource bind group WGPUBindGroupEntry common_bg_entries[] = { { nullptr, 0, bd->renderResources.Uniforms, 0, MEMALIGN(sizeof(Uniforms), 16), 0, 0 }, { nullptr, 1, 0, 0, 0, bd->renderResources.Sampler, 0 }, }; - WGPUBindGroupDescriptor common_bg_descriptor = {}; common_bg_descriptor.layout = bg_layouts[0]; common_bg_descriptor.entryCount = sizeof(common_bg_entries) / sizeof(WGPUBindGroupEntry); common_bg_descriptor.entries = common_bg_entries; bd->renderResources.CommonBindGroup = wgpuDeviceCreateBindGroup(bd->wgpuDevice, &common_bg_descriptor); - - WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bg_layouts[1], bd->renderResources.FontTextureView); - bd->renderResources.ImageBindGroup = image_bind_group; bd->renderResources.ImageBindGroupLayout = bg_layouts[1]; - bd->renderResources.ImageBindGroups.SetVoidPtr(ImHashData(&bd->renderResources.FontTextureView, sizeof(ImTextureID)), image_bind_group); SafeRelease(vertex_shader_desc.module); SafeRelease(pixel_shader_desc.module); @@ -766,8 +856,10 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects() SafeRelease(bd->pipelineState); SafeRelease(bd->renderResources); - ImGuiIO& io = ImGui::GetIO(); - io.Fonts->SetTexID(0); // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplWGPU_DestroyTexture(tex); for (unsigned int i = 0; i < bd->numFramesInFlight; i++) SafeRelease(bd->pFrameResources[i]); @@ -782,16 +874,19 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info) // Setup backend capabilities flags ImGui_ImplWGPU_Data* bd = IM_NEW(ImGui_ImplWGPU_Data)(); io.BackendRendererUserData = (void*)bd; +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) #if defined(__EMSCRIPTEN__) - io.BackendRendererName = "imgui_impl_webgpu_emscripten"; -#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) - io.BackendRendererName = "imgui_impl_webgpu_dawn"; -#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) - io.BackendRendererName = "imgui_impl_webgpu_wgpu"; + io.BackendRendererName = "imgui_impl_wgpu (Dawn, Emscripten)"; // compiled & linked using EMSCRIPTEN with "--use-port=emdawnwebgpu" flag #else - io.BackendRendererName = "imgui_impl_webgpu"; + io.BackendRendererName = "imgui_impl_wgpu (Dawn, Native)"; +#endif +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) + io.BackendRendererName = "imgui_impl_wgpu (WGPU, Native)"; +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) + io.BackendRendererName = "imgui_impl_wgpu (WGVK, Native)"; #endif io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. bd->initInfo = *init_info; bd->wgpuDevice = init_info->Device; @@ -801,13 +896,10 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info) bd->numFramesInFlight = init_info->NumFramesInFlight; bd->frameIndex = UINT_MAX; - bd->renderResources.FontTexture = nullptr; - bd->renderResources.FontTextureView = nullptr; bd->renderResources.Sampler = nullptr; bd->renderResources.Uniforms = nullptr; bd->renderResources.CommonBindGroup = nullptr; bd->renderResources.ImageBindGroups.Data.reserve(100); - bd->renderResources.ImageBindGroup = nullptr; bd->renderResources.ImageBindGroupLayout = nullptr; // Create buffers with a default size (they will later be grown as needed) @@ -831,6 +923,7 @@ void ImGui_ImplWGPU_Shutdown() ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplWGPU_InvalidateDeviceObjects(); delete[] bd->pFrameResources; @@ -842,7 +935,8 @@ void ImGui_ImplWGPU_Shutdown() io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } @@ -850,9 +944,180 @@ void ImGui_ImplWGPU_NewFrame() { ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); if (!bd->pipelineState) - ImGui_ImplWGPU_CreateDeviceObjects(); + if (!ImGui_ImplWGPU_CreateDeviceObjects()) + IM_ASSERT(0 && "ImGui_ImplWGPU_CreateDeviceObjects() failed!"); } +//------------------------------------------------------------------------- +// Internal Helpers +// Those are currently used by our example applications. +//------------------------------------------------------------------------- + +bool ImGui_ImplWGPU_IsSurfaceStatusError(WGPUSurfaceGetCurrentTextureStatus status) +{ +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) + return (status == WGPUSurfaceGetCurrentTextureStatus_Error); +#else + return (status == WGPUSurfaceGetCurrentTextureStatus_OutOfMemory || status == WGPUSurfaceGetCurrentTextureStatus_DeviceLost); +#endif +} + +bool ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(WGPUSurfaceGetCurrentTextureStatus status) +{ +#if defined(__EMSCRIPTEN__) && !defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) + return (status == WGPUSurfaceGetCurrentTextureStatus_Timeout || status == WGPUSurfaceGetCurrentTextureStatus_Outdated || status == WGPUSurfaceGetCurrentTextureStatus_Lost); +#else + return (status == WGPUSurfaceGetCurrentTextureStatus_Timeout || status == WGPUSurfaceGetCurrentTextureStatus_Outdated || status == WGPUSurfaceGetCurrentTextureStatus_Lost || status == WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal); +#endif +} + +// Helpers to obtain a string +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) +const char* ImGui_ImplWGPU_GetErrorTypeName(WGPUErrorType type) +{ + switch (type) + { + case WGPUErrorType_Validation: return "Validation"; + case WGPUErrorType_OutOfMemory: return "OutOfMemory"; + case WGPUErrorType_Unknown: return "Unknown"; + case WGPUErrorType_Internal: return "Internal"; + default: return "Unknown"; + } +} +const char* ImGui_ImplWGPU_GetDeviceLostReasonName(WGPUDeviceLostReason type) +{ + switch (type) + { + case WGPUDeviceLostReason_Unknown: return "Unknown"; + case WGPUDeviceLostReason_Destroyed: return "Destroyed"; + case WGPUDeviceLostReason_CallbackCancelled: return "CallbackCancelled"; + case WGPUDeviceLostReason_FailedCreation: return "FailedCreation"; + default: return "Unknown"; + } +} +#elif !defined(__EMSCRIPTEN__) +const char* ImGui_ImplWGPU_GetLogLevelName(WGPULogLevel level) +{ + switch (level) + { + case WGPULogLevel_Error: return "Error"; + case WGPULogLevel_Warn: return "Warn"; + case WGPULogLevel_Info: return "Info"; + case WGPULogLevel_Debug: return "Debug"; + case WGPULogLevel_Trace: return "Trace"; + default: return "Unknown"; + } +} +#endif + +const char* ImGui_ImplWGPU_GetBackendTypeName(WGPUBackendType type) +{ + switch (type) + { + case WGPUBackendType_WebGPU: return "WebGPU"; + case WGPUBackendType_D3D11: return "D3D11"; + case WGPUBackendType_D3D12: return "D3D12"; + case WGPUBackendType_Metal: return "Metal"; + case WGPUBackendType_Vulkan: return "Vulkan"; + case WGPUBackendType_OpenGL: return "OpenGL"; + case WGPUBackendType_OpenGLES: return "OpenGLES"; + default: return "Unknown"; + } +} + +const char* ImGui_ImplWGPU_GetAdapterTypeName(WGPUAdapterType type) +{ + switch (type) + { + case WGPUAdapterType_DiscreteGPU: return "DiscreteGPU"; + case WGPUAdapterType_IntegratedGPU: return "IntegratedGPU"; + case WGPUAdapterType_CPU: return "CPU"; + default: return "Unknown"; + } +} + +void ImGui_ImplWGPU_DebugPrintAdapterInfo(const WGPUAdapter& adapter) +{ + WGPUAdapterInfo info = {}; + wgpuAdapterGetInfo(adapter, &info); + printf("description: \"%.*s\"\n", (int)info.description.length, info.description.data); + printf("vendor: \"%.*s\", vendorID: %x\n", (int)info.vendor.length, info.vendor.data, info.vendorID); + printf("architecture: \"%.*s\"\n", (int) info.architecture.length, info.architecture.data); + printf("device: \"%.*s\", deviceID: %x\n", (int)info.device.length, info.device.data, info.deviceID); + printf("backendType: \"%s\"\n", ImGui_ImplWGPU_GetBackendTypeName(info.backendType)); + printf("adapterType: \"%s\"\n", ImGui_ImplWGPU_GetAdapterTypeName(info.adapterType)); + wgpuAdapterInfoFreeMembers(info); +} + +#ifndef __EMSCRIPTEN__ + +#if defined(__APPLE__) +// MacOS specific: is necessary to compile with "-x objective-c++" flags +// (e.g. using cmake: set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++") ) +#include +#if TARGET_OS_OSX +#include +#include +#endif +#endif + +WGPUSurface ImGui_ImplWGPU_CreateWGPUSurfaceHelper(ImGui_ImplWGPU_CreateSurfaceInfo* info) +{ + WGPUSurfaceDescriptor surface_descriptor = {}; + WGPUSurface surface = {}; +#if defined(__APPLE__) && TARGET_OS_OSX + if (strcmp(info->System, "cocoa") == 0) + { + IM_ASSERT(info->RawWindow != nullptr); + NSWindow* ns_window = (NSWindow*)info->RawWindow; + id metal_layer = [CAMetalLayer layer]; + [ns_window.contentView setWantsLayer : YES] ; + [ns_window.contentView setLayer : metal_layer] ; + WGPUSurfaceSourceMetalLayer surface_src_metal = {}; + surface_src_metal.chain.sType = WGPUSType_SurfaceSourceMetalLayer; + surface_src_metal.layer = metal_layer; + surface_descriptor.nextInChain = &surface_src_metal.chain; + surface = wgpuInstanceCreateSurface(info->Instance, &surface_descriptor); + } +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) + if (strcmp(info->System, "wayland") == 0) + { + IM_ASSERT(info->RawDisplay != nullptr && info->RawSurface != nullptr); + WGPUSurfaceSourceWaylandSurface surface_src_wayland = {}; + surface_src_wayland.chain.sType = WGPUSType_SurfaceSourceWaylandSurface; + surface_src_wayland.display = info->RawDisplay; + surface_src_wayland.surface = info->RawSurface; + surface_descriptor.nextInChain = &surface_src_wayland.chain; + surface = wgpuInstanceCreateSurface(info->Instance, &surface_descriptor); + } + else if (strcmp(info->System, "x11") == 0) + { + IM_ASSERT(info->RawDisplay != nullptr && info->RawWindow != nullptr); + WGPUSurfaceSourceXlibWindow surface_src_xlib = {}; + surface_src_xlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow; + surface_src_xlib.display = info->RawDisplay; + surface_src_xlib.window = (uint64_t)info->RawWindow; + surface_descriptor.nextInChain = &surface_src_xlib.chain; + surface = wgpuInstanceCreateSurface(info->Instance, &surface_descriptor); + } +#elif defined(_WIN32) + if (strcmp(info->System, "win32") == 0) + { + IM_ASSERT(info->RawWindow != nullptr && info->RawInstance != nullptr); + WGPUSurfaceSourceWindowsHWND surface_src_hwnd = {}; + surface_src_hwnd.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; + surface_src_hwnd.hinstance = info->RawInstance; + surface_src_hwnd.hwnd = info->RawWindow; + surface_descriptor.nextInChain = &surface_src_hwnd.chain; + surface = wgpuInstanceCreateSurface(info->Instance, &surface_descriptor); + } +#else + IM_ASSERT(0 && "Unsupported WebGPU native platform!"); +#endif + return surface; +} +#endif // #ifndef __EMSCRIPTEN__ + //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index f9fb03776..c06c25f8a 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -1,18 +1,24 @@ // dear imgui: Renderer for WebGPU -// This needs to be used along with a Platform Binding (e.g. GLFW) -// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL2, SDL3) +// (Please note that WebGPU is a recent API, may not be supported by all browser, and its ecosystem is generally a mess) -// Important note to dawn and/or wgpu users: when targeting native platforms (i.e. NOT emscripten), -// one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. +// When targeting native platforms: +// - One of IMGUI_IMPL_WEBGPU_BACKEND_DAWN, IMGUI_IMPL_WEBGPU_BACKEND_WGPU or IMGUI_IMPL_WEBGPU_BACKEND_WGVK *must* be provided. +// When targeting Emscripten: +// - We now defaults to IMGUI_IMPL_WEBGPU_BACKEND_DAWN and requires Emscripten 4.0.10+, which correspond to using Emscripten '--use-port=emdawnwebgpu'. +// - Emscripten < 4.0.10 is not supported anymore (old '-sUSE_WEBGPU=1' option). +// - We can still define IMGUI_IMPL_WEBGPU_BACKEND_WGPU to use Emscripten '-s USE_WEBGPU=1' which is marked as obsolete by Emscripten. // Add #define to your imconfig.h file, or as a compilation flag in your build system. -// This requirement will be removed once WebGPU stabilizes and backends converge on a unified interface. +// This requirement may be removed once WebGPU stabilizes and backends converge on a unified interface. //#define IMGUI_IMPL_WEBGPU_BACKEND_DAWN //#define IMGUI_IMPL_WEBGPU_BACKEND_WGPU +//#define IMGUI_IMPL_WEBGPU_BACKEND_WGVK // Implemented features: -// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures). // 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. @@ -26,12 +32,21 @@ #include "imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE +// Setup Emscripten default if not specified. +#if defined(__EMSCRIPTEN__) && !defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) && !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) +#include +#define IMGUI_IMPL_WEBGPU_BACKEND_DAWN +#endif + #include +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) && !defined(__EMSCRIPTEN__) +#include // WGPULogLevel +#endif // Initialization data, for ImGui_ImplWGPU_Init() struct ImGui_ImplWGPU_InitInfo { - WGPUDevice Device; + WGPUDevice Device = nullptr; int NumFramesInFlight = 3; WGPUTextureFormat RenderTargetFormat = WGPUTextureFormat_Undefined; WGPUTextureFormat DepthStencilFormat = WGPUTextureFormat_Undefined; @@ -55,6 +70,9 @@ IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURen IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplWGPU_UpdateTexture(ImTextureData* tex); + // [BETA] Selected render state data shared with callbacks. // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplWGPU_RenderDrawData() call. // (Please open an issue if you feel you need access to more data) @@ -64,4 +82,38 @@ struct ImGui_ImplWGPU_RenderState WGPURenderPassEncoder RenderPassEncoder; }; +//------------------------------------------------------------------------- +// Internal Helpers +// Those are currently used by our example applications. +//------------------------------------------------------------------------- + +// (Optional) Helper to wrap some of the Dawn/WGPU/Emscripten quirks +bool ImGui_ImplWGPU_IsSurfaceStatusError(WGPUSurfaceGetCurrentTextureStatus status); +bool ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(WGPUSurfaceGetCurrentTextureStatus status); // Return whether the texture is suboptimal and may need to be recreated. + +// (Optional) Helper for debugging/logging +void ImGui_ImplWGPU_DebugPrintAdapterInfo(const WGPUAdapter& adapter); +const char* ImGui_ImplWGPU_GetBackendTypeName(WGPUBackendType type); +const char* ImGui_ImplWGPU_GetAdapterTypeName(WGPUAdapterType type); +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) +const char* ImGui_ImplWGPU_GetDeviceLostReasonName(WGPUDeviceLostReason type); +const char* ImGui_ImplWGPU_GetErrorTypeName(WGPUErrorType type); +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) +const char* ImGui_ImplWGPU_GetLogLevelName(WGPULogLevel level); +#endif + +// (Optional) Helper to create a surface on macOS/Wayland/X11/Window +#ifndef __EMSCRIPTEN__ +struct ImGui_ImplWGPU_CreateSurfaceInfo +{ + WGPUInstance Instance; + const char* System; // "cocoa" | "wayland" | "x11" | "win32" + void* RawWindow; // NSWindow* | 0 | Window | HWND + void* RawDisplay; // 0 | wl_display* | Display* | 0 + void* RawSurface; // | wl_surface* | 0 | 0 + void* RawInstance; // 0 | 0 | 0 | HINSTANCE +}; +WGPUSurface ImGui_ImplWGPU_CreateWGPUSurfaceHelper(ImGui_ImplWGPU_CreateSurfaceInfo* info); +#endif // #ifndef __EMSCRIPTEN__ + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 687bc5a18..de92040aa 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -5,8 +5,8 @@ // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. // [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 VK_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Gamepad support. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // 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. @@ -21,6 +21,14 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-01-28: Inputs: Minor optimization not submitting gamepad input if packet number has not changed (reworked from 2025-09-23 attempt). (#9202, #8556) +// 2025-12-03: Inputs: handle WM_IME_CHAR/WM_IME_COMPOSITION messages to support Unicode inputs on MBCS (non-Unicode) Windows. (#9099, #3653, #5961) +// 2025-10-19: Inputs: Revert previous change to allow for io.ClearInputKeys() on focus-out not losing gamepad state. +// 2025-09-23: Inputs: Minor optimization not submitting gamepad input if packet number has not changed. +// 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. +// 2025-04-30: Inputs: Fixed an issue where externally losing mouse capture (due to e.g. focus loss) would fail to claim it again the next subsequent click. (#8594) +// 2025-03-10: When dealing with OEM keys, use scancodes instead of translated keycodes to choose ImGuiKey values. (#7136, #7201, #7206, #7306, #7670, #7672, #8468) +// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support. // 2024-07-08: Inputs: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768) // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. // 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). @@ -120,6 +128,7 @@ struct ImGui_ImplWin32_Data HMODULE XInputDLL; PFN_XInputGetCapabilities XInputGetCapabilities; PFN_XInputGetState XInputGetState; + DWORD XInputPacketNumber; #endif ImGui_ImplWin32_Data() { memset((void*)this, 0, sizeof(*this)); } @@ -174,7 +183,7 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) bd->LastMouseCursor = ImGuiMouseCursor_COUNT; ImGui_ImplWin32_UpdateKeyboardCodePage(io); - // Set platform dependent data in viewport + // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd; IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch @@ -190,7 +199,7 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) "xinput1_2.dll", // DirectX SDK "xinput1_1.dll" // DirectX SDK }; - for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++) + for (int n = 0; n < IM_COUNTOF(xinput_dll_names); n++) if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n])) { bd->XInputDLL = dll; @@ -219,6 +228,7 @@ void ImGui_ImplWin32_Shutdown() ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD @@ -229,6 +239,7 @@ void ImGui_ImplWin32_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + platform_io.ClearPlatformHandlers(); IM_DELETE(bd); } @@ -256,6 +267,8 @@ static bool ImGui_ImplWin32_UpdateMouseCursor(ImGuiIO& io, ImGuiMouseCursor imgu case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; + case ImGuiMouseCursor_Wait: win32_cursor = IDC_WAIT; break; + case ImGuiMouseCursor_Progress: win32_cursor = IDC_APPSTARTING; break; case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break; } ::SetCursor(::LoadCursor(nullptr, win32_cursor)); @@ -331,8 +344,6 @@ static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io) { #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io); - //if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. - // return; // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. @@ -349,6 +360,9 @@ static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io) if (!bd->HasGamepad || bd->XInputGetState == nullptr || bd->XInputGetState(0, &xinput_state) != ERROR_SUCCESS) return; io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + if (bd->XInputPacketNumber != 0 && bd->XInputPacketNumber == xinput_state.dwPacketNumber) + return; + bd->XInputPacketNumber = xinput_state.dwPacketNumber; #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) #define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0); } @@ -428,6 +442,8 @@ ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam) if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED)) return ImGuiKey_KeypadEnter; + const int scancode = (int)LOBYTE(HIWORD(lParam)); + //IMGUI_DEBUG_LOG("scancode %3d, keycode = 0x%02X\n", scancode, wParam); switch (wParam) { case VK_TAB: return ImGuiKey_Tab; @@ -445,17 +461,17 @@ ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam) case VK_SPACE: return ImGuiKey_Space; case VK_RETURN: return ImGuiKey_Enter; case VK_ESCAPE: return ImGuiKey_Escape; - case VK_OEM_7: return ImGuiKey_Apostrophe; + //case VK_OEM_7: return ImGuiKey_Apostrophe; case VK_OEM_COMMA: return ImGuiKey_Comma; - case VK_OEM_MINUS: return ImGuiKey_Minus; + //case VK_OEM_MINUS: return ImGuiKey_Minus; case VK_OEM_PERIOD: return ImGuiKey_Period; - case VK_OEM_2: return ImGuiKey_Slash; - case VK_OEM_1: return ImGuiKey_Semicolon; - case VK_OEM_PLUS: return ImGuiKey_Equal; - case VK_OEM_4: return ImGuiKey_LeftBracket; - case VK_OEM_5: return ImGuiKey_Backslash; - case VK_OEM_6: return ImGuiKey_RightBracket; - case VK_OEM_3: return ImGuiKey_GraveAccent; + //case VK_OEM_2: return ImGuiKey_Slash; + //case VK_OEM_1: return ImGuiKey_Semicolon; + //case VK_OEM_PLUS: return ImGuiKey_Equal; + //case VK_OEM_4: return ImGuiKey_LeftBracket; + //case VK_OEM_5: return ImGuiKey_Backslash; + //case VK_OEM_6: return ImGuiKey_RightBracket; + //case VK_OEM_3: return ImGuiKey_GraveAccent; case VK_CAPITAL: return ImGuiKey_CapsLock; case VK_SCROLL: return ImGuiKey_ScrollLock; case VK_NUMLOCK: return ImGuiKey_NumLock; @@ -547,8 +563,29 @@ ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam) case VK_F24: return ImGuiKey_F24; case VK_BROWSER_BACK: return ImGuiKey_AppBack; case VK_BROWSER_FORWARD: return ImGuiKey_AppForward; - default: return ImGuiKey_None; + default: break; } + + // Fallback to scancode + // https://handmade.network/forums/t/2011-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names + switch (scancode) + { + case 41: return ImGuiKey_GraveAccent; // VK_OEM_8 in EN-UK, VK_OEM_3 in EN-US, VK_OEM_7 in FR, VK_OEM_5 in DE, etc. + case 12: return ImGuiKey_Minus; + case 13: return ImGuiKey_Equal; + case 26: return ImGuiKey_LeftBracket; + case 27: return ImGuiKey_RightBracket; + case 86: return ImGuiKey_Oem102; + case 43: return ImGuiKey_Backslash; + case 39: return ImGuiKey_Semicolon; + case 40: return ImGuiKey_Apostrophe; + case 51: return ImGuiKey_Comma; + case 52: return ImGuiKey_Period; + case 53: return ImGuiKey_Slash; + default: break; + } + + return ImGuiKey_None; } // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. @@ -588,7 +625,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA { // Most backends don't have silent checks like this one, but we need it because WndProc are called early in CreateWindow(). // We silently allow both context or just only backend data to be nullptr. - if (ImGui::GetCurrentContext() == NULL) + if (ImGui::GetCurrentContext() == nullptr) return 0; return ImGui_ImplWin32_WndProcHandlerEx(hwnd, msg, wParam, lParam, ImGui::GetIO()); } @@ -597,7 +634,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, ImGuiIO& io) { ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io); - if (bd == NULL) + if (bd == nullptr) return 0; switch (msg) { @@ -618,8 +655,8 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPA bd->MouseTrackedArea = area; } POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) }; - if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &mouse_pos) == FALSE) // WM_NCMOUSEMOVE are provided in absolute coordinates. - return 0; + if (msg == WM_NCMOUSEMOVE) // WM_NCMOUSEMOVE are absolute coordinates. + ::ScreenToClient(hwnd, &mouse_pos); io.AddMouseSourceEvent(mouse_source); io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y); return 0; @@ -658,7 +695,10 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPA if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; } if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } - if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr) + HWND hwnd_with_capture = ::GetCapture(); + if (bd->MouseButtonsDown != 0 && hwnd_with_capture != hwnd) // Did we externally lost capture? + bd->MouseButtonsDown = 0; + if (bd->MouseButtonsDown == 0 && hwnd_with_capture == nullptr) ::SetCapture(hwnd); // Allow us to read mouse coordinates when dragging mouse outside of our window bounds. bd->MouseButtonsDown |= 1 << button; io.AddMouseSourceEvent(mouse_source); @@ -736,6 +776,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPA case WM_SETFOCUS: case WM_KILLFOCUS: io.AddFocusEvent(msg == WM_SETFOCUS); +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + bd->XInputPacketNumber = 0; // FIXME: Technically, calling io.ClearInputKeys() directly would require this as well. +#endif return 0; case WM_INPUTLANGCHANGE: ImGui_ImplWin32_UpdateKeyboardCodePage(io); @@ -750,10 +793,28 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPA else { wchar_t wch = 0; - ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1); + ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 2, &wch, 1); io.AddInputCharacter(wch); } return 0; + case WM_IME_COMPOSITION: + { + // Handling WM_IME_COMPOSITION ensure that WM_IME_CHAR value is correct even for MBCS apps. + // (see #9099, #3653 and https://stackoverflow.com/questions/77450354 topics) + LRESULT result = ::DefWindowProcW(hwnd, msg, wParam, lParam); + return (lParam & GCS_RESULTSTR) ? 1 : result; + } + case WM_IME_CHAR: + if (::IsWindowUnicode(hwnd) == FALSE) + { + if (::IsDBCSLeadByte(HIBYTE(wParam))) + wParam = (WPARAM)MAKEWORD(HIBYTE(wParam), LOBYTE(wParam)); + wchar_t wch = 0; + ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 2, &wch, 1); + io.AddInputCharacterUTF16(wch); + return 1; + } + return 0; case WM_SETCURSOR: // This is required to restore cursor when transitioning from e.g resize borders to client area. if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor(io, bd->LastMouseCursor)) @@ -777,7 +838,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPA // - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. // - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. // but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, -// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. +// neither of which we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. //--------------------------------------------------------------------------------------------------------- // This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable. // ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically. @@ -790,9 +851,9 @@ static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD) { typedef LONG(WINAPI* PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*, ULONG, ULONGLONG); static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = nullptr; - if (RtlVerifyVersionInfoFn == nullptr) - if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll")) - RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo"); + if (RtlVerifyVersionInfoFn == nullptr) + if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll")) + RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo"); if (RtlVerifyVersionInfoFn == nullptr) return FALSE; @@ -800,10 +861,10 @@ static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD) ULONGLONG conditionMask = 0; versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); versionInfo.dwMajorVersion = major; - versionInfo.dwMinorVersion = minor; - VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); - VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); - return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) == 0) ? TRUE : FALSE; + versionInfo.dwMinorVersion = minor; + VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) == 0) ? TRUE : FALSE; } #define _IsWindowsVistaOrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA @@ -861,16 +922,16 @@ float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) UINT xdpi = 96, ydpi = 96; if (_IsWindows8Point1OrGreater()) { - static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process - static PFN_GetDpiForMonitor GetDpiForMonitorFn = nullptr; - if (GetDpiForMonitorFn == nullptr && shcore_dll != nullptr) + static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process + static PFN_GetDpiForMonitor GetDpiForMonitorFn = nullptr; + if (GetDpiForMonitorFn == nullptr && shcore_dll != nullptr) GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor"); - if (GetDpiForMonitorFn != nullptr) - { - GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + if (GetDpiForMonitorFn != nullptr) + { + GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! - return xdpi / 96.0f; - } + return xdpi / 96.0f; + } } #ifndef NOGDI const HDC dc = ::GetDC(nullptr); diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 0dfd56a80..d2a960059 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -5,8 +5,8 @@ // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. // [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 VK_* values are obsolete since 1.87 and not supported since 1.91.5] -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Gamepad support. +// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // 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. @@ -26,7 +26,7 @@ IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); -// Win32 message handler your application need to call. +// Win32 message handler your application needs to call. // - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper. // - You should COPY the line below into your .cpp code to forward declare the function and then you can call it. // - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE. @@ -40,7 +40,7 @@ extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg // - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. // - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. // but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, -// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. +// neither of which we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness(); IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor diff --git a/backends/sdlgpu3/build_instructions.txt b/backends/sdlgpu3/build_instructions.txt new file mode 100644 index 000000000..79a09200e --- /dev/null +++ b/backends/sdlgpu3/build_instructions.txt @@ -0,0 +1,42 @@ + +Instructions to rebuild imgui_impl_sdlgpu3_shaders.h +(You don't need to copy this folder if you are using the backend as-is) + +1) Compile the raw shader files to SPIRV: + + glslc -o vertex.spv -c shader.vert + glslc -o fragment.spv -c shader.frag + + +2) Build SDL_shadercross (https://github.com/libsdl-org/SDL_shadercross) + + +3-A) Compiling for the Vulkan Driver: + + Nothing to do, you just need the previous vertex.spv/fragment.spv, proceed to step 4 + + +3-B) Compiling for the DirectX 12 Driver: + + ./shadercross vertex.spv -s SPIRV -d DXBC -t vertex -e main -o vertex.dxbc + ./shadercross fragment.spv -s SPIRV -d DXBC -t fragment -e main -o fragment.dxbc + + Proceed to step 4 + + +3-C) Compiling for Metal (On windows you'll need the Metal Developer Tools for Windows, on linux you might use wine, but I never tested it): + + ./shadercross vertex.spv -s SPIRV -d MSL -t vertex -e main -o vertex.metal + ./shadercross fragment.spv -s SPIRV -d MSL -t fragment -e main -o fragment.metal + + xcrun -sdk macosx metal -o vertex.ir -c vertex.metal + xcrun -sdk macosx metal -o fragment.ir -c fragment.metal + xcrun -sdk macosx metallib -o vertex.metallib -c vertex.ir + xcrun -sdk macosx metallib -o fragment.metallib -c fragment.ir + + note: use .metal outputs for updating msl_vertex / msl_fragment variables, and use .metallib outputs for metallib_vertex / metallib_fragment variables + + Proceed to step 4 + + +4) Use a tool like https://notisrac.github.io/FileToCArray/ or misc/fonts/binary_to_compressed_c.cpp in imgui repository to convert the file to a uint8_t array. diff --git a/backends/sdlgpu3/shader.frag b/backends/sdlgpu3/shader.frag new file mode 100644 index 000000000..ab9ce184e --- /dev/null +++ b/backends/sdlgpu3/shader.frag @@ -0,0 +1,15 @@ +#version 450 core +layout(location = 0) out vec4 fColor; + +layout(set=2, 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); +} diff --git a/backends/sdlgpu3/shader.vert b/backends/sdlgpu3/shader.vert new file mode 100644 index 000000000..3a85a9038 --- /dev/null +++ b/backends/sdlgpu3/shader.vert @@ -0,0 +1,24 @@ +#version 450 core +layout(location = 0) in vec2 aPos; +layout(location = 1) in vec2 aUV; +layout(location = 2) in vec4 aColor; + +layout(set=1,binding=0) uniform UBO +{ + vec2 uScale; + vec2 uTranslate; +} ubo; + +layout(location = 0) out struct +{ + vec4 Color; + vec2 UV; +} Out; + +void main() +{ + Out.Color = aColor; + Out.UV = aUV; + gl_Position = vec4(aPos * ubo.uScale + ubo.uTranslate, 0, 1); + gl_Position.y *= -1.0f; +} diff --git a/backends/vulkan/build_instructions.txt b/backends/vulkan/build_instructions.txt new file mode 100644 index 000000000..1f028d96f --- /dev/null +++ b/backends/vulkan/build_instructions.txt @@ -0,0 +1,4 @@ + +Script to rebuild shaders stored inside imgui_impl_vulkan.h +(You don't need to copy this folder if you are using the backend as-is) + diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index de5cd6697..7839764cd 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -1,18 +1,33 @@ _(You may browse this at https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md or view this file with any Markdown viewer)_ -## Dear ImGui: Backends +# Dear ImGui: Backends -### Integrating backends +## Index + +- [Introduction](#introduction) + - [Getting Started](#getting-started) + - [What are Backends?](#what-are-backends) +- [Using standard Backends](#using-standard-backends) +- [Using third-party Backends](#using-third-party-backends) +- [Writing your own Backend](#writing-your-own-backend) + - [Using a custom engine?](#using-a-custom-engine) + - [Platform: Implementing your Platform Backend](#platform-implementing-your-platform-backend) + - [Rendering: Implementing your RenderDrawData function](#rendering-implementing-your-renderdrawdata-function) + - [Rendering: Adding support for `ImGuiBackendFlags_RendererHasTextures` (1.92+)](#rendering-adding-support-for-imguibackendflags_rendererhastextures-192) + +## Introduction + +### Getting Started 💡 The **[Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) wiki guide** has examples of how to integrate Dear ImGui into an existing application.
The [EXAMPLES.MD](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md) documentation may also be worth a read. -### What are backends? +### What are Backends? Dear ImGui is highly portable and only requires a few things to run and render, typically: - Required: providing mouse/keyboard inputs (fed into the `ImGuiIO` structure). - - Required: uploading the font atlas texture into graphics memory. + - Required: creating, updating and destroying textures. - Required: rendering indexed textured triangles with a clipping rectangle. Extra features are opt-in, our backends try to support as many as possible: @@ -34,13 +49,13 @@ and the backends which we are describing here (backends/ folder). - You should be able to write backends for pretty much any platform and any 3D graphics API. e.g. you can get creative and use software rendering or render remotely on a different machine. -### Standard backends +## Using standard Backends **The [backends/](https://github.com/ocornut/imgui/blob/master/backends) folder contains backends for popular platforms/graphics API, which you can use in your application or engine to easily integrate Dear ImGui.** Each backend is typically self-contained in a pair of files: imgui_impl_XXXX.cpp + imgui_impl_XXXX.h. - The 'Platform' backends are in charge of: mouse/keyboard/gamepad inputs, cursor shape, timing, and windowing.
- e.g. Windows ([imgui_impl_win32.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_win32.cpp)), GLFW ([imgui_impl_glfw.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_glfw.cpp)), SDL2 ([imgui_impl_sdl2.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_sdl2.cpp)), etc. + e.g. Windows ([imgui_impl_win32.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_win32.cpp)), SDL3 ([imgui_impl_sdl3.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_sdl3.cpp)), GLFW ([imgui_impl_glfw.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_glfw.cpp)), etc. - The 'Renderer' backends are in charge of: creating atlas texture, and rendering imgui draw data.
e.g. DirectX11 ([imgui_impl_dx11.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp)), OpenGL/WebGL ([imgui_impl_opengl3.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_opengl3.cpp)), Vulkan ([imgui_impl_vulkan.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.cpp)), etc. @@ -53,44 +68,42 @@ For example, the [example_win32_directx11](https://github.com/ocornut/imgui/tree **Once Dear ImGui is setup and running, run and refer to `ImGui::ShowDemoWindow()` in imgui_demo.cpp for usage of the end-user API.** -### List of backends +### List of standard Backends In the [backends/](https://github.com/ocornut/imgui/blob/master/backends) folder: List of Platforms Backends: - imgui_impl_android.cpp ; Android native app API - imgui_impl_glfw.cpp ; GLFW (Windows, macOS, Linux, etc.) http://www.glfw.org/ - imgui_impl_osx.mm ; macOS native API (not as feature complete as glfw/sdl backends) - imgui_impl_sdl2.cpp ; SDL2 (Windows, macOS, Linux, iOS, Android) https://www.libsdl.org - imgui_impl_sdl3.cpp ; SDL3 (Windows, macOS, Linux, iOS, Android) https://www.libsdl.org (*EXPERIMENTAL UNTIL SDL3 IS RELEASED*) - imgui_impl_win32.cpp ; Win32 native API (Windows) - imgui_impl_glut.cpp ; GLUT/FreeGLUT (this is prehistoric software and absolutely not recommended today!) + imgui_impl_android.cpp ; Android native app API + imgui_impl_glfw.cpp ; GLFW (Windows, macOS, Linux, etc.) http://www.glfw.org/ + imgui_impl_osx.mm ; macOS native API (not as feature complete as glfw/sdl backends) + imgui_impl_sdl2.cpp ; SDL2 (Windows, macOS, Linux, iOS, Android) https://www.libsdl.org + imgui_impl_sdl3.cpp ; SDL3 (Windows, macOS, Linux, iOS, Android) https://www.libsdl.org + imgui_impl_win32.cpp ; Win32 native API (Windows) + imgui_impl_glut.cpp ; GLUT/FreeGLUT (this is prehistoric software and absolutely not recommended today!) List of Renderer Backends: - imgui_impl_dx9.cpp ; DirectX9 - imgui_impl_dx10.cpp ; DirectX10 - imgui_impl_dx11.cpp ; DirectX11 - imgui_impl_dx12.cpp ; DirectX12 - imgui_impl_metal.mm ; Metal (with ObjC) - imgui_impl_opengl2.cpp ; OpenGL 2 (legacy, fixed pipeline <- don't use with modern OpenGL context) - imgui_impl_opengl3.cpp ; OpenGL 3/4, OpenGL ES 2, OpenGL ES 3 (modern programmable pipeline) + imgui_impl_dx9.cpp ; DirectX9 + imgui_impl_dx10.cpp ; DirectX10 + imgui_impl_dx11.cpp ; DirectX11 + imgui_impl_dx12.cpp ; DirectX12 + imgui_impl_metal.mm ; Metal (ObjC or C++) + imgui_impl_opengl2.cpp ; OpenGL 2 (legacy fixed pipeline. Don't use with modern OpenGL code!) + imgui_impl_opengl3.cpp ; OpenGL 3/4, OpenGL ES 2/3, WebGL + imgui_impl_sdlgpu3.cpp ; SDL_GPU (portable 3D graphics API of SDL3) imgui_impl_sdlrenderer2.cpp ; SDL_Renderer (optional component of SDL2 available from SDL 2.0.18+) - imgui_impl_sdlrenderer3.cpp ; SDL_Renderer (optional component of SDL3 available from SDL 3.0.0+) - imgui_impl_vulkan.cpp ; Vulkan - imgui_impl_wgpu.cpp ; WebGPU (web and desktop) + imgui_impl_sdlrenderer3.cpp ; SDL_Renderer (optional component of SDL3. Prefer using SDL_GPU!). + imgui_impl_vulkan.cpp ; Vulkan + imgui_impl_wgpu.cpp ; WebGPU (web + desktop) List of high-level Frameworks Backends (combining Platform + Renderer): imgui_impl_allegro5.cpp + imgui_impl_null.cpp Emscripten is also supported! -The SDL+GL, GLFW+GL and GLFW+WebGPU examples are all ready to build and run with Emscripten. - -### Backends for third-party frameworks, graphics API or other languages - -See https://github.com/ocornut/imgui/wiki/Bindings for the full list (e.g. Adventure Game Studio, Cinder, Cocos2d-x, Game Maker Studio2, Godot, LÖVE+LUA, Magnum, Monogame, Ogre, openFrameworks, OpenSceneGraph, SFML, Sokol, Unity, Unreal Engine and many others). +The SDL2+GL, SDL3+GL, GLFW+GL and GLFW+WebGPU examples are all ready to build and run with Emscripten. ### Recommended Backends @@ -98,18 +111,32 @@ If you are not sure which backend to use, the recommended platform/frameworks fo |Library |Website |Backend |Note | |--------|--------|--------|-----| -| GLFW | https://github.com/glfw/glfw | imgui_impl_glfw.cpp | | +| SDL3 | https://www.libsdl.org | imgui_impl_sdl3.cpp | Recommended | | SDL2 | https://www.libsdl.org | imgui_impl_sdl2.cpp | | +| GLFW | https://github.com/glfw/glfw | imgui_impl_glfw.cpp | | | Sokol | https://github.com/floooh/sokol | [util/sokol_imgui.h](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h) | Lower-level than GLFW/SDL | +If your application runs on Windows or if you are using multi-viewport, the win32 backend handles some details a little better than other backends. + +## Using third-party Backends + +See https://github.com/ocornut/imgui/wiki/Bindings for the full list (e.g. Adventure Game Studio, Cinder, Cocos2d-x, Game Maker Studio2, Godot, LÖVE+LUA, Magnum, Monogame, Ogre, openFrameworks, OpenSceneGraph, SFML, Sokol, Unity, Unreal Engine and many others). + +## Writing your own Backend ### Using a custom engine? You will likely be tempted to start by rewrite your own backend using your own custom/high-level facilities...
Think twice! -If you are new to Dear ImGui, first try using the existing backends as-is. +TL;DR; +- Writing your own Renderer Backend is easy. +- Writing your own Platform Backend is harder and you are more likely to introduce bugs. +- **It is unlikely you will add value to your project by creating your own backend.** + +**Consider using the existing backends as-is**. You will save lots of time integrating the library. +Standard backends are battle-tested and handle subtleties that you are likely to implement incorrectly. You can LATER decide to rewrite yourself a custom backend if you really need to. In most situations, custom backends have fewer features and more bugs than the standard backends we provide. If you want portability, you can use multiple backends and choose between them either at compile time @@ -131,16 +158,221 @@ Suggestion: try using a non-portable backend first (e.g. win32 + underlying grap your desktop builds working first. This will get you running faster and get your acquainted with how Dear ImGui works and is setup. You can then rewrite a custom backend using your own engine API... -Generally: -It is unlikely you will add value to your project by creating your own backend. - Also: The [multi-viewports feature](https://github.com/ocornut/imgui/wiki/Multi-Viewports) of the 'docking' branch allows Dear ImGui windows to be seamlessly detached from the main application window. This is achieved using an extra layer to the Platform and Renderer backends, which allows Dear ImGui to communicate platform-specific requests such as: "create an additional OS window", "create a render context", "get the OS position of this -window" etc. See 'ImGuiPlatformIO' for details. +window", but some things are more difficult "find OS window under mouse position BUT with some windows marked as passthrough". See 'ImGuiPlatformIO' for details. Supporting the multi-viewports feature correctly using 100% of your own abstractions is more difficult than supporting single-viewport. If you decide to use unmodified imgui_impl_XXXX.cpp files, you can automatically benefit from improvements and fixes related to viewports and platform windows without extra work on your side. + +### Platform: Implementing your Platform Backend + +The Platform backends in impl_impl_XXX.cpp files contain many implementations. + +**In your `ImGui_ImplXXX_Init()` function:** +- You can allocate your backend data and use `io.BackendPlatformUserData` to store/retrieve it later. +- Set `io.BackendPlatformName` to a name `"imgui_impl_xxxx"` which will be available in e.g. About box. +- Set `io.BackendPlatformUserData` to your backend data. +- Set `io.BackendFlags` with supported optional features: + - `ImGuiBackendFlags_HasGamepad`: supports gamepad and currently has one connected. + - `ImGuiBackendFlags_HasMouseCursors`: supports honoring GetMouseCursor() value to change the OS cursor shape. + - `ImGuiBackendFlags_HasSetMousePos`: supports io.WantSetMousePos requests to reposition the OS mouse position (only used if io.ConfigNavMoveSetMousePos is set). + - `ImGuiBackendFlags_PlatformHasViewports` supports multiple viewports. (multi-viewports only) + - `ImGuiBackendFlags_HasMouseHoveredViewport` supports calling io.AddMouseViewportEvent() with the viewport under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag. If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under mouse position, as it doesn't know about foreign windows. (multi-viewports only) + +**In your `ImGui_ImplXXX_NewFrame()` function:** +- Set `io.DeltaTime` to the time elapsed (in seconds) since last frame. +- Set `io.DisplaySize` to your window size. +- Set `io.DisplayFrameBufferSize` to your window pixel density (macOS/iOS only). +- Update mouse cursor shape is supported. + +**In your `ImGui_ImplXXX_NewFrame()` function or event handlers:** +- **Mouse Support** + - Use `io.AddMousePosEvent()`, `io.AddMouseButtonEvent()`, `io.AddMouseWheelEvent()` to pass mouse events. + - Use `io.AddMouseSourceEvent()` if you are able to distinguish Mouse from TouchScreen from Pen inputs. TouchScreen and Pen inputs requires different logic for some Dear ImGui features. + - Use `io.AddMouseViewportEvent()` to specify which viewport/OS window is being hovered by the mouse. Read instructions carefully as this is not as simple as it seems! (multi-viewports only) +- **Keyboard Support** + - Use `io.AddKeyEvent()` to pass key events. + - Use `io.AddInputCharacter()` to pass text/character events. +- **Gamepad Support** + - Use `io.AddKeyEvent()` and `io.AddKeyAnalogEvent()` to pass gamepad events, using `ImGuiKey_GamepadXXX` values. +- **Miscellaneous** + - Clipboard Support: setup `Platform_GetClipboardTextFn()`, `Platform_SetClipboardTextFn()` handlers in `ImGuiPlatformIO`. + - Open in Shell support: setup `Platform_OpenInShellFn()` handler in `ImGuiPlatformIO`. + - IME Support: setup `Platform_SetImeDataFn()` handler in `ImGuiPlatformIO`. + - Use `io.AddFocusEvent()` to notify when application window gets focused/unfocused. +- **Multi-viewport Support** + - Update monitor list if supported. + - Setup all required handlers in `ImGuiPlatformIO` to create/destroy/move/resize/title/focus/etc. windows. + +### Rendering: Implementing your RenderDrawData function + +Note: set `ImGuiBackendFlags_RendererHasVtxOffset` to signify your backend can handle rendering with a vertex offset (`ImDrawCmd::VtxOffset` field). +Otherwise, rendering will be limited to 64K vertices per window, which may be limiting for advanced plot. +As an alternative, you may also use `#define ImDrawIdx unsigned int` in your `imconfig.h` file to support 32-bit indices. + +```cpp +void MyImGuiBackend_RenderDrawData(ImDrawData* draw_data) +{ + // TODO: Update textures. + // - Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // - This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates. + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + MyImGuiBackend_UpdateTexture(tex); + + // TODO: Setup render state: + // - Alpha-blending enabled + // - No backface culling + // - No depth testing, no depth writing + // - Scissor enabled + MyEngineSetupenderState(); + + // TODO: Setup texture sampling state + // - Sample with bilinear filtering (NOT point/nearest filtering). + // - Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering. + + // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize + + // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize + + // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. + + // Render command lists + ImVec2 clip_off = draw_data->DisplayPos; + ImVec2 clip_scale = draw_data->FramebufferScale; + for (const ImDrawList* draw_list : draw_data->CmdLists) + { + const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + MyEngineSetupenderState(); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + // - Clipping coordinates are provided in imgui coordinates space: + // - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size + // - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values. + // - In the interest of supporting multi-viewport applications (see 'docking' branch on github), + // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. + // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) + 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; + + // We are using scissoring to clip some objects. All low-level graphics API should support it. + // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches + // (some elements visible outside their bounds) but you can fix that once everything else works! + MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y); + + // The texture for the draw call is specified by pcmd->GetTexID(). + // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. + MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); + + // Render 'pcmd->ElemCount/3' indexed triangles. + // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices. + MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset); + } + } + } +} +``` + +### Rendering: Adding support for `ImGuiBackendFlags_RendererHasTextures` (1.92+) + +Version [1.92.0](https://github.com/ocornut/imgui/releases/tag/v1.92.0) (June 2025), added texture support in Rendering Backends, which is the backbone for supporting dynamic font scaling among other things. + +**In order to move forward and take advantage of all new features, support for `ImGuiBackendFlags_RendererHasTextures` will likely be REQUIRED for all backends before June 2026.** + +`ImFontAtlas` functions such as `Build()`, `GetTexDataAsRGBA32()`, `GetTexDataAsAlpha8()`, `SetTexID()`, `IsBuilt()` were obsoleted in favor if iterating a `Textures[]` array and updating their state when requested by Dear ImGui. + +**TD;DR: List of commits which added support for `ImGuiBackendFlags_RendererHasTextures` in standard backends:** + +- Allegro5: [ee8941e](https://github.com/ocornut/imgui/commit/ee8941e) (+35 lines) +- DirectX9: [75efba7](https://github.com/ocornut/imgui/commit/75efba7) (+48 lines) +- DirectX10: [2d2b1bc](https://github.com/ocornut/imgui/commit/2d2b1bc) (+40 lines) +- DirectX11: [372fd27](https://github.com/ocornut/imgui/commit/372fd27) (+40 lines) +- DirectX12: [eefe5d5](https://github.com/ocornut/imgui/commit/eefe5d5) (+87 lines) +- Metal: [26c017d](https://github.com/ocornut/imgui/commit/26c017d) (+55 lines) +- OpenGL Legacy: [0430c55](https://github.com/ocornut/imgui/commit/0430c55) (+25 lines) +- OpenGL3/WebGL/ES: [dbb91a5](https://github.com/ocornut/imgui/commit/dbb91a5) (+47 lines) +- SDL_Renderer2: [9fa65cd](https://github.com/ocornut/imgui/commit/9fa65cd) (+20 lines) +- SDL_Renderer3: [e538883](https://github.com/ocornut/imgui/commit/e538883) (+19 lines) +- SDL_GPU: [16fe666](https://github.com/ocornut/imgui/commit/16fe666) (+41 lines) +- Vulkan: [abe294b](https://github.com/ocornut/imgui/commit/abe294b) (+33 lines) +- WGPU: [571dae9](https://github.com/ocornut/imgui/commit/571dae9) (+30 lines) + +**Instructions:** + +- Set `ImGuiBackendFlags_RendererHasTextures` to signify your backend can handle the feature. +- During rendering, e.g. in your RenderDrawData function, iterate `ImDrawData->Textures` array and process all textures. +- During shutdown, iterate the `ImGui::GetPlatformIO().Textures` and destroy all textures. +- (Both arrays are `ImVector`. They are only in different location because: to allow advanced users to perform multi-threaded rendering, we store a pointer to the texture list in ImDrawData, with the aim that multi-threaded rendering users replace it with their own pointer.) + +Pseudo-code for processing a texture: +```cpp +if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + MyImGuiBackend_UpdateTexture(tex); +``` +```cpp +void MyImGuiBackend_UpdateTexture(ImTextureData* tex) +{ + if (tex->Status == ImTextureStatus_WantCreate) + { + // Create texture based on tex->Width, tex->Height. + // - Most backends only support tex->Format == ImTextureFormat_RGBA32. + // - Backends for particularly memory constrained platforms may support tex->Format == ImTextureFormat_Alpha8. + + // Upload all texture pixels + // - Read from our CPU-side copy of the texture and copy to your graphics API. + // - Use tex->Width, tex->Height, tex->GetPixels(), tex->GetPixelsAt(), tex->GetPitch() as needed. + + // Store your data, and acknowledge creation. + tex->SetTexID(xxxx); // Specify backend-specific ImTextureID identifier which will be stored in ImDrawCmd. + tex->SetStatus(ImTextureStatus_OK); + tex->BackendUserData = xxxx; // Store more backend data if needed (most backend allocate a small texture to store data in there) + } + if (tex->Status == ImTextureStatus_WantUpdates) + { + // Upload a rectangle of pixels to the existing texture + // - We only ever write to textures regions which have never been used before! + // - Use tex->TexID or tex->BackendUserData to retrieve your stored data. + // - Use tex->UpdateRect.x/y, tex->UpdateRect.w/h to obtain the block position and size. + // - Use tex->Updates[] to obtain individual sub-regions within tex->UpdateRect. Not recommended. + // - Read from our CPU-side copy of the texture and copy to your graphics API. + // - Use tex->Width, tex->Height, tex->GetPixels(), tex->GetPixelsAt(), tex->GetPitch() as needed. + + // Acknowledge update + tex->SetStatus(ImTextureStatus_OK); + } + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + { + // If you use staged rendering and have in-flight renders, changed tex->UnusedFrames > 0 check to higher count as needed e.g. > 2 + + // Destroy texture + // - Use tex->TexID or tex->BackendUserData to retrieve your stored data. + // - Destroy texture in your graphics API. + + // Acknowledge destruction + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); + } +} +``` +Refer to "List of commits which added support for `ImGuiBackendFlags_RendererHasTextures` in standard backends" above for concrete examples of this. diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5a49416e2..c82bfd768 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,9 +36,1674 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.91.6 WIP (In Progress) + VERSION 1.92.7 WIP (In Progress) ----------------------------------------------------------------------- +Breaking Changes: + + - Separator(): fixed a legacy quirk where Separator() was submitting a zero-height + item for layout purpose, even though it draws a 1-pixel separator. + The fix could affect code e.g. computing height from multiple widgets in order to + allocate vertical space for a footer or multi-line status bar. (#2657, #9263) + The "Console" example had such a bug: + float footer_height = style.ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + BeginChild("ScrollingRegion", { 0, -footer_height }); + Should be: + float footer_height = style.ItemSpacing.y + style.SeparatorSize + ImGui::GetFrameHeightWithSpacing(); + BeginChild("ScrollingRegion", { 0, -footer_height }); + When such idiom was used and assuming zero-height Separator, it is likely that + in 1.92.7 the resulting window will have unexpected 1 pixel scrolling range. + - Multi-Select: renamed ImGuiMultiSelectFlags_SelectOnClick to ImGuiMultiSelectFlags_SelectOnAuto. + Kept inline redirection enum (will obsolete). + - Combo(), ListBox(): commented out legacy signatures which were obsoleted in 1.90 + (Nov 2023), when the getter callback type was changed from: + getter type: bool (*getter)(void* user_data, int idx, const char** out_text) + To: + getter type: const char* (*getter)(void* user_data, int idx) + +Other Changes: + +- TreeNode: + - Moved TreeNodeGetOpen() helper to public API. I was hesitant to make this public + because I intend to provide a more generic and feature-full version, but in the meanwhile + this will do. (#3823, #9251, #7553, #6754, #5423, #2958, #2079, #1947, #1131, #722) + - In 'Demo->Property Editor' demonstrate a way to perform tree clipping by fast-forwarding + through non-visible chunks. (#3823, #9251, #6990, #6042) + Using SetNextItemStorageID() + TreeNodeGetOpen() makes this notably easier than + it was prior to 1.91. +- InputText: + - Shift+Enter in multi-line editor always adds a new line, regardless of + ImGuiInputTextFlags_CtrlEnterForNewLine being set or not. (#9239) + - Reworked io.ConfigInputTextEnterKeepActive mode so that pressing Enter will + deactivate/reactivate the item in order for e.g. IsItemDeactivatedAfterEdit() + signals to be emitted the same way regardless of that setting. (#9001, #9115) + - Fixed a glitch when using ImGuiInputTextFlags_ElideLeft where the local x offset + would be incorrect during the deactivation frame. (#9298) + - Fixed a crash introduced in 1.92.6 when handling ImGuiInputTextFlags_CallbackResize + in certain situations. (#9174) + - Fixed selection highlight Y1 offset being very slightly off (since 1.92.3). (#9311) [@v-ein] + - InputTextMultiline: fixed an issue introduced in 1.92.3 where line count calculated + for vertical scrollbar range would be +1 when the widget is inactive, word-wrap is + disabled and the text buffer ends with '\n'. Fixed a similar issue related to clipping + large amount of text. + - InputTextMultiline: avoid going through reactivation code and fixed losing revert value + when activating scrollbar. + - InputTextMultiline: fixed an issue where edit buffer wouldn't be reapplied to back + buffer on the IsItemDeactivatedAfterEdit() frame. This could create issues when + using the idiom of not applying edits before IsItemDeactivatedAfterEdit(). + (#9308, #8915, #8273) +- Style: + - Border sizes are now scaled (and rounded) by ScaleAllSizes(). + - When using large values with ScallAllSizes(), the following items thickness + are scaled to integer amounts: + - InputText Caret/cursor thickness. (#7031) + - CloseButton() thickness. + - TextLink() underline thickness. + - ColorButton() border thickness. + - Separator() thickness, via scaling newly added style.SeparatorSize. (#2657, #9263) +- Nav: + - Changed Gamepad mapping for "Activate with Text Input" action: (#8803, #787) + - Previously: press North button (PS4/PS5 triangle, Switch X, Xbox Y). + - Now: long press (hold) Activate button (PS4/PS5 cross, Switch B, Xbox A) for ~0.60 secs. + This is rarely used, somehow easier to discover, and frees a button for other uses. + See updated Gamepad Control Sheets: https://www.dearimgui.com/controls_sheets + - Short Gamepad Activation press on InputText() always activate with Text Input mode. + - Popups: Shift+F10 or Menu key can now open popups menus when using + BeginPopupContextItem(), BeginPopupContextWindow() or OpenPopupOnItemClick(). + (#8803, #9270) [@exelix11, @ocornut] + - Popups: pressing North button (PS4/PS5 triangle, SwitchX, Xbox Y) also open popups menus. +- Multi-Select: + - Added ImGuiMultiSelectFlags_SelectOnClickAlways mode (rarely used). + This prevents Drag and Drop of multiple items, but it allows to start a new Box-Selection + from inside an existing selection (Excel style). (#9307, #1861) +- Clipper: + - Clear `DisplayStart`/`DisplayEnd` fields when `Step()` returns false. + - Added `UserIndex` helper storage. This is solely a convenience for cases where + you may want to carry an index around. + - Always pulls current context on `ImGuiListClipper::Begin()`, consistent with public + API design, and avoids issues with clipper instances outliving contexts. (#9324, #5856) +- Scrollbar: + - Implemented a custom tweak to extend hit-testing bounding box when window is sitting + at the edge of a viewport (e.g. fullscreen or docked window), so that e.g. mouse the + mouse at the extreme of the screen will reach the scrollbar. (#9276) + - Fixed an issue which could lead initial click to move the current scroll by a pixel. +- Button: + - Moved ImGuiButtonFlags_AllowOverlap from imgui_internal.h to imgui.h, + as a convenience for when using e.g. InvisibleButton(). +- Focus: fixed fallback "Debug" window temporarily taking focus and setting io.WantCaptureKeyboard + for one frame on e.g. application boot if no other windows are submitted. (#9243) +- DrawList: + - PathArcTo(): fixed erroneous segment count for pathologically small arcs on large + circles. (#9331, #9313) [@thedmd, @epajarre] +- Memory: + - Discard/GC of ImDrawList buffers for unused windows favor restoring them to + ~Size*1.05 instead of Capacity when awakening again. Facilitate releasing ImDrawList + buffers after unusual usage spike. (#9303) + - Fixed GetForegroundDrawList()/GetBackgroundDrawList() per-viewport buffers not being + collected when unused for io.ConfigMemoryCompactTimer amount of time. (#9303) +- Demo: fixed IMGUI_DEMO_MARKER locations for examples applets. (#9261, #3689) [@pthom] +- Internals: + - ButtonBehavior: fixed internal/low-level ImGuiButtonFlags_PressedOnRelease + (as well as equivalent ImGuiSelectableFlags_SelectOnRelease for Selectable) from + not taking current active id. ImGuiButtonFlags_NoHoldingActiveID allows that. + This was pretty sure only used internally by MenuItem(). +- Backends: + - DirectX9, OpenGL2, OpenGL3, Metal, SDLGPU3, SDLRenderer2, SDLRenderer3: fixed easy-to-fix + issues in code assuming ImTextureID_Invalid is always defined to 0. (#9295, #9310) + - GLFW: mouse cursor is properly restored if changed by user app/code while using + glfwSetInputMode(..., GLFW_CURSOR_DISABLED) or ImGuiConfigFlags_NoMouseCursorChange. + Amend optimization done in 1.92.6. + - SDLGPU3: removed unnecessary call to SDL_WaitForGPUIdle when releasing + vertex/index buffers. (#9262) [@jaenis] + - WebGPU: fixed version check for Emscripten 5.0.0+. + - WebGPU: removed support for Emscripten <4.0.10. (#9281) [@ypujante] + - WebGPU: added support for WGVK native backend via IMGUI_IMPL_WEBGPU_BACKEND_WGVK, + using SPIRV shaders if WGSL is not available. (#9316, #9246, #9257) [@r-lyeh] + (WGVK is a lightweight alternative to Dawn or WGPU for native applications, + which is easier to build/setup, see: https://github.com/manuel5975p/WGVK) +- Examples: + - Emscripten: added `tabindex=-1` to canvas in our shell_minimal.htm. Without it, + the canvas was not focusable in the DOM, which in turn make some backends + (e.g. pongasoft/emscripten-glfw) not receive focus loss events. (#9259) [@pthom] + - Emscripten: fixed minor rendering issues with our HTML shell. (#9281) [@ypujante] + - hidden small blue outline when canvas is focused on Chrome. + - hidden scrollbar in Firefox. + - Vulkan: added ImGui_ImplVulkan_PipelineInfo::ExtraDynamicStates[] to allow specifying + extra dynamic states to add when creating the VkPipeline. (#9211) [@DziubanMaciej] + - Vulkan: ImGui_ImplVulkan_AddTexture() skips updating descriptor_set if failing + to allocate one. (#8677) [@micb25] + - WebGPU: fixed undefined behaviors in example code for requesting adapter + and device. (#9246, #9256) [@r-lyeh] + - SDL2+WebGPU: fixed hi-dpi handling. (#9300) [@ypujante] + - GLFW/SDL2/SDL3+WebGPU: added support for WGVK. (#9316, #9246, #9257) [@r-lyeh, @ocornut] + - GLFW/SDL2/SDL3+WebGPU: removed support for Emscripten <4.0.10. (#9281) [@ypujante] + + +----------------------------------------------------------------------- + VERSION 1.92.6 (2026-02-17) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.6 + +Breaking Changes: + +- Fonts: + - `AddFontDefault()` now automatically selects an embedded font between: + - `AddFontDefaultBitmap()`: classic pixel-clean font. Recommended at Size 13px with no scaling. + - `AddFontDefaultVector()`: new scalable font. Recommended at any higher size. + - The default selection is based on (style.FontSizeBase * FontScaleMain * FontScaleDpi) + reaching a small threshold, but old codebases may not set any of them properly. + As as a result, it is likely that old codebase may still default to AddFontDefaultBitmap(). + - Prefer explicitly calling either of them based on your own logic! + You can call `AddFontDefaultBitmap()` to ensure legacy behavior. + - Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which did + erroneously make a copy of the font data, essentially defeating the purpose + of this flag and wasting memory (undetected since July 2015 and now spotted + by @TellowKrinkle, this is perhaps the oldest bug in Dear ImGui history, + albeit for a rarely used feature!) (#9086, #8465) + HOWEVER, fixing this bug is likely to surface bugs in user/app code: + - Prior to 1.92, font data only needs to be available during the `atlas->AddFontXXX()` call. + Since 1.92, font data needs to available until `atlas->RemoveFont()`, or more typically + until a shutdown of the owning context or font atlas. + - The fact that handling of `FontDataOwnedByAtlas = false` was broken bypassed + the issue altogether. + - Removed `ImFontConfig::PixelSnapV` added in 1.92 which turns out is unnecessary + (and was mis-documented). Post-rescale `GlyphOffset` is always rounded. + - Popups: changed compile-time `ImGuiPopupFlags popup_flags = 1` default value to be `= 0` for + `BeginPopupContextItem()`, `BeginPopupContextWindow()`, `BeginPopupContextVoid()`, `OpenPopupOnItemClick()`. + The default value has same meaning before and after. (#9157, #9146) + - Before this version, those functions had a `ImGuiPopupFlags popup_flags = 1` default + value in their function signature. This was introduced by a change on 2020/06/23 (1.77) + while changing the signature from `int mouse_button` to `ImGuiPopupFlags popup_flags` + and trying to preserve then-legacy behavior. + - We have now changed this behavior to: cleanup a very old API quirk, facilitate use by + bindings, and to remove the last and error-prone non-zero default value. Also because we + deemed it extremely rare to use those helper functions with the Left mouse button! + As using the LMB would generally be triggered via another widget, + e.g. a Button() + a OpenPopup()/BeginPopup() call. + - Before: The default = 1 means `ImGuiPopupFlags_MouseButtonRight`. + Explicitly passing a literal 0 means `ImGuiPopupFlags_MouseButtonLeft`. + - After: The default = 0 means `ImGuiPopupFlags_MouseButtonRight`. + Explicitly passing a literal 1 also means `ImGuiPopupFlags_MouseButtonRight`. + (if legacy behavior are enabled) or will assert (if legacy behavior are disabled). + - TL;DR: if you don't want to use right mouse button for popups, always specify it + explicitly using a named `ImGuiPopupFlags_MouseButtonXXXX` value. + Recap: + - BeginPopupContextItem("foo"); // Behavior unchanged (use Right button) + - BeginPopupContextItem("foo", ImGuiPopupFlags_MouseButtonLeft); // Behavior unchanged (use Left button) + - BeginPopupContextItem("foo", ImGuiPopupFlags_MouseButtonLeft | xxx); // Behavior unchanged (use Left button + flags) + - BeginPopupContextItem("foo", ImGuiPopupFlags_MouseButtonRight | xxx); // Behavior unchanged (use Right button + flags) + - BeginPopupContextItem("foo", 1); // Behavior unchanged (as a courtesy we legacy interpret 1 as ImGuiPopupFlags_MouseButtonRight, will assert if disabling legacy behaviors. + - BeginPopupContextItem("foo", 0); // !! Behavior changed !! Was Left button. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft. + - BeginPopupContextItem("foo", ImGuiPopupFlags_NoReopen); // !! Behavior changed !! Was Left button + flags. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft | xxx. +- Commented out legacy names obsoleted in 1.90 (Sept 2023): + - `BeginChildFrame()` --> `BeginChild()` with `ImGuiChildFlags_FrameStyle` flag. + - `EndChildFrame()` --> `EndChild()`. + - `ShowStackToolWindow()` --> `ShowIDStackToolWindow()`. + - `IM_OFFSETOF()` --> `offsetof()`. + - `IM_FLOOR()` --> `IM_TRUNC()` [internal, for positive values only] +- Hashing: handling of "###" operator to reset to seed within a string identifier + doesn't include the "###" characters in the output hash anymore: + Before: `GetID("Hello###World") == GetID("###World") != GetID("World")` + After: `GetID("Hello###World") == GetID("###World") == GetID("World")` + - This has the property of facilitating concatenating and manipulating + identifiers using "###", and will allow fixing other dangling issues. + - This will invalidate hashes (stored in .ini data) for Tables and Windows + that are using the "###" operators. (#713, #1698) +- Renamed helper macro `IM_ARRAYSIZE()` -> `IM_COUNTOF()`. Kept redirection/legacy name. +- Backends: + - Vulkan: optional `ImGui_ImplVulkanH_DestroyWindow()` helper used by our example + code does not call `vkDestroySurfaceKHR()`: because surface is created by caller + of `ImGui_ImplVulkanH_CreateOrResizeWindow()`, it is more consistent. (#9163) + +Other Changes: + +- Fonts: + - Added `AddFontDefaultVector()`: a new embedded monospace scalable font: ProggyForever! + From https://github.com/ocornut/proggyforever: + "ProggyForever is an MIT-licensed partial reimplementation of the ProggyVector + font (originally by Tristan Grimmer), which itself is a vector-based + reinterpretation of the ProggyClean bitmap font that happily served as + Dear ImGui default font for over 10 years." [...] + "I commissioned Thiebault Courot to recreate this, applied various minor tweaks + and fixes, and reworked his editing pipeline toward shipping FontForge source + files so we can allow and track future changes." + - TL;DR; there was no strictly MIT-licensed matching font. We made it! + - The font data was carefully subsetted, trimmed and compressed so the embedded + data is ~14 KB. Embedding a scalable default font ensures that Dear ImGui can + be easily and readily used in all contexts, even without file system access. + - Expect minor fixes/improvements in following releases. + - As always you can opt-out of the embedded font data if desired. + - `AddFontDefault()` now automatically selects an embedded font between + the classic pixel-looking one and the new scalable one. + Prefer calling `AddFontDefaultVector()` or `AddFontDefaultBitmap()` explicitly. + - Fixed a crash when trying to use `AddFont()` with `MergeMode==true` on a font that + has already been rendered. (#9162) [@ocornut, @cyfewlp] + - Fixed an issue where using `PushFont()` from the implicit/fallback "Debug" window + when its recorded state is collapsed would incorrectly early out. This would break + e.g. using direct draw-list calls such as `GetForegroundDrawList()` with current font. + (#9210, #8865) + - Fixed an issue related to `EllipsisChar` handling, while changing + font loader or font loader flags dynamically in Style->Fonts menus. + - imgui_freetype: fixed overwriting `ImFontConfig::PixelSnapH` when hinting + is enabled, creating side-effects when later disabling hinting or + dynamically switching to stb_truetype rasterizer. + - Adding new fonts after removing all fonts mid-frame properly updates current state. +- Textures: + - Fixed a building issue when `ImTextureID` is defined as a struct. + - Fixed displaying texture # in Metrics/Debugger window. +- Menus: + - Fixed `MenuItem()` label position and `BeginMenu()` arrow/icon/popup positions, + when used inside a line with a baseline offset. + - Made navigation into menu-bar auto wrap on X axis. (#9178) +- TreeNode: + - Fixed highlight position when used inside a line with a large text baseline offset. + (it never quite worked in this situation; but then most of the time the text + baseline offset ends up being zero or `FramePadding.y` for a given line). +- Tables: + - Fixed an issue where a very thin scrolling table would advance parent layout + slightly differently depending on its visibility (caused by a mismatch + between hard minimum window size and table minimum size). + - Fixed an issue where submitting non-integer row heights would eventually + advance table parent layout by +0/+1 depending on its visibility. + - Fixed losing stored display order when reducing column count or when .ini + data has missing or duplicate values. (#9108, #4046) +- ColorEdit: + - Added R/G/B/A color markers next to each component (enabled by default). + - Added `ImGuiColorEditFlags_NoColorMarkers` to disable them. + - Added `style.ColorMarkerSize` to configure width of color component markers. +- Sliders, Drags: + - Added `ImGuiSliderFlags_ColorMarkers` to opt-in adding R/G/B/A color markers + next to each components, in multi-components functions. + - Added a way to select a specific marker color. +- InputText: + - InputTextMultiline(): fixed a minor bug where Shift+Wheel would allow a small + horizontal scroll offset when there should be none. (#9249) + - ImGuiInputTextCallbackData: `SelectAll()` also sets `CursorPos` to `SelectionEnd`. + - ImGuiInputTextCallbackData: Added `SetSelection()` helper. + - ImGuiInputTextCallbackData: Added `ID` and `EventActivated` members. (#9174) +- Text, InputText: + - Reworked word-wrapping logic: + - Try to not wrap in the middle of contiguous punctuation. (#8139, #8439, #9094) + - Try to not wrap between a punctuation and a digit. (#8503) + - Inside `InputTextMultiline()` with WordWrap enabled: prefer keeping blanks at + the end of a line rather than at the beginning of next line. (#8990, #3237) + - Fixed low-level word-wrapping function reading from `*text_end` when passed + a string range. (#9107) [@achabense] + - Changed `RenderTextEllipsis()` logic to not trim trailing blanks before + the ellipsis, making ellipsis position more consistent and not arbitrary + hiding the possibility of multiple blanks. (#9229) +- Nav: + - Fixed remote/shortcut InputText() not teleporting mouse cursor when + nav cursor is visible and `io.ConfigNavMoveSetMousePos` is enabled. + - Fixed a looping/wrapping issue when used in menu layer. (#9178) + - Fixed speed scale for resizing/moving with keyboard/gamepad. We incorrectly + used `io.DisplayFramebufferScale` as a scaling factor (very old code), + effectively making those actions faster on macOS/iOS retina screens. + (changed this to use a style scale factor that's not fully formalized yet) + - Fixed an UBSan warning when using in a `ImGuiListClipper` region . (#9160) +- Scrollbar: fixed a code-path leading to a divide-by-zero (which would not be + noticeable by user but detected by sanitizers). (#9089) [@judicaelclair] +- InvisibleButton: allow calling with size (0,0) to fit to available content + size. (#9166, #7623) +- Tooltips, Disabled: fixed `EndDisabledOverrideReenable()` assertion when + nesting a tooltip in a disabled block. (#9180, #7640) [@RegimantasSimkus] +- Added `GetItemFlags()` in public API for consistency and to expose generic + flags of last submitted item. (#9127) +- Misc: fixed build on ARM64/ARM64EC targets trying to use SSE/immintrin.h. + (#9209, #5943, #4091) [@navvyswethgraphics] +- Log/Capture: + - Fixed erroneously injecting extra carriage returns in output text buffer + when `ItemSpacing.y` > `FramePadding.y + 1` while emitting items. +- Images: + - Added `style.ImageRounding`, `ImGuiStyleVar_ImageRounding `to configure + rounding of `Image()` widgets. (#2942, #845) + - `ImageButton()` doesn't use a clamped `style.FrameRounding` value but instead + adjust inner image rounding when `FramePadding > `FrameRounding`. (#2942, #845) +- Shortcuts: + - IsItemHovered() without `ImGuiHoveredFlags_AllowWhenBlockedByActiveItem` + doesn't filter out the signal when activated item is a shortcut remote activation; + (which mimics what's done internally in the `ItemHoverable()` function). (#9138) + - Fixed tooltip placement being affected for a frame when located over an item + activated by `SetNextItemShortcut()`. (#9138) +- Error Handling: + - Improved error handling and recovery for `EndMenu()`/`EndCombo()`. (#1651, #9165, #8499) + - Improved error handling and recovery for `TableSetupColumn()`. +- Debug Tools: + - Debug Log: fixed incorrectly printing characters in IO log when submitting + non-ASCII values to `io.AddInputCharacter()`. (#9099) + - Debug Log: can output to debugger on Windows via Win32 `OutputDebugString()` (#5855) +- Demo: + - Slightly improve `Selectable()` demos. (#9193) +- Backends: + - DirectX10: added `SamplerNearest` in `ImGui_ImplDX10_RenderState`. + (+renamed `SamplerDefault` to `SamplerLinear`, which was tagged as beta API) + - DirectX11: added `SamplerNearest` in ImGui_ImplDX11_RenderState. + (+renamed `SamplerDefault` to `SamplerLinear`, which was tagged as beta API) + - GLFW: Avoid repeated `glfwSetCursor()` / `glfwSetInputMode()` unnecessary calls. + Lowers overhead for very high framerates (e.g. 10k+ FPS). [@maxliani] + - GLFW: Added `IMGUI_IMPL_GLFW_DISABLE_X11` / `IMGUI_IMPL_GLFW_DISABLE_WAYLAND` to + forcefully disable either. (#9109, #9116) + Try to set them automatically if headers are not accessible. (#9225) + - OpenGL3: Fixed embedded loader multiple init/shutdown cycles broken on some + platforms. (#8792, #9112) + - SDL2, SDL3: changed `GetClipboardText()` handler to return NULL on error aka + clipboard contents is not text. Consistent with other backends. (#9168) + - SDL2, SDL3: systems other than X11 are back to starting mouse capture on mouse down + (reverts 1.91.9 change). Only X11 requires waiting for a drag by default (not ideal, + but a better default for X11 users). Waiting for a drag to start mouse capture leads to + input drops when dragging after clicking on the edge of a window. + (#3650, #6410, #9235, #3956, #3835) + - SDL2, SDL3: added `ImGui_ImplSDL2_SetMouseCaptureMode()`/`ImGui_ImplSDL3_SetMouseCaptureMode()` + function for X11 users to disable mouse capturing/grabbing. (#3650, #6410, #9235, #3956, #3835) + - When attached to a debugger may want to call: + - `ImGui_ImplSDL3_SetMouseCaptureMode(ImGui_ImplSDL3_MouseCaptureMode_Disabled);` + - But you can also configure your system or debugger to automatically release + mouse grab when crashing/breaking in debugger, e.g. + - console: `setxkbmap -option grab:break_actions && xdotool key XF86Ungrab` + - or use a GDB script to call `SDL_CaptureMouse(false)`. See #3650. + - On platforms other than X11 this is unnecessary. + - SDL_GPU3: added `SamplerNearest` in `ImGui_ImplSDLGPU3_RenderState`. + - SDL_GPU3: macOS version can use MSL shaders in order to support macOS 10.14+ + (vs Metallib shaders requiring macOS 14+). Requires application calling + `SDL_CreateGPUDevice()` with `SDL_GPU_SHADERFORMAT_MSL`. (#9076) [@Niminem] + - Vulkan: helper for creating a swapchain (used by examples and multi-viewports) + selects `VkSwapchainCreateInfoKHR`'s `compositeAlpha` value based on + `cap.supportedCompositeAlpha`, which seems to be required on some Android + devices. (#8784) [@FelixStach] + - WebGPU: fixes for Emscripten 5.0.0 (note: current examples do not build with 5.0.1). + - Win32: handle `WM_IME_CHAR`/`WM_IME_COMPOSITION` to support Unicode inputs on + MBCS (non-Unicode) Windows. (#9099, #3653, #5961) [@ulhc, @ocornut, @Othereum] + - Win32: minor optimization not submitting gamepad input if packet number has not + changed (reworked previous 1.92.4). (#9202, #8556) [@AhmedSamyMousa, @MidTerm-CN] +- Examples: + - Win32+DirectX12: ignore seemingly incorrect `D3D12_MESSAGE_ID_FENCE_ZERO_WAIT` + warning on startups on some setups. (#9084, #9093) [@RT2Code, @LeoGautheron] + + +----------------------------------------------------------------------- + VERSION 1.92.5 (Released 2025-11-20) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.5 + +Breaking Changes: + + - Keys: commented out legacy names which were obsoleted in 1.89.0 (August 2022). + - ImGuiKey_ModCtrl --> ImGuiMod_Ctrl + - ImGuiKey_ModShift --> ImGuiMod_Shift + - ImGuiKey_ModAlt --> ImGuiMod_Alt + - ImGuiKey_ModSuper --> ImGuiMod_Super + - IO: commented out legacy io.ClearInputCharacters() obsoleted in 1.89.8 (Aug 2023). + Using io.ClearInputKeys() is enough. + - BeginChild: commented out legacy names which were obsoleted in 1.90.0 (Nov 2023), + 1.90.9 (July 2024), 1.91.1 (August 2024). (#462, #7687) + - ImGuiChildFlags_Border --> ImGuiChildFlags_Borders + - ImGuiWindowFlags_NavFlattened --> ImGuiChildFlags_NavFlattened (moved to ImGuiChildFlags). + - ImGuiWindowFlags_AlwaysUseWindowPadding --> ImGuiChildFlags_AlwaysUseWindowPadding (moved to ImGuiChildFlags). + So: + - BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0) + - BeginChild(name, size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding) --> BeginChild(name, size, ImGuiChildFlags_AlwaysUseWindowPadding, 0) + - Commented out legacy SetItemAllowOverlap() obsoleted in 1.89.7: this never worked right. + Use SetNextItemAllowOverlap() _before_ item instead. + +Other Changes: + +- Windows: + - Config flag io.ConfigWindowsMoveFromTitleBarOnly is now latched during + Begin(), effectively allowing to change the value on a per-window basis. + (although there is a better internal mechanism for it). + - Fixed single-axis auto-sizing (via double-clicking a border or passing + 0.0f on one axis of SetNextWindowSize() call) to take account of remaining + scrollbar on the other axis. (#9060) + - Fixed an issue where repeated calls to SetNextWindowSize() using 0.0f + to auto-size on a given axis would keep marking ini settings as dirty. +- Tables: + - Fixed a bug where nesting BeginTable()->Begin()->BeginTable() would + result in temporarily incorrect state, which would lead to bugs to side effects + in various locations, e.g. GetContentRegionAvail() calls or using clipper. (#9005) + EndTable() was mistakenly restoring a wrong current table. + - Angled headers: fixed an auto-resize feedback loop that could + affect tables with empty non-resizing columns using angled headers, making + them typically flicker back and forth between +0 and +1 pixels. +- Disabled: fixed a bug when a previously enabled item that got nav focus + and then turns disabled could still be activated using keyboard. (#9036) +- InputText: + - When buffer is not resizable, trying to paste contents that cannot + fit will now truncate text to nearest UTF-8 codepoint boundaries, + instead of completely ignoring the paste. (#9029) + - Avoid continuously overwriting ownership of ImGuiKey_Enter/_KeypadEnter + keys in order to allow e.g. external Shortcut override behavior. (#9004) + - When using a callback to reduce/manipulate the value of BufTextLen, + we do not require anymore that CursorPos be clamped by user code. (#9029) + - Fixed an assert when using ImGuiInputTextFlags_ReadOnly and making + underlying contents shorter while text is selected. (#9069, #3237) + (regression from 1.92.3) + - InputTextMultiline: fixed a crash when using ImGuiInputTextFlags_WordWrap and + resizing the parent window while keeping the multi-line field active (which is + most typically achieved when resizing programmatically or via a docking layout + reacting to a platform window resize). (#3237, #9007) [@anton-kl, @ocornut] +- Nav: + - Reworked PageUp/PageDown logic to pick same-page top/bottom page based + on inner rectangle rather than clipping rectangle, ensuring consistent + (but occasionally less practical) navigation result when a window is + partially out of screen. (#787) + - Improved/clarified behavior when requesting PageUp/PageDown from a + focused item which is outside of visible boundaries: now ends up one + page away from focused item. (#9079) + - Clipper: fixed an issue when using up/down from an item outside of + visible bound and using the clipper. (#9079) +- Fonts: + - Calling ImFontAtlas::Clear() mid-frame without re-adding a font will + lead to a more explicit crash. +- Textures: + - Fixed an issue preventing multi-contexts from using each others' fonts + if context 2 runs after context 1's Render() function. (#9039) +- MultiSelect: added ImGuiMultiSelectFlags_NoSelectOnRightClick to disable default + right-click processing, which selects item on mouse down and is designed for + context-menus. (#8200, #9015) +- Groups: fixed an issue reporting IsItemEdited() signal after EndGroup() when + triggered by some widgets e.g. Checkbox(), Selectable() and many others, which + cleared ActiveId at the same time as editing. (#9028) + Note that IsItemDeactivatedAfterEdit() was not affected, only IsItemEdited(). +- Misc: standardized casing of keyboard mods in comments and demo, showing + as e.g. "Ctrl" instead of "CTRL". +- CI: Added Dear ImGui Test Suite to CI builds. [@rokups] +- Drag and Drop: + - Added ImGuiDragDropFlags_AcceptDrawAsHovered to make accepting item render + as hovered, which can allow using e.g. Button() as drop target. (#8632) + - Pressing Escape while carrying a payload automatically cancel the + active drag and drop. (#9071) + - Style: added ImGuiCol_DragDropTargetBg, style.DragDropTargetRounding, + style.DragDropTargetBorderSize and style.DragDropTargetPadding to configure + the drop target highlight. (#9056) [@aaronkirkham] +- Demo: About Box: emit infos to convey when IM_ASSERT() macro is disabled, + - so users don't miss out on programming errors being reported. + - so it is included in config/build info submitted in new GitHub Issues. +- Debug Tools: + - Fixed DebugTextEncoding() potentially reading out of bounds when + provided a trailing truncated UTF-8 sequence. + - Metrics: fixed table and columns rect highlight from display when + debug/metrics window is not in the same viewport as the table. +- Backends: + - NULL: added imgui_impl_null platform/renderer backend. + This is designed if you need to run e.g. context with no input or no output. + - GLFW: fixed building on Linux platforms where Wayland headers + are not available. (#9024, #8969, #8921, #8920) [@jagot] + - GLFW: lower minimum requirement from GLFW 3.1 to GLFW 3.0. Though + a recent version e.g GLFW 3.4 is highly recommended! (#9055) [@Clownacy] + - GLFW: fixed last `ImGui_ImplGlfw_Shutdown()` not immediately clearing the context + map, which would be detected by leak trackers. (#9075, #8676, #8239, #8069) [@erincatto] + - SDL3: fixed Platform_OpenInShellFn() return value (the return value + was unused in core but might be used by a direct caller). (#9027) [@achabense] + - SDL3: fixed an issue with missing characters events when an already active text + field changes viewports. (#9054) + - Vulkan: added IMGUI_IMPL_VULKAN_VOLK_FILENAME to configure path to + Volk (default to "volk.h"). (#9008, #7722, #6582, #4854) [@mwlasiuk] + - WebGPU: update to compile with Dawn and Emscripten's 4.0.10+ + '--use-port=emdawnwebgpu' ports. (#8381, #8898, #7435) [@brutpitt, @trbabb] + When using Emscripten 4.0.10+, backend now defaults to IMGUI_IMPL_WEBGPU_BACKEND_DAWN + instead of IMGUI_IMPL_WEBGPU_BACKEND_WGPU, if neither are specified. + - WebGPU: added various internal/optional helpers to wrap some of the + Dawn/WGPU/Emscripten debacle quirks: (#8381) [@brutpitt] + - ImGui_ImplWGPU_CreateWGPUSurfaceHelper(). + - ImGui_ImplWGPU_IsSurfaceStatusError(), ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(). + - ImGui_ImplWGPU_DebugPrintAdapterInfo(), + - ImGui_ImplWGPU_GetBackendTypeName(), ImGui_ImplWGPU_GetAdapterTypeName(), + ImGui_ImplWGPU_GetDeviceLostReasonName(), ImGui_ImplWGPU_GetErrorTypeName(), + ImGui_ImplWGPU_GetLogLevelName(). + - Win32: Revert 1.92.4 change of comparing dwPacketNumber, which prevents + refreshing accurate gamepad info after focus-out + io.ClearInputKeys(). (#8556) +- Examples: + - NULL: update examples_null to use imgui_impl_null (which is a bit overengineering + but somehow consistent). + - GLFW+WebGPU: update example for latest specs, to work on Emscripten 4.0.10+, + latest Dawn-Native and WGPU-Native. (#8381, #8567, #8191, #7435) [@brutpitt] + - GLFW+WebGPU: removed unnecessary ImGui_ImplWGPU_InvalidateDeviceObjects() call + during surface resize. (#8381) + - SDL2+WebGPU: added new example (Emscripten + native Dawn/WGPU). (#8381) [@brutpitt] + - SDL3+WebGPU: added new example (Emscripten + native Dawn/WGPU). (#8381) [@brutpitt] + - Win32+OpenGL3: enable DPI awareness. (#9083) + + +----------------------------------------------------------------------- + VERSION 1.92.4 (Released 2025-10-14) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.4 + +Breaking Changes: + +- Backends: + - TreeNode, Selectable, Clipper: commented out legacy names obsoleted in + 1.89.7 (July 2023) and 1.89.9 (Sept 2023): + ImGuiTreeNodeFlags_AllowItemOverlap --> ImGuiTreeNodeFlags_AllowOverlap + ImGuiSelectableFlags_AllowItemOverlap --> ImGuiSelectableFlags_AllowOverlap + ImGuiListClipper::IncludeRangeByIndices() --> ImGuiListClipper::IncludeItemsByIndex() + - Vulkan: moved some fields in ImGui_ImplVulkan_InitInfo: + init_info.RenderPass --> init_info.PipelineInfoMain.RenderPass + init_info.Subpass --> init_info.PipelineInfoMain.Subpass + init_info.MSAASamples --> init_info.PipelineInfoMain.MSAASamples + init_info.PipelineRenderingCreateInfo --> init_info.PipelineInfoMain.PipelineRenderingCreateInfo + It makes things more consistent and was desirable to introduce new settings for + secondary viewports. (#8946, #8110, #8111, #8686) [@ocornut, @SuperRonan, @sylmroz] + - Vulkan: renamed ImGui_ImplVulkan_MainPipelineCreateInfo --> ImGui_ImplVulkan_PipelineInfo + (introduced very recently and only used by `ImGui_ImplVulkan_CreateMainPipeline()` + so it should not affect many users). (#8110, #8111) + - Vulkan: helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a + `VkImageUsageFlags image_usage` argument. + It was previously hardcoded to `VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT` and defaults + to that when the value is 0. In theory the function is an internal helper but + since it's used by our examples some may have used it. (#8946, #8111, #8686) + +Other Changes: + +- Windows: added lower-right resize grip on child windows using both + ImGuiChildFlags_ResizeX and ImGuiChildFlags_ResizeY flags. (#8501) [@aleksijuvani] + The grip is not visible before hovering to reduce clutter. +- Style: added ImGuiCol_UnsavedMarker, color of the unsaved document marker when + using ImGuiWindowFlags_UnsavedDocument/ImGuiTabItemFlags_UnsavedDocument. (#8983) +- IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() + helpers to null all handlers. (#8945, #2769) +- Tables: changed ImGuiTableFlags_NoBordersInBody behavior to not draw border in + body even when resizing. (#8893) +- Inputs: + - Shortcuts: added support for combining ImGuiInputFlags_RouteFocused + (which is the default route) with ImGuiInputFlags_RouteOverActive, allowing + to steal shortcuts from active item without using global routing. (#9004) +- InputText: + - Fixed single-line InputText() not applying fine character clipping + properly (regression in 1.92.3). (#8967) [@Cyphall] + - Fixed an infinite loop error happening if a custom input text + callback modifies/clear BufTextLen before calling InsertChars(). + (regression from 1.92.3). Note that this never really worked correctly, but + previously it would only temporary wreck cursor position, and since 1.92.3 it + would go in an infinite loop. (#8994, #3237) +- Textures: + - Fixed a crash if texture status is set to ImTextureStatus_WantDestroy by a backend + after it had already been destroyed. This would typically happen when calling + ImGui_ImplXXXX_InvalidateDeviceObjects() helpers twice in a row. (#8977, #8811) + - Allowed backend to destroy texture while inside the NewFrame/EndFrame + scope. Basically if a backend decide to destroy a texture that we didn't request + to destroy (for e.g. freeing resources) the texture is immediately set to + a ImTextureStatus_WantCreate status again. (#8811) + - Fixed an issue preventing multi-contexts sharing a ImFontAtlas from + being possible to destroy in any order. + - Fixed not updating ImTextureData's RefCount when destroying a context + using a shared ImFontAtlas, leading standard backends to not properly + free texture resources. (#8975) [@icrashstuff] +- Demo: fixed layout issue in "Layout & Scrolling -> Scrolling" section. +- Misc: Relaxed internal assert in MarkItemEdited() to allow for more use cases. (#8997) +- Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode, + Android Studio & more) to provide nicer display for ImVec2, ImVec4, ImVector etc. + See misc/debuggers/ for details. (#8950) [@mentlerd] +- CI: updated Windows CI scripts to generate/use VulkanSDK. (#8925, #8778) [@yaz0r] +- Docs: updated FAQ with new "What is the difference between Dear ImGui and + traditional UI toolkits?" entry. (#8862) +- Backends: + - All backends call ImGuiPlatformIO::ClearPlatformHandlers() and + ClearRendererHandlers() on shutdown, so as not to leave function pointers + which may be dangling when using backend in e.g. DLL. (#8945, #2769) + - DirectX12: reuse a command list and allocator for texture uploads instead + of recreating them each time. (#8963, #8465) [@RT2Code] + - DirectX12: Rework synchronization logic. (#8961) [@RT2Code] + (presumably fixes old hard-to-repro crash issues such as #3463, #5018) + - DirectX12: Reuse texture upload buffer and grow it only when + necessary. (#9002) [@RT2Code] + - DirectX12: Enable swapchain tearing if available. (#8965) [@RT2Code] + - OpenGL3: fixed GL loader to work on Haiku OS which does not support + `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes] + - GLFW: fixed build on platform that are neither Windows, macOS or + known Unixes (Regression in 1.92.3). (#8969, #8920, #8921) [@oktonion] + - SDL2,SDL3: avoid using the SDL_GetGlobalMouseState() path when one of our + window is hovered, as the event data is reliable and enough in this case. + - Fix mouse coordinates issue in fullscreen apps with macOS notch. (#7919, #7786) + - Essentially a workaround for SDL3 bug which will be fixed in SDL 3.3.0. + - Better perf on X11 as querying global position requires a round trip to X11 server. + - Win32: minor optimization not submitting gamepad io again if + XInput's dwPacketNumber has not changed. (#8556) [@MidTerm-CN] + - Vulkan: added a way to specify custom shaders by filling init fields + CustomShaderVertCreateInfo and CustomShaderFragCreateInfo. (#8585, #8271) [@johan0A] + - DX9,DX10,DX11,DX12,Metal,Vulkan,WGPU,SDLRenderer2,SDLRenderer3: + ensure that a texture in ImTextureStatus_WantDestroy state always turn to + ImTextureStatus_Destroyed even if your underlying graphics data was already + destroyed. (#8977) +- Examples: + - SDL2+DirectX11: Try WARP software driver if hardware driver is + not available. (#5924, #5562) + - SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82] + - Win32+DirectX12: Rework synchronization logic. (#8961) [@RT2Code] + - Made examples's main.cpp consistent with returning 1 on error. + + +----------------------------------------------------------------------- + VERSION 1.92.3 (Released 2025-09-17) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.3 + +Other Changes: + +- Fonts: fixed merging a font and specifying a font target in DstFont + that's not the last added font (regression in 1.92). (#8912) +- Fonts: fixed an assertion failure when a rectangle entry has been reused + 1024 times (e.g. due to constant change of font size). (#8906) [@cfillion] +- Clipper, Tables: added ImGuiListClipperFlags_NoSetTableRowCounters as a way to + disable the assumption that 1 clipper item == 1 table row, which breaks when + e.g. using clipper with ItemsHeight=1 in order to clip in pixel units. (#8886) +- Scrollbar, Style: added configurable style.ScrollbarPadding value and corresponding + ImGuiStyleVar_ScrollbarPadding enum, instead of an hard-coded computed default. (#8895) +- Nav: fixed Ctrl+Tab window appearing as empty when the sole active and focused + window has the ImGuiWindowFlags_NoNavFocus flag. (#8914) +- Nav: fixed a crash that could occur when opening a popup following the processing + of a global shortcut while no windows were focused. +- Bullet: fixed tessellation which looked out of place in very large sizes. +- InputText: added ImGuiInputTextFlags_WordWrap flag to word-wrap multi-line buffers. + (#3237, #952, #1062, #7363). Current caveats: + - This is marked as beta because not being tested enough. + Please report any incorrect cursor movement, selection behavior etc. bug to #3237. + - Wrapping style is not ideal. Wrapping of long words/sections (e.g. words + larger than total available width) may be particularly unpleasing. + - Wrapping width needs to always account for the possibility of a vertical scrollbar. + - It is currently much slower than regular text fields: + - Ballpark estimate of cost on my 2019 desktop PC: + For a 100 KB text buffer: +~0.3 ms/+~1.0 ms (Optimized vs Debug builds). + - The CPU cost is very roughly proportional to text length, so a 10 KB buffer + should cost about ten times less. +- InputText, InputInt, InputFloat: fixed an issue where using Escape to revert + would not write back the reverted value during the IsItemDeactivatedAfterEdit() + frame if the provided input buffer doesn't store temporary edits. + (regression in 1.91.7) (#8915, #8273) +- InputText: fixed an issue where using Escape with ImGuiInputTextFlags_EscapeClearsAll + would not write back the cleared value during the IsItemDeactivatedAfterEdit() + frame if the provided input buffer doesn't store temporary edits. (#8915, #8273) +- InputText: allow passing an empty string with buf_size==0. (#8907) + In theory the buffer size should always account for a zero-terminator, but idioms + such as using InputTextMultiline() with ImGuiInputTextFlags_ReadOnly to display + a text blob are facilitated by allowing this. +- InputText: refactored internals to simplify and optimizing rendering of selection. + Very large selection (e.g. 1 MB) now take less overhead. +- InputText: revert a change in 1.79 where pressing Down or PageDown on the last line + of a multi-line buffer without a trailing carriage return would keep the cursor + unmoved. We revert back to move to the end of line in this situation. +- InputText: fixed pressing End (without Shift) in a multi-line selection from + mistakenly moving cursor based on selection start. +- Focus, InputText: fixed an issue where SetKeyboardFocusHere() did not work + on InputTextMultiline() fields with ImGuiInputTextFlags_AllowTabInput, since + they normally inhibit activation to allow tabbing through multiple items. (#8928) +- Selectable: added ImGuiSelectableFlags_SelectOnNav to auto-select an item when + moved into, unless Ctrl is held. (automatic when in a BeginMultiSelect() block). +- TabBar: fixed an issue were forcefully selecting a tab using internal API would + be ignored on first/appearing frame before tabs are submitted (#8929, #6681) +- DrawList: fixed CloneOutput() unnecessarily taking a copy of the ImDrawListSharedData + pointer, which could to issue when deleting the cloned list. (#8894, #1860) +- DrawList: made AddCallback() assert when passing a null callback. +- Debug Tools: ID Stack Tool: fixed using fixed-size buffers preventing long identifiers + from being displayed in the tool. (#8905, #4631) +- Debug Tools: ID Stack Tool: when ### is used, uncontributing prefix before the ### + is now skipped. (#8904, #4631) +- Debug Tools: ID Stack Tool: added option to hex-encode non-ASCII characters in + output path. (#8904, #4631) +- Debug Tools: ID Stack Tool: fixed a crash when using PushOverrideID(0) during + a query. (#8937, #4631) +- Debug Tools: Fixed assertion failure when opening a combo box while using + io.ConfigDebugBeginReturnValueOnce/ConfigDebugBeginReturnValueLoop. (#8931) [@harrymander] +- Demo: tweaked ShowFontSelector() and ShowStyleSelector() to update selection + while navigating and to not close popup automatically. +- CI: Updates Windows CI to use a more recent VulkanSDK. (#8925, #8778) [@yaz0r] +- Examples: Android: Android+OpenGL3: update Gradle project (#8888, #8878) [@scribam] +- Examples: GLFW+OpenGL2, GLFW+Vulkan, GLFW+Metal, Win32+Vulkan: Fixed not applying + content scale consistently with other examples. (#8921, #8756) +- Backends: GLFW: distinguish X11 vs Wayland to fix various scaling issues. + (#8920, #8921) [@TheBrokenRail, @pthom, @ocornut] + - window/monitor content scales are always reported as 1.0 on Wayland. + - framebuffer scales are always reported as 1.0 on X11. +- Backends: SDL2: window/monitor content scales are always reported as 1.0 on Wayland. + (#8920, #8921) [@TheBrokenRail, @pthom, @ocornut] +- Backends: SDL3: use SDL_GetWindowDisplayScale() on Mac to obtain DisplayFrameBufferScale, + fixing incorrect values during resolution changes e.g. going fullscreen. + (#8703, #4414) [@jclounge] +- Backends: SDL_GPU: Added ImGui_ImplSDLGPU3_InitInfo::SwapchainComposition and + PresentMode to configure how secondary viewports are created. Currently only used + multi-viewport mode. (#8892) [@PTSVU] +- Backends: Vulkan: added ImGui_ImplVulkan_CreateMainPipeline() to recreate pipeline + without reinitializing backend. (#8110, #8111) [@SuperRonan] + + +----------------------------------------------------------------------- + VERSION 1.92.2b (Released 2025-08-13) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.2b + +Changes: + +- Fixed IsItemHovered() failing on disabled items and items that have no + identifier (e.g. Text() calls) when holding mouse button. (#8877, #8883) + [Regression in 1.92.2]. +- Made IsItemHovered() on holding mouse button down on disabled items not + leak between items when the window cannot be moved. +- Backends: Allegro5: Fixed texture format setup which didn't work on all + setups/drivers. (#8770, #8465) +- Backends: Allegro5: Added ImGui_ImplAllegro5_SetDisplay() function to + change current ALLEGRO_DISPLAY, as Allegro applications often need to do that. +- Backends: Allegro5: Fixed missing support for ImGuiKey_PrintScreen + under Windows, as raw Allegro 5 does not receive it. + + +----------------------------------------------------------------------- + VERSION 1.92.2 (Released 2025-08-11) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.2 + +Breaking Changes: + +- Tabs: Renamed ImGuiTabBarFlags_FittingPolicyResizeDown to ImGuiTabBarFlags_FittingPolicyShrink. + Kept inline redirection enum (will obsolete). (#261, #351) +- Backends: SDL_GPU3: changed ImTextureID type from SDL_GPUTextureSamplerBinding* to SDL_GPUTexture*, + which is more natural and easier for user to manage. If you need to change the current sampler, + you can access the ImGui_ImplSDLGPU3_RenderState struct. (#8866, #8163, #7998, #7988) + +Other Changes: + +- Fixed an old inconsistency between IsItemHovered() and internal hovering check, + where IsItemHovered() would return true to mouse was first clicked on the + background of a non-moveable window then moved over the item or button. + Note that while it is consistent with other logic, there is a possibility + that some third-party code may accidentally relied on this. One can always use + ImGuiHoveredFlags_AllowWhenBlockedByActiveItem to bypass the active id check. + (#8877) [@achabense, @ocornut] +- Fonts: fixed an issue when a font using MergeMode has a reference size + specified but the target font doesn't. Usually either all fonts should + have a reference size (only required when specifying e.g. GlyphOffset), + or none should have a reference size. +- Fonts: fixed a crash when changing texture format when using a legacy + backend. Most commonly would happen when calling GetTexDataAsRGBA32() + then immediately calling GetTexDataAsAlpha8(). (#8824) +- Windows: fixed an issue where resizable child windows would emit border + logic when hidden/non-visible (e.g. when in a docked window that is not + selected), impacting code not checking for BeginChild() return value. (#8815) +- Textures: Fixed support for `#define ImTextureID_Invalid` to non-zero value: + ImFontAtlas() was incorrectly cleared with zeroes. (#8860, #8745) [@cfillion] +- Tables: fixed TableGetRowIndex() which never correctly worked when using + a clipper (it exists for consistency but is almost never used, as it is + often more convenient to use index in caller-code, whereas TableGetRowIndex() + includes header rows). +- Tables: fixed imgui_internal.h's TableGetHoveredRow() the same way. (#7350, #6588, #6250) +- Tabs: added new fitting policy ImGuiTabBarFlags_FittingPolicyMixed + and made it the default. This policy shrink tab width down to a given amount, + and then beyond that it enable scrolling buttons. (#3421, #8800) +- Tabs: added style.TabMinWidthShrink, ImGuiStyleVar_TabMinWidthShrink to + control the width to shrink to in ImGuiTabBarFlags_FittingPolicyMixed mode. + (#3421, #8800). +- Tabs: when scrolling is enabled, track selected tabs when resizing down + parent container. This does not prevent to horizontally scroll it out of + view during normal operations. (#3421, #8800) +- Tabs: added style.TabMinWidthBase, ImGuiStyleVar_TabMinWidthBase to control + the base minimum width of a tab (default to 1.0f). This is the size before + any potential shrinking is applied. +- Tabs: fixed tab bar underline not drawing below scroll buttons, when + they are enabled (minor regression from 1.90). (#6820, #4859, #5022, #5239) +- Tabs: made scrolling buttons never keyboard/gamepad navigation candidates. +- Nav, Tables: fixed navigation within scrolling tables when item boundaries + goes beyond columns limits. The fix done in 1.89.6 didn't work correctly + on scrolling windows. (#8816, #2221) +- Nav: fixed a bug where ImGuiKey_NavGamepadMenu (==ImGuiKey_GamepadFaceLeft) + button couldn't toggle between main and menu layers while navigating a Modal + window. (#8834) +- Error Handling: minor improvements to error handling for TableGetSortSpecs() + and TableSetBgColor() calls. (#1651, #8499) +- Misc: fixed building with IMGUI_DISABLE_DEBUG_TOOLS only. (#8796) +- Misc: fixed building with IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION. (#8794) +- Misc: removed more redundant inline static linkage from imgui_internal.h to + facilitate using in C++ modules. (#8813, #8682, #8358) [@stripe2933] +- Misc: ImVector: skip memcpy in operator= if `Data` isn't initialized in order + to play nice with -fsanitize=undefined. (#8874) [@i25e] +- CI: Added SDL3 builds to MacOS and Windows. (#8819, #8778) [@scribam] +- CI: Updated Windows CI to use a more recent SDL2. (#8819, #8778) [@scribam] +- Examples: SDL3+Metal: added SDL3+Metal example. (#8827, #8825) [@shi-yan] +- Examples: SDL3+SDL_GPU: use SDL_WaitAndAcquireGPUSwapchainTexture() instead + of SDL_AcquireGPUSwapchainTexture(). (#8830) [@itsdanott] +- Examples: SDL3+SDL_GPU: use SDL_GPU_PRESENTMODE_VSYNC present mode. +- Backends: OpenGL3: add and call embedded loader shutdown in ImGui_ImplOpenGL3_Shutdown() + to facilitate multiple init/shutdown cycles in same process. (#8792) [@tim-rex] +- Backends: OpenGL2, OpenGL3: set GL_UNPACK_ALIGNMENT to 1 before updating + textures. (#8802) [@Daandelange] +- Backends: SDL_GPU3: expose current SDL_GPUSampler* in the ImGui_ImplSDLGPU3_RenderState + struct. (#8866, #8163, #7998, #7988) +- Backends: Vulkan: Fixed texture update corruption introduced in 1.92.0, + affecting some drivers/setups. (#8801, #8755, #8840) [@Retro52, @Miolith] +- Backends: Vulkan: Avoid calling vkCmdBindDescriptorSets() when texture + has not changed. (#8666) [@micb25] + + +----------------------------------------------------------------------- + VERSION 1.92.1 (Released 2025-07-09) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.1 + +Changes: + +- Fonts: added ImFontAtlas::SetFontLoader() to dynamically change font + loader at runtime without using internal API. (#8752, #8465) +- Fonts: fixed a bug where dynamically changing font loader would lose + the Fallback and Ellipsis glyphs under some circumstance. (#8763) +- Fonts: for large size fonts, layout/size calculation only load glyphs metrics. + Actual glyphs are renderer+packed when used by drawing functions. (#8758, #8465) +- Fonts: set a maximum font size of 512.0f at ImGui:: API level to reduce + edge cases (e.g. out of memory errors). ImDrawList:: API doesn't have the + constraint. (#8758) +- Fonts: Restore ImFontConfig::FontNo being a 32-bits value as this is needed + to pass full range of information into e.g. FreeType's face_index, as higher + bits are used from FreeType 2.6.1. (#8775) [@Valakor] + (the field has been erroneously reduced from 32-bits to 8-bit in 1.92.0) +- Fonts, Tables: fixed PushFont() having no effect when called after submitting + a hidden column. (#8865) +- Textures: Fixed support for `#define ImTextureID_Invalid` to non-zero value: + ImTextureData() was incorrectly cleared with zeroes. (#8745) [@rachit7645] +- Demo: Added "Text -> Font Size" demo section. (#8738) [@Demonese] +- CI: Fixed dllimport/dllexport tests. (#8757) [@AidanSun05] +- CI: Updated to use latest Windows image + VS2022. +- Debug Tools: added IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS to detect + id conflicts _before_ hovering. This is very slow and should only be used + temporarily. (#8651, #7961, #7669) +- Examples: GLFW+OpenGL3, GLFW+WGPU: Emscripten Makefiles uses GLFW port + 'contrib.glfw3' which offers better HiDPI support. (#8742) [@pthom] +- Backends: GLFW, SDL2 made ImGui_ImplGLFW_GetContentScaleXXX() and + ImGui_ImplSDL2_GetContentScaleXXXX() helpers return 1.0f on Emscripten + and Android platforms, matching macOS logic. (#8742, #8733) [@pthom] +- Backends: SDL3: avoid calling SDL_StartTextInput() again if already active. + (fixes e.g.: an issue on iOS where the keyboard animation will popup every + time the user types a key + probably other things) (#8727) [@morrazzzz] +- Backends: OSX: added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress + mouse cursor support. (#8739) [@cfillion] +- Backends: Allegro5: fixed texture update broken on some platforms where + ALLEGRO_LOCK_WRITEONLY needed all texels to be rewritten. (#8770) +- Backends: Vulkan: use nonCoherentAtomSize to align upload_size, fixing + validation error on some setups. (#8743, #8744) [@tquante] +- Backends: Vulkan: fixed texture synchronization issue introduced in 1.92.0, + leading to validation layers reacting. (#8772) [@Majora320] + + +----------------------------------------------------------------------- + VERSION 1.92.0 (Released 2025-06-25) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.0 + +THIS VERSION CONTAINS THE LARGEST AMOUNT OF BREAKING CHANGES SINCE 2015! +I TRIED REALLY HARD TO KEEP THEM TO A MINIMUM, REDUCE THE AMOUNT OF INTERFERENCE, +BUT INEVITABLY SOME USERS OR THIRD-PARTY EXTENSIONS WILL BE AFFECTED. + +For instructions to upgrade your custom backend: +--> See https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md + +IN ORDER TO HELP US IMPROVE THE TRANSITION PROCESS, INCL. DOCUMENTATION AND COMMENTS, +PLEASE REPORT **ANY** DOUBT, CONFUSION, QUESTIONS, FEEDBACK TO: + https://github.com/ocornut/imgui/issues/ +If you are using custom widgets, internals or third-party extension that are somehow +breaking and aren't obvious how to solve, please post in Issues so we can gather +data and share solutions that may help others. + +As part of the plan to reduce impact of API breaking changes, several unfinished +changes/features/refactors related to font and text systems and scaling will be +part of subsequent releases (1.92.1+). + +If you are updating from an old version, and expecting a massive or difficult update, +consider first updating to 1.91.9 to reduce the amount of changes. + +Breaking changes: + +- Fonts: **IMPORTANT**: if your app was solving the OSX/iOS Retina screen specific + logical vs display scale problem by setting io.DisplayFramebufferScale (e.g. to 2.0f) + + setting io.FontGlobalScale (e.g. to 1.0f/2.0f) + loading fonts at scaled sizes (e.g. size X * 2.0f): + - This WILL NOT map correctly to the new system! Because font will rasterize as requested size. + - With a legacy backend (< 1.92): + - Instead of setting io.FontGlobalScale = 1.0f/N -> set ImFontCfg::RasterizerDensity = N. + - This already worked before, but is now pretty much required. + - With a new backend (1.92+), + - This should be all automatic. + - FramebufferScale is automatically used to set current font RasterizerDensity. + - FramebufferScale is a per-viewport property provided by backend through the + Platform_GetWindowFramebufferScale() handler in 'docking' branch. +- Fonts: **IMPORTANT** on Font Sizing: + - Before 1.92, fonts were of a single size. They can now be dynamically sized. + - PushFont() API now has a REQUIRED size parameter. + void PushFont(ImFont* font) --> void PushFont(ImFont* font, float size); + - PushFont(font, 0.0f) // Change font and keep current size + - PushFont(NULL, 20.0f) // Keep font and change current size + - PushFont(font, 20.0f) // Change font and set size to 20.0f + - PushFont(font, style.FontSizeBase * 2.0f) // Change font and set size to be twice bigger than current size. + - PushFont(font, font->LegacySize) // Change font and set size to size passed to AddFontXXX() function. Same as pre-1.92 behavior, for fixed size fonts. + - To use old behavior use 'ImGui::PushFont(font, font->LegacySize)' at call site. + We intentionally didn't add a default parameter because it would make the long-term + transition more difficult. + - Kept inline redirection font. Will obsolete. + - Global scale factors may be applied over the provided size. + This is why it is called 'FontSizeBase' in the style structure. + - Global scale factors are: 'style.FontScaleMain', 'style.FontScaleDpi' and maybe more. + - ImFont::FontSize was removed and does not make sense anymore. + - ImFont::LegacySize is the size passed to AddFont(). + - Removed support for old PushFont(NULL) which was a shortcut for "revert to default font". + `PushFont(NULL, some_size)` now keeps current change and changes size. + - Renamed/moved 'io.FontGlobalScale' to 'style.FontScaleMain'. +- Fonts: **IMPORTANT** on Font Merging: + - When searching for a glyph in multiple merged fonts: we search for the FIRST font source + which contains the desired glyph. Because the user doesn't need to provide glyph ranges + any more, it is possible that a glyph that you expected to fetch from a secondary/merged + icon font may be erroneously fetched from the primary font. + - We added `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to exclude from a given font source: + // Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range + static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; + ImFontConfig cfg1; + cfg1.GlyphExcludeRanges = exclude_ranges; + io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); + // Add Font Source 2, which expects to use the range above + ImFontConfig cfg2; + cfg2.MergeMode = true; + io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2); + - You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` to + see list of glyphs available in multiple font sources. This can facilitate understanding + which font input is providing which glyph. +- Fonts: **IMPORTANT** on Thread Safety: + - A few functions such as font->CalcTextSizeA() were by sheer luck (== accidentally) + thread-safe even though we had never provided that guarantee before. They are + definitively not thread-safe anymore as new glyphs may be loaded. + +- Textures: + - All API functions taking a 'ImTextureID' parameter are now taking a 'ImTextureRef': + - ImTextureRef ais small composite structure which may be constructed from a ImTextureID. + (or constructed from a ImTextureData* which represent a texture which will generally + be ready by the time of rendering). + - Affected functions are: + - ImGui::Image(), ImGui::ImageWithBg(), ImGui::ImageButton(), + - ImDrawList::AddImage(), ImDrawList::AddImageQuad(), ImDrawList::AddImageRounded(). + - We suggest that C users and any higher-level language bindings generators may + facilitate converting this in some way, aka emulating the trivial C++ constructor. +- Fonts: obsoleted ImFontAtlas::GetTexDataAsRGBA32(), GetTexDataAsAlpha8(), Build(), SetTexID() + and IsBuilt() functions. The new protocol for backends to handle textures doesn't need them. + Kept redirection functions (will obsolete). + - A majority of old backends should still work with new code (behaving like they did before). + - For instructions to upgrade your custom backend: + https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md + - Calling ImFontAtlas::Build() before initializing new backends will erroneously trigger + preloading all glyphs. Will be detected with an assertion after the backend is initialized. +- Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) + since v1.91.8. It is quite important you keep it automatic until we decide if we want + to provide a way to express finer policy, otherwise you will likely waste texture space + when using large glyphs. Note that the imgui_freetype backend doesn't use and does not + need oversampling. +- Fonts: specifying glyph ranges is now unnecessary. + - The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. + - All GetGlyphRangesXXXX() functions are now marked obsolete: + - GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), + GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), + GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), + GetGlyphRangesThai(), GetGlyphRangesVietnamese(). +- Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327) + (it vaguely made sense with the old system as if unspecified textures width maxed up + to 4096 but that limit isn't necessary anymore, and Renderer_TextureMaxWidth covers this) + However you may set TexMinWidth = TexMaxWidth for the same effect. +- Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on + ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself. + An assert will trigger if you don't. +- Fonts: obsoleted ImGui::SetWindowFontScale() which is not useful anymore. Prefer using + PushFont(NULL, style.FontSizeBase * factor) or to manipulate other scaling factors. +- Fonts: obsoleted ImFont::Scale which is not useful anymore. +- Fonts: changed ImFont::CalcWordWrapPositionA() to ImFont::CalcWordWrapPosition(): + - old: const char* CalcWordWrapPositionA(float scale, const char* text, ....); + - new: const char* CalcWordWrapPosition (float size, const char* text, ....); + The leading 'float scale' parameters was changed to 'float size'. + This was necessary as 'scale' is assuming a unique font size. + Kept inline redirection function assuming using font->LegacySize. +- Fonts: generally reworked Internals of ImFontAtlas and ImFont. + While in theory a vast majority of users shouldn't be affected, some use cases or + extensions might be. Among other things: + - ImDrawCmd::TextureId has been changed to ImDrawCmd::TexRef. + - ImFontAtlas::TexID has been changed to ImFontAtlas::TexRef. + - ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[]. + - ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourceCount. + - Each ImFont has a number of ImFontBaked instances corresponding to actively used + sizes. ImFont::GetFontBaked(size) retrieves the one for a given size. + - Fields moved from ImFont to ImFontBaked: + - ImFont::IndexAdvanceX[] -> ImFontBaked::IndexAdvanceX[] + - ImFont::Glyphs[] -> ImFontBaked::Glyphs[] + - ImFont::Ascent, Descent -> ImFontBaked::Ascent, Descent + - ImFont::FindGlyph() -> ImFontBaked::FindGlyph() + - ImFont::FindGlyphNoFallback() -> ImFontBaked::FindGlyphNoFallback() + - ImFont::GetCharAdvance() -> ImFontBaked::GetCharAdvance() + - Widget code may use ImGui::GetFontBaked() instead of ImGui::GetFont() to + access font data for current font at current font size. + (and you may use font->GetFontBaked(size) to access it for any other size.) + g.Font == ImGui::GetFont() + g.FontSize == ImGui::GetFontSize() + g.FontBaked == ImGui::GetFontBaked() == ImGui::GetFont()->GetFontBaked(ImGui::GetFontSize()) + - Fields moved from ImFontAtlas to ImTextureData + - ImFontAtlas->TexWidth -> ImFontAtlas->TexData->Width + - ImFontAtlas->TexHeight -> ImFontAtlas->TexData->Height + - ImFontAtlas->TexPixelsAlpha8 -> ImFontAtlas->TexData->GetPixels() (when ImFontAtlas::TexDesiredFormat == ImTextureFormat_Alpha8) + - ImFontAtlas->TexPixelsRGBA32 -> ImFontAtlas->TexData->GetPixels() (when ImFontAtlas::TexDesiredFormat == ImTextureFormat_RGBA32) + Please report if you are affected! +- Fonts: (users of imgui_freetype) + - renamed ImFontAtlas::FontBuilderFlags to ImFontAtlas::FontLoaderFlags. + - renamed ImFontConfig::FontBuilderFlags to ImFontConfig::FontLoaderFlags. + - renamed ImGuiFreeTypeBuilderFlags to ImGuiFreeTypeLoaderFlags. + - if you used runtime imgui_freetype selection rather than the default compile-time + option provided by IMGUI_ENABLE_FREETYPE: + - renamed/reworked ImFontBuilderIO into ImFontLoader, + - renamed ImGuiFreeType::GetBuilderForFreeType() to ImGuiFreeType::GetFontLoader() + - old: io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType() + - new: io.Fonts->FontLoader = ImGuiFreeType::GetFontLoader() +- DrawList: Renamed ImDrawList::PushTextureID()/PopTextureID() to PushTexture()/PopTexture(). +- Fonts: (users of custom rectangles) + - Renamed AddCustomRectRegular() to AddCustomRect(). (#8466) + - Added GetCustomRect() as a replacement for GetCustomRectByIndex() + CalcCustomRectUV(). (#8466) + - The output type of GetCustomRect() is now ImFontAtlasRect, which include UV coordinates. + - ImFontAtlasCustomRect::X --> ImFontAtlasRect::x + - ImFontAtlasCustomRect::Y --> ImFontAtlasRect::y + - ImFontAtlasCustomRect::Width --> ImFontAtlasRect::w + - ImFontAtlasCustomRect::Height --> ImFontAtlasRect::h + Before: + const ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(custom_rect_id); + ImVec2 uv0, uv1; + atlas->GetCustomRectUV(r, &uv0, &uv1); + ImGui::Image(atlas->TexRef, ImVec2(r->w, r->h), uv0, uv1); + After: + ImFontAtlasRect r; + atlas->GetCustomRect(custom_rect_id, &r); + ImGui::Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1); + We added a redirecting typedef but haven't attempted to magically redirect + the field names, as this API is rarely used and the fix is simple. + - Obsoleted AddCustomRectFontGlyph() as the API does not make sense for scalable fonts: + - Kept existing function which uses the font "default size" (Sources[0]->LegacySize). + - Added a helper AddCustomRectFontGlyphForSize() which is immediately marked obsolete, + but can facilitate transitioning old code. + - Prefer adding a font source (ImFontConfig) using a custom/procedural loader. +- Backends: removed ImGui_ImplXXXX_CreateFontsTexture()/ImGui_ImplXXXX_DestroyFontsTexture() + for all backends that had them. They should not be necessary any more. + - removed ImGui_ImplMetal_CreateFontsTexture(), ImGui_ImplMetal_DestroyFontsTexture(). + - removed ImGui_ImplOpenGL2_CreateFontsTexture(), ImGui_ImplOpenGL2_DestroyFontsTexture(). + - removed ImGui_ImplOpenGL3_CreateFontsTexture(), ImGui_ImplOpenGL3_DestroyFontsTexture(). + - removed ImGui_ImplSDLGPU3_CreateFontsTexture(), ImGui_ImplSDLGPU3_DestroyFontsTexture(). + - removed ImGui_ImplSDLRenderer2_CreateFontsTexture(), ImGui_ImplSDLRenderer2_DestroyFontsTexture(). + - removed ImGui_ImplSDLRenderer3_CreateFontsTexture(), ImGui_ImplSDLRenderer3_DestroyFontsTexture(). + - removed ImGui_ImplVulkan_CreateFontsTexture(), ImGui_ImplVulkan_DestroyFontsTexture(). +- Layout: commented out legacy ErrorCheckUsingSetCursorPosToExtendParentBoundaries() fallback + obsoleted in 1.89 (August 2022) which allowed a SetCursorPos()/SetCursorScreenPos() call WITHOUT AN ITEM + to extend parent window/cell boundaries. Replaced with assert/tooltip that would already happens if + previously using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#5548, #4510, #3355, #1760, #1490, #4152, #150) + - Incorrect way to make a window content size 200x200: + Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); + - Correct ways to make a window content size 200x200: + Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); + Begin(...) + Dummy(ImVec2(200,200)) + End(); + - TL;DR; if the assert triggers, you can add a Dummy({0,0}) call to validate extending parent boundaries. +- TreeNode: renamed ImGuiTreeNodeFlags_NavLeftJumpsBackHere to ImGuiTreeNodeFlags_NavLeftJumpsToParent + for clarity. Kept inline redirection enum (will obsolete). (#1079, #8639) +- Commented out PushAllowKeyboardFocus()/PopAllowKeyboardFocus() which was obsoleted + in 1.89.4 (March 2023). (#3092) + - PushAllowKeyboardFocus(bool tab_stop) --> PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); + - PopAllowKeyboardFocus() --> PopItemFlag(). +- Commented out ImGuiListClipper::ForceDisplayRangeByIndices() which was obsoleted + in 1.89.6 (June 2023). + - ForceDisplayRangeByIndices() --> IncludeItemsByIndex() +- Backends: SDL3: Fixed casing typo in function name: (#8509, #8163, #7998, #7988) [@puugz] + - Imgui_ImplSDLGPU3_PrepareDrawData() --> ImGui_ImplSDLGPU3_PrepareDrawData() +- Internals: RenderTextEllipsis() function removed the 'float clip_max_x' parameter directly + preceding 'float ellipsis_max_x'. Values were identical for a vast majority of users. (#8387) + +Non-breaking Fonts/Textures related changes: + +- Textures: added partial texture update protocol. (#8465, #3761) + - The Renderer Backend needs to set io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures + and handle texture updates requests. + - New structs: ImTextureData, ImTextureRect. + - New enums: ImTextureStatus, ImTextureFormat. + - During its ImGui_ImplXXXX_RenderDrawData() call, the backend can now access a texture list + in ImDrawData::Textures[]. Textures may have four distinct states: + - ImTextureStatus_WantCreate: requesting backend to create a texture. + - ImTextureStatus_WantUpdates: requesting backend to copy given blocks from the CPU side + copy of the texture to your graphics pipeline. + A 'tex->Updates[]' list of update is provided as well as a single 'tex->UpdatesRect' bounding box. + - ImTextureStatus_WantDestroy: requesting backend to destroy the texture. + - A 'int UnusedFrames' value is provided to conveniently defer destroying. + - Backend is generally free to destroy textures whenever they like. + - ImTextureStatus_OK: nothing to do. + - Almost all standard backends have been updated to support this. + - Backends have allowed to destroy textures at any time if they desire so. + A list is available in platform_io.Textures[] for this purpose and for backend shutdown. + - Both stb_truetype and FreeType backends have been updated to work with the new + system, and they now share more code than before. + - Added '#define IMGUI_HAS_TEXTURES' to facilitate compile-time checks for third-party + extensions until this is merged with a definitive version number to check. +- Fonts: font backend/loader may easily be changed dynamically, allowing users to compare + rasterizers outputs and features. imgui_freetype is generally beneficial. +- Fonts: ImFontAtlas::AddFontXXX() functions may be called at any time during the frame. +- Fonts: ImFontAtlas::AddFontXXX() can fail more gracefully if error handling is configured + to not assert (this will be better exposed via future font flags). +- Fonts: added style.FontScaleBase scaling factor (previously called io.FontGlobalScale). +- Fonts: added style.FontScaleDpi scaling factor. This is designed to be be changed on + per-monitor/per-viewport basis, which `io.ConfigDpiScaleFonts` does automatically. + (which is why it is separate from FontScaleBase). +- Fonts: added optional font_size parameter to ImGui::PushFont() function. +- Fonts: added ImFontAtlas::RemoveFont() function. +- Fonts: added ImFontAtlas::CompactCache() function. +- Fonts: added ImFontAtlas::TexDesiredFormat field (default to ImTextureFormat_RGBA32, + can be changed to ImTextureFormat_Alpha8). +- Fonts: added ImFontAtlas::TexMinWidth, TexMinHeight, TexMaxWidth, TexMaxHeight fields. +- Fonts: added ImFontConfig::PixelSnapV to align scaled GlyphOffset.y to pixel boundaries. +- Fonts: added ImFontConfig::GlyphExcludeRanges[], which behave similarly to + ImFontConfig::GlyphRanges[], but has the opposite meaning. It is tailored to situations + where merged fonts have overlapping characters. +- Fonts: added "Input Glyphs Overlap Detection Tool" which dumps a list of glyphs + provided by merged sources, which may help setting up a GlyphExcludeRanges[] filter. +- Fonts: added ImFontAtlas::FontBackendName (which is surfaced in the "About Dear ImGui" + window and other locations). +- Fonts: added ImFontFlags (currently needs to be passed through ImFontConfig until + we revamp font loading API): + - ImFontFlags_NoLoadError: disable erroring/assert when calling AddFontXXX() + with missing file/data. Calling code is expected to check AddFontXXX() return value. + - ImFontFlags_NoLoadGlyphs: disable loading new glyphs. + - ImFontFlags_LockBakedSizes: disable loading new baked sizes, disable garbage + collecting current ones. e.g. if you want to lock a font to a single size. +- Fonts: the general design has changed toward meaning that a ImFont doesn't have + have a specific size: it may be bound and drawn with any size. + - We store ImFontBaked structures internally, which are a cache of information + for a given size being drawn. You should not need to deal with ImFontBaked directly. + - ImFontBaked structures may be cleaned up between frames when unused, pointers + to them are only valid for the current frame. + - Added ImFontBaked::IsGlyphLoaded() function. +- Fonts: Custom Rect packing has generally been reworked. (#8107, #7962, #1282) + - ImFontAtlas::AddCustomRect() (previously AddCustomRectRegular()/AddCustomRectFontGlyph()) + functions will immediately return a packed rectangle identifier, and you can write your + pixels immediately - previously had to wait for Build() to be called. + This is also the case when using a legacy backend. + - Custom packed rectangles may be moved during a texture change, aka practically anytime. + Always refer to latest uvs/position returned by GetCustomRect(). + - AddCustomRect() returns ImFontAtlasRectId_Invalid on failure. + - Added ImFontAtlas::RemoveCustomRect() function. + - GetCustomRect() can safely return false and not crash when passed an invalid or removed id. +- Fonts: texture is now stored in a single format CPU side (save ~25% when using RGBA). +- Fonts: changing current font to one from a different atlas is supported. (#8080) +- Fonts: automatic baking of an "..." ellipsis works better with monospace fonts. +- Fonts: each ImFontConfig font source may provide a custom backend/loader. +- Fonts: added platform_io.Renderer_TextureMaxWidth/Renderer_TextureMaxHeight fields + for Renderer Backend to specify if there is a maximum accepted texture size (not yet used). +- Fonts: added compile-time overridable '#define ImTextureID_Invalid 0' if you need 0 + to be a valid low-level texture identifier. +- Fonts: reworked text ellipsis logic to ensure a "..." is always displayed instead + of a single character. (#7024) +- Fonts: word-wrapping code handle ideographic comma & full stop (U+3001, U+3002). (#8540) +- Fonts: fixed CalcWordWrapPosition() fallback when width is too small to wrap: + would use a +1 offset instead of advancing to the next UTF-8 codepoint. (#8540) +- Debug Tools: Fonts section: add font preview, add "Remove" button, add "Load all glyphs" + button, add selection to change font backend when both are compiled in. +- Renderer Backends: + - Backends: DX9/DX10/DX11/DX12, Vulkan, OpenGL2/3, Metal, SDLGPU3, SDLRenderer2/3, WebGPU, Allegro5: + - Added ImGuiBackendFlags_RendererHasTextures support for all backends. (#8465, #3761, #3471) + [@ocornut, @ShironekoBen, @thedmd] + - Added ImGui_ImplXXXX_UpdateTexture(ImTextureData* tex) functions for all backends. + Available if you want to start uploading textures right after ImGui::Render() and without + waiting for the call to ImGui_ImplXXXX_RenderDrawData(). Also useful if you use a staged or + multi-threaded rendering schemes, where you might want to set ImDrawData::Textures = NULL. (#8597, #1860) +- Special thanks for fonts/texture related feedback to: @thedmd, @ShironekoBen, @rodrigorc, + @pathogendavid, @itamago, @rokups, @DucaRii, @Aarkham, @cyfewlp. + +Other Changes: + +- IO: variations in analog-only components of gamepad events do not interfere + with trickling of mouse position events (#4921, #8508) +- Windows: fixed SetNextWindowCollapsed()/SetWindowCollapsed() bypassing the + codepath that preserve last contents size when collapsed, resulting in + programmatically uncollapsing auto-sizing windows having them flicker size + for a frame. (#7691) [@achabense] +- Windows: clicking on a window close button doesn't claim focus and bring to front. (#8683) +- Windows: loosened code to allow hovering of resize grips, borders, and table + borders while hovering a sibling child window, so that the code in master matches + one in docking (they accidentally diverged). (#8554) +- Windows: BeginChild(): fixed being unable to combine manual resize on one axis + and automatic resize on the other axis. (#8690) + e.g. neither ImGuiChildFlags_ResizeX | ImGuiChildFlags_AutoResizeY + or ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX worked before. +- TreeNode: added experimental flags to draw tree hierarchy outlines linking + parent and tree nodes: (#2920) + - ImGuiTreeNodeFlags_DrawLinesNone: No lines drawn (default value in style.TreeLinesFlags). + - ImGuiTreeNodeFlags_DrawLinesFull: Horizontal lines to child nodes. Vertical line drawn down to TreePop() position: cover full contents. + - ImGuiTreeNodeFlags_DrawLinesToNodes: Horizontal lines to child nodes. Vertical line drawn down to bottom-most child node. + - Added style.TreeLinesFlags which stores the default setting, + which may be overridden in individual TreeNode() calls. + - Added style.TreeLinesSize (default to 1.0f). + - Added style.TreeLinesRadius (default to 0.0f). + - Added ImGuiCol_TreeLines (in default styles this is the same as ImGuiCol_Border). + - Caveats: + - Tree nodes may be used in many creative ways (manually positioning openable + nodes in unusual ways, using indent to create tree-looking structures, etc.) + and the feature may not accurately represent them in every cases. + - The feature adds a little cost as extra data needs to be stored. + (ImGuiTreeNodeFlags_DrawLinesToNodes is slower than ImGuiTreeNodeFlags_DrawLinesFull + which may be meaningful on very large trees, as it needs to record bottom-most + Y position even for clipped nodes). + - The feature is unlikely to ever work properly when using a coarse clipper + such as ImGuiListClipper. +- TreeNode: fixed incorrect clipping of arrow/bullet when using ImGuiTreeNodeFlags_SpanAllColumns. +- InputText: fixed cursor positioning issue using up/down keys near end of lines while + editing non-ASCII text. (Regression from 1.91.2) (#8635, #7925) +- InputText: fixed a buffer overrun that could happen when using dynamically resizing + buffers (e.g. imgui_stdlib.cpp for std::string, or ImGuiInputTextFlags_CallbackRezize) + and programmatically making an insertion. (#8689) [@ocornut, @m9710797] +- Tables: fixed TableHeader() eager vertical clipping of text which may be noticeable + with FramePadding.y was too small. (#6236) +- Tables: fixed an assert when combining Tables, Frozen Rows, Clipper and BeginMultiSelect() + in a certain order. (#8595, #8250) +- Tabs: fixes small issues with how "..." ellipsis moved depending on visibility + of Close Button or Unsaved Document marker. (#8387) +- Tooltips: tooltips have a maximum size corresponding to host display/monitor size, + which mitigates edge case issues in multi-viewport scenarios where abnormally large + windows (e.g. determined programmatically) can lead to renderer backend trying to + create abnormally large framebuffers. +- TextLinkOpenURL(): added bool return value on click. (#8645, #8451, #7660) +- Scroll: fixed contents size, scrollbar visibility and scrolling resetting issues + with abnormally large contents ranges. (#3609, #8215) +- Clipper: some mitigation/improvements for abnormally large contents ranges. (#3609, #8215) +- Nav: fixed assertion when holding gamepad FaceLeft/West button to open + CTRL+Tab windowing + pressing a keyboard key. (#8525) +- Nav: fixed scroll fallback (when there are no interactive widgets to jump to) not + being enabled on windows with menu or title bar. +- Nav: fixed an issue handling PageUp/PageDown on windows with abnormally large contents + range which could lead to clipper requesting very large ranges. +- Error Handling: added better error report and recovery for extraneous + EndPopup() call. (#1651, #8499) +- Error Handling: added better error report and recovery when calling EndFrame() + or Render() without NewFrame(). Was previously only an assert. +- Style, InputText: added ImGuiCol_InputTextCursor to configure color of + the InputText cursor/caret. (#7031) +- Platform IME: added ImGuiPlatformImeData::ViewportId info (backported from Docking branch). +- Platform IME: added ImGuiPlatformImeData::WantTextInput which might set independently + of WantVisible. This is set in the same structure because activating text input generally + requires providing a window to the backend. (#8584, #6341) +- DrawList: Fixed a regression from 1.91.1 where a Begin()/PushFont()/AddImage() sequence + would not restore the correct atlas Texture Identifier when the PushFont() call used + a font from a different atlas. (#8694, caused by #3224, #3875, #6398, #7903) +- Misc: added extra operators to ImVec4 in IMGUI_DEFINE_MATH_OPERATORS block. (#8510) [@gan74] +- Misc: removed static linkage from operators to facilitate using in C++ modules. (#8682, #8358) [@johmani] +- Demo: changed default framed item width to use Min(GetFontSize() * 12, GetContentRegionAvail().x * 0.40f). +- Renderer Backends: + - Backends: SDLGPU3: Fixed creating atlas texture earlier than other backends, preventing + to load fonts between the Init and NewFrames calls. + - Backends: SDLGPU3: Made ImGui_ImplSDLGPU3_PrepareDrawData() reuse GPU Transfer Buffers which + were unusually slow to recreate every frame. Much faster now. (#8534) [@ocornut, @TheMode] + - Backends: SDLGPU3: added support for ImDrawCallback_ResetRenderState. (#8599) + - Backends: OpenGL3: made GLES 3.20 contexts not access GL_CONTEXT_PROFILE_MASK nor + GL_PRIMITIVE_RESTART. (#8664) [@DyXel] + - Backends: DirectX12: Fixed build on MinGW. (#8702, #4594) [@playday3008] + - Backends: DirectX10, DirectX11, DirectX12: Honor FramebufferScale to allow for custom + platform backends and experiments using it (consistently with other renderer backends, + even though in normal condition it is not set under Windows). (#8412) [@WSSDude] + - Backends: Vulkan: Deep-copy ImGui_ImplVulkan_InitInfo::PipelineRenderingCreateInfo's + pColorAttachmentFormats buffer when set, in order to reduce common user-error of + specifying a pointer to data that gets out of scope. (#8282) + - Backends: Vulkan: Load dynamic rendering functions using vkGetDeviceProcAddr() + + try both non-KHR and KHR versions. (#8600, #8326, #8365) [@ChrisTom-94] + - Backends: Vulkan: fixed validation errors in window create/resize helpers used by examples + and by multi-viewports implementation, which would typically trigger errors while detaching + secondary viewports. (#8600, #8176) [@ChrisTom-94] +- Platform Backends: + - Backends: GLFW: added ImGui_ImplGlfw_GetContentScaleForMonitor(), ImGui_ImplGlfw_GetContentScaleForWindow() + helpers. They are wrappers to glfwGetMonitorContentScale()/glfwGetWindowContentScale(), with compile-time + GLFW version checks + returning 1.0f on Apple platform. + - Backends: GLFW: Added support for multiple Dear ImGui contexts. (#8676, #8239, #8069) + - Backends: SDL2: added ImGui_ImplSDL2_GetDpiScaleForDisplay() and ImGui_ImplSDL2_GetContentScaleForWindow() + helpers. They are wrappers to SDL_GetDisplayDPI(), with compile-time SDL version checks + returning 1.0f + on Apple platforms. SDL3 already does this by default. + - Backends: Win32: Fixed an issue where externally losing mouse capture (due to e.g. focus loss) + would fail to claim it again the next subsequent click. (#8594) + - Backends: SDL2, SDL3, OSX: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad + regardless of ImGuiConfigFlags_NavEnableGamepad being set. (#8508) + - Backends: SDL2, SDL3: don't attempt to call SDL_CaptureMouse() on drivers where we don't + call SDL_GetGlobalMouseState(). This is specifically for Wayland but we currently use + the same white-list as SDL_GetGlobalMouseState(). (#8561) [@vs49688] + - Backends: GLFW, SDL2, SDL3: include GLFW/SDL version number in io.BackendPlatformName. + - Backends: SDL3: Update for SDL3 api changes: revert SDL_GetClipboardText() + memory ownership change. (#8530, #7801) [@Green-Sky] + - Backends: SDL3: honor ImGuiPlatformImeData::WantTextInput as an alternative + way to call SDL_StartTextInput(), without IME being necessarily visible. (#8584) + - Backends: SDL3: fixed pulling SDL_PROP_WINDOW_COCOA_WINDOW_POINTER into + viewport->PlatformHandleRaw. (#8725, #8726) [@eertbleyen] + - Backends: OSX: ImGui_ImplOSX_HandleEvent() only process event for window containing + our view. (#8644) [@BingoXuan] +- Examples: + - Examples: Made many examples DPI aware by default. + The single-viewport is basically: + - Query monitor DPI scale. Helpers are provided in some backends. + - Call style.ScaleAllSizes() and set style.FontScaleDpi with this factor. + Multi-viewport applications may set both of those flags: + - io.ConfigDpiScaleFonts = true; + - io.ConfigDpiScaleViewports = true; + Which will scale fonts but NOT style padding/spacings/thicknesses yet. + - Examples: Apple+Metal, Apple+OpenGL: add Makefile (CLion opens them nicely). (#8637) [@pthom] + - Examples: DirectX12+Win32: also test for IsIconic() for sleeping since we don't seem to + get a DXGI_STATUS_OCCLUDED signal when minimized. (#8603) [@dooann] + + +----------------------------------------------------------------------- + VERSION 1.91.9b (Released 2025-03-17) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.9b + +- Tables: Fixed assert when loading .ini settings with reordered columns. (#8496, #7934) +- Tables: Fixed issues when loading .ini settings for a table with columns using + ImGuiTableColumnFlags_DefaultHide or ImGuiTableColumnFlags_DefaultSort. (#8496, #7934) + + +----------------------------------------------------------------------- + VERSION 1.91.9 (Released 2025-03-14) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.9 + +Breaking changes: + +- Image: removed 'tint_col' and 'border_col' parameter from Image() function. (#8131, #8238) + - Old function signature: + void Image (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 tint_col = (1,1,1,1), ImVec4 border_col = (0,0,0,0)); + - New function signatures: + void Image (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1)); + void ImageWithBg(ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 bg_col = (0,0,0,0), ImVec4 tint_col = (1,1,1,1)); + - TL;DR: 'border_col' had misleading side-effect on layout, 'bg_col' was missing, parameter order couldn't be consistent with ImageButton(). + - New behavior always use ImGuiCol_Border color + style.ImageBorderSize / ImGuiStyleVar_ImageBorderSize. + - Old behavior altered border size (and therefore layout) based on border color's + alpha, which caused variety of problems. + - Old behavior used a fixed value of 1.0f for border size which was not tweakable. + - Kept legacy signature (will obsolete), which mimics the old behavior, + but uses Max(1.0f, style.ImageBorderSize) when border_col is specified. + - Added ImageWithBg() function which has both 'bg_col' (which was missing) and 'tint_col'. + It was impossible to add 'bg_col' to Image() with a parameter order consistent with + other functions, so we decided to remove 'tint_col' and introduce ImageWithBg(). +- Renamed ImFontConfig::GlyphExtraSpacing.x option to GlyphExtraAdvanceX. (#242) +- Renamed style.TabMinWidthForCloseButton to style.TabCloseButtonMinWidthUnselected. +- Backends: Vulkan: Added 'uint32_t api_version' argument to ImGui_ImplVulkan_LoadFunctions(). + Note that it was also added to ImGui_ImplVulkan_InitInfo but for the later it is optional. + (#8326, #8365, #8400) +- Internals: Fonts: ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[], +- Internals: Fonts: ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourcesCount. +- Internals: Menus: reworked mangling of menu windows to use "###Menu_00" etc. instead + of "##Menu_00", allowing them to also store the menu name before it. This shouldn't + affect code unless directly accessing menu window from their mangled name. + +Other changes: + +- Fixed IsItemDeactivatedAfterEdit() signal being broken for Checkbox(), + RadioButton(), Selectable(). Regression from 2025/01/13. (#8370) +- Windows: Fixed an issue where BeginChild() inside a collapsed Begin() + wouldn't inherit the SkipItems flag, resulting in missing coarse clipping + opportunities for code not checking the BeginChild() return value. +- Windows, Style: Added style.WindowBorderHoverPadding setting to configure + inner/outer padding applied to hit-testing of windows borders and detection + of hovered window. +- InputText: Allow CTRL+Shift+Z to redo even outside of OSX. (#8389) [@tanksdude] +- InputText: Pasting a multi-line buffer into a single-line edit replaces + carriage return by spaces. (#8459) +- InputTextWithHint(): Fixed buffer-overflow (luckily often with no visible effect) + when a user callback modified the buffer contents in a way that altered the + visibility of the preview/hint buffer. (#8368) [@m9710797, @ocornut] +- Scrollbar: Rework logic that fades-out scrollbar when it becomes too small, + which amusingly made it disappear when using very big font/frame size. +- Scrollbar: Automatically stabilize ScrollbarX visibility when detecting a + feedback loop manifesting with ScrollbarX visibility toggling on and off + repeatedly. (#8488, #3285, #4539) + (feedback loops of this sort can manifest in various situations, but combining + horizontal + vertical scrollbar + using a clipper with varying width items is + one frequent cause. The better solution is to, either: (1) enforce visibility + by using ImGuiWindowFlags_AlwaysHorizontalScrollbar or (2) declare stable + contents width with SetNextWindowContentSize(), if you can compute it.) +- Tables: fixed calling SetNextWindowScroll() on clipped scrolling table + to not leak the value into a subsequent window. (#8196) +- Tables: fixed an issue where Columns Visible/Width state wouldn't be correctly + restored when hot-reloading .ini state. (#7934) +- Tables: tamed some .ini settings optimizations to more accurately allow + overwriting/hot-reloading settings in more situations. (#7934) +- Tables, Error Handling: Recovery from invalid index in TableSetColumnIndex(). (#1651) +- Image: Added ImageWithBg() variant with bg color and tint color. (#8131, #8238) +- Image, Style: Added style.ImageBorderSize, ImGuiStyleVar_ImageBorderSize. (#8131, #8238) +- Selectable: Fixed horizontal label alignment with SelectableTextAlign.x > 0 and + specifying a selectable size. (#8338) +- Tabs, Style: made the Close Button of selected tabs always visible by default, + without requiring to hover the tab. (#8387) + - Added style.TabCloseButtonMinWidthSelected/TabCloseButtonMinWidthUnselected settings + to configure visibility of the Close Button for selected and unselected tabs. + (-1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width) + - Default for selected tabs: TabCloseButtonMinWidthSelected = -1.0f (always visible) + - Default for unselected tabs: TabCloseButtonMinWidthUnselected = 0.0f (visible when hovered) +- Tabs: fixed middle-mouse-button to close tab not checking that close button + is hovered, merely its visibility. (#8399, #8387) [@nicovanbentum] +- TextLink(), TextLinkOpenURL(): fixed honoring text baseline alignment. + (#8451, #7660) [@achabense] +- TextLinkOpenURL(): fixed default Win32 io.PlatformOpenInShellFn handler to + handle UTF-8 regardless of system regional settings. (#7660) [@achabense] +- Disabled: Fixed an issue restoring Alpha in EndDisabled() when using nested + BeginDisabled() calls with PushStyleVar(ImGuiStyleVar_DisabledAlpha) within. (#8454, #7640) +- Clipper: Fixed an issue where passing an out of bound index to IncludeItemByIndex() + could incorrectly offset the final cursor, even if that index was not iterated through. + One case where it would manifest was calling Combo() with an out of range index. (#8450) +- Debug Tools: Added io.ConfigDebugHighlightIdConflictsShowItemPicker (defaults to true) + to allow disabled Item Picker suggestion in user facing builds. (#7961, #7669) +- Debug Tools: Tweaked layout of ID Stack Tool and always display full path. (#4631) +- imgui_freetype: update lunasvg API to support v3.0+. (#8656, #6842, #6591) [@moretromain] +- Misc: Various zealous warning fixes for newer version of Clang. +- Misc: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursors + (busy/wait/hourglass shape, with or without an arrow cursor). +- Demo: Reorganized "Widgets" section to be alphabetically ordered and split in more functions. +- Demo: Combos: demonstrate a very simple way to add a filter to a combo, + by showing the filter inside the combo contents. (#718) +- Examples: SDL3: Added comments to clarify setup for users of the unfortunate + SDL_MAIN_USE_CALLBACKS feature. (#8455) +- IO: Added ImGuiKey_Oem102 to ImGuiKey enum. (#7136, #7201, #7206, #7306, #8468) +- Backends: reworked key handlers to use/prioritize untranslated scancodes instead of + translated keycodes when dealing with OEM keys which are too difficult to find a reliable + translated mapping on all systems, backends and keyboard layout. + (#7136, #7201, #7206, #7306, #7670, #7672, #8468) + - The affected keys are: ImGuiKey_Apostrophe, ImGuiKey_Comma, ImGuiKey_Minus, ImGuiKey_Period, + ImGuiKey_Slash, ImGuiKey_Semicolon, ImGuiKey_Equal, ImGuiKey_LeftBracket, ImGuiKey_RightBracket, + ImGuiKey_Backslash, ImGuiKey_GraveAccent, and newly introduced ImGuiKey_Oem102. + - This is NOT affecting characters used the text inputs. + - Fixes many cases of keys not emitting a ImGuiKey value with certain keyboard layouts. + - Makes emitted ImGuiKey values more consistent regardless of keyboard mapping, + but you may be getting different values as before. + - Win32, SDL2, SDL3: Use scancodes for OEM keys. + - GLFW: GLFW_KEY_WORLD_1 and GLFW_KEY_WORLD_2 are emitting ImGuiKey_Oem102. +- Backends: GLFW: Fixed clipboard handler assertion when using GLFW <= 3.2.1 compiled + with asserts enabled. (#8452) +- Backends: SDL2, SDL3: Using SDL_OpenURL() in platform_io.Platform_OpenInShellFn + handler. (#7660) [@achabense] +- Backends: SDL2, SDL3, Win32, Allegro5: Added support for ImGuiMouseCursor_Wait + and ImGuiMouseCursor_Progress cursors. +- Backends: SDL2, SDL3: Avoid calling SDL_GetGlobalMouseState() when mouse is in + relative mode. (#8425, #8407) [@TheMode] +- Backends: SDL2, SDL3: Only start SDL_CaptureMouse() when mouse is being dragged, + to mitigate issues with e.g. Linux debuggers not claiming capture back on debug + break. (#6410, #3650) +- Backends: OpenGL3: Lazily reinitialize embedded GL loader for when calling backend + from e.g. other DLL boundaries. (#8406) +- Backends: DirectX12: Fixed an issue where pre-1.91.5 legacy ImGui_ImplDX12_Init() + signature started breaking in 1.91.8 due to missing command queue. (#8429) +- Backends: Metal: Fixed a crash on application resources. (#8367, #7419) [@anszom] +- Backends: Vulkan: Added ApiVersion field in ImGui_ImplVulkan_InitInfo. + Default to header version if unspecified. (#8326, #8365) [@mklefrancois] +- Backends: Vulkan: Dynamic rendering path loads "vkCmdBeginRendering/vkCmdEndRendering" + (without -KHR suffix) on API 1.3. (#8326, #8365) [@mklefrancois] +- Backends: WebGPU: Recreate image bind groups during render to allow reuse of + WGPUTextureView pointers. (#8426, #8046, #7765, #8027) [@pplux, @Jairard] +- Backends: WebGPU: Fix for DAWN API change WGPUProgrammableStageDescriptor -> WGPUComputeState. + [@PhantomCloak] (#8369) +- Backends: WebGPU: Fix for webgpu-native API changes. (#8426) [@pplux] + + +----------------------------------------------------------------------- + VERSION 1.91.8 (Released 2025-01-31) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.8 + +Breaking changes: + +- ColorEdit, ColorPicker: redesigned how alpha is displayed in the preview + square. (#8335, #1578, #346) + - Removed ImGuiColorEditFlags_AlphaPreview (made value 0): it is now the default behavior. + - Prior to 1.91.8: alpha was made opaque in the preview by default _unless_ using ImGuiColorEditFlags_AlphaPreview. + - We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. + - The new flags may be combined better and allow finer controls: + - ImGuiColorEditFlags_AlphaOpaque: disable alpha in the preview, but alpha value still editable. + - ImGuiColorEditFlags_AlphaNoBg: disable rendering a checkerboard background behind transparent color. + - ImGuiColorEditFlags_AlphaPreviewHalf: display half opaque / half transparent preview. +- Backends: SDLGPU3: Renamed ImGui_ImplSDLGPU3_InitInfo::GpuDevice to Device + for consistency. (#8163, #7998, #7988) + +Other changes: + +- imgui_freetype: fixed issue where glyph advances would incorrectly be + snapped to pixels. Effectively it would only be noticeable when hinting + is disabled with ImGuiFreeTypeBuilderFlags_NoHinting, as hinting itself + snaps glyph advances. +- Inputs: added IsMouseReleasedWithDelay() helper. (#8337, #8320) + Use if you absolutely need to distinguish single-click from double-clicks + by introducing a delay. This is a very rarely used UI idiom, but some apps + use this: e.g. MS Explorer single-click on an icon triggers a rename. + Generally use with 'delay >= io.MouseDoubleClickTime' + combine with a + 'GetMouseClickedCount() == 1' check. +- Windows: legacy SetWindowFontScale() is properly inherited by nested child + windows. Note that an upcoming major release should make this obsolete, + but in the meanwhile it works better now. (#2701, #8138, #1018) +- Windows, Style: Fixed small rendering issues with menu bar, resize grip and + scrollbar when using thick border sizes. (#8267, #7887) +- Windows: Fixed IsItemXXXX() functions not working on append-version of EndChild(). (#8350) + Also made some of the fields accessible after BeginChild() to match Begin() logic. +- Error Handling: Recovery from missing EndMenuBar() call. (#1651) +- Tables, Menus: Fixed using BeginTable() in menu layer (any menu bar). (#8355) + It previously overrode the current layer back to main layer, which caused an issue + with MainMenuBar attempted to release focus when leaving the menu layer. +- Tables, Menus: Fixed tables or child windows submitted inside BeginMainMenuBar() + being unable to save their settings, as the main menu bar uses _NoSavedSettings. (#8356) +- ColorEdit, ColorPicker: Fixed alpha preview broken in 1.91.7. (#8336, #8241). [@PathogenDavid] +- Tabs, Style: reworked selected overline rendering to better accommodate + for rounded tabs. Reduced default thickness (style.TabBarOverlineSize), + increased default rounding (style.TabRounding). (#8334) [@Kian738, @ocornut] +- Debug Tools: Tweaked font preview. +- ImDrawList: texture baked storage for thick line reduced from ~64x64 to ~32x32. (#3245) +- Fonts: IndexLookup[] table hold 16-bit values even in ImWchar32 mode, + as it accounts for number of glyphs in same font. This is favorable to + CalcTextSize() calls touching less memory. +- Fonts: OversampleH/OversampleV defaults to 0 for automatic selection. + - OversampleH == 0 --> use 1 or 2 depending on font size and use of PixelSnapH. + - OversampleV == 0 --> always use 1. +- ImFontAtlas: made calling ClearFonts() call ClearInputData(), as calling + one without the other is never correct. (#8174, #6556, #6336, #4723) +- Examples: DirectX12: Reduced number of frame in flight from 3 to 2 in + provided example, to reduce latency. +- Examples: Vulkan: better handle VK_SUBOPTIMAL_KHR being returned by + vkAcquireNextImageKHR() or vkQueuePresentKHR(). (#7825, #7831) [@NostraMagister] +- Backends: SDL2: removed assert preventing using ImGui_ImplSDL2_SetGamepadMode() + with ImGui_ImplSDL2_GamepadMode_Manual and an empty array. (#8329) +- Backends: SDL3: removed assert preventing using ImGui_ImplSDL3_SetGamepadMode() + with ImGui_ImplSDL3_GamepadMode_Manual and an empty array. (#8329) +- Backends: SDLGPU3: Exposed ImGui_ImplSDLGPU3_CreateDeviceObjects()/_DestroyDeviceObjects(). + Removed return value from ImGui_ImplSDLGPU3_CreateFontsTexture(). (#8163, #7998, #7988) +- Backends: SDL_Renderer2/3: Use endian-dependent RGBA32 texture format, to match + SDL_Color. (#8327) [@dkosmari] +- Backends: DirectX12: Texture upload use the command queue provided in + ImGui_ImplDX12_InitInfo instead of creating its own. +- Backends: OSX: Removed notification observer when shutting down. (#8331) [@jrachele] + + +----------------------------------------------------------------------- + VERSION 1.91.7 (Released 2025-01-14) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.7 + +Breaking changes: + +- TreeNode: renamed ImGuiTreeNodeFlags_SpanTextWidth to ImGuiTreeNodeFlags_SpanLabelWidth + for consistency with other names. Kept redirection enum (will obsolete). (#6937) + +Other changes: + +- Fixed issues with IsItemDeactivated() and IsItemDeactivatedAfterEdit() not + emitting a reliable signal when an item is deactivated externally: e.g. + via an explicit clear of focus, clear of active id, opening of modal etc. + (#5184, #5904, #6766, #8303, #8004) + - It used to work when the interruption happened in the frame before the + active item as submitted, but not after. It should work in both cases now. + - While this is not specific to a certain widgets, typically it would + mostly be noticeable on InputText() because it keeps ActiveId for a + longer time while allowing other interaction to happen. +- Error Handling: Fixed bugs recovering from within a table that created + a child window, and from nested child windows. (#1651) +- Error Handling: Turned common EndTable() and other TableXXX functions + fail cases into a recoverable error. (#1651, #8314) +- Error Handling: Basic error handling options in Demo->Tools->Debug Options. (#1651) +- InputText: Fixed a bug where character replacements performed from a callback + were not applied when pasting from clipboard. (#8229) +- InputText: Fixed issue when activating a ReadOnly field when the underlying + value is being modified. (#8242) +- InputText: Added sanity check to detect some cases of passing a non + zero-terminated input buffer. +- InputText: Fixed not calling CallbackEdit on revert/clear with Escape key, + although IsItemEdited() was behaving correctly. (#8273) +- Tables: Fixed TableAngledHeadersRow() creating an infinite horizontal + scrolling region when the table is hosted in a viewport with negative + coordinates (left of primary monitor, with multi-viewports enabled). +- Tables, MultiSelect: Fixed an issue where column width may be mismeasured + when calling BeginMultiSelect() while inside a table. (#8250) +- TreeNode, Tables: Added ImGuiTreeNodeFlags_LabelSpanAllColumns to make + the label (not only the highlight/frame) also spans all columns. This is + useful for table rows where you know nothing else is submitted. (#8318, #3565) + Obviously best used with ImGuiTableFlags_NoBordersInBodyUntilResize. +- Selectable: Fixed horizontal label alignment when combined with using + ImGuiSelectableFlags_SpanAllColumns. (#8338) +- Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to disable keyboard + modifiers altering the tweak speed. Useful if you want to alter tweak speed + yourself based on your own logic. (#8223) +- Nav: Fixed an issue where Alt key would clear current active item on + windows with the ImGuiWindowFlags_NoNavInputs flag. (#8231) +- Debug Tools: Debug Log: hovering 0xXXXXXXXX values in log is allowed even + if a popup is blocking mouse access to the debug log window. (#5855) +- Debug Tools: Item Picker: Always available in Tools menu regardless of value + of io.ConfigDebugIsDebuggerPresent. (#2673) +- Fonts: Fixed miscalculation of Ellipsis ("...") character width when automatically + created from a single comma character, affecting some fonts/settings (not all). +- Demo: Added label edition to Property Editor demo + fix an ID issue. (#8266) [@moritz-h] +- Misc: Fixed misc/cpp/imgui_stdlib.h/.cpp not supporting IMGUI_DISABLE. (#8294) [@juur] +- Misc: Fixed MinGW builds not using UTF-8 friendly _wfopen(). (#8300) +- Backends: SDLGPU3 for SDL3: Added backend for SDL_GPU! (#8163, #7998, #7988) [@DeltaW0x]. +- Backends: SDL3: Added ImGui_ImplSDL3_InitForSDLGPU() for consistency, even + though it is currently not doing anything particular. (#8163, #7998, #7988) +- Backends: Allegro5: Avoid calling al_set_mouse_cursor() repeatedly since it appears + to leak on on X11 (#8256). [@Helodity] +- Backends: Metal: Fixed leaks when using metal-cpp. (#8276, #8166) [@selimsandal] +- Backends: Metal: Fixed resource leak when using multiple contexts. (#7419) [@anszom] +- Backends: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for + platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) [@Zer0xFF] +- Backends: Vulkan: Added a few more ImGui_ImplVulkanH_XXX helper functions + primarily for the purpose of making our examples simpler. +- Backends: Vulkan: Added IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE to clarify + how many image sampler descriptors are expected to be available in the provided + descriptor pool. Current backend needs 1 but it is expected that by end of Q1 2025 + this number will grow (will stay a small number). (#6642) +- Backends: DX11: Expose vertex constant buffer in ImGui_ImplDX11_RenderState. + Reset projection matrix in ImDrawCallback_ResetRenderState handlers. (#6969, #5834, #7468, #3590) +- Backends: DX10: Expose ImGui_ImplDX10_RenderState for completeness. (#6969, #5834, #7468, #3590) +- Examples: Added Win32+Vulkan example for completeness. (#8180) [@jristic] + + +----------------------------------------------------------------------- + VERSION 1.91.6 (Released 2024-12-11) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.6 + Breaking changes: - Backends: DX12: Changed ImGui_ImplDX12_Init() signature to take a @@ -48,17 +1713,40 @@ Breaking changes: - We provide convenience legacy fields to pass a single descriptor, matching the old API, but upcoming features will want multiple. - Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete). +- Misc: changed CRC32 table from CRC32-adler to CRC32c polynomial in order to + be compatible with the result of SSE 4.2 instructions. (#8169, #4933) [@Teselka] + - As a result, some .ini data may be partially lost when storing checksums + (docking and tables information particularly). + - Because some users have crafted and storing .ini data as a way to workaround + limitations of the docking API, we are providing a '#define IMGUI_USE_LEGACY_CRC32_ADLER' + compile-time option to keep using old CRC32 tables if you cannot afford invalidating + old .ini data. Other changes: -- Error Handling: fixed cases where recoverable error handling would crash when +- Error Handling: fixed cases where recoverable error handling would crash when processing errors outside of the NewFrame()..EndFrame() scope. (#1651) +- Tables: fixed SetNextWindowScroll() value being ignored by BeginTable() during + the first frame or when scrolling flags have changed. (#8196) +- InputText: added ImGuiInputTextFlags_ElideLeft to elide left side and ensure right side + of contents is visible when whole text is not fitting (useful for paths/filenames). + (#1442, #1440, #4391, #7208, #8216) [@kucoman, @ocornut] +- InputText: reactivating last activated InputText() doesn't restore horizontal scrolling + (which was disabled during deactivation anyway). - Misc: changed embedded ProggyClean encoding to save a bit of binary space (~12kb to 9.5kb). - Misc: added IMGUI_DISABLE_DEFAULT_FONT to strip embedded font from binary. (#8161) [@demonese] -- Tools: binary_to_compressed_c: added -u8/-u32/-base85 export options. - Demo: example tree used by Property Editor & Selection demos properly freed on application closure. (#8158) [@Legulysse] +- Fonts: fixed AddCustomRect() not being packed with TexGlyphPadding + not accounted + for surface area used to determine best-guess texture size. (#8107) [@YarikTH, @ocornut] +- Misc: use SSE 4.2 crc32 instructions when available. (#8169, #4933) [@Teselka] +- Tools: binary_to_compressed_c: added -u8/-u32/-base85 export options. +- Backends: DirectX12: Let user specifies the DepthStencilView format by setting + ImGui_ImplDX12_InitInfo::DSVFormat. (#8217) [@bmarques1995] +- Backends: Vulkan: Make user-provided descriptor pool optional. As a convenience, + when setting init_info->DescriptorPoolSize then the backend will create and manage + one itself. (#8172, #4867) [@zeux] - Examples: Win32+DX12: Using a basic free-list allocator to manage multiple SRV descriptors. @@ -72,11 +1760,13 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v Breaking changes: - Commented out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before). - - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022). + - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022). Use IsKeyDown() instead. - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022). + - GetKeyIndex() is removed (obsoleted March 2022). The indirection is now unnecessary. - Pre-1.87 backends are not supported: - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields. - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields. + - you can use IsKeyDown() instead of reading from io.KeysDown[]. - For more references: - read 1.87 and 1.88 part of API BREAKING CHANGES in imgui.cpp or read Changelog for 1.87 and 1.88. - read https://github.com/ocornut/imgui/issues/4921 @@ -96,7 +1786,7 @@ Other changes: initial mouse down event. - Note that it may reveal incorrect usage if you were using InputInt/InputFloat without persistent storage by relying solely on e.g. IsItemDeactivatedAfterEdit(): - this was never supported and didn't work consistantly (see #8149). + this was never supported and didn't work consistently (see #8149). - InputText: fixed a bug (regression in 1.91.2) where modifying text buffer within a callback would sometimes prevents further appending to the buffer. - Tabs, Style: made ImGuiCol_TabDimmedSelectedOverline alpha 0 (not visible) in default @@ -140,7 +1830,7 @@ Breaking changes: allows casting any pointer/integer type without warning: - May warn: ImGui::Image((void*)MyTextureData, ...); - May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...); - - Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...); + - Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...); - Note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors and extra render parameters) if you like. - IO: moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool. @@ -162,7 +1852,7 @@ Other changes: - Set io.ConfigNavCursorVisibleAuto = true (default) to enable automatic toggling of cursor visibility (mouse click hide the cursor, arrow keys makes it visible). - Set io.ConfigNavCursorVisibleAlways to keep cursor always visible. - - Nav: added NavSetCursorVisible(bool visible) function to manipulate visibility of + - Nav: added SetNavCursorVisible(bool visible) function to manipulate visibility of navigation cursor (e.g. set default state, or after some actions). (#1074, #2048, #7237, #8059) - Nav: added io.ConfigNavEscapeClearFocusItem and io.ConfigNavEscapeClearFocusWindow to change how pressing Escape affects navigation. (#8059, #2048, #1074, #3200) @@ -200,7 +1890,7 @@ Other changes: supported by InputFloat, InputInt, InputScalar etc. widgets. It actually never was. (#8065, #3946) - imgui_freetype: Added support for plutosvg (as an alternative to lunasvg) to render OpenType SVG fonts. Requires defining IMGUI_ENABLE_FREETYPE_PLUTOSVG along with IMGUI_ENABLE_FREETYPE. - Providing headers/librairies for plutosvg + plutovg is up to you (see #7927 for help). + Providing headers/libraries for plutosvg + plutovg is up to you (see #7927 for help). (#7927, #7187, #6591, #6607) [@pthom] - Backends: DX11, DX12, SDLRenderer2/3. Vulkan, WGPU: expose selected state in ImGui_ImplXXXX_RenderState structures during render loop user draw callbacks. @@ -279,7 +1969,7 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v Breaking changes: - - Internals: using multiple overlayed ButtonBehavior() with same ID will now have the + - Internals: using multiple overlaid ButtonBehavior() with same ID will now have the io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030) It was one of the rare case where using same ID is legal. Workarounds: - use single ButtonBehavior() call with multiple _MouseButton flags @@ -383,7 +2073,7 @@ Other changes: by an extra pixel + rework the change so that contents doesn't overlap the bottom and right border in a scrolling table. (#6765, #3752, #7428) - Tables: fixed an issue resizing columns or querying hovered column/row when using multiple - synched instances that are layed out at different X positions. (#7933) + synced instances that are laid out at different X positions. (#7933) - Tabs: avoid queuing a refocus when tab is already focused, which would have the side-effect of e.g. closing popup on a mouse release. (#7914) - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) @@ -616,7 +2306,7 @@ Other changes: - Windows: BeginChild(): fixed a glitch when during a resize of a child window which is tightly close to the boundaries of its parent (e.g. with zero WindowPadding), the child position could have temporarily be moved around by erroneous padding application. (#7706) -- TabBar, Style: added ImGuiTabBarFlags_DrawSelectedOverline option to draw an horizontal +- TabBar, Style: added ImGuiTabBarFlags_DrawSelectedOverline option to draw a horizontal line over selected tabs to increase visibility. This is used by docking. Added corresponding ImGuiCol_TabSelectedOverline and ImGuiCol_TabDimmedSelectedOverline colors. - Tables: added TableGetHoveredColumn() to public API, as an alternative to testing for @@ -679,7 +2369,7 @@ Other changes: - Scrollbar: made scrolling logic more standard: clicking above or below the grab scrolls by one page, holding mouse button repeats scrolling. (#7328, #150) - Scrollbar: fixed miscalculation of vertical scrollbar visibility when required - solely by the presence of an horizontal scrollbar. (#1574) + solely by the presence of a horizontal scrollbar. (#1574) - InputScalar, InputInt, InputFloat: added ImGuiInputTextFlags_ParseEmptyRefVal to parse an empty field as zero-value. (#7305) [@supermerill, @ocornut] - InputScalar, InputInt, InputFloat: added ImGuiInputTextFlags_DisplayEmptyRefVal @@ -813,7 +2503,7 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v Breaking changes: -- TreeNode: Fixed a layout inconsistency when using a empty/hidden label followed +- TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282) Before: TreeNode("##Hidden"); SameLine(); Text("Hello"); // This was actually incorrect! BUT appeared to look ok with the default style @@ -912,7 +2602,7 @@ Other changes: Note that only simple polygons (no self-intersections, no holes) are supported. - DrawList: Allow AddText() to accept null ranges. (#3615, 7391) - Docs: added more wiki links to headers of imgui.h/imgui.cpp to facilitate discovery - of interesting resources, because github doesn't allow Wiki to be crawled by search engines. + of interesting resources, because GitHub doesn't allow Wiki to be crawled by search engines. - This is the main wiki: https://github.com/ocornut/imgui/wiki - This is the crawlable version: https://github-wiki-see.page/m/ocornut/imgui/wiki Adding a link to the crawlable version, even though it is not intended for humans, @@ -1064,7 +2754,7 @@ Other changes: - BeginChild(): Resize borders rendered even when ImGuiWindowFlags_NoBackground is specified. (#1710, #7194) - Fixed some auto-resizing path using style.WindowMinSize.x (instead of x/y) - for both axises since 1.90. (#7106) [@n0bodysec] + for both axes since 1.90. (#7106) [@n0bodysec] - Scrolling: internal scrolling value is rounded instead of truncated, as a way to reduce speed asymmetry when (incorrectly) attempting to scroll by non-integer amount. (#6677) - Navigation (Keyboard/gamepad): @@ -1222,7 +2912,7 @@ Other changes: - Combining this with also specifying ImGuiChildFlags_AlwaysAutoResize disables this optimization, meaning child contents will never be clipped (not recommended). - Please be considerate that child are full windows and carry significant overhead: - combining auto-resizing for both axises to create a non-scrolling child to merely draw + combining auto-resizing for both axes to create a non-scrolling child to merely draw a border would be better more optimally using BeginGroup(). (see #1496) (until we come up with new helpers for framed groups and work-rect adjustments). - BeginChild(): made it possible to use SetNextWindowSizeConstraints() rectangle, often @@ -1284,7 +2974,7 @@ Other changes: parent-menu would erroneously close the child-menu. (Regression from 1.88). (#6869) - MenuBar: Fixed an issue where layouting an item in the menu-bar would erroneously register contents size in a way that would affect the scrolling layer. - Was most often noticeable when using an horizontal scrollbar. (#6789) + Was most often noticeable when using a horizontal scrollbar. (#6789) - InputText: - InputTextMultiline: Fixed a crash pressing Down on last empty line of a multi-line buffer. (regression from 1.89.2, only happened in some states). (#6783, #6000) @@ -1490,7 +3180,7 @@ Breaking changes: - Moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only - tweaked once during app initialisation, we are exceptionally accepting the breakage. + tweaked once during app initialization, we are exceptionally accepting the breakage. Majority of users should not even notice. - Overlapping items: (#6512, #3909, #517) - Added 'SetNextItemAllowOverlap()' (called before an item) as a replacement for using @@ -1590,7 +3280,7 @@ Breaking changes: - Commented out obsolete/redirecting functions that were marked obsolete more than two years ago: - ListBoxHeader() -> use BeginListBox() - ListBoxFooter() -> use EndListBox() - - Note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for refeence. + - Note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference. - Backends: SDL_Renderer: Renamed 'imgui_impl_sdlrenderer.h/cpp' to 'imgui_impl_sdlrenderer2.h/cpp', in order to accommodate for upcoming SDL3 and change in its SDL_Renderer API. (#6286) - Backends: GLUT: Removed call to ImGui::NewFrame() from ImGui_ImplGLUT_NewFrame(). @@ -1754,7 +3444,7 @@ Other changes: - Public API: PushTabStop(false) / PopTabStop() - Internal: PushItemFlag(ImGuiItemFlags_NoTabStop, true); - Internal: Directly pass ImGuiItemFlags_NoTabStop to ItemAdd() for custom widgets. -- Nav: Tabbing/Shift-Tabbing can more reliably be used to step out of an item that is not +- Nav: Tabbing/Shift+Tabbing can more reliably be used to step out of an item that is not tab-stoppable. (#3092, #5759, #787) - Nav: Made Enter key submit the same type of Activation event as Space key, allowing to press buttons with Enter. (#5606) @@ -1817,20 +3507,20 @@ Other changes: due to how unique table instance id was generated. (#6140) [@ocornut, @rodrigorc] - Inputs, Scrolling: Made horizontal scroll wheel and horizontal scroll direction consistent across backends/os. (#4019, #6096, #1463) [@PathogenDavid, @ocornut, @rokups] - - Clarified that 'wheel_y > 0.0f' scrolls Up, 'wheel_y > 0.0f' scrolls Down. - Clarified that 'wheel_x > 0.0f' scrolls Left, 'wheel_x > 0.0f' scrolls Right. + - Clarified that 'wheel_y > 0.0f' scrolls Up, 'wheel_y < 0.0f' scrolls Down. + Clarified that 'wheel_x > 0.0f' scrolls Left, 'wheel_x < 0.0f' scrolls Right. - Backends: Fixed horizontal scroll direction for Win32 and SDL backends. (#4019) - Shift+WheelY support on non-OSX machines was already correct. (#2424, #1463) (whereas on OSX machines Shift+WheelY turns into WheelX at the OS level). - If you use a custom backend, you should verify horizontal wheel direction. - - Axises are flipped by OSX for mouse & touch-pad when 'Natural Scrolling' is on. - - Axises are flipped by Windows for touch-pad when 'Settings->Touchpad->Down motion scrolls up' is on. + - Axes are flipped by OSX for mouse & touch-pad when 'Natural Scrolling' is on. + - Axes are flipped by Windows for touch-pad when 'Settings->Touchpad->Down motion scrolls up' is on. - You can use 'Demo->Tools->Debug Log->IO" to visualize values submitted to Dear ImGui. - Known issues remaining with Emscripten: - The magnitude of wheeling values on Emscripten was improved but isn't perfect. (#6096) - When running the Emscripten app on a Mac with a mouse, SHIFT+WheelY doesn't turn into WheelX. This is because we don't know that we are running on Mac and apply our own Shift+swapping - on top of OSX' own swapping, so wheel axises are swapped twice. Emscripten apps may need + on top of OSX's own swapping, so wheel axes are swapped twice. Emscripten apps may need to find a way to detect this and set io.ConfigMacOSXBehaviors manually (if you know a way let us know!), or offer the "OSX-style behavior" option to their user. - Window: Avoid rendering shapes for hidden resize grips. @@ -1856,7 +3546,7 @@ Other changes: values for io.DeltaTime, and browser features such as "privacy.resistFingerprinting=true" can exacerbate that. (#6114, #3644) - Backends: OSX: Fixed scroll/wheel scaling for devices emitting events with - hasPreciseScrollingDeltas==false (e.g. non-Apple mices). + hasPreciseScrollingDeltas==false (e.g. non-Apple mice). - Backends: Win32: flipping WM_MOUSEHWHEEL horizontal value to match other backends and offer consistent horizontal scrolling direction. (#4019) - Backends: SDL2: flipping SDL_MOUSEWHEEL horizontal value to match other backends and @@ -2060,7 +3750,7 @@ Other Changes: - Scrolling: Mitigated issue where multi-axis mouse-wheel inputs (usually from touch pad events) are incorrectly locking scrolling in a parent window. (#4559, #3795, #2604) - Scrolling: Exposed SetNextWindowScroll() in public API. Useful to remove a scrolling - delay in some situations where e.g. windows need to be synched. (#1526) + delay in some situations where e.g. windows need to be synced. (#1526) - InputText: added experimental io.ConfigInputTextEnterKeepActive feature to make pressing Enter keep the input active and select all text. - InputText: numerical fields automatically accept full-width characters (U+FF01..U+FF5E) @@ -2104,7 +3794,7 @@ Other Changes: - Menus: Fixed using IsItemHovered()/IsItemClicked() on BeginMenu(). (#5775) - Menus, Popups: Experimental fix for issue where clicking on an open BeginMenu() item called from a window which is neither a popup neither a menu used to incorrectly close and reopen the menu - (the fix may have side-effect and is labelld as experimental as we may need to revert). (#5775) + (the fix may have side-effect and is labelled as experimental as we may need to revert). (#5775) - Menus, Nav: Fixed keyboard/gamepad navigation occasionally erroneously landing on menu-item in parent window when the parent is not a popup. (#5730) - Menus, Nav: Fixed not being able to close a menu with Left arrow when parent is not a popup. (#5730) @@ -2242,7 +3932,7 @@ Other Changes: always lead to menu closure. Fixes using items that are not MenuItem() or BeginItem() at the root level of a popup with a child menu opened. - Menus: Menus emitted from the main/scrolling layer are not part of the same menu-set as menus emitted - from the menu-bar, avoiding accidental hovering from one to the other. (#3496, #4797) [@rokups] + from the menu-bar, avoiding accidental hovering from one to the other. (#3496, #4797) [@rokups] - Style: Adjust default value of GrabMinSize from 10.0f to 12.0f. - Stack Tool: Added option to copy item path to clipboard. (#4631) - Settings: Fixed out-of-bounds read when .ini file on disk is empty. (#5351) [@quantum5] @@ -2309,6 +3999,7 @@ Breaking Changes: - For all calls to IO new functions, the Dear ImGui context should be bound/current. - Reworked IO keyboard input API: (#4921, #2625, #3724) [@thedmd, @ocornut] - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. + - You can use IsKeyDown() instead of reading from io.KeysDown[]. - For keyboard modifiers, you can call io.AddKeyEvent() with ImGuiKey_ModXXX values, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. - Added io.SetKeyEventNativeData() function (optional) to pass native and old legacy indices. @@ -2343,7 +4034,7 @@ Breaking Changes: io.AddKeyEvent(), io.AddKeyAnalogEvent(). - Added io.AddKeyAnalogEvent() function, obsoleting writing directly to io.NavInputs[] arrays. - Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) -- Removed support for legacy arithmetic operators (+,+-,*,/) when inputing text into a slider/drag. (#4917, #3184) +- Removed support for legacy arithmetic operators (+,+-,*,/) when inputting text into a slider/drag. (#4917, #3184) This doesn't break any api/code but a feature that was accessible by end-users (which seemingly no one used). (Instead you may implement custom expression evaluators to provide a better version of this). - Backends: GLFW: backend now uses glfwSetCursorPosCallback(). @@ -2399,7 +4090,7 @@ Other Changes: - Backends: GLFW: Retrieve mouse position using glfwSetCursorPosCallback() + fallback when focused but not hovered/captured. - Backends: GLFW: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4921) - Backends: GLFW: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing - callbacks after iniitializing backend. (#4981) + callbacks after initializing backend. (#4981) - Backends: Win32: Submit keys and key mods using io.AddKeyEvent(). (#2625, #4921) - Backends: Win32: Retrieve mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. - Backends: Win32: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4921) @@ -2481,7 +4172,7 @@ Other Changes: - Menus: fixed sub-menu items inside a popups from closing the popup. - Menus: fixed top-level menu from not consistently using style.PopupRounding. (#4788) - InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) -- Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) +- InputText, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - InputText: fixed ReadOnly flag preventing callbacks from receiving the text buffer. (#4762) [@actondev] @@ -2512,7 +4203,7 @@ Other Changes: - Misc: Fix MinGW DLL build issue (when IMGUI_API is defined). [@rokups] - CI: Add MinGW DLL build to test suite. [@rokups] - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce - likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling + likelihood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) - Backends: OpenGL3: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers, and perhaps improve performances. (#4468, #4504, #2981, #3381) [@parbo] @@ -2588,7 +4279,7 @@ Other Changes: - Nav: Fixed vertical scoring offset when wrapping on Y in a decorated window. - Nav: Improve scrolling behavior when navigating to an item larger than view. - TreePush(): removed unnecessary/inconsistent legacy behavior where passing a NULL value to - the TreePush(const char*) and TreePush(const void*) functions would use an hard-coded replacement. + the TreePush(const char*) and TreePush(const void*) functions would use a hard-coded replacement. The only situation where that change would make a meaningful difference is TreePush((const char*)NULL) (_explicitly_ casting a null pointer to const char*), which is unlikely and will now crash. You may replace it with anything else. @@ -2711,9 +4402,9 @@ Other Changes: - Fonts: Functions with a 'float size_pixels' parameter can accept zero if it is set in ImFontSize::SizePixels. - Fonts: Prefer using U+FFFD character for fallback instead of '?', if available. (#4269) - Fonts: Use U+FF0E dot character to construct an ellipsis if U+002E '.' is not available. (#4269) -- Fonts: Added U+FFFD ("replacement character") to default asian glyphs ranges. (#4269) +- Fonts: Added U+FFFD ("replacement character") to default Asian glyphs ranges. (#4269) - Fonts: Fixed calling ClearTexData() (clearing CPU side font data) triggering an assert in NewFrame(). (#3487) -- DrawList: Fixed AddCircle/AddCircleFilled() with auto-tesselation not using accelerated paths for small circles. +- DrawList: Fixed AddCircle/AddCircleFilled() with auto-tessellation not using accelerated paths for small circles. Fixed AddCircle/AddCircleFilled() with 12 segments which had a broken edge. (#4419, #4421) [@thedmd] - Demo: Fixed requirement in 1.83 to link with imgui_demo.cpp if IMGUI_DISABLE_METRICS_WINDOW is not set. (#4171) Normally the right way to disable compiling the demo is to set IMGUI_DISABLE_DEMO_WINDOWS, but we want to avoid @@ -2757,7 +4448,7 @@ Other Changes: - Examples: OSX+OpenGL2: Fix event forwarding (fix key remaining stuck when using shortcuts with Cmd/Super key). Other OSX examples were not affected. (#4253, #1873) [@rokups] - Examples: Updated all .vcxproj to VS2015 (toolset v140) to facilitate usage with vcpkg. -- Examples: SDL2: Accommodate for vcpkg install having headers in SDL2/SDL.h vs SDL.h. +- Examples: SDL2: Accommodate for vcpkg install having headers in SDL2/SDL.h vs SDL.h. ----------------------------------------------------------------------- @@ -2879,7 +4570,7 @@ Breaking Changes: - ImDrawList: clarified that PathArcTo()/PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would lead to counter-clockwise paths which and have an effect on anti-aliasing. - InputText: renamed ImGuiInputTextFlags_AlwaysInsertMode to ImGuiInputTextFlags_AlwaysOverwrite, old name was an - incorrect description of behavior. Was ostly used by memory editor. Kept inline redirection function. (#2863) + incorrect description of behavior. Was mostly used by memory editor. Kept inline redirection function. (#2863) - Moved 'misc/natvis/imgui.natvis' to 'misc/debuggers/imgui.natvis' as we will provide scripts for other debuggers. - Style: renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as its meaning changed. (#3808) [@thedmd] @@ -2977,7 +4668,7 @@ Other Changes: - For a Platform Monitor, the work area is generally the full area minus space used by task-bars. - All of this has been the case in 'docking' branch for a long time. What we've done is merely merging a small chunk of the multi-viewport logic into 'master' to standardize some concepts ahead of time. -- Tables: Fixed PopItemWidth() or multi-components items not restoring per-colum ItemWidth correctly. (#3760) +- Tables: Fixed PopItemWidth() or multi-components items not restoring per-column ItemWidth correctly. (#3760) - Window: Fixed minor title bar text clipping issue when FramePadding is small/zero and there are no close button in the window. (#3731) - SliderInt: Fixed click/drag when v_min==v_max from setting the value to zero. (#3774) [@erwincoumans] @@ -3014,7 +4705,7 @@ Other Changes: User needs to call ImGui_ImplVulkan_LoadFunctions() with their custom loader prior to other functions. - Backends: Metal: Fixed texture storage mode when building on Mac Catalyst. (#3748) [@Belinsky-L-V] - Backends: OSX: Fixed mouse position not being reported when mouse buttons other than left one are down. (#3762) [@rokups] -- Backends: WebGPU: Added enderer backend for WebGPU support (imgui_impl_wgpu.cpp) (#3632) [@bfierz] +- Backends: WebGPU: Added renderer backend for WebGPU support (imgui_impl_wgpu.cpp) (#3632) [@bfierz] Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break. - Examples: WebGPU: Added Emscripten+WebGPU example. (#3632) [@bfierz] - Backends: GLFW: Added ImGui_ImplGlfw_InitForOther() initialization call to use with non OpenGL API. (#3632) @@ -3213,12 +4904,12 @@ Other Changes: - Columns: Fix inverted ClipRect being passed to renderer when using certain primitives inside of a fully clipped column. (#3475) [@szreder] - Popups, Tooltips: Fix edge cases issues with positioning popups and tooltips when they are larger than - viewport on either or both axises. [@Rokups] + viewport on either or both axes. [@Rokups] - Fonts: AddFontDefault() adjust its vertical offset based on floor(size/13) instead of always +1. Was previously done by altering DisplayOffset.y but wouldn't work for DPI scaled font. - Metrics: Various tweaks, listing windows front-to-back, greying inactive items when possible. - Demo: Add simple InputText() callbacks demo (aside from the more elaborate ones in 'Examples->Console'). -- Backends: OpenGL3: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 contexts which have +- Backends: OpenGL3: Fix to avoid compiling/calling glBindSampler() on ES or pre-3.3 contexts which have the defines set by a loader. (#3467, #1985) [@jjwebb] - Backends: Vulkan: Some internal refactor aimed at allowing multi-viewport feature to create their own render pass. (#3455, #3459) [@FunMiles] @@ -3307,7 +4998,7 @@ Other Changes: and allowed to pass them to InvisibleButton(): ImGuiButtonFlags_MouseButtonLeft/Right/Middle. This is a small but rather important change because lots of multi-button behaviors could previously only be achieved using lower-level/internal API. Now also available via high-level InvisibleButton() - with is a de-facto versatile building block to creating custom widgets with the public API. + with is a de facto versatile building block to creating custom widgets with the public API. - Fonts: Fixed ImFontConfig::GlyphExtraSpacing and ImFontConfig::PixelSnapH settings being pulled from the merged/target font settings when merging fonts, instead of being pulled from the source font settings. @@ -3454,7 +5145,7 @@ Other Changes: ImGuiListClipper as the first thing after Begin() could largely break size calculations. (#3073) - Added optional support for Unicode plane 1-16 (#2538, #2541, #2815) [@cloudwu, @samhocevar] - Compile-time enable with '#define IMGUI_USE_WCHAR32' in imconfig.h. - - More onsistent handling of unsupported code points (0xFFFD). + - More consistent handling of unsupported code points (0xFFFD). - Surrogate pairs are supported when submitting UTF-16 data via io.AddInputCharacterUTF16(), allowing for more complete CJK input. - sizeof(ImWchar) goes from 2 to 4. IM_UNICODE_CODEPOINT_MAX goes from 0xFFFF to 0x10FFFF. @@ -3509,7 +5200,7 @@ Breaking Changes: - ShowTestWindow() -> use ShowDemoWindow() - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow) - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) - - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f) + - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)) - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing() - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding @@ -3538,7 +5229,7 @@ Other Changes: - Inputs: Added ImGuiMouseButton enum for convenience (e.g. ImGuiMouseButton_Right=1). We forever guarantee that the existing value will not changes so existing code is free to use 0/1/2. -- Nav: Fixed a bug where the initial CTRL-Tab press while in a child window sometimes selected +- Nav: Fixed a bug where the initial CTRL+Tab press while in a child window sometimes selected the current root window instead of always selecting the previous root window. (#787) - ColorEdit: Fix label alignment when using ImGuiColorEditFlags_NoInputs. (#2955) [@rokups] - ColorEdit: In HSV display of a RGB stored value, attempt to locally preserve Saturation @@ -3711,7 +5402,7 @@ Other Changes: mostly for consistency. (#2159, #2160) [@goran-w] - Selectable: Added ImGuiSelectableFlags_AllowItemOverlap flag in public api (was previously internal only). - Style: Allow style.WindowMenuButtonPosition to be set to ImGuiDir_None to hide the collapse button. (#2634, #2639) -- Font: Better ellipsis ("...") drawing implementation. Instead of drawing three pixel-ey dots (which was glaringly +- Font: Better ellipsis ("...") drawing implementation. Instead of drawing three pixely dots (which was glaringly unfitting with many types of fonts) we first attempt to find a standard ellipsis glyphs within the loaded set. Otherwise we render ellipsis using '.' from the font from where we trim excessive spacing to make it as narrow as possible. (#2775) [@rokups] @@ -4047,7 +5738,7 @@ Other Changes: - InputText: Fixed an edge case crash that would happen if another widget sharing the same ID is being swapped with an InputText that has yet to be activated. - InputText: Fixed various display corruption related to swapping the underlying buffer while - a input widget is active (both for writable and read-only paths). Often they would manifest + an input widget is active (both for writable and read-only paths). Often they would manifest when manipulating the scrollbar of a multi-line input text. - ColorEdit, ColorPicker, ColorButton: Added ImGuiColorEditFlags_InputHSV to manipulate color values encoded as HSV (in order to avoid HSV<>RGB round trips and associated singularities). @@ -4058,7 +5749,7 @@ Other Changes: reading the 4th float in the array (value was read and discarded). (#2384) [@haldean] - MenuItem, Selectable: Fixed disabled widget interfering with navigation (fix c2db7f63 in 1.67). - Tabs: Fixed a crash when using many BeginTabBar() recursively (didn't affect docking). (#2371) -- Tabs: Added extra mis-usage error recovery. Past the assert, common mis-usage don't lead to +- Tabs: Added extra misusage error recovery. Past the assert, common misusage don't lead to hard crashes any more, facilitating integration with scripting languages. (#1651) - Tabs: Fixed ImGuiTabItemFlags_SetSelected being ignored if the tab is not visible (with scrolling policy enabled) or if is currently appearing. @@ -4101,7 +5792,7 @@ Breaking Changes: - Removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). - Made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). - If for some reason your time step calculation gives you a zero value, replace it with a arbitrarily small value! + If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value! Other Changes: @@ -4199,7 +5890,7 @@ Other Changes: in the parent window, so there is no mismatch between the layout in parent and the position of the child window. - InputFloat: When using ImGuiInputTextFlags_ReadOnly the step buttons are disabled. (#2257) - DragFloat: Fixed broken mouse direction change with power!=1.0. (#2174, #2206) [@Joshhua5] -- Nav: Fixed an keyboard issue where holding Activate/Space for longer than two frames on a button would unnecessary +- Nav: Fixed a keyboard issue where holding Activate/Space for longer than two frames on a button would unnecessary keep the focus on the parent window, which could steal it from newly appearing windows. (#787) - Nav: Fixed animated window titles from being updated when displayed in the CTRL+Tab list. (#787) - Error recovery: Extraneous/undesired calls to End() are now being caught by an assert in the End() function closer @@ -4351,7 +6042,7 @@ Changes: then separate your changes into several patches that can more easily be applied to 1.64 on a per-file basis. What I found worked nicely for me, was to open the diff of the old patches in an interactive merge/diff tool, search for the corresponding function in the new code and apply the chunks manually. -- As a reminder, if you have any change to imgui.cpp it is a good habit to discuss them on the github, +- As a reminder, if you have any change to imgui.cpp it is a good habit to discuss them on the GitHub, so a solution applicable on the Master branch can be found. If your company has changes that you cannot disclose you may also contact me privately. @@ -4386,7 +6077,7 @@ Other Changes: - Nav: Added a CTRL+TAB window list and changed the highlight system accordingly. The change is motivated by upcoming Docking features. (#787) - Nav: Made CTRL+TAB skip menus + skip the current navigation window if is has the ImGuiWindow_NoNavFocus set. (#787) - While it was previously possible, you won't be able to CTRL-TAB out and immediately back in a window with the + While it was previously possible, you won't be able to CTRL+TAB out and immediately back in a window with the ImGuiWindow_NoNavFocus flag. - Window: Allow menu and popups windows from ignoring the style.WindowMinSize values so short menus/popups are not padded. (#1909) - Window: Added global io.ConfigResizeWindowsFromEdges option to enable resizing windows from their edges and from @@ -4421,7 +6112,7 @@ Other Changes: - Fixed assertion when transitioning from an active ID to another within a group, affecting ColorPicker (broken in 1.62). (#2023, #820, #956, #1875). - Fixed PushID() from keeping alive the new ID Stack top value (if a previously active widget shared the ID it would be erroneously kept alive). - Fixed horizontal mouse wheel not forwarding the request to the parent window if ImGuiWindowFlags_NoScrollWithMouse is set. (#1463, #1380, #1502) -- Fixed a include build issue for Cygwin in non-POSIX (Win32) mode. (#1917, #1319, #276) +- Fixed an include build issue for Cygwin in non-POSIX (Win32) mode. (#1917, #1319, #276) - ImDrawList: Improved handling for worst-case vertices reservation policy when large amount of text (e.g. 1+ million character strings) are being submitted in a single call. It would typically have crashed InputTextMultiline(). (#200) - OS/Windows: Fixed missing ImmReleaseContext() call in the default Win32 IME handler. (#1932) [@vby] @@ -4656,7 +6347,7 @@ Other Changes: - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. - Basic controls: arrows to navigate, Alt to enter menus, Space to activate item, Enter to edit text, - Escape to cancel/close, Ctrl-Tab to focus windows, etc. + Escape to cancel/close, Ctrl+Tab to focus windows, etc. - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set. - For more advanced uses, you may want to read from io.NavActive or io.NavVisible. Read imgui.cpp for more details. @@ -4706,7 +6397,7 @@ Other Changes: - Style: Exposed ImGuiStyleVar_WindowTitleAlign, ImGuiStyleVar_ScrollbarSize, ImGuiStyleVar_ScrollbarRounding, ImGuiStyleVar_GrabRounding + added an assert to reduce accidental breakage. (#1181) - Style: Added style.MouseCursorScale help when using the software mouse cursor facility. (#939). -- Style: Close button nows display a cross before hovering. Fixed cross positioning being a little off. Uses button colors for highlight when hovering. (#707) +- Style: Close buttons now display a cross before hovering. Fixed cross positioning being a little off. Uses button colors for highlight when hovering. (#707) - Popup: OpenPopup() Always reopen existing pop-ups. (Removed imgui_internal.h's OpenPopupEx() which was used for this.) (#1497, #1533). - Popup: BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid(), OpenPopupOnItemClick() all react on mouse release instead of mouse press. (~#439) - Popup: Better handling of user mistakenly calling OpenPopup() every frame (with the 'reopen_existing' option). @@ -4729,7 +6420,7 @@ Other Changes: - Drag and Drop: Increased payload type string to 32 characters instead of 8. (#143) - Drag and Drop: TreeNode as drop target displays rectangle over full frame. (#1597, #143) - DragFloat: Fix/workaround for backends which do not preserve a valid mouse position when dragged out of bounds. (#1559) -- InputFloat: Allow inputing value using scientific notation e.g. "1e+10". +- InputFloat: Allow inputting value using scientific notation e.g. "1e+10". - InputDouble: Added InputDouble() function. We use a format string instead of a 'decimal_precision' parameter to also for "%e" and variants. (#1011) - Slider, Combo: Use ImGuiCol_FrameBgHovered color when hovered. (#1456) [@stfx] @@ -4774,7 +6465,7 @@ Other Changes: - Demo: Improved Selectable() examples. (#1528) - Demo: Tweaked the Child demos, added a menu bar to the second child to test some navigation functions. - Demo: Console: Using ImGuiCol_Text to be more friendly to color changes. -- Demo: Using IM_COL32() instead of ImColor() in ImDrawList centric contexts. Trying to phase out use of the ImColor helper whenever possible. +- Demo: Using IM_COL32() instead of ImColor() in ImDrawList-centric contexts. Trying to phase out use of the ImColor helper whenever possible. - Examples: Files in examples/ now include their own changelog so it is easier to occasionally update your backends if needed. - Examples: Using Dark theme by default. (#707). Tweaked demo code. - Examples: Added support for horizontal mouse wheel for API that allows it. (#1463) [@tseeker] @@ -4892,7 +6583,7 @@ Other Changes: - Window: Added ImGuiWindowFlags_ResizeFromAnySide flag to resize from any borders or from the lower-left corner of a window. This requires your backend to honor GetMouseCursor() requests for full usability. (#822) -- Window: Sizing fixes when using SetNextWindowSize() on individual axises. +- Window: Sizing fixes when using SetNextWindowSize() on individual axes. - Window: Hide new window for one frame until they calculate their size. Also fixes SetNextWindowPos() given a non-zero pivot. (#1694) - Window: Made mouse wheel scrolling accommodate better to windows that are smaller than the scroll step. @@ -5194,7 +6885,7 @@ Other Changes: check the Demo window and comment for `ImGuiColorEditFlags_`. Some of the options it supports are: two color picker types (hue bar + sat/val rectangle, hue wheel + rotating sat/val triangle), display as u8 or float, lifting 0.0..1.0 constraints - (currently rgba only), context menus, alpha bar, background checkerboard options, preview tooltip, + (currently rgba only), context menus, alpha bar, background checkerboard options, preview tooltip, basic revert. For simple use, calling the existing `ColorEdit4()` function as you did before will be enough, as you can now open the color picker from there. - Added `SetColorEditOptions()` to set default color options (e.g. if you want HSV over RGBA, @@ -5296,7 +6987,7 @@ Other Changes: certain style settings and zero WindowMinSize). - EndGroup(): Made IsItemHovered() work when an item was activated within the group. (#849) - BulletText(): Fixed stopping to display formatted string after the '##' mark. -- Closing the focused window restore focus to the first active root window in descending z-order .(part of #727) +- Closing the focused window restore focus to the first active root window in descending z-order. (part of #727) - Word-wrapping: Fixed a bug where we never wrapped after a 1 character word. [@sronsse] - Word-wrapping: Fixed TextWrapped() overriding wrap position if one is already set. (#690) - Word-wrapping: Fixed incorrect testing for negative wrap coordinates, they are perfectly legal. (#706) @@ -5333,7 +7024,7 @@ Other Changes: - Demo: ShowStyleEditor: show font character map / grid in more details. - Demo: Console: Fixed a completion bug when multiple candidates are equals and match until the end. - Demo: Fixed 1-byte off overflow in the ShowStyleEditor() combo usage. (#783) [@bear24rw] -- Examples: Accessing ImVector fields directly, feel less stl-ey. (#810) +- Examples: Accessing ImVector fields directly, feel less stl-y. (#810) - Examples: OpenGL*: Saving/restoring existing scissor rectangle for completeness. (#807) - Examples: OpenGL*: Saving/restoring active texture number (the value modified by glActiveTexture). (#1087, #1088, #1116) - Examples: OpenGL*: Saving/restoring separate color/alpha blend functions correctly. (#1120) [@greggman] @@ -5497,7 +7188,7 @@ Other Changes: after a text input modification (e.g. "0.0" --> "0.000" would keep returning true). (#564) - DragFloat(): Always apply value when mouse is held/widget active, so that an always-resetting variable (e.g. non saved local) can be passed. -- InputText(): OS X friendly behaviors: (@zhiayang), (#473) +- InputText(): OS X friendly behaviors: (@zhiayang), (#473) - Word movement uses ALT key; - Shortcuts uses CMD key; - Double-clicking text select a single word; @@ -5622,7 +7313,7 @@ Changes: - PlotHistogram(): improved rendering of histogram with a lot of values. - Dummy(): creates an item so functions such as IsItemHovered() can be used. - BeginChildFrame() helper: added the extra_flags parameter. -- Scrollbar: fixed rounding of background + child window consistenly have ChildWindowBg color under ScrollbarBg fill. (#355). +- Scrollbar: fixed rounding of background + child window consistently have ChildWindowBg color under ScrollbarBg fill. (#355). - Scrollbar: background color less translucent in default style so it works better when changing background color. - Scrollbar: fixed minor rendering offset when borders are enabled. (#365) - ImDrawList: fixed 1 leak per ImDrawList using the ChannelsSplit() API (via Columns). (#318) @@ -5638,7 +7329,7 @@ Changes: - Internal: Extracted a EndFrame() function out of Render() but kept it internal/private + clarified some asserts. (#335) - Internal: Added missing IMGUI_API definitions in imgui_internal.h (#326) - Internal: ImLoadFileToMemory() return void\* instead of taking void*\* + allow optional int\* file_size. -- Demo: Horizontal scrollbar demo allows to enable simultanaeous scrollbars on both axises. +- Demo: Horizontal scrollbar demo allows to enable simultaneous scrollbars on both axes. - Tools: binary_to_compressed_c.cpp: added -nocompress option. - Examples: Added example for the Marmalade platform. - Examples: Added batch files to build Windows examples with VS. @@ -5683,7 +7374,7 @@ Other Changes: - ImDrawList: Added an assert on overflowing index value (#292). - ImDrawList: Fixed issues with channels split/merge. Now functional without manually adding a draw cmd. Added comments. - ImDrawData: Added ScaleClipRects() helper useful when rendering scaled. (#287). -- Fixed Bullet() inconsistent layout behaviour when clipped. +- Fixed Bullet() inconsistent layout behavior when clipped. - Fixed IsWindowHovered() not taking account of window hoverability (may be disabled because of a popup). - Fixed InvisibleButton() not honoring negative size consistently with other widgets that do so. - Fixed OpenPopup() accessing current window, effectively opening "Debug" when called from an empty window stack. @@ -5695,7 +7386,7 @@ Other Changes: the first place so it's not really a useful default. - Begin(): Minor fixes with windows main clipping rectangle (e.g. child window with border). - Begin(): Window flags are only read on the first call of the frame. Subsequent calls ignore flags, which allows - appending to a window without worryin about flags. + appending to a window without worrying about flags. - InputText(): ignore character input when ctrl/alt are held. (Normally those text input are ignored by most wrappers.) (#279). - Demo: Fixed incorrectly formed string passed to Combo (#298). - Demo: Added simple Log demo. @@ -5729,7 +7420,7 @@ Other Changes: and more natural to extend ImGui. However please note that none of the content in imgui_internal.h is guaranteed for forward-compatibility and code using those types/functions may occasionally break. (#219) - All sample code is in imgui_demo.cpp. Please keep this file in your project and consider allowing your code to call - the ShowTestWindow() function as de-facto guide to ImGui features. It will be stripped out by the linker when unused. + the ShowTestWindow() function as de facto guide to ImGui features. It will be stripped out by the linker when unused. - Added GetContentRegionAvail() helper (basically GetContentRegionMax() - GetCursorPos()). - Added ImGuiWindowFlags_NoInputs for totally input-passthru window. - Button(): honor negative size consistently with other widgets that do so (width -100 to align the button 100 pixels @@ -6094,7 +7785,7 @@ Other Changes: - IsItemHovered() return false if another widget is active, aka we can't use what we are hovering now. - Added IsItemHoveredRect() if old behavior of IsItemHovered() is needed (e.g. for implementing the drop side of a drag'n drop operation). -- IsItemhovered() include space taken by label and behave consistently for all widgets (#145) +- IsItemHovered() include space taken by label and behave consistently for all widgets (#145) - Auto-filling child window feed their content size to parent (#170) - InputText() removed the odd ~ characters when clipping. - InputText() update its width in case of resize initiated programmatically while the widget is active. @@ -6158,7 +7849,7 @@ Other Changes: - Sliders: Fixed parsing of decimal precision back from format string when using %%. - Sliders: Fixed hovering bounding test excluding padding between outer frame and grab (there was a few pixels dead-zone). - Separator() logs itself as text when passing through text log. -- Optimisation: TreeNodeV() early out if SkipItems is set without formatting. +- Optimization: TreeNodeV() early out if SkipItems is set without formatting. - Moved various static buffers into state. Increase the formatted string buffer from 1K to 3K. - Examples: Example console keeps focus on input box at all times. - Examples: Updated to GLFW 3.1. Moved to examples/libs/ folder. @@ -6287,8 +7978,8 @@ Other Changes: Callback now passed an "EventFlag" parameter. - InputText: Added ImGuiInputTextFlags_CharsUppercase and ImGuiInputTextFlags_CharsNoBlank stock filters. - PushItemWidth() can take negative value to right-align items. -- Optimisation: Columns offsets cached to avoid unnecessary binary search. -- Optimisation: Optimized CalcTextSize() function by about 25% (they are often the bottleneck when +- Optimization: Columns offsets cached to avoid unnecessary binary search. +- Optimization: Optimized CalcTextSize() function by about 25% (they are often the bottleneck when submitting thousands of clipped items). - Added ImGuiCol_ChildWindowBg, ImGuiStyleVar_ChildWindowRounding for completeness and flexibility. - Added BeginChild() variant that takes an ImGuiID. @@ -6331,7 +8022,7 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v Breaking Changes: -- Big update! Initialisation had to be changed. You don't need to load PNG data anymore. Th +- Big update! Initialization had to be changed. You don't need to load PNG data anymore. The new system gives you uncompressed texture data. - This sequence: const void* png_data; @@ -6341,7 +8032,7 @@ Breaking Changes: - Became: unsigned char* pixels; int width, height; - // io.Fonts->AddFontFromFileTTF("myfontfile.ttf", 24.0f); // Optionally load another font + // io.Fonts->AddFontFromFileTTF("myfontfile.ttf", 24.0f); // Optionally load another font io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // io.Fonts->TexID = (your_texture_identifier); @@ -6363,7 +8054,7 @@ Other Changes: - Added IsItemActive() to tell if last widget is being held / modified (as opposed to just being hovered). Useful for custom dragging behaviors. - Style: Added FrameRounding setting for a more rounded look (default to 0 for now). -- Window: Fixed using multiple Begin/End pair on the same wnidow. +- Window: Fixed using multiple Begin/End pair on the same window. - Window: Fixed style.WindowMinSize not being honored properly. - Window: Added SetCursorScreenPos() helper (WindowPos+CursorPos = ScreenPos). - ColorEdit3: clicking on color square change the edition. The toggle button is hidden by default. @@ -6394,10 +8085,10 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v - Dragging outside area of a widget while it is active doesn't trigger hover on other widgets. - Activating widget bring parent window to front if not already. - Checkbox and Radio buttons activate on click-release to be consistent with other widgets and most UI. -- InputText() nows consume input characters immediately so they cannot be reused if +- InputText() now consumes input characters immediately so they cannot be reused if ImGui::Update is called again with a call to ImGui::Render(). (fixes #105) - Examples: Console: added support for History callbacks + some cleanup. -- Various small optimisations. +- Various small optimizations. - Cleanup and other fixes. @@ -6600,7 +8291,7 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v Breaking Changes: -- The behaviour of PixelCenterOffset changed! You may need to change your value if you had set +- The behavior of PixelCenterOffset changed! You may need to change your value if you had set it to non-default in your code and/or offset your projection matrix by 0.5 pixels. It is likely that the default PixelCenterOffset value of 0.0 is now suitable unless your rendering uses some form of multisampling. @@ -6694,7 +8385,7 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v - OpenGL example now use the fixed function-pipeline + cleanups, down by 150 lines. - Added quick & dirty Makefiles for MacOSX and Linux. -- Simplified the DrawList system, ImDrawCmd include the clipping rectangle + some optimisations. +- Simplified the DrawList system, ImDrawCmd include the clipping rectangle + some optimizations. - Fixed warnings for more stringent compilation settings. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 5cc9cdb53..fb946c4b3 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -34,7 +34,7 @@ Only if you: Then please [use the Discussions forums](https://github.com/ocornut/imgui/discussions) instead of opening an issue. -If Dear ImGui is successfully showing in your app and you have used Dear ImGui before, you can open an Issue. Any form of discussions is welcome as a new issue. +If Dear ImGui is successfully showing in your app and you have used Dear ImGui before, you can open an Issue. Any form of discussions is welcome as a new issue. ## How to open an issue @@ -52,7 +52,7 @@ Steps: - **Please INCLUDE CODE. Provide a Minimal, Complete, and Verifiable Example ([MCVE](https://stackoverflow.com/help/mcve)) to demonstrate your problem**. An ideal submission includes a small piece of code that anyone can paste into one of the examples applications (examples/../main.cpp) or demo (imgui_demo.cpp) to understand and reproduce it. **Narrowing your problem to its shortest and purest form is the easiest way to understand it, explain it and fix it**. Please test your shortened code to ensure it exhibits the problem. **Often while creating the MCVE you will solve the problem!** Many questions that are missing a standalone verifiable example are missing the actual cause of their issue in the description, which ends up wasting everyone's time. - **Attach screenshots (or GIF/video) to clarify the context**. They often convey useful information that is omitted by the description. You can drag pictures/files in the message edit box. Avoid using 3rd party image hosting services, prefer the long-term longevity of GitHub attachments (you can drag pictures into your post). On Windows, you can use [ScreenToGif](https://www.screentogif.com/) to easily capture .gif files. - **If you are discussing an assert or a crash, please provide a debugger callstack**. Never state "it crashes" without additional information. If you don't know how to use a debugger and retrieve a callstack, learning about it will be useful. -- **Please make sure that your project has asserts enabled.** Calls to IM_ASSERT() are scattered in the code to help catch common issues. When an assert is triggered read the comments around it. By default IM_ASSERT() calls the standard assert() function. To verify that your asserts are enabled, add the line `IM_ASSERT(false);` in your main() function. Your application should display an error message and abort. If your application doesn't report an error, your asserts are disabled. +- **Please make sure that your project has asserts enabled.** Calls to IM_ASSERT() are scattered in the code to help catch common issues. When an assert is triggered read the comments around it. By default IM_ASSERT() calls the standard assert() function. To verify that your asserts are enabled, add the line `IM_ASSERT(false);` in your main() function. Your application should display an error message and abort. If your application doesn't report an error, your asserts are disabled. - Please state if you have made substantial modifications to your copy of Dear ImGui or the back-end. - If you are not calling Dear ImGui directly from C++, please provide information about your Language and the wrapper/binding you are using. - Be mindful that messages are being sent to the mailbox of "Watching" users. Try to proofread your messages before sending them. Edits are not seen by those users unless they browse the site. @@ -65,17 +65,17 @@ If you have been using Dear ImGui for a while or have been using C/C++ for sever ## How to open a Pull Request -- **Please understand that by submitting a PR you are also submitting a request for the maintainer to review your code and then take over its maintenance.** PR should be crafted both in the interest of the end-users and also to ease the maintainer into understanding and accepting it. +- **Please understand that by submitting a PR you are also submitting a request for the maintainer to review your code and then take over its maintenance.** PR should be crafted both in the interest of the end-users and also to ease the maintainer into understanding and accepting it. - Many PRs are useful to demonstrate a need and a possible solution but aren't adequate for merging (causing other issues, not seeing other aspects of the big picture, etc.). In doubt, don't hesitate to push a PR because that is always the first step toward pointing toward a problem, and finding the mergeable solution! Even if a PR stays unmerged for a long time, its presence can be useful for other users and helps toward finding a general solution. -- **When adding a feature,** please describe the usage context (how you intend to use it, why you need it, etc.). Be mindful of [The XY Problem](http://xyproblem.info/). +- **When adding a feature,** please describe the usage context (how you intend to use it, why you need it, etc.). Be mindful of [The XY Problem](http://xyproblem.info/). - **When fixing a warning or compilation problem,** please post the compiler log and specify the compiler version and platform you are using. - **Attach screenshots (or GIF/video) to clarify the context and demonstrate the feature at a glance.** You can drag pictures/files in the message edit box. Prefer the long-term longevity of GitHub attachments over 3rd party hosting (you can drag pictures into your post). -- **Make sure your code follows the coding style already used in the codebase:** 4 spaces indentations (no tabs), `local_variable`, `FunctionName()`, `MemberName`, `// Text Comment`, `//CodeComment();`, C-style casts, etc.. We don't use modern C++ idioms and tend to use only a minimum of C++11 features. The applications under examples/ are generally less consistent because they sometimes try to mimic the coding style often adopted by a certain ecosystem (e.g. DirectX-related code tend to use the style of their sample). +- **Make sure your code follows the coding style already used in the codebase:** 4 spaces indentations (no tabs), `local_variable`, `FunctionName()`, `MemberName`, `// Text Comment`, `//CodeComment();`, C-style casts, etc.. We don't use modern C++ idioms and tend to use only a minimum of C++11 features. The applications under examples/ are generally less consistent because they sometimes try to mimic the coding style often adopted by a certain ecosystem (e.g. DirectX-related code tend to use the style of their sample). - **Make sure you create a branch dedicated to the pull request**. In Git, 1 PR is associated to 1 branch. If you keep pushing to the same branch after you submitted the PR, your new commits will appear in the PR (we can still cherry-pick individual commits). ## Copyright / Contributor License Agreement Any code you submit will become part of the repository and be distributed under the [Dear ImGui license](https://github.com/ocornut/imgui/blob/master/LICENSE.txt). By submitting code to the project you agree that the code is your work and that you can give it to the project. -You also agree by submitting your code that you grant all transferrable rights to the code to the project maintainer, including for example re-licensing the code, modifying the code, and distributing it in source or binary forms. Specifically, this includes a requirement that you assign copyright to the project maintainer. For this reason, do not modify any copyright statements in files in any PRs. +You also agree by submitting your code that you grant all transferable rights to the code to the project maintainer, including for example re-licensing the code, modifying the code, and distributing it in source or binary forms. Specifically, this includes a requirement that you assign copyright to the project maintainer. For this reason, do not modify any copyright statements in files in any PRs. diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 66ad24ef7..676d745bc 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -3,7 +3,7 @@ _(You may browse this at https://github.com/ocornut/imgui/blob/master/docs/EXAMP ## Dear ImGui: Examples **The [examples/](https://github.com/ocornut/imgui/blob/master/examples) folder example applications (standalone, ready-to-build) for variety of -platforms and graphics APIs.** They all use standard backends from the [backends/](https://github.com/ocornut/imgui/blob/master/backends) folder (see [BACKENDS.md](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md)). +platforms and graphics APIs.** They all use standard backends from the [backends/](https://github.com/ocornut/imgui/blob/master/backends) folder (see [docs/BACKENDS.md](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md)). The purpose of Examples is to showcase integration with backends, let you try Dear ImGui, and guide you toward integrating Dear ImGui in your own application/game/engine. @@ -58,7 +58,7 @@ Allegro 5 example.
Android + OpenGL3 (ES) example.
= main.cpp + imgui_impl_android.cpp + imgui_impl_opengl3.cpp -[example_apple_metal/](https://github.com/ocornut/imgui/blob/master/examples/example_metal/)
+[example_apple_metal/](https://github.com/ocornut/imgui/tree/master/examples/example_apple_metal/)
OSX & iOS + Metal example.
= main.m + imgui_impl_osx.mm + imgui_impl_metal.mm
It is based on the "cross-platform" game template provided with Xcode as of Xcode 9. @@ -72,7 +72,7 @@ OSX + OpenGL2 example.
You may prefer to use the GLFW Or SDL backends, which will also support Windows and Linux.) [example_glfw_wgpu/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_wgpu/)
-GLFW + WebGPU example. Supports Emscripten (web) or Dawn (desktop)
+GLFW + WebGPU example. Supports Emscripten (web), Dawn (native), WGPU (native).
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_wgpu.cpp Note that the 'example_glfw_opengl3' and 'example_sdl2_opengl3' examples also supports Emscripten! @@ -109,9 +109,11 @@ Note that GLUT/FreeGLUT is largely obsolete software, prefer using GLFW or SDL. [example_null/](https://github.com/ocornut/imgui/blob/master/examples/example_null/)
Null example, compile and link imgui, create context, run headless with no inputs and no graphics output.
-= main.cpp
+= main.cpp + imgui_impl_null.cpp
This is used to quickly test compilation of core imgui files in as many setups as possible. Because this application doesn't create a window nor a graphic context, there's no graphics output. +Please note that imgui_impl_null itself is a rather empty backend. We provide it for consistency but +it is similarly easy to create a skeleton application without the null backend. [example_sdl2_directx11/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_directx11/)
SDL2 + DirectX11 example, Windows only.
@@ -139,8 +141,8 @@ This support building with Emscripten and targeting WebGL.
Prefer using that if you are using modern GL or WebGL in your application. [example_sdl2_sdlrenderer2/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_sdlrenderer2/)
-SDL2 (Win32, Mac, Linux, etc.) + SDL_Renderer for SDL2 (most graphics backends are supported underneath)
-= main.cpp + imgui_impl_sdl2.cpp + imgui_impl_sdlrenderer.cpp
+SDL2 (Win32, Mac, Linux, etc.) + SDL_Renderer for SDL2 example.
+= main.cpp + imgui_impl_sdl2.cpp + imgui_impl_sdlrenderer2.cpp
This requires SDL 2.0.18+ (released November 2021)
[example_sdl2_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_vulkan/)
@@ -149,6 +151,44 @@ SDL2 (Win32, Mac, Linux, etc.) + Vulkan example.
This is quite long and tedious, because: Vulkan.
For this example, the main.cpp file exceptionally use helpers function from imgui_impl_vulkan.h/cpp. +[example_sdl2_wgpu/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_wgpu/)
+SDL2 + WebGPU example. Supports Emscripten (web), Dawn (native), WGPU (native).
+= main.cpp + imgui_impl_sdl2.cpp + imgui_impl_wgpu.cpp +(note that the 'example_sdl2_opengl3' example also supports Emscripten!) + +[example_sdl3_directx11/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_directx11/)
+SDL3 + DirectX11 examples, Windows only.
+= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_dx11.cpp
+ +[example_sdl3_metal/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_metal/)
+SDL3 + Metal example, Mac only.
+= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_metal.mm
+ +[example_sdl3_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_opengl3/)
+SDL3 (Win32, Mac, Linux, etc.) + OpenGL3+/ES2/ES3 example.
+= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_opengl3.cpp
+This uses more modern GL calls and custom shaders.
+This support building with Emscripten and targeting WebGL.
+ +[example_sdl3_sdlgpu3/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_sdlgpu3/)
+SDL3 (Win32, Mac, Linux, etc.) + SDL_GPU for SDL3 example.
+= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_sdlrenderer3.cpp
+ +[example_sdl3_sdlrenderer3/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_sdlrenderer3/)
+SDL3 (Win32, Mac, Linux, etc.) + SDL_Renderer for SDL3 example.
+= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_sdlrenderer3.cpp
+ +[example_sdl3_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_vulkan/)
+SDL3 (Win32, Mac, Linux, etc.) + Vulkan example.
+= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_vulkan.cpp
+This is quite long and tedious, because: Vulkan.
+For this example, the main.cpp file exceptionally use helpers function from imgui_impl_vulkan.h/cpp. + +[example_sdl3_wgpu/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_wgpu/)
+SDL3 + WebGPU example. Supports Emscripten (web), Dawn (native), WGPU (native).
+= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_wgpu.cpp +(note that the 'example_sdl3_opengl3' example also supports Emscripten!) + [example_win32_directx9/](https://github.com/ocornut/imgui/blob/master/examples/example_win32_directx9/)
DirectX9 example, Windows only.
= main.cpp + imgui_impl_win32.cpp + imgui_impl_dx9.cpp @@ -167,15 +207,19 @@ DirectX12 example, Windows only.
This is quite long and tedious, because: DirectX12. [example_win32_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_win32_opengl3/)
-Raw Windows + OpenGL3 + example (modern, programmable pipeline)
+Raw Windows + OpenGL3 example (modern, programmable pipeline)
= main.cpp + imgui_impl_win32.cpp + imgui_impl_opengl3.cpp
+[example_win32_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_win32_vulkan/)
+Raw Windows + Vulkan example
+= main.cpp + imgui_impl_win32.cpp + imgui_impl_vulkan.cpp
+ ### Miscellaneous **Building** -Unfortunately nowadays it is still tedious to create and maintain portable build files using external +Unfortunately, nowadays it is still tedious to create and maintain portable build files using external libraries (the kind we're using here to create a window and render 3D triangles) without relying on third party software and build systems. For most examples here we choose to provide: - Makefiles for Linux/OSX @@ -191,22 +235,22 @@ If you are interested in using Cmake to build and links examples, see: **About mouse cursor latency** -Dear ImGui has no particular extra lag for most behaviors, +Dear ImGui does not introduce significant extra lag for most behaviors, e.g. the last value passed to 'io.AddMousePosEvent()' before NewFrame() will result in windows being moved to the right spot at the time of EndFrame()/Render(). At 60 FPS your experience should be pleasant. -However, consider that OS mouse cursors are typically drawn through a very specific hardware accelerated -path and will feel smoother than the majority of contents rendered via regular graphics API (including, +However, consider that OS mouse cursors are typically rendered through a very specific hardware-accelerated +path, which makes them feel smoother than the majority of content rendered via regular graphics API (including, but not limited to Dear ImGui windows). Because UI rendering and interaction happens on the same plane as the mouse, that disconnect may be jarring to particularly sensitive users. You may experiment with enabling the io.MouseDrawCursor flag to request Dear ImGui to draw a mouse cursor using the regular graphics API, to help you visualize the difference between a "hardware" cursor and a regularly rendered software cursor. -However, rendering a mouse cursor at 60 FPS will feel sluggish so you likely won't want to enable that at +However, rendering a mouse cursor at 60 FPS will feel sluggish, so you likely won't want to enable that at all times. It might be beneficial for the user experience to switch to a software rendered cursor _only_ when an interactive drag is in progress. -Note that some setup or GPU drivers are likely to be causing extra display lag depending on their settings. -If you feel that dragging windows feels laggy and you are not sure what the cause is: try to build a simple -drawing a flat 2D shape directly under the mouse cursor! +Note that some setup configurations or GPU drivers may introduce additional display lag depending on their settings. +If you notice that dragging windows is laggy and you are not sure what the cause is: try drawing a simple +2D shape directly under the mouse cursor to help identify the issue! diff --git a/docs/FAQ.md b/docs/FAQ.md index 8dde1967b..f81fb8b02 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -13,18 +13,20 @@ or view this file with any Markdown viewer. :---------------------------------------------------------- | | [Where is the documentation?](#q-where-is-the-documentation) | | [What is this library called?](#q-what-is-this-library-called) | +| [What is the difference between Dear ImGui and traditional UI toolkits?](#q-what-is-the-difference-between-dear-imgui-and-traditional-ui-toolkits) | | [Which version should I get?](#q-which-version-should-i-get) | | **Q&A: Integration** | | **[How to get started?](#q-how-to-get-started)** | | **[How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?](#q-how-can-i-tell-whether-to-dispatch-mousekeyboard-to-dear-imgui-or-my-application)** | | [How can I enable keyboard or gamepad controls?](#q-how-can-i-enable-keyboard-or-gamepad-controls) | | [How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)](#q-how-can-i-use-this-on-a-machine-without-mouse-keyboard-or-screen-input-share-remote-display) | +| [How can I create my own backend?](#q-how-can-i-create-my-own-backend) | [I integrated Dear ImGui in my engine and little squares are showing instead of text...](#q-i-integrated-dear-imgui-in-my-engine-and-little-squares-are-showing-instead-of-text) | | [I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-clipping-or-disappearing-when-i-move-windows-around) | | [I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-displaying-outside-their-expected-windows-boundaries) | | **Q&A: Usage** | -| **[About the ID Stack system..
Why is my widget not reacting when I click on it?
Why is the wrong widget reacting when I click on one?
How can I have widgets with an empty label?
How can I have multiple widgets with the same label?
How can I have multiple windows with the same label?](#q-about-the-id-stack-system)** | -| [How can I display an image? What is ImTextureID, how does it work?](#q-how-can-i-display-an-image-what-is-imtextureid-how-does-it-work)| +| **[About the ID Stack system...](#q-about-the-id-stack-system)**
**[How can I have multiple widgets with the same label?](#q-how-can-i-have-multiple-widgets-with-the-same-label) (using `##` or `PushID()`)**
**[How can I have widgets with an empty label?](#q-how-can-i-have-widgets-with-an-empty-label) (using `##`)**
**[How can I animate the label of an existing widget?](#q-how-can-i-change-the-label-of-an-existing-widget) (using `###`)**
**[General description of the label and ID Stack system.](#general-description-of-the-label-and-id-stack-system)** | +| [How can I display an image?](#q-how-can-i-display-an-image)
[What are ImTextureID/ImTextureRef?](#q-what-are-imtextureidimtextureref)| | [How can I use maths operators with ImVec2?](#q-how-can-i-use-maths-operators-with-imvec2) | | [How can I use my own maths types instead of ImVec2/ImVec4?](#q-how-can-i-use-my-own-maths-types-instead-of-imvec2imvec4) | | [How can I interact with standard C++ types (such as std::string and std::vector)?](#q-how-can-i-interact-with-standard-c-types-such-as-stdstring-and-stdvector) | @@ -53,6 +55,7 @@ or view this file with any Markdown viewer. - Handy [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide to integrate Dear ImGui in an existing application. - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the [examples/](https://github.com/ocornut/imgui/blob/master/examples/) folder to explain how to integrate Dear ImGui with your own engine/application. You can run those applications and explore them. - See demo code in [imgui_demo.cpp](https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp) and particularly the `ImGui::ShowDemoWindow()` function. The demo covers most features of Dear ImGui, so you can read the code and see its output. +- See pthom's online [imgui_explorer](https://pthom.github.io/imgui_explorer) which is a web version of the demo with a source code browser. - See documentation: [Backends](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md), [Examples](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md), [Fonts](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md). - See documentation and comments at the top of [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp) + general API comments in [imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h). - The [Glossary](https://github.com/ocornut/imgui/wiki/Glossary) page may be useful. @@ -74,6 +77,60 @@ or view this file with any Markdown viewer. --- +### Q: What is the difference between Dear ImGui and traditional UI toolkits? + +Here's a very simplified comparison between the approach taken by Dear ImGui vs traditional toolkits: + +| Dear ImGui | Qt/GTK/WPF... | +|--------------------------|--------------------------| +| UI fully issued on every update. | UI issued once then later modified. | +| UI layout is fully dynamic and can change at any time.
UI is generally emitted programmatically, which empowers reflecting a dynamic set of data. | UI layout is mostly static.
UI may be emitted programmatically or from data created by offline tools. UI need extra code to evolve, which is often tedious and error-prone if it needs to be reflecting dynamic data and systems. | +| Application can submit UI based on arbitrary logic and then forget about it. | Application needs more bookkeeping of UI elements. | +| UI library stores minimal amounts of data. At one point in time, it typically doesn't know or remember which other widgets are displayed and which widgets are coming next. As a result, certain layout features (alignment, resizing) are not as easy to implement or require ad-hoc code. | UI library stores entire widgets tree and state. UI library can use this retained data to easily layout things. | +| UI code may be added anywhere.
You can even create UI to edit a local variable! | UI code needs to be added in dedicated spots. | +| UI layout/logic/action/data bindings are all nearby in the code. | UI layout/logic/action/data bindings in distinct functions, files or formats. | +| Data is naturally always synchronized. | Use callback/signal/slot for synchronizing data (error-prone). | +| API is simple and easy to learn. In particular, doing basic things is very easy. | API is more complex and specialized. | +| API is low-level (raw language types). | API are higher-level (more abstractions, advanced language features). | +| Less fancy look and feel. | Standard look and feel. | +| Compile yourself. Easy to debug, hack, modify, study. | Mostly use precompiled libraries. Compiling, modifying or studying is daunting if not impossible. | +| Run on every platform. | Run on limited desktop platforms. | + +Idiomatic Dear ImGui code: +```cpp +if (ImGui::Button("Save")) + MySaveFunction(); + +ImGui::SliderFloat("Slider", &m_MyValue, 0.0f, 1.0f); +``` +Idiomatic code with traditional toolkit: +```cpp +UiButton* button = new UiButton("Save"); +button->OnClick = &MySaveFunction; +parent->Add(button); + +UiSlider* slider = new UiSlider("Slider"); +slider->SetRange(0.0f, 1.0f); +slider->BindData(&m_MyValue); +parent->Add(slider); +``` +This is only meant to give you a intuitive feeling of the main differences, but pros & cons go deeper than that. + +Some of those properties are typically associated to the umbrella term "IMGUI", but the term has no simple and well-agreed definition. There are many erroneous statements and misunderstandings with what IMGUI means. It is partly caused by the fact that most popular IMGUI implementations (including Dear ImGui) have originated from game industry needs and have targeted specific use cases, causing people to conflate IMGUI properties with what a specific library does. However, it is perfectly possible to implement an IMGUI library that would have very different properties than e.g. Dear ImGui. My take on defining what an IMGUI is: + +**IMGUI refers to the API: literally the interface between the application and the UI system.** +- An IMGUI API favors the application code owning its data and being the single source of truth for it. +- An IMGUI API tries to minimize the application having to retain/manage data related to the UI system. +- An IMGUI API tries to minimize the UI system having to retain/manage data related to the application. +- Synchronization between application data and UI data is natural and less error-prone. + +**IMGUI does NOT refer to the implementation. Whatever happens inside the UI library code doesn't matter.** +
Also see: [Links to many articles about the IMGUI paradigm](https://github.com/ocornut/imgui/wiki/#about-the-imgui-paradigm). + +##### [Return to Index](#index) + +--- + ### Q: Which version should I get? I occasionally tag [Releases](https://github.com/ocornut/imgui/releases) but it is generally safe and recommended to sync to master/latest. The library is fairly stable and regressions tend to be fixed fast when reported. @@ -92,8 +149,8 @@ Many projects are using this branch and it is kept in sync with master regularly ### Q: How to get started? Read [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started).
-Read [EXAMPLES.md](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md).
-Read [BACKENDS.md](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md).
+Read [docs/EXAMPLES.md](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md).
+Read [docs/BACKENDS.md](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md).
Read `PROGRAMMER GUIDE` section of [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp).
The [Wiki](https://github.com/ocornut/imgui/wiki) is a hub to many resources and links. @@ -159,9 +216,18 @@ Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-lik --- +### Q: How can I create my own backend? +- See [docs/BACKENDS.md](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md). +- See Documentation at the top of imgui.cpp. + +##### [Return to Index](#index) + +--- + ### Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... Your renderer backend is not using the font texture correctly or it hasn't been uploaded to the GPU. -- If this happens using the standard backends: A) have you modified the font atlas after `ImGui_ImplXXX_NewFrame()`? B) maybe the texture failed to upload, which **can if your texture atlas is too big**. Also see [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md). +- If this happens using standard backends (before 1.92): A) have you modified the font atlas after `ImGui_ImplXXX_NewFrame()`? B) maybe the texture failed to upload, which **can if your texture atlas is too big**. Also see [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md). +- If this happens using standard backends (after 1.92): please report. - If this happens with a custom backend: make sure you have uploaded the font texture to the GPU, that all shaders are rendering states are setup properly (e.g. texture is bound). Compare your code to existing backends and use a graphics debugger such as [RenderDoc](https://renderdoc.org) to debug your rendering states. ##### [Return to Index](#index) @@ -197,15 +263,19 @@ ctx->RSSetScissorRects(1, &r); # Q&A: Usage -### Q: About the ID Stack system... -### Q: Why is my widget not reacting when I click on it? -### Q: Why is the wrong widget reacting when I click on one? -### Q: How can I have widgets with an empty label? -### Q: How can I have multiple widgets with the same label? -### Q: How can I have multiple windows with the same label? +## Q: About the ID Stack system... **USING THE SAME LABEL+ID IS THE MOST COMMON USER MISTAKE!**
**USING AN EMPTY LABEL IS THE SAME AS USING THE SAME LABEL AS YOUR PARENT WIDGET!** +
Read the questions in this section for a more general understand of how labels and ID works in Dear ImGui. + +TL;DR; +- Widgets labels are also used to compute Widgets unique identifiers. +- Unique identifiers are hashes of the label + of the parent scope (e.g. parent window or parent tree node labels). +- You can use `PushID()` to append to the identifier without making it visible. +- You can use `"##something"` in a label to append to the identifier without making it visible. +- You can use `"###something"` in a label to make the identifier ignore the visible part. + @@ -233,19 +303,102 @@ for (int n = 0; n < 3; n++) ImGui::End(); - +
-A primer on labels and the ID Stack... +### Q: How can I have multiple widgets with the same label? + +A. When widgets are in a same scope and finite, you can use a `"##something"` suffix which will be part of the identifier but not visible as a label. + +```cpp +Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play") +Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from other buttons +Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from other buttons +``` + +B. More generally, e.g. in loops, you can use `PushID()/PopID()` to push a prefix which will be part of the identifier. + +```cpp +// Using PushID() with a string +for (int i = 0; i < 100; i++) +{ + MyObject* obj = Objects[i]; + PushID(obj->Name); + Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click") + PopID(); +} +``` + +```cpp +// Using PushID() with an index +for (int i = 0; i < 100; i++) +{ + PushID(i); + Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click") + PopID(); +} +``` + +### Q: How can I have widgets with an empty label? + +If you want to completely hide the label, but still need an ID: + +```cpp +Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox! +``` +##### [Return to Index](#index) + +### Q: How can I make a label dynamic? + +Dear ImGui is very dynamic so you can submit different widgets every frame. +However, in order to preserve widget state (eg. which tree node is open; which button is focused) the library internaly refers to their unique ID. + +Occasionally you might want to change a label while preserving a constant ID. This allows you to change/animate labels while persisting associated state. +For example, you may want to include varying information in a window title bar or button label. + +Using "###" exclude the preceeding part from ID computation: +```cpp +Button("Hello###ID"); // Label = "Enable", ID = hash of (..., "ID") +Button("World###ID"); // Label = "Disable", ID = hash of (..., "ID") // Same ID, different label +``` +Using a same ID ensure that associated related e.g. weither the widget is focused, won't be lost when the label changes. + + + + + + +
+
+// Window label has animating FPS counter
+// Window ID stays the same = hash of "MyGame"
+char buf[128];
+sprintf(buf, "My game (%.1f FPS)###MyGame", io.Framerate);
+ImGui::Begin(buf);
+ 
+// Label changes between "Enable" and "Disable"
+// ID stays the same = hash of ("MyGame", "MyButton")
+if (ImGui::Button(enabled ? "Disable###MyButton" : "Enable###MyButton", { -FLT_MIN, 0.0f }))
+    enabled = !enabled;
+ 
+ImGui::End();
+
+
+ +(Hint: I'd suggest wrapping sprintf in something more compact to use, e.g. [ocornut/Str](https://github.com/ocornut/Str) for what I personally use). + +##### [Return to Index](#index) + +### General description of the label and ID Stack system Dear ImGui internally needs to uniquely identify UI elements. -Elements that are typically not clickable (such as calls to the Text functions) don't need an ID. -Interactive widgets (such as calls to Button buttons) need a unique ID. +Elements that are typically not clickable (such as calls to Text() functions) don't need an ID. +Interactive widgets (such as calls to Button() functions) need a unique ID. **Unique IDs are used internally to track active widgets and occasionally associate state to widgets.
Unique IDs are implicitly built from the hash of multiple elements that identify the "path" to the UI element.** -Since Dear ImGui 1.85, you can use `Demo>Tools>ID Stack Tool` or call `ImGui::ShowIDStackToolWindow()`. The tool display intermediate values leading to the creation of a unique ID, making things easier to debug and understand. +You can use `Demo>Tools>ID Stack Tool` or call `ImGui::ShowIDStackToolWindow()`. The tool display intermediate values leading to the creation of a unique ID, making things easier to debug and understand. ![Stack tool](https://user-images.githubusercontent.com/8225057/136235657-a0ea5665-dcd1-423f-9be6-dc3f8ced8f12.png) @@ -299,20 +452,7 @@ Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo Button("##foo"); // Label = "", ID = hash of ("MyWindow", "##foo") // Different from window End(); ``` -- If you want to completely hide the label, but still need an ID: -```cpp -Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox! -``` -- Occasionally/rarely you might want to change a label while preserving a constant ID. This allows -you to animate labels. For example, you may want to include varying information in a window title bar, -but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: -```cpp -Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID") -Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same ID, different label -sprintf(buf, "My game (%f FPS)###MyGame", fps); -Begin(buf); // Variable title, ID = hash of "MyGame" -``` - Solving ID conflict in a more general manner: Use `PushID()` / `PopID()` to create scopes and manipulate the ID stack, as to avoid ID conflicts within the same window. This is the most convenient way of distinguishing ID when iterating and @@ -375,23 +515,73 @@ node open/closed state differently. See what makes more sense in your situation! --- -### Q: How can I display an image? What is ImTextureID, how does it work? +### Q: How can I display an image? +### Q: What are ImTextureID/ImTextureRef? -Short explanation: -- Refer to [Image Loading and Displaying Examples](https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples) on the [Wiki](https://github.com/ocornut/imgui/wiki). +**Short explanation:** - You may use functions such as `ImGui::Image()`, `ImGui::ImageButton()` or lower-level `ImDrawList::AddImage()` to emit draw calls that will use your own textures. +- To load and display your own textures, refer to [Image Loading and Displaying Examples](https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples) on the [Wiki](https://github.com/ocornut/imgui/wiki). - Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as an opaque ImTextureID value. - By default ImTextureID can store up to 64-bits. You may `#define` it to a custom type/structure if you need. - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason), but the examples linked above may be useful references. +**Details:** + +1.92 introduced `ImTextureRef` in June 2025. +- All drawing functions using `ImTextureID` were changed to use `ImTextureRef`. +- You can trivially create a `ImTextureRef` from a `ImTextureID`. +- **If you use Image functions with textures that you have loaded/created yourself, you will mostly likely only ever store/manipulate `ImTextureID` and then pass them as `ImTextureRef`.** +- You only NEED to manipulate `ImTextureRef` when dealing with textures managed by the backend itself, aka mainly the atlas texture for now. +- We intentionally do not provide an implicit `ImTextureRef` -> `ImTextureID` cast operator because it is technically lossy to convert ImTextureRef to ImTextureID before rendering. + +**ImTextureID = backend specific, low-level identifier for a texture uploaded in GPU/graphics system.** +```cpp +#ifndef ImTextureID +typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or integer). A majority of backends are ok with that. +#endif +``` +- When a Rendered Backend creates a texture, it store its native identifier into a ImTextureID value (e.g. Used by DX11 backend to a `ID3D11ShaderResourceView*`; Used by OpenGL backends to store `GLuint`; Used by SDLGPU backend to store a `SDL_GPUTextureSamplerBinding*`, etc.). +- User may submit their own textures to e.g. `ImGui::Image()` function by passing this value. +- During the rendering loop, the Renderer Backend retrieve the ImTextureID, which stored inside a ImTextureRef, which is stored inside ImDrawCmd. +- Compile-time type configuration: + - To use something other than a 64-bit value: add '#define ImTextureID MyTextureType*' in your imconfig.h file. + - This can be whatever to you want it to be! read the FAQ entry about textures for details. + - You may decide to store a higher-level structure containing texture, sampler, shader etc. with various constructors if you like. You will need to implement ==/!= operators. + +**ImTextureRef = higher-level identifier for a texture.** +```cpp +// Store a ImTextureID _or_ a ImTextureData*. +struct ImTextureRef +{ + ImTextureRef() { _TexData = NULL; _TexID = ImTextureID_Invalid; } + ImTextureRef(ImTextureID tex_id) { _TexData = NULL; _TexID = tex_id; } + inline ImTextureID GetTexID() const { return _TexData ? _TexData->TexID : _TexID; } + + // Members (either are set, never both!) + ImTextureData* _TexData; // A texture, generally owned by a ImFontAtlas. Will convert to ImTextureID during render loop, after texture has been uploaded. + ImTextureID _TexID; // _OR_ Low-level backend texture identifier, if already uploaded or created by user/app. Generally provided to e.g. ImGui::Image() calls. +}; +``` +- The identifier is valid even before the texture has been uploaded to the GPU/graphics system. +- This is what gets passed to functions such as `ImGui::Image()`, `ImDrawList::AddImage()`. +- This is what gets stored in draw commands (`ImDrawCmd`) to identify a texture during rendering. + - When a texture is created by user code (e.g. custom images), we directly store the low-level `ImTextureID`. + - Because of this, when displaying your own texture you are likely to ever only manage ImTextureID values on your side. + - When a texture is created by the backend, we store a `ImTextureData*` which becomes an indirection to extract the `ImTextureID` value during rendering, after texture upload has happened. + - To create a `ImTextureRef` from a `ImTextureData*` you can use `ImTextureData::GetTexRef()`. + We intentionally do not provide an `ImTextureRef` constructor for this: we don't expect this to be frequently useful to the end-user, and it would be erroneously called by many legacy code. + - There is no constructor to create a `ImTextureRef` from a `ImTextureData*` as we don't expect this to be useful to the end-user, and it would be erroneously called by many legacy code. + - If you want to bind the current atlas when using custom rectangles, you can use `io.Fonts->TexRef`. + - Binding generators for languages such as C (which don't have constructors), should provide a helper, e.g. `inline ImTextureRef ImTextureRefFromID(ImTextureID tex_id) { ImTextureRef tex_ref = { ._TexData = NULL, .TexID = tex_id }; return tex_ref; }` + **Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.** Long explanation: - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices. At the end of the frame, those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code to render them is generally fairly short (a few dozen lines). In the examples/ folder, we provide functions for popular graphics APIs (OpenGL, DirectX, etc.). - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API. - We carry the information to identify a "texture" in the ImTextureID type. +We carry the information to identify a "texture" in the ImTextureID type, which itself tends to be stored inside a ImTextureRef. ImTextureID default to ImU64 aka 8 bytes worth of data: just enough to store one pointer or integer of your choice. -Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely passes ImTextureID values until they reach your rendering function. +Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely passes values until they reach your rendering function. - In the [examples/](https://github.com/ocornut/imgui/tree/master/examples) backends, for each graphics API we decided on a type that is likely to be a good representation for specifying an image from the end-user perspective. This is what the _examples_ rendering functions are using: ```cpp OpenGL: @@ -539,30 +729,56 @@ ImGui::End(); ### Q: How should I handle DPI in my application? -The short answer is: obtain the desired DPI scale, load your fonts resized with that scale (always round down fonts size to the nearest integer), and scale your Style structure accordingly using `style.ScaleAllSizes()`. +Since 1.92 (June 2025) fonts may be dynamically used at any size. -Your application may want to detect DPI change and reload the fonts and reset style between frames. +**Scaling fonts** -Your ui code should avoid using hardcoded constants for size and positioning. Prefer to express values as multiple of reference values such as `ImGui::GetFontSize()` or `ImGui::GetFrameHeight()`. So e.g. instead of seeing a hardcoded height of 500 for a given item/window, you may want to use `30*ImGui::GetFontSize()` instead. +Select default size: +```cpp +style.FontSizeBase = 20.0f; +``` +Scale all fonts: +```cpp +style.FontScaleDpi = 2.0f; +``` -Down the line Dear ImGui will provide a variety of standardized reference values to facilitate using this. +To change font size: +```cpp +ImGui::PushFont(NULL, 42.0f); // This will be multiplied by style.FontScaleDpi +``` +To change font and font size: +```cpp +ImGui::PushFont(new_font, 42.0f); +``` -Applications in the `examples/` folder are not DPI aware partly because they are unable to load a custom font from the file-system (may change that in the future). +In `docking` branch or with multi-viewports: +```cpp +io.ConfigDpiScaleFonts = true; // (Docking branch only) Automatically overwrite style.FontScaleDpi in Begin() when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now. +io.ConfigDpiScaleViewports = true; // (Docking branch only) Scale Dear ImGui and Platform Windows when Monitor DPI changes. +``` -The reason DPI is not auto-magically solved in stock examples is that we don't yet have a satisfying solution for the "multi-dpi" problem (using the `docking` branch: when multiple viewport windows are over multiple monitors using different DPI scales). The current way to handle this on the application side is: -- Create and maintain one font atlas per active DPI scale (e.g. by iterating `platform_io.Monitors[]` before `NewFrame()`). -- Hook `platform_io.OnChangedViewport()` to detect when a `Begin()` call makes a Dear ImGui window change monitor (and therefore DPI). -- In the hook: swap atlas, swap style with correctly sized one, and remap the current font from one atlas to the other (you may need to maintain a remapping table of your fonts at varying DPI scales). +**Scaling style** (paddings, spacings, thicknesses) -This approach is relatively easy and functional but comes with two issues: -- It's not possibly to reliably size or position a window ahead of `Begin()` without knowing on which monitor it'll land. -- Style override may be lost during the `Begin()` call crossing monitor boundaries. You may need to do some custom scaling mumbo-jumbo if you want your `OnChangedViewport()` handler to preserve style overrides. +This is still massively work in progress, expect turbulence. +Style values are currently not easily scalable dynamically. +For single viewport application you can call once: +```cpp +style.ScaleAllSizes(factor); // call once! +``` +If you need to change the scaling factor, it is currently most practical to reset the style and call this again with a new value. -Please note that if you are not using multi-viewports with multi-monitors using different DPI scales, you can ignore that and use the simpler technique recommended at the top. +Your UI code should avoid using hardcoded constants for size and positioning. Prefer to express values as multiple of reference values such as `ImGui::GetFontSize()` or `ImGui::GetFrameHeight()`. So e.g. instead of seeing a hardcoded height of 500 for a given item/window, you may want to use `30*ImGui::GetFontSize()` instead. -On Windows, in addition to scaling the font size (make sure to round to an integer) and using `style.ScaleAllSizes()`, you will need to inform Windows that your application is DPI aware. If this is not done, Windows will scale the application window and the UI text will be blurry. Potential solutions to indicate DPI awareness on Windows are: +Down the line Dear ImGui will provide a variety of standardized reference values to facilitate using this. This is expected to happen during subsequent 1.92.x releases. -- For SDL: the flag `SDL_WINDOW_ALLOW_HIGHDPI` needs to be passed to `SDL_CreateWindow()``. +Applications in the `examples/` folder are partly DPI aware but they are unable to load a custom font from the file-system, so they look ugly (may change that in the future). + +The reason DPI is not auto-magically solved in stock examples is that we don't yet have a satisfying solution for the "multi-dpi" problem (using the `docking` branch: when multiple viewport windows are over multiple monitors using different DPI scales) specifically for the `ImGuiStyle` structure. Fonts are however now perfectly scalable. + +**On Windows, you need to inform Windows that your application is DPI aware!** +If this is not done, Windows will scale the application window and the UI text will be blurry. Potential solutions to indicate DPI awareness on Windows are: +- For SDL2: the flag `SDL_WINDOW_ALLOW_HIGHDPI` needs to be passed to `SDL_CreateWindow()` + call `::SetProcessDPIAware()`. +- For SDL3: the flag `SDL_WINDOW_HIGH_PIXEL_DENSITY` needs to be passed to `SDL_CreateWindow()`. - For GLFW: this is done automatically. - For other Windows projects with other backends, or wrapper projects: - We provide a `ImGui_ImplWin32_EnableDpiAwareness()` helper method in the Win32 backend. @@ -614,9 +830,12 @@ Use the font atlas to pack them into a single texture. Read [docs/FONTS.md](http --- ### Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? -When loading a font, pass custom Unicode ranges to specify the glyphs to load. +Since 1.92 (June 2025) and with an updated backend, it is not necessary to specify glyph ranges at all. + +Before 1.92, when loading a font, pass custom Unicode ranges to specify the glyphs to load. ```cpp +// [BEFORE 1.92] // Add default Japanese ranges io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, nullptr, io.Fonts->GetGlyphRangesJapanese()); @@ -640,8 +859,8 @@ Text input: it is up to your application to pass the right character code by cal The applications in examples/ are doing that. Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode). You may also use `MultiByteToWideChar()` or `ToUnicode()` to retrieve Unicode codepoints from MultiByte characters or keyboard state. -Windows: if your language is relying on an Input Method Editor (IME), you can write your HWND to ImGui::GetMainViewport()->PlatformHandleRaw -for the default implementation of GetPlatformIO().Platform_SetImeDataFn() to set your Microsoft IME position correctly. +Windows: if your language is relying on an Input Method Editor (IME), you can write your HWND to `ImGui::GetMainViewport()->PlatformHandleRaw` +for the default implementation of `GetPlatformIO().Platform_SetImeDataFn()` to set your Microsoft IME position correctly. ##### [Return to Index](#index) @@ -666,7 +885,7 @@ You may take a look at: Yes. People have written game editors, data browsers, debuggers, profilers, and all sorts of non-trivial tools with the library. In my experience, the simplicity of the API is very empowering. Your UI runs close to your live data. Make the tools always-on and everybody in the team will be inclined to create new tools (as opposed to more "offline" UI toolkits where only a fraction of your team effectively creates tools). The list of sponsors below is also an indicator that serious game teams have been using the library. -Dear ImGui is very programmer centric and the immediate-mode GUI paradigm might require you to readjust some habits before you can realize its full potential. Dear ImGui is about making things that are simple, efficient, and powerful. +Dear ImGui is very programmer-centric and the immediate-mode GUI paradigm might require you to readjust some habits before you can realize its full potential. Dear ImGui is about making things that are simple, efficient, and powerful. Dear ImGui is built to be efficient and scalable toward the needs for AAA-quality applications running all day. The IMGUI paradigm offers different opportunities for optimization than the more typical RMGUI paradigm. diff --git a/docs/FONTS.md b/docs/FONTS.md index c451af61c..1e51cde48 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -2,21 +2,33 @@ _(You may browse this at https://github.com/ocornut/imgui/blob/master/docs/FONTS ## Dear ImGui: Using Fonts -The code in imgui.cpp embeds a copy of 'ProggyClean.ttf' (by Tristan Grimmer), -a 13 pixels high, pixel-perfect font used by default. We embed it in the source code so you can use Dear ImGui without any file system access. ProggyClean does not scale smoothly, therefore it is recommended that you load your own file when using Dear ImGui in an application aiming to look nice and wanting to support multiple resolutions. +The code in Dear ImGui embeds a copy of [ProggyClean.ttf](https://github.com/bluescan/proggyfonts) by Tristan Grimmer, +a 13 pixels high, pixel-perfect font used by default. ProggyClean does not scale very nicely. -You may also load external .TTF/.OTF files. +The code in Dear ImGui embeds a partial copy of [ProggyForever.ttf](https://github.com/ocornut/proggyforever) by Disco Hello & Tristan Grimmer, +a new font mimicking ProggyClean which does scale nicely. + +We embed fonts in the code so you can use Dear ImGui without any file system access. +If you don't use them you can set `IMGUI_DISABLE_DEFAULT_FONT` in your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file to ship binaries without the fonts and save about ~26 KB. + +Calling io.Fonts->AddFontDefaultVector() loads ProggyForever. +Calling io.Fonts->AddFontDefaultBitmap() loads ProggyClean. +Calling io.Fonts->AddFontDefault() selects one based on the expected default font size (when `style.FontSizeBase * style.FontScaleMain * style.FontSizeDpi >= 15` we use ProggyForever). + +You may also load external .TTF/.OTF files, see instructions on this page. In the [misc/fonts/](https://github.com/ocornut/imgui/tree/master/misc/fonts) folder you can find a few suggested fonts, provided as a convenience. **Also read the FAQ:** https://www.dearimgui.com/faq (there is a Fonts section!) ## Index - [Troubleshooting](#troubleshooting) +- [New! Dynamic Fonts system in 1.92 (June 2025)](#new-dynamic-fonts-system-in-192-june-2025) - [How should I handle DPI in my application?](#how-should-i-handle-dpi-in-my-application) - [Fonts Loading Instructions](#fonts-loading-instructions) - [Loading Font Data from Memory](#loading-font-data-from-memory) - [Loading Font Data Embedded In Source Code](#loading-font-data-embedded-in-source-code) - [Using Icon Fonts](#using-icon-fonts) + - [Excluding Overlapping Ranges](#excluding-overlapping-ranges) - [Using FreeType Rasterizer (imgui_freetype)](#using-freetype-rasterizer-imgui_freetype) - [Using Colorful Glyphs/Emojis](#using-colorful-glyphsemojis) - [Using Custom Glyph Ranges](#using-custom-glyph-ranges) @@ -35,7 +47,7 @@ In the [misc/fonts/](https://github.com/ocornut/imgui/tree/master/misc/fonts) fo ### (1) Invalid filename due to use of `\` or unexpected working directory. -See [About Filenames](#about-filenames). AddFontXXX functions should assert if the filename is incorrect. +See [About Filenames](#about-filenames). AddFontXXX() functions should assert if the filename is incorrect. ### (2) Invalid UTF-8 encoding of your non-ASCII strings. @@ -43,14 +55,18 @@ See [About UTF-8 Encoding](#about-utf-8-encoding). Use the encoding viewer to co ### (3) Missing glyph ranges. -You need to load a font with explicit glyph ranges if you want to use non-ASCII characters. See [Fonts Loading Instructions](#fonts-loading-instructions). Use [Debug Tools](#debug-tools) confirm loaded fonts and loaded glyph ranges. +🆕 **Since 1.92, with an up to date backend: specifying glyph ranges is unnecessary.** -This is a current constraint of Dear ImGui (which we will lift in the future): when loading a font you need to specify which characters glyphs to load. +⏪ Before 1.92: you need to load a font with explicit glyph ranges if you want to use non-ASCII characters. See [Fonts Loading Instructions](#fonts-loading-instructions). Use [Debug Tools](#debug-tools) confirm loaded fonts and loaded glyph ranges. + +This was a previous constraint of Dear ImGui (lifted in 1.92): when loading a font you need to specify which characters glyphs to load. All loaded fonts glyphs are rendered into a single texture atlas ahead of time. Calling either of `io.Fonts->GetTexDataAsAlpha8()`, `io.Fonts->GetTexDataAsRGBA32()` or `io.Fonts->Build()` will build the atlas. This is generally called by the Renderer backend, e.g. `ImGui_ImplDX11_NewFrame()` calls it. **If you use custom glyphs ranges, make sure the array is persistent** and available during the calls to `GetTexDataAsAlpha8()/GetTexDataAsRGBA32()/Build()`. ### (4) Font atlas texture fails to upload to GPU. -This is often of byproduct of point 3. If you have large number of glyphs or multiple fonts, the texture may become too big for your graphics API. **The typical result of failing to upload a texture is if every glyph or everything appears as empty white rectangles.** Mind the fact that some graphics drivers have texture size limitation. If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours. +🆕 **Since 1.92, with an up to date backend: atlas is built incrementally and dynamically resized, this is less likely to happen** + +:rewind: This is often of byproduct of point 3. If you have large number of glyphs or multiple fonts, the texture may become too big for your graphics API. **The typical result of failing to upload a texture is if every glyph or everything appears as empty white rectangles.** Mind the fact that some graphics drivers have texture size limitation. If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours. ![empty squares](https://github.com/user-attachments/assets/68b50fb5-8b9d-4c38-baec-6ac384f06d26) @@ -60,9 +76,22 @@ Some solutions: - Reduce glyphs ranges by calculating them from source localization data. You can use the `ImFontGlyphRangesBuilder` for this purpose and rebuilding your atlas between frames when new characters are needed. This will be the biggest win! - Set `io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;` to disable rounding the texture height to the next power of two. -- Set `io.Fonts.TexDesiredWidth` to specify a texture width to reduce maximum texture height (see comment in `ImFontAtlas::Build()` function). -Future versions of Dear ImGui should solve this problem. +##### [Return to Index](#index) + +--------------------------------------- + +## New! Dynamic Fonts system in 1.92 (June 2025) + +v1.92 introduces a newer, dynamic font system. It requires backend to support the `ImGuiBackendFlags_HasTextures` feature: +- Users of icons, Asian and non-English languages do not need to pre-build all glyphs ahead of time. Saving on loading time, memory, and also reducing issues with missing glyphs. Specifying glyph ranges is not needed anymore. +- `PushFont(NULL, new_size)` may be used anytime to change font size. +- Packing custom rectangles is more convenient as pixels may be written to immediately. +- Any update to fonts previously required backend specific calls to re-upload the texture, and said calls were not portable across backends. It is now possible to scale fonts etc. in a way that doesn't require you to make backend-specific calls. +- It is possible to plug a custom loader/backend to any font source. + +See [#8465](https://github.com/ocornut/imgui/issues/8465) for more details. + ##### [Return to Index](#index) @@ -70,7 +99,10 @@ Future versions of Dear ImGui should solve this problem. ## How should I handle DPI in my application? -See [FAQ entry](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-how-should-i-handle-dpi-in-my-application). +Since 1.92, with an updated backend, you can set `style.FontScaleDpi = your_content_scale;` to scale all fonts. +
You can call `style.ScaleAllSizes(xxx)` at init time or every frame at the beginning of your main loop to scale sizes/paddings. +
Since 1.92, with an updated backend, macOS style pixel/backing style scale is automatically handled. +
See [FAQ entry](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-how-should-i-handle-dpi-in-my-application) for more details. ##### [Return to Index](#index) @@ -78,13 +110,32 @@ See [FAQ entry](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-how-s ## Fonts Loading Instructions +**Select base size** +```cpp +ImGuiStyle& style = ImGui::GetStyle(); +style.FontSizeBase = 20.0f; +``` + **Load default font:** ```cpp ImGuiIO& io = ImGui::GetIO(); -io.Fonts->AddFontDefault(); +io.Fonts->AddFontDefaultVector(); // Load embedded scalable font. +``` +```cpp +io.Fonts->AddFontDefaultBitmap(); // Load embedded bitmap font (legacy). +``` +```cpp +io.Fonts->AddFontDefault(); // Load embedded font (legacy: auto-selected between the two above). ``` **Load .TTF/.OTF file with:** + +🆕 **Since 1.92, with an up to date backend: passing a size is not necessary** +```cpp +ImGuiIO& io = ImGui::GetIO(); +io.Fonts->AddFontFromFileTTF("font.ttf"); +``` +:rewind: **Before 1.92, or without an up to date backend:** ```cpp ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels); @@ -95,14 +146,14 @@ If you get an assert stating "Could not load font file!", your font filename is ```cpp // Init ImGuiIO& io = ImGui::GetIO(); -ImFont* font1 = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels); -ImFont* font2 = io.Fonts->AddFontFromFileTTF("anotherfont.otf", size_pixels); +ImFont* font1 = io.Fonts->AddFontFromFileTTF("font.ttf",); +ImFont* font2 = io.Fonts->AddFontFromFileTTF("anotherfont.otf"); ``` In your application loop, select which font to use: ```cpp ImGui::Text("Hello"); // use the default font (which is the first loaded font) -ImGui::PushFont(font2); +ImGui::PushFont(font2, 0.0f); // change font, keep current size ImGui::Text("Hello with another font"); ImGui::PopFont(); ``` @@ -110,13 +161,22 @@ ImGui::PopFont(); **For advanced options create a ImFontConfig structure and pass it to the AddFont() function (it will be copied internally):** ```cpp ImFontConfig config; -config.OversampleH = 2; -config.OversampleV = 1; -config.GlyphExtraSpacing.x = 1.0f; +config.OversampleH = 1.0f; ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config); ``` **Combine multiple fonts into one:** + +🆕 **Since 1.92, with an up to date backend: specifying glyph ranges is unnecessary.** +```cpp +// Load a first font +ImFont* font = io.Fonts->AddFontDefaultVector(); +ImFontConfig config; +config.MergeMode = true; +io.Fonts->AddFontFromFileTTF("DroidSans.ttf", 0.0f, &config); // Merge into first font to add e.g. Asian characters +io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 0.0f, &config); // Merge into first font to add Icons +``` +:rewind: **Before 1.92, or without an up to date backend:** ```cpp // Load a first font ImFont* font = io.Fonts->AddFontDefault(); @@ -134,6 +194,7 @@ io.Fonts->Build(); **Add a fourth parameter to bake specific font ranges only:** +🆕 **Since 1.92, with an up to date backend: specifying glyph ranges is unnecessary. All the GetGlyphRangesXXX() functions are marked obsolete.** ```cpp // Basic Latin, Extended Latin io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, nullptr, io.Fonts->GetGlyphRangesDefault()); @@ -148,17 +209,25 @@ See [Using Custom Glyph Ranges](#using-custom-glyph-ranges) section to create yo **Example loading and using a Japanese font:** +🆕 **Since 1.92, with an up to date backend:** +```cpp +ImGuiIO& io = ImGui::GetIO(); +io.Fonts->AddFontFromFileTTF("NotoSansCJKjp-Medium.otf"); +``` + +:rewind: **Before 1.92, or without an up to date backend:** ```cpp ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("NotoSansCJKjp-Medium.otf", 20.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); ``` + ```cpp ImGui::Text(u8"こんにちは!テスト %d", 123); if (ImGui::Button(u8"ロード")) { // do stuff } -ImGui::InputText("string", buf, IM_ARRAYSIZE(buf)); +ImGui::InputText("string", buf, IM_COUNTOF(buf)); ImGui::SliderFloat("float", &f, 0.0f, 1.0f); ``` @@ -184,6 +253,7 @@ ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; ImFont* font = io.Fonts->AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg); ``` +IMPORTANT: Since 1.92, when using `FontDataOwnedByAtlas = false`, font data needs to available until `atlas->RemoveFont()`, or more typically until a shutdown of the owning context or font atlas. It was not immediately noticeable in 1.92.0 due to a bug in handling `FontDataOwnedByAtlas = false`, which was fixed in 1.92.6. ##### [Return to Index](#index) @@ -218,11 +288,23 @@ To refer to the icon UTF-8 codepoints from your C++ code, you may use those head So you can use `ICON_FA_SEARCH` as a string that will render as a "Search" icon. +🆕 **Since 1.92, with an up to date backend: specifying glyph ranges is unnecessary. You can omit this parameter.** Example Setup: ```cpp // Merge icons into default tool font #include "IconsFontAwesome.h" ImGuiIO& io = ImGui::GetIO(); +io.Fonts->AddFontDefaultVector(); +ImFontConfig config; +config.MergeMode = true; +config.GlyphMinAdvanceX = 13.0f; // Use if you want to make the icon monospaced +io.Fonts->AddFontFromFileTTF("fonts/fontawesome-webfont.ttf", 13.0f, &config); +``` +:rewind: **Before 1.92:** +```cpp +// Merge icons into default tool font +#include "IconsFontAwesome.h" +ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontDefault(); ImFontConfig config; @@ -243,7 +325,8 @@ See Links below for other icons fonts and related tools. **Monospace Icons?** -To make your icon look more monospace and facilitate alignment, you may want to set the ImFontConfig::GlyphMinAdvanceX value when loading an icon font. +To make your icon look more monospace and facilitate alignment, you may want to set the `ImFontConfig::GlyphMinAdvanceX` value when loading an icon font. +If you `GlyphMinAdvanceX` you need to pass a `font_size` to `AddFontXXX()` calls, as the MinAdvanceX value will be specified for the given size and scaled otherwise. **Screenshot** @@ -254,11 +337,49 @@ Here's an application using icons ("Avoyd", https://www.avoyd.com): --------------------------------------- +### Excluding Overlapping Ranges + +🆕 **Since 1.92, with an up to date backend: glyphs ranges are ignored**: when loading a glyph, input fonts in the merge list are queried in order. The first font which has the glyph loads it. +
‼️ **If you are merging several fonts, you may have undesirable overlapping ranges.** You can use `ImFontConfig::GlyphExcludeRanges[] `to specify ranges to ignore in a given Input. + +```cpp +// Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range +static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; +ImFontConfig cfg1; +cfg1.GlyphExcludeRanges = exclude_ranges; +io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); + +// Add Font Source 2, which expects to use the range above +ImFontConfig cfg2; +cfg2.MergeMode = true; +io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2); +``` +Another (silly) example: +```cpp +// Remove 'A'-'Z' from first font +static ImWchar exclude_ranges[] = { 'A', 'Z', 0 }; +ImFontConfig cfg1; +cfg1.GlyphExcludeRanges = exclude_ranges; +io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); + +// Load another font to fill the gaps +ImFontConfig cfg2; +cfg2.MergeMode = true; +io.Fonts->AddFontFromFileTTF("Roboto-Medium.ttf", 0.0f, &cfg2); +``` +![image](https://github.com/user-attachments/assets/f3d751d3-1aee-4698-bd9b-f511902f56bb) + +You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` to see list of glyphs available in multiple font sources. This can facilitate understanding which font input is providing which glyph. + +##### [Return to Index](#index) + +--------------------------------------- + ## Using FreeType Rasterizer (imgui_freetype) -- Dear ImGui uses imstb\_truetype.h to rasterize fonts (with optional oversampling). This technique and its implementation are not ideal for fonts rendered at small sizes, which may appear a little blurry or hard to read. -- There is an implementation of the ImFontAtlas builder using FreeType that you can use in the [misc/freetype/](https://github.com/ocornut/imgui/tree/master/misc/freetype) folder. -- FreeType supports auto-hinting which tends to improve the readability of small fonts. +- Dear ImGui uses [stb_truetype.h](https://github.com/nothings/stb/) to rasterize fonts (with optional oversampling). This technique and its implementation are not ideal for fonts rendered at small sizes, which may appear a little blurry or hard to read. +- You can however use `imgui_freetype.cpp` from the [misc/freetype/](https://github.com/ocornut/imgui/tree/master/misc/freetype) folder. Compile with this file and add `#define IMGUI_ENABLE_FREETYPE` to your imconfig.h file or build system to automatically activate it. +- FreeType supports auto-hinting which tends to improve the readability of small fonts. It makes a big difference especially at smaller resolutions. - Read documentation in the [misc/freetype/](https://github.com/ocornut/imgui/tree/master/misc/freetype) folder. - Correct sRGB space blending will have an important effect on your font rendering quality. @@ -280,10 +401,9 @@ Here's an application using icons ("Avoyd", https://www.avoyd.com): io.Fonts->AddFontFromFileTTF("../../../imgui_dev/data/fonts/NotoSans-Regular.ttf", 16.0f); static ImWchar ranges[] = { 0x1, 0x1FFFF, 0 }; static ImFontConfig cfg; -cfg.OversampleH = cfg.OversampleV = 1; cfg.MergeMode = true; -cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_LoadColor; -io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\seguiemj.ttf", 16.0f, &cfg, ranges); +cfg.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_LoadColor; +io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\seguiemj.ttf", 16.0f, &cfg); ``` ##### [Return to Index](#index) @@ -292,7 +412,9 @@ io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\seguiemj.ttf", 16.0f, &cfg, ra ## Using Custom Glyph Ranges -You can use the `ImFontGlyphRangesBuilder` helper to create glyph ranges based on text input. For example: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs. +🆕 **Since 1.92, with an up to date backend: specifying glyph ranges is unnecessary. Therefore this is not really useful any more.** + +:rewind: You can use the `ImFontGlyphRangesBuilder` helper to create glyph ranges based on text input. For example: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs. ```cpp ImVector ranges; ImFontGlyphRangesBuilder builder; @@ -311,16 +433,25 @@ io.Fonts->Build(); // Build the atlas while ## Using Custom Colorful Icons +🆕 **Since 1.92, with an up to date backend: this system has been revamped.** + +TL;DR; With the new system, it is recommended that you create a custom `ImFontLoader` and register your fonts with it. +`AddCustomRectFontGlyph()` has been obsoleted because its API does not make much sense with resizable fonts. + +You can ask questions in [#8466](https://github.com/ocornut/imgui/issues/8466). + +:rewind: **Before 1.92:** + As an alternative to rendering colorful glyphs using imgui_freetype with `ImGuiFreeTypeBuilderFlags_LoadColor`, you may allocate your own space in the texture atlas and write yourself into it. **(This is a BETA api, use if you are familiar with dear imgui and with your rendering backend)** - You can use the `ImFontAtlas::AddCustomRect()` and `ImFontAtlas::AddCustomRectFontGlyph()` api to register rectangles that will be packed into the font atlas texture. Register them before building the atlas, then call Build()`. -- You can then use `ImFontAtlas::GetCustomRectByIndex(int)` to query the position/size of your rectangle within the texture, and blit/copy any graphics data of your choice into those rectangles. +- You can then use `ImFontAtlas::GetCustomRect(int)` to query the position/size of your rectangle within the texture, and blit/copy any graphics data of your choice into those rectangles. - This API is beta because it is likely to change in order to support multi-dpi (multiple viewports on multiple monitors with varying DPI scale). #### Pseudo-code: ```cpp // Add font, then register two custom 13x13 rectangles mapped to glyph 'a' and 'b' of this font -ImFont* font = io.Fonts->AddFontDefault(); +ImFont* font = io.Fonts->AddFontDefaultVector(); int rect_ids[2]; rect_ids[0] = io.Fonts->AddCustomRectFontGlyph(font, 'a', 13, 13, 13+1); rect_ids[1] = io.Fonts->AddCustomRectFontGlyph(font, 'b', 13, 13, 13+1); @@ -333,10 +464,8 @@ unsigned char* tex_pixels = nullptr; int tex_width, tex_height; io.Fonts->GetTexDataAsRGBA32(&tex_pixels, &tex_width, &tex_height); -for (int rect_n = 0; rect_n < IM_ARRAYSIZE(rect_ids); rect_n++) -{ - int rect_id = rect_ids[rect_n]; - if (const ImFontAtlasCustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) +for (int rect_n = 0; rect_n < IM_COUNTOF(rect_ids); rect_n++) + if (const ImTextureRect* rect = io.Fonts->GetCustomRect(rect_ids[rect_n])) { // Fill the custom rectangle with red pixels (in reality you would draw/copy your bitmap data here!) for (int y = 0; y < rect->Height; y++) @@ -346,7 +475,6 @@ for (int rect_n = 0; rect_n < IM_ARRAYSIZE(rect_ids); rect_n++) *p++ = IM_COL32(255, 0, 0, 255); } } -} ``` ##### [Return to Index](#index) @@ -436,7 +564,20 @@ You can use the `UTF-8 Encoding viewer` in `Metrics/Debugger` to verify the cont ## Credits/Licenses For Fonts Included In Repository -Some fonts files are available in the `misc/fonts/` folder: +Embedded in source code: + +**ProggyClean.ttf**, by Tristan Grimmer +
MIT License +
(recommended loading setting: Size = 13.0, GlyphOffset.y = +1, PixelSnapH = true) +
https://github.com/bluescan/proggyfonts + +**ProggyForever.ttf**, by Disco Hello, Tristan Grimmer +
MIT License +
https://github.com/ocornut/proggyforever + +Extra fonts files are available in the `misc/fonts/` folder. +Compared to 2014 when they were first introduced, we now have better font support and we embed ProggyForever. +I believe all the files here are unnecessary nowadays. You can find font yourself. They might eventually be removed. **Roboto-Medium.ttf**, by Christian Robetson
Apache License 2.0 @@ -451,15 +592,10 @@ Some fonts files are available in the `misc/fonts/` folder:
Apache License 2.0
https://www.fontsquirrel.com/fonts/droid-sans -**ProggyClean.ttf**, by Tristan Grimmer -
MIT License -
(recommended loading setting: Size = 13.0, GlyphOffset.y = +1) -
http://www.proggyfonts.net/ - **ProggyTiny.ttf**, by Tristan Grimmer
MIT License
(recommended loading setting: Size = 10.0, GlyphOffset.y = +1) -
http://www.proggyfonts.net/ +
https://github.com/bluescan/proggyfonts **Karla-Regular.ttf**, by Jonathan Pinhorn
SIL OPEN FONT LICENSE Version 1.1 @@ -485,15 +621,14 @@ Some fonts files are available in the `misc/fonts/` folder: #### MONOSPACE FONTS -Pixel Perfect: -- Proggy Fonts, by Tristan Grimmer http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php -- Sweet16, Sweet16 Mono, by Martin Sedlak (Latin + Supplemental + Extended A) https://github.com/kmar/Sweet16Font (also include an .inl file to use directly in dear imgui.) +image -Regular: -- Google Noto Mono Fonts https://www.google.com/get/noto/ -- Typefaces for source code beautification https://github.com/chrissimpkins/codeface -- Programmation fonts http://s9w.github.io/font_compare/ -- Inconsolata http://www.levien.com/type/myfonts/inconsolata.html +- Proggy Fonts, by Tristan Grimmer https://github.com/bluescan/proggyfonts +- Sweet16, Sweet16 Mono, by Martin Sedlak (Latin + Supplemental + Extended A) https://github.com/kmar/Sweet16Font +- Google Noto Mono Fonts: https://www.google.com/get/noto/ +- Typefaces for source code beautification: https://github.com/chrissimpkins/codeface +- Programmation fonts: http://s9w.github.io/font_compare/ +- Inconsolata: http://www.levien.com/type/myfonts/inconsolata.html - Adobe Source Code Pro: Monospaced font family for ui & coding environments https://github.com/adobe-fonts/source-code-pro - Monospace/Fixed Width Programmer's Fonts http://www.lowing.org/fonts/ diff --git a/docs/README.md b/docs/README.md index c47f03b9b..283dd479b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,7 @@ Businesses: support continued development and maintenance via invoiced sponsorin | [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Releases & Changelogs](#releases--changelogs) - [Demo](#demo) - [Getting Started & Integration](#getting-started--integration) | :----------------------------------------------------------: | | [Gallery](#gallery) - [Support, FAQ](#support-frequently-asked-questions-faq) - [How to help](#how-to-help) - **[Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding)** - [Credits](#credits) - [License](#license) | -| [Wiki](https://github.com/ocornut/imgui/wiki) - [Extensions](https://github.com/ocornut/imgui/wiki/Useful-Extensions) - [Languages bindings & frameworks backends](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) | +| [Wiki](https://github.com/ocornut/imgui/wiki) - [Extensions](https://github.com/ocornut/imgui/wiki/Useful-Extensions) - [Language bindings & framework backends](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) | ### The Pitch @@ -39,10 +39,12 @@ Dear ImGui is particularly suited to integration in game engines (for tooling), ### Usage -**The core of Dear ImGui is self-contained within a few platform-agnostic files** which you can easily compile in your application/engine. They are all the files in the root folder of the repository (imgui*.cpp, imgui*.h). **No specific build process is required**. You can add the .cpp files into your existing project. +**The core of Dear ImGui is self-contained within a few platform-agnostic files** which you can easily compile in your application/engine. They are all the files in the root folder of the repository (`imgui*.cpp`, `imgui*.h`). **No specific build process is required**: you can add all files into your existing project. **Backends for a variety of graphics API and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui. +C++20 users wishing to use a module may the use [stripe2933/imgui-module](https://github.com/stripe2933/imgui-module) third-party extension. + See the [Getting Started & Integration](#getting-started--integration) section of this document for more details. After Dear ImGui is set up in your application, you can use it from \_anywhere\_ in your program loop: @@ -50,11 +52,11 @@ After Dear ImGui is set up in your application, you can use it from \_anywhere\_ ImGui::Text("Hello, world %d", 123); if (ImGui::Button("Save")) MySaveFunction(); -ImGui::InputText("string", buf, IM_ARRAYSIZE(buf)); +ImGui::InputText("string", buf, IM_COUNTOF(buf)); ImGui::SliderFloat("float", &f, 0.0f, 1.0f); ``` -![sample code output (dark, segoeui font, freetype)](https://user-images.githubusercontent.com/8225057/191050833-b7ecf528-bfae-4a9f-ac1b-f3d83437a2f4.png) -![sample code output (light, segoeui font, freetype)](https://user-images.githubusercontent.com/8225057/191050838-8742efd4-504d-4334-a9a2-e756d15bc2ab.png) +sample code output (dark) +sample code output (light) ```cpp // Create a window called "My First Tool", with a menu bar. @@ -88,7 +90,7 @@ for (int n = 0; n < 50; n++) ImGui::EndChild(); ImGui::End(); ``` -![my_first_tool_v188](https://user-images.githubusercontent.com/8225057/191055698-690a5651-458f-4856-b5a9-e8cc95c543e2.gif) +![my_first_tool_v192 6](https://github.com/user-attachments/assets/6c76658c-302f-403b-af26-d517e2bfb0d4) Dear ImGui allows you to **create elaborate tools** as well as very short-lived ones. On the extreme side of short-livedness: using the Edit&Continue (hot code reload) feature of modern compilers you can add a few widgets to tweak variables while your application is running, and remove the code a minute later! Dear ImGui is not just for tweaking values. You can use it to trace a running algorithm by just emitting text commands. You can use it along with your own reflection data to browse your dataset live. You can use it to expose the internals of a subsystem in your engine, to create a logger, an inspection tool, a profiler, a debugger, an entire game-making editor/framework, etc. @@ -107,35 +109,11 @@ Reading the changelogs is a good way to keep up to date with the things Dear ImG ### Demo -Calling the `ImGui::ShowDemoWindow()` function will create a demo window showcasing a variety of features and examples. The code is always available for reference in `imgui_demo.cpp`. [Here's how the demo looks](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v167/v167-misc.png). +Calling the `ImGui::ShowDemoWindow()` function will create a demo window showcasing a variety of features and examples. The code is always available for reference in `imgui_demo.cpp`. +- [imgui_explorer](https://pthom.github.io/imgui_explorer): Web version of the demo w/ source code browser, courtesy of [@pthom](https://github.com/pthom). You should be able to build the examples from sources. If you don't, let us know! If you want to have a quick look at some Dear ImGui features, you can download Windows binaries of the demo app here: -- [imgui-demo-binaries-20240105.zip](https://www.dearimgui.com/binaries/imgui-demo-binaries-20240105.zip) (Windows, 1.90.1 WIP, built 2024/01/05, master) or [older binaries](https://www.dearimgui.com/binaries). - -The demo applications are not DPI aware so expect some blurriness on a 4K screen. For DPI awareness in your application, you can load/reload your font at a different scale and scale your style with `style.ScaleAllSizes()` (see [FAQ](https://www.dearimgui.com/faq)). - -### Getting Started & Integration - -See the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide for details. - -On most platforms and when using C++, **you should be able to use a combination of the [imgui_impl_xxxx](https://github.com/ocornut/imgui/tree/master/backends) backends without modification** (e.g. `imgui_impl_win32.cpp` + `imgui_impl_dx11.cpp`). If your engine supports multiple platforms, consider using more imgui_impl_xxxx files instead of rewriting them: this will be less work for you, and you can get Dear ImGui running immediately. You can _later_ decide to rewrite a custom backend using your custom engine functions if you wish so. - -Integrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/keyboard/gamepad inputs 2) uploading a texture to your GPU/render engine 3) providing a render function that can bind textures and render textured triangles, which is essentially what Backends are doing. The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications doing just that: setting up a window and using backends. If you follow the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide it should in theory takes you less than an hour to integrate Dear ImGui. **Make sure to spend time reading the [FAQ](https://www.dearimgui.com/faq), comments, and the examples applications!** - -Officially maintained backends/bindings (in repository): -- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_Renderer, Vulkan, WebGPU. -- Platforms: GLFW, SDL2/SDL3, Win32, Glut, OSX, Android. -- Frameworks: Allegro5, Emscripten. - -[Third-party backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) wiki page: -- Languages: C, C# and: Beef, ChaiScript, CovScript, Crystal, D, Go, Haskell, Haxe/hxcpp, Java, JavaScript, Julia, Kotlin, Lobster, Lua, Nim, Odin, Pascal, PureBasic, Python, ReaScript, Ruby, Rust, Swift, Zig... -- Frameworks: AGS/Adventure Game Studio, Amethyst, Blender, bsf, Cinder, Cocos2d-x, Defold, Diligent Engine, Ebiten, Flexium, GML/Game Maker Studio, GLEQ, Godot, GTK3, Irrlicht Engine, JUCE, LÖVE+LUA, Mach Engine, Magnum, Marmalade, Monogame, NanoRT, nCine, Nim Game Lib, Nintendo 3DS/Switch/WiiU (homebrew), Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, raylib, SFML, Sokol, Unity, Unreal Engine 4/5, UWP, vtk, VulkanHpp, VulkanSceneGraph, Win32 GDI, WxWidgets. -- Many bindings are auto-generated (by good old [cimgui](https://github.com/cimgui/cimgui) or newer/experimental [dear_bindings](https://github.com/dearimgui/dear_bindings)), you can use their metadata output to generate bindings for other languages. - -[Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page: -- Automation/testing, Text editors, node editors, timeline editors, plotting, software renderers, remote network access, memory editors, gizmos, etc. Notable and well supported extensions include [ImPlot](https://github.com/epezent/implot) and [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine). - -Also see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas. +- [imgui-demo-binaries-20260225.zip](https://www.dearimgui.com/binaries/imgui-demo-binaries-20260225.zip) (Windows, 1.92.6, built 2026/02/25, master) or [older binaries](https://www.dearimgui.com/binaries). ### Gallery @@ -150,6 +128,35 @@ For a list of third-party widgets and extensions, check out the [Useful Extensio | Custom engine [erhe](https://github.com/tksuoran/erhe) (docking branch)
[![erhe](https://user-images.githubusercontent.com/8225057/190203358-6988b846-0686-480e-8663-1311fbd18abd.jpg)](https://user-images.githubusercontent.com/994606/147875067-a848991e-2ad2-4fd3-bf71-4aeb8a547bcf.png) | Custom engine for [Wonder Boy: The Dragon's Trap](http://www.TheDragonsTrap.com) (2017)
[![the dragon's trap](https://user-images.githubusercontent.com/8225057/190203379-57fcb80e-4aec-4fec-959e-17ddd3cd71e5.jpg)](https://cloud.githubusercontent.com/assets/8225057/20628927/33e14cac-b329-11e6-80f6-9524e93b048a.png) | | Custom engine (untitled)
[![editor white](https://user-images.githubusercontent.com/8225057/190203393-c5ac9f22-b900-4d1e-bfeb-6027c63e3d92.jpg)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v160/editor_white.png) | Tracy Profiler ([github](https://github.com/wolfpld/tracy))
[![tracy profiler](https://user-images.githubusercontent.com/8225057/190203401-7b595f6e-607c-44d3-97ea-4c2673244dfb.jpg)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v176/tracy_profiler.png) | +### Getting Started & Integration + +See the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide for details. + +On most platforms and when using C++, **you should be able to use a combination of the [imgui_impl_xxxx](https://github.com/ocornut/imgui/tree/master/backends) backends without modification** (e.g. `imgui_impl_win32.cpp` + `imgui_impl_dx11.cpp`). If your engine supports multiple platforms, consider using more imgui_impl_xxxx files instead of rewriting them: this will be less work for you, and you can get Dear ImGui running immediately. You can _later_ decide to rewrite a custom backend using your custom engine functions if you wish so. + +Integrating Dear ImGui within your custom engine is a matter of mainly 1) wiring mouse/keyboard/gamepad inputs 2) uploading a texture to your GPU/render engine 3) providing a render function that can create/update textures and render textured triangles. This is exactly what backends are doing. +- The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications setting up a window and using standard backends. +- The [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide has instructions to integrate imgui into an existing application using standard backends. It should in theory take you less than an hour to integrate Dear ImGui into your existing codebase where support libraries are linked. Less if you read carefully. +- The [Backends](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md) guide explains what backends are doing, and has instructions to implement a custom backend. You can also refer to the source code of our ~20 backends to understand how they work. +- Generally, **make sure to spend time reading the [FAQ](https://www.dearimgui.com/faq), comments, and the examples applications!** + +Officially maintained backends (in repository): +- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_GPU, SDL_Renderer2/3, Vulkan, WebGPU. +- Platforms: GLFW, SDL2/SDL3, Win32, Glut, OSX, Android. +- Frameworks: Allegro5, Emscripten. + +[Third-party backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) wiki page: +- Languages: C, C# and: Beef, ChaiScript, CovScript, Crystal, D, Go, Haskell, Haxe/hxcpp, Java, JavaScript, Julia, Kotlin, Lobster, Lua, Nim, Odin, Pascal, PureBasic, Python, ReaScript, Ruby, Rust, Swift, Zig... +- Frameworks: AGS/Adventure Game Studio, Amethyst, Blender, bsf, Cinder, Cocos2d-x, Defold, Diligent Engine, Ebiten, Flexium, GML/Game Maker Studio, GLEQ, Godot, GTK3, Irrlicht Engine, JUCE, LÖVE+LUA, Mach Engine, Magnum, Marmalade, Monogame, NanoRT, nCine, Nim Game Lib, Nintendo 3DS/Switch/WiiU (homebrew), Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, raylib, SFML, Sokol, Unity, Unreal Engine 4/5, UWP, vtk, VulkanHpp, VulkanSceneGraph, Win32 GDI, WxWidgets. +- Many bindings are auto-generated (by good old [cimgui](https://github.com/cimgui/cimgui) or our newer [dear_bindings](https://github.com/dearimgui/dear_bindings)), you can use their metadata output to generate bindings for other languages. + +[Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page: + +[![Useful extensions thumbnails](https://github.com/user-attachments/assets/e6b0aa7c-bf53-41c5-ac69-bea3098b1dee)](https://github.com/ocornut/imgui/wiki/Useful-Extensions) +- Automation/testing, Text editors, node editors, timeline editors, plotting, software renderers, remote network access, memory editors, gizmos, etc. Notable and well supported extensions include [ImPlot](https://github.com/epezent/implot), [ImPlot3d](https://github.com/brenocq/implot3d) and [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine). + +Also see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas. + ### Support, Frequently Asked Questions (FAQ) See: [Frequently Asked Questions (FAQ)](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md) where common questions are answered. @@ -184,7 +191,7 @@ How to help - See [GitHub Forum/Issues](https://github.com/ocornut/imgui/issues). - You may help with development and submit pull requests! Please understand that by submitting a PR you are also submitting a request for the maintainer to review your code and then take over its maintenance forever. PR should be crafted both in the interest of the end-users and also to ease the maintainer into understanding and accepting it. - See [Help wanted](https://github.com/ocornut/imgui/wiki/Help-Wanted) on the [Wiki](https://github.com/ocornut/imgui/wiki/) for some more ideas. -- Be a [Funding Supporter](https://github.com/ocornut/imgui/wiki/Funding)! Have your company financially support this project via invoiced sponsors/maintenance or by buying a license for [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine) (please reach out: omar AT dearimgui DOT com). +- Be a [Funding Supporter](https://github.com/ocornut/imgui/wiki/Funding)! Have your company financially support this project via invoiced sponsors/maintenance or by buying a license for [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine) (please reach out: contact AT dearimgui DOT com). Sponsors -------- @@ -203,7 +210,7 @@ Dear ImGui is using software and services provided free of charge for open sourc Credits ------- -Developed by [Omar Cornut](https://www.miracleworld.net) and every direct or indirect [contributors](https://github.com/ocornut/imgui/graphs/contributors) to the GitHub. The early version of this library was developed with the support of [Media Molecule](https://www.mediamolecule.com) and first used internally on the game [Tearaway](https://tearaway.mediamolecule.com) (PS Vita). +Developed by [Omar Cornut](https://www.miracleworld.net) and every direct or indirect [contributors](https://github.com/ocornut/imgui/graphs/contributors) to the GitHub. The early version of this library was developed with the support of [Media Molecule](https://www.mediamolecule.com) and first used internally on the game [Tearaway](https://youtu.be/w0oxBviRGlU) (PS Vita). Recurring contributors include Rokas Kupstys [@rokups](https://github.com/rokups) (2020-2022): a good portion of work on automation system and regression tests now available in [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine). @@ -211,10 +218,11 @@ Maintenance/support contracts, sponsoring invoices and other B2B transactions ar Omar: "I first discovered the IMGUI paradigm at [Q-Games](https://www.q-games.com) where Atman Binstock had dropped his own simple implementation in the codebase, which I spent quite some time improving and thinking about. It turned out that Atman was exposed to the concept directly by working with Casey. When I moved to Media Molecule I rewrote a new library trying to overcome the flaws and limitations of the first one I've worked with. It became this library and since then I have spent an unreasonable amount of time iterating and improving it." -Embeds [ProggyClean.ttf](https://www.proggyfonts.net) font by Tristan Grimmer (MIT license). +Embeds [ProggyClean](https://www.proggyfonts.net) font by Tristan Grimmer (MIT license). +
Embeds [ProggyForever](https://github.com/ocornut/proggyforever) fonts by Disco Hello, Tristan Grimmer (MIT license).
Embeds [stb_textedit.h, stb_truetype.h, stb_rect_pack.h](https://github.com/nothings/stb/) by Sean Barrett (public domain). -Inspiration, feedback, and testing for early versions: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. Also thank you to everyone posting feedback, questions and patches on GitHub. +Inspiration, feedback, and testing for early versions: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. Special thanks to Alex Evans, Patrick Doane, Marco Koegler for kindly helping. Also thank you to everyone posting feedback, questions and patches on GitHub. License ------- diff --git a/docs/TODO.txt b/docs/TODO.txt index 2a42874cd..6107e5dc0 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -9,7 +9,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - doc: add a proper documentation system (maybe relying on automation? #435) - doc: checklist app to verify backends/integration of imgui (test inputs, rendering, callback, etc.). - doc/tips: tips of the day: website? applet in imgui_club? - + - window: preserve/restore relative focus ordering (persistent or not), and e.g. of multiple reappearing windows (#2304) -> also see docking reference to same #. - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis). (#690) - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass. @@ -22,13 +22,10 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - window: using SetWindowPos() inside Begin() and moving the window with the mouse reacts a very ugly glitch. We should just defer the SetWindowPos() call. - window: GetWindowSize() returns (0,0) when not calculated? (#1045) - window: investigate better auto-positioning for new windows. - - window: top most window flag? more z-order contrl? (#2574) + - window: top most window flag? more z-order control? (#2574) - window/size: manually triggered auto-fit (double-click on grip) shouldn't resize window down to viewport size? - window/size: how to allow to e.g. auto-size vertically to fit contents, but be horizontally resizable? Assuming SetNextWindowSize() is modified to treat -1.0f on each axis as "keep as-is" (would be good but might break erroneous code): Problem is UpdateWindowManualResize() and lots of code treat (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) together. - window/opt: freeze window flag: if not focused/hovered, return false, render with previous ImDrawList. and/or reduce refresh rate. -> this may require enforcing that it is illegal to submit contents if Begin returns false. - - window/child: background options for child windows, border option (disable rounding). - - window/child: allow resizing of child windows (possibly given min/max for each axis?.) - - window/child: allow SetNextWindowContentSize() to work on child windows. - window/clipping: some form of clipping when DisplaySize (or corresponding viewport) is zero. - window/tabbing: add a way to signify that a window or docked window requires attention (e.g. blinking title bar, trying to click behind a modal). - window/id_stack: add e.g. window->GetIDFromPath() with support for leading / and ../ (#1390, #331) -> model from test engine. @@ -57,7 +54,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - widgets: start exposing PushItemFlag() and ImGuiItemFlags - widgets: alignment options in style (e.g. center Selectable, Right-Align within Button, etc.) #1260 - widgets: activate by identifier (trigger button, focus given id) - - widgets: custom glyph/shapes replacements for stock sapes. (also #6090 #2431 #2235 #6517) + - widgets: custom glyph/shapes replacements for stock shapes. (also #6090 #2431 #2235 #6517) - widgets: coloredit: keep reporting as active when picker is on? - widgets: group/scalarn functions: expose more per-component information. e.g. store NextItemData.ComponentIdx set by scalarn function, groups can expose them back somehow. - selectable: using (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported. @@ -90,7 +87,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - input text multi-line: line numbers? status bar? (follow up on #200) - input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725) - input text multi-line: better horizontal scrolling support (#383, #1224) - - input text multi-line: single call to AddText() should be coarse clipped on InputTextEx() end. - input number: optional range min/max for Input*() functions - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled) - input number: use mouse wheel to step up/down @@ -137,7 +133,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - image/image button: misalignment on padded/bordered button? - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that? - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt() - - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate. + - slider: add dragging-based widgets to edit values with mouse (on 2 axes), saving screen real-estate. - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign) - slider: relative dragging? + precision dragging - slider: step option (#1183) @@ -213,7 +209,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs. - log: obsolete LogButtons().... (was: LogButtons() options for specifying depth and/or hiding depth slider) - - filters: set a current filter that certains items (e.g. tree node) can automatically query to hide themselves + - filters: set a current filter that certain items (e.g. tree node) can automatically query to hide themselves - filters: handle wild-cards (with implicit leading/trailing *), reg-exprs - filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb) @@ -244,22 +240,14 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font: arbitrary line spacing. (#2945) - font: MergeMode: flags to select overwriting or not (this is now very easy with refactored ImFontAtlasBuildWithStbTruetype) - - font: free the Alpha buffer if user only requested RGBA. -!- font: better CalcTextSizeA() API, at least for simple use cases. current one is horrible (perhaps have simple vs extended versions). + - font: better CalcTextSizeA() API, at least for simple use cases. current one is horrible (perhaps have simple vs extended versions). - font: for the purpose of RenderTextEllipsis(), it might be useful that CalcTextSizeA() can ignore the trailing padding? - font: a CalcTextHeight() helper could run faster than CalcTextSize().y - font: enforce monospace through ImFontConfig (for icons?) + create dual ImFont output from same input, reusing rasterized data but with different glyphs/AdvanceX - - font: finish CustomRectRegister() to allow mapping Unicode codepoint to custom texture data - - font: remove ID from CustomRect registration, it seems unnecessary! - font: make it easier to submit own bitmap font (same texture, another texture?). (#2127, #2575) - - font: PushFontSize API (#1018) - font: MemoryTTF taking ownership confusing/not obvious, maybe default should be opposite? - font: storing MinAdvanceX per font would allow us to skip calculating line width (under a threshold of character count) in loops looking for block width - - font/demo: add tools to show glyphs used by a text blob, display U16 value, list missing glyphs. - font/demo: demonstrate use of ImFontGlyphRangesBuilder. - - font/atlas: add a missing Glyphs.reserve() - - font/atlas: incremental updates - - font/atlas: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. - font/draw: vertical and/or rotated text renderer (#705) - vertical is easier clipping wise - font/draw: need to be able to specify wrap start position. - font/draw: better reserve policy for large horizontal block of text (shouldn't reserve for all clipped lines). also see #3349. @@ -268,7 +256,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font: optimization: for monospace font (like the default one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance (need to make sure TAB is still correct), would save on cache line. - font: add support for kerning, probably optional. A) perhaps default to (32..128)^2 matrix ~ 9K entries = 36KB, then hash for non-ascii?. B) or sparse lookup into per-char list? - font: add a simpler CalcTextSizeA() api? current one ok but not welcome if user needs to call it directly (without going through ImGui::CalcTextSize) - - font: fix AddRemapChar() to work before atlas has been built. - font: (api breaking) remove "TTF" from symbol names. also because it now supports OTF. - font/opt: Considering storing standalone AdvanceX table as 16-bit fixed point integer? - font/opt: Glyph currently 40 bytes (2+9*4). Consider storing UV as 16-bits integer? (->32 bytes). X0/Y0/X1/Y1 as 16 fixed-point integers? Or X0/Y0 as float and X1/Y1 as fixed8_8? @@ -279,7 +266,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: expose wrap around flags/logic to allow e.g. grid based layout (pressing NavRight on the right-most element would go to the next row, etc.). see internal's NavMoveRequestTryWrapping(). - nav: patterns to make it possible for arrows key to update selection (see JustMovedTo in range_select branch) - nav: restore/find nearest NavId when current one disappear (e.g. pressed a button that disappear, or perhaps auto restoring when current button change name) - - nav: SetItemDefaultFocus() level of priority, so widget like Selectable when inside a popup could claim a low-priority default focus on the first selected iem + - nav: SetItemDefaultFocus() level of priority, so widgets like Selectable when inside a popup could claim a low-priority default focus on the first selected item - nav: holding space to repeat a button doesn't show button activated during hold. - nav: NavFlattened: init requests don't work properly on flattened siblings. - nav: NavFlattened: pageup/pagedown/home/end don't work properly on flattened siblings. @@ -321,7 +308,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - examples: window minimize, maximize (#583) - examples: provide a zero frame-rate/idle example. - - examples: dx11/dx12: try to use new swapchain blit models (#2970) + - examples: dx11/dx12: try to use new swapchain blit models (#2970, #9031) - backends: report it better when not able to create texture? - backends: glfw: could go idle when minimized? if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) { glfwWaitEvents(); continue; } // issue: DeltaTime will be super high on resume, perhaps provide a way to let impl know (#440) - backends: opengl: rename imgui_impl_opengl2 to impl_opengl_legacy and imgui_impl_opengl3 to imgui_impl_opengl? (#1900) @@ -329,7 +316,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - backends: opengl: explicitly disable GL_STENCIL_TEST in bindings. - backends: vulkan: viewport: support for synchronized swapping of multiple swap chains. - backends: bgfx: https://gist.github.com/RichardGale/6e2b74bc42b3005e08397236e4be0fd0 - - backends: emscriptem: with refactored examples, we could provide a direct imgui_impl_emscripten platform layer (see eg. https://github.com/floooh/sokol-samples/blob/master/html5/imgui-emsc.cc#L42) + - backends: emscripten: with refactored examples, we could provide a direct imgui_impl_emscripten platform layer (see eg. https://github.com/floooh/sokol-samples/blob/master/html5/imgui-emsc.cc#L42) - bindings: ways to use clang ast dump to generate bindings or helpers for bindings? (e.g. clang++ -Xclang -ast-dump=json imgui.h) (--> use https://github.com/dearimgui/dear_bindings) diff --git a/examples/README.txt b/examples/README.txt index 6db2f3c48..1a711e7b8 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -1,9 +1,12 @@ -See BACKENDS and EXAMPLES files in the docs/ folder, or on the web at: https://github.com/ocornut/imgui/tree/master/docs +New to Dear ImGui? +- Check out the Getting Started guide: https://github.com/ocornut/imgui/wiki/Getting-Started +- About Backends: docs/BACKENDS.md file, or on the web: https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md +- About Examples: docs/EXAMPLES.md file, or on the web: https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md Backends = Helper code to facilitate integration with platforms/graphics api (used by Examples + should be used by your app). Examples = Standalone applications showcasing integration with platforms/graphics api. -Some Examples have extra README files in their respective directory, please check them too! +A few Examples have extra README files in their respective directory, please check them too! Once Dear ImGui is running (in either examples or your own application/game/engine), run and refer to ImGui::ShowDemoWindow() in imgui_demo.cpp for the end-user API. diff --git a/examples/example_allegro5/README.md b/examples/example_allegro5/README.md index 4af31f6f3..940b47fdb 100644 --- a/examples/example_allegro5/README.md +++ b/examples/example_allegro5/README.md @@ -24,9 +24,9 @@ You may install Allegro using vcpkg: git clone https://github.com/Microsoft/vcpkg cd vcpkg bootstrap-vcpkg.bat -vcpkg install allegro5 --triplet=x86-windows ; for win32 -vcpkg install allegro5 --triplet=x64-windows ; for win64 -vcpkg integrate install ; register include / libs in Visual Studio +vcpkg install allegro5 --triplet=x86-windows ; for win32 +vcpkg install allegro5 --triplet=x64-windows ; for win64 +vcpkg integrate install ; register include / libs in Visual Studio ``` Build: diff --git a/examples/example_allegro5/main.cpp b/examples/example_allegro5/main.cpp index 3ca061cc6..ebb6d76b0 100644 --- a/examples/example_allegro5/main.cpp +++ b/examples/example_allegro5/main.cpp @@ -28,7 +28,7 @@ int main(int, char**) al_install_mouse(); al_init_primitives_addon(); al_set_new_display_flags(ALLEGRO_RESIZABLE); - ALLEGRO_DISPLAY* display = al_create_display(1280, 720); + ALLEGRO_DISPLAY* display = al_create_display(1280, 800); al_set_window_title(display, "Dear ImGui Allegro 5 example"); ALLEGRO_EVENT_QUEUE* queue = al_create_event_queue(); al_register_event_source(queue, al_get_display_event_source(display)); @@ -49,19 +49,21 @@ int main(int, char**) ImGui_ImplAllegro5_Init(display); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); bool show_demo_window = true; diff --git a/examples/example_android_opengl3/android/.gitignore b/examples/example_android_opengl3/android/.gitignore index 3c7a61910..41afcd57c 100644 --- a/examples/example_android_opengl3/android/.gitignore +++ b/examples/example_android_opengl3/android/.gitignore @@ -1,12 +1,14 @@ -.cxx -.externalNativeBuild build/ *.iml - -.idea .gradle +.idea +.DS_Store +/captures +.externalNativeBuild +.cxx local.properties # Android Studio puts a Gradle wrapper here, that we don't want: gradle/ +!gradle/libs.versions.toml gradlew* diff --git a/examples/example_android_opengl3/android/app/build.gradle b/examples/example_android_opengl3/android/app/build.gradle index 53181baa2..e142f1917 100644 --- a/examples/example_android_opengl3/android/app/build.gradle +++ b/examples/example_android_opengl3/android/app/build.gradle @@ -1,16 +1,16 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} android { - compileSdkVersion 33 - buildToolsVersion "33.0.2" - ndkVersion "25.2.9519653" + namespace 'imgui.example.android' + compileSdk 36 defaultConfig { applicationId "imgui.example.android" - namespace "imgui.example.android" - minSdkVersion 24 - targetSdkVersion 33 + minSdk 24 + targetSdk 36 versionCode 1 versionName "1.0" } @@ -21,26 +21,17 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt') } } - compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } - kotlinOptions { - jvmTarget="11" + jvmTarget = '11' } - externalNativeBuild { cmake { - path "../../CMakeLists.txt" + path file('../../CMakeLists.txt') version '3.22.1' } } } -repositories { - mavenCentral() -} -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml b/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml index 5a1e2d9e5..6410b5a13 100644 --- a/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml +++ b/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - + window); ImGui_ImplOpenGL3_Init("#version 300 es"); + // Setup scaling + float main_scale = 2.0f; + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. + // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! // - Android: The TTF files have to be placed into the assets/ directory (android/app/src/main/assets), we use our GetAssetData() helper to retrieve them. + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); - // We load the default font with increased size to improve readability on many devices with "high" DPI. - // FIXME: Put some effort into DPI awareness. // Important: when calling AddFontFromMemoryTTF(), ownership of font_data is transferred by Dear ImGui by default (deleted is handled by Dear ImGui), unless we set FontDataOwnedByAtlas=false in ImFontConfig - ImFontConfig font_cfg; - font_cfg.SizePixels = 22.0f; - io.Fonts->AddFontDefault(&font_cfg); - //void* font_data; - //int font_data_size; + //void* font_data; + //int font_data_size; //ImFont* font; //font_data_size = GetAssetData("segoeui.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size); //IM_ASSERT(font != nullptr); //font_data_size = GetAssetData("DroidSans.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size); //IM_ASSERT(font != nullptr); //font_data_size = GetAssetData("Roboto-Medium.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size); //IM_ASSERT(font != nullptr); //font_data_size = GetAssetData("Cousine-Regular.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 15.0f); + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size); //IM_ASSERT(font != nullptr); //font_data_size = GetAssetData("ArialUni.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size); //IM_ASSERT(font != nullptr); - // Arbitrary scale-up - // FIXME: Put some effort into DPI awareness - ImGui::GetStyle().ScaleAllSizes(3.0f); - g_Initialized = true; } diff --git a/examples/example_apple_metal/Makefile b/examples/example_apple_metal/Makefile new file mode 100644 index 000000000..9412c9b53 --- /dev/null +++ b/examples/example_apple_metal/Makefile @@ -0,0 +1,21 @@ +# Makefile for example_apple_metal, for macOS only (**not iOS**) +CXX = clang++ +EXE = example_apple_metal +IMGUI_DIR = ../../ +SOURCES = main.mm +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_osx.mm $(IMGUI_DIR)/backends/imgui_impl_metal.mm + +CXXFLAGS = -std=c++11 -ObjC++ -fobjc-arc -Wall -Wextra -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +FRAMEWORKS = -framework AppKit -framework Metal -framework MetalKit -framework QuartzCore -framework GameController + +all: $(EXE) + +$(EXE): $(SOURCES) + $(CXX) $(CXXFLAGS) $^ $(FRAMEWORKS) -o $@ + +run: all + ./$(EXE) + +clean: + rm -f $(EXE) *.o diff --git a/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj b/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj index 4bb4fc288..bf3c80d6e 100644 --- a/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj +++ b/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -209,7 +209,8 @@ 8307E7B620E9F9C700473790 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1200; + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1530; ORGANIZATIONNAME = "Warren Moore"; TargetAttributes = { 8307E7C320E9F9C900473790 = { @@ -268,9 +269,9 @@ 8309BDBB253CCCAD0045E2A1 /* imgui_impl_metal.mm in Sources */, 83BBEA0920EB54E700295997 /* imgui.cpp in Sources */, 83BBEA0720EB54E700295997 /* imgui_demo.cpp in Sources */, - 83BBEA0520EB54E700295997 /* imgui_draw.cpp in Sources */, + 83BBEA0520EB54E700295997 /* imgui_draw.cpp in Sources */, 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */, - 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */, + 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */, 8309BDA5253CCC070045E2A1 /* main.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -281,10 +282,10 @@ files = ( 8309BDBE253CCCB60045E2A1 /* imgui_impl_metal.mm in Sources */, 8309BDBF253CCCB60045E2A1 /* imgui_impl_osx.mm in Sources */, - 83BBEA0A20EB54E700295997 /* imgui.cpp in Sources */, - 83BBEA0820EB54E700295997 /* imgui_demo.cpp in Sources */, - 83BBEA0620EB54E700295997 /* imgui_draw.cpp in Sources */, - 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */, + 83BBEA0A20EB54E700295997 /* imgui.cpp in Sources */, + 83BBEA0820EB54E700295997 /* imgui_demo.cpp in Sources */, + 83BBEA0620EB54E700295997 /* imgui_draw.cpp in Sources */, + 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */, 07A82ED92139418F0078D120 /* imgui_widgets.cpp in Sources */, 8309BDA8253CCC080045E2A1 /* main.mm in Sources */, ); @@ -327,9 +328,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -383,9 +386,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -405,8 +410,11 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/iOS/Info-iOS.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "org.imgui.example.apple-metal-ios"; PRODUCT_NAME = example_apple_metal; SDKROOT = iphoneos; @@ -422,8 +430,11 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/iOS/Info-iOS.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "org.imgui.example.apple-metal-ios"; PRODUCT_NAME = example_apple_metal; SDKROOT = iphoneos; @@ -439,10 +450,14 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/macOS/Info-macOS.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = "org.imgui.example.apple-metal-macos"; PRODUCT_NAME = example_apple_metal; SDKROOT = macosx; @@ -456,10 +471,14 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/macOS/Info-macOS.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = "org.imgui.example.apple-metal-macos"; PRODUCT_NAME = example_apple_metal; SDKROOT = macosx; diff --git a/examples/example_apple_metal/main.mm b/examples/example_apple_metal/main.mm index 109ef6153..b8edbd2d1 100644 --- a/examples/example_apple_metal/main.mm +++ b/examples/example_apple_metal/main.mm @@ -69,19 +69,21 @@ ImGui_ImplMetal_Init(_device); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); return self; @@ -94,7 +96,7 @@ -(void)loadView { - self.view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, 1200, 720)]; + self.view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, 1200, 800)]; } -(void)viewDidLoad @@ -129,7 +131,7 @@ if (renderPassDescriptor == nil) { [commandBuffer commit]; - return; + return; } // Start the Dear ImGui frame @@ -192,7 +194,7 @@ [renderEncoder popDebugGroup]; [renderEncoder endEncoding]; - // Present + // Present [commandBuffer presentDrawable:view.currentDrawable]; [commandBuffer commit]; } @@ -319,9 +321,20 @@ #if TARGET_OS_OSX -int main(int argc, const char * argv[]) +int main(int, const char**) { - return NSApplicationMain(argc, argv); + @autoreleasepool + { + [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + AppDelegate *appDelegate = [[AppDelegate alloc] init]; // creates window + [NSApp setDelegate:appDelegate]; + + [NSApp activateIgnoringOtherApps:YES]; + [NSApp run]; + } + return 0; } #else diff --git a/examples/example_apple_opengl2/Makefile b/examples/example_apple_opengl2/Makefile new file mode 100644 index 000000000..4ad5fa692 --- /dev/null +++ b/examples/example_apple_opengl2/Makefile @@ -0,0 +1,21 @@ +# Makefile for example_apple_metal, for macOS only (**not iOS**) +CXX = clang++ +EXE = example_apple_opengl2 +IMGUI_DIR = ../../ +SOURCES = main.mm +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_osx.mm $(IMGUI_DIR)/backends/imgui_impl_opengl2.cpp + +CXXFLAGS = -std=c++11 -ObjC++ -fobjc-arc -Wall -Wextra -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +FRAMEWORKS = -framework Cocoa -framework OpenGL -framework GameController + +all: $(EXE) + +$(EXE): $(SOURCES) + $(CXX) $(CXXFLAGS) $(SOURCES) -o $(EXE) $(FRAMEWORKS) + +run: all + ./$(EXE) + +clean: + rm -f $(EXE) *.o diff --git a/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj b/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj index a168373d4..9770e4396 100644 --- a/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj +++ b/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj @@ -289,7 +289,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_NAME = "$(TARGET_NAME)"; USER_HEADER_SEARCH_PATHS = ../..; }; @@ -299,7 +299,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_NAME = "$(TARGET_NAME)"; USER_HEADER_SEARCH_PATHS = ../..; }; diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 815c0f72e..1cfa882c8 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -57,19 +57,21 @@ ImGui_ImplOpenGL2_Init(); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); } @@ -169,9 +171,9 @@ -(NSWindow*)window { if (_window != nil) - return (_window); + return _window; - NSRect viewRect = NSMakeRect(100.0, 100.0, 100.0 + 1280.0, 100 + 720.0); + NSRect viewRect = NSMakeRect(100.0, 100.0, 100.0 + 1280.0, 100 + 800.0); _window = [[NSWindow alloc] initWithContentRect:viewRect styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable|NSWindowStyleMaskClosable backing:NSBackingStoreBuffered defer:YES]; [_window setTitle:@"Dear ImGui OSX+OpenGL2 Example"]; @@ -179,7 +181,7 @@ [_window setOpaque:YES]; [_window makeKeyAndOrderFront:NSApp]; - return (_window); + return _window; } -(void)setupMenu diff --git a/examples/example_glfw_metal/main.mm b/examples/example_glfw_metal/main.mm index e9bc63acb..0ded0a3bd 100644 --- a/examples/example_glfw_metal/main.mm +++ b/examples/example_glfw_metal/main.mm @@ -27,44 +27,33 @@ static void glfw_error_callback(int error, const char* description) int main(int, char**) { - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls - - // Setup style - ImGui::StyleColorsDark(); - //ImGui::StyleColorsLight(); - - // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. - // - Read 'docs/FONTS.md' for more instructions and details. - // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); - //IM_ASSERT(font != nullptr); - - // Setup window glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) return 1; // Create window with graphics context + float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+Metal example", nullptr, nullptr); + GLFWwindow* window = glfwCreateWindow((int)(1280 * main_scale), (int)(800 * main_scale), "Dear ImGui GLFW+Metal example", nullptr, nullptr); if (window == nullptr) return 1; + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + id device = MTLCreateSystemDefaultDevice(); id commandQueue = [device newCommandQueue]; @@ -72,6 +61,24 @@ int main(int, char**) ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplMetal_Init(device); + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); + NSWindow *nswin = glfwGetCocoaWindow(window); CAMetalLayer *layer = [CAMetalLayer layer]; layer.device = device; diff --git a/examples/example_glfw_opengl2/Makefile b/examples/example_glfw_opengl2/Makefile index 1f15c15c0..a8a0128dc 100644 --- a/examples/example_glfw_opengl2/Makefile +++ b/examples/example_glfw_opengl2/Makefile @@ -21,6 +21,7 @@ SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui SOURCES += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl2.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) +LINUX_GL_LIBS = -lGL CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends CXXFLAGS += -g -Wall -Wformat @@ -32,7 +33,7 @@ LIBS = ifeq ($(UNAME_S), Linux) #LINUX ECHO_MESSAGE = "Linux" - LIBS += -lGL `pkg-config --static --libs glfw3` + LIBS += $(LINUX_GL_LIBS) `pkg-config --static --libs glfw3` CXXFLAGS += `pkg-config --cflags glfw3` CFLAGS = $(CXXFLAGS) diff --git a/examples/example_glfw_opengl2/main.cpp b/examples/example_glfw_opengl2/main.cpp index 1fcec2b2a..48584eaf8 100644 --- a/examples/example_glfw_opengl2/main.cpp +++ b/examples/example_glfw_opengl2/main.cpp @@ -40,7 +40,8 @@ int main(int, char**) return 1; // Create window with graphics context - GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL2 example", nullptr, nullptr); + float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only + GLFWwindow* window = glfwCreateWindow((int)(1280 * main_scale), (int)(800 * main_scale), "Dear ImGui GLFW+OpenGL2 example", nullptr, nullptr); if (window == nullptr) return 1; glfwMakeContextCurrent(window); @@ -57,24 +58,31 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL2_Init(); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state diff --git a/examples/example_glfw_opengl3/Makefile.emscripten b/examples/example_glfw_opengl3/Makefile.emscripten index bd972abff..8d2f6e7bd 100644 --- a/examples/example_glfw_opengl3/Makefile.emscripten +++ b/examples/example_glfw_opengl3/Makefile.emscripten @@ -32,8 +32,12 @@ EMS = ##--------------------------------------------------------------------- # ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only) -EMS += -s DISABLE_EXCEPTION_CATCHING=1 -LDFLAGS += -s USE_GLFW=3 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1 +# Note: For glfw, we use emscripten-glfw port (contrib.glfw3) instead of ('-s USE_GLFW=3' in LDFLAGS) to get a better support for High DPI displays. +EMS += -s DISABLE_EXCEPTION_CATCHING=1 --use-port=contrib.glfw3 +LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1 + +# Build as single file (binary text encoded in .html file) +#LDFLAGS += -sSINGLE_FILE # Uncomment next line to fix possible rendering bugs with Emscripten version older then 1.39.0 (https://github.com/ocornut/imgui/issues/2877) #EMS += -s BINARYEN_TRAP_MODE=clamp diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 3afe251e3..20d39fccc 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -43,11 +43,17 @@ int main(int, char**) // Decide GL+GLSL versions #if defined(IMGUI_IMPL_OPENGL_ES2) - // GL ES 2.0 + GLSL 100 + // GL ES 2.0 + GLSL 100 (WebGL 1.0) const char* glsl_version = "#version 100"; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); +#elif defined(IMGUI_IMPL_OPENGL_ES3) + // GL ES 3.0 + GLSL 300 es (WebGL 2.0) + const char* glsl_version = "#version 300 es"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); #elif defined(__APPLE__) // GL 3.2 + GLSL 150 const char* glsl_version = "#version 150"; @@ -65,7 +71,8 @@ int main(int, char**) #endif // Create window with graphics context - GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL3 example", nullptr, nullptr); + float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only + GLFWwindow* window = glfwCreateWindow((int)(1280 * main_scale), (int)(800 * main_scale), "Dear ImGui GLFW+OpenGL3 example", nullptr, nullptr); if (window == nullptr) return 1; glfwMakeContextCurrent(window); @@ -82,6 +89,11 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); #ifdef __EMSCRIPTEN__ @@ -90,20 +102,22 @@ int main(int, char**) ImGui_ImplOpenGL3_Init(glsl_version); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state diff --git a/examples/example_glfw_vulkan/CMakeLists.txt b/examples/example_glfw_vulkan/CMakeLists.txt index 443a144ea..75475dbae 100644 --- a/examples/example_glfw_vulkan/CMakeLists.txt +++ b/examples/example_glfw_vulkan/CMakeLists.txt @@ -15,7 +15,9 @@ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DVK_PROTOTYPES") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_PROTOTYPES") # GLFW -set(GLFW_DIR ../../../glfw) # Set this to point to an up-to-date GLFW repo +if(NOT GLFW_DIR) + set(GLFW_DIR ../../../glfw) # Set this to point to an up-to-date GLFW repo +endif() option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" OFF) option(GLFW_BUILD_TESTS "Build the GLFW test programs" OFF) option(GLFW_BUILD_DOCS "Build the GLFW documentation" OFF) diff --git a/examples/example_glfw_vulkan/Makefile b/examples/example_glfw_vulkan/Makefile new file mode 100644 index 000000000..1a84082d1 --- /dev/null +++ b/examples/example_glfw_vulkan/Makefile @@ -0,0 +1,83 @@ +# +# Cross Platform Makefile +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# +# You will need GLFW (http://www.glfw.org): +# Linux: +# apt-get install libglfw-dev +# Mac OS X: +# brew install glfw +# MSYS2: +# pacman -S --noconfirm --needed mingw-w64-x86_64-toolchain mingw-w64-x86_64-glfw +# + +#CXX = g++ +#CXX = clang++ + +EXE = example_glfw_vulkan +IMGUI_DIR = ../.. +SOURCES = main.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_vulkan.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) +LINUX_GL_LIBS = -lGL + +CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +CXXFLAGS += -g -Wall -Wformat +LIBS = + +##--------------------------------------------------------------------- +## BUILD FLAGS PER PLATFORM +##--------------------------------------------------------------------- + +ifeq ($(UNAME_S), Linux) #LINUX + ECHO_MESSAGE = "Linux" + LIBS += $(LINUX_GL_LIBS) `pkg-config --static --libs glfw3 vulkan` + + CXXFLAGS += `pkg-config --cflags glfw3 vulkan` + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(UNAME_S), Darwin) #APPLE + ECHO_MESSAGE = "Mac OS X" + LIBS += -framework Cocoa -framework IOKit -framework CoreVideo + LIBS += `pkg-config --libs glfw3 vulkan` + LIBS += -L/usr/local/lib -L/opt/local/lib -L/opt/homebrew/lib + #LIBS += -lglfw3 + + LIBS += `pkg-config --cflags glfw3 vulkan` + CXXFLAGS += -I/usr/local/include -I/opt/local/include -I/opt/homebrew/include + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(OS), Windows_NT) + ECHO_MESSAGE = "MinGW" + LIBS += -lgdi32 -limm32 + LIBS += `pkg-config --libs glfw3 vulkan` + + CXXFLAGS += `pkg-config --cflags glfw3 vulkan` + CFLAGS = $(CXXFLAGS) +endif + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(ECHO_MESSAGE) + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 901b46c4c..d70c7ce1b 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -38,6 +38,7 @@ //#define APP_USE_UNLIMITED_FRAME_RATE #ifdef _DEBUG #define APP_USE_VULKAN_DEBUG_REPORT +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; #endif // Data @@ -47,12 +48,11 @@ static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; static uint32_t g_QueueFamily = (uint32_t)-1; static VkQueue g_Queue = VK_NULL_HANDLE; -static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static ImGui_ImplVulkanH_Window g_MainWindowData; -static int g_MinImageCount = 2; +static uint32_t g_MinImageCount = 2; static bool g_SwapChainRebuild = false; static void glfw_error_callback(int error, const char* description) @@ -61,7 +61,7 @@ static void glfw_error_callback(int error, const char* description) } static void check_vk_result(VkResult err) { - if (err == 0) + if (err == VK_SUCCESS) return; fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); if (err < 0) @@ -85,35 +85,6 @@ static bool IsExtensionAvailable(const ImVector& properti return false; } -static VkPhysicalDevice SetupVulkan_SelectPhysicalDevice() -{ - uint32_t gpu_count; - VkResult err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, nullptr); - check_vk_result(err); - IM_ASSERT(gpu_count > 0); - - ImVector gpus; - gpus.resize(gpu_count); - err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus.Data); - check_vk_result(err); - - // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers - // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple - // dedicated GPUs) is out of scope of this sample. - for (VkPhysicalDevice& device : gpus) - { - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(device, &properties); - if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) - return device; - } - - // Use first GPU (Integrated) is a Discrete one is not available. - if (gpu_count > 0) - return gpus[0]; - return VK_NULL_HANDLE; -} - static void SetupVulkan(ImVector instance_extensions) { VkResult err; @@ -177,23 +148,12 @@ static void SetupVulkan(ImVector instance_extensions) } // Select Physical Device (GPU) - g_PhysicalDevice = SetupVulkan_SelectPhysicalDevice(); + g_PhysicalDevice = ImGui_ImplVulkanH_SelectPhysicalDevice(g_Instance); + IM_ASSERT(g_PhysicalDevice != VK_NULL_HANDLE); // Select graphics queue family - { - uint32_t count; - vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, nullptr); - VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues); - for (uint32_t i = 0; i < count; i++) - if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) - { - g_QueueFamily = i; - break; - } - free(queues); - IM_ASSERT(g_QueueFamily != (uint32_t)-1); - } + g_QueueFamily = ImGui_ImplVulkanH_SelectQueueFamilyIndex(g_PhysicalDevice); + IM_ASSERT(g_QueueFamily != (uint32_t)-1); // Create Logical Device (with 1 queue) { @@ -229,18 +189,19 @@ static void SetupVulkan(ImVector instance_extensions) } // Create Descriptor Pool - // The example only requires a single combined image sampler descriptor for the font image and only uses one descriptor set (for that) - // If you wish to load e.g. additional textures you may need to alter pools sizes. + // If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets. { VkDescriptorPoolSize pool_sizes[] = { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE }, }; 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 = 1; - pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.maxSets = 0; + for (VkDescriptorPoolSize& pool_size : pool_sizes) + pool_info.maxSets += pool_size.descriptorCount; + pool_info.poolSizeCount = (uint32_t)IM_COUNTOF(pool_sizes); pool_info.pPoolSizes = pool_sizes; err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); check_vk_result(err); @@ -251,11 +212,9 @@ static void SetupVulkan(ImVector instance_extensions) // Your real engine/app may not use them. static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { - wd->Surface = surface; - // Check for WSI support VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -265,7 +224,8 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + wd->Surface = surface; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode #ifdef APP_USE_UNLIMITED_FRAME_RATE @@ -273,12 +233,12 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface #else VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; #endif - wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_COUNTOF(present_modes)); //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); } static void CleanupVulkan() @@ -295,24 +255,23 @@ static void CleanupVulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void CleanupVulkanWindow() +static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) { - ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator); + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); } static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkResult err; - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { @@ -381,11 +340,11 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores } @@ -398,7 +357,8 @@ int main(int, char**) // Create window with Vulkan context glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+Vulkan example", nullptr, nullptr); + float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only + GLFWwindow* window = glfwCreateWindow((int)(1280 * main_scale), (int)(800 * main_scale), "Dear ImGui GLFW+Vulkan example", nullptr, nullptr); if (!glfwVulkanSupported()) { printf("GLFW: Vulkan Not Supported\n"); @@ -434,9 +394,15 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForVulkan(window, true); ImGui_ImplVulkan_InitInfo init_info = {}; + //init_info.ApiVersion = VK_API_VERSION_1_3; // Pass in your value of VkApplicationInfo::apiVersion, otherwise will default to header version. init_info.Instance = g_Instance; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; @@ -444,29 +410,31 @@ int main(int, char**) init_info.Queue = g_Queue; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; - init_info.RenderPass = wd->RenderPass; - init_info.Subpass = 0; init_info.MinImageCount = g_MinImageCount; init_info.ImageCount = wd->ImageCount; - init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; init_info.Allocator = g_Allocator; + init_info.PipelineInfoMain.RenderPass = wd->RenderPass; + init_info.PipelineInfoMain.Subpass = 0; + init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state @@ -490,7 +458,7 @@ int main(int, char**) if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) { ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0); g_MainWindowData.FrameIndex = 0; g_SwapChainRebuild = false; } @@ -564,7 +532,7 @@ int main(int, char**) ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); - CleanupVulkanWindow(); + CleanupVulkanWindow(&g_MainWindowData); CleanupVulkan(); glfwDestroyWindow(window); diff --git a/examples/example_glfw_wgpu/CMakeLists.txt b/examples/example_glfw_wgpu/CMakeLists.txt index 8e164e488..53bcda339 100644 --- a/examples/example_glfw_wgpu/CMakeLists.txt +++ b/examples/example_glfw_wgpu/CMakeLists.txt @@ -1,23 +1,40 @@ -# Building for desktop (WebGPU-native) with Dawn: +# Building for desktop with Dawn: # 1. git clone https://github.com/google/dawn dawn # 2. cmake -B build -DIMGUI_DAWN_DIR=dawn # 3. cmake --build build # The resulting binary will be found at one of the following locations: -# * build/Debug/example_glfw_wgpu[.exe] -# * build/example_glfw_wgpu[.exe] +# * build/example_glfw_wgpu[.exe] or build/Debug/example_glfw_wgpu[.exe] + +# Building for desktop with WGPU-Native: +# 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases +# 2. unzip the downloaded file in your_preferred_folder +# 3. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory) +# 4. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_glfw_wgpu[.exe] or build/Debug/example_glfw_wgpu[.exe] + +# Building for desktop with WGVK (MUCH EASIER) +# 1. git clone https://github.com/manuel5975p/WGVK dawn +# 2. cmake -B build -DIMGUI_WGVK_DIR=wgvk +# 3. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_glfw_wgpu[.exe] or build/Debug/example_glfw_wgpu[.exe] # Building for Emscripten: # 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html # 2. Install Ninja build system # 3. emcmake cmake -G Ninja -B build +# (optional) -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py", see ReadMe.md # 3. cmake --build build # 4. emrun build/index.html -cmake_minimum_required(VERSION 3.10.2) +cmake_minimum_required(VERSION 3.22) # Dawn requires CMake >= 3.22 project(imgui_example_glfw_wgpu C CXX) +set(IMGUI_EXECUTABLE example_glfw_wgpu) + if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) endif() set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17 @@ -25,93 +42,182 @@ set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17 # Dear ImGui set(IMGUI_DIR ../../) -# Libraries +set(IMGUI_EXAMPLE_SOURCE_FILES + # Example code + main.cpp + # Dear ImGui Backend files + ${IMGUI_DIR}/backends/imgui_impl_glfw.cpp + ${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp + # Dear ImGui files + ${IMGUI_DIR}/imgui.cpp + ${IMGUI_DIR}/imgui_draw.cpp + ${IMGUI_DIR}/imgui_demo.cpp + ${IMGUI_DIR}/imgui_tables.cpp + ${IMGUI_DIR}/imgui_widgets.cpp +) + if(EMSCRIPTEN) - if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "3.1.57") - set(IMGUI_EMSCRIPTEN_GLFW3 "--use-port=contrib.glfw3" CACHE STRING "Choose between --use-port=contrib.glfw3 and -sUSE_GLFW=3 for GLFW implementation (default to --use-port=contrib.glfw3)") - else() - # cannot use contrib.glfw3 prior to 3.1.57 - set(IMGUI_EMSCRIPTEN_GLFW3 "-sUSE_GLFW=3" CACHE STRING "Use -sUSE_GLFW=3 for GLFW implementation" FORCE) - endif() - set(LIBRARIES glfw) - add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) + if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "4.0.10") + set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "--use-port=emdawnwebgpu" CACHE STRING "Default to --use-port=emdawnwebgpu. You can override to provide your own local port.") + else() + message(FATAL_ERROR "emdawnwebgpu needs EMSCRIPTEN version >= 4.0.10") + endif() + + if(NOT IMGUI_EMSCRIPTEN_GLFW3) + # Defaults to contrib.glfw3 because Emscripten version is > 3.1.57 + set(IMGUI_EMSCRIPTEN_GLFW3 "--use-port=contrib.glfw3" CACHE STRING "Choose between --use-port=contrib.glfw3 and -sUSE_GLFW=3 for GLFW implementation (default to --use-port=contrib.glfw3)") + endif() + + set(LIBRARIES glfw) + add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) +else() # Native/Desktop build + + # Check DAWN/WGPU/WGVK directory + # If it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR or IMGUI_WGVK_DIR must be specified + if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR AND NOT IMGUI_WGVK_DIR) + message(FATAL_ERROR "Please specify one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") + endif() + if((IMGUI_DAWN_DIR AND (IMGUI_WGPU_DIR OR IMGUI_WGVK_DIR)) OR (IMGUI_WGPU_DIR AND IMGUI_WGVK_DIR)) + message(FATAL_ERROR "Please specify only one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") + endif() + + if(APPLE) # Add SDL2 module to get Surface, with libs and file property for MacOS build + set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++") + set(OS_LIBRARIES "-framework CoreFoundation -framework QuartzCore -framework Metal -framework MetalKit -framework Cocoa") + endif() + + find_package(glfw3 REQUIRED) + + if(IMGUI_DAWN_DIR) + list(APPEND CMAKE_PREFIX_PATH ${IMGUI_DAWN_DIR}) + find_package(Dawn) # Search for a Dawn installation using IMGUI_DAWN_DIR in CMAKE_PREFIX_PATH + if(Dawn_FOUND) + message("Dawn Installation has been found!") + set(LIBRARIES dawn::webgpu_dawn glfw ${OS_LIBRARIES}) + else() + set(IMGUI_DAWN_DIR CACHE PATH "Path to Dawn repository") + + option(DAWN_FETCH_DEPENDENCIES "Use fetch_dawn_dependencies.py as an alternative to using depot_tools" ON) + set(DAWN_BUILD_MONOLITHIC_LIBRARY "STATIC" CACHE STRING "Build monolithic library: SHARED, STATIC, or OFF.") + + option(DAWN_USE_GLFW OFF) # disable builtin GLFW in DAWN when we use SDL2 / SDL3 + + # Dawn builds many things by default - disable things we don't need + option(DAWN_BUILD_SAMPLES "Enables building Dawn's samples" OFF) + option(TINT_BUILD_CMD_TOOLS "Build the Tint command line tools" OFF) + option(TINT_BUILD_DOCS "Build documentation" OFF) + option(TINT_BUILD_TESTS "Build tests" OFF) + if(NOT APPLE) + option(TINT_BUILD_MSL_WRITER "Build the MSL output writer" OFF) + endif() + if(WIN32) + option(DAWN_FORCE_SYSTEM_COMPONENT_LOAD "Allow system component fallback" ON) + option(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" OFF) + option(TINT_BUILD_WGSL_READER "Build the WGSL input reader" ON) + option(TINT_BUILD_GLSL_WRITER "Build the GLSL output writer" OFF) + option(TINT_BUILD_GLSL_VALIDATOR "Build the GLSL output validator" OFF) + option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ON) + option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON) + endif() + # check if WAYLAND is the current Session Type and enable DAWN_USE_WAYLAND Wayland option @compile time + # You can override this using: cmake -DDAWN_USE_WAYLAND=X (X = ON | OFF) + if(LINUX) + if($ENV{XDG_SESSION_TYPE} MATCHES wayland) + option(DAWN_USE_WAYLAND "Enable support for Wayland surface" ON) + endif() + endif() + + add_subdirectory("${IMGUI_DAWN_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/dawn" EXCLUDE_FROM_ALL) + + set(LIBRARIES webgpu_dawn glfw) + endif() + endif() + + if(IMGUI_WGPU_DIR) + set(WGPU_NATIVE_LIB_DIR ${IMGUI_WGPU_DIR}/lib) + find_library(WGPU_LIBRARY NAMES libwgpu_native.a wgpu_native.lib wgpu_native HINTS ${WGPU_NATIVE_LIB_DIR} REQUIRED) + if(WIN32) + set(OS_LIBRARIES d3dcompiler ws2_32 userenv bcrypt ntdll opengl32 Propsys RuntimeObject) + elseif(UNIX AND NOT APPLE) + set(OS_LIBRARIES "-lm -ldl") + endif() + + set(LIBRARIES glfw ${WGPU_LIBRARY} ${OS_LIBRARIES}) + endif() + + if(IMGUI_WGVK_DIR) + endif() +endif() + +add_executable(${IMGUI_EXECUTABLE} ${IMGUI_EXAMPLE_SOURCE_FILES}) + +target_include_directories(${IMGUI_EXECUTABLE} PUBLIC + ${IMGUI_DIR} + ${IMGUI_DIR}/backends +) + +target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_EXAMPLE_GLFW_WGPU") + +# compiler option only for IMGUI_EXAMPLE_SOURCE_FILES +if (MSVC) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /W4) # warning level 4 else() - # Dawn wgpu desktop - set(DAWN_FETCH_DEPENDENCIES ON) - set(IMGUI_DAWN_DIR CACHE PATH "Path to Dawn repository") - if (NOT IMGUI_DAWN_DIR) - message(FATAL_ERROR "Please specify the Dawn repository by setting IMGUI_DAWN_DIR") - endif() - - option(DAWN_FETCH_DEPENDENCIES "Use fetch_dawn_dependencies.py as an alternative to using depot_tools" ON) - - # Dawn builds many things by default - disable things we don't need - option(DAWN_BUILD_SAMPLES "Enables building Dawn's samples" OFF) - option(TINT_BUILD_CMD_TOOLS "Build the Tint command line tools" OFF) - option(TINT_BUILD_DOCS "Build documentation" OFF) - option(TINT_BUILD_TESTS "Build tests" OFF) - if (NOT APPLE) - option(TINT_BUILD_MSL_WRITER "Build the MSL output writer" OFF) - endif() - if(WIN32) - option(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" OFF) - option(TINT_BUILD_WGSL_READER "Build the WGSL input reader" ON) - option(TINT_BUILD_GLSL_WRITER "Build the GLSL output writer" OFF) - option(TINT_BUILD_GLSL_VALIDATOR "Build the GLSL output validator" OFF) - option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" OFF) - option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON) - endif() - - add_subdirectory("${IMGUI_DAWN_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/dawn" EXCLUDE_FROM_ALL) - - set(LIBRARIES webgpu_dawn webgpu_cpp webgpu_glfw glfw) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC -Wall) # -Wextra -Wpedantic endif() -add_executable(example_glfw_wgpu - main.cpp - # backend files - ${IMGUI_DIR}/backends/imgui_impl_glfw.cpp - ${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp - # Dear ImGui files - ${IMGUI_DIR}/imgui.cpp - ${IMGUI_DIR}/imgui_draw.cpp - ${IMGUI_DIR}/imgui_demo.cpp - ${IMGUI_DIR}/imgui_tables.cpp - ${IMGUI_DIR}/imgui_widgets.cpp -) -IF(NOT EMSCRIPTEN) - target_compile_definitions(example_glfw_wgpu PUBLIC - "IMGUI_IMPL_WEBGPU_BACKEND_DAWN" - ) -endif() -target_include_directories(example_glfw_wgpu PUBLIC - ${IMGUI_DIR} - ${IMGUI_DIR}/backends -) +# In this example IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU internal define is set according to: +# EMSCRIPTEN: by used FLAG +# --use-port=emdawnwebgpu --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN defined +# NATIVE: by used SDK installation directory +# if IMGUI_DAWN_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN defined +# if IMGUI_WGPU_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU defined -target_link_libraries(example_glfw_wgpu PUBLIC ${LIBRARIES}) +if(NOT EMSCRIPTEN) # WegGPU-Native settings + # Set IMGUI_IMPL_WEBGPU_BACKEND_XXXX based on the SDK (directory) used + if(IMGUI_DAWN_DIR) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN") + if(NOT Dawn_FOUND) + target_link_libraries(${IMGUI_EXECUTABLE} INTERFACE webgpu_cpp) + endif() + endif() + if(IMGUI_WGPU_DIR) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU") + target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGPU_DIR}/include) + endif() + if(IMGUI_WGVK_DIR) + target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) + if (MSVC) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) + endif() + endif() -# Emscripten settings -if(EMSCRIPTEN) - if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3") - target_compile_options(example_glfw_wgpu PUBLIC - "${IMGUI_EMSCRIPTEN_GLFW3}" + target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${LIBRARIES}) + +else() # Emscripten settings + set(CMAKE_EXECUTABLE_SUFFIX ".html") + + if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3") + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "${IMGUI_EMSCRIPTEN_GLFW3}") + endif() + message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation") + + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}") + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN") + message(STATUS "Using ${IMGUI_EMSCRIPTEN_WEBGPU_FLAG} WebGPU implementation") + + target_link_options(${IMGUI_EXECUTABLE} PRIVATE + "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}" + "${IMGUI_EMSCRIPTEN_GLFW3}" + "-sWASM=1" + "-sASYNCIFY=1" + "-sALLOW_MEMORY_GROWTH=1" + "-sNO_EXIT_RUNTIME=0" + "-sASSERTIONS=1" + "-sDISABLE_EXCEPTION_CATCHING=1" + "-sNO_FILESYSTEM=1" + "--shell-file=${CMAKE_CURRENT_LIST_DIR}/../libs/emscripten/shell_minimal.html" ) - endif() - message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation") - target_link_options(example_glfw_wgpu PRIVATE - "-sUSE_WEBGPU=1" - "${IMGUI_EMSCRIPTEN_GLFW3}" - "-sWASM=1" - "-sALLOW_MEMORY_GROWTH=1" - "-sNO_EXIT_RUNTIME=0" - "-sASSERTIONS=1" - "-sDISABLE_EXCEPTION_CATCHING=1" - "-sNO_FILESYSTEM=1" - ) - set_target_properties(example_glfw_wgpu PROPERTIES OUTPUT_NAME "index") - # copy our custom index.html to build directory - add_custom_command(TARGET example_glfw_wgpu POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_LIST_DIR}/web/index.html" $ - ) + set_target_properties(${IMGUI_EXECUTABLE} PROPERTIES OUTPUT_NAME "index") endif() diff --git a/examples/example_glfw_wgpu/Makefile.emscripten b/examples/example_glfw_wgpu/Makefile.emscripten index 5c79f0c77..ff386fec5 100644 --- a/examples/example_glfw_wgpu/Makefile.emscripten +++ b/examples/example_glfw_wgpu/Makefile.emscripten @@ -6,8 +6,8 @@ # This Makefile assumes you have loaded emscripten's environment. # (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead) # -# Running `make` will produce three files: -# - web/index.html (current stored in the repository) +# Running `make -f Makefile.emscripten` will produce three files: +# - web/index.html # - web/index.js # - web/index.wasm # @@ -16,11 +16,11 @@ CC = emcc CXX = em++ WEB_DIR = web -EXE = $(WEB_DIR)/index.js +EXE = $(WEB_DIR)/index.html IMGUI_DIR = ../.. SOURCES = main.cpp -SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp SOURCES += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_wgpu.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) CPPFLAGS = @@ -32,9 +32,20 @@ EMS = ##--------------------------------------------------------------------- # ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only) -EMS += -s DISABLE_EXCEPTION_CATCHING=1 -LDFLAGS += -s USE_GLFW=3 -s USE_WEBGPU=1 -LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1 +# Note: For glfw, we use emscripten-glfw port (contrib.glfw3) instead of (-s USE_GLFW=3) to get a better support for High DPI displays. +EMS += -s DISABLE_EXCEPTION_CATCHING=1 --use-port=contrib.glfw3 +LDFLAGS += -s WASM=1 +LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 +LDFLAGS += -s ASYNCIFY=1 +LDFLAGS += -s NO_EXIT_RUNTIME=0 +LDFLAGS += -s ASSERTIONS=1 + +# Using Dawn-based WebGPU port (requires Emscripten >= 4.0.10) +EMS += --use-port=emdawnwebgpu +LDFLAGS += --use-port=emdawnwebgpu + +# Build as single file (binary text encoded in .html file) +#LDFLAGS += -sSINGLE_FILE # Emscripten allows preloading a file or folder to be accessible at runtime. # The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts" @@ -56,7 +67,7 @@ endif CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends #CPPFLAGS += -g CPPFLAGS += -Wall -Wformat -Os $(EMS) -#LDFLAGS += --shell-file shell_minimal.html +LDFLAGS += --shell-file ../libs/emscripten/shell_minimal.html LDFLAGS += $(EMS) ##--------------------------------------------------------------------- diff --git a/examples/example_glfw_wgpu/README.md b/examples/example_glfw_wgpu/README.md index 399d431ff..c1c40a59f 100644 --- a/examples/example_glfw_wgpu/README.md +++ b/examples/example_glfw_wgpu/README.md @@ -1,4 +1,141 @@ -## How to Build +## How to Build + + +--- + +### Using CMake +#### Building for desktop (WebGPU-native) with Google Dawn: + 1. `git clone https://github.com/google/dawn dawn` + 2. `cmake -B build -DIMGUI_DAWN_DIR=dawn` + 3. `cmake --build build` +The resulting binary will be found at one of the following locations: + * build/Debug/example_glfw_wgpu[.exe] + * build/example_glfw_wgpu[.exe] + +#### Building for desktop (WebGPU-Native) with WGPU: + 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases + 2. unzip the downloaded file in `your_preferred_folder` + 3. `cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder` ("full path" or "relative" starting from current directory) + 4. `cmake --build build` +The resulting binary will be found at one of the following locations: + * build/Debug/example_glfw_wgpu[.exe] + * build/example_glfw_wgpu[.exe] + +#### Building for Emscripten: + 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html + 2. Install Ninja build system + 3. `emcmake cmake -G Ninja -B build` + - (optional) `-DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py"`, see below + 4. `cmake --build build` + +#### Sync Emscripten with latest Google Dawn: +If you want to sync Emscripten with latest DAWN release it's necessary to download the `port-emdawnwgpu-package` (released daily by Google) here: +https://github.com/google/dawn/releases +Unpack it in your preferred folder and to replace the step 3 with: + +3. `emcmake cmake -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py" -G Ninja -B build` + +**N.B.** +For the WASM code produced by Emscripten to work correctly, it will also be necessary to have the "corresponding" (or newer) version of Google Canary (nightly build for developers) that includes the latest changes + + +--- + +### CMake by step + +#### Generate Dawn Native: + +- `cmake -G Ninja -DIMGUI_DAWN_DIR=path_to_sdk_dir -B where_to_build_dir` + - Using `IMGUI_DAWN_DIR` set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define + +#### Generate WGPU Native: + +- `cmake -G Ninja -DIMGUI_WGPU_DIR=path_to_sdk_dir -B where_to_build_dir` + + - Using `IMGUI_WGPU_DIR` set `IMGUI_IMPL_WEBGPU_BACKEND_WGPU` compiler define + +#### Generate Emscripten: + +- `emcmake cmake -G Ninja -B where_to_build_dir`\ +CMake checks the EMSCRIPEN version then: + - if EMS >= 4.0.10 uses `--use-port=emdawnwebgpu` flag to build + - it set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define + - if EMS < 4.0.10 the build aborts (`-sUSE_WEBGPU=1` is no longer supported by our examples and our WGPU backend) + +#### Generate Emscripten using external WebGPU library (emdawnwebgpu_pkg) + +- `emcmake cmake -G Ninja -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path_to_emdawnwebgpu_pkg" -B where_to_build_dir` + - it set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define + - *To use external WebGPU library it's necessary to have EMS >= 4.0.10 or the minimum requirements specified by the package:* + - https://github.com/google/dawn/releases + +#### Build time + +Once the procedure for the specific builder is generated, the build command is **always the same**: + +- Build using CMake + - `cmake --build where_to_build_dir` + - It will use selected builder to build the example. + +- Build explicitly: + - `cd where_to_build_dir` + - `ninja` + - This is the builder chosen during the generation phase + +--- + +### CMake useful options +#### Generator types (alternative to **ninja** builder): +- `-G Ninja` to build with __ninja__ builder +- `-G "Unix Makefiles"` to build with __make__ builder +- `-G "Visual Studio 17 2022" -A x64` to create a VS 2022 solution (.sln) file, Windows only + - **Native build only** + - Not **officially** supported to build Google Dawn + +Example: +- using **make** instead **ninja**: + - `cmake -G "Unix Makefiles" -DIMGUI_DAWN_DIR=path_to_sdk_dir -B where_to_build_dir` + +**Syntax is case sensitive and the "" are necessary in case of spaces between words* + +#### Directories +- The directory path can be absolute or relative (starting from the current directory) +- It's necessary to use different `where_to_build_dir` for different CMake generations + + +#### Build type +The default build type is **Debug** +It is possible to use a different build type using: +- `-DCMAKE_BUILD_TYPE=Release` +- `-DCMAKE_BUILD_TYPE=MinSizeRel` +- `-DCMAKE_BUILD_TYPE=RelWithDebInfo` + +Example: +- building **Release**: + - `cmake -G ninja -DIMGUI_WGPU_DIR=path_to_sdk_dir -DCMAKE_BUILD_TYPE=Release -B where_to_build_dir ` + +#### GLFW / SDL2 / SDL3 includes, libraries, search paths and package manager + +Includes and libraries, by default, are searched in system/compiler paths (environment variables): you can add the path to your development tools to the environment variables without having to modify the `CMakeLists.txt` file. +- e.g. CLang search in path specified from the following environment variables: + - include files: CPATH, C_INCLUDE_PATH, CPLUS_INCLUDE_PATH + - library files: LIBRARY_PATH + +If you are using a package manager (**vcpkg** / **conan** / ... ) you need/can to specify it, adding to cmake command: +- `-DCMAKE_TOOLCHAIN_FILE=path/to/package_manager.cmake` + +Examples: + +- using **vcpkg** package manager it's necessary adding: + - `-DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake` + +- full cmake command using **vcpkg** package manager: + - `cmake -G Ninja -DIMGUI_DAWN_DIR=path_to_sdk_dir -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake -B where_to_build_dir` + +--- + + +### Using makefile - You need to install Emscripten from https://emscripten.org/docs/getting_started/downloads.html, and have the environment variables set, as described in https://emscripten.org/docs/getting_started/downloads.html#installation-instructions @@ -10,6 +147,8 @@ - Requires recent Emscripten as WGPU is still a work-in-progress API. +--- + ## How to Run To run on a local machine: diff --git a/examples/example_glfw_wgpu/main.cpp b/examples/example_glfw_wgpu/main.cpp index f510987ed..9d48258ba 100644 --- a/examples/example_glfw_wgpu/main.cpp +++ b/examples/example_glfw_wgpu/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for using GLFW + WebGPU +// Dear ImGui: standalone example application for GLFW + WebGPU // - Emscripten is supported for publishing on web. See https://emscripten.org. // - Dawn is used as a WebGPU implementation on desktop. @@ -12,54 +12,44 @@ #include "imgui_impl_glfw.h" #include "imgui_impl_wgpu.h" #include - -#ifdef __EMSCRIPTEN__ -#include -#include -#include -#else -#include -#endif - +#include #include -#include -#include // This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details. #ifdef __EMSCRIPTEN__ +#include +#include #include "../libs/emscripten/emscripten_mainloop_stub.h" #endif -// Global WebGPU required states -static WGPUInstance wgpu_instance = nullptr; -static WGPUDevice wgpu_device = nullptr; -static WGPUSurface wgpu_surface = nullptr; -static WGPUTextureFormat wgpu_preferred_fmt = WGPUTextureFormat_RGBA8Unorm; -static WGPUSwapChain wgpu_swap_chain = nullptr; -static int wgpu_swap_chain_width = 1280; -static int wgpu_swap_chain_height = 720; +#include +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) +#include +#endif + +// Data +static WGPUInstance wgpu_instance = nullptr; +static WGPUDevice wgpu_device = nullptr; +static WGPUSurface wgpu_surface = nullptr; +static WGPUQueue wgpu_queue = nullptr; +static WGPUSurfaceConfiguration wgpu_surface_configuration = {}; +static int wgpu_surface_width = 1280; +static int wgpu_surface_height = 800; // Forward declarations static bool InitWGPU(GLFWwindow* window); -static void CreateSwapChain(int width, int height); +WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, GLFWwindow* window); static void glfw_error_callback(int error, const char* description) { printf("GLFW Error %d: %s\n", error, description); } -static void wgpu_error_callback(WGPUErrorType error_type, const char* message, void*) +static void ResizeSurface(int width, int height) { - const char* error_type_lbl = ""; - switch (error_type) - { - case WGPUErrorType_Validation: error_type_lbl = "Validation"; break; - case WGPUErrorType_OutOfMemory: error_type_lbl = "Out of memory"; break; - case WGPUErrorType_Unknown: error_type_lbl = "Unknown"; break; - case WGPUErrorType_DeviceLost: error_type_lbl = "Device lost"; break; - default: error_type_lbl = "Unknown"; - } - printf("%s error: %s\n", error_type_lbl, message); + wgpu_surface_configuration.width = wgpu_surface_width = width; + wgpu_surface_configuration.height = wgpu_surface_height = height; + wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration); } // Main code @@ -72,19 +62,23 @@ int main(int, char**) // Make sure GLFW does not initialize any graphics context. // This needs to be done explicitly later. glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - GLFWwindow* window = glfwCreateWindow(wgpu_swap_chain_width, wgpu_swap_chain_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr); + + // Create window + float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only + wgpu_surface_width = (int)(wgpu_surface_width * main_scale); + wgpu_surface_height = (int)(wgpu_surface_height * main_scale); + GLFWwindow* window = glfwCreateWindow(wgpu_surface_width, wgpu_surface_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr); if (window == nullptr) return 1; // Initialize the WebGPU environment if (!InitWGPU(window)) { - if (window) - glfwDestroyWindow(window); + glfwDestroyWindow(window); glfwTerminate(); return 1; } - CreateSwapChain(wgpu_swap_chain_width, wgpu_swap_chain_height); + glfwShowWindow(window); // Setup Dear ImGui context @@ -98,6 +92,11 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOther(window, true); #ifdef __EMSCRIPTEN__ @@ -106,27 +105,28 @@ int main(int, char**) ImGui_ImplWGPU_InitInfo init_info; init_info.Device = wgpu_device; init_info.NumFramesInFlight = 3; - init_info.RenderTargetFormat = wgpu_preferred_fmt; + init_info.RenderTargetFormat = wgpu_surface_configuration.format; init_info.DepthStencilFormat = WGPUTextureFormat_Undefined; ImGui_ImplWGPU_Init(&init_info); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - // - Emscripten allows preloading a file or folder to be accessible at runtime. See Makefile for details. - //io.Fonts->AddFontDefault(); + // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); #ifndef IMGUI_DISABLE_FILE_FUNCTIONS - //io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf", 15.0f); - //io.Fonts->AddFontFromFileTTF("fonts/ProggyTiny.ttf", 10.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf"); //IM_ASSERT(font != nullptr); #endif @@ -160,11 +160,24 @@ int main(int, char**) // React to changes in screen size int width, height; glfwGetFramebufferSize((GLFWwindow*)window, &width, &height); - if (width != wgpu_swap_chain_width || height != wgpu_swap_chain_height) + if (width != wgpu_surface_width || height != wgpu_surface_height) + ResizeSurface(width, height); + + // Check surface status for error. If texture is not optimal, try to reconfigure the surface. + WGPUSurfaceTexture surface_texture; + wgpuSurfaceGetCurrentTexture(wgpu_surface, &surface_texture); + if (ImGui_ImplWGPU_IsSurfaceStatusError(surface_texture.status)) { - ImGui_ImplWGPU_InvalidateDeviceObjects(); - CreateSwapChain(width, height); - ImGui_ImplWGPU_CreateDeviceObjects(); + fprintf(stderr, "Unrecoverable Surface Texture status=%#.8x\n", surface_texture.status); + abort(); + } + if (ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(surface_texture.status)) + { + if (surface_texture.texture) + wgpuTextureRelease(surface_texture.texture); + if (width > 0 && height > 0) + ResizeSurface(width, height); + continue; } // Start the Dear ImGui frame @@ -212,17 +225,21 @@ int main(int, char**) // Rendering ImGui::Render(); -#ifndef __EMSCRIPTEN__ - // Tick needs to be called in Dawn to display validation errors - wgpuDeviceTick(wgpu_device); -#endif + WGPUTextureViewDescriptor view_desc = {}; + view_desc.format = wgpu_surface_configuration.format; + view_desc.dimension = WGPUTextureViewDimension_2D ; + view_desc.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED; + view_desc.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED; + view_desc.aspect = WGPUTextureAspect_All; + + WGPUTextureView texture_view = wgpuTextureCreateView(surface_texture.texture, &view_desc); WGPURenderPassColorAttachment color_attachments = {}; color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; color_attachments.loadOp = WGPULoadOp_Clear; color_attachments.storeOp = WGPUStoreOp_Store; color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w }; - color_attachments.view = wgpuSwapChainGetCurrentTextureView(wgpu_swap_chain); + color_attachments.view = texture_view; WGPURenderPassDescriptor render_pass_desc = {}; render_pass_desc.colorAttachmentCount = 1; @@ -238,14 +255,16 @@ int main(int, char**) WGPUCommandBufferDescriptor cmd_buffer_desc = {}; WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc); - WGPUQueue queue = wgpuDeviceGetQueue(wgpu_device); - wgpuQueueSubmit(queue, 1, &cmd_buffer); + wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer); #ifndef __EMSCRIPTEN__ - wgpuSwapChainPresent(wgpu_swap_chain); + wgpuSurfacePresent(wgpu_surface); + // Tick needs to be called in Dawn to display validation errors +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) + wgpuDeviceTick(wgpu_device); #endif - - wgpuTextureViewRelease(color_attachments.view); +#endif + wgpuTextureViewRelease(texture_view); wgpuRenderPassEncoderRelease(pass); wgpuCommandEncoderRelease(encoder); wgpuCommandBufferRelease(cmd_buffer); @@ -259,92 +278,282 @@ int main(int, char**) ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); + wgpuSurfaceUnconfigure(wgpu_surface); + wgpuSurfaceRelease(wgpu_surface); + wgpuQueueRelease(wgpu_queue); + wgpuDeviceRelease(wgpu_device); + wgpuInstanceRelease(wgpu_instance); + glfwDestroyWindow(window); glfwTerminate(); return 0; } -#ifndef __EMSCRIPTEN__ -static WGPUAdapter RequestAdapter(WGPUInstance instance) +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) +static WGPUAdapter RequestAdapter(wgpu::Instance& instance) { - auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* message, void* pUserData) + wgpu::Adapter acquired_adapter; + wgpu::RequestAdapterOptions adapter_options; + auto onRequestAdapter = [&](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message) { - if (status == WGPURequestAdapterStatus_Success) - *(WGPUAdapter*)(pUserData) = adapter; - else - printf("Could not get WebGPU adapter: %s\n", message); -}; - WGPUAdapter adapter; - wgpuInstanceRequestAdapter(instance, nullptr, onAdapterRequestEnded, (void*)&adapter); - return adapter; -} - -static WGPUDevice RequestDevice(WGPUAdapter& adapter) -{ - auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status, WGPUDevice device, const char* message, void* pUserData) - { - if (status == WGPURequestDeviceStatus_Success) - *(WGPUDevice*)(pUserData) = device; - else - printf("Could not get WebGPU device: %s\n", message); + if (status != wgpu::RequestAdapterStatus::Success) + { + printf("Failed to get an adapter: %s\n", message.data); + return; + } + acquired_adapter = std::move(adapter); }; - WGPUDevice device; - wgpuAdapterRequestDevice(adapter, nullptr, onDeviceRequestEnded, (void*)&device); - return device; + + // Synchronously (wait until) acquire Adapter + wgpu::Future waitAdapterFunc { instance.RequestAdapter(&adapter_options, wgpu::CallbackMode::WaitAnyOnly, onRequestAdapter) }; + wgpu::WaitStatus waitStatusAdapter = instance.WaitAny(waitAdapterFunc, UINT64_MAX); + IM_ASSERT(acquired_adapter != nullptr && waitStatusAdapter == wgpu::WaitStatus::Success && "Error on Adapter request"); + return acquired_adapter.MoveToCHandle(); } -#endif -static bool InitWGPU(GLFWwindow* window) +static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter) { - wgpu::Instance instance = wgpuCreateInstance(nullptr); + // Set device callback functions + wgpu::DeviceDescriptor device_desc; + device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, + [](const wgpu::Device&, wgpu::DeviceLostReason type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetDeviceLostReasonName((WGPUDeviceLostReason)type), msg.data); } + ); + device_desc.SetUncapturedErrorCallback( + [](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetErrorTypeName((WGPUErrorType)type), msg.data); } + ); -#ifdef __EMSCRIPTEN__ - wgpu_device = emscripten_webgpu_get_device(); - if (!wgpu_device) - return false; -#else - WGPUAdapter adapter = RequestAdapter(instance.Get()); - if (!adapter) - return false; - wgpu_device = RequestDevice(adapter); -#endif + wgpu::Device acquired_device; + auto onRequestDevice = [&](wgpu::RequestDeviceStatus status, wgpu::Device local_device, wgpu::StringView message) + { + if (status != wgpu::RequestDeviceStatus::Success) + { + printf("Failed to get an device: %s\n", message.data); + return; + } + acquired_device = std::move(local_device); + }; + // Synchronously (wait until) get Device + wgpu::Future waitDeviceFunc { adapter.RequestDevice(&device_desc, wgpu::CallbackMode::WaitAnyOnly, onRequestDevice) }; + wgpu::WaitStatus waitStatusDevice = instance.WaitAny(waitDeviceFunc, UINT64_MAX); + IM_ASSERT(acquired_device != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request"); + return acquired_device.MoveToCHandle(); +} +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) +static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2) +{ + IM_UNUSED(userdata2); + if (status == WGPURequestAdapterStatus_Success) + { + WGPUAdapter* extAdapter = (WGPUAdapter*)userdata1; + *extAdapter = adapter; + } + else + { + printf("Request_adapter status=%#.8x message=%.*s\n", status, (int) message.length, message.data); + } +} + +static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata1, void* userdata2) +{ + IM_UNUSED(userdata2); + if (status == WGPURequestDeviceStatus_Success) + { + WGPUDevice* extDevice = (WGPUDevice*)userdata1; + *extDevice = device; + } + else + { + printf("Request_device status=%#.8x message=%.*s\n", status, (int) message.length, message.data); + } +} + +static WGPUAdapter RequestAdapter(WGPUInstance& instance) +{ + WGPURequestAdapterOptions adapter_options = {}; + + WGPUAdapter local_adapter = nullptr; + WGPURequestAdapterCallbackInfo adapterCallbackInfo = {}; + adapterCallbackInfo.mode = WGPUCallbackMode_WaitAnyOnly; + adapterCallbackInfo.callback = handle_request_adapter; + adapterCallbackInfo.userdata1 = &local_adapter; + + WGPUFuture future = wgpuInstanceRequestAdapter(instance, &adapter_options, adapterCallbackInfo); + WGPUFutureWaitInfo waitInfo = { future, false }; + wgpuInstanceWaitAny(instance, 1, &waitInfo, ~0ull); + IM_ASSERT(local_adapter && "Error on Adapter request"); + return local_adapter; +} + +static WGPUDevice RequestDevice(WGPUInstance& instance, WGPUAdapter& adapter) +{ + WGPUDevice local_device = nullptr; + WGPURequestDeviceCallbackInfo deviceCallbackInfo = {}; + deviceCallbackInfo.mode = WGPUCallbackMode_WaitAnyOnly; + deviceCallbackInfo.callback = handle_request_device; + deviceCallbackInfo.userdata1 = &local_device; + WGPUFuture future = wgpuAdapterRequestDevice(adapter, nullptr, deviceCallbackInfo); + WGPUFutureWaitInfo waitInfo = { future, false }; + wgpuInstanceWaitAny(instance, 1, &waitInfo, ~0ull); + IM_ASSERT(local_device && "Error on Device request"); + return local_device; +} +#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU + +bool InitWGPU(GLFWwindow* window) +{ + WGPUTextureFormat preferred_fmt = WGPUTextureFormat_Undefined; // acquired from SurfaceCapabilities + + // Google DAWN backend: Adapter and Device acquisition, Surface creation +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) + wgpu::InstanceDescriptor instance_desc = {}; + static constexpr wgpu::InstanceFeatureName timedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny; + instance_desc.requiredFeatureCount = 1; + instance_desc.requiredFeatures = &timedWaitAny; + wgpu::Instance instance = wgpu::CreateInstance(&instance_desc); + + wgpu::Adapter adapter = RequestAdapter(instance); + ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter.Get()); + + wgpu_device = RequestDevice(instance, adapter); + + // Create the surface. #ifdef __EMSCRIPTEN__ - wgpu::SurfaceDescriptorFromCanvasHTMLSelector html_surface_desc = {}; - html_surface_desc.selector = "#canvas"; + wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvas_desc = {}; + canvas_desc.selector = "#canvas"; + wgpu::SurfaceDescriptor surface_desc = {}; - surface_desc.nextInChain = &html_surface_desc; - wgpu::Surface surface = instance.CreateSurface(&surface_desc); - - wgpu::Adapter adapter = {}; - wgpu_preferred_fmt = (WGPUTextureFormat)surface.GetPreferredFormat(adapter); + surface_desc.nextInChain = &canvas_desc; + wgpu_surface = instance.CreateSurface(&surface_desc).MoveToCHandle(); #else - wgpu::Surface surface = wgpu::glfw::CreateSurfaceForWindow(instance, window); - if (!surface) + wgpu_surface = CreateWGPUSurface(instance.Get(), window); +#endif + if (!wgpu_surface) return false; - wgpu_preferred_fmt = WGPUTextureFormat_BGRA8Unorm; + + // Moving Dawn objects into WGPU handles + wgpu_instance = instance.MoveToCHandle(); + + WGPUSurfaceCapabilities surface_capabilities = {}; + wgpuSurfaceGetCapabilities(wgpu_surface, adapter.Get(), &surface_capabilities); + + preferred_fmt = surface_capabilities.formats[0]; + + // WGPU backend: Adapter and Device acquisition, Surface creation +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) + WGPUInstanceDescriptor instanceDesc = {}; + WGPUInstanceFeatureName timedWaitAny = WGPUInstanceFeatureName_TimedWaitAny; + instanceDesc.requiredFeatureCount = 1; + instanceDesc.requiredFeatures = &timedWaitAny; + wgpu_instance = wgpuCreateInstance(&instanceDesc); + +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) + wgpuSetLogCallback( + [](WGPULogLevel level, WGPUStringView msg, void* userdata) { fprintf(stderr, "%s: %.*s\n", ImGui_ImplWGPU_GetLogLevelName(level), (int)msg.length, msg.data); }, nullptr + ); + wgpuSetLogLevel(WGPULogLevel_Warn); #endif - wgpu_instance = instance.MoveToCHandle(); - wgpu_surface = surface.MoveToCHandle(); + WGPUAdapter adapter = RequestAdapter(wgpu_instance); + ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter); - wgpuDeviceSetUncapturedErrorCallback(wgpu_device, wgpu_error_callback, nullptr); + wgpu_device = RequestDevice(wgpu_instance, adapter); + + // Create the surface. + wgpu_surface = CreateWGPUSurface(wgpu_instance, window); + if (!wgpu_surface) + return false; + + WGPUSurfaceCapabilities surface_capabilities = {}; + wgpuSurfaceGetCapabilities(wgpu_surface, adapter, &surface_capabilities); + + preferred_fmt = surface_capabilities.formats[0]; +#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU + + wgpu_surface_configuration.presentMode = WGPUPresentMode_Fifo; + wgpu_surface_configuration.alphaMode = WGPUCompositeAlphaMode_Auto; + wgpu_surface_configuration.usage = WGPUTextureUsage_RenderAttachment; + wgpu_surface_configuration.width = wgpu_surface_width; + wgpu_surface_configuration.height = wgpu_surface_height; + wgpu_surface_configuration.device = wgpu_device; + wgpu_surface_configuration.format = preferred_fmt; + + wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration); + wgpu_queue = wgpuDeviceGetQueue(wgpu_device); return true; } -static void CreateSwapChain(int width, int height) +// GLFW helper to create a WebGPU surface, used only in WGPU-Native. DAWN-Native already has a built-in function +// As of today (2025/10) there is no "official" support in GLFW to create a surface for WebGPU backend +// This stub uses "low level" GLFW calls to acquire information from a specific Window Manager. +// Currently supported platforms: Windows / Linux (X11 and Wayland) / MacOS. Not necessary nor available with EMSCRIPTEN. +#ifndef __EMSCRIPTEN__ + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +#define GLFW_HAS_X11_OR_WAYLAND 1 +#else +#define GLFW_HAS_X11_OR_WAYLAND 0 +#endif +#ifdef _WIN32 +#undef APIENTRY +#ifndef GLFW_EXPOSE_NATIVE_WIN32 // for glfwGetWin32Window() +#define GLFW_EXPOSE_NATIVE_WIN32 +#endif +#elif defined(__APPLE__) +#ifndef GLFW_EXPOSE_NATIVE_COCOA // for glfwGetCocoaWindow() +#define GLFW_EXPOSE_NATIVE_COCOA +#endif +#elif GLFW_HAS_X11_OR_WAYLAND +#ifndef GLFW_EXPOSE_NATIVE_X11 // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.) +#define GLFW_EXPOSE_NATIVE_X11 +#endif +#ifndef GLFW_EXPOSE_NATIVE_WAYLAND +#if defined(__has_include) && __has_include() +#define GLFW_EXPOSE_NATIVE_WAYLAND +#endif +#endif +#endif +#include +#undef Status // X11 headers are leaking this and also 'Success', 'Always', 'None', all used in DAWN api. Add #undef if necessary. + +WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, GLFWwindow* window) { - if (wgpu_swap_chain) - wgpuSwapChainRelease(wgpu_swap_chain); - wgpu_swap_chain_width = width; - wgpu_swap_chain_height = height; - WGPUSwapChainDescriptor swap_chain_desc = {}; - swap_chain_desc.usage = WGPUTextureUsage_RenderAttachment; - swap_chain_desc.format = wgpu_preferred_fmt; - swap_chain_desc.width = width; - swap_chain_desc.height = height; - swap_chain_desc.presentMode = WGPUPresentMode_Fifo; - wgpu_swap_chain = wgpuDeviceCreateSwapChain(wgpu_device, wgpu_surface, &swap_chain_desc); + ImGui_ImplWGPU_CreateSurfaceInfo create_info = {}; + create_info.Instance = instance; +#if defined(GLFW_EXPOSE_NATIVE_COCOA) + { + create_info.System = "cocoa"; + create_info.RawWindow = (void*)glfwGetCocoaWindow(window); + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) + if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) + { + create_info.System = "wayland"; + create_info.RawDisplay = (void*)glfwGetWaylandDisplay(); + create_info.RawSurface = (void*)glfwGetWaylandWindow(window); + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#elif defined(GLFW_EXPOSE_NATIVE_X11) + if (glfwGetPlatform() == GLFW_PLATFORM_X11) + { + create_info.System = "x11"; + create_info.RawWindow = (void*)glfwGetX11Window(window); + create_info.RawDisplay = (void*)glfwGetX11Display(); + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#elif defined(GLFW_EXPOSE_NATIVE_WIN32) + { + create_info.System = "win32"; + create_info.RawWindow = (void*)glfwGetWin32Window(window); + create_info.RawInstance = (void*)::GetModuleHandle(NULL); + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#else +#error "Unsupported WebGPU native platform!" + return nullptr; +#endif } +#endif // #ifndef __EMSCRIPTEN__ diff --git a/examples/example_glfw_wgpu/web/index.html b/examples/example_glfw_wgpu/web/index.html deleted file mode 100644 index a2a91c4a7..000000000 --- a/examples/example_glfw_wgpu/web/index.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - Dear ImGui Emscripten+GLFW+WebGPU example - - - - - - - diff --git a/examples/example_glut_opengl2/main.cpp b/examples/example_glut_opengl2/main.cpp index 58539ca5f..ca8017f5b 100644 --- a/examples/example_glut_opengl2/main.cpp +++ b/examples/example_glut_opengl2/main.cpp @@ -48,7 +48,7 @@ int main(int argc, char** argv) glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS); #endif glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_MULTISAMPLE); - glutInitWindowSize(1280, 720); + glutInitWindowSize(1280, 800); glutCreateWindow("Dear ImGui GLUT+OpenGL2 Example"); // Setup GLUT display function @@ -78,21 +78,22 @@ int main(int argc, char** argv) // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. ImGui_ImplGLUT_InstallFuncs(); - // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Main loop diff --git a/examples/example_null/example_null.vcxproj b/examples/example_null/example_null.vcxproj new file mode 100644 index 000000000..8ef991f3f --- /dev/null +++ b/examples/example_null/example_null.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D} + example_win32_directx11 + + + + Application + true + Unicode + v140 + + + Application + true + Unicode + v140 + + + Application + false + true + Unicode + v140 + + + Application + false + true + Unicode + v140 + + + + + + + + + + + + + + + + + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + + + + Level4 + Disabled + ..\..;..\..\backends;%(AdditionalIncludeDirectories); + /utf-8 %(AdditionalOptions) + + + true + d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + $(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + Console + + + + + Level4 + Disabled + ..\..;..\..\backends;%(AdditionalIncludeDirectories); + /utf-8 %(AdditionalOptions) + + + true + d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + $(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + Console + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%(AdditionalIncludeDirectories); + false + /utf-8 %(AdditionalOptions) + + + true + true + true + d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + $(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + Console + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%(AdditionalIncludeDirectories); + false + /utf-8 %(AdditionalOptions) + + + true + true + true + d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + $(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + Console + + + + + + + + + + + true + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/example_null/example_null.vcxproj.filters b/examples/example_null/example_null.vcxproj.filters new file mode 100644 index 000000000..6d575a8f5 --- /dev/null +++ b/examples/example_null/example_null.vcxproj.filters @@ -0,0 +1,57 @@ + + + + + {0587d7a3-f2ce-4d56-b84f-a0005d3bfce6} + + + {08e36723-ce4f-4cff-9662-c40801cf1acf} + + + + + imgui + + + imgui + + + imgui + + + sources + + + + + imgui + + + sources + + + imgui + + + imgui + + + imgui + + + imgui + + + sources + + + + + + imgui + + + imgui + + + \ No newline at end of file diff --git a/examples/example_null/main.cpp b/examples/example_null/main.cpp index f7153cc48..eba675670 100644 --- a/examples/example_null/main.cpp +++ b/examples/example_null/main.cpp @@ -4,22 +4,26 @@ #include "imgui.h" #include +// For imgui_impl_null: use relative filename + embed implementation directly by including the .cpp file. +// This is to simplify casual building of this example from all sorts of test scripts. +#include "../../backends/imgui_impl_null.h" +#include "../../backends/imgui_impl_null.cpp" + int main(int, char**) { IMGUI_CHECKVERSION(); + ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); - // Build atlas - unsigned char* tex_pixels = nullptr; - int tex_w, tex_h; - io.Fonts->GetTexDataAsRGBA32(&tex_pixels, &tex_w, &tex_h); + ImGui_ImplNullPlatform_Init(); + ImGui_ImplNullRender_Init(); for (int n = 0; n < 20; n++) { printf("NewFrame() %d\n", n); - io.DisplaySize = ImVec2(1920, 1080); - io.DeltaTime = 1.0f / 60.0f; + ImGui_ImplNullPlatform_NewFrame(); + ImGui_ImplNullRender_NewFrame(); ImGui::NewFrame(); static float f = 0.0f; @@ -32,6 +36,8 @@ int main(int, char**) } printf("DestroyContext()\n"); + ImGui_ImplNullRender_Shutdown(); + ImGui_ImplNullPlatform_Shutdown(); ImGui::DestroyContext(); return 0; } diff --git a/examples/example_sdl2_directx11/main.cpp b/examples/example_sdl2_directx11/main.cpp index 47c852d27..cf3971646 100644 --- a/examples/example_sdl2_directx11/main.cpp +++ b/examples/example_sdl2_directx11/main.cpp @@ -33,10 +33,13 @@ int main(int, char**) // Setup SDL // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems, // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to the latest version of SDL is recommended!) +#ifdef _WIN32 + ::SetProcessDPIAware(); +#endif if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { printf("Error: %s\n", SDL_GetError()); - return -1; + return 1; } // From 2.0.18: Enable native IME. @@ -45,12 +48,13 @@ int main(int, char**) #endif // Setup window + float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+DirectX11 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+DirectX11 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); if (window == nullptr) { printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); - return -1; + return 1; } SDL_SysWMinfo wmInfo; @@ -76,24 +80,31 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForD3D(window); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state @@ -201,6 +212,7 @@ int main(int, char**) bool CreateDeviceD3D(HWND hWnd) { // Setup swap chain + // This is a basic setup. Optimally could use e.g. DXGI_SWAP_EFFECT_FLIP_DISCARD and handle fullscreen mode differently. See #8979 for suggestions. DXGI_SWAP_CHAIN_DESC sd; ZeroMemory(&sd, sizeof(sd)); sd.BufferCount = 2; @@ -221,7 +233,10 @@ bool CreateDeviceD3D(HWND hWnd) //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; D3D_FEATURE_LEVEL featureLevel; const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, }; - if (D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext) != S_OK) + HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext); + if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available. + res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext); + if (res != S_OK) return false; CreateRenderTarget(); diff --git a/examples/example_sdl2_metal/main.mm b/examples/example_sdl2_metal/main.mm index d54812710..ea2afdab3 100644 --- a/examples/example_sdl2_metal/main.mm +++ b/examples/example_sdl2_metal/main.mm @@ -30,19 +30,21 @@ int main(int, char**) //ImGui::StyleColorsLight(); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Setup SDL @@ -51,7 +53,7 @@ int main(int, char**) if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { printf("Error: %s\n", SDL_GetError()); - return -1; + return 1; } // Inform SDL that we will be using metal for rendering. Without this hint initialization of metal renderer may fail. @@ -60,7 +62,7 @@ int main(int, char**) // Enable native IME. SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL+Metal example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL+Metal example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 800, SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); if (window == nullptr) { printf("Error creating window: %s\n", SDL_GetError()); diff --git a/examples/example_sdl2_opengl2/main.cpp b/examples/example_sdl2_opengl2/main.cpp index a70edd225..3b3412b14 100644 --- a/examples/example_sdl2_opengl2/main.cpp +++ b/examples/example_sdl2_opengl2/main.cpp @@ -17,15 +17,21 @@ #include #include #include +#ifdef _WIN32 +#include // SetProcessDPIAware() +#endif // Main code int main(int, char**) { // Setup SDL +#ifdef _WIN32 + ::SetProcessDPIAware(); +#endif if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { printf("Error: %s\n", SDL_GetError()); - return -1; + return 1; } // From 2.0.18: Enable native IME. @@ -39,12 +45,13 @@ int main(int, char**) SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); if (window == nullptr) { printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); - return -1; + return 1; } SDL_GLContext gl_context = SDL_GL_CreateContext(window); @@ -62,24 +69,31 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL2_Init(); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state diff --git a/examples/example_sdl2_opengl3/Makefile.emscripten b/examples/example_sdl2_opengl3/Makefile.emscripten index da0348435..bc06adeb7 100644 --- a/examples/example_sdl2_opengl3/Makefile.emscripten +++ b/examples/example_sdl2_opengl3/Makefile.emscripten @@ -36,6 +36,9 @@ EMS += -s USE_SDL=2 EMS += -s DISABLE_EXCEPTION_CATCHING=1 LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1 +# Build as single file (binary text encoded in .html file) +#LDFLAGS += -sSINGLE_FILE + # Uncomment next line to fix possible rendering bugs with Emscripten version older then 1.39.0 (https://github.com/ocornut/imgui/issues/2877) #EMS += -s BINARYEN_TRAP_MODE=clamp #EMS += -s SAFE_HEAP=1 ## Adds overhead diff --git a/examples/example_sdl2_opengl3/main.cpp b/examples/example_sdl2_opengl3/main.cpp index 2a4d7b9e7..0d992d47e 100644 --- a/examples/example_sdl2_opengl3/main.cpp +++ b/examples/example_sdl2_opengl3/main.cpp @@ -17,6 +17,9 @@ #else #include #endif +#ifdef _WIN32 +#include // SetProcessDPIAware() +#endif // This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details. #ifdef __EMSCRIPTEN__ @@ -27,20 +30,30 @@ int main(int, char**) { // Setup SDL +#ifdef _WIN32 + ::SetProcessDPIAware(); +#endif if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { printf("Error: %s\n", SDL_GetError()); - return -1; + return 1; } // Decide GL+GLSL versions #if defined(IMGUI_IMPL_OPENGL_ES2) - // GL ES 2.0 + GLSL 100 + // GL ES 2.0 + GLSL 100 (WebGL 1.0) const char* glsl_version = "#version 100"; SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#elif defined(IMGUI_IMPL_OPENGL_ES3) + // GL ES 3.0 + GLSL 300 es (WebGL 2.0) + const char* glsl_version = "#version 300 es"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); #elif defined(__APPLE__) // GL 3.2 Core + GLSL 150 const char* glsl_version = "#version 150"; @@ -66,15 +79,22 @@ int main(int, char**) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); if (window == nullptr) { printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); - return -1; + return 1; } SDL_GLContext gl_context = SDL_GL_CreateContext(window); + if (gl_context == nullptr) + { + printf("Error: SDL_GL_CreateContext(): %s\n", SDL_GetError()); + return 1; + } + SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SetSwapInterval(1); // Enable vsync @@ -89,25 +109,32 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state diff --git a/examples/example_sdl2_sdlrenderer2/main.cpp b/examples/example_sdl2_sdlrenderer2/main.cpp index 31fa3f9bf..b69867354 100644 --- a/examples/example_sdl2_sdlrenderer2/main.cpp +++ b/examples/example_sdl2_sdlrenderer2/main.cpp @@ -15,6 +15,9 @@ #include "imgui_impl_sdlrenderer2.h" #include #include +#ifdef _WIN32 +#include // SetProcessDPIAware() +#endif #if !SDL_VERSION_ATLEAST(2,0,17) #error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function @@ -24,10 +27,13 @@ int main(int, char**) { // Setup SDL +#ifdef _WIN32 + ::SetProcessDPIAware(); +#endif if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { printf("Error: %s\n", SDL_GetError()); - return -1; + return 1; } // From 2.0.18: Enable native IME. @@ -36,18 +42,19 @@ int main(int, char**) #endif // Create window with SDL_Renderer graphics context + float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+SDL_Renderer example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+SDL_Renderer example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); if (window == nullptr) { printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); - return -1; + return 1; } SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); if (renderer == nullptr) { SDL_Log("Error creating SDL_Renderer!"); - return -1; + return 1; } //SDL_RendererInfo info; //SDL_GetRendererInfo(renderer, &info); @@ -64,24 +71,31 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForSDLRenderer(window, renderer); ImGui_ImplSDLRenderer2_Init(renderer); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state diff --git a/examples/example_sdl2_vulkan/Makefile b/examples/example_sdl2_vulkan/Makefile new file mode 100644 index 000000000..e722ab035 --- /dev/null +++ b/examples/example_sdl2_vulkan/Makefile @@ -0,0 +1,80 @@ +# +# Cross Platform Makefile +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# +# You will need SDL2 (http://www.libsdl.org): +# Linux: +# apt-get install libsdl2-dev +# Mac OS X: +# brew install sdl2 +# MSYS2: +# pacman -S mingw-w64-i686-SDL2 +# + +#CXX = g++ +#CXX = clang++ + +EXE = example_sdl2_vulkan +IMGUI_DIR = ../.. +SOURCES = main.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl2.cpp $(IMGUI_DIR)/backends/imgui_impl_vulkan.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) + +CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +CXXFLAGS += -g -Wall -Wformat +LIBS = + +##--------------------------------------------------------------------- +## BUILD FLAGS PER PLATFORM +##--------------------------------------------------------------------- + +ifeq ($(UNAME_S), Linux) #LINUX + ECHO_MESSAGE = "Linux" + LIBS += -lGL -ldl + LIBS += `pkg-config --libs sdl2 vulkan` + CXXFLAGS += `pkg-config --cflags sdl2 vulkan` + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(UNAME_S), Darwin) #APPLE + ECHO_MESSAGE = "Mac OS X" + LIBS += -framework Cocoa -framework IOKit -framework CoreVideo + LIBS += `pkg-config --libs sdl2 vulkan` + LIBS += -L/usr/local/lib -L/opt/local/lib + + CXXFLAGS += `pkg-config --cflags sdl2 vulkan` + CXXFLAGS += -I/usr/local/include -I/opt/local/include + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(OS), Windows_NT) + ECHO_MESSAGE = "MinGW" + LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2 vulkan` + + CXXFLAGS += `pkg-config --cflags sdl2 vulkan` + CFLAGS = $(CXXFLAGS) +endif + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(ECHO_MESSAGE) + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) diff --git a/examples/example_sdl2_vulkan/main.cpp b/examples/example_sdl2_vulkan/main.cpp index cb116b76e..f39534393 100644 --- a/examples/example_sdl2_vulkan/main.cpp +++ b/examples/example_sdl2_vulkan/main.cpp @@ -20,6 +20,9 @@ #include // abort #include #include +#ifdef _WIN32 +#include // SetProcessDPIAware() +#endif // Volk headers #ifdef IMGUI_IMPL_VULKAN_USE_VOLK @@ -30,6 +33,7 @@ //#define APP_USE_UNLIMITED_FRAME_RATE #ifdef _DEBUG #define APP_USE_VULKAN_DEBUG_REPORT +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; #endif // Data @@ -39,7 +43,6 @@ static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; static uint32_t g_QueueFamily = (uint32_t)-1; static VkQueue g_Queue = VK_NULL_HANDLE; -static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; @@ -49,7 +52,7 @@ static bool g_SwapChainRebuild = false; static void check_vk_result(VkResult err) { - if (err == 0) + if (err == VK_SUCCESS) return; fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); if (err < 0) @@ -73,35 +76,6 @@ static bool IsExtensionAvailable(const ImVector& properti return false; } -static VkPhysicalDevice SetupVulkan_SelectPhysicalDevice() -{ - uint32_t gpu_count; - VkResult err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, nullptr); - check_vk_result(err); - IM_ASSERT(gpu_count > 0); - - ImVector gpus; - gpus.resize(gpu_count); - err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus.Data); - check_vk_result(err); - - // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers - // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple - // dedicated GPUs) is out of scope of this sample. - for (VkPhysicalDevice& device : gpus) - { - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(device, &properties); - if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) - return device; - } - - // Use first GPU (Integrated) is a Discrete one is not available. - if (gpu_count > 0) - return gpus[0]; - return VK_NULL_HANDLE; -} - static void SetupVulkan(ImVector instance_extensions) { VkResult err; @@ -165,23 +139,12 @@ static void SetupVulkan(ImVector instance_extensions) } // Select Physical Device (GPU) - g_PhysicalDevice = SetupVulkan_SelectPhysicalDevice(); + g_PhysicalDevice = ImGui_ImplVulkanH_SelectPhysicalDevice(g_Instance); + IM_ASSERT(g_PhysicalDevice != VK_NULL_HANDLE); // Select graphics queue family - { - uint32_t count; - vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, nullptr); - VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues); - for (uint32_t i = 0; i < count; i++) - if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) - { - g_QueueFamily = i; - break; - } - free(queues); - IM_ASSERT(g_QueueFamily != (uint32_t)-1); - } + g_QueueFamily = ImGui_ImplVulkanH_SelectQueueFamilyIndex(g_PhysicalDevice); + IM_ASSERT(g_QueueFamily != (uint32_t)-1); // Create Logical Device (with 1 queue) { @@ -217,18 +180,19 @@ static void SetupVulkan(ImVector instance_extensions) } // Create Descriptor Pool - // The example only requires a single combined image sampler descriptor for the font image and only uses one descriptor set (for that) - // If you wish to load e.g. additional textures you may need to alter pools sizes. + // If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets. { VkDescriptorPoolSize pool_sizes[] = { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE }, }; 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 = 1; - pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.maxSets = 0; + for (VkDescriptorPoolSize& pool_size : pool_sizes) + pool_info.maxSets += pool_size.descriptorCount; + pool_info.poolSizeCount = (uint32_t)IM_COUNTOF(pool_sizes); pool_info.pPoolSizes = pool_sizes; err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); check_vk_result(err); @@ -239,11 +203,9 @@ static void SetupVulkan(ImVector instance_extensions) // Your real engine/app may not use them. static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { - wd->Surface = surface; - // Check for WSI support VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -253,20 +215,21 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + wd->Surface = surface; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode -#ifdef APP_UNLIMITED_FRAME_RATE +#ifdef APP_USE_UNLIMITED_FRAME_RATE VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; #else VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; #endif - wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_COUNTOF(present_modes)); //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); } static void CleanupVulkan() @@ -283,24 +246,23 @@ static void CleanupVulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void CleanupVulkanWindow() +static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) { - ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator); + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); } static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkResult err; - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { @@ -369,11 +331,11 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores } @@ -381,10 +343,13 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) int main(int, char**) { // Setup SDL +#ifdef _WIN32 + ::SetProcessDPIAware(); +#endif if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { printf("Error: %s\n", SDL_GetError()); - return -1; + return 1; } // From 2.0.18: Enable native IME. @@ -393,12 +358,13 @@ int main(int, char**) #endif // Create window with Vulkan graphics context + float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+Vulkan example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+Vulkan example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); if (window == nullptr) { printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); - return -1; + return 1; } ImVector extensions; @@ -434,9 +400,15 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForVulkan(window); ImGui_ImplVulkan_InitInfo init_info = {}; + //init_info.ApiVersion = VK_API_VERSION_1_3; // Pass in your value of VkApplicationInfo::apiVersion, otherwise will default to header version. init_info.Instance = g_Instance; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; @@ -444,29 +416,31 @@ int main(int, char**) init_info.Queue = g_Queue; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; - init_info.RenderPass = wd->RenderPass; - init_info.Subpass = 0; init_info.MinImageCount = g_MinImageCount; init_info.ImageCount = wd->ImageCount; - init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; init_info.Allocator = g_Allocator; + init_info.PipelineInfoMain.RenderPass = wd->RenderPass; + init_info.PipelineInfoMain.Subpass = 0; + init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state @@ -504,7 +478,7 @@ int main(int, char**) if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) { ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0); g_MainWindowData.FrameIndex = 0; g_SwapChainRebuild = false; } @@ -573,7 +547,7 @@ int main(int, char**) ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); - CleanupVulkanWindow(); + CleanupVulkanWindow(&g_MainWindowData); CleanupVulkan(); SDL_DestroyWindow(window); diff --git a/examples/example_sdl2_wgpu/CMakeLists.txt b/examples/example_sdl2_wgpu/CMakeLists.txt new file mode 100644 index 000000000..3bed79fd4 --- /dev/null +++ b/examples/example_sdl2_wgpu/CMakeLists.txt @@ -0,0 +1,214 @@ +# Building for desktop with Dawn: +# 1. git clone https://github.com/google/dawn dawn +# 2. cmake -B build -DIMGUI_DAWN_DIR=dawn +# 3. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_sdl2_wgpu[.exe] or build/Debug/example_sdl2_wgpu[.exe] + +# Building for desktop with WGPU-Native: +# 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases +# 2. unzip the downloaded file in your_preferred_folder +# 3. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory) +# 4. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_sdl2_wgpu[.exe] or build/Debug/example_sdl2_wgpu[.exe] + +# Building for desktop with WGVK (MUCH EASIER) +# 1. git clone https://github.com/manuel5975p/WGVK dawn +# 2. cmake -B build -DIMGUI_WGVK_DIR=wgvk +# 3. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_sdl2_wgpu[.exe] or build/Debug/example_sdl2_wgpu[.exe] + +# Building for Emscripten: +# 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html +# 2. Install Ninja build system +# 3. emcmake cmake -G Ninja -B build +# (optional) -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py", see ReadMe.md +# 3. cmake --build build +# 4. emrun build/index.html + +cmake_minimum_required(VERSION 3.22) # Dawn requires CMake >= 3.22 +project(imgui_example_sdl2_wgpu C CXX) + +set(IMGUI_EXECUTABLE example_sdl2_wgpu) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) +endif() + +set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17 + +# Dear ImGui +set(IMGUI_DIR ../../) + +# ImGui example commons source files +set(IMGUI_EXAMPLE_SOURCE_FILES + main.cpp + # backend files + ${IMGUI_DIR}/backends/imgui_impl_sdl2.cpp + ${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp + # Dear ImGui files + ${IMGUI_DIR}/imgui.cpp + ${IMGUI_DIR}/imgui_draw.cpp + ${IMGUI_DIR}/imgui_demo.cpp + ${IMGUI_DIR}/imgui_tables.cpp + ${IMGUI_DIR}/imgui_widgets.cpp +) + +if(EMSCRIPTEN) + if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "4.0.10") + set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "--use-port=emdawnwebgpu" CACHE STRING "Default to --use-port=emdawnwebgpu. You can override to provide your own local port.") + else() + message(FATAL_ERROR "emdawnwebgpu needs EMSCRIPTEN version >= 4.0.10") + endif() + + add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) +else() # Native/Desktop build + + # Check DAWN/WGPU/WGVK directory + # If it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR or IMGUI_WGVK_DIR must be specified + if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR AND NOT IMGUI_WGVK_DIR) + message(FATAL_ERROR "Please specify one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") + endif() + if((IMGUI_DAWN_DIR AND (IMGUI_WGPU_DIR OR IMGUI_WGVK_DIR)) OR (IMGUI_WGPU_DIR AND IMGUI_WGVK_DIR)) + message(FATAL_ERROR "Please specify only one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") + endif() + + if(APPLE) # Add SDL2 module to get Surface, with libs and file property for MacOS build + set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++") + set(OS_LIBRARIES "-framework CoreFoundation -framework QuartzCore -framework Metal -framework MetalKit -framework Cocoa") + endif() + + find_package(SDL2 REQUIRED) # SDL_MAIN_HANDLED + + if(IMGUI_DAWN_DIR) + list(APPEND CMAKE_PREFIX_PATH ${IMGUI_DAWN_DIR}) + find_package(Threads) # required from Dawn installation + find_package(Dawn) # Search for a Dawn installation using IMGUI_DAWN_DIR in CMAKE_PREFIX_PATH + if(Dawn_FOUND) + message("Dawn Installation has been found!") + set(LIBRARIES dawn::webgpu_dawn ${OS_LIBRARIES}) + else() + set(IMGUI_DAWN_DIR CACHE PATH "Path to Dawn repository") + + option(DAWN_USE_GLFW OFF) # disable builtin GLFW in DAWN when we use SDL2 / SDL3 + + option(DAWN_FETCH_DEPENDENCIES "Use fetch_dawn_dependencies.py as an alternative to using depot_tools" ON) + set(DAWN_BUILD_MONOLITHIC_LIBRARY "STATIC" CACHE STRING "Build monolithic library: SHARED, STATIC, or OFF.") + + # Dawn builds many things by default - disable things we don't need + option(DAWN_BUILD_SAMPLES "Enables building Dawn's samples" OFF) + option(TINT_BUILD_CMD_TOOLS "Build the Tint command line tools" OFF) + option(TINT_BUILD_DOCS "Build documentation" OFF) + option(TINT_BUILD_TESTS "Build tests" OFF) + if(NOT APPLE) + option(TINT_BUILD_MSL_WRITER "Build the MSL output writer" OFF) + endif() + if(WIN32) + option(DAWN_FORCE_SYSTEM_COMPONENT_LOAD "Allow system component fallback" ON) + option(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" OFF) + option(TINT_BUILD_WGSL_READER "Build the WGSL input reader" ON) + option(TINT_BUILD_GLSL_WRITER "Build the GLSL output writer" OFF) + option(TINT_BUILD_GLSL_VALIDATOR "Build the GLSL output validator" OFF) + option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ON) + option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON) + endif() + # check if WAYLAND is the current Session Type and enable DAWN_USE_WAYLAND Wayland option @compile time + # You can override this using: cmake -DDAWN_USE_WAYLAND=X (X = ON | OFF) + if(LINUX) + if($ENV{XDG_SESSION_TYPE} MATCHES wayland) + option(DAWN_USE_WAYLAND "Enable support for Wayland surface" ON) + endif() + endif() + + add_subdirectory("${IMGUI_DAWN_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/dawn" EXCLUDE_FROM_ALL) + + set(LIBRARIES webgpu_dawn ${OS_LIBRARIES}) + endif() + endif() + + if(IMGUI_WGPU_DIR) + set(WGPU_NATIVE_LIB_DIR ${IMGUI_WGPU_DIR}/lib) + find_library(WGPU_LIBRARY NAMES libwgpu_native.a wgpu_native.lib wgpu_native HINTS ${WGPU_NATIVE_LIB_DIR} REQUIRED) + if(WIN32) + set(OS_LIBRARIES d3dcompiler ws2_32 userenv bcrypt ntdll opengl32 Propsys RuntimeObject) + elseif(UNIX AND NOT APPLE) + set(OS_LIBRARIES "-lm -ldl") + endif() + + set(LIBRARIES ${WGPU_LIBRARY} ${OS_LIBRARIES}) + endif() + + if(IMGUI_WGVK_DIR) + endif() + +endif() + +add_executable(${IMGUI_EXECUTABLE} ${IMGUI_EXAMPLE_SOURCE_FILES}) + +target_include_directories(${IMGUI_EXECUTABLE} PUBLIC + ${IMGUI_DIR} + ${IMGUI_DIR}/backends + ${SDL2_INCLUDE_DIRS} +) + +target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_EXAMPLE_SDL2_WGPU") + +# compiler option only for IMGUI_EXAMPLE_SOURCE_FILES +if (MSVC) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /W4) # warning level 4 +else() + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC -Wall) # -Wextra -Wpedantic +endif() + +# IMGUI_IMPL_WEBGPU_BACKEND_DAWN/WGPU internal define is set according to: +# EMSCRIPTEN: by used FLAG +# --use-port=emdawnwebgpu --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN enabled (+EMSCRIPTEN) +# NATIVE: by used SDK installation directory +# if IMGUI_DAWN_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN enabled +# if IMGUI_WGPU_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU enabled + +if(NOT EMSCRIPTEN) # WegGPU-Native settings + if(IMGUI_DAWN_DIR) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN") + if(NOT Dawn_FOUND) + target_link_libraries(${IMGUI_EXECUTABLE} INTERFACE webgpu_cpp) + endif() + endif() + if(IMGUI_WGPU_DIR) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU") + target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGPU_DIR}/include) + endif() + if(IMGUI_WGVK_DIR) + target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) + if (MSVC) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) + endif() + endif() + + target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${LIBRARIES} ${SDL2_LIBRARIES}) +else() # Emscripten settings + set(CMAKE_EXECUTABLE_SUFFIX ".html") + + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}") + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN") + message(STATUS "Using ${IMGUI_EMSCRIPTEN_WEBGPU_FLAG} WebGPU implementation") + + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "-sUSE_SDL=2") + target_link_options(${IMGUI_EXECUTABLE} PRIVATE + "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}" + "-sUSE_SDL=2" + "-sWASM=1" + "-sASYNCIFY=1" + "-sALLOW_MEMORY_GROWTH=1" + "-sNO_EXIT_RUNTIME=0" + "-sASSERTIONS=1" + "-sDISABLE_EXCEPTION_CATCHING=1" + "-sNO_FILESYSTEM=1" + "--shell-file=${CMAKE_CURRENT_LIST_DIR}/../libs/emscripten/shell_minimal.html" + ) + set_target_properties(${IMGUI_EXECUTABLE} PROPERTIES OUTPUT_NAME "index") +endif() diff --git a/examples/example_sdl2_wgpu/Makefile.emscripten b/examples/example_sdl2_wgpu/Makefile.emscripten new file mode 100644 index 000000000..ebc7f97c9 --- /dev/null +++ b/examples/example_sdl2_wgpu/Makefile.emscripten @@ -0,0 +1,99 @@ +# +# Makefile to use with emscripten +# See https://emscripten.org/docs/getting_started/downloads.html +# for installation instructions. +# +# This Makefile assumes you have loaded emscripten's environment. +# (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead) +# +# Running `make -f Makefile.emscripten` will produce three files: +# - web/index.html +# - web/index.js +# - web/index.wasm +# +# All three are needed to run the demo. + +CC = emcc +CXX = em++ +WEB_DIR = web +EXE = $(WEB_DIR)/index.html +IMGUI_DIR = ../.. +SOURCES = main.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl2.cpp $(IMGUI_DIR)/backends/imgui_impl_wgpu.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) +CPPFLAGS = +LDFLAGS = +EMS = + +##--------------------------------------------------------------------- +## EMSCRIPTEN OPTIONS +##--------------------------------------------------------------------- + +# ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only) +EMS += -s USE_SDL=2 +EMS += -s DISABLE_EXCEPTION_CATCHING=1 +LDFLAGS += -s WASM=1 +LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 +LDFLAGS += -s ASYNCIFY=1 +LDFLAGS += -s NO_EXIT_RUNTIME=0 +LDFLAGS += -s ASSERTIONS=1 + +# Using Dawn-based WebGPU port (requires Emscripten >= 4.0.10) +EMS += --use-port=emdawnwebgpu +LDFLAGS += --use-port=emdawnwebgpu + +# Build as single file (binary text encoded in .html file) +#LDFLAGS += -sSINGLE_FILE + +# Emscripten allows preloading a file or folder to be accessible at runtime. +# The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts" +# See documentation for more details: https://emscripten.org/docs/porting/files/packaging_files.html +# (Default value is 0. Set to 1 to enable file-system and include the misc/fonts/ folder as part of the build.) +USE_FILE_SYSTEM ?= 0 +ifeq ($(USE_FILE_SYSTEM), 0) +LDFLAGS += -s NO_FILESYSTEM=1 +CPPFLAGS += -DIMGUI_DISABLE_FILE_FUNCTIONS +endif +ifeq ($(USE_FILE_SYSTEM), 1) +LDFLAGS += --no-heap-copy --preload-file ../../misc/fonts@/fonts +endif + +##--------------------------------------------------------------------- +## FINAL BUILD FLAGS +##--------------------------------------------------------------------- + +CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +#CPPFLAGS += -g +CPPFLAGS += -Wall -Wformat -Os $(EMS) +LDFLAGS += --shell-file ../libs/emscripten/shell_minimal.html +LDFLAGS += $(EMS) + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(EXE) + +$(WEB_DIR): + mkdir $@ + +serve: all + python3 -m http.server -d $(WEB_DIR) + +$(EXE): $(OBJS) $(WEB_DIR) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +clean: + rm -f $(EXE) $(OBJS) $(WEB_DIR)/*.js $(WEB_DIR)/*.wasm $(WEB_DIR)/*.wasm.pre diff --git a/examples/example_sdl2_wgpu/README.md b/examples/example_sdl2_wgpu/README.md new file mode 100644 index 000000000..3a0f2a630 --- /dev/null +++ b/examples/example_sdl2_wgpu/README.md @@ -0,0 +1,163 @@ +## How to Build + + +--- + +### Using CMake +#### Building for desktop (WebGPU-native) with Google Dawn: + 1. `git clone https://github.com/google/dawn dawn` + 2. `cmake -B build -DIMGUI_DAWN_DIR=dawn` + 3. `cmake --build build` +The resulting binary will be found at one of the following locations: + * build/Debug/example_sdl2_wgpu[.exe] + * build/example_sdl2_wgpu[.exe] + +#### Building for desktop (WebGPU-Native) with WGPU: + 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases + 2. unzip the downloaded file in `your_preferred_folder` + 3. `cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder` ("full path" or "relative" starting from current directory) + 4. `cmake --build build` +The resulting binary will be found at one of the following locations: + * build/Debug/example_sdl2_wgpu[.exe] + * build/example_sdl2_wgpu[.exe] + +#### Building for Emscripten: + 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html + 2. Install Ninja build system + 3. `emcmake cmake -G Ninja -B build` + - (optional) `-DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py"`, see below + 4. `cmake --build build` + +#### Sync Emscripten with latest Google Dawn: +If you want to sync Emscripten with latest DAWN release it's necessary to download the `port-emdawnwgpu-package` (released daily by Google) here: +https://github.com/google/dawn/releases +Unpack it in your preferred folder and to replace the step 3 with: + +3. `emcmake cmake -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py" -G Ninja -B build` + +**N.B.** +For the WASM code produced by Emscripten to work correctly, it will also be necessary to have the "corresponding" (or newer) version of Google Canary (nightly build for developers) that includes the latest changes + + +--- + +### CMake by step + +#### Generate Dawn Native: + +- `cmake -G Ninja -DIMGUI_DAWN_DIR=path_to_sdk_dir -B where_to_build_dir` + - Using `IMGUI_DAWN_DIR` set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define + +#### Generate WGPU Native: + +- `cmake -G Ninja -DIMGUI_WGPU_DIR=path_to_sdk_dir -B where_to_build_dir` + + - Using `IMGUI_WGPU_DIR` set `IMGUI_IMPL_WEBGPU_BACKEND_WGPU` compiler define + +#### Generate Emscripten: + +- `emcmake cmake -G Ninja -B where_to_build_dir`\ +CMake checks the EMSCRIPEN version then: + - if EMS >= 4.0.10 uses `--use-port=emdawnwebgpu` flag to build + - it set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define + - if EMS < 4.0.10 the build aborts (`-sUSE_WEBGPU=1` is no longer supported by our examples and our WGPU backend) + +#### Generate Emscripten using external WebGPU library (emdawnwebgpu_pkg) + +- `emcmake cmake -G Ninja -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path_to_emdawnwebgpu_pkg" -B where_to_build_dir` + - it set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define + - *To use external WebGPU library it's necessary to have EMS >= 4.0.10 or the minimum requirements specified by the package:* + - https://github.com/google/dawn/releases + +#### Build time + +Once the procedure for the specific builder is generated, the build command is **always the same**: + +- Build using CMake + - `cmake --build where_to_build_dir` + - It will use selected builder to build the example. + +- Build explicitly: + - `cd where_to_build_dir` + - `ninja` + - This is the builder chosen during the generation phase + +--- + +### CMake useful options +#### Generator types (alternative to **ninja** builder): +- `-G Ninja` to build with __ninja__ builder +- `-G "Unix Makefiles"` to build with __make__ builder +- `-G "Visual Studio 17 2022" -A x64` to create a VS 2022 solution (.sln) file, Windows only + - **Native build only** + - Not **officially** supported to build Google Dawn + +Example: +- using **make** instead **ninja**: + - `cmake -G "Unix Makefiles" -DIMGUI_DAWN_DIR=path_to_sdk_dir -B where_to_build_dir` + +**Syntax is case sensitive and the "" are necessary in case of spaces between words* + +#### Directories +- The directory path can be absolute or relative (starting from the current directory) +- It's necessary to use different `where_to_build_dir` for different CMake generations + + +#### Build type +The default build type is **Debug** +It is possible to use a different build type using: +- `-DCMAKE_BUILD_TYPE=Release` +- `-DCMAKE_BUILD_TYPE=MinSizeRel` +- `-DCMAKE_BUILD_TYPE=RelWithDebInfo` + +Example: +- building **Release**: + - `cmake -G ninja -DIMGUI_WGPU_DIR=path_to_sdk_dir -DCMAKE_BUILD_TYPE=Release -B where_to_build_dir ` + +#### GLFW / SDL2 / SDL3 includes, libraries, search paths and package manager + +Includes and libraries, by default, are searched in system/compiler paths (environment variables): you can add the path to your development tools to the environment variables without having to modify the `CMakeLists.txt` file. +- e.g. CLang search in path specified from the following environment variables: + - include files: CPATH, C_INCLUDE_PATH, CPLUS_INCLUDE_PATH + - library files: LIBRARY_PATH + +If you are using a package manager (**vcpkg** / **conan** / ... ) you need/can to specify it, adding to cmake command: +- `-DCMAKE_TOOLCHAIN_FILE=path/to/package_manager.cmake` + +Examples: + +- using **vcpkg** package manager it's necessary adding: + - `-DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake` + +- full cmake command using **vcpkg** package manager: + - `cmake -G Ninja -DIMGUI_DAWN_DIR=path_to_sdk_dir -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake -B where_to_build_dir` + +--- + + +### Using makefile + +- You need to install Emscripten from https://emscripten.org/docs/getting_started/downloads.html, and have the environment variables set, as described in https://emscripten.org/docs/getting_started/downloads.html#installation-instructions + +- Depending on your configuration, in Windows you may need to run `emsdk/emsdk_env.bat` in your console to access the Emscripten command-line tools. + +- You may also refer to our [Continuous Integration setup](https://github.com/ocornut/imgui/tree/master/.github/workflows) for Emscripten setup. + +- Then build using `make -f Makefile.emscripten` while in the `example_glfw_wgpu/` directory. + +- Requires recent Emscripten as WGPU is still a work-in-progress API. + +--- + +## How to Run + +To run on a local machine: +- Make sure your browse supports WGPU and it is enabled. WGPU is still WIP not enabled by default in most browser. +- `make serve` will use Python3 to spawn a local webserver, you can then browse http://localhost:8000 to access your build. +- Otherwise, generally you will need a local webserver: + - Quoting [https://emscripten.org/docs/getting_started](https://emscripten.org/docs/getting_started/Tutorial.html#generating-html):
+_"Unfortunately several browsers (including Chrome, Safari, and Internet Explorer) do not support file:// [XHR](https://emscripten.org/docs/site/glossary.html#term-xhr) requests, and can’t load extra files needed by the HTML (like a .wasm file, or packaged file data as mentioned lower down). For these browsers you’ll need to serve the files using a [local webserver](https://emscripten.org/docs/getting_started/FAQ.html#faq-local-webserver) and then open http://localhost:8000/hello.html."_ + - Emscripten SDK has a handy `emrun` command: `emrun web/example_glfw_wgpu.html --browser firefox` which will spawn a temporary local webserver (in Firefox). See https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html for details. + - You may use Python 3 builtin webserver: `python -m http.server -d web` (this is what `make serve` uses). + - You may use Python 2 builtin webserver: `cd web && python -m SimpleHTTPServer`. + - If you are accessing the files over a network, certain browsers, such as Firefox, will restrict Gamepad API access to secure contexts only (e.g. https only). diff --git a/examples/example_sdl2_wgpu/main.cpp b/examples/example_sdl2_wgpu/main.cpp new file mode 100644 index 000000000..6898d03cd --- /dev/null +++ b/examples/example_sdl2_wgpu/main.cpp @@ -0,0 +1,526 @@ +// Dear ImGui: standalone example application for using SDL2 + WebGPU +// - Emscripten is supported for publishing on web. See https://emscripten.org. +// - Dawn is used as a WebGPU implementation on desktop. + +// 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 + +#include "imgui.h" +#include "imgui_impl_sdl2.h" +#include "imgui_impl_wgpu.h" +#include +#include + +// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details. +#ifdef __EMSCRIPTEN__ +#include +#include +#include "../libs/emscripten/emscripten_mainloop_stub.h" +#endif + +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) +#include +#endif + +// Data +static WGPUInstance wgpu_instance = nullptr; +static WGPUDevice wgpu_device = nullptr; +static WGPUSurface wgpu_surface = nullptr; +static WGPUQueue wgpu_queue = nullptr; +static WGPUSurfaceConfiguration wgpu_surface_configuration = {}; +static int wgpu_surface_width = 1280; +static int wgpu_surface_height = 800; + +// Forward declarations +static bool InitWGPU(SDL_Window* window); +WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, SDL_Window* window); + +static void ResizeSurface(int width, int height) +{ + wgpu_surface_configuration.width = wgpu_surface_width = width; + wgpu_surface_configuration.height = wgpu_surface_height = height; + wgpuSurfaceConfigure(wgpu_surface, (WGPUSurfaceConfiguration*)&wgpu_surface_configuration); +} + +// Main code +int main(int, char**) +{ + // Setup SDL + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER); + + // Create window with graphics context + float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); + wgpu_surface_width = (int)(wgpu_surface_width * main_scale); + wgpu_surface_height = (int)(wgpu_surface_height * main_scale); + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+WebGPU example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, wgpu_surface_width, wgpu_surface_height, window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return 1; + } + + // Initialize WGPU + InitWGPU(window); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + + // Setup Platform/Renderer backends + ImGui_ImplSDL2_InitForOther(window); + ImGui_ImplWGPU_InitInfo init_info; + init_info.Device = wgpu_device; + init_info.NumFramesInFlight = 3; + init_info.RenderTargetFormat = wgpu_surface_configuration.format; + init_info.DepthStencilFormat = WGPUTextureFormat_Undefined; + ImGui_ImplWGPU_Init(&init_info); + + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); +#ifndef IMGUI_DISABLE_FILE_FUNCTIONS + //io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf"); + //IM_ASSERT(font != nullptr); +#endif + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; +#ifdef __EMSCRIPTEN__ + // For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file. + // You may manually call LoadIniSettingsFromMemory() to load settings from your own storage. + io.IniFilename = nullptr; + EMSCRIPTEN_MAINLOOP_BEGIN +#else + while (!done) +#endif + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL2_ProcessEvent(&event); + if (event.type == SDL_QUIT) + done = true; + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) + done = true; + } + + // React to changes in screen size + int width, height; + SDL_GetWindowSizeInPixels(window, &width, &height); + if (width != wgpu_surface_width || height != wgpu_surface_height) + ResizeSurface(width, height); + + // Check surface status for error. If texture is not optimal, try to reconfigure the surface. + WGPUSurfaceTexture surface_texture; + wgpuSurfaceGetCurrentTexture(wgpu_surface, &surface_texture); + if (ImGui_ImplWGPU_IsSurfaceStatusError(surface_texture.status)) + { + fprintf(stderr, "Unrecoverable Surface Texture status=%#.8x\n", surface_texture.status); + abort(); + } + if (ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(surface_texture.status)) + { + if (surface_texture.texture) + wgpuTextureRelease(surface_texture.texture); + if (width > 0 && height > 0) + ResizeSurface(width, height); + continue; + } + + // Start the Dear ImGui frame + ImGui_ImplWGPU_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + + WGPUTextureViewDescriptor view_desc = {}; + view_desc.format = wgpu_surface_configuration.format; + view_desc.dimension = WGPUTextureViewDimension_2D; + view_desc.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED; + view_desc.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED; + view_desc.aspect = WGPUTextureAspect_All; + + WGPUTextureView texture_view = wgpuTextureCreateView(surface_texture.texture, &view_desc); + + WGPURenderPassColorAttachment color_attachments = {}; + color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; + color_attachments.loadOp = WGPULoadOp_Clear; + color_attachments.storeOp = WGPUStoreOp_Store; + color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w }; + color_attachments.view = texture_view; + + WGPURenderPassDescriptor render_pass_desc = {}; + render_pass_desc.colorAttachmentCount = 1; + render_pass_desc.colorAttachments = &color_attachments; + render_pass_desc.depthStencilAttachment = nullptr; + + WGPUCommandEncoderDescriptor enc_desc = {}; + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(wgpu_device, &enc_desc); + + WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc); + ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass); + wgpuRenderPassEncoderEnd(pass); + + WGPUCommandBufferDescriptor cmd_buffer_desc = {}; + WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc); + wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer); + +#ifndef __EMSCRIPTEN__ + wgpuSurfacePresent(wgpu_surface); + // Tick needs to be called in Dawn to display validation errors +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) + wgpuDeviceTick(wgpu_device); +#endif +#endif + wgpuTextureViewRelease(texture_view); + wgpuRenderPassEncoderRelease(pass); + wgpuCommandEncoderRelease(encoder); + wgpuCommandBufferRelease(cmd_buffer); + } +#ifdef __EMSCRIPTEN__ + EMSCRIPTEN_MAINLOOP_END; +#endif + + // Cleanup + ImGui_ImplWGPU_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + + wgpuSurfaceUnconfigure(wgpu_surface); + wgpuSurfaceRelease(wgpu_surface); + wgpuQueueRelease(wgpu_queue); + wgpuDeviceRelease(wgpu_device); + wgpuInstanceRelease(wgpu_instance); + + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} + +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) +static WGPUAdapter RequestAdapter(wgpu::Instance& instance) +{ + wgpu::Adapter acquired_adapter; + wgpu::RequestAdapterOptions adapter_options; + auto onRequestAdapter = [&](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message) + { + if (status != wgpu::RequestAdapterStatus::Success) + { + printf("Failed to get an adapter: %s\n", message.data); + return; + } + acquired_adapter = std::move(adapter); + }; + + // Synchronously (wait until) acquire Adapter + wgpu::Future waitAdapterFunc { instance.RequestAdapter(&adapter_options, wgpu::CallbackMode::WaitAnyOnly, onRequestAdapter) }; + wgpu::WaitStatus waitStatusAdapter = instance.WaitAny(waitAdapterFunc, UINT64_MAX); + IM_ASSERT(acquired_adapter != nullptr && waitStatusAdapter == wgpu::WaitStatus::Success && "Error on Adapter request"); + return acquired_adapter.MoveToCHandle(); +} + +static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter) +{ + // Set device callback functions + wgpu::DeviceDescriptor device_desc; + device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, + [](const wgpu::Device&, wgpu::DeviceLostReason type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetDeviceLostReasonName((WGPUDeviceLostReason)type), msg.data); } + ); + device_desc.SetUncapturedErrorCallback( + [](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetErrorTypeName((WGPUErrorType)type), msg.data); } + ); + + wgpu::Device acquired_device; + auto onRequestDevice = [&](wgpu::RequestDeviceStatus status, wgpu::Device local_device, wgpu::StringView message) + { + if (status != wgpu::RequestDeviceStatus::Success) + { + printf("Failed to get an device: %s\n", message.data); + return; + } + acquired_device = std::move(local_device); + }; + + // Synchronously (wait until) get Device + wgpu::Future waitDeviceFunc { adapter.RequestDevice(&device_desc, wgpu::CallbackMode::WaitAnyOnly, onRequestDevice) }; + wgpu::WaitStatus waitStatusDevice = instance.WaitAny(waitDeviceFunc, UINT64_MAX); + IM_ASSERT(acquired_device != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request"); + return acquired_device.MoveToCHandle(); +} +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) +static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2) +{ + IM_UNUSED(userdata2); + if (status == WGPURequestAdapterStatus_Success) + { + WGPUAdapter* extAdapter = (WGPUAdapter*)userdata1; + *extAdapter = adapter; + } + else + { + printf("Request_adapter status=%#.8x message=%.*s\n", status, (int)message.length, message.data); + } +} + +static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata1, void* userdata2) +{ + IM_UNUSED(userdata2); + if (status == WGPURequestDeviceStatus_Success) + { + WGPUDevice* extDevice = (WGPUDevice*)userdata1; + *extDevice = device; + } + else + { + printf("Request_device status=%#.8x message=%.*s\n", status, (int)message.length, message.data); + } +} + +static WGPUAdapter RequestAdapter(WGPUInstance& instance) +{ + WGPURequestAdapterOptions adapter_options = {}; + + WGPUAdapter local_adapter = nullptr; + WGPURequestAdapterCallbackInfo adapterCallbackInfo = {}; + adapterCallbackInfo.mode = WGPUCallbackMode_WaitAnyOnly; + adapterCallbackInfo.callback = handle_request_adapter; + adapterCallbackInfo.userdata1 = &local_adapter; + + WGPUFuture future = wgpuInstanceRequestAdapter(instance, &adapter_options, adapterCallbackInfo); + WGPUFutureWaitInfo waitInfo = { future, false }; + wgpuInstanceWaitAny(instance, 1, &waitInfo, ~0ull); + IM_ASSERT(local_adapter && "Error on Adapter request"); + return local_adapter; +} + +static WGPUDevice RequestDevice(WGPUInstance& instance, WGPUAdapter& adapter) +{ + WGPUDevice local_device = nullptr; + WGPURequestDeviceCallbackInfo deviceCallbackInfo = {}; + deviceCallbackInfo.mode = WGPUCallbackMode_WaitAnyOnly; + deviceCallbackInfo.callback = handle_request_device; + deviceCallbackInfo.userdata1 = &local_device; + WGPUFuture future = wgpuAdapterRequestDevice(adapter, nullptr, deviceCallbackInfo); + WGPUFutureWaitInfo waitInfo = { future, false }; + wgpuInstanceWaitAny(instance, 1, &waitInfo, ~0ull); + IM_ASSERT(local_device && "Error on Device request"); + return local_device; +} +#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU + +static bool InitWGPU(SDL_Window* window) +{ + WGPUTextureFormat preferred_fmt = WGPUTextureFormat_Undefined; // acquired from SurfaceCapabilities + + // Google DAWN backend: Adapter and Device acquisition, Surface creation +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) + wgpu::InstanceDescriptor instance_desc = {}; + static constexpr wgpu::InstanceFeatureName timedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny; + instance_desc.requiredFeatureCount = 1; + instance_desc.requiredFeatures = &timedWaitAny; + wgpu::Instance instance = wgpu::CreateInstance(&instance_desc); + + wgpu::Adapter adapter = RequestAdapter(instance); + ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter.Get()); + + wgpu_device = RequestDevice(instance, adapter); + + // Create the surface. +#ifdef __EMSCRIPTEN__ + wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvas_desc = {}; + canvas_desc.selector = "#canvas"; + + wgpu::SurfaceDescriptor surface_desc = {}; + surface_desc.nextInChain = &canvas_desc; + wgpu::Surface surface = instance.CreateSurface(&surface_desc); +#else + wgpu::Surface surface = CreateWGPUSurface(instance.Get(), window); +#endif + if (!surface) + return false; + + // Moving Dawn objects into WGPU handles + wgpu_instance = instance.MoveToCHandle(); + wgpu_surface = surface.MoveToCHandle(); + + WGPUSurfaceCapabilities surface_capabilities = {}; + wgpuSurfaceGetCapabilities(wgpu_surface, adapter.Get(), &surface_capabilities); + + preferred_fmt = surface_capabilities.formats[0]; + + // WGPU backend: Adapter and Device acquisition, Surface creation +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) + WGPUInstanceDescriptor instanceDesc = {}; + WGPUInstanceFeatureName timedWaitAny = WGPUInstanceFeatureName_TimedWaitAny; + instanceDesc.requiredFeatureCount = 1; + instanceDesc.requiredFeatures = &timedWaitAny; + wgpu_instance = wgpuCreateInstance(&instanceDesc); + +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) + wgpuSetLogCallback( + [](WGPULogLevel level, WGPUStringView msg, void* userdata) { fprintf(stderr, "%s: %.*s\n", ImGui_ImplWGPU_GetLogLevelName(level), (int)msg.length, msg.data); }, nullptr + ); + wgpuSetLogLevel(WGPULogLevel_Warn); +#endif + + WGPUAdapter adapter = RequestAdapter(wgpu_instance); + ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter); + + wgpu_device = RequestDevice(wgpu_instance, adapter); + + // Create the surface. + wgpu_surface = CreateWGPUSurface(wgpu_instance, window); + if (!wgpu_surface) + return false; + + WGPUSurfaceCapabilities surface_capabilities = {}; + wgpuSurfaceGetCapabilities(wgpu_surface, adapter, &surface_capabilities); + + preferred_fmt = surface_capabilities.formats[0]; +#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU + + wgpu_surface_configuration.presentMode = WGPUPresentMode_Fifo; + wgpu_surface_configuration.alphaMode = WGPUCompositeAlphaMode_Auto; + wgpu_surface_configuration.usage = WGPUTextureUsage_RenderAttachment; + wgpu_surface_configuration.width = wgpu_surface_width; + wgpu_surface_configuration.height = wgpu_surface_height; + wgpu_surface_configuration.device = wgpu_device; + wgpu_surface_configuration.format = preferred_fmt; + + wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration); + wgpu_queue = wgpuDeviceGetQueue(wgpu_device); + + return true; +} + +// SDL2 helper to create a WebGPU surface (exclusively!) for Native/Desktop applications: available only together with WebGPU/WGPU backend +// As of today (2025/10/31) there is no "official" support in SDL2 to create a surface for WebGPU backend. +// This stub uses "low level" SDL2 calls to acquire information from a specific Window Manager. +// Currently supported platforms: Windows / Linux (X11 and Wayland) / MacOS. Not necessary nor available with EMSCRIPTEN. +#ifndef __EMSCRIPTEN__ + +#include +#undef Status // X11 headers are leaking this and also 'Success', 'Always', 'None', all used in DAWN api. Add #undef if necessary. + +WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, SDL_Window* window) +{ + SDL_SysWMinfo sysWMInfo; + SDL_VERSION(&sysWMInfo.version); + SDL_GetWindowWMInfo(window, &sysWMInfo); + + ImGui_ImplWGPU_CreateSurfaceInfo create_info = {}; + create_info.Instance = instance; +#if defined(SDL_VIDEO_DRIVER_COCOA) + { + create_info.System = "cocoa"; + create_info.RawWindow = (void*)sysWMInfo.info.cocoa.window; + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#elif defined(SDL_VIDEO_DRIVER_WAYLAND) || defined(SDL_VIDEO_DRIVER_X11) + const char* sdl_driver = SDL_GetCurrentVideoDriver(); + if (sdl_driver && strcmp(sdl_driver, "wayland") == 0) + { + create_info.System = "wayland"; + create_info.RawDisplay = (void*)sysWMInfo.info.wl.display; + create_info.RawSurface = (void*)sysWMInfo.info.wl.surface; + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } + else + { + create_info.System = "x11"; + create_info.RawWindow = (void*)sysWMInfo.info.x11.window; + create_info.RawDisplay = (void*)sysWMInfo.info.x11.display; + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#elif defined(SDL_VIDEO_DRIVER_WINDOWS) + { + create_info.System = "win32"; + create_info.RawWindow = (void*)sysWMInfo.info.win.window; + create_info.RawInstance = (void*)sysWMInfo.info.win.hinstance; + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#else +#error "Unsupported WebGPU native platform!" + return nullptr; +#endif +} +#endif // #ifndef __EMSCRIPTEN__ diff --git a/examples/example_sdl3_directx11/build_win32.bat b/examples/example_sdl3_directx11/build_win32.bat new file mode 100644 index 000000000..b84b01f1a --- /dev/null +++ b/examples/example_sdl3_directx11/build_win32.bat @@ -0,0 +1,8 @@ +@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. +@set OUT_DIR=Debug +@set OUT_EXE=example_sdl3_directx11 +@set INCLUDES=/I..\.. /I..\..\backends /I%SDL3_DIR%\include /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" +@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_dx11.cpp ..\..\imgui*.cpp +@set LIBS=/LIBPATH:%SDL3_DIR%\lib\x86 SDL3.lib /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d11.lib d3dcompiler.lib shell32.lib +mkdir %OUT_DIR% +cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console diff --git a/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj new file mode 100644 index 000000000..340901c3d --- /dev/null +++ b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {009DAC16-1A9C-47BE-9770-A30A046E8090} + example_sdl3_directx11 + 8.1 + example_sdl3_directx11 + + + + Application + true + MultiByte + v140 + + + Application + true + MultiByte + v140 + + + Application + false + true + MultiByte + v140 + + + Application + false + true + MultiByte + v140 + + + + + + + + + + + + + + + + + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include;%(AdditionalIncludeDirectories) + /utf-8 %(AdditionalOptions) + + + true + %SDL3_DIR%\lib\x86;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + SDL3.lib;d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include;%(AdditionalIncludeDirectories) + /utf-8 %(AdditionalOptions) + + + true + %SDL3_DIR%\lib\x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + SDL3.lib;d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include;%(AdditionalIncludeDirectories) + false + /utf-8 %(AdditionalOptions) + + + true + true + true + %SDL3_DIR%\lib\x86;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + SDL3.lib;d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + Console + + + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include;%(AdditionalIncludeDirectories) + false + /utf-8 %(AdditionalOptions) + + + true + true + true + %SDL3_DIR%\lib\x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + SDL3.lib;d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj.filters b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj.filters new file mode 100644 index 000000000..0579b7a64 --- /dev/null +++ b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + {0587d7a3-f2ce-4d56-b84f-a0005d3bfce6} + + + {08e36723-ce4f-4cff-9662-c40801cf1acf} + + + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + + + imgui + + + sources + + + imgui + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + + + + imgui + + + imgui + + + \ No newline at end of file diff --git a/examples/example_sdl3_directx11/main.cpp b/examples/example_sdl3_directx11/main.cpp new file mode 100644 index 000000000..4337d4e2d --- /dev/null +++ b/examples/example_sdl3_directx11/main.cpp @@ -0,0 +1,260 @@ +// Dear ImGui: standalone example application for SDL3 + DirectX 11 +// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.) + +// 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 + +#include "imgui.h" +#include "imgui_impl_sdl3.h" +#include "imgui_impl_dx11.h" +#include +#include // printf +#include + +// Data +static ID3D11Device* g_pd3dDevice = nullptr; +static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr; +static IDXGISwapChain* g_pSwapChain = nullptr; +static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr; + +// Forward declarations of helper functions +bool CreateDeviceD3D(HWND hWnd); +void CleanupDeviceD3D(); +void CreateRenderTarget(); +void CleanupRenderTarget(); + +// Main code +int main(int, char**) +{ + // Setup SDL + // [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function] + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) + { + printf("Error: SDL_Init(): %s\n", SDL_GetError()); + return 1; + } + + // Setup window + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_WindowFlags window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+DirectX11 example", (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return 1; + } + + SDL_PropertiesID props = SDL_GetWindowProperties(window); + HWND hwnd = (HWND)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); + + // Initialize Direct3D + if (!CreateDeviceD3D(hwnd)) + { + CleanupDeviceD3D(); + return 1; + } + + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + SDL_ShowWindow(window); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForD3D(window); + ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); + + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; + while (!done) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + // [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function] + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL3_ProcessEvent(&event); + if (event.type == SDL_EVENT_QUIT) + done = true; + if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) + done = true; + if (event.type == SDL_EVENT_WINDOW_RESIZED && event.window.windowID == SDL_GetWindowID(window)) + { + // Release all outstanding references to the swap chain's buffers before resizing. + CleanupRenderTarget(); + g_pSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0); + CreateRenderTarget(); + } + } + + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function] + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } + + // Start the Dear ImGui frame + ImGui_ImplDX11_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w }; + g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr); + g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + + g_pSwapChain->Present(1, 0); // Present with vsync + //g_pSwapChain->Present(0, 0); // Present without vsync + } + + // Cleanup + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function] + ImGui_ImplDX11_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + + CleanupDeviceD3D(); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} + +// Helper functions to use DirectX11 +bool CreateDeviceD3D(HWND hWnd) +{ + // Setup swap chain + // This is a basic setup. Optimally could use e.g. DXGI_SWAP_EFFECT_FLIP_DISCARD and handle fullscreen mode differently. See #8979 for suggestions. + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + sd.BufferCount = 2; + sd.BufferDesc.Width = 0; + sd.BufferDesc.Height = 0; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.BufferDesc.RefreshRate.Numerator = 60; + sd.BufferDesc.RefreshRate.Denominator = 1; + sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.OutputWindow = hWnd; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.Windowed = TRUE; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + + UINT createDeviceFlags = 0; + //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; + D3D_FEATURE_LEVEL featureLevel; + const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, }; + HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext); + if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available. + res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext); + if (res != S_OK) + return false; + + CreateRenderTarget(); + return true; +} + +void CleanupDeviceD3D() +{ + CleanupRenderTarget(); + if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; } + if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; } + if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; } +} + +void CreateRenderTarget() +{ + ID3D11Texture2D* pBackBuffer; + g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView); + pBackBuffer->Release(); +} + +void CleanupRenderTarget() +{ + if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; } +} diff --git a/examples/example_sdl3_metal/Makefile b/examples/example_sdl3_metal/Makefile new file mode 100644 index 000000000..9093ae133 --- /dev/null +++ b/examples/example_sdl3_metal/Makefile @@ -0,0 +1,48 @@ +# +# You will need SDL3 (http://www.libsdl.org): +# brew install sdl3 +# + +#CXX = g++ +#CXX = clang++ + +EXE = example_sdl3_metal +IMGUI_DIR = ../.. +SOURCES = main.mm +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl3.cpp $(IMGUI_DIR)/backends/imgui_impl_metal.mm +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) + +LIBS = -framework Metal -framework MetalKit -framework Cocoa -framework IOKit -framework CoreVideo -framework QuartzCore +LIBS += `pkg-config --libs sdl3` +LIBS += -L/usr/local/lib -L/opt/local/lib + +CXXFLAGS += `pkg-config --cflags sdl3` +CXXFLAGS += -I/usr/local/include -I/opt/local/include +CXXFLAGS += -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +CXXFLAGS += -Wall -Wformat +CFLAGS = $(CXXFLAGS) + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:%.mm + $(CXX) $(CXXFLAGS) -ObjC++ -fobjc-weak -fobjc-arc -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.mm + $(CXX) $(CXXFLAGS) -ObjC++ -fobjc-weak -fobjc-arc -c -o $@ $< + +all: $(EXE) + @echo Build complete + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) diff --git a/examples/example_sdl3_metal/main.mm b/examples/example_sdl3_metal/main.mm new file mode 100644 index 000000000..e6e306624 --- /dev/null +++ b/examples/example_sdl3_metal/main.mm @@ -0,0 +1,210 @@ +// Dear ImGui: standalone example application for SDL3 + Metal +// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.) + +// 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 + +#include "imgui.h" +#include "imgui_impl_sdl3.h" +#include "imgui_impl_metal.h" +#include // printf, fprintf +#include + +#import +#import + +// Main code +int main(int, char**) +{ + // Setup SDL + // [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function] + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) + { + printf("Error: SDL_Init(): %s\n", SDL_GetError()); + return 1; + } + + // Create SDL window graphics context + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_WindowFlags window_flags = SDL_WINDOW_METAL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+Metal example", (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return 1; + } + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + SDL_ShowWindow(window); + + // Create Metal device _before_ creating the view/layer + id metalDevice = MTLCreateSystemDefaultDevice(); + if (!metalDevice) + { + printf("Error: failed to create Metal device.\n"); + SDL_DestroyWindow(window); + SDL_Quit(); + return 1; + } + SDL_MetalView view = SDL_Metal_CreateView(window); + CAMetalLayer* layer = (__bridge CAMetalLayer*)SDL_Metal_GetLayer(view); + layer.device = metalDevice; + layer.pixelFormat = MTLPixelFormatBGRA8Unorm; + + id commandQueue = [layer.device newCommandQueue]; + MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor new]; + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + + // Setup Platform/Renderer backends + ImGui_ImplMetal_Init(layer.device); + ImGui_ImplSDL3_InitForMetal(window); + + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + float clear_color[4] = { 0.45f, 0.55f, 0.60f, 1.00f }; + + // Main loop + bool done = false; + while (!done) + { + @autoreleasepool + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + // [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function] + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL3_ProcessEvent(&event); + if (event.type == SDL_EVENT_QUIT) + done = true; + if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) + done = true; + } + + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function] + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } + + int width, height; + SDL_GetWindowSizeInPixels(window, &width, &height); + + layer.drawableSize = CGSizeMake(width, height); + id drawable = [layer nextDrawable]; + + id commandBuffer = [commandQueue commandBuffer]; + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color[0] * clear_color[3], clear_color[1] * clear_color[3], clear_color[2] * clear_color[3], clear_color[3]); + renderPassDescriptor.colorAttachments[0].texture = drawable.texture; + renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; + renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; + id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + [renderEncoder pushDebugGroup:@"ImGui demo"]; + + // Start the Dear ImGui frame + ImGui_ImplMetal_NewFrame(renderPassDescriptor); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + ImGui_ImplMetal_RenderDrawData(draw_data, commandBuffer, renderEncoder); + + [renderEncoder popDebugGroup]; + [renderEncoder endEncoding]; + + [commandBuffer presentDrawable:drawable]; + [commandBuffer commit]; + } + } + + // Cleanup + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function] + ImGui_ImplMetal_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} diff --git a/examples/example_sdl3_opengl3/Makefile b/examples/example_sdl3_opengl3/Makefile index 741e97d0c..d9c6eac9e 100644 --- a/examples/example_sdl3_opengl3/Makefile +++ b/examples/example_sdl3_opengl3/Makefile @@ -45,20 +45,21 @@ endif ifeq ($(UNAME_S), Darwin) #APPLE ECHO_MESSAGE = "Mac OS X" - LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl3-config --libs` + LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo + LIBS += `pkg-config --libs sdl3` LIBS += -L/usr/local/lib -L/opt/local/lib - CXXFLAGS += `pkg-config sdl3 --cflags` + CXXFLAGS += `pkg-config --cflags sdl3` CXXFLAGS += -I/usr/local/include -I/opt/local/include CFLAGS = $(CXXFLAGS) endif ifeq ($(OS), Windows_NT) - ECHO_MESSAGE = "MinGW" - LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl3` + ECHO_MESSAGE = "MinGW" + LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl3` - CXXFLAGS += `pkg-config --cflags sdl3` - CFLAGS = $(CXXFLAGS) + CXXFLAGS += `pkg-config --cflags sdl3` + CFLAGS = $(CXXFLAGS) endif ##--------------------------------------------------------------------- diff --git a/examples/example_sdl3_opengl3/Makefile.emscripten b/examples/example_sdl3_opengl3/Makefile.emscripten index 9e9ffd603..ce873ed1a 100644 --- a/examples/example_sdl3_opengl3/Makefile.emscripten +++ b/examples/example_sdl3_opengl3/Makefile.emscripten @@ -36,10 +36,13 @@ EMS = ##--------------------------------------------------------------------- # ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only) -EMS += -s USE_SDL=2 +EMS += -s USE_SDL=3 EMS += -s DISABLE_EXCEPTION_CATCHING=1 LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1 +# Build as single file (binary text encoded in .html file) +#LDFLAGS += -sSINGLE_FILE + # Uncomment next line to fix possible rendering bugs with Emscripten version older then 1.39.0 (https://github.com/ocornut/imgui/issues/2877) #EMS += -s BINARYEN_TRAP_MODE=clamp #EMS += -s SAFE_HEAP=1 ## Adds overhead diff --git a/examples/example_sdl3_opengl3/main.cpp b/examples/example_sdl3_opengl3/main.cpp index e7f239f47..a36cfaba9 100644 --- a/examples/example_sdl3_opengl3/main.cpp +++ b/examples/example_sdl3_opengl3/main.cpp @@ -18,7 +18,6 @@ #include #endif -// This example doesn't compile with Emscripten yet! Awaiting SDL3 support. #ifdef __EMSCRIPTEN__ #include "../libs/emscripten/emscripten_mainloop_stub.h" #endif @@ -27,20 +26,28 @@ int main(int, char**) { // Setup SDL + // [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function] if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) { printf("Error: SDL_Init(): %s\n", SDL_GetError()); - return -1; + return 1; } // Decide GL+GLSL versions #if defined(IMGUI_IMPL_OPENGL_ES2) - // GL ES 2.0 + GLSL 100 + // GL ES 2.0 + GLSL 100 (WebGL 1.0) const char* glsl_version = "#version 100"; SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#elif defined(IMGUI_IMPL_OPENGL_ES3) + // GL ES 3.0 + GLSL 300 es (WebGL 2.0) + const char* glsl_version = "#version 300 es"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); #elif defined(__APPLE__) // GL 3.2 Core + GLSL 150 const char* glsl_version = "#version 150"; @@ -61,17 +68,24 @@ int main(int, char**) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN; - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+OpenGL3 example", 1280, 720, window_flags); + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+OpenGL3 example", (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); if (window == nullptr) { printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); - return -1; + return 1; } - SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); SDL_GLContext gl_context = SDL_GL_CreateContext(window); + if (gl_context == nullptr) + { + printf("Error: SDL_GL_CreateContext(): %s\n", SDL_GetError()); + return 1; + } + SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SetSwapInterval(1); // Enable vsync + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); SDL_ShowWindow(window); // Setup Dear ImGui context @@ -85,25 +99,32 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplSDL3_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state @@ -127,6 +148,7 @@ int main(int, char**) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + // [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function] SDL_Event event; while (SDL_PollEvent(&event)) { @@ -136,6 +158,8 @@ int main(int, char**) if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) done = true; } + + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function] if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) { SDL_Delay(10); @@ -197,6 +221,7 @@ int main(int, char**) #endif // Cleanup + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function] ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL3_Shutdown(); ImGui::DestroyContext(); diff --git a/examples/example_sdl3_sdlgpu3/Makefile b/examples/example_sdl3_sdlgpu3/Makefile new file mode 100644 index 000000000..c3159d888 --- /dev/null +++ b/examples/example_sdl3_sdlgpu3/Makefile @@ -0,0 +1,73 @@ +# +# Cross Platform Makefile +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# +# You will need SDL3 (http://www.libsdl.org) which is still unreleased/unpackaged. + +#CXX = g++ +#CXX = clang++ + +EXE = example_sdl3_sdlgpu3 +IMGUI_DIR = ../.. +SOURCES = main.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl3.cpp $(IMGUI_DIR)/backends/imgui_impl_sdlgpu3.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) + +CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +CXXFLAGS += -g -Wall -Wformat +LIBS = + + +##--------------------------------------------------------------------- +## BUILD FLAGS PER PLATFORM +##--------------------------------------------------------------------- + +ifeq ($(UNAME_S), Linux) #LINUX + ECHO_MESSAGE = "Linux" + LIBS += -ldl `pkg-config sdl3 --libs` + + CXXFLAGS += `pkg-config sdl3 --cflags` + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(UNAME_S), Darwin) #APPLE + ECHO_MESSAGE = "Mac OS X" + LIBS += -framework Cocoa -framework IOKit -framework CoreVideo `pkg-config --libs sdl3` + LIBS += -L/usr/local/lib -L/opt/local/lib + + CXXFLAGS += `pkg-config sdl3 --cflags` + CXXFLAGS += -I/usr/local/include -I/opt/local/include + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(OS), Windows_NT) + ECHO_MESSAGE = "MinGW" + LIBS += -lgdi32 -limm32 `pkg-config --static --libs sdl3` + + CXXFLAGS += `pkg-config --cflags sdl3` + CFLAGS = $(CXXFLAGS) +endif + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(ECHO_MESSAGE) + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) diff --git a/examples/example_sdl3_sdlgpu3/build_win64.bat b/examples/example_sdl3_sdlgpu3/build_win64.bat new file mode 100644 index 000000000..ad7a2d603 --- /dev/null +++ b/examples/example_sdl3_sdlgpu3/build_win64.bat @@ -0,0 +1,14 @@ +@REM Build for Visual Studio compiler. Run your copy of vcvars64.bat or vcvarsall.bat to setup command-line compiler. + +@set OUT_EXE=example_sdl3_sdlgpu3 +@set INCLUDES=/I..\.. /I..\..\backends /I%SDL3_DIR%\include +@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_sdlgpu3.cpp ..\..\imgui*.cpp +@set LIBS=/LIBPATH:%SDL3_DIR%\lib\x64 SDL3.lib shell32.lib + +@set OUT_DIR=Debug +mkdir %OUT_DIR% +cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console + +@set OUT_DIR=Release +@REM mkdir %OUT_DIR% +@REM cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console diff --git a/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj b/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj new file mode 100644 index 000000000..3d034f52c --- /dev/null +++ b/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {c22cb6f8-39a5-4dda-90ed-4aca4e81e1e5} + example_sdl3_sdlgpu3 + 8.1 + + + + Application + true + MultiByte + v140 + + + Application + true + MultiByte + v140 + + + Application + false + true + MultiByte + v140 + + + Application + false + true + MultiByte + v140 + + + + + + + + + + + + + + + + + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL3;%(AdditionalIncludeDirectories) + _MBCS;%(PreprocessorDefinitions) + /utf-8 %(AdditionalOptions) + + + true + %SDL3_DIR%\lib\x86;%(AdditionalLibraryDirectories) + SDL3.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL3;%(AdditionalIncludeDirectories) + _MBCS;%(PreprocessorDefinitions) + /utf-8 %(AdditionalOptions) + + + true + %SDL3_DIR%\lib\x64;%(AdditionalLibraryDirectories) + SDL3.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL3;%(AdditionalIncludeDirectories) + false + _MBCS;%(PreprocessorDefinitions) + /utf-8 %(AdditionalOptions) + + + true + true + true + %SDL3_DIR%\lib\x86;%(AdditionalLibraryDirectories) + SDL3.lib;%(AdditionalDependencies) + Console + + + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL3;%(AdditionalIncludeDirectories) + false + _MBCS;%(PreprocessorDefinitions) + /utf-8 %(AdditionalOptions) + + + true + true + true + %SDL3_DIR%\lib\x64;%(AdditionalLibraryDirectories) + SDL3.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj.filters b/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj.filters new file mode 100644 index 000000000..4710b550c --- /dev/null +++ b/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj.filters @@ -0,0 +1,60 @@ + + + + + sources + + + sources + + + sources + + + imgui + + + imgui + + + imgui + + + imgui + + + imgui + + + + + sources + + + sources + + + sources + + + imgui + + + imgui + + + imgui + + + + + + + + {9044ef92-2afa-42f2-92df-ac473c7c32b3} + + + {ef84458b-039a-4902-8455-4e33df5a8578} + + + \ No newline at end of file diff --git a/examples/example_sdl3_sdlgpu3/main.cpp b/examples/example_sdl3_sdlgpu3/main.cpp new file mode 100644 index 000000000..8edbbbbad --- /dev/null +++ b/examples/example_sdl3_sdlgpu3/main.cpp @@ -0,0 +1,232 @@ +// Dear ImGui: standalone example application for SDL3 + SDL_GPU +// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.) + +// 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_sdlgpu3.cpp/.h in their own engine/app. +// - Unlike other backends, the user must call the function ImGui_ImplSDLGPU_PrepareDrawData() BEFORE issuing a SDL_GPURenderPass containing ImGui_ImplSDLGPU_RenderDrawData. +// Calling the function is MANDATORY, otherwise the ImGui will not upload neither the vertex nor the index buffer for the GPU. See imgui_impl_sdlgpu3.cpp for more info. + +#include "imgui.h" +#include "imgui_impl_sdl3.h" +#include "imgui_impl_sdlgpu3.h" +#include // printf, fprintf +#include // abort +#include + +// This example doesn't compile with Emscripten yet! Awaiting SDL3 support. +#ifdef __EMSCRIPTEN__ +#include "../libs/emscripten/emscripten_mainloop_stub.h" +#endif + +// Main code +int main(int, char**) +{ + // Setup SDL + // [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function] + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) + { + printf("Error: SDL_Init(): %s\n", SDL_GetError()); + return 1; + } + + // Create SDL window graphics context + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_WindowFlags window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+SDL_GPU example", (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return 1; + } + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + SDL_ShowWindow(window); + + // Create GPU Device + SDL_GPUDevice* gpu_device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_METALLIB, true, nullptr); + if (gpu_device == nullptr) + { + printf("Error: SDL_CreateGPUDevice(): %s\n", SDL_GetError()); + return 1; + } + + // Claim window for GPU Device + if (!SDL_ClaimWindowForGPUDevice(gpu_device, window)) + { + printf("Error: SDL_ClaimWindowForGPUDevice(): %s\n", SDL_GetError()); + return 1; + } + SDL_SetGPUSwapchainParameters(gpu_device, window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForSDLGPU(window); + ImGui_ImplSDLGPU3_InitInfo init_info = {}; + init_info.Device = gpu_device; + init_info.ColorTargetFormat = SDL_GetGPUSwapchainTextureFormat(gpu_device, window); + init_info.MSAASamples = SDL_GPU_SAMPLECOUNT_1; // Only used in multi-viewports mode. + init_info.SwapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR; // Only used in multi-viewports mode. + init_info.PresentMode = SDL_GPU_PRESENTMODE_VSYNC; + ImGui_ImplSDLGPU3_Init(&init_info); + + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; + while (!done) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + // [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function] + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL3_ProcessEvent(&event); + if (event.type == SDL_EVENT_QUIT) + done = true; + if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) + done = true; + } + + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function] + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } + + // Start the Dear ImGui frame + ImGui_ImplSDLGPU3_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f); + + SDL_GPUCommandBuffer* command_buffer = SDL_AcquireGPUCommandBuffer(gpu_device); // Acquire a GPU command buffer + + SDL_GPUTexture* swapchain_texture; + SDL_WaitAndAcquireGPUSwapchainTexture(command_buffer, window, &swapchain_texture, nullptr, nullptr); // Acquire a swapchain texture + + if (swapchain_texture != nullptr && !is_minimized) + { + // This is mandatory: call ImGui_ImplSDLGPU3_PrepareDrawData() to upload the vertex/index buffer! + ImGui_ImplSDLGPU3_PrepareDrawData(draw_data, command_buffer); + + // Setup and start a render pass + SDL_GPUColorTargetInfo target_info = {}; + target_info.texture = swapchain_texture; + target_info.clear_color = SDL_FColor { clear_color.x, clear_color.y, clear_color.z, clear_color.w }; + target_info.load_op = SDL_GPU_LOADOP_CLEAR; + target_info.store_op = SDL_GPU_STOREOP_STORE; + target_info.mip_level = 0; + target_info.layer_or_depth_plane = 0; + target_info.cycle = false; + SDL_GPURenderPass* render_pass = SDL_BeginGPURenderPass(command_buffer, &target_info, 1, nullptr); + + // Render ImGui + ImGui_ImplSDLGPU3_RenderDrawData(draw_data, command_buffer, render_pass); + + SDL_EndGPURenderPass(render_pass); + } + + // Submit the command buffer + SDL_SubmitGPUCommandBuffer(command_buffer); + } + + // Cleanup + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function] + SDL_WaitForGPUIdle(gpu_device); + ImGui_ImplSDL3_Shutdown(); + ImGui_ImplSDLGPU3_Shutdown(); + ImGui::DestroyContext(); + + SDL_ReleaseWindowFromGPUDevice(gpu_device, window); + SDL_DestroyGPUDevice(gpu_device); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} diff --git a/examples/example_sdl3_sdlrenderer3/Makefile b/examples/example_sdl3_sdlrenderer3/Makefile index 238576c7c..c278f7830 100644 --- a/examples/example_sdl3_sdlrenderer3/Makefile +++ b/examples/example_sdl3_sdlrenderer3/Makefile @@ -26,7 +26,7 @@ LIBS = ifeq ($(UNAME_S), Linux) #LINUX ECHO_MESSAGE = "Linux" - LIBS += -ldl `pkg-config sdl3 --libs` + LIBS += -ldl `pkg-config sdl3 --libs` CXXFLAGS += `pkg-config sdl3 --cflags` CFLAGS = $(CXXFLAGS) @@ -34,20 +34,21 @@ endif ifeq ($(UNAME_S), Darwin) #APPLE ECHO_MESSAGE = "Mac OS X" - LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl3-config --libs` + LIBS += -framework Cocoa -framework IOKit -framework CoreVideo + LIBS += `pkg-config --libs sdl3` LIBS += -L/usr/local/lib -L/opt/local/lib - CXXFLAGS += `pkg-config sdl3 --cflags` + CXXFLAGS += `pkg-config --cflags sdl3` CXXFLAGS += -I/usr/local/include -I/opt/local/include CFLAGS = $(CXXFLAGS) endif ifeq ($(OS), Windows_NT) - ECHO_MESSAGE = "MinGW" - LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl3` + ECHO_MESSAGE = "MinGW" + LIBS += -lgdi32 -limm32 `pkg-config --static --libs sdl3` - CXXFLAGS += `pkg-config --cflags sdl3` - CFLAGS = $(CXXFLAGS) + CXXFLAGS += `pkg-config --cflags sdl3` + CFLAGS = $(CXXFLAGS) endif ##--------------------------------------------------------------------- diff --git a/examples/example_sdl3_sdlrenderer3/main.cpp b/examples/example_sdl3_sdlrenderer3/main.cpp index 4b9b86e9b..88f9ac7fb 100644 --- a/examples/example_sdl3_sdlrenderer3/main.cpp +++ b/examples/example_sdl3_sdlrenderer3/main.cpp @@ -15,36 +15,37 @@ #include "imgui_impl_sdlrenderer3.h" #include #include -#if defined(IMGUI_IMPL_OPENGL_ES2) -#include -#else -#include + +#ifdef __EMSCRIPTEN__ +#include "../libs/emscripten/emscripten_mainloop_stub.h" #endif // Main code int main(int, char**) { // Setup SDL + // [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function] if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) { printf("Error: SDL_Init(): %s\n", SDL_GetError()); - return -1; + return 1; } // Create window with SDL_Renderer graphics context - Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN; - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+SDL_Renderer example", 1280, 720, window_flags); + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_WindowFlags window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+SDL_Renderer example", (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); if (window == nullptr) { printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); - return -1; + return 1; } SDL_Renderer* renderer = SDL_CreateRenderer(window, nullptr); SDL_SetRenderVSync(renderer, 1); if (renderer == nullptr) { SDL_Log("Error: SDL_CreateRenderer(): %s\n", SDL_GetError()); - return -1; + return 1; } SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); SDL_ShowWindow(window); @@ -60,25 +61,31 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplSDL3_InitForSDLRenderer(window, renderer); ImGui_ImplSDLRenderer3_Init(renderer); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state @@ -102,6 +109,7 @@ int main(int, char**) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + // [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function] SDL_Event event; while (SDL_PollEvent(&event)) { @@ -111,6 +119,8 @@ int main(int, char**) if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) done = true; } + + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function] if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) { SDL_Delay(10); @@ -161,7 +171,7 @@ int main(int, char**) // Rendering ImGui::Render(); - //SDL_RenderSetScale(renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); + SDL_SetRenderScale(renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); SDL_SetRenderDrawColorFloat(renderer, clear_color.x, clear_color.y, clear_color.z, clear_color.w); SDL_RenderClear(renderer); ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer); @@ -172,6 +182,7 @@ int main(int, char**) #endif // Cleanup + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function] ImGui_ImplSDLRenderer3_Shutdown(); ImGui_ImplSDL3_Shutdown(); ImGui::DestroyContext(); diff --git a/examples/example_sdl3_vulkan/Makefile b/examples/example_sdl3_vulkan/Makefile new file mode 100644 index 000000000..e1956ff67 --- /dev/null +++ b/examples/example_sdl3_vulkan/Makefile @@ -0,0 +1,77 @@ +# +# Cross Platform Makefile +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# +# You will need SDL3 (http://www.libsdl.org) which is still unreleased/unpackaged. +# Mac OS X: +# brew install sdl3 + +#CXX = g++ +#CXX = clang++ + +EXE = example_sdl3_vulkan +IMGUI_DIR = ../.. +SOURCES = main.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl3.cpp $(IMGUI_DIR)/backends/imgui_impl_vulkan.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) + +CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +CXXFLAGS += -g -Wall -Wformat +LIBS = + + +##--------------------------------------------------------------------- +## BUILD FLAGS PER PLATFORM +##--------------------------------------------------------------------- + +ifeq ($(UNAME_S), Linux) #LINUX + ECHO_MESSAGE = "Linux" + LIBS += -ldl + LIBS += `pkg-config --libs sdl3 vulkan` + + CXXFLAGS += `pkg-config --cflags sdl3 vulkan` + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(UNAME_S), Darwin) #APPLE + ECHO_MESSAGE = "Mac OS X" + LIBS += -framework Cocoa -framework IOKit -framework CoreVideo + LIBS += `pkg-config --libs sdl3 vulkan` + LIBS += -L/usr/local/lib -L/opt/local/lib + + CXXFLAGS += `pkg-config --cflags sdl3 vulkan` + CXXFLAGS += -I/usr/local/include -I/opt/local/include + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(OS), Windows_NT) + ECHO_MESSAGE = "MinGW" + LIBS += -lgdi32 -limm32 `pkg-config --static --libs sdl3 vulkan` + + CXXFLAGS += `pkg-config --cflags sdl3 vulkan` + CFLAGS = $(CXXFLAGS) +endif + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(ECHO_MESSAGE) + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) diff --git a/examples/example_sdl3_vulkan/main.cpp b/examples/example_sdl3_vulkan/main.cpp index dde3c0d4c..f461aab4c 100644 --- a/examples/example_sdl3_vulkan/main.cpp +++ b/examples/example_sdl3_vulkan/main.cpp @@ -35,6 +35,7 @@ //#define APP_USE_UNLIMITED_FRAME_RATE #ifdef _DEBUG #define APP_USE_VULKAN_DEBUG_REPORT +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; #endif // Data @@ -44,7 +45,6 @@ static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; static uint32_t g_QueueFamily = (uint32_t)-1; static VkQueue g_Queue = VK_NULL_HANDLE; -static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; @@ -54,7 +54,7 @@ static bool g_SwapChainRebuild = false; static void check_vk_result(VkResult err) { - if (err == 0) + if (err == VK_SUCCESS) return; fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); if (err < 0) @@ -78,35 +78,6 @@ static bool IsExtensionAvailable(const ImVector& properti return false; } -static VkPhysicalDevice SetupVulkan_SelectPhysicalDevice() -{ - uint32_t gpu_count; - VkResult err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, nullptr); - check_vk_result(err); - IM_ASSERT(gpu_count > 0); - - ImVector gpus; - gpus.resize(gpu_count); - err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus.Data); - check_vk_result(err); - - // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers - // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple - // dedicated GPUs) is out of scope of this sample. - for (VkPhysicalDevice& device : gpus) - { - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(device, &properties); - if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) - return device; - } - - // Use first GPU (Integrated) is a Discrete one is not available. - if (gpu_count > 0) - return gpus[0]; - return VK_NULL_HANDLE; -} - static void SetupVulkan(ImVector instance_extensions) { VkResult err; @@ -170,23 +141,12 @@ static void SetupVulkan(ImVector instance_extensions) } // Select Physical Device (GPU) - g_PhysicalDevice = SetupVulkan_SelectPhysicalDevice(); + g_PhysicalDevice = ImGui_ImplVulkanH_SelectPhysicalDevice(g_Instance); + IM_ASSERT(g_PhysicalDevice != VK_NULL_HANDLE); // Select graphics queue family - { - uint32_t count; - vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, nullptr); - VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues); - for (uint32_t i = 0; i < count; i++) - if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) - { - g_QueueFamily = i; - break; - } - free(queues); - IM_ASSERT(g_QueueFamily != (uint32_t)-1); - } + g_QueueFamily = ImGui_ImplVulkanH_SelectQueueFamilyIndex(g_PhysicalDevice); + IM_ASSERT(g_QueueFamily != (uint32_t)-1); // Create Logical Device (with 1 queue) { @@ -222,18 +182,19 @@ static void SetupVulkan(ImVector instance_extensions) } // Create Descriptor Pool - // The example only requires a single combined image sampler descriptor for the font image and only uses one descriptor set (for that) - // If you wish to load e.g. additional textures you may need to alter pools sizes. + // If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets. { VkDescriptorPoolSize pool_sizes[] = { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE }, }; 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 = 1; - pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.maxSets = 0; + for (VkDescriptorPoolSize& pool_size : pool_sizes) + pool_info.maxSets += pool_size.descriptorCount; + pool_info.poolSizeCount = (uint32_t)IM_COUNTOF(pool_sizes); pool_info.pPoolSizes = pool_sizes; err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); check_vk_result(err); @@ -244,11 +205,9 @@ static void SetupVulkan(ImVector instance_extensions) // Your real engine/app may not use them. static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { - wd->Surface = surface; - // Check for WSI support VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -258,20 +217,21 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + wd->Surface = surface; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode -#ifdef APP_UNLIMITED_FRAME_RATE +#ifdef APP_USE_UNLIMITED_FRAME_RATE VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; #else VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; #endif - wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_COUNTOF(present_modes)); //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); } static void CleanupVulkan() @@ -288,24 +248,23 @@ static void CleanupVulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void CleanupVulkanWindow() +static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) { - ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator); + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); } static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkResult err; - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { @@ -374,11 +333,11 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores } @@ -386,19 +345,21 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) int main(int, char**) { // Setup SDL - if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD) != 0) + // [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function] + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) { printf("Error: SDL_Init(): %s\n", SDL_GetError()); - return -1; + return 1; } // Create window with Vulkan graphics context - SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_HIDDEN); - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+Vulkan example", 1280, 720, window_flags); + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_WindowFlags window_flags = SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+Vulkan example", (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); if (window == nullptr) { printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); - return -1; + return 1; } ImVector extensions; @@ -438,9 +399,15 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplSDL3_InitForVulkan(window); ImGui_ImplVulkan_InitInfo init_info = {}; + //init_info.ApiVersion = VK_API_VERSION_1_3; // Pass in your value of VkApplicationInfo::apiVersion, otherwise will default to header version. init_info.Instance = g_Instance; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; @@ -448,29 +415,31 @@ int main(int, char**) init_info.Queue = g_Queue; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; - init_info.RenderPass = wd->RenderPass; - init_info.Subpass = 0; init_info.MinImageCount = g_MinImageCount; init_info.ImageCount = wd->ImageCount; - init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; init_info.Allocator = g_Allocator; + init_info.PipelineInfoMain.RenderPass = wd->RenderPass; + init_info.PipelineInfoMain.Subpass = 0; + init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state @@ -487,6 +456,7 @@ int main(int, char**) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + // [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function] SDL_Event event; while (SDL_PollEvent(&event)) { @@ -496,6 +466,8 @@ int main(int, char**) if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) done = true; } + + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function] if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) { SDL_Delay(10); @@ -508,7 +480,7 @@ int main(int, char**) if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) { ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0); g_MainWindowData.FrameIndex = 0; g_SwapChainRebuild = false; } @@ -571,13 +543,14 @@ int main(int, char**) } // Cleanup + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function] err = vkDeviceWaitIdle(g_Device); check_vk_result(err); ImGui_ImplVulkan_Shutdown(); ImGui_ImplSDL3_Shutdown(); ImGui::DestroyContext(); - CleanupVulkanWindow(); + CleanupVulkanWindow(&g_MainWindowData); CleanupVulkan(); SDL_DestroyWindow(window); diff --git a/examples/example_sdl3_wgpu/CMakeLists.txt b/examples/example_sdl3_wgpu/CMakeLists.txt new file mode 100644 index 000000000..85de861e3 --- /dev/null +++ b/examples/example_sdl3_wgpu/CMakeLists.txt @@ -0,0 +1,213 @@ +# Building for desktop with Dawn: +# 1. git clone https://github.com/google/dawn dawn +# 2. cmake -B build -DIMGUI_DAWN_DIR=dawn +# 3. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_sdl3_wgpu[.exe] or build/Debug/example_sdl3_wgpu[.exe] + +# Building for desktop with WGPU-Native: +# 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases +# 2. unzip the downloaded file in your_preferred_folder +# 3. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory) +# 4. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_sdl3_wgpu[.exe] or build/Debug/example_sdl3_wgpu[.exe] + +# Building for desktop with WGVK (MUCH EASIER) +# 1. git clone https://github.com/manuel5975p/WGVK dawn +# 2. cmake -B build -DIMGUI_WGVK_DIR=wgvk +# 3. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_sdl3_wgpu[.exe] or build/Debug/example_sdl3_wgpu[.exe] + +# Building for Emscripten: +# 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html +# 2. Install Ninja build system +# 3. emcmake cmake -G Ninja -B build +# (optional) -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py", see ReadMe.md +# 4. cmake --build build +# 5. emrun build/index.html + +cmake_minimum_required(VERSION 3.22) # Dawn requires CMake >= 3.22 +project(imgui_example_sdl3_wgpu C CXX) + +set(IMGUI_EXECUTABLE example_sdl3_wgpu) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) +endif() + +set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17 + +# Dear ImGui +set(IMGUI_DIR ../../) + +# ImGui example commons source files +set(IMGUI_EXAMPLE_SOURCE_FILES + main.cpp + # backend files + ${IMGUI_DIR}/backends/imgui_impl_sdl3.cpp + ${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp + # Dear ImGui files + ${IMGUI_DIR}/imgui.cpp + ${IMGUI_DIR}/imgui_draw.cpp + ${IMGUI_DIR}/imgui_demo.cpp + ${IMGUI_DIR}/imgui_tables.cpp + ${IMGUI_DIR}/imgui_widgets.cpp +) + +if(EMSCRIPTEN) + if(EMSCRIPTEN_VERSION VERSION_LESS "4.0.15") + message(FATAL_ERROR "Using Emscripten with SDL3 needs Emscripten version >= 4.0.15") + endif() + # emdawnwebgpu was introduced in 4.0.10 so, due to the prior requirement, this will work + set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "--use-port=emdawnwebgpu" CACHE STRING "Default to --use-port=emdawnwebgpu. You can override to provide your own local port.") + + add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) +else() # Native/Desktop build + + # Check DAWN/WGPU/WGVK directory + # If it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR or IMGUI_WGVK_DIR must be specified + if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR AND NOT IMGUI_WGVK_DIR) + message(FATAL_ERROR "Please specify one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") + endif() + if((IMGUI_DAWN_DIR AND (IMGUI_WGPU_DIR OR IMGUI_WGVK_DIR)) OR (IMGUI_WGPU_DIR AND IMGUI_WGVK_DIR)) + message(FATAL_ERROR "Please specify only one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") + endif() + + if(APPLE) # Add SDL3 module to get Surface, with libs and file property for MacOS build + set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++") + set(OS_LIBRARIES "-framework CoreFoundation -framework QuartzCore -framework Metal -framework MetalKit -framework Cocoa") + endif() + + find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3) + + if(IMGUI_DAWN_DIR) + list(APPEND CMAKE_PREFIX_PATH ${IMGUI_DAWN_DIR}) + find_package(Threads) # required from Dawn installation + find_package(Dawn) # Search for a Dawn installation using IMGUI_DAWN_DIR in CMAKE_PREFIX_PATH + if(Dawn_FOUND) + message("Dawn Installation has been found!") + set(LIBRARIES dawn::webgpu_dawn ${OS_LIBRARIES}) + else() + set(IMGUI_DAWN_DIR CACHE PATH "Path to Dawn repository") + + option(DAWN_USE_GLFW OFF) # disable builtin GLFW in DAWN when we use SDL2 / SDL3 + + option(DAWN_FETCH_DEPENDENCIES "Use fetch_dawn_dependencies.py as an alternative to using depot_tools" ON) + set(DAWN_BUILD_MONOLITHIC_LIBRARY "STATIC" CACHE STRING "Build monolithic library: SHARED, STATIC, or OFF.") + + # Dawn builds many things by default - disable things we don't need + option(DAWN_BUILD_SAMPLES "Enables building Dawn's samples" OFF) + option(TINT_BUILD_CMD_TOOLS "Build the Tint command line tools" OFF) + option(TINT_BUILD_DOCS "Build documentation" OFF) + option(TINT_BUILD_TESTS "Build tests" OFF) + if(NOT APPLE) + option(TINT_BUILD_MSL_WRITER "Build the MSL output writer" OFF) + endif() + if(WIN32) + option(DAWN_FORCE_SYSTEM_COMPONENT_LOAD "Allow system component fallback" ON) + option(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" OFF) + option(TINT_BUILD_WGSL_READER "Build the WGSL input reader" ON) + option(TINT_BUILD_GLSL_WRITER "Build the GLSL output writer" OFF) + option(TINT_BUILD_GLSL_VALIDATOR "Build the GLSL output validator" OFF) + option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ON) + option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON) + endif() + # check if WAYLAND is the current Session Type and enable DAWN_USE_WAYLAND Wayland option @compile time + # You can override this using: cmake -DDAWN_USE_WAYLAND=X (X = ON | OFF) + if(LINUX) + if($ENV{XDG_SESSION_TYPE} MATCHES wayland) + option(DAWN_USE_WAYLAND "Enable support for Wayland surface" ON) + endif() + endif() + + add_subdirectory("${IMGUI_DAWN_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/dawn" EXCLUDE_FROM_ALL) + + set(LIBRARIES webgpu_dawn ${OS_LIBRARIES}) + endif() + endif() + + if(IMGUI_WGPU_DIR) + set(WGPU_NATIVE_LIB_DIR ${IMGUI_WGPU_DIR}/lib) + find_library(WGPU_LIBRARY NAMES libwgpu_native.a wgpu_native.lib wgpu_native HINTS ${WGPU_NATIVE_LIB_DIR} REQUIRED) + if(WIN32) + set(OS_LIBRARIES d3dcompiler ws2_32 userenv bcrypt ntdll opengl32 Propsys RuntimeObject) + elseif(UNIX AND NOT APPLE) + set(OS_LIBRARIES "-lm -ldl") + endif() + + set(LIBRARIES ${WGPU_LIBRARY} ${OS_LIBRARIES}) + endif() + + if(IMGUI_WGVK_DIR) + endif() +endif() + +add_executable(${IMGUI_EXECUTABLE} ${IMGUI_EXAMPLE_SOURCE_FILES}) + +target_include_directories(${IMGUI_EXECUTABLE} PUBLIC + ${IMGUI_DIR} + ${IMGUI_DIR}/backends + ${SDL3_INCLUDE_DIRS} +) + +target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_EXAMPLE_SDL3_WGPU") + +# Enable warning level compiler option only for IMGUI_EXAMPLE_SOURCE_FILES +if (MSVC) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /W4) # warning level 4 +else() + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC -Wall) # -Wextra -Wpedantic +endif() + +# IMGUI_IMPL_WEBGPU_BACKEND_DAWN/WGPU internal define is set according to: +# EMSCRIPTEN: by used FLAG +# --use-port=emdawnwebgpu --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN enabled (+EMSCRIPTEN) +# NATIVE: by used SDK installation directory +# if IMGUI_DAWN_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN enabled +# if IMGUI_WGPU_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU enabled + +if(NOT EMSCRIPTEN) # WegGPU-Native settings + if(IMGUI_DAWN_DIR) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN") + if(NOT Dawn_FOUND) + target_link_libraries(${IMGUI_EXECUTABLE} INTERFACE webgpu_cpp) + endif() + endif() + if(IMGUI_WGPU_DIR) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU") + target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGPU_DIR}/include) + endif() + if(IMGUI_WGVK_DIR) + target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) + if (MSVC) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) + endif() + endif() + + target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${LIBRARIES} SDL3::SDL3) +else() # Emscripten settings + set(CMAKE_EXECUTABLE_SUFFIX ".html") + + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}") + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN") + message(STATUS "Using ${IMGUI_EMSCRIPTEN_WEBGPU_FLAG} WebGPU implementation") + + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "-sUSE_SDL=3") + target_link_options(${IMGUI_EXECUTABLE} PRIVATE + "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}" + "-sUSE_SDL=3" + "-sWASM=1" + "-sASYNCIFY=1" + "-sALLOW_MEMORY_GROWTH=1" + "-sNO_EXIT_RUNTIME=0" + "-sASSERTIONS=1" + "-sDISABLE_EXCEPTION_CATCHING=1" +# "-sNO_FILESYSTEM=1" + "--shell-file=${CMAKE_CURRENT_LIST_DIR}/../libs/emscripten/shell_minimal.html" + ) + set_target_properties(${IMGUI_EXECUTABLE} PROPERTIES OUTPUT_NAME "index") +endif() diff --git a/examples/example_sdl3_wgpu/Makefile.emscripten b/examples/example_sdl3_wgpu/Makefile.emscripten new file mode 100644 index 000000000..c84acc77f --- /dev/null +++ b/examples/example_sdl3_wgpu/Makefile.emscripten @@ -0,0 +1,99 @@ +# +# Makefile to use with emscripten +# See https://emscripten.org/docs/getting_started/downloads.html +# for installation instructions. +# +# This Makefile assumes you have loaded emscripten's environment. +# (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead) +# +# Running `make -f Makefile.emscripten` will produce three files: +# - web/index.html +# - web/index.js +# - web/index.wasm +# +# All three are needed to run the demo. + +CC = emcc +CXX = em++ +WEB_DIR = web +EXE = $(WEB_DIR)/index.html +IMGUI_DIR = ../.. +SOURCES = main.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl3.cpp $(IMGUI_DIR)/backends/imgui_impl_wgpu.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) +CPPFLAGS = +LDFLAGS = +EMS = + +##--------------------------------------------------------------------- +## EMSCRIPTEN OPTIONS +##--------------------------------------------------------------------- + +# ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only) +EMS += -s USE_SDL=3 +EMS += -s DISABLE_EXCEPTION_CATCHING=1 +LDFLAGS += -s WASM=1 +LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 +LDFLAGS += -s ASYNCIFY=1 +LDFLAGS += -s NO_EXIT_RUNTIME=0 +LDFLAGS += -s ASSERTIONS=1 + +# Using Dawn-based WebGPU port (requires Emscripten >= 4.0.10) +EMS += --use-port=emdawnwebgpu +LDFLAGS += --use-port=emdawnwebgpu + +# Build as single file (binary text encoded in .html file) +#LDFLAGS += -sSINGLE_FILE + +# Emscripten allows preloading a file or folder to be accessible at runtime. +# The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts" +# See documentation for more details: https://emscripten.org/docs/porting/files/packaging_files.html +# (Default value is 0. Set to 1 to enable file-system and include the misc/fonts/ folder as part of the build.) +USE_FILE_SYSTEM ?= 0 +ifeq ($(USE_FILE_SYSTEM), 0) +LDFLAGS += -s NO_FILESYSTEM=1 +CPPFLAGS += -DIMGUI_DISABLE_FILE_FUNCTIONS +endif +ifeq ($(USE_FILE_SYSTEM), 1) +LDFLAGS += --no-heap-copy --preload-file ../../misc/fonts@/fonts +endif + +##--------------------------------------------------------------------- +## FINAL BUILD FLAGS +##--------------------------------------------------------------------- + +CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +#CPPFLAGS += -g +CPPFLAGS += -Wall -Wformat -Os $(EMS) +LDFLAGS += --shell-file ../libs/emscripten/shell_minimal.html +LDFLAGS += $(EMS) + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(EXE) + +$(WEB_DIR): + mkdir $@ + +serve: all + python3 -m http.server -d $(WEB_DIR) + +$(EXE): $(OBJS) $(WEB_DIR) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +clean: + rm -f $(EXE) $(OBJS) $(WEB_DIR)/*.js $(WEB_DIR)/*.wasm $(WEB_DIR)/*.wasm.pre diff --git a/examples/example_sdl3_wgpu/README.md b/examples/example_sdl3_wgpu/README.md new file mode 100644 index 000000000..c7d6aec7b --- /dev/null +++ b/examples/example_sdl3_wgpu/README.md @@ -0,0 +1,163 @@ +## How to Build + + +--- + +### Using CMake +#### Building for desktop (WebGPU-native) with Google Dawn: + 1. `git clone https://github.com/google/dawn dawn` + 2. `cmake -B build -DIMGUI_DAWN_DIR=dawn` + 3. `cmake --build build` +The resulting binary will be found at one of the following locations: + * build/Debug/example_sdl3_wgpu[.exe] + * build/example_sdl3_wgpu[.exe] + +#### Building for desktop (WebGPU-Native) with WGPU: + 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases + 2. unzip the downloaded file in `your_preferred_folder` + 3. `cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder` ("full path" or "relative" starting from current directory) + 5. `cmake --build build` +The resulting binary will be found at one of the following locations: + * build/Debug/example_sdl3_wgpu[.exe] + * build/example_sdl3_wgpu[.exe] + +#### Building for Emscripten: + 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html + 2. Install Ninja build system + 3. `emcmake cmake -G Ninja -B build` + - (optional) `-DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py"`, see below + 4. `cmake --build build` + +#### Sync Emscripten with latest Google Dawn: +If you want to sync Emscripten with latest DAWN release it's necessary to download the `port-emdawnwgpu-package` (released daily by Google) here: +https://github.com/google/dawn/releases +Unpack it in your preferred folder and to replace the step 3 with: + +3. `emcmake cmake -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py" -G Ninja -B build` + +**N.B.** +For the WASM code produced by Emscripten to work correctly, it will also be necessary to have the "corresponding" (or newer) version of Google Canary (nightly build for developers) that includes the latest changes + + +--- + +### CMake by step + +#### Generate Dawn Native: + +- `cmake -G Ninja -DIMGUI_DAWN_DIR=path_to_sdk_dir -B where_to_build_dir` + - Using `IMGUI_DAWN_DIR` set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define + +#### Generate WGPU Native: + +- `cmake -G Ninja -DIMGUI_WGPU_DIR=path_to_sdk_dir -B where_to_build_dir` + + - Using `IMGUI_WGPU_DIR` set `IMGUI_IMPL_WEBGPU_BACKEND_WGPU` compiler define + +#### Generate Emscripten: + +- `emcmake cmake -G Ninja -B where_to_build_dir`\ +CMake checks the EMSCRIPEN version then: + - if EMS >= 4.0.10 uses `--use-port=emdawnwebgpu` flag to build + - it set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define + - if EMS < 4.0.10 the build aborts (`-sUSE_WEBGPU=1` is no longer supported by our examples and our WGPU backend + +#### Generate Emscripten using external WebGPU library (emdawnwebgpu_pkg) + +- `emcmake cmake -G Ninja -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path_to_emdawnwebgpu_pkg" -B where_to_build_dir` + - it set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define + - *To use external WebGPU library it's necessary to have EMS >= 4.0.10 or the minimum requirements specified by the package:* + - https://github.com/google/dawn/releases + +#### Build time + +Once the procedure for the specific builder is generated, the build command is **always the same**: + +- Build using CMake + - `cmake --build where_to_build_dir` + - It will use selected builder to build the example. + +- Build explicitly: + - `cd where_to_build_dir` + - `ninja` + - This is the builder chosen during the generation phase + +--- + +### CMake useful options +#### Generator types (alternative to **ninja** builder): +- `-G Ninja` to build with __ninja__ builder +- `-G "Unix Makefiles"` to build with __make__ builder +- `-G "Visual Studio 17 2022" -A x64` to create a VS 2022 solution (.sln) file, Windows only + - **Native build only** + - Not **officially** supported to build Google Dawn + +Example: +- using **make** instead **ninja**: + - `cmake -G "Unix Makefiles" -DIMGUI_DAWN_DIR=path_to_sdk_dir -B where_to_build_dir` + +**Syntax is case sensitive and the "" are necessary in case of spaces between words* + +#### Directories +- The directory path can be absolute or relative (starting from the current directory) +- It's necessary to use different `where_to_build_dir` for different CMake generations + + +#### Build type +The default build type is **Debug** +It is possible to use a different build type using: +- `-DCMAKE_BUILD_TYPE=Release` +- `-DCMAKE_BUILD_TYPE=MinSizeRel` +- `-DCMAKE_BUILD_TYPE=RelWithDebInfo` + +Example: +- building **Release**: + - `cmake -G ninja -DIMGUI_WGPU_DIR=path_to_sdk_dir -DCMAKE_BUILD_TYPE=Release -B where_to_build_dir ` + +#### GLFW / SDL2 / SDL3 includes, libraries, search paths and package manager + +Includes and libraries, by default, are searched in system/compiler paths (environment variables): you can add the path to your development tools to the environment variables without having to modify the `CMakeLists.txt` file. +- e.g. CLang search in path specified from the following environment variables: + - include files: CPATH, C_INCLUDE_PATH, CPLUS_INCLUDE_PATH + - library files: LIBRARY_PATH + +If you are using a package manager (**vcpkg** / **conan** / ... ) you need/can to specify it, adding to cmake command: +- `-DCMAKE_TOOLCHAIN_FILE=path/to/package_manager.cmake` + +Examples: + +- using **vcpkg** package manager it's necessary adding: + - `-DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake` + +- full cmake command using **vcpkg** package manager: + - `cmake -G Ninja -DIMGUI_DAWN_DIR=path_to_sdk_dir -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake -B where_to_build_dir` + +--- + + +### Using makefile + +- You need to install Emscripten from https://emscripten.org/docs/getting_started/downloads.html, and have the environment variables set, as described in https://emscripten.org/docs/getting_started/downloads.html#installation-instructions + +- Depending on your configuration, in Windows you may need to run `emsdk/emsdk_env.bat` in your console to access the Emscripten command-line tools. + +- You may also refer to our [Continuous Integration setup](https://github.com/ocornut/imgui/tree/master/.github/workflows) for Emscripten setup. + +- Then build using `make -f Makefile.emscripten` while in the `example_glfw_wgpu/` directory. + +- Requires recent Emscripten as WGPU is still a work-in-progress API. + +--- + +## How to Run + +To run on a local machine: +- Make sure your browse supports WGPU and it is enabled. WGPU is still WIP not enabled by default in most browser. +- `make serve` will use Python3 to spawn a local webserver, you can then browse http://localhost:8000 to access your build. +- Otherwise, generally you will need a local webserver: + - Quoting [https://emscripten.org/docs/getting_started](https://emscripten.org/docs/getting_started/Tutorial.html#generating-html):
+_"Unfortunately several browsers (including Chrome, Safari, and Internet Explorer) do not support file:// [XHR](https://emscripten.org/docs/site/glossary.html#term-xhr) requests, and can’t load extra files needed by the HTML (like a .wasm file, or packaged file data as mentioned lower down). For these browsers you’ll need to serve the files using a [local webserver](https://emscripten.org/docs/getting_started/FAQ.html#faq-local-webserver) and then open http://localhost:8000/hello.html."_ + - Emscripten SDK has a handy `emrun` command: `emrun web/example_glfw_wgpu.html --browser firefox` which will spawn a temporary local webserver (in Firefox). See https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html for details. + - You may use Python 3 builtin webserver: `python -m http.server -d web` (this is what `make serve` uses). + - You may use Python 2 builtin webserver: `cd web && python -m SimpleHTTPServer`. + - If you are accessing the files over a network, certain browsers, such as Firefox, will restrict Gamepad API access to secure contexts only (e.g. https only). diff --git a/examples/example_sdl3_wgpu/main.cpp b/examples/example_sdl3_wgpu/main.cpp new file mode 100644 index 000000000..4aee4ef6a --- /dev/null +++ b/examples/example_sdl3_wgpu/main.cpp @@ -0,0 +1,538 @@ +// Dear ImGui: standalone example application for using SDL3 + WebGPU +// - Emscripten is supported for publishing on web. See https://emscripten.org. +// - Dawn is used as a WebGPU implementation on desktop. + +// 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 + + +#include "imgui.h" +#include "imgui_impl_sdl3.h" +#include "imgui_impl_wgpu.h" +#include +#include +#include + +// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details. +#ifdef __EMSCRIPTEN__ +#include +#include +#include "../libs/emscripten/emscripten_mainloop_stub.h" +#endif + +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) +#include +#endif + +// Data +static WGPUInstance wgpu_instance = nullptr; +static WGPUDevice wgpu_device = nullptr; +static WGPUSurface wgpu_surface = nullptr; +static WGPUQueue wgpu_queue = nullptr; +static WGPUSurfaceConfiguration wgpu_surface_configuration = {}; +static int wgpu_surface_width = 1280; +static int wgpu_surface_height = 800; + +// Forward declarations +static bool InitWGPU(SDL_Window* window); +WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, SDL_Window* window); + +static void ResizeSurface(int width, int height) +{ + wgpu_surface_configuration.width = wgpu_surface_width = width; + wgpu_surface_configuration.height = wgpu_surface_height = height; + wgpuSurfaceConfigure( wgpu_surface, (WGPUSurfaceConfiguration*)&wgpu_surface_configuration ); +} + +// Main code +int main(int, char**) +{ + // Setup SDL + // [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function] + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) + { + printf("Error: SDL_Init(): %s\n", SDL_GetError()); + return 1; + } + + // Create SDL window graphics context + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_WindowFlags window_flags = SDL_WINDOW_RESIZABLE; + wgpu_surface_width = (int)(wgpu_surface_width * main_scale); + wgpu_surface_height = (int)(wgpu_surface_height * main_scale); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+WebGPU example", wgpu_surface_width, wgpu_surface_height, window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return 1; + } + + // Initialize WGPU + InitWGPU(window); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForOther(window); + + ImGui_ImplWGPU_InitInfo init_info; + init_info.Device = wgpu_device; + init_info.NumFramesInFlight = 3; + init_info.RenderTargetFormat = wgpu_surface_configuration.format; + init_info.DepthStencilFormat = WGPUTextureFormat_Undefined; + ImGui_ImplWGPU_Init(&init_info); + + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); +#ifndef IMGUI_DISABLE_FILE_FUNCTIONS + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); +#endif + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; +#ifdef __EMSCRIPTEN__ + // For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file. + // You may manually call LoadIniSettingsFromMemory() to load settings from your own storage. + io.IniFilename = nullptr; + EMSCRIPTEN_MAINLOOP_BEGIN +#else + while (!done) +#endif + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + // [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function] + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL3_ProcessEvent(&event); + if (event.type == SDL_EVENT_QUIT) + done = true; + if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) + done = true; + } + + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function] + // React to changes in screen size + int width, height; + SDL_GetWindowSize(window, &width, &height); + if (width != wgpu_surface_width || height != wgpu_surface_height) + ResizeSurface(width, height); + + // Check surface status for error. If texture is not optimal, try to reconfigure the surface. + WGPUSurfaceTexture surface_texture; + wgpuSurfaceGetCurrentTexture(wgpu_surface, &surface_texture); + if (ImGui_ImplWGPU_IsSurfaceStatusError(surface_texture.status)) + { + fprintf(stderr, "Unrecoverable Surface Texture status=%#.8x\n", surface_texture.status); + abort(); + } + if (ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(surface_texture.status)) + { + if (surface_texture.texture) + wgpuTextureRelease(surface_texture.texture); + if (width > 0 && height > 0) + ResizeSurface(width, height); + continue; + } + + // Start the Dear ImGui frame + ImGui_ImplWGPU_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + + WGPUTextureViewDescriptor view_desc = {}; + view_desc.format = wgpu_surface_configuration.format; + view_desc.dimension = WGPUTextureViewDimension_2D; + view_desc.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED; + view_desc.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED; + view_desc.aspect = WGPUTextureAspect_All; + + WGPUTextureView texture_view = wgpuTextureCreateView(surface_texture.texture, &view_desc); + + WGPURenderPassColorAttachment color_attachments = {}; + color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; + color_attachments.loadOp = WGPULoadOp_Clear; + color_attachments.storeOp = WGPUStoreOp_Store; + color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w }; + color_attachments.view = texture_view; + + WGPURenderPassDescriptor render_pass_desc = {}; + render_pass_desc.colorAttachmentCount = 1; + render_pass_desc.colorAttachments = &color_attachments; + render_pass_desc.depthStencilAttachment = nullptr; + + WGPUCommandEncoderDescriptor enc_desc = {}; + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(wgpu_device, &enc_desc); + + WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc); + ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass); + wgpuRenderPassEncoderEnd(pass); + + WGPUCommandBufferDescriptor cmd_buffer_desc = {}; + WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc); + wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer); + +#ifndef __EMSCRIPTEN__ + wgpuSurfacePresent(wgpu_surface); + // Tick needs to be called in Dawn to display validation errors +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) + wgpuDeviceTick(wgpu_device); +#endif +#endif + wgpuTextureViewRelease(texture_view); + wgpuRenderPassEncoderRelease(pass); + wgpuCommandEncoderRelease(encoder); + wgpuCommandBufferRelease(cmd_buffer); + } +#ifdef __EMSCRIPTEN__ + EMSCRIPTEN_MAINLOOP_END; +#endif + + // Cleanup + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function] + ImGui_ImplWGPU_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + + wgpuSurfaceUnconfigure(wgpu_surface); + wgpuSurfaceRelease(wgpu_surface); + wgpuQueueRelease(wgpu_queue); + wgpuDeviceRelease(wgpu_device); + wgpuInstanceRelease(wgpu_instance); + + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} + +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) +static WGPUAdapter RequestAdapter(wgpu::Instance& instance) +{ + wgpu::Adapter acquired_adapter; + wgpu::RequestAdapterOptions adapter_options; + auto onRequestAdapter = [&](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message) + { + if (status != wgpu::RequestAdapterStatus::Success) + { + printf("Failed to get an adapter: %s\n", message.data); + return; + } + acquired_adapter = std::move(adapter); + }; + + // Synchronously (wait until) acquire Adapter + wgpu::Future waitAdapterFunc { instance.RequestAdapter(&adapter_options, wgpu::CallbackMode::WaitAnyOnly, onRequestAdapter) }; + wgpu::WaitStatus waitStatusAdapter = instance.WaitAny(waitAdapterFunc, UINT64_MAX); + IM_ASSERT(acquired_adapter != nullptr && waitStatusAdapter == wgpu::WaitStatus::Success && "Error on Adapter request"); + return acquired_adapter.MoveToCHandle(); +} + +static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter) +{ + // Set device callback functions + wgpu::DeviceDescriptor device_desc; + device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, + [](const wgpu::Device&, wgpu::DeviceLostReason type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetDeviceLostReasonName((WGPUDeviceLostReason)type), msg.data); } + ); + device_desc.SetUncapturedErrorCallback( + [](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetErrorTypeName((WGPUErrorType)type), msg.data); } + ); + + wgpu::Device acquired_device; + auto onRequestDevice = [&](wgpu::RequestDeviceStatus status, wgpu::Device local_device, wgpu::StringView message) + { + if (status != wgpu::RequestDeviceStatus::Success) + { + printf("Failed to get an device: %s\n", message.data); + return; + } + acquired_device = std::move(local_device); + }; + + // Synchronously (wait until) get Device + wgpu::Future waitDeviceFunc { adapter.RequestDevice(&device_desc, wgpu::CallbackMode::WaitAnyOnly, onRequestDevice) }; + wgpu::WaitStatus waitStatusDevice = instance.WaitAny(waitDeviceFunc, UINT64_MAX); + IM_ASSERT(acquired_device != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request"); + return acquired_device.MoveToCHandle(); +} +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) +static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2) +{ + IM_UNUSED(userdata2); + if (status == WGPURequestAdapterStatus_Success) + { + WGPUAdapter* extAdapter = (WGPUAdapter*)userdata1; + *extAdapter = adapter; + } + else + { + printf("Request_adapter status=%#.8x message=%.*s\n", status, (int)message.length, message.data); + } +} + +static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata1, void* userdata2) +{ + IM_UNUSED(userdata2); + if (status == WGPURequestDeviceStatus_Success) + { + WGPUDevice* extDevice = (WGPUDevice*)userdata1; + *extDevice = device; + } + else + { + printf("Request_device status=%#.8x message=%.*s\n", status, (int)message.length, message.data); + } +} + +static WGPUAdapter RequestAdapter(WGPUInstance& instance) +{ + WGPURequestAdapterOptions adapter_options = {}; + + WGPUAdapter local_adapter = nullptr; + WGPURequestAdapterCallbackInfo adapterCallbackInfo = {}; + adapterCallbackInfo.mode = WGPUCallbackMode_WaitAnyOnly; + adapterCallbackInfo.callback = handle_request_adapter; + adapterCallbackInfo.userdata1 = &local_adapter; + + WGPUFuture future = wgpuInstanceRequestAdapter(instance, &adapter_options, adapterCallbackInfo); + WGPUFutureWaitInfo waitInfo = { future, false }; + wgpuInstanceWaitAny(instance, 1, &waitInfo, ~0ull); + IM_ASSERT(local_adapter && "Error on Adapter request"); + return local_adapter; +} + +static WGPUDevice RequestDevice(WGPUInstance& instance, WGPUAdapter& adapter) +{ + WGPUDevice local_device = nullptr; + WGPURequestDeviceCallbackInfo deviceCallbackInfo = {}; + deviceCallbackInfo.mode = WGPUCallbackMode_WaitAnyOnly; + deviceCallbackInfo.callback = handle_request_device; + deviceCallbackInfo.userdata1 = &local_device; + WGPUFuture future = wgpuAdapterRequestDevice(adapter, nullptr, deviceCallbackInfo); + WGPUFutureWaitInfo waitInfo = { future, false }; + wgpuInstanceWaitAny(instance, 1, &waitInfo, ~0ull); + IM_ASSERT(local_device && "Error on Device request"); + return local_device; +} +#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU + +static bool InitWGPU(SDL_Window* window) +{ + WGPUTextureFormat preferred_fmt = WGPUTextureFormat_Undefined; // acquired from SurfaceCapabilities + + // Google DAWN backend: Adapter and Device acquisition, Surface creation +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) + wgpu::InstanceDescriptor instance_desc = {}; + static constexpr wgpu::InstanceFeatureName timedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny; + instance_desc.requiredFeatureCount = 1; + instance_desc.requiredFeatures = &timedWaitAny; + wgpu::Instance instance = wgpu::CreateInstance(&instance_desc); + + wgpu::Adapter adapter = RequestAdapter(instance); + ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter.Get()); + + wgpu_device = RequestDevice(instance, adapter); + + // Create the surface. +#ifdef __EMSCRIPTEN__ + wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvas_desc = {}; + canvas_desc.selector = "#canvas"; + + wgpu::SurfaceDescriptor surface_desc = {}; + surface_desc.nextInChain = &canvas_desc; + wgpu::Surface surface = instance.CreateSurface(&surface_desc); +#else + wgpu::Surface surface = CreateWGPUSurface(instance.Get(), window); +#endif + if (!surface) + return false; + + // Moving Dawn objects into WGPU handles + wgpu_instance = instance.MoveToCHandle(); + wgpu_surface = surface.MoveToCHandle(); + + WGPUSurfaceCapabilities surface_capabilities = {}; + wgpuSurfaceGetCapabilities(wgpu_surface, adapter.Get(), &surface_capabilities); + + preferred_fmt = surface_capabilities.formats[0]; + + // WGPU backend: Adapter and Device acquisition, Surface creation +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) + WGPUInstanceDescriptor instanceDesc = {}; + WGPUInstanceFeatureName timedWaitAny = WGPUInstanceFeatureName_TimedWaitAny; + instanceDesc.requiredFeatureCount = 1; + instanceDesc.requiredFeatures = &timedWaitAny; + wgpu_instance = wgpuCreateInstance(&instanceDesc); + +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) + wgpuSetLogCallback( + [](WGPULogLevel level, WGPUStringView msg, void* userdata) { fprintf(stderr, "%s: %.*s\n", ImGui_ImplWGPU_GetLogLevelName(level), (int)msg.length, msg.data); }, nullptr + ); + wgpuSetLogLevel(WGPULogLevel_Warn); +#endif + + WGPUAdapter adapter = RequestAdapter(wgpu_instance); + ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter); + + wgpu_device = RequestDevice(wgpu_instance, adapter); + + // Create the surface. + wgpu_surface = CreateWGPUSurface(wgpu_instance, window); + if (!wgpu_surface) + return false; + + WGPUSurfaceCapabilities surface_capabilities = {}; + wgpuSurfaceGetCapabilities(wgpu_surface, adapter, &surface_capabilities); + + preferred_fmt = surface_capabilities.formats[0]; +#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU + + wgpu_surface_configuration.presentMode = WGPUPresentMode_Fifo; + wgpu_surface_configuration.alphaMode = WGPUCompositeAlphaMode_Auto; + wgpu_surface_configuration.usage = WGPUTextureUsage_RenderAttachment; + wgpu_surface_configuration.width = wgpu_surface_width; + wgpu_surface_configuration.height = wgpu_surface_height; + wgpu_surface_configuration.device = wgpu_device; + wgpu_surface_configuration.format = preferred_fmt; + + wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration); + wgpu_queue = wgpuDeviceGetQueue(wgpu_device); + + return true; +} + +// SDL3 helper to create a WebGPU surface (exclusively!) for Native/Desktop applications: available only together with WebGPU/WGPU backend +// As of today (2025/10) there is no "official" support in SDL3 to create a surface for WebGPU backend +// This stub uses "low level" SDL3 calls to acquire information from a specific Window Manager. +// Currently supported platforms: Windows / Linux (X11 and Wayland) / MacOS. Not necessary nor available with EMSCRIPTEN. +#ifndef __EMSCRIPTEN__ + +#if defined(SDL_PLATFORM_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, SDL_Window* window) +{ + SDL_PropertiesID propertiesID = SDL_GetWindowProperties(window); + + ImGui_ImplWGPU_CreateSurfaceInfo create_info = {}; + create_info.Instance = instance; +#if defined(SDL_PLATFORM_MACOS) + { + create_info.System = "cocoa"; + create_info.RawWindow = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, NULL); + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#elif defined(SDL_PLATFORM_LINUX) + if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0) + { + create_info.System = "wayland"; + create_info.RawDisplay = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, NULL); + create_info.RawSurface = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL); + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } + else if (!SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11")) + { + create_info.System = "x11"; + create_info.RawWindow = (void*)SDL_GetNumberProperty(propertiesID, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); + create_info.RawDisplay = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL); + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#elif defined(SDL_PLATFORM_WIN32) + { + create_info.System = "win32"; + create_info.RawWindow = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); + create_info.RawInstance = (void*)::GetModuleHandle(NULL); + return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); + } +#else +#error "Unsupported WebGPU native platform!" + return nullptr; +#endif +} +#endif // #ifndef __EMSCRIPTEN__ diff --git a/examples/example_win32_directx10/main.cpp b/examples/example_win32_directx10/main.cpp index 21198da9a..2c3420709 100644 --- a/examples/example_win32_directx10/main.cpp +++ b/examples/example_win32_directx10/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for DirectX 10 +// Dear ImGui: standalone example application for Windows API + DirectX 10 // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq @@ -30,11 +30,14 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code int main(int, char**) { + // Make process DPI aware and obtain main monitor scale + ImGui_ImplWin32_EnableDpiAwareness(); + float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); + // Create application window - //ImGui_ImplWin32_EnableDpiAwareness(); WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr }; ::RegisterClassExW(&wc); - HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX10 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr); + HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX10 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr); // Initialize Direct3D if (!CreateDeviceD3D(hwnd)) @@ -59,24 +62,31 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX10_Init(g_pd3dDevice); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state @@ -190,6 +200,7 @@ int main(int, char**) bool CreateDeviceD3D(HWND hWnd) { // Setup swap chain + // This is a basic setup. Optimally could use handle fullscreen mode differently. See #8979 for suggestions. DXGI_SWAP_CHAIN_DESC sd; ZeroMemory(&sd, sizeof(sd)); sd.BufferCount = 2; diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 5285df102..524538ac1 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for DirectX 11 +// Dear ImGui: standalone example application for Windows API + DirectX 11 // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq @@ -30,11 +30,14 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code int main(int, char**) { + // Make process DPI aware and obtain main monitor scale + ImGui_ImplWin32_EnableDpiAwareness(); + float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); + // Create application window - //ImGui_ImplWin32_EnableDpiAwareness(); WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr }; ::RegisterClassExW(&wc); - HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr); + HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr); // Initialize Direct3D if (!CreateDeviceD3D(hwnd)) @@ -59,24 +62,31 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state @@ -190,6 +200,7 @@ int main(int, char**) bool CreateDeviceD3D(HWND hWnd) { // Setup swap chain + // This is a basic setup. Optimally could use e.g. DXGI_SWAP_EFFECT_FLIP_DISCARD and handle fullscreen mode differently. See #8979 for suggestions. DXGI_SWAP_CHAIN_DESC sd; ZeroMemory(&sd, sizeof(sd)); sd.BufferCount = 2; diff --git a/examples/example_win32_directx12/example_win32_directx12.vcxproj b/examples/example_win32_directx12/example_win32_directx12.vcxproj index bb98c4141..c4b81e76f 100644 --- a/examples/example_win32_directx12/example_win32_directx12.vcxproj +++ b/examples/example_win32_directx12/example_win32_directx12.vcxproj @@ -21,34 +21,34 @@ {b4cf9797-519d-4afe-a8f4-5141a6b521d3} example_win32_directx12 - 10.0.20348.0 + 10.0 Application true Unicode - v140 + v142 Application true Unicode - v140 + v142 Application false true Unicode - v140 + v142 Application false true Unicode - v140 + v142 diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 257410e2c..0d8bb20b1 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for DirectX 12 +// Dear ImGui: standalone example application for Windows API + DirectX 12 // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq @@ -10,7 +10,7 @@ #include "imgui_impl_win32.h" #include "imgui_impl_dx12.h" #include -#include +#include #include #ifdef _DEBUG @@ -23,8 +23,8 @@ #endif // Config for example app -static const int APP_NUM_FRAMES_IN_FLIGHT = 3; -static const int APP_NUM_BACK_BUFFERS = 3; +static const int APP_NUM_FRAMES_IN_FLIGHT = 2; +static const int APP_NUM_BACK_BUFFERS = 2; static const int APP_SRV_HEAP_SIZE = 64; struct FrameContext @@ -54,11 +54,11 @@ struct ExampleDescriptorHeapAllocator HeapHandleIncrement = device->GetDescriptorHandleIncrementSize(HeapType); FreeIndices.reserve((int)desc.NumDescriptors); for (int n = desc.NumDescriptors; n > 0; n--) - FreeIndices.push_back(n); + FreeIndices.push_back(n - 1); } void Destroy() { - Heap = NULL; + Heap = nullptr; FreeIndices.clear(); } void Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle) @@ -92,6 +92,7 @@ static ID3D12Fence* g_fence = nullptr; static HANDLE g_fenceEvent = nullptr; static UINT64 g_fenceLastSignaledValue = 0; static IDXGISwapChain3* g_pSwapChain = nullptr; +static bool g_SwapChainTearingSupport = false; static bool g_SwapChainOccluded = false; static HANDLE g_hSwapChainWaitableObject = nullptr; static ID3D12Resource* g_mainRenderTargetResource[APP_NUM_BACK_BUFFERS] = {}; @@ -102,18 +103,21 @@ bool CreateDeviceD3D(HWND hWnd); void CleanupDeviceD3D(); void CreateRenderTarget(); void CleanupRenderTarget(); -void WaitForLastSubmittedFrame(); -FrameContext* WaitForNextFrameResources(); +void WaitForPendingOperations(); +FrameContext* WaitForNextFrameContext(); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code int main(int, char**) { + // Make process DPI aware and obtain main monitor scale + ImGui_ImplWin32_EnableDpiAwareness(); + float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); + // Create application window - //ImGui_ImplWin32_EnableDpiAwareness(); WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr }; ::RegisterClassExW(&wc); - HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX12 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr); + HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX12 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr); // Initialize Direct3D if (!CreateDeviceD3D(hwnd)) @@ -138,6 +142,11 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); @@ -146,6 +155,7 @@ int main(int, char**) init_info.CommandQueue = g_pd3dCommandQueue; init_info.NumFramesInFlight = APP_NUM_FRAMES_IN_FLIGHT; init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + init_info.DSVFormat = DXGI_FORMAT_UNKNOWN; // Allocating SRV descriptors (for textures) is up to the application, so we provide callbacks. // (current version of the backend will only allocate one descriptor, future versions will need to allocate more) init_info.SrvDescriptorHeap = g_pd3dSrvDescHeap; @@ -153,20 +163,25 @@ int main(int, char**) init_info.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle) { return g_pd3dSrvDescHeapAlloc.Free(cpu_handle, gpu_handle); }; ImGui_ImplDX12_Init(&init_info); + // Before 1.91.6: our signature was using a single descriptor. From 1.92, specifying SrvDescriptorAllocFn/SrvDescriptorFreeFn will be required to benefit from new features. + //ImGui_ImplDX12_Init(g_pd3dDevice, APP_NUM_FRAMES_IN_FLIGHT, DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap, g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart()); + // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state @@ -192,7 +207,7 @@ int main(int, char**) break; // Handle window screen locked - if (g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED) + if ((g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED) || ::IsIconic(hwnd)) { ::Sleep(10); continue; @@ -244,7 +259,7 @@ int main(int, char**) // Rendering ImGui::Render(); - FrameContext* frameCtx = WaitForNextFrameResources(); + FrameContext* frameCtx = WaitForNextFrameContext(); UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex(); frameCtx->CommandAllocator->Reset(); @@ -270,19 +285,17 @@ int main(int, char**) g_pd3dCommandList->Close(); g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList); + g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue); + frameCtx->FenceValue = g_fenceLastSignaledValue; // Present HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync - //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync + //HRESULT hr = g_pSwapChain->Present(0, g_SwapChainTearingSupport ? DXGI_PRESENT_ALLOW_TEARING : 0); // Present without vsync g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED); - - UINT64 fenceValue = g_fenceLastSignaledValue + 1; - g_pd3dCommandQueue->Signal(g_fence, fenceValue); - g_fenceLastSignaledValue = fenceValue; - frameCtx->FenceValue = fenceValue; + g_frameIndex++; } - WaitForLastSubmittedFrame(); + WaitForPendingOperations(); // Cleanup ImGui_ImplDX12_Shutdown(); @@ -301,6 +314,7 @@ int main(int, char**) bool CreateDeviceD3D(HWND hWnd) { // Setup swap chain + // This is a basic setup. Optimally could handle fullscreen mode differently. See #8979 for suggestions. DXGI_SWAP_CHAIN_DESC1 sd; { ZeroMemory(&sd, sizeof(sd)); @@ -339,6 +353,15 @@ bool CreateDeviceD3D(HWND hWnd) pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true); + + // Disable breaking on this warning because of a suspected bug in the D3D12 SDK layer, see #9084 for details. + const int D3D12_MESSAGE_ID_FENCE_ZERO_WAIT_ = 1424; // not in all copies of d3d12sdklayers.h + D3D12_MESSAGE_ID disabledMessages[] = { (D3D12_MESSAGE_ID)D3D12_MESSAGE_ID_FENCE_ZERO_WAIT_ }; + D3D12_INFO_QUEUE_FILTER filter = {}; + filter.DenyList.NumIDs = 1; + filter.DenyList.pIDList = disabledMessages; + pInfoQueue->AddStorageFilterEntries(&filter); + pInfoQueue->Release(); pdx12Debug->Release(); } @@ -397,14 +420,24 @@ bool CreateDeviceD3D(HWND hWnd) return false; { - IDXGIFactory4* dxgiFactory = nullptr; + IDXGIFactory5* dxgiFactory = nullptr; IDXGISwapChain1* swapChain1 = nullptr; if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK) return false; + + BOOL allow_tearing = FALSE; + dxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing)); + g_SwapChainTearingSupport = (allow_tearing == TRUE); + if (g_SwapChainTearingSupport) + sd.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + if (dxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1) != S_OK) return false; if (swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain)) != S_OK) return false; + if (g_SwapChainTearingSupport) + dxgiFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER); + swapChain1->Release(); dxgiFactory->Release(); g_pSwapChain->SetMaximumFrameLatency(APP_NUM_BACK_BUFFERS); @@ -453,49 +486,33 @@ void CreateRenderTarget() void CleanupRenderTarget() { - WaitForLastSubmittedFrame(); + WaitForPendingOperations(); for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++) if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = nullptr; } } -void WaitForLastSubmittedFrame() +void WaitForPendingOperations() { - FrameContext* frameCtx = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT]; + g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue); - UINT64 fenceValue = frameCtx->FenceValue; - if (fenceValue == 0) - return; // No fence was signaled - - frameCtx->FenceValue = 0; - if (g_fence->GetCompletedValue() >= fenceValue) - return; - - g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent); - WaitForSingleObject(g_fenceEvent, INFINITE); + g_fence->SetEventOnCompletion(g_fenceLastSignaledValue, g_fenceEvent); + ::WaitForSingleObject(g_fenceEvent, INFINITE); } -FrameContext* WaitForNextFrameResources() +FrameContext* WaitForNextFrameContext() { - UINT nextFrameIndex = g_frameIndex + 1; - g_frameIndex = nextFrameIndex; - - HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, nullptr }; - DWORD numWaitableObjects = 1; - - FrameContext* frameCtx = &g_frameContext[nextFrameIndex % APP_NUM_FRAMES_IN_FLIGHT]; - UINT64 fenceValue = frameCtx->FenceValue; - if (fenceValue != 0) // means no fence was signaled + FrameContext* frame_context = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT]; + if (g_fence->GetCompletedValue() < frame_context->FenceValue) { - frameCtx->FenceValue = 0; - g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent); - waitableObjects[1] = g_fenceEvent; - numWaitableObjects = 2; + g_fence->SetEventOnCompletion(frame_context->FenceValue, g_fenceEvent); + HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, g_fenceEvent }; + ::WaitForMultipleObjects(2, waitableObjects, TRUE, INFINITE); } + else + ::WaitForSingleObject(g_hSwapChainWaitableObject, INFINITE); - WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE); - - return frameCtx; + return frame_context; } // Forward declare message handler from imgui_impl_win32.cpp @@ -516,10 +533,11 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_SIZE: if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED) { - WaitForLastSubmittedFrame(); CleanupRenderTarget(); - HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT); - assert(SUCCEEDED(result) && "Failed to resize swapchain."); + DXGI_SWAP_CHAIN_DESC1 desc = {}; + g_pSwapChain->GetDesc1(&desc); + HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), desc.Format, desc.Flags); + IM_ASSERT(SUCCEEDED(result) && "Failed to resize swapchain."); CreateRenderTarget(); } return 0; diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index 422248bcb..fbe4c3983 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for DirectX 9 +// Dear ImGui: standalone example application for Windows API + DirectX 9 // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq @@ -28,11 +28,14 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code int main(int, char**) { + // Make process DPI aware and obtain main monitor scale + ImGui_ImplWin32_EnableDpiAwareness(); + float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); + // Create application window - //ImGui_ImplWin32_EnableDpiAwareness(); WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr }; ::RegisterClassExW(&wc); - HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX9 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr); + HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX9 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr); // Initialize Direct3D if (!CreateDeviceD3D(hwnd)) @@ -57,24 +60,31 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX9_Init(g_pd3dDevice); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state diff --git a/examples/example_win32_opengl3/build_mingw.bat b/examples/example_win32_opengl3/build_mingw.bat index 767218585..e9f804f5d 100644 --- a/examples/example_win32_opengl3/build_mingw.bat +++ b/examples/example_win32_opengl3/build_mingw.bat @@ -1,4 +1,4 @@ -@REM Build for MINGW64 or 32 from MSYS2. +@REM Build for MINGW64 or 32 from MSYS2. @set OUT_DIR=Debug @set OUT_EXE=example_win32_opengl3 @set INCLUDES=-I../.. -I../../backends diff --git a/examples/example_win32_opengl3/main.cpp b/examples/example_win32_opengl3/main.cpp index 8ecd27a20..d18ea0bd8 100644 --- a/examples/example_win32_opengl3/main.cpp +++ b/examples/example_win32_opengl3/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for Win32 + OpenGL 3 +// Dear ImGui: standalone example application for Windows API + OpenGL // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq @@ -15,7 +15,7 @@ #define WIN32_LEAN_AND_MEAN #endif #include -#include +#include #include // Data stored per platform window @@ -36,11 +36,14 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code int main(int, char**) { + // Make process DPI aware and obtain main monitor scale + ImGui_ImplWin32_EnableDpiAwareness(); + float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); + // Create application window - //ImGui_ImplWin32_EnableDpiAwareness(); WNDCLASSEXW wc = { sizeof(wc), CS_OWNDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr }; ::RegisterClassExW(&wc); - HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui Win32+OpenGL3 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr); + HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui Win32+OpenGL3 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr); // Initialize OpenGL if (!CreateDeviceWGL(hwnd, &g_MainWindow)) @@ -67,24 +70,31 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + // Setup Platform/Renderer backends ImGui_ImplWin32_InitForOpenGL(hwnd); ImGui_ImplOpenGL3_Init(); // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state diff --git a/examples/example_win32_vulkan/build_win32.bat b/examples/example_win32_vulkan/build_win32.bat new file mode 100644 index 000000000..4bfb7cafc --- /dev/null +++ b/examples/example_win32_vulkan/build_win32.bat @@ -0,0 +1,9 @@ +@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. +@set OUT_DIR=Debug +@set OUT_EXE=example_win32_vulkan +@set INCLUDES=/I..\.. /I..\..\backends /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I %VULKAN_SDK%\include +@set SOURCES=main.cpp ..\..\backends\imgui_impl_vulkan.cpp ..\..\backends\imgui_impl_win32.cpp ..\..\imgui*.cpp +@set LIBS=/libpath:%VULKAN_SDK%\lib32 vulkan-1.lib + +mkdir %OUT_DIR% +cl /nologo /Zi /MD /utf-8 %INCLUDES% /D UNICODE /D _UNICODE %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% diff --git a/examples/example_win32_vulkan/build_win64.bat b/examples/example_win32_vulkan/build_win64.bat new file mode 100644 index 000000000..f8c66f799 --- /dev/null +++ b/examples/example_win32_vulkan/build_win64.bat @@ -0,0 +1,9 @@ +@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. +@set OUT_DIR=Debug +@set OUT_EXE=example_win32_vulkan +@set INCLUDES=/I..\.. /I..\..\backends /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I %VULKAN_SDK%\include +@set SOURCES=main.cpp ..\..\backends\imgui_impl_vulkan.cpp ..\..\backends\imgui_impl_win32.cpp ..\..\imgui*.cpp +@set LIBS=/libpath:%VULKAN_SDK%\lib vulkan-1.lib + +mkdir %OUT_DIR% +cl /nologo /Zi /MD /utf-8 %INCLUDES% /D UNICODE /D _UNICODE %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% diff --git a/examples/example_win32_vulkan/example_win32_vulkan.vcxproj b/examples/example_win32_vulkan/example_win32_vulkan.vcxproj new file mode 100644 index 000000000..dab8afd4e --- /dev/null +++ b/examples/example_win32_vulkan/example_win32_vulkan.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88} + example_win32_directx11 + + + + Application + true + Unicode + v140 + + + Application + true + Unicode + v140 + + + Application + false + true + Unicode + v140 + + + Application + false + true + Unicode + v140 + + + + + + + + + + + + + + + + + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + + + + Level4 + Disabled + ..\..;..\..\backends;%VULKAN_SDK%\include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions) + /utf-8 %(AdditionalOptions) + + + true + vulkan-1.lib;%(AdditionalDependencies) + %VULKAN_SDK%\lib32;%(AdditionalLibraryDirectories) + Console + + + + + Level4 + Disabled + ..\..;..\..\backends;%VULKAN_SDK%\include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions) + /utf-8 %(AdditionalOptions) + + + true + vulkan-1.lib;%(AdditionalDependencies) + %VULKAN_SDK%\lib;%(AdditionalLibraryDirectories) + Console + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%VULKAN_SDK%\include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions) + false + /utf-8 %(AdditionalOptions) + + + true + true + true + vulkan-1.lib;%(AdditionalDependencies) + %VULKAN_SDK%\lib32;%(AdditionalLibraryDirectories) + Console + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%VULKAN_SDK%\include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions) + /utf-8 %(AdditionalOptions) + + + true + true + true + vulkan-1.lib;%(AdditionalDependencies) + %VULKAN_SDK%\lib;%(AdditionalLibraryDirectories) + Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/example_win32_vulkan/example_win32_vulkan.vcxproj.filters b/examples/example_win32_vulkan/example_win32_vulkan.vcxproj.filters new file mode 100644 index 000000000..c91a95810 --- /dev/null +++ b/examples/example_win32_vulkan/example_win32_vulkan.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + {0587d7a3-f2ce-4d56-b84f-a0005d3bfce6} + + + {08e36723-ce4f-4cff-9662-c40801cf1acf} + + + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + + + imgui + + + sources + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + imgui + + + + + + imgui + + + imgui + + + \ No newline at end of file diff --git a/examples/example_win32_vulkan/main.cpp b/examples/example_win32_vulkan/main.cpp new file mode 100644 index 000000000..7f1ccd0c8 --- /dev/null +++ b/examples/example_win32_vulkan/main.cpp @@ -0,0 +1,569 @@ +// Dear ImGui: standalone example application for Windows API + Vulkan + +// 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. + +#include "imgui.h" +#include "imgui_impl_win32.h" +#define VK_USE_PLATFORM_WIN32_KHR +#include "imgui_impl_vulkan.h" +#include +#include // printf, fprintf +#include // abort +#include + +// Volk headers +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK +#define VOLK_IMPLEMENTATION +#include +#endif + +//#define APP_USE_UNLIMITED_FRAME_RATE +#ifdef _DEBUG +#define APP_USE_VULKAN_DEBUG_REPORT +#endif + +// Data +static VkAllocationCallbacks* g_Allocator = nullptr; +static VkInstance g_Instance = VK_NULL_HANDLE; +static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; +static VkDevice g_Device = VK_NULL_HANDLE; +static uint32_t g_QueueFamily = (uint32_t)-1; +static VkQueue g_Queue = VK_NULL_HANDLE; +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; +static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; +static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; + +static ImGui_ImplVulkanH_Window g_MainWindowData; +static uint32_t g_MinImageCount = 2; +static bool g_SwapChainRebuild = false; + +static void check_vk_result(VkResult err) +{ + if (err == VK_SUCCESS) + return; + fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); + if (err < 0) + abort(); +} + +#ifdef APP_USE_VULKAN_DEBUG_REPORT +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) +{ + (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments + fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); + return VK_FALSE; +} +#endif // APP_USE_VULKAN_DEBUG_REPORT + +static bool IsExtensionAvailable(const ImVector& properties, const char* extension) +{ + for (const VkExtensionProperties& p : properties) + if (strcmp(p.extensionName, extension) == 0) + return true; + return false; +} + +static void SetupVulkan(ImVector instance_extensions) +{ + VkResult err; +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK + volkInitialize(); +#endif + + // Create Vulkan Instance + { + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + + // Enumerate available extensions + uint32_t properties_count; + ImVector properties; + vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, nullptr); + properties.resize(properties_count); + err = vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, properties.Data); + check_vk_result(err); + + // Enable required extensions + if (IsExtensionAvailable(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) + instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); +#ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME + if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) + { + instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + } +#endif + + // Enabling validation layers +#ifdef APP_USE_VULKAN_DEBUG_REPORT + const char* layers[] = { "VK_LAYER_KHRONOS_validation" }; + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = layers; + instance_extensions.push_back("VK_EXT_debug_report"); +#endif + + // Create Vulkan Instance + create_info.enabledExtensionCount = (uint32_t)instance_extensions.Size; + create_info.ppEnabledExtensionNames = instance_extensions.Data; + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK + volkLoadInstance(g_Instance); +#endif + + // Setup the debug report callback +#ifdef APP_USE_VULKAN_DEBUG_REPORT + auto f_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(f_vkCreateDebugReportCallbackEXT != nullptr); + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; + debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debug_report_ci.pfnCallback = debug_report; + debug_report_ci.pUserData = nullptr; + err = f_vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); + check_vk_result(err); +#endif + } + + // Select Physical Device (GPU) + g_PhysicalDevice = ImGui_ImplVulkanH_SelectPhysicalDevice(g_Instance); + IM_ASSERT(g_PhysicalDevice != VK_NULL_HANDLE); + + // Select graphics queue family + g_QueueFamily = ImGui_ImplVulkanH_SelectQueueFamilyIndex(g_PhysicalDevice); + IM_ASSERT(g_QueueFamily != (uint32_t)-1); + + // Create Logical Device (with 1 queue) + { + ImVector device_extensions; + device_extensions.push_back("VK_KHR_swapchain"); + + // Enumerate physical device extension + uint32_t properties_count; + ImVector properties; + vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, nullptr); + properties.resize(properties_count); + vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, properties.Data); +#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME + if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) + device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); +#endif + + const float queue_priority[] = { 1.0f }; + VkDeviceQueueCreateInfo queue_info[1] = {}; + queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[0].queueFamilyIndex = g_QueueFamily; + queue_info[0].queueCount = 1; + queue_info[0].pQueuePriorities = queue_priority; + VkDeviceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); + create_info.pQueueCreateInfos = queue_info; + create_info.enabledExtensionCount = (uint32_t)device_extensions.Size; + create_info.ppEnabledExtensionNames = device_extensions.Data; + err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device); + check_vk_result(err); + vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); + } + + // Create Descriptor Pool + // If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets. + { + VkDescriptorPoolSize pool_sizes[] = + { + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE }, + }; + 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 = 0; + for (VkDescriptorPoolSize& pool_size : pool_sizes) + pool_info.maxSets += pool_size.descriptorCount; + pool_info.poolSizeCount = (uint32_t)IM_COUNTOF(pool_sizes); + pool_info.pPoolSizes = pool_sizes; + err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); + check_vk_result(err); + } +} + +// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo. +// Your real engine/app may not use them. +static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) +{ + // Check for WSI support + VkBool32 res; + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); + if (res != VK_TRUE) + { + fprintf(stderr, "Error no WSI support on physical device 0\n"); + exit(-1); + } + + // Select Surface Format + const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->Surface = surface; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); + + // Select Present Mode +#ifdef APP_USE_UNLIMITED_FRAME_RATE + VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; +#else + VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; +#endif + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_COUNTOF(present_modes)); + //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); + + // Create SwapChain, RenderPass, Framebuffer, etc. + IM_ASSERT(g_MinImageCount >= 2); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); +} + +static void CleanupVulkan() +{ + vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); + +#ifdef APP_USE_VULKAN_DEBUG_REPORT + // Remove the debug report callback + auto f_vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); + f_vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); +#endif // APP_USE_VULKAN_DEBUG_REPORT + + vkDestroyDevice(g_Device, g_Allocator); + vkDestroyInstance(g_Instance, g_Allocator); +} + +static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) +{ + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); +} + +static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) +{ + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) + return; + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); + + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; + { + err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking + check_vk_result(err); + + err = vkResetFences(g_Device, 1, &fd->Fence); + check_vk_result(err); + } + { + err = vkResetCommandPool(g_Device, fd->CommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(fd->CommandBuffer, &info); + check_vk_result(err); + } + { + VkRenderPassBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.renderPass = wd->RenderPass; + info.framebuffer = fd->Framebuffer; + info.renderArea.extent.width = wd->Width; + info.renderArea.extent.height = wd->Height; + info.clearValueCount = 1; + info.pClearValues = &wd->ClearValue; + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); + } + + // Record dear imgui primitives into command buffer + ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer); + + // Submit command buffer + vkCmdEndRenderPass(fd->CommandBuffer); + { + VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &image_acquired_semaphore; + info.pWaitDstStageMask = &wait_stage; + info.commandBufferCount = 1; + info.pCommandBuffers = &fd->CommandBuffer; + info.signalSemaphoreCount = 1; + info.pSignalSemaphores = &render_complete_semaphore; + + err = vkEndCommandBuffer(fd->CommandBuffer); + check_vk_result(err); + err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); + check_vk_result(err); + } +} + +static void FramePresent(ImGui_ImplVulkanH_Window* wd) +{ + if (g_SwapChainRebuild) + return; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkPresentInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &render_complete_semaphore; + info.swapchainCount = 1; + info.pSwapchains = &wd->Swapchain; + info.pImageIndices = &wd->FrameIndex; + VkResult err = vkQueuePresentKHR(g_Queue, &info); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) + return; + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); + wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores +} + +LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Main code +int main(int, char**) +{ + // Make process DPI aware and obtain main monitor scale + ImGui_ImplWin32_EnableDpiAwareness(); + float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); + + // Create application window + WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr }; + ::RegisterClassExW(&wc); + HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui Win32+Vulkan Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr); + + ImVector extensions; + extensions.push_back("VK_KHR_surface"); + extensions.push_back("VK_KHR_win32_surface"); + SetupVulkan(extensions); + + // Create Window Surface + VkSurfaceKHR surface; + VkResult err; + VkWin32SurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + createInfo.hwnd = hwnd; + createInfo.hinstance = ::GetModuleHandle(nullptr); + if (vkCreateWin32SurfaceKHR(g_Instance, &createInfo, nullptr, &surface) != VK_SUCCESS) + { + printf("Failed to create Vulkan surface.\n"); + return 1; + } + + // Show the window + // FIXME: Retrieve client size from window itself. + ImGui_ImplVulkanH_Window* wd = &g_MainWindowData; + SetupVulkanWindow(wd, surface, (int)(1280 * main_scale), (int)(800 * main_scale)); + ::ShowWindow(hwnd, SW_SHOWDEFAULT); + ::UpdateWindow(hwnd); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + + // Setup Platform/Renderer backends + ImGui_ImplWin32_Init(hwnd); + ImGui_ImplVulkan_InitInfo init_info = {}; + //init_info.ApiVersion = VK_API_VERSION_1_3; // Pass in your value of VkApplicationInfo::apiVersion, otherwise will default to header version. + init_info.Instance = g_Instance; + init_info.PhysicalDevice = g_PhysicalDevice; + init_info.Device = g_Device; + init_info.QueueFamily = g_QueueFamily; + init_info.Queue = g_Queue; + init_info.PipelineCache = g_PipelineCache; + init_info.DescriptorPool = g_DescriptorPool; + init_info.MinImageCount = g_MinImageCount; + init_info.ImageCount = wd->ImageCount; + init_info.Allocator = g_Allocator; + init_info.PipelineInfoMain.RenderPass = wd->RenderPass; + init_info.PipelineInfoMain.Subpass = 0; + init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + init_info.CheckVkResultFn = check_vk_result; + ImGui_ImplVulkan_Init(&init_info); + + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; + while (!done) + { + // Poll and handle messages (inputs, window resize, etc.) + // See the WndProc() function below for our to dispatch events to the Win32 backend. + MSG msg; + while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) + { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + if (msg.message == WM_QUIT) + done = true; + } + if (done) + break; + + // Start the Dear ImGui frame + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f); + if (!is_minimized) + { + wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; + wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; + wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; + wd->ClearValue.color.float32[3] = clear_color.w; + FrameRender(wd, draw_data); + FramePresent(wd); + } + } + + // Cleanup + err = vkDeviceWaitIdle(g_Device); + check_vk_result(err); + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplWin32_Shutdown(); + ImGui::DestroyContext(); + + CleanupVulkanWindow(&g_MainWindowData); + CleanupVulkan(); + + ::DestroyWindow(hwnd); + ::UnregisterClassW(wc.lpszClassName, wc.hInstance); + + return 0; +} + +// Helper functions + +// Forward declare message handler from imgui_impl_win32.cpp +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Win32 message handler +// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. +// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. +// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. +// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. +LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) + return true; + + switch (msg) + { + case WM_SIZE: + if (g_Device != VK_NULL_HANDLE && wParam != SIZE_MINIMIZED) + { + // Resize swap chain + int fb_width = (UINT)LOWORD(lParam); + int fb_height = (UINT)HIWORD(lParam); + if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) + { + ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0); + g_MainWindowData.FrameIndex = 0; + g_SwapChainRebuild = false; + } + } + return 0; + case WM_SYSCOMMAND: + if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu + return 0; + break; + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + return ::DefWindowProcW(hWnd, msg, wParam, lParam); +} diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index ea9f65cbd..96087aeac 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -35,6 +35,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_sdlrenderer3", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_vulkan", "example_sdl3_vulkan\example_sdl3_vulkan.vcxproj", "{663A7E89-1E42-4222-921C-177F5B5910DF}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_vulkan", "example_win32_vulkan\example_win32_vulkan.vcxproj", "{0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_sdlgpu3", "example_sdl3_sdlgpu3\example_sdl3_sdlgpu3.vcxproj", "{C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_directx11", "example_sdl3_directx11\example_sdl3_directx11.vcxproj", "{009DAC16-1A9C-47BE-9770-A30A046E8090}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_null", "example_null\example_null.vcxproj", "{1A0BF63C-18EF-4BAE-A8DA-055481B11F5D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -171,6 +179,38 @@ Global {663A7E89-1E42-4222-921C-177F5B5910DF}.Release|Win32.Build.0 = Release|Win32 {663A7E89-1E42-4222-921C-177F5B5910DF}.Release|x64.ActiveCfg = Release|x64 {663A7E89-1E42-4222-921C-177F5B5910DF}.Release|x64.Build.0 = Release|x64 + {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88}.Debug|Win32.ActiveCfg = Debug|Win32 + {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88}.Debug|Win32.Build.0 = Debug|Win32 + {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88}.Debug|x64.ActiveCfg = Debug|x64 + {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88}.Debug|x64.Build.0 = Debug|x64 + {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88}.Release|Win32.ActiveCfg = Release|Win32 + {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88}.Release|Win32.Build.0 = Release|Win32 + {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88}.Release|x64.ActiveCfg = Release|x64 + {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88}.Release|x64.Build.0 = Release|x64 + {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Debug|Win32.ActiveCfg = Debug|Win32 + {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Debug|Win32.Build.0 = Debug|Win32 + {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Debug|x64.ActiveCfg = Debug|x64 + {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Debug|x64.Build.0 = Debug|x64 + {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Release|Win32.ActiveCfg = Release|Win32 + {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Release|Win32.Build.0 = Release|Win32 + {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Release|x64.ActiveCfg = Release|x64 + {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Release|x64.Build.0 = Release|x64 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Debug|Win32.ActiveCfg = Debug|Win32 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Debug|Win32.Build.0 = Debug|Win32 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Debug|x64.ActiveCfg = Debug|x64 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Debug|x64.Build.0 = Debug|x64 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Release|Win32.ActiveCfg = Release|Win32 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Release|Win32.Build.0 = Release|Win32 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Release|x64.ActiveCfg = Release|x64 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Release|x64.Build.0 = Release|x64 + {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D}.Debug|Win32.ActiveCfg = Debug|Win32 + {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D}.Debug|Win32.Build.0 = Debug|Win32 + {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D}.Debug|x64.ActiveCfg = Debug|x64 + {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D}.Debug|x64.Build.0 = Debug|x64 + {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D}.Release|Win32.ActiveCfg = Release|Win32 + {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D}.Release|Win32.Build.0 = Release|Win32 + {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D}.Release|x64.ActiveCfg = Release|x64 + {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/libs/emscripten/shell_minimal.html b/examples/libs/emscripten/shell_minimal.html index bcf626261..939dc1779 100644 --- a/examples/libs/emscripten/shell_minimal.html +++ b/examples/libs/emscripten/shell_minimal.html @@ -5,7 +5,7 @@ Dear ImGui Emscripten example - +