Compare commits
2 Commits
slomp/webg
...
slomp/seq-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7046736d9 | ||
|
|
ddd74323bf |
4
.github/actions/test-tracy/action.yml
vendored
@@ -24,8 +24,8 @@ runs:
|
|||||||
cmake --build tests/tracy/build --parallel
|
cmake --build tests/tracy/build --parallel
|
||||||
cmake -E rm -rf tests/tracy/build
|
cmake -E rm -rf tests/tracy/build
|
||||||
|
|
||||||
# same with TRACY_MANUAL_LIFETIME
|
# same with TRACY_DELAYED_INIT and TRACY_MANUAL_LIFETIME
|
||||||
cmake -B tests/tracy/build -S tests/tracy -DCMAKE_BUILD_TYPE=Release -DTRACY_MANUAL_LIFETIME=ON ${{ inputs.extra_cmake_flags }}
|
cmake -B tests/tracy/build -S tests/tracy -DCMAKE_BUILD_TYPE=Release -DTRACY_DELAYED_INIT=ON -DTRACY_MANUAL_LIFETIME=ON ${{ inputs.extra_cmake_flags }}
|
||||||
cmake --build tests/tracy/build --parallel
|
cmake --build tests/tracy/build --parallel
|
||||||
cmake -E rm -rf tests/tracy/build
|
cmake -E rm -rf tests/tracy/build
|
||||||
|
|
||||||
|
|||||||
49
.github/workflows/android.yml
vendored
@@ -1,49 +0,0 @@
|
|||||||
name: android
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: android-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-android:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: archlinux:base-devel
|
|
||||||
steps:
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pacman -Syu --noconfirm && pacman -S --noconfirm --needed cmake git ninja nodejs unzip
|
|
||||||
- name: Setup NDK
|
|
||||||
uses: nttld/setup-ndk@v1
|
|
||||||
id: setup-ndk
|
|
||||||
with:
|
|
||||||
ndk-version: r29
|
|
||||||
- run: echo "ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }}" >> $GITHUB_ENV
|
|
||||||
- name: Trust git repo
|
|
||||||
run: git config --global --add safe.directory '*'
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Cache CPM packages
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.CPM_SOURCE_CACHE }}
|
|
||||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
|
||||||
restore-keys: ${{ runner.os }}-cpm-
|
|
||||||
- name: Library v7
|
|
||||||
run: |
|
|
||||||
cmake -B build-v7 -DCMAKE_BUILD_TYPE=Release -DTRACY_ENABLE=ON -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a
|
|
||||||
cmake --build build-v7
|
|
||||||
- name: Library v8
|
|
||||||
run: |
|
|
||||||
cmake -B build-v8 -DCMAKE_BUILD_TYPE=Release -DTRACY_ENABLE=ON -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a
|
|
||||||
cmake --build build-v8
|
|
||||||
61
.github/workflows/appimage.yml
vendored
@@ -1,61 +0,0 @@
|
|||||||
name: appimage
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
workflow_dispatch:
|
|
||||||
workflow_call:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: appimage-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
|
||||||
PANDOC_CACHE_DIR: ${{ github.workspace }}/pandoc-cache
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-appimage:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: ubuntu:24.04
|
|
||||||
steps:
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
apt-get update
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
||||||
build-essential cmake ninja-build meson pkg-config git ca-certificates \
|
|
||||||
curl file desktop-file-utils nodejs patchelf \
|
|
||||||
libegl-dev libxkbcommon-dev libdbus-1-dev libssl-dev \
|
|
||||||
libffi-dev libexpat1-dev wayland-protocols
|
|
||||||
- name: Trust git repo
|
|
||||||
run: git config --global --add safe.directory '*'
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Cache CPM packages
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.CPM_SOURCE_CACHE }}
|
|
||||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
|
||||||
restore-keys: ${{ runner.os }}-cpm-
|
|
||||||
- name: Cache pandoc
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.PANDOC_CACHE_DIR }}
|
|
||||||
key: ${{ runner.os }}-pandoc-${{ hashFiles('cmake/manual.cmake') }}
|
|
||||||
- name: Cache wayland
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: profiler/build-appimage/wayland
|
|
||||||
key: ${{ runner.os }}-wayland-${{ hashFiles('profiler/appimage/build.sh') }}
|
|
||||||
- name: Build AppImage
|
|
||||||
run: profiler/appimage/build.sh
|
|
||||||
env:
|
|
||||||
GIT_REV: ${{ github.sha }}
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: appimage
|
|
||||||
path: profiler/build-appimage/tracy-profiler-x86_64.AppImage
|
|
||||||
17
.github/workflows/emscripten.yml
vendored
@@ -7,16 +7,8 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: emscripten-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
||||||
PANDOC_CACHE_DIR: ${{ github.workspace }}/pandoc-cache
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-emscripten:
|
build-emscripten:
|
||||||
@@ -28,7 +20,7 @@ jobs:
|
|||||||
- name: Setup emscripten
|
- name: Setup emscripten
|
||||||
uses: emscripten-core/setup-emsdk@v16
|
uses: emscripten-core/setup-emsdk@v16
|
||||||
with:
|
with:
|
||||||
version: 6.0.9
|
version: 4.0.10
|
||||||
- name: Trust git repo
|
- name: Trust git repo
|
||||||
run: git config --global --add safe.directory '*'
|
run: git config --global --add safe.directory '*'
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -38,11 +30,6 @@ jobs:
|
|||||||
path: ${{ env.CPM_SOURCE_CACHE }}
|
path: ${{ env.CPM_SOURCE_CACHE }}
|
||||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
||||||
restore-keys: ${{ runner.os }}-cpm-
|
restore-keys: ${{ runner.os }}-cpm-
|
||||||
- name: Cache pandoc
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.PANDOC_CACHE_DIR }}
|
|
||||||
key: ${{ runner.os }}-pandoc-${{ hashFiles('cmake/manual.cmake') }}
|
|
||||||
- name: Profiler GUI
|
- name: Profiler GUI
|
||||||
run: |
|
run: |
|
||||||
cmake -G Ninja -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=MinSizeRel -DGIT_REV=${{ github.sha }} -DCMAKE_TOOLCHAIN_FILE=${{env.EMSDK}}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
|
cmake -G Ninja -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=MinSizeRel -DGIT_REV=${{ github.sha }} -DCMAKE_TOOLCHAIN_FILE=${{env.EMSDK}}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
|
||||||
@@ -57,7 +44,7 @@ jobs:
|
|||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
cp profiler/build/index.html bin
|
cp profiler/build/index.html bin
|
||||||
cp profiler/build/favicon.svg bin
|
cp profiler/build/favicon.svg bin
|
||||||
cp profiler/build/embed.tracy bin
|
cp profiler/build/tracy-profiler.data bin
|
||||||
cp profiler/build/tracy-profiler.js.gz bin
|
cp profiler/build/tracy-profiler.js.gz bin
|
||||||
cp profiler/build/tracy-profiler.js.zst bin
|
cp profiler/build/tracy-profiler.js.zst bin
|
||||||
cp profiler/build/tracy-profiler.wasm.gz bin
|
cp profiler/build/tracy-profiler.wasm.gz bin
|
||||||
|
|||||||
7
.github/workflows/latex.yml
vendored
@@ -8,13 +8,6 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: latex-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-manual:
|
build-manual:
|
||||||
|
|
||||||
|
|||||||
79
.github/workflows/linux-cli.yml
vendored
@@ -1,79 +0,0 @@
|
|||||||
name: linux-cli
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
workflow_dispatch:
|
|
||||||
workflow_call:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: linux-cli-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-linux-cli:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: ubuntu:24.04
|
|
||||||
steps:
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
apt-get update
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
||||||
build-essential cmake git ca-certificates curl nodejs
|
|
||||||
- name: Trust git repo
|
|
||||||
run: git config --global --add safe.directory '*'
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Cache CPM packages
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.CPM_SOURCE_CACHE }}
|
|
||||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
|
||||||
restore-keys: ${{ runner.os }}-cpm-
|
|
||||||
- name: Build update
|
|
||||||
run: |
|
|
||||||
cmake -B update/build -S update -DCMAKE_BUILD_TYPE=Release -DNO_ISA_EXTENSIONS=ON -DGIT_REV=${{ github.sha }}
|
|
||||||
cmake --build update/build --parallel
|
|
||||||
- name: Build capture
|
|
||||||
run: |
|
|
||||||
cmake -B capture/build -S capture -DCMAKE_BUILD_TYPE=Release -DNO_ISA_EXTENSIONS=ON -DGIT_REV=${{ github.sha }}
|
|
||||||
cmake --build capture/build --parallel
|
|
||||||
- name: Build csvexport
|
|
||||||
run: |
|
|
||||||
cmake -B csvexport/build -S csvexport -DCMAKE_BUILD_TYPE=Release -DNO_ISA_EXTENSIONS=ON -DGIT_REV=${{ github.sha }}
|
|
||||||
cmake --build csvexport/build --parallel
|
|
||||||
- name: Build import
|
|
||||||
run: |
|
|
||||||
cmake -B import/build -S import -DCMAKE_BUILD_TYPE=Release -DNO_ISA_EXTENSIONS=ON -DGIT_REV=${{ github.sha }}
|
|
||||||
cmake --build import/build --parallel
|
|
||||||
- name: Build merge
|
|
||||||
run: |
|
|
||||||
cmake -B merge/build -S merge -DCMAKE_BUILD_TYPE=Release -DNO_ISA_EXTENSIONS=ON -DGIT_REV=${{ github.sha }}
|
|
||||||
cmake --build merge/build --parallel
|
|
||||||
- name: Build monitor
|
|
||||||
run: |
|
|
||||||
cmake -B monitor/build -S monitor -DCMAKE_BUILD_TYPE=Release -DNO_ISA_EXTENSIONS=ON -DGIT_REV=${{ github.sha }}
|
|
||||||
cmake --build monitor/build --parallel
|
|
||||||
- name: Stage release files
|
|
||||||
run: |
|
|
||||||
mkdir -p linux-cli
|
|
||||||
cp update/build/tracy-update linux-cli/
|
|
||||||
cp capture/build/tracy-capture linux-cli/
|
|
||||||
cp capture/build/tracy-capture-daemon linux-cli/
|
|
||||||
cp csvexport/build/tracy-csvexport linux-cli/
|
|
||||||
cp import/build/tracy-import-chrome linux-cli/
|
|
||||||
cp import/build/tracy-import-fuchsia linux-cli/
|
|
||||||
cp merge/build/tracy-merge linux-cli/
|
|
||||||
cp monitor/build/tracy-monitor linux-cli/
|
|
||||||
strip linux-cli/tracy-*
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: linux-cli
|
|
||||||
path: linux-cli
|
|
||||||
55
.github/workflows/linux.yml
vendored
@@ -7,16 +7,8 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: linux-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
||||||
PANDOC_CACHE_DIR: ${{ github.workspace }}/pandoc-cache
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-linux:
|
build-linux:
|
||||||
@@ -24,7 +16,7 @@ jobs:
|
|||||||
container: archlinux:base-devel
|
container: archlinux:base-devel
|
||||||
steps:
|
steps:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pacman -Syu --noconfirm && pacman -S --noconfirm --needed freetype2 debuginfod wayland dbus libxkbcommon libglvnd meson cmake git wayland-protocols nodejs lua clang sdl3_image python-jinja
|
run: pacman -Syu --noconfirm && pacman -S --noconfirm --needed freetype2 debuginfod wayland dbus libxkbcommon libglvnd meson cmake git wayland-protocols nodejs lua
|
||||||
- name: Trust git repo
|
- name: Trust git repo
|
||||||
run: git config --global --add safe.directory '*'
|
run: git config --global --add safe.directory '*'
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -34,58 +26,39 @@ jobs:
|
|||||||
path: ${{ env.CPM_SOURCE_CACHE }}
|
path: ${{ env.CPM_SOURCE_CACHE }}
|
||||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
||||||
restore-keys: ${{ runner.os }}-cpm-
|
restore-keys: ${{ runner.os }}-cpm-
|
||||||
- name: Cache pandoc
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.PANDOC_CACHE_DIR }}
|
|
||||||
key: ${{ runner.os }}-pandoc-${{ hashFiles('cmake/manual.cmake') }}
|
|
||||||
- name: Determine build parallelism
|
|
||||||
run: |
|
|
||||||
if [ "${ACT:-}" != "true" ] && [ "${FORGEJO_ACTIONS:-}" != "true" ]; then
|
|
||||||
echo "CMAKE_PARALLEL=--parallel 2" >> "$GITHUB_ENV"
|
|
||||||
else
|
|
||||||
echo "CMAKE_PARALLEL=--parallel" >> "$GITHUB_ENV"
|
|
||||||
fi
|
|
||||||
- name: Dyna example / clang
|
|
||||||
run: |
|
|
||||||
CC=clang CXX=clang++ cmake -B examples/dyna/build-clang -S examples/dyna -DCMAKE_BUILD_TYPE=Release
|
|
||||||
cmake --build examples/dyna/build-clang $CMAKE_PARALLEL
|
|
||||||
- name: Dyna example / gcc
|
|
||||||
run: |
|
|
||||||
CC=gcc CXX=g++ cmake -B examples/dyna/build-gcc -S examples/dyna -DCMAKE_BUILD_TYPE=Release
|
|
||||||
cmake --build examples/dyna/build-gcc $CMAKE_PARALLEL
|
|
||||||
- name: Profiler GUI
|
- name: Profiler GUI
|
||||||
run: |
|
run: |
|
||||||
cmake -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build profiler/build $CMAKE_PARALLEL
|
if [ "${ACT:-}" != "true" ] && [ "${FORGEJO_ACTIONS:-}" != "true" ]; then
|
||||||
|
cmake --build profiler/build
|
||||||
|
else
|
||||||
|
cmake --build profiler/build --parallel
|
||||||
|
fi
|
||||||
- name: Update utility
|
- name: Update utility
|
||||||
run: |
|
run: |
|
||||||
cmake -B update/build -S update -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B update/build -S update -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build update/build $CMAKE_PARALLEL
|
cmake --build update/build --parallel
|
||||||
- name: Capture utility
|
- name: Capture utility
|
||||||
run: |
|
run: |
|
||||||
cmake -B capture/build -S capture -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B capture/build -S capture -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build capture/build $CMAKE_PARALLEL
|
cmake --build capture/build --parallel
|
||||||
- name: Csvexport utility
|
- name: Csvexport utility
|
||||||
run: |
|
run: |
|
||||||
cmake -B csvexport/build -S csvexport -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B csvexport/build -S csvexport -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build csvexport/build $CMAKE_PARALLEL
|
cmake --build csvexport/build --parallel
|
||||||
- name: Import utilities
|
- name: Import utilities
|
||||||
run: |
|
run: |
|
||||||
cmake -B import/build -S import -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B import/build -S import -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build import/build $CMAKE_PARALLEL
|
cmake --build import/build --parallel
|
||||||
- name: Merge utility
|
- name: Merge utility
|
||||||
run: |
|
run: |
|
||||||
cmake -B merge/build -S merge -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B merge/build -S merge -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build merge/build $CMAKE_PARALLEL
|
cmake --build merge/build --parallel
|
||||||
- name: Monitor utility
|
|
||||||
run: |
|
|
||||||
cmake -B monitor/build -S monitor -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
|
||||||
cmake --build monitor/build $CMAKE_PARALLEL
|
|
||||||
- name: Library (cmake)
|
- name: Library (cmake)
|
||||||
run: |
|
run: |
|
||||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DTRACY_ENABLE=ON
|
cmake -B build -DCMAKE_BUILD_TYPE=Release -DTRACY_ENABLE=ON
|
||||||
cmake --build build $CMAKE_PARALLEL
|
cmake --build build
|
||||||
|
cmake --install build
|
||||||
env:
|
env:
|
||||||
CMAKE_INSTALL_PREFIX: ${{ github.workspace }}/bin
|
CMAKE_INSTALL_PREFIX: ${{ github.workspace }}/bin
|
||||||
- name: Library (meson)
|
- name: Library (meson)
|
||||||
@@ -101,12 +74,10 @@ jobs:
|
|||||||
cp profiler/build/tracy-profiler bin
|
cp profiler/build/tracy-profiler bin
|
||||||
cp update/build/tracy-update bin
|
cp update/build/tracy-update bin
|
||||||
cp capture/build/tracy-capture bin
|
cp capture/build/tracy-capture bin
|
||||||
cp capture/build/tracy-capture-daemon bin
|
|
||||||
cp csvexport/build/tracy-csvexport bin
|
cp csvexport/build/tracy-csvexport bin
|
||||||
cp import/build/tracy-import-chrome bin
|
cp import/build/tracy-import-chrome bin
|
||||||
cp import/build/tracy-import-fuchsia bin
|
cp import/build/tracy-import-fuchsia bin
|
||||||
cp merge/build/tracy-merge bin
|
cp merge/build/tracy-merge bin
|
||||||
cp monitor/build/tracy-monitor bin
|
|
||||||
strip bin/tracy-*
|
strip bin/tracy-*
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
35
.github/workflows/macos.yml
vendored
@@ -6,22 +6,13 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
workflow_call:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: macos-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
||||||
PANDOC_CACHE_DIR: ${{ github.workspace }}/pandoc-cache
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-macos:
|
build-macos:
|
||||||
runs-on: macos-latest
|
runs-on: macos-15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Cache CPM packages
|
- name: Cache CPM packages
|
||||||
@@ -30,23 +21,14 @@ jobs:
|
|||||||
path: ${{ env.CPM_SOURCE_CACHE }}
|
path: ${{ env.CPM_SOURCE_CACHE }}
|
||||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
||||||
restore-keys: ${{ runner.os }}-cpm-
|
restore-keys: ${{ runner.os }}-cpm-
|
||||||
- name: Cache pandoc
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.PANDOC_CACHE_DIR }}
|
|
||||||
key: ${{ runner.os }}-pandoc-${{ hashFiles('cmake/manual.cmake') }}
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: brew install pkg-config meson
|
run: brew install pkg-config glfw meson
|
||||||
- name: Trust git repo
|
- name: Trust git repo
|
||||||
run: git config --global --add safe.directory '*'
|
run: git config --global --add safe.directory '*'
|
||||||
- name: Build library
|
|
||||||
run: meson setup -Dtracy_enable=true build && meson compile -C build
|
|
||||||
- name: Test application
|
|
||||||
uses: ./.github/actions/test-tracy
|
|
||||||
- name: Build profiler
|
- name: Build profiler
|
||||||
run: |
|
run: |
|
||||||
cmake -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=Release -DBUNDLE=ON -DGIT_REV=${{ github.sha }}
|
cmake -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build profiler/build --parallel 2 --config Release
|
cmake --build profiler/build --parallel --config Release
|
||||||
- name: Build update
|
- name: Build update
|
||||||
run: |
|
run: |
|
||||||
cmake -B update/build -S update -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B update/build -S update -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
@@ -67,19 +49,20 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake -B merge/build -S merge -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B merge/build -S merge -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build merge/build --parallel --config Release
|
cmake --build merge/build --parallel --config Release
|
||||||
|
- name: Build library
|
||||||
|
run: meson setup -Dprefix=$GITHUB_WORKSPACE/bin/lib -Dtracy_enable=true build && meson compile -C build && meson install -C build
|
||||||
|
- name: Test application
|
||||||
|
uses: ./.github/actions/test-tracy
|
||||||
- name: Package artifacts
|
- name: Package artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
cp -r profiler/build/tracy-profiler.app bin
|
cp profiler/build/tracy-profiler bin
|
||||||
cp update/build/tracy-update bin
|
cp update/build/tracy-update bin
|
||||||
cp capture/build/tracy-capture bin
|
cp capture/build/tracy-capture bin
|
||||||
cp capture/build/tracy-capture-daemon bin
|
|
||||||
cp csvexport/build/tracy-csvexport bin
|
cp csvexport/build/tracy-csvexport bin
|
||||||
cp import/build/tracy-import-chrome bin
|
cp import/build/tracy-import-chrome bin
|
||||||
cp import/build/tracy-import-fuchsia bin
|
cp import/build/tracy-import-fuchsia bin
|
||||||
cp merge/build/tracy-merge bin
|
cp merge/build/tracy-merge bin
|
||||||
strip bin/tracy-profiler.app/Contents/MacOS/tracy-profiler
|
|
||||||
strip bin/tracy-update bin/tracy-capture bin/tracy-capture-daemon bin/tracy-csvexport bin/tracy-import-chrome bin/tracy-import-fuchsia bin/tracy-merge
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: macos
|
name: macos
|
||||||
|
|||||||
7
.github/workflows/mingw.yml
vendored
@@ -7,13 +7,6 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: mingw-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
||||||
|
|
||||||
|
|||||||
40
.github/workflows/release.yml
vendored
@@ -4,62 +4,26 @@ on:
|
|||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: release-${{ github.event.release.tag_name }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release-build-windows:
|
release-build-windows:
|
||||||
uses: ./.github/workflows/windows.yml
|
uses: ./.github/workflows/windows.yml
|
||||||
|
|
||||||
release-build-macos:
|
|
||||||
uses: ./.github/workflows/macos.yml
|
|
||||||
|
|
||||||
release-build-manual:
|
release-build-manual:
|
||||||
uses: ./.github/workflows/latex.yml
|
uses: ./.github/workflows/latex.yml
|
||||||
|
|
||||||
release-build-appimage:
|
|
||||||
uses: ./.github/workflows/appimage.yml
|
|
||||||
|
|
||||||
release-build-linux-cli:
|
|
||||||
uses: ./.github/workflows/linux-cli.yml
|
|
||||||
|
|
||||||
attach-to-release:
|
attach-to-release:
|
||||||
needs: [release-build-windows, release-build-macos, release-build-manual, release-build-appimage, release-build-linux-cli]
|
needs: [release-build-windows, release-build-manual]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
- name: Create versioned Windows zip
|
- name: Create versioned zip
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ github.event.release.tag_name }}"
|
VERSION="${{ github.event.release.tag_name }}"
|
||||||
VERSION_NO_V="${VERSION#v}"
|
VERSION_NO_V="${VERSION#v}"
|
||||||
cd windows
|
cd windows
|
||||||
zip -r ../windows-$VERSION_NO_V.zip .
|
zip -r ../windows-$VERSION_NO_V.zip .
|
||||||
- name: Create versioned macOS zip
|
|
||||||
run: |
|
|
||||||
VERSION="${{ github.event.release.tag_name }}"
|
|
||||||
VERSION_NO_V="${VERSION#v}"
|
|
||||||
cd macos
|
|
||||||
chmod +x tracy-* tracy-profiler.app/Contents/MacOS/*
|
|
||||||
zip -r ../macos-$VERSION_NO_V.zip .
|
|
||||||
- name: Create versioned Linux zip
|
|
||||||
run: |
|
|
||||||
VERSION="${{ github.event.release.tag_name }}"
|
|
||||||
VERSION_NO_V="${VERSION#v}"
|
|
||||||
mkdir linux
|
|
||||||
cp appimage/* linux/
|
|
||||||
cp linux-cli/* linux/
|
|
||||||
cd linux
|
|
||||||
zip -r ../linux-$VERSION_NO_V.zip .
|
|
||||||
- uses: softprops/action-gh-release@v2
|
- uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
windows-*.zip
|
windows-*.zip
|
||||||
macos-*.zip
|
|
||||||
linux-*.zip
|
|
||||||
manual/tracy.pdf
|
manual/tracy.pdf
|
||||||
|
|||||||
36
.github/workflows/windows.yml
vendored
@@ -8,16 +8,8 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: windows-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm-cache
|
||||||
PANDOC_CACHE_DIR: ${{ github.workspace }}/pandoc-cache
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build-windows:
|
||||||
@@ -30,32 +22,17 @@ jobs:
|
|||||||
path: ${{ env.CPM_SOURCE_CACHE }}
|
path: ${{ env.CPM_SOURCE_CACHE }}
|
||||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
key: ${{ runner.os }}-cpm-${{ hashFiles('**/vendor.cmake', '**/CMakeLists.txt') }}
|
||||||
restore-keys: ${{ runner.os }}-cpm-
|
restore-keys: ${{ runner.os }}-cpm-
|
||||||
- name: Cache pandoc
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.PANDOC_CACHE_DIR }}
|
|
||||||
key: ${{ runner.os }}-pandoc-${{ hashFiles('cmake/manual.cmake') }}
|
|
||||||
- uses: microsoft/setup-msbuild@v2
|
- uses: microsoft/setup-msbuild@v2
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- run: pip install meson ninja
|
||||||
- name: Trust git repo
|
- name: Trust git repo
|
||||||
run: git config --global --add safe.directory '*'
|
run: git config --global --add safe.directory '*'
|
||||||
- name: Install meson
|
|
||||||
run: python -m pip install --upgrade meson
|
|
||||||
- name: Library (meson)
|
|
||||||
env:
|
|
||||||
CCACHE_DISABLE: '1'
|
|
||||||
run: |
|
|
||||||
$vs = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
|
||||||
foreach ($line in & cmd /c "`"$vs\VC\Auxiliary\Build\vcvarsall.bat`" x64 && set") {
|
|
||||||
if ($line -match '^([^=]+)=(.*)$') { [Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process') }
|
|
||||||
}
|
|
||||||
meson setup -Dtracy_enable=true build-meson
|
|
||||||
meson compile -C build-meson
|
|
||||||
- name: Test application
|
|
||||||
uses: ./.github/actions/test-tracy
|
|
||||||
- name: Build profiler
|
- name: Build profiler
|
||||||
run: |
|
run: |
|
||||||
cmake -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build profiler/build --parallel 2 --config Release
|
cmake --build profiler/build --parallel --config Release
|
||||||
- name: Build update
|
- name: Build update
|
||||||
run: |
|
run: |
|
||||||
cmake -B update/build -S update -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B update/build -S update -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
@@ -76,13 +53,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake -B merge/build -S merge -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
cmake -B merge/build -S merge -DCMAKE_BUILD_TYPE=Release -DGIT_REV=${{ github.sha }}
|
||||||
cmake --build merge/build --parallel --config Release
|
cmake --build merge/build --parallel --config Release
|
||||||
|
- name: Test application
|
||||||
|
uses: ./.github/actions/test-tracy
|
||||||
- name: Package artifacts
|
- name: Package artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir bin
|
mkdir bin
|
||||||
copy profiler\build\Release\tracy-profiler.exe bin
|
copy profiler\build\Release\tracy-profiler.exe bin
|
||||||
copy update\build\Release\tracy-update.exe bin
|
copy update\build\Release\tracy-update.exe bin
|
||||||
copy capture\build\Release\tracy-capture.exe bin
|
copy capture\build\Release\tracy-capture.exe bin
|
||||||
copy capture\build\Release\tracy-capture-daemon.exe bin
|
|
||||||
copy csvexport\build\Release\tracy-csvexport.exe bin
|
copy csvexport\build\Release\tracy-csvexport.exe bin
|
||||||
copy import\build\Release\tracy-import-chrome.exe bin
|
copy import\build\Release\tracy-import-chrome.exe bin
|
||||||
copy import\build\Release\tracy-import-fuchsia.exe bin
|
copy import\build\Release\tracy-import-fuchsia.exe bin
|
||||||
|
|||||||
14
.gitignore
vendored
@@ -1,12 +1,16 @@
|
|||||||
.vs
|
.vs
|
||||||
_build
|
_build
|
||||||
_compiler
|
_compiler
|
||||||
|
tools/*
|
||||||
*.d
|
*.d
|
||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
*.swp
|
*.swp
|
||||||
*.obj
|
*.obj
|
||||||
imgui.ini
|
imgui.ini
|
||||||
|
test/tracy_test
|
||||||
|
test/tracy_test.exe
|
||||||
|
*/build/unix/*-*
|
||||||
manual/t*.aux
|
manual/t*.aux
|
||||||
manual/t*.log
|
manual/t*.log
|
||||||
manual/t*.out
|
manual/t*.out
|
||||||
@@ -17,17 +21,19 @@ manual/t*.bbl
|
|||||||
manual/t*.blg
|
manual/t*.blg
|
||||||
manual/t*.fdb_latexmk
|
manual/t*.fdb_latexmk
|
||||||
manual/t*.fls
|
manual/t*.fls
|
||||||
manual/tracy.md
|
profiler/build/win32/packages
|
||||||
profiler/build-appimage
|
profiler/build/win32/Tracy.aps
|
||||||
.deps/
|
.deps/
|
||||||
.dirstamp
|
.dirstamp
|
||||||
/_*/**
|
/_*/**
|
||||||
/**/__pycache__/**
|
/**/__pycache__/**
|
||||||
|
extra/vswhere.exe
|
||||||
extra/tracy-build
|
extra/tracy-build
|
||||||
.cache
|
.cache
|
||||||
.uv-cache/
|
|
||||||
.venv/
|
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
profiler/build/wasm/Tracy-release.*
|
||||||
|
profiler/build/wasm/Tracy-debug.*
|
||||||
|
profiler/build/wasm/embed.tracy
|
||||||
examples/ToyPathTracer/Windows/TestCpu
|
examples/ToyPathTracer/Windows/TestCpu
|
||||||
examples/ToyPathTracer/Windows/x64
|
examples/ToyPathTracer/Windows/x64
|
||||||
*.user
|
*.user
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
# Working rules
|
|
||||||
|
|
||||||
## Permissions and scope
|
|
||||||
|
|
||||||
- Answer questions; execute state changes only when explicitly instructed. Anything that changes state (files, git history, services, external systems) requires an explicit instruction in the current conversation. Never infer permission from context, momentum, or a previously similar instruction.
|
|
||||||
- Do exactly what was named. If you discover the same defect in a sibling, report it — do not fix it.
|
|
||||||
|
|
||||||
## Code
|
|
||||||
|
|
||||||
- Follow the style conventions already present in the repository — observe the files you touch and their related ones (siblings in the same layer, neighbors with the same role) before writing, and deviate only when told.
|
|
||||||
- Comments carry only what is not inferable from the code and is not already stated in the commit message, e.g. contracts, platform divergences. No narration of what the code does.
|
|
||||||
- Do not follow architectural precedent merely because it exists. Follow it when it is valid; when it is invalid — copy-pasted logic, a wrong home for shared code, a workaround that outlived its reason — propose the improvement instead.
|
|
||||||
- Do not assume a library behaves identically across platforms and standard-library implementations; verify behavior on the toolchains the code actually ships with.
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
- Prove behavior, not just compilation: build the affected targets and run the changed path, including the failure paths. Label compile-only claims as such.
|
|
||||||
- Test non-trivial new logic before committing it; the first run of new code is where environment and API assumptions break.
|
|
||||||
- State verification limits explicitly (what could not be exercised, and why).
|
|
||||||
|
|
||||||
## Commits
|
|
||||||
|
|
||||||
- Subject: one line, imperative, capitalized, ends with a period — like `Bump LZ4 to 1.10.0.`, `Use the system wayland-protocols package.`.
|
|
||||||
- Body: only genuine rationale, mechanism, or constraints; skip it when the change is self-evident from the diff. Never recap decision processes or alternatives considered.
|
|
||||||
- One logical change per commit. Never bundle unrelated edits.
|
|
||||||
- The rationale, mechanism, non-obvious constraints — belong in the commit message, not in code comments.
|
|
||||||
- Explain a pattern once, at its first occurrence; repeat occurrences at other sites state only what they do.
|
|
||||||
- No process narration ("this commit", "as discussed"), no references to the conversation.
|
|
||||||
@@ -1,329 +0,0 @@
|
|||||||
---
|
|
||||||
name: tracy-gui-verify
|
|
||||||
description: Build and drive the Tracy Profiler GUI headlessly via two routes: the emscripten (web) GUI in a headless browser with an instrumented harness, or the native GUI under X11. Use when developing or testing the profiler GUI.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Test the Tracy GUI headlessly
|
|
||||||
|
|
||||||
Two routes run the profiler GUI on a display-less machine. Pick one per test goal:
|
|
||||||
|
|
||||||
| | Route A — Web (emscripten) | Route B — Native (X11) |
|
|
||||||
|---|---|---|
|
|
||||||
|Display|Browser; no X11/Wayland/DISPLAY — the browser is the display server|Xvfb (virtual X server) + openbox (window manager)|
|
|
||||||
|Input|Puppeteer mouse/keyboard|xdotool mouse/keyboard|
|
|
||||||
|Ground truth|Instrumented harness (`debug_snapshot`/`debug_clear`) — app-side mouse/ID state|Screenshot diffs (PIL `ImageChops`) — no harness|
|
|
||||||
|Can test|Loaded-trace GUI: toolbar, panels, targeting, DPI scaling|Live TCP connection to a client, native file dialogs (save/open), X11/GLFW input|
|
|
||||||
|Cannot test|Live client (browser cannot open a TCP server), native file dialogs|No known GUI-level gap; verification precision is lower (no hit-test oracle)|
|
|
||||||
|Prereqs|emsdk (`$EMSDK_DIR`, default `~/emsdk`), CPM cache|Xvfb, openbox, xdotool, ImageMagick (`import`), mesa swrast|
|
|
||||||
|
|
||||||
# Route A — Web GUI (emscripten, headless browser)
|
|
||||||
|
|
||||||
Build the emscripten profiler GUI into an instrumented temporary workspace, serve it, and drive it with the browser tool. The harness exports `debug_snapshot`/`debug_clear`, exposing the app-side mouse/ID state — the ground truth for targeting and verification, not screenshots.
|
|
||||||
|
|
||||||
Constants marked M are workstation-dependent — re-verify them. Everything else is stable; do not re-derive it.
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sh <this-skill-dir>/setup.sh # workspace=/tmp/tracy-protocol
|
|
||||||
# or: TRACY_WEB_WS=/elsewhere TRACY_WEB_REPO=<repo> sh setup.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
`<this-skill-dir>` is the project-scoped `.omp/skills/tracy-gui-verify/` under the active project root — not a fixed home directory. If multiple copies exist (one per project that has used this skill), use the copy inside the repo being tested; `TRACY_WEB_REPO` controls which repo is copied into the workspace.
|
|
||||||
|
|
||||||
The script does: fresh source copy (excludes `.git` and build dirs) → applies `harness.patch` (adds `debug_snapshot`/`debug_clear` exports + event/frame recording to the emscripten backend, in the COPY only) → configures → builds Release with the emsdk at `$EMSDK_DIR` (default `~/emsdk`) and the CPM cache at `~/.cache/cpm`.
|
|
||||||
|
|
||||||
**Pitfall:** if the workspace is deleted while a `tracy-web` server is running, stop the hub process first — a stale server keeps the *deleted* cwd and silently returns empty responses while the port still accepts connections.
|
|
||||||
|
|
||||||
**Pitfall:** a `tracy-web` that died in an earlier session leaves the daemon in `failed` state: `stop` only reports the old failure, and `start` keeps returning "Daemon tracy-web has unacknowledged completion notifications" while the port stays free. Drain the daemon with `hub op=logs name=tracy-web` (the log also shows the cause), then relaunch with `hub op=restart name=tracy-web` (retained spec). If the port is NOT free, a stale process owns it — often one serving an earlier (now-stale) build directory: the new `httpd.py` dies on the bind and your requests silently hit the old directory. Check `ss -tlnp | grep 8000` and kill the owner. After any start/restart, verify the server is actually serving: `curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8000/index.html` must return 200 — the daemon message is not proof of a server.
|
|
||||||
|
|
||||||
**After editing the GUI in the repo:** copy the changed files into `$WS/src/` (same relative path), re-run `cmake --build $WS/build -j$(nproc)`, reload the page. If a change touches `profiler/src/BackendEmscripten.cpp` or `profiler/CMakeLists.txt`, the harness patch may no longer apply — re-apply it manually (keep the `Dbg*` harness section, the `DbgPushEvent`/`DbgPushFrame` calls in the mouse callbacks + `NewFrame`, and the two `debug_*` exports in `-sEXPORTED_FUNCTIONS`).
|
|
||||||
|
|
||||||
## Serve + open + ready
|
|
||||||
|
|
||||||
Server (long-running → `hub`):
|
|
||||||
|
|
||||||
```
|
|
||||||
hub op=start name=tracy-web application=python3 args=["$WS/build/httpd.py"]
|
|
||||||
cwd=$WS/build ready={port:8000} persist=true
|
|
||||||
```
|
|
||||||
|
|
||||||
`httpd.py` sends the COOP/COEP headers required for pthreads/SharedArrayBuffer — use it, not a bare http.server.
|
|
||||||
|
|
||||||
Browser:
|
|
||||||
|
|
||||||
```
|
|
||||||
browser open url=http://127.0.0.1:8000/index.html viewport={width:1600,height:900}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Ready signal** (in a `run` cell; `run` code executes in Node scope — page JS goes through `tab.evaluate`):
|
|
||||||
|
|
||||||
```js
|
|
||||||
let title = null;
|
|
||||||
for (let i = 0; i < 360; i++) {
|
|
||||||
title = await tab.evaluate(() => document.title);
|
|
||||||
if (/ - Tracy Profiler/.test(title)) break;
|
|
||||||
await new Promise(r => setTimeout(r, 250));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Title is `Tracy Profiler X.Y.Z` until the preloaded `embed.tracy` (DarkRL capture) finishes loading, then `<trace> (embed.tracy) - Tracy Profiler X.Y.Z` (`profiler/src/main.cpp` `SetWindowTitleCallback`). Wait ~1.5 s after, then confirm the app loop lives: `snap().frames.at(-1).tick > 0`.
|
|
||||||
|
|
||||||
## Harness API (ground truth)
|
|
||||||
|
|
||||||
```js
|
|
||||||
const snap = () => tab.evaluate(() => JSON.parse(Module.ccall('debug_snapshot','string',[],[])));
|
|
||||||
const clear = () => tab.evaluate(() => Module.ccall('debug_clear','void',[],[]));
|
|
||||||
```
|
|
||||||
|
|
||||||
`snap()` → `{ dpr, innerW, innerH, bufW, bufH, rectL, rectT, rectW, rectH, title, cursor, frames:[…64], events:[…64] }` (newest last).
|
|
||||||
|
|
||||||
- `frames[]`: one sample **per rAF tick, including idle ticks**. `tick` = main-loop count (always advances), `f` = `ImGui::GetFrameCount()` (advances only when a frame actually renders — the idle gate, §Idle), `mx/my` = app mouse position, `md[3]` = mouse button states, `q` = pending input events, `wa` = `tracy::s_wasActive` at tick start, `hi`/`ai` = ImGui `HoveredId`/`ActiveId`.
|
|
||||||
- `events[]`: raw callback data as the app received it. `type` 0=move 1=down 2=up 3=enter 4=leave 5=wheel; `tx/ty` = Emscripten int `targetX/Y`; `ax/ay` = position passed to ImGui (`-1` for non-move); `t` = DOMHighResTimeStamp.
|
|
||||||
|
|
||||||
## Coordinate mapping
|
|
||||||
|
|
||||||
```
|
|
||||||
appX = floor( trunc(cssX - rectL) * dpr ) // device px, what ImGui hit-tests
|
|
||||||
cssX = appX / dpr // inverse for targeting
|
|
||||||
screenshot_px = cssX * dpr
|
|
||||||
```
|
|
||||||
|
|
||||||
Chain: DOM `clientX` → Emscripten `targetX = int(clientX - (rect.left|0))` (`src/lib/libhtml5.js`, `EmscriptenMouseEvent.targetX` is an `int`) → Tracy `AddMousePosEvent(targetX * dpr, …)` (`BackendEmscripten.cpp`) → ImGui `ImFloor` (`imgui.cpp` `AddMousePosEvent`). Canvas fills the viewport, so `rectL/rectT` are 0 (re-read anyway).
|
|
||||||
|
|
||||||
**M: `dpr` on this workstation's headless browser is 1.25** (canvas buffer 2000×1125 for a 1600×900 viewport; screenshots are captured at device px). Never assume 1. App positions come in multiples of `dpr`; verify targeting within ±1 px via `frames[].mx/my`.
|
|
||||||
|
|
||||||
## Click protocol
|
|
||||||
|
|
||||||
Click = move, press, refresh through the hold, release, verify — all against rendered frames. The idle gate (§Idle) renders only 3 frames per wake, and a hold with no mouse events at all refreshes nothing, so a fixed-wait down/up from a cold idle can be swallowed (both events received, zero frames rendered) or released after the wake budget expires. Issuing mouse moves while the button is down keeps the budget alive: a same-coord re-move is delivered (verified: it reaches the canvas handler, wakes the app, and a same-coord hold click straddles cleanly) and is the safest refresh — zero hover drift; a ±`dx` px wiggle works too, but must stay inside the hit area:
|
|
||||||
|
|
||||||
```js
|
|
||||||
async function click(cssX, cssY, holdMs = 400) {
|
|
||||||
await page.mouse.move(cssX, cssY); // 1. ALWAYS move first
|
|
||||||
await new Promise(r => setTimeout(r, 150)); // 2. >= 1 rendered frame
|
|
||||||
const pre = await snap();
|
|
||||||
const pf = pre.frames.at(-1);
|
|
||||||
await clear();
|
|
||||||
await page.mouse.down(); // 3. press
|
|
||||||
let df = null, hiDuring = null;
|
|
||||||
const t0 = Date.now();
|
|
||||||
while (Date.now() - t0 < holdMs) {
|
|
||||||
await new Promise(r => setTimeout(r, 70));
|
|
||||||
await page.mouse.move(cssX, cssY); // 4. same-coord re-move refreshes the wake budget
|
|
||||||
const f = (await snap()).frames.at(-1);
|
|
||||||
if (f.md[0] === 1) { df ??= f.f; hiDuring = f.hi; }
|
|
||||||
}
|
|
||||||
await page.mouse.up(); // 5. release
|
|
||||||
let uf = null;
|
|
||||||
for (let i = 0; i < 40; i++) {
|
|
||||||
await new Promise(r => setTimeout(r, 40));
|
|
||||||
const f = (await snap()).frames.at(-1);
|
|
||||||
if (f.md[0] === 0) { uf = f.f; break; }
|
|
||||||
}
|
|
||||||
await new Promise(r => setTimeout(r, 400));
|
|
||||||
const s = await snap();
|
|
||||||
const d = s.dpr, ex = Math.floor(Math.trunc(cssX - s.rectL) * d), ey = Math.floor(Math.trunc(cssY - s.rectT) * d);
|
|
||||||
return { hoverID: pf.hi, hiDuring, appPos: [pf.mx, pf.my], expected: [ex, ey],
|
|
||||||
straddle: { df, uf },
|
|
||||||
ok: df !== null && uf !== null && uf > df && hiDuring === pf.hi &&
|
|
||||||
Math.abs(pf.mx - ex) <= 1 && Math.abs(pf.my - ey) <= 1 };
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
|
|
||||||
1. **Move before every click.** The emscripten mousemove handler is the only thing that sets the app mouse position; mousedown/mouseup only queue button state. Fresh page position is invalid (`-FLT_MAX`) — a click without a prior move activates nothing.
|
|
||||||
2. **Press and release must each render** in separate frames with the item hovered (ImGui's `PressedOnClickRelease` — doc table at `imgui_widgets.cpp:489-497`; input trickling, `ConfigInputTrickleEventQueue`, defers a same-frame release). The same-coord refresh is what guarantees rendered frames across the hold from a cold idle.
|
|
||||||
3. **Verify every click:** `ok: true` (position landed, press and release each rendered, hover held — `hiDuring === hoverID`), plus a screenshot diff as supplementary visual confirmation. Non-ImGui targets (timeline zone bars) have `hi = 0` by design — for those verify by screenshot, not hover.
|
|
||||||
4. **Double-click trap:** two same-position clicks <300 ms apart = double-click (`MouseDoubleClickTime = 0.3 s`) → zoom/thread-info actions fire. Wait ≥350 ms between repeated clicks at one spot.
|
|
||||||
5. **Drag:** move → down → (move + wait 70 ms)×n → up. **Wheel:** `page.mouse.wheel({ deltaY: ±500 })` after positioning. **Keys:** `page.keyboard.type(..., { delay: 50 })` after focusing the input by click; `page.keyboard.press('Enter')`.
|
|
||||||
6. **Danger:** toolbar device x 10–32 (css 8–26) = red power button (leftmost toolbar icon, hover ID 3313137574), closes the trace view (back to "Get started"). Never click it unless intended.
|
|
||||||
|
|
||||||
## Idle and power saving
|
|
||||||
|
|
||||||
`main.cpp DrawContents` keeps an `activeFrames = 3` budget refreshed on any input event (`tracy::s_wasActive`), a connected client, view animation, or a queued input; exhausted → `sleep 16 ms` with **no `ImGui::NewFrame` and no GL draw** (canvas keeps the last frame). Expect ~97 % of ticks to skip rendering while idle.
|
|
||||||
|
|
||||||
- Clicks from a cold idle land only via the click protocol's hold refresh (§Click protocol) — a fixed-wait down/up can be swallowed by the idle gate (both events received, zero frames rendered). **Before interacting, assert `tick`/`f` is advancing**; a stalled main loop (initial trace parse, hidden tab) renders nothing and drops everything. During the initial load the cursor style is `wait`.
|
|
||||||
- `focusLostLimit` (reduce render rate on focus loss) is desktop-only; it does not exist in the emscripten path.
|
|
||||||
- `canvas.style.cursor` is **not** a hover oracle: this ImGui build sets the Hand cursor only in `TextLink`, not on buttons.
|
|
||||||
|
|
||||||
## Targeting
|
|
||||||
|
|
||||||
Screenshots never resolve targeting. Full-page shots arrive downscaled JPEG (1024 px wide for a 2000-px device buffer; the factor varies), and `page.screenshot({ clip })` returns a PNG at device scale — a W×H CSS clip returns W·dpr × H·dpr px (CSS = clip offset + px/dpr). Icon buttons are 15–25 device px wide, so visual estimates carry ±20 px error, enough to hit the neighboring button or a gap. Target from the app's own hit-testing — sweep the row and click the run's center:
|
|
||||||
|
|
||||||
```js
|
|
||||||
async function sweepRow(dpr, cssY, devFrom, devTo, step = 4) {
|
|
||||||
const ranges = []; let cur = null;
|
|
||||||
for (let dev = devFrom; dev <= devTo; dev += step) {
|
|
||||||
await page.mouse.move(dev / dpr, cssY);
|
|
||||||
await new Promise(r => setTimeout(r, 50));
|
|
||||||
const s = await snap();
|
|
||||||
const hi = s.frames[s.frames.length - 1].hi;
|
|
||||||
if (cur && hi === cur.id) cur.end = dev;
|
|
||||||
else { if (cur) ranges.push(cur); cur = { id: hi, start: dev, end: dev }; }
|
|
||||||
}
|
|
||||||
ranges.push(cur);
|
|
||||||
return ranges.filter(r => r.id !== 0 && r.end - r.start >= 4);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Same non-zero `hi` across consecutive steps = one widget; `hi = 0` = gap. Click the run's center: `click((r.start + r.end) / 2 / dpr, cssY)`.
|
|
||||||
- **Layout is state-dependent:** docked panels (Statistics, Flame, …) resize the main window and shift the toolbar — **re-sweep after any state change.**
|
|
||||||
- **Close popups before sweeping:** while a popup is open, hovers outside the popup can read `hi = 0` even over live widgets (verified with ZoomPopup over the toolbar). Clicking a % option in ZoomPopup does not close the popup — click an empty area first.
|
|
||||||
- Hover IDs are stable across sessions (label hashes) — the map below holds for the default state (fresh load, no panels, 1600×900, dpr 1.25).
|
|
||||||
- Zoomed region shots (for correlating IDs with icons): `page.screenshot({clip})` returns a Buffer — write it with Node `fs`, then `read` the file:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const fs = require('fs');
|
|
||||||
const buf = await page.screenshot({ clip: { x: 0, y: 0, width: 720, height: 28 } });
|
|
||||||
fs.writeFileSync('/tmp/toolbar-zoom.png', buf);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Toolbar map — default state (device px; css = device/1.25; row y css 15)
|
|
||||||
|
|
||||||
| device x | hover ID | function |
|
|
||||||
|---|---|---|
|
|
||||||
| 10–32 | 3313137574 | ⏻ power (red, leftmost) — **closes the trace view (DANGER)** |
|
|
||||||
| 44–68 | 608241489 | ⚙ Options gear — opens the Options window (clickable; do not confuse with the power button) |
|
|
||||||
| 81–185 | 2246346107 | 💬 Messages panel toggle |
|
|
||||||
| 199–259 | 3177774389 | 🔍 Find zone toggle (search input hover ID 3637961452, css ~x 100–440, y ~90) |
|
|
||||||
| 271–367 | 1419322276 | 📊 Statistics toggle |
|
|
||||||
| 383–451 | 322727105 | 🔥 Flame graph toggle |
|
|
||||||
| 463–551 | 37993378 | 💾 Memory graph toggle |
|
|
||||||
| 567–667 | 2133895361 | ⚖ Compare toggle |
|
|
||||||
| 679–735 | 1808024465 | ⌗ Info (trace information) toggle |
|
|
||||||
| 751–775 | 2535445525 | 🛠 wrench → ToolsPopup (Playback, CPU data, Annotations, Limits, Wait stacks, Frame statistics) |
|
|
||||||
| 791–811 | 3101356487 | 📖 book → User manual toggle |
|
|
||||||
| 827–851 | 2335673581 | 🔍+ → ZoomPopup (50–300 % user scale) |
|
|
||||||
| 864–888 | 1263603982 | ◀ previous frame — focuses the timeline on the previous frame (view range shrinks to the frame's timespan) |
|
|
||||||
| ~890–1010 | 0 | frame set name + count text ("Frames: N") — LMB jumps to a specified frame (custom hit-test, like timeline zones) |
|
|
||||||
| 1012–1036 | 2142681495 | ▶ next frame — focuses the timeline on the next frame (verified: view range 25.82 s → 203.38 ms) |
|
|
||||||
| 1052–1072 | 2961929287 | ▼ frame set selection (switch the active frame set) |
|
|
||||||
|
|
||||||
Button names and functions are documented in the user manual's *Control menu* section (`manual/tracy.md`, generated into the profiler build directory by the build, e.g. `build/profiler/manual/tracy.md`). The web build omits *Connection* (live capture only) and *Tracy Assist* (desktop only).
|
|
||||||
|
|
||||||
## User scale (DPI zoom)
|
|
||||||
|
|
||||||
The 🔍+ button (hover ID 2335673581) — *Display scale* in the user manual — opens a ZoomPopup with 50–300 % steps.
|
|
||||||
In the web build this scales the UI **layout only**: fonts, `Style::ScaleAllSizes`, and window sizes (`main.cpp SetupDPIScale`, `scale = devicePixelRatio × userScale`). The canvas buffer and the mouse→app mapping stay at `devicePixelRatio` (`BackendEmscripten.cpp`) — hit regions become physically larger in the same coordinate space, targeting precision is unchanged, and the standard click protocol works as-is.
|
|
||||||
|
|
||||||
Use it when small buttons are hard to hit or details are hard to read in screenshots. At 200 % every toolbar hit region is roughly 2× (verified: power 16→40 px, gear 25→50 px, Messages 105→190 px wide; row height 24→46 device px). The layout **reflows completely** at any scale — re-sweep after changing it and after setting it back. `userScale` is session-only unless Options → "Save UI scale" is on; a fresh page load starts at 100 %.
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- A *connected* live-streaming client cannot be tested in the web build (the browser GUI cannot open a TCP server). The embedded-trace GUI is the live-app proxy: real trace data, full GUI — for live-client behavior use Route B.
|
|
||||||
- `embed.tracy` (10 MB) is downloaded from share.nereid.pl at configure time — network needed once per fresh workspace; the file then lives in `$WS/build`.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
Sources for the behavior the rules above encode — consult them when re-verifying a constant or when a rule fails:
|
|
||||||
|
|
||||||
- `profiler/src/BackendEmscripten.cpp` — emscripten input handlers, canvas/dpr sizing, cursor mapping.
|
|
||||||
- `profiler/src/main.cpp:540-574` — idle gate (`activeFrames` budget).
|
|
||||||
- `profiler/src/profiler/TracyMouse.cpp` — per-frame click state cache.
|
|
||||||
- `profiler/CMakeLists.txt:334-353` — emscripten link options.
|
|
||||||
- ImGui 1.92.9b (vendored copy in the CPM cache, `~/.cache/cpm/imgui/`): `imgui_widgets.cpp:483-545` (click semantics table), `imgui.cpp:11278-11403` (event trickling), `imgui.cpp:1736` (trickle default on), `imgui.cpp:1992-2016` (AddMousePosEvent floor/dedup).
|
|
||||||
- Emscripten 5.0.7: `src/lib/libhtml5.js` (`fillMouseEventData`, `targetX = e.clientX - (rect.left|0)` into HEAP32), `system/include/emscripten/html5.h` (`EmscriptenMouseEvent.targetX` is `int`; `EmscriptenWheelEvent` embeds a `mouse` sub-struct).
|
|
||||||
- Upstream: imgui issue #4921 (new input event API — events spread over multiple frames), PR #2525 (same-frame click/release), issue #1992 (rapid down/up "soft clicks" dropped).
|
|
||||||
|
|
||||||
# Route B — Native GUI (X11, headless)
|
|
||||||
|
|
||||||
Run the native (GLFW) GUI on a virtual X server for what Route A cannot test: a live TCP connection to a client, the native file dialogs (save/open flows), and X11 input semantics. Stack: Xvfb + openbox + xdotool + ImageMagick `import`. Constants marked M were verified on this workstation (1280x800 screen, openbox, profiler window client area at (1,20)); re-verify after any screen/WM change, and always confirm against a screenshot before clicking.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cmake -S profiler -B /tmp/prof-x11 -DCMAKE_BUILD_TYPE=Release -DLEGACY=ON -DGTK_FILESELECTOR=ON
|
|
||||||
cmake --build /tmp/prof-x11 --target tracy-profiler -j$(nproc)
|
|
||||||
```
|
|
||||||
|
|
||||||
- `LEGACY=ON` selects the X11 (GLFW) backend; the Linux default is Wayland.
|
|
||||||
- `GTK_FILESELECTOR=ON` routes NFD through GTK. **Without it** the default Linux NFD uses the XDG desktop portal and fails headless with the notification "File selector is not available".
|
|
||||||
- Verify after building: `ldd /tmp/prof-x11/tracy-profiler | grep -c gtk` ≥ 1.
|
|
||||||
|
|
||||||
## Services
|
|
||||||
|
|
||||||
```
|
|
||||||
hub op=start name=xvfb application=Xvfb args=[":99","-screen","0","1280x800x24"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Ready when `/tmp/.X11-unix/X99` exists — check the socket. (If you add `-nolisten tcp` to the Xvfb args, a TCP port readiness check cannot work; the UNIX socket is then the only listener.) Then:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
DISPLAY=:99 nohup openbox >/dev/null 2>&1 &
|
|
||||||
```
|
|
||||||
|
|
||||||
**openbox is required.** Without a window manager the window lands at an arbitrary position, `xdotool windowactivate` fails ("WM claims not to support _NET_ACTIVE_WINDOW"), and keyboard focus has no owner. Under openbox the profiler window is placed deterministically (M: (1,20), 1278x775 on a 1280x800 screen).
|
|
||||||
|
|
||||||
## Run
|
|
||||||
|
|
||||||
```sh
|
|
||||||
DISPLAY=:99 LIBGL_ALWAYS_SOFTWARE=1 nohup /tmp/prof-x11/tracy-profiler >gui.log 2>&1 &
|
|
||||||
```
|
|
||||||
|
|
||||||
- `LIBGL_ALWAYS_SOFTWARE=1` — no GPU; mesa swrast renders.
|
|
||||||
- Window: `DISPLAY=:99 xdotool search --name "Tracy Profiler"` → `getwindowgeometry` for the origin; `windowactivate --sync` to focus.
|
|
||||||
- Live test client: `tracy-monitor -p $(pgrep -n sleep)` style — attach to a long-lived process; the in-process client listens on 8086 and advertises via UDP (discovery works headless; the client line appears under "Discovered clients").
|
|
||||||
|
|
||||||
## Interaction rules
|
|
||||||
|
|
||||||
1. **Verify every click with a screenshot diff** — `import -window root` before and after, then PIL `ImageChops.difference(a, b).getbbox()`: no (or irrelevant) bbox = the click missed. Expect at least one swallowed click; re-issue it and re-verify.
|
|
||||||
2. Move before click; sleep ≥0.3 s between two clicks at the same spot (ImGui double-click threshold is 0.3 s).
|
|
||||||
3. Keyboard (`xdotool key`) is reliable once openbox owns focus.
|
|
||||||
4. First launch only: an achievements prompt appears bottom-right — click "No" (M: 725,585). The choice persists in the Tracy config; later launches skip it.
|
|
||||||
5. A stale "Client not ready / another server" notification means the client was replaced underneath the GUI — click **Reconnect** (M: 738,574); there is no automatic retry.
|
|
||||||
|
|
||||||
## Coordinates (M: screen px, 1280x800, window at (1,20))
|
|
||||||
|
|
||||||
|Target|Coords|
|
|
||||||
|---|---|
|
|
||||||
|Get started → Connect|101,227|
|
|
||||||
|Get started → wrench (About panel)|317,120|
|
|
||||||
|Connection popup → Save trace…|64,202|
|
|
||||||
|Connection popup → Reconnect|738,574|
|
|
||||||
|GTK save dialog → Save button|1195,773|
|
|
||||||
|GTK overwrite confirmation → Replace|790,482|
|
|
||||||
|Tracy save-confirmation → Save trace|757,540|
|
|
||||||
|
|
||||||
Directly clicked and verified: Connect, Save trace…, GTK Save, Replace, and the save-confirmation button. Derived as the verified window-relative offset translated to the openbox window origin (confirm on first use): wrench, Reconnect, achievements "No".
|
|
||||||
|
|
||||||
The Get-started panel, connection popup, and the two confirmation panels keep fixed offsets inside the profiler window; the GTK dialogs are separate top-level windows sized to the screen. Derive any missing coordinate from a screenshot rather than guessing.
|
|
||||||
|
|
||||||
## Save flow (end-to-end)
|
|
||||||
|
|
||||||
1. Start the client, click **Connect** (the GUI never auto-connects).
|
|
||||||
2. The connection popup opens automatically on connect; click **Save trace…**.
|
|
||||||
3. GTK dialog: the Name entry is focused and the last-used directory is preselected; `xdotool type --delay 30 <name>`; click **Save** (1195,773).
|
|
||||||
4. Existing target file → a separate overwrite-confirmation window appears; click **Replace** (790,482).
|
|
||||||
5. Tracy's own confirmation panel appears (shows the final `Path:`); click its **Save trace** (757,540).
|
|
||||||
6. Verify the file (below).
|
|
||||||
|
|
||||||
## File and connection checks
|
|
||||||
|
|
||||||
- Live connection: `ss -tnp | grep 8086` shows `ESTAB` between `tracy-profiler` and the client process; the window title becomes `<program> @ <date> - Tracy Profiler X.Y.Z`.
|
|
||||||
- Saved `.tracy`: first 4 bytes = `74 72 FD 50`; `tail -c +11 file | zstd -t` → `premature end` means the frame decodes fully (expected — the cut falls past the final frame into trailing metadata); `unsupported format` means a bad offset or a non-trace file.
|
|
||||||
- No `file.tmp` may remain after a completed save; a save killed mid-write leaves the previous file intact (the GUI writes through `SafeFileWrite`, tmp + rename-on-commit).
|
|
||||||
- Video evidence while interacting: `ffmpeg -f x11grab -video_size 1280x800 -i :99 -t N -c:v libx264 out.mp4` (background it; it records the whole interaction).
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- No debug harness — the screenshot diff is the only oracle; targeting is coordinate-from-screenshot, slower and coarser than Route A's hit-test sweeps.
|
|
||||||
- The verified coordinates are tied to the M constants (1280x800 screen, openbox default theme, 100 % UI scale); re-derive from a screenshot if any of them changes.
|
|
||||||
- The GTK dialogs are separate top-level windows whose layout follows the system GTK theme.
|
|
||||||
|
|
||||||
## Teardown
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pkill -f prof-x11; pkill -f tracy-monitor
|
|
||||||
pkill -f "sleep 600" # the test target from §Run — adjust to what you started
|
|
||||||
pkill openbox
|
|
||||||
# hub op=stop name=xvfb
|
|
||||||
```
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- `profiler/src/BackendGlfw.cpp` — X11/GLFW input handlers (mouse/keyboard → ImGui).
|
|
||||||
- `profiler/CMakeLists.txt:5-7` — `NO_FILESELECTOR`, `GTK_FILESELECTOR`, `LEGACY` (X11 vs default Wayland) options.
|
|
||||||
- `cmake/vendor.cmake:222-242` — NFD packaging: `NFD_PORTAL` on by default on Linux, the `GTK_FILESELECTOR` switch, the `nfd-xdg-foreign-v2.patch`.
|
|
||||||
- `server/TracySafeFileWrite.hpp` — the GUI save's tmp + rename-on-commit (the invariants in §File and connection checks).
|
|
||||||
- NFD (btzy/nativefiledialog-extended, pinned in `cmake/vendor.cmake`) — the GTK dialog behavior (Name entry focus, last-directory memory, overwrite confirmation).
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
diff -r a/profiler/CMakeLists.txt b/profiler/CMakeLists.txt
|
|
||||||
--- a/profiler/CMakeLists.txt
|
|
||||||
+++ b/profiler/CMakeLists.txt
|
|
||||||
@@ -340,7 +340,7 @@
|
|
||||||
-sMAXIMUM_MEMORY=4gb
|
|
||||||
-sSTACK_SIZE=1048576
|
|
||||||
-sPTHREAD_POOL_SIZE=8
|
|
||||||
- -sEXPORTED_FUNCTIONS=_main,_nativeOpenFile,_tracy_paste_clipboard
|
|
||||||
+ -sEXPORTED_FUNCTIONS=_main,_nativeOpenFile,_tracy_paste_clipboard,_debug_snapshot,_debug_clear
|
|
||||||
-sEXPORTED_RUNTIME_METHODS=ccall
|
|
||||||
-sENVIRONMENT=web,worker
|
|
||||||
--preload-file embed.tracy
|
|
||||||
diff -r a/profiler/src/BackendEmscripten.cpp b/profiler/src/BackendEmscripten.cpp
|
|
||||||
--- a/profiler/src/BackendEmscripten.cpp
|
|
||||||
+++ b/profiler/src/BackendEmscripten.cpp
|
|
||||||
@@ -1,6 +1,8 @@
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
+#include <cstdarg>
|
|
||||||
+#include <algorithm>
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <EGL/eglext.h>
|
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
@@ -10,7 +12,146 @@
|
|
||||||
#include "Backend.hpp"
|
|
||||||
#include "RunQueue.hpp"
|
|
||||||
#include "profiler/TracyImGui.hpp"
|
|
||||||
+#include "imgui_internal.h"
|
|
||||||
|
|
||||||
+// ================= DEBUG HARNESS (temporary copy, not for upstream) =================
|
|
||||||
+struct DbgEvent
|
|
||||||
+{
|
|
||||||
+ int type; // 0=move 1=down 2=up 3=enter 4=leave 5=wheel
|
|
||||||
+ int button;
|
|
||||||
+ int targetX;
|
|
||||||
+ int targetY;
|
|
||||||
+ double appX; // what was passed to ImGui (targetX * dpr), -1 if n/a
|
|
||||||
+ double appY;
|
|
||||||
+ double t; // DOMHighResTimeStamp
|
|
||||||
+ int wheelX;
|
|
||||||
+ int wheelY;
|
|
||||||
+};
|
|
||||||
+struct DbgFrame
|
|
||||||
+{
|
|
||||||
+ double t;
|
|
||||||
+ int tick; // emscripten main-loop tick count (rAF)
|
|
||||||
+ int imguiFrame; // ImGui::GetFrameCount()
|
|
||||||
+ float mx, my; // io.MousePos
|
|
||||||
+ int md0, md1, md2;
|
|
||||||
+ int queue; // pending ImGui input events
|
|
||||||
+ int wasActive; // tracy::s_wasActive at tick start
|
|
||||||
+ unsigned hi; // ImGui hovered item ID
|
|
||||||
+ unsigned ai; // ImGui active item ID
|
|
||||||
+};
|
|
||||||
+static DbgEvent s_dbgEvents[512];
|
|
||||||
+static int s_dbgEventHead = 0, s_dbgEventN = 0;
|
|
||||||
+static DbgFrame s_dbgFrames[512];
|
|
||||||
+static int s_dbgFrameHead = 0, s_dbgFrameN = 0;
|
|
||||||
+static int s_dbgTick = 0;
|
|
||||||
+
|
|
||||||
+static void DbgPushEvent( int type, int button, int tx, int ty, double ax, double ay, double t, int wx = 0, int wy = 0 )
|
|
||||||
+{
|
|
||||||
+ auto& e = s_dbgEvents[s_dbgEventHead];
|
|
||||||
+ e.type = type; e.button = button; e.targetX = tx; e.targetY = ty;
|
|
||||||
+ e.appX = ax; e.appY = ay; e.t = t; e.wheelX = wx; e.wheelY = wy;
|
|
||||||
+ s_dbgEventHead = ( s_dbgEventHead + 1 ) % 512;
|
|
||||||
+ if ( s_dbgEventN < 512 ) s_dbgEventN++;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void DbgPushFrame()
|
|
||||||
+{
|
|
||||||
+ auto& f = s_dbgFrames[s_dbgFrameHead];
|
|
||||||
+ auto& io = ImGui::GetIO();
|
|
||||||
+ f.t = EM_ASM_DOUBLE( { return performance.now(); } );
|
|
||||||
+ f.tick = s_dbgTick;
|
|
||||||
+ f.imguiFrame = ImGui::GetFrameCount();
|
|
||||||
+ f.mx = io.MousePos.x; f.my = io.MousePos.y;
|
|
||||||
+ f.md0 = io.MouseDown[0]; f.md1 = io.MouseDown[1]; f.md2 = io.MouseDown[2];
|
|
||||||
+ auto ctx = ImGui::GetCurrentContext();
|
|
||||||
+ f.queue = ctx ? int( ctx->InputEventsQueue.Size ) : -1;
|
|
||||||
+ f.wasActive = tracy::s_wasActive;
|
|
||||||
+ f.hi = ctx ? ctx->HoveredId : 0;
|
|
||||||
+ f.ai = ctx ? ctx->ActiveId : 0;
|
|
||||||
+ s_dbgFrameHead = ( s_dbgFrameHead + 1 ) % 512;
|
|
||||||
+ if ( s_dbgFrameN < 512 ) s_dbgFrameN++;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+extern "C" EMSCRIPTEN_KEEPALIVE void debug_clear()
|
|
||||||
+{
|
|
||||||
+ s_dbgEventHead = s_dbgEventN = s_dbgFrameHead = s_dbgFrameN = 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+// Escape a C string for embedding in a JSON string literal (", \, control chars).
|
|
||||||
+static void JsonEscape( char* out, size_t outsz, const char* in )
|
|
||||||
+{
|
|
||||||
+ size_t o = 0;
|
|
||||||
+ for( const unsigned char* p = ( const unsigned char* )in; *p; p++ )
|
|
||||||
+ {
|
|
||||||
+ if ( *p == '"' || *p == '\\' )
|
|
||||||
+ {
|
|
||||||
+ if ( o + 2 >= outsz ) break;
|
|
||||||
+ out[o++] = '\\';
|
|
||||||
+ out[o++] = char( *p );
|
|
||||||
+ }
|
|
||||||
+ else if ( *p < 0x20 )
|
|
||||||
+ {
|
|
||||||
+ if ( o + 6 >= outsz ) break;
|
|
||||||
+ o += size_t( snprintf( out + o, outsz - o, "\\u%04x", *p ) );
|
|
||||||
+ }
|
|
||||||
+ else
|
|
||||||
+ {
|
|
||||||
+ if ( o + 1 >= outsz ) break;
|
|
||||||
+ out[o++] = char( *p );
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ out[o] = 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+extern "C" EMSCRIPTEN_KEEPALIVE const char* debug_snapshot()
|
|
||||||
+{
|
|
||||||
+ static char buf[512 * 1024];
|
|
||||||
+ size_t off = 0;
|
|
||||||
+ auto put = [ & ]( const char* fmt, ... )
|
|
||||||
+ {
|
|
||||||
+ if ( off + 1 >= sizeof( buf ) ) return;
|
|
||||||
+ va_list args;
|
|
||||||
+ va_start( args, fmt );
|
|
||||||
+ int n = vsnprintf( buf + off, sizeof( buf ) - off - 1, fmt, args );
|
|
||||||
+ va_end( args );
|
|
||||||
+ if ( n > 0 ) off = std::min( off + size_t( n ), sizeof( buf ) - 1 );
|
|
||||||
+ };
|
|
||||||
+ const auto dpr = EM_ASM_DOUBLE( { return window.devicePixelRatio; } );
|
|
||||||
+ const auto innerW = EM_ASM_INT( { return window.innerWidth; } );
|
|
||||||
+ const auto innerH = EM_ASM_INT( { return window.innerHeight; } );
|
|
||||||
+ const auto bufW = EM_ASM_INT( { return document.getElementById( 'canvas' ).width; } );
|
|
||||||
+ const auto bufH = EM_ASM_INT( { return document.getElementById( 'canvas' ).height; } );
|
|
||||||
+ const auto rectL = EM_ASM_DOUBLE( { var r = document.getElementById( 'canvas' ).getBoundingClientRect(); return r.left; } );
|
|
||||||
+ const auto rectT = EM_ASM_DOUBLE( { var r = document.getElementById( 'canvas' ).getBoundingClientRect(); return r.top; } );
|
|
||||||
+ const auto rectW = EM_ASM_DOUBLE( { var r = document.getElementById( 'canvas' ).getBoundingClientRect(); return r.width; } );
|
|
||||||
+ const auto rectH = EM_ASM_DOUBLE( { var r = document.getElementById( 'canvas' ).getBoundingClientRect(); return r.height; } );
|
|
||||||
+ char title[256];
|
|
||||||
+ EM_ASM( { stringToUTF8( document.title, $0, 256 ); }, title );
|
|
||||||
+ char cursor[64];
|
|
||||||
+ EM_ASM( { stringToUTF8( document.getElementById( 'canvas' ).style.cursor || "", $0, 64 ); }, cursor );
|
|
||||||
+ char title_e[1536];
|
|
||||||
+ char cursor_e[384];
|
|
||||||
+ JsonEscape( title_e, sizeof( title_e ), title );
|
|
||||||
+ JsonEscape( cursor_e, sizeof( cursor_e ), cursor );
|
|
||||||
+ put( "{\"dpr\":%.6f,\"innerW\":%d,\"innerH\":%d,\"bufW\":%d,\"bufH\":%d,\"rectL\":%.3f,\"rectT\":%.3f,\"rectW\":%.3f,\"rectH\":%.3f,\"title\":\"%s\",\"cursor\":\"%s\",\"frames\":[", dpr, innerW, innerH, bufW, bufH, rectL, rectT, rectW, rectH, title_e, cursor_e );
|
|
||||||
+ const int fn = std::min( s_dbgFrameN, 64 );
|
|
||||||
+ for ( int i = 0; i < fn; i++ )
|
|
||||||
+ {
|
|
||||||
+ auto& f = s_dbgFrames[ ( s_dbgFrameHead - fn + i + 512 ) % 512 ];
|
|
||||||
+ put( "%s{\"t\":%.3f,\"tick\":%d,\"f\":%d,\"mx\":%.1f,\"my\":%.1f,\"md\":[%d,%d,%d],\"q\":%d,\"wa\":%d,\"hi\":%u,\"ai\":%u}", i ? "," : "", f.t, f.tick, f.imguiFrame, f.mx, f.my, f.md0, f.md1, f.md2, f.queue, f.wasActive, f.hi, f.ai );
|
|
||||||
+ }
|
|
||||||
+ put( "],\"events\":[");
|
|
||||||
+ const int en = std::min( s_dbgEventN, 64 );
|
|
||||||
+ for ( int i = 0; i < en; i++ )
|
|
||||||
+ {
|
|
||||||
+ auto& e = s_dbgEvents[ ( s_dbgEventHead - en + i + 512 ) % 512 ];
|
|
||||||
+ put( "%s{\"type\":%d,\"btn\":%d,\"tx\":%d,\"ty\":%d,\"ax\":%.3f,\"ay\":%.3f,\"t\":%.3f,\"wx\":%d,\"wy\":%d}", i ? "," : "", e.type, e.button, e.targetX, e.targetY, e.appX, e.appY, e.t, e.wheelX, e.wheelY );
|
|
||||||
+ }
|
|
||||||
+ put( "]}");
|
|
||||||
+ buf[off] = 0;
|
|
||||||
+ return buf;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static std::function<void()> s_redraw;
|
|
||||||
static std::function<void(float)> s_scaleChanged;
|
|
||||||
static std::function<int(void)> s_isBusy;
|
|
||||||
@@ -223,32 +364,38 @@
|
|
||||||
emscripten_set_mousedown_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL {
|
|
||||||
ImGui::GetIO().AddMouseButtonEvent( e->button == 0 ? 0 : 3 - e->button, true );
|
|
||||||
tracy::s_wasActive = true;
|
|
||||||
+ DbgPushEvent( 1, e->button, e->targetX, e->targetY, -1, -1, e->timestamp );
|
|
||||||
return EM_TRUE;
|
|
||||||
} );
|
|
||||||
emscripten_set_mouseup_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL {
|
|
||||||
ImGui::GetIO().AddMouseButtonEvent( e->button == 0 ? 0 : 3 - e->button, false );
|
|
||||||
tracy::s_wasActive = true;
|
|
||||||
+ DbgPushEvent( 2, e->button, e->targetX, e->targetY, -1, -1, e->timestamp );
|
|
||||||
return EM_TRUE;
|
|
||||||
} );
|
|
||||||
emscripten_set_mousemove_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL {
|
|
||||||
const auto scale = EM_ASM_DOUBLE( { return window.devicePixelRatio; } );
|
|
||||||
ImGui::GetIO().AddMousePosEvent( e->targetX * scale, e->targetY * scale );
|
|
||||||
tracy::s_wasActive = true;
|
|
||||||
+ DbgPushEvent( 0, -1, e->targetX, e->targetY, e->targetX * scale, e->targetY * scale, e->timestamp );
|
|
||||||
return EM_TRUE;
|
|
||||||
} );
|
|
||||||
- emscripten_set_mouseleave_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent*, void* ) -> EM_BOOL {
|
|
||||||
+ emscripten_set_mouseleave_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL {
|
|
||||||
ImGui::GetIO().AddFocusEvent( false );
|
|
||||||
tracy::s_wasActive = true;
|
|
||||||
+ DbgPushEvent( 4, -1, e->targetX, e->targetY, -1, -1, e->timestamp );
|
|
||||||
return EM_TRUE;
|
|
||||||
} );
|
|
||||||
- emscripten_set_mouseenter_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent*, void* ) -> EM_BOOL {
|
|
||||||
+ emscripten_set_mouseenter_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL {
|
|
||||||
ImGui::GetIO().AddFocusEvent( true );
|
|
||||||
tracy::s_wasActive = true;
|
|
||||||
+ DbgPushEvent( 3, -1, e->targetX, e->targetY, -1, -1, e->timestamp );
|
|
||||||
return EM_TRUE;
|
|
||||||
} );
|
|
||||||
emscripten_set_wheel_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenWheelEvent* e, void* ) -> EM_BOOL {
|
|
||||||
ImGui::GetIO().AddMouseWheelEvent( e->deltaX * -0.05, e->deltaY * -0.05 );
|
|
||||||
tracy::s_wasActive = true;
|
|
||||||
+ DbgPushEvent( 5, -1, e->mouse.targetX, e->mouse.targetY, -1, -1, e->mouse.timestamp, int( e->deltaX ), int( e->deltaY ) );
|
|
||||||
return EM_TRUE;
|
|
||||||
} );
|
|
||||||
emscripten_set_keydown_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, [] ( int, const EmscriptenKeyboardEvent* e, void* ) -> EM_BOOL {
|
|
||||||
@@ -298,6 +445,8 @@
|
|
||||||
|
|
||||||
void Backend::NewFrame( int& w, int& h )
|
|
||||||
{
|
|
||||||
+ s_dbgTick++;
|
|
||||||
+ DbgPushFrame();
|
|
||||||
const auto scale = GetDpiScale();
|
|
||||||
if( scale != s_prevScale )
|
|
||||||
{
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# setup.sh - build the instrumented Tracy web GUI workspace (see SKILL.md).
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# sh setup.sh
|
|
||||||
# TRACY_WEB_WS=/custom/ws TRACY_WEB_REPO=<repo> sh setup.sh
|
|
||||||
#
|
|
||||||
# Idempotent: reuses an existing source copy and build dir, skips
|
|
||||||
# already-applied harness patch hunks, rebuilds.
|
|
||||||
set -e
|
|
||||||
|
|
||||||
WS="${TRACY_WEB_WS:-/tmp/tracy-protocol}"
|
|
||||||
# The skill is project-scoped: <repo>/.omp/skills/tracy-gui-verify/
|
|
||||||
SKILL_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
||||||
if [ -n "$TRACY_WEB_REPO" ]; then
|
|
||||||
REPO="$TRACY_WEB_REPO"
|
|
||||||
else
|
|
||||||
REPO=$(cd "$SKILL_DIR/../../.." 2>/dev/null && pwd) || REPO=""
|
|
||||||
fi
|
|
||||||
EMSDK="${EMSDK_DIR:-$HOME/emsdk}"
|
|
||||||
|
|
||||||
[ -x "$EMSDK/emsdk" ] || { echo "error: emsdk not found at $EMSDK (set EMSDK_DIR)" >&2; exit 1; }
|
|
||||||
[ -f "$REPO/profiler/CMakeLists.txt" ] || { echo "error: not a Tracy repo: '$REPO' (set TRACY_WEB_REPO)" >&2; exit 1; }
|
|
||||||
|
|
||||||
. "$EMSDK/emsdk_env.sh" >/dev/null 2>&1 || true
|
|
||||||
command -v emcc >/dev/null || { echo "error: emcc not on PATH after emsdk_env.sh" >&2; exit 1; }
|
|
||||||
|
|
||||||
if [ ! -f "$WS/src/profiler/CMakeLists.txt" ]; then
|
|
||||||
echo "[setup] fresh source copy -> $WS/src (from $REPO)"
|
|
||||||
mkdir -p "$WS/src"
|
|
||||||
# -C applies to both archive creation and extraction; exclude all build dirs
|
|
||||||
tar -C "$REPO" \
|
|
||||||
--exclude=.git \
|
|
||||||
--exclude='*/build*' \
|
|
||||||
-cf - . | tar -xf - -C "$WS/src"
|
|
||||||
else
|
|
||||||
echo "[setup] reusing source copy in $WS/src (copy changed repo files into it first)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# The harness is fully in place iff each patch hunk's signature line is present.
|
|
||||||
# patch --forward skips a whole file once its first hunk is detected as already
|
|
||||||
# applied, so a failed later hunk is invisible in patch's output and exit code —
|
|
||||||
# verify the content instead of trusting either.
|
|
||||||
harness_complete()
|
|
||||||
{
|
|
||||||
grep -q '_debug_snapshot' "$WS/src/profiler/CMakeLists.txt" \
|
|
||||||
&& grep -qF 'EMSCRIPTEN_KEEPALIVE const char* debug_snapshot' "$WS/src/profiler/src/BackendEmscripten.cpp" \
|
|
||||||
&& grep -qF 'DbgPushEvent( 1,' "$WS/src/profiler/src/BackendEmscripten.cpp" \
|
|
||||||
&& grep -qF 's_dbgTick++;' "$WS/src/profiler/src/BackendEmscripten.cpp"
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "[setup] applying instrumentation harness"
|
|
||||||
if harness_complete; then
|
|
||||||
echo "[setup] harness already applied"
|
|
||||||
rm -f "$WS/src/profiler/CMakeLists.txt.rej" "$WS/src/profiler/src/BackendEmscripten.cpp.rej"
|
|
||||||
else
|
|
||||||
rm -f "$WS/src/profiler/CMakeLists.txt.rej" "$WS/src/profiler/src/BackendEmscripten.cpp.rej"
|
|
||||||
patch -p1 -d "$WS/src" --forward --no-backup-if-mismatch < "$SKILL_DIR/harness.patch" \
|
|
||||||
|| {
|
|
||||||
if harness_complete; then
|
|
||||||
# mixed run: some hunks already present (skipped), the rest applied
|
|
||||||
rm -f "$WS/src/profiler/CMakeLists.txt.rej" "$WS/src/profiler/src/BackendEmscripten.cpp.rej"
|
|
||||||
else
|
|
||||||
rej=$(ls "$WS/src/profiler/CMakeLists.txt.rej" "$WS/src/profiler/src/BackendEmscripten.cpp.rej" 2>/dev/null || true)
|
|
||||||
if [ -n "$rej" ]; then
|
|
||||||
kept="The unapplied hunks are kept in:
|
|
||||||
$(printf '%s\n' $rej | sed 's/^/ /')"
|
|
||||||
else
|
|
||||||
kept="No .rej files were produced (fatal patch error); the patch output above is the only evidence."
|
|
||||||
fi
|
|
||||||
cat >&2 <<EOF
|
|
||||||
error: harness patch did not fully apply — $WS/src has drifted from the repo.
|
|
||||||
$kept
|
|
||||||
Do not build from this workspace. Either re-apply the harness by hand
|
|
||||||
(SKILL.md, "After editing the GUI in the repo"), or start fresh:
|
|
||||||
rm -rf $WS && sh <this-skill-dir>/setup.sh
|
|
||||||
EOF
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$WS/build/CMakeCache.txt" ]; then
|
|
||||||
echo "[setup] configuring"
|
|
||||||
cmake -S "$WS/src/profiler" -B "$WS/build" \
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE="$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCPM_SOURCE_CACHE="$HOME/.cache/cpm"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[setup] building (first full build ~3-5 min, incremental ~30 s)"
|
|
||||||
cmake --build "$WS/build" -j "$(nproc)"
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
[setup] done.
|
|
||||||
workspace : $WS
|
|
||||||
repo : $REPO
|
|
||||||
emcc : $(emcc --version | sed -n 1p)
|
|
||||||
serve : hub op=start name=tracy-web application=python3 args=[$WS/build/httpd.py] cwd=$WS/build ready={port:8000} persist=true
|
|
||||||
open : browser open url=http://127.0.0.1:8000/index.html viewport={width:1600,height:900}
|
|
||||||
ready : document.title matches / - Tracy Profiler/ (see SKILL.md)
|
|
||||||
EOF
|
|
||||||
2
.vscode/settings.json
vendored
@@ -6,7 +6,7 @@
|
|||||||
"${workspaceFolder}/import",
|
"${workspaceFolder}/import",
|
||||||
"${workspaceFolder}/merge",
|
"${workspaceFolder}/merge",
|
||||||
"${workspaceFolder}/update",
|
"${workspaceFolder}/update",
|
||||||
"${workspaceFolder}/tests/tracy",
|
"${workspaceFolder}/test",
|
||||||
"${workspaceFolder}",
|
"${workspaceFolder}",
|
||||||
],
|
],
|
||||||
"cmake.buildDirectory": "${sourceDirectory}/build",
|
"cmake.buildDirectory": "${sourceDirectory}/build",
|
||||||
|
|||||||
@@ -121,12 +121,12 @@ set_option(TRACY_NO_CODE_TRANSFER "Disable collection of source code" OFF TracyC
|
|||||||
set_option(TRACY_NO_CONTEXT_SWITCH "Disable capture of context switches" OFF TracyClient)
|
set_option(TRACY_NO_CONTEXT_SWITCH "Disable capture of context switches" OFF TracyClient)
|
||||||
set_option(TRACY_NO_EXIT "Client executable does not exit until all profile data is sent to server" OFF TracyClient)
|
set_option(TRACY_NO_EXIT "Client executable does not exit until all profile data is sent to server" OFF TracyClient)
|
||||||
set_option(TRACY_NO_SAMPLING "Disable call stack sampling" OFF TracyClient)
|
set_option(TRACY_NO_SAMPLING "Disable call stack sampling" OFF TracyClient)
|
||||||
set_option(TRACY_NO_WAIT_STACKS "Disable capture of context switch call stacks" OFF TracyClient)
|
|
||||||
set_option(TRACY_NO_VERIFY "Disable zone validation for C API" OFF TracyClient)
|
set_option(TRACY_NO_VERIFY "Disable zone validation for C API" OFF TracyClient)
|
||||||
set_option(TRACY_NO_VSYNC_CAPTURE "Disable capture of hardware Vsync events" OFF TracyClient)
|
set_option(TRACY_NO_VSYNC_CAPTURE "Disable capture of hardware Vsync events" OFF TracyClient)
|
||||||
set_option(TRACY_NO_FRAME_IMAGE "Disable the frame image support and its thread" OFF TracyClient)
|
set_option(TRACY_NO_FRAME_IMAGE "Disable the frame image support and its thread" OFF TracyClient)
|
||||||
set_option(TRACY_NO_SYSTEM_TRACING "Disable systrace sampling" OFF TracyClient)
|
set_option(TRACY_NO_SYSTEM_TRACING "Disable systrace sampling" OFF TracyClient)
|
||||||
set_option(TRACY_PATCHABLE_NOPSLEDS "Enable nopsleds for efficient patching by system-level tools (e.g. rr)" OFF TracyClient)
|
set_option(TRACY_PATCHABLE_NOPSLEDS "Enable nopsleds for efficient patching by system-level tools (e.g. rr)" OFF TracyClient)
|
||||||
|
set_option(TRACY_DELAYED_INIT "Enable delayed initialization of the library (init on first call)" OFF TracyClient)
|
||||||
set_option(TRACY_MANUAL_LIFETIME "Enable the manual lifetime management of the profile" OFF TracyClient)
|
set_option(TRACY_MANUAL_LIFETIME "Enable the manual lifetime management of the profile" OFF TracyClient)
|
||||||
set_option(TRACY_FIBERS "Enable fibers support" OFF TracyClient)
|
set_option(TRACY_FIBERS "Enable fibers support" OFF TracyClient)
|
||||||
set_option(TRACY_NO_CRASH_HANDLER "Disable crash handling" OFF TracyClient)
|
set_option(TRACY_NO_CRASH_HANDLER "Disable crash handling" OFF TracyClient)
|
||||||
@@ -136,8 +136,7 @@ set_option(TRACY_LIBUNWIND_BACKTRACE "Use libunwind backtracing where supported"
|
|||||||
set_option(TRACY_SYMBOL_OFFLINE_RESOLVE "Instead of full runtime symbol resolution, only resolve the image path and offset to enable offline symbol resolution" OFF TracyClient)
|
set_option(TRACY_SYMBOL_OFFLINE_RESOLVE "Instead of full runtime symbol resolution, only resolve the image path and offset to enable offline symbol resolution" OFF TracyClient)
|
||||||
set_option(TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT "Enable libbacktrace to support dynamically loaded elfs in symbol resolution resolution after the first symbol resolve operation" OFF TracyClient)
|
set_option(TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT "Enable libbacktrace to support dynamically loaded elfs in symbol resolution resolution after the first symbol resolve operation" OFF TracyClient)
|
||||||
set_option(TRACY_DEBUGINFOD "Enable debuginfod support" OFF TracyClient)
|
set_option(TRACY_DEBUGINFOD "Enable debuginfod support" OFF TracyClient)
|
||||||
set_option(TRACY_IGNORE_MEMORY_FAULTS "Ignore instrumentation errors from memory free events that do not have a matching allocation, or from repeated allocations of the same address" OFF TracyClient)
|
set_option(TRACY_IGNORE_MEMORY_FAULTS "Ignore instrumentation errors from memory free events that do not have a matching allocation" OFF TracyClient)
|
||||||
set_option(TRACY_OPENGL_AUTO_CALIBRATION "Periodically recalibrate OpenGL GPU/CPU clock drift (forces a CPU/GPU sync each time)" OFF TracyClient)
|
|
||||||
|
|
||||||
# advanced
|
# advanced
|
||||||
set_option(TRACY_VERBOSE "[advanced] Verbose output from the profiler" OFF TracyClient)
|
set_option(TRACY_VERBOSE "[advanced] Verbose output from the profiler" OFF TracyClient)
|
||||||
@@ -152,6 +151,9 @@ if(rocprofiler-sdk_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# handle incompatible combinations
|
# handle incompatible combinations
|
||||||
|
if(TRACY_MANUAL_LIFETIME AND NOT TRACY_DELAYED_INIT)
|
||||||
|
message(FATAL_ERROR "TRACY_MANUAL_LIFETIME can not be activated with disabled TRACY_DELAYED_INIT")
|
||||||
|
endif()
|
||||||
if(TRACY_DISALLOW_HW_TIMER AND NOT TRACY_TIMER_FALLBACK)
|
if(TRACY_DISALLOW_HW_TIMER AND NOT TRACY_TIMER_FALLBACK)
|
||||||
message(FATAL_ERROR "TRACY_DISALLOW_HW_TIMER can not be activated with disabled TRACY_TIMER_FALLBACK")
|
message(FATAL_ERROR "TRACY_DISALLOW_HW_TIMER can not be activated with disabled TRACY_TIMER_FALLBACK")
|
||||||
endif()
|
endif()
|
||||||
@@ -179,8 +181,7 @@ set(tracy_includes
|
|||||||
${TRACY_PUBLIC_DIR}/tracy/TracyMetal.hmm
|
${TRACY_PUBLIC_DIR}/tracy/TracyMetal.hmm
|
||||||
${TRACY_PUBLIC_DIR}/tracy/TracyOpenCL.hpp
|
${TRACY_PUBLIC_DIR}/tracy/TracyOpenCL.hpp
|
||||||
${TRACY_PUBLIC_DIR}/tracy/TracyOpenGL.hpp
|
${TRACY_PUBLIC_DIR}/tracy/TracyOpenGL.hpp
|
||||||
${TRACY_PUBLIC_DIR}/tracy/TracyVulkan.hpp
|
${TRACY_PUBLIC_DIR}/tracy/TracyVulkan.hpp)
|
||||||
${TRACY_PUBLIC_DIR}/tracy/TracyWebGPU.hpp)
|
|
||||||
|
|
||||||
set(client_includes
|
set(client_includes
|
||||||
${TRACY_PUBLIC_DIR}/client/tracy_concurrentqueue.h
|
${TRACY_PUBLIC_DIR}/client/tracy_concurrentqueue.h
|
||||||
@@ -211,19 +212,15 @@ set(common_includes
|
|||||||
${TRACY_PUBLIC_DIR}/common/TracyAlign.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyAlign.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyAlloc.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyAlloc.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyApi.h
|
${TRACY_PUBLIC_DIR}/common/TracyApi.h
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyAssert.hpp
|
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyColor.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyColor.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyForceInline.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyForceInline.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyFormat.h
|
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyMutex.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyMutex.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyProtocol.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyProtocol.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyQueue.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyQueue.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracySocket.hpp
|
${TRACY_PUBLIC_DIR}/common/TracySocket.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyStackFrames.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyStackFrames.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyString.hpp
|
|
||||||
${TRACY_PUBLIC_DIR}/common/TracySystem.hpp
|
${TRACY_PUBLIC_DIR}/common/TracySystem.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyTaggedUserlandAddress.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyTaggedUserlandAddress.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyVersion.hpp
|
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyWinFamily.hpp
|
${TRACY_PUBLIC_DIR}/common/TracyWinFamily.hpp
|
||||||
${TRACY_PUBLIC_DIR}/common/TracyYield.hpp)
|
${TRACY_PUBLIC_DIR}/common/TracyYield.hpp)
|
||||||
|
|
||||||
@@ -276,11 +273,6 @@ configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
|
|||||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/TracyConfig.cmake
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/TracyConfig.cmake
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
||||||
write_basic_package_version_file(
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/TracyConfigVersion.cmake"
|
|
||||||
COMPATIBILITY SameMinorVersion)
|
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/TracyConfigVersion.cmake"
|
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
|
||||||
|
|
||||||
option(TRACY_CLIENT_PYTHON "Whether to build Tracy python client library" OFF)
|
option(TRACY_CLIENT_PYTHON "Whether to build Tracy python client library" OFF)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
@PACKAGE_INIT@
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
set(TRACY_VERSION_STRING "@TRACY_VERSION_STRING@")
|
|
||||||
|
|
||||||
include(CMakeFindDependencyMacro)
|
include(CMakeFindDependencyMacro)
|
||||||
find_dependency(Threads REQUIRED)
|
find_dependency(Threads REQUIRED)
|
||||||
|
|
||||||
|
|||||||
212
NEWS
@@ -5,95 +5,9 @@ here.
|
|||||||
vx.xx.x (2026-xx-xx)
|
vx.xx.x (2026-xx-xx)
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
- Added the monitor utility. This Linux-specific tool can launch or attach
|
|
||||||
to a program that has no Tracy instrumentation and perform a profiling run
|
|
||||||
based on sampling call stacks.
|
|
||||||
- It is now possible to load traces available on the web in the web demo
|
|
||||||
of the profiler. See the manual for details.
|
|
||||||
- Pandoc is now required to build the graphical profiler. The build system
|
|
||||||
will automatically download the tool. The pandoc binary will be stored
|
|
||||||
in the user cache directory.
|
|
||||||
- The profiler listening port can now be retrieved during runtime via the
|
|
||||||
TracyPort macro.
|
|
||||||
- The capture daemon will now lock the directory it writes to, so that two
|
|
||||||
simultaneously running daemons will not stomp on each other's data.
|
|
||||||
- Section rows on the timeline can now be grouped by their category.
|
|
||||||
- System tracing on Linux now properly parses rare non-default kernel data
|
|
||||||
structure formats.
|
|
||||||
- Removed filtering of context switch data when saving a trace to disk, as
|
|
||||||
it was losing the thread wake graph data.
|
|
||||||
- Fixed standard deviation in find zone window when the self or running time
|
|
||||||
option was enabled.
|
|
||||||
- Find zone window now works with GPU zones (histogram, statistics, self
|
|
||||||
time).
|
|
||||||
- Changed the GPU zones icon from an eye to a display monitor.
|
|
||||||
- User GUI scale in the profiler can be now controlled with Ctrl + plus/minus
|
|
||||||
and reset with Ctrl + zero.
|
|
||||||
- Various hardening improvements for correctness across the code base.
|
|
||||||
|
|
||||||
v0.14.1 (2026-08-22)
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
- Dropped support for loading pre-0.11 traces.
|
|
||||||
- Silenced MSVC warning C4366: The result of the unary '&' operator may be
|
|
||||||
unaligned.
|
|
||||||
- Fixed section setup in on-demand mode.
|
|
||||||
- Hardened string length encoding in protocol.
|
|
||||||
- Fixed compilation failure related to VSync when targeting 32-bit on
|
|
||||||
Windows.
|
|
||||||
- Added TRACY_NO_WAIT_STACKS compile time macro.
|
|
||||||
- Improved documentation of requirements in edge cases:
|
|
||||||
- Memory allocation reporting needs to be done under a lock.
|
|
||||||
- Manual lifetime management requires shutdown of the profiler.
|
|
||||||
- Documented how zones behave when crossing the on-demand connection
|
|
||||||
boundary.
|
|
||||||
- Mention that statistics time range limit also affects source view.
|
|
||||||
- Memory events are now attributed to virtual fiber threads.
|
|
||||||
- Added TRACY_ASSERT macro allowing user-defined handling of asserts in the
|
|
||||||
profiler.
|
|
||||||
- Fixed csvexport options: -t, --sep, --filter.
|
|
||||||
- Configuration of plot color now matches the documented RGB channels order.
|
|
||||||
- Link profiler executable with libdl (required by old glibc versions).
|
|
||||||
- Improve coverage of link-time macro mismatch detection.
|
|
||||||
- Removed TRACY_DELAYED_INIT option. It was no-op since 2020.
|
|
||||||
- Fixed illegal instruction in rpmalloc on ARMv6 and older.
|
|
||||||
- Fixed handling of source code queries in no-callstack builds. Previously
|
|
||||||
the profiling session would hang at exit.
|
|
||||||
- Hardened Lua on-demand handling.
|
|
||||||
- Fixed system tracing on Linux kernels older than 4.1.
|
|
||||||
- Linux system tracing setup failures are now reported via Tracy debug
|
|
||||||
message functionality.
|
|
||||||
- Improved version handling in CMake / Meson / Python build systems.
|
|
||||||
- Fixed D3D12 readback buffer error.
|
|
||||||
- Child zone list in zone info window can be now sorted by name or by time.
|
|
||||||
- File dialog failures (e.g. related to the XDG portal) are now reported
|
|
||||||
in the modal dialog.
|
|
||||||
- Fixed NO_FILESELECTOR builds.
|
|
||||||
- Fixed possible profiler hang when there was not enough space to draw
|
|
||||||
a plot on the timeline.
|
|
||||||
- Update check can be now interrupted, e.g. when exiting the profiler.
|
|
||||||
- Patched the file selector library to use modern Wayland protocol for
|
|
||||||
setting parent-child window relation.
|
|
||||||
|
|
||||||
|
|
||||||
v0.14.0 (2026-08-09)
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
- API break: removed "secure" variants of memory alloc and free macros. The
|
|
||||||
secure code path is now always enabled. Migrate by removing "Secure" from
|
|
||||||
the macros you use, e.g. TracySecureAlloc(...) -> TracyAlloc(...).
|
|
||||||
- Added printf-style variants of the C API zone text and name macros:
|
|
||||||
TracyCZoneTextF and TracyCZoneNameF now accept a format string and
|
|
||||||
arguments, instead of a fixed-length text buffer.
|
|
||||||
- Added tracy-capture-daemon for automated multi-client trace capture.
|
- Added tracy-capture-daemon for automated multi-client trace capture.
|
||||||
- Added tracy-merge utility for combining multiple trace files into one.
|
- Added tracy-merge utility for combining multiple trace files into one.
|
||||||
- Added support for Windows on ARM64 with MSVC.
|
- Added support for Windows on ARM64 with MSVC.
|
||||||
- Added support for WebGPU.
|
|
||||||
- Binary releases are now also provided for macOS (needs manual quarantine
|
|
||||||
handling) and Linux (AppImage with Ubuntu 24.04 base, requires Wayland).
|
|
||||||
- Trace-specific settings storage has been completely overhauled. It is now
|
|
||||||
possible to make the settings sidecar file public, saved next to the trace
|
|
||||||
file.
|
|
||||||
- External frames are now omitted in the single-line call stack list visible
|
- External frames are now omitted in the single-line call stack list visible
|
||||||
in messages list, or in memory allocation info window.
|
in messages list, or in memory allocation info window.
|
||||||
- External frames are now hidden by default in various contexts where they
|
- External frames are now hidden by default in various contexts where they
|
||||||
@@ -101,44 +15,9 @@ v0.14.0 (2026-08-09)
|
|||||||
- Flame graph window.
|
- Flame graph window.
|
||||||
- Call stack window.
|
- Call stack window.
|
||||||
- Statistics window (sampling mode).
|
- Statistics window (sampling mode).
|
||||||
- Wait stacks have a separate setting for external frames, as you would
|
|
||||||
typically want to know what caused the wait state, which can be inferred
|
|
||||||
from the external code.
|
|
||||||
- External frames are now dimmed out in call stacks in various parts of UI.
|
- External frames are now dimmed out in call stacks in various parts of UI.
|
||||||
- Sample markers on the timeline are now colored according to sample type:
|
|
||||||
- Green for samples in the profiled program,
|
|
||||||
- Blue for samples in external code,
|
|
||||||
- Red for samples in kernel.
|
|
||||||
- Single-line call stacks now have ellipsis at the end, if there are frames
|
- Single-line call stacks now have ellipsis at the end, if there are frames
|
||||||
remaining.
|
remaining.
|
||||||
- Sample entry stacks window can now also show the stacks by which a symbol
|
|
||||||
was reached, i.e. it was present anywhere on the call stack, not only when
|
|
||||||
it was executing. Recursive re-entries can be either ignored or counted
|
|
||||||
separately.
|
|
||||||
- Entry stacks are now also available for symbols which were only reached,
|
|
||||||
but never directly sampled, e.g. in the assembly view of a call site with
|
|
||||||
purely child sample costs, or in the statistics window for symbols which
|
|
||||||
only have inclusive counts.
|
|
||||||
- Fixed inclusive sample counts of symbols with inline functions. Each
|
|
||||||
sample was counted multiple times: once for the symbol itself and once for
|
|
||||||
every of its inline functions present on the call stack.
|
|
||||||
- Fixed context switch samples leaking into per-symbol sample lists and
|
|
||||||
child sample data on trace load. This inflated range-limited sample counts
|
|
||||||
and child call costs, and made loaded traces disagree with live sessions,
|
|
||||||
where such samples are excluded from sampling statistics.
|
|
||||||
- Fixed wrong results of range-limited child call queries during live
|
|
||||||
capture, when some samples had to wait for context switch data to arrive.
|
|
||||||
- Fixed crashes when opening the sample entry stacks window for symbols
|
|
||||||
without any sampling data.
|
|
||||||
- Sampling statistics percentages are now calculated against the number of
|
|
||||||
samples that were actually collected. Previously, enabling the range
|
|
||||||
filter switched the calculation to a theoretical single-thread estimate,
|
|
||||||
which changed both the meaning and the scale of the displayed values.
|
|
||||||
- Find zone sample count percentages are now relative to the matched zones,
|
|
||||||
consistent with the time percentages in the same list, instead of being
|
|
||||||
relative to the whole trace.
|
|
||||||
- Context switch samples are now consistently excluded from all sampling
|
|
||||||
statistics views.
|
|
||||||
- System tracing on Windows has been refactored to be more robust.
|
- System tracing on Windows has been refactored to be more robust.
|
||||||
- Tracing on Arm macOS will now have more precise timer readings.
|
- Tracing on Arm macOS will now have more precise timer readings.
|
||||||
- Extended CUDA support to track some previously missing memory operations.
|
- Extended CUDA support to track some previously missing memory operations.
|
||||||
@@ -150,8 +29,7 @@ v0.14.0 (2026-08-09)
|
|||||||
- Added inline call stack list to the zone information window.
|
- Added inline call stack list to the zone information window.
|
||||||
- The "call stack" button for opening the call stack in a separate window
|
- The "call stack" button for opening the call stack in a separate window
|
||||||
is still there.
|
is still there.
|
||||||
- Call stacks are now also displayed in zone and memory allocation tooltips
|
- Call stacks are now also displayed in zone tooltips (single-line).
|
||||||
(single-line).
|
|
||||||
- Implemented heuristic reconstruction of zone call stacks for zones that
|
- Implemented heuristic reconstruction of zone call stacks for zones that
|
||||||
were captured without call stacks.
|
were captured without call stacks.
|
||||||
- Requires sampling to be enabled, and at least one sample in the zone
|
- Requires sampling to be enabled, and at least one sample in the zone
|
||||||
@@ -193,16 +71,13 @@ v0.14.0 (2026-08-09)
|
|||||||
- Chat topic description is now provided, based on the first user question.
|
- Chat topic description is now provided, based on the first user question.
|
||||||
- Each assistant reply is now labeled with used model and reply time.
|
- Each assistant reply is now labeled with used model and reply time.
|
||||||
- Follow-up questions can be automatically suggested.
|
- Follow-up questions can be automatically suggested.
|
||||||
- It is now possible to alter the assistant personality.
|
|
||||||
- Expanded LLM attachments.
|
- Expanded LLM attachments.
|
||||||
- You can now attach complete symbol assembly.
|
- You can now attach complete symbol assembly.
|
||||||
- Entry call stacks can be now attached (previously it was only regular
|
- Entry call stacks can be now attached (previously it was only regular
|
||||||
call stacks).
|
call stacks).
|
||||||
- Crash call stack attachments are now annotated with crash info.
|
- Crash call stack attachments are now annotated with crash info.
|
||||||
- Wait stack attachments now contain wait metadata.
|
|
||||||
- Source code can be attached (also with execution costs in symbol view).
|
- Source code can be attached (also with execution costs in symbol view).
|
||||||
- Zone histogram data can be attached for analysis.
|
- Zone histogram data can be attached for analysis.
|
||||||
- Added MCP server.
|
|
||||||
- Markdown renderer improvements.
|
- Markdown renderer improvements.
|
||||||
- Tables are now properly rendered.
|
- Tables are now properly rendered.
|
||||||
- Tasklist rendering has been implemented.
|
- Tasklist rendering has been implemented.
|
||||||
@@ -226,7 +101,6 @@ v0.14.0 (2026-08-09)
|
|||||||
locations won't be fixed.
|
locations won't be fixed.
|
||||||
- Call stack window will now display notification if viewing a crash call
|
- Call stack window will now display notification if viewing a crash call
|
||||||
stack.
|
stack.
|
||||||
- Call stack window will now show a proper label if viewing a wait stack.
|
|
||||||
- Removal of Tracy crash handler stack from the reported crash call stack
|
- Removal of Tracy crash handler stack from the reported crash call stack
|
||||||
should now work again on Linux.
|
should now work again on Linux.
|
||||||
- In disassembly line view, source file names are now displayed instead of
|
- In disassembly line view, source file names are now displayed instead of
|
||||||
@@ -238,8 +112,6 @@ v0.14.0 (2026-08-09)
|
|||||||
- Added TRACY_DISALLOW_HW_TIMER define for virtualized environments and WSL2,
|
- Added TRACY_DISALLOW_HW_TIMER define for virtualized environments and WSL2,
|
||||||
which may not have reliable access to hardware timer registers. Falls back
|
which may not have reliable access to hardware timer registers. Falls back
|
||||||
to standard library timer with reduced resolution.
|
to standard library timer with reduced resolution.
|
||||||
- Fixed TRACY_NO_CODE_TRANSFER define. It didn't prevent source code transfer
|
|
||||||
before.
|
|
||||||
- Fixed DPI scaling on macOS.
|
- Fixed DPI scaling on macOS.
|
||||||
- Thread names are no longer truncated to 15 characters on Apple.
|
- Thread names are no longer truncated to 15 characters on Apple.
|
||||||
- Executable path is now inspected when looking for PDB files on Windows.
|
- Executable path is now inspected when looking for PDB files on Windows.
|
||||||
@@ -269,20 +141,14 @@ v0.14.0 (2026-08-09)
|
|||||||
- Mismatched versions will break at linking.
|
- Mismatched versions will break at linking.
|
||||||
- As a reminder, Tracy *always* required using the same set of compilation
|
- As a reminder, Tracy *always* required using the same set of compilation
|
||||||
options for the entire program.
|
options for the entire program.
|
||||||
- Message windows were improved.
|
- Message windows will now properly show full message in a tooltip for
|
||||||
- Full message will be now displayed in a tooltip for multi-line messages.
|
multi-line messages.
|
||||||
- Added right click context menu with option to copy message to clipboard.
|
|
||||||
- Greatly improved the in-profiler user manual.
|
- Greatly improved the in-profiler user manual.
|
||||||
- There is now chapter tree and the manual contents are displayed section
|
- There is now chapter tree and the manual contents are displayed section
|
||||||
by section.
|
by section.
|
||||||
- Links to chapters are now properly working.
|
- Links to chapters are now properly working.
|
||||||
- The "bclogo" blocks are now correctly processed and displayed as proper
|
- The "bclogo" blocks are now correctly processed.
|
||||||
admonitions.
|
|
||||||
- The font awesome icons now show as in the rest of the UI.
|
- The font awesome icons now show as in the rest of the UI.
|
||||||
- Footnotes are now rendered as proper footnotes.
|
|
||||||
- Tables are now rendered as intended.
|
|
||||||
- LaTeX math is now converted to readable form.
|
|
||||||
- Added a button to download the full PDF manual to the user manual window.
|
|
||||||
- Call stack window will now show the thread viewed call stack originates
|
- Call stack window will now show the thread viewed call stack originates
|
||||||
from (if possible).
|
from (if possible).
|
||||||
- "Visible threads" checkboxes in messages, flame graph and wait stacks
|
- "Visible threads" checkboxes in messages, flame graph and wait stacks
|
||||||
@@ -305,75 +171,7 @@ v0.14.0 (2026-08-09)
|
|||||||
- The profiler can no longer be built with the statistics disabled.
|
- The profiler can no longer be built with the statistics disabled.
|
||||||
- Fixed NVCC builds.
|
- Fixed NVCC builds.
|
||||||
- Fixed possible lockups in Vulkan timer calibration loop.
|
- Fixed possible lockups in Vulkan timer calibration loop.
|
||||||
- Flame graph improvements.
|
- The flame graph view now supports zooming in and panning with the mouse.
|
||||||
- The flame graph view now supports zooming in and panning with the mouse.
|
|
||||||
- Sampling flame graph items can be now also grouped by name, in addition
|
|
||||||
to symbol address.
|
|
||||||
- Sampling flame graph no longer omits display of unknown symbols. These
|
|
||||||
are now aggregated into blocks named after the executable image.
|
|
||||||
- General application crash information polish in the profiler UI.
|
|
||||||
- The achievements system has been converted to use markdown renderer.
|
|
||||||
- Offline symbol resolution with the update utility now supports custom
|
|
||||||
addr2line-compatible tools via -a and -A command line parameters.
|
|
||||||
Additionally, it is now possible to reset all call stack frame symbols to
|
|
||||||
unresolved with the -R parameter.
|
|
||||||
- Periodic recalibration of the clock drift in OpenGL contexts can be enabled
|
|
||||||
with the TRACY_OPENGL_AUTO_CALIBRATION compilation define. Note that this
|
|
||||||
requires a full CPU/GPU sync on each calibration event. These events will
|
|
||||||
not fire more often than once every second.
|
|
||||||
- Additional hardening of OpenGL tracing.
|
|
||||||
- Added missing C API for shared locks.
|
|
||||||
- Implemented semi-unique, nonsense random name generator.
|
|
||||||
- Can be used to set a trace description.
|
|
||||||
- Will be used to provide default description for newly added annotations.
|
|
||||||
- Polished look and feel of annotation regions on the timeline.
|
|
||||||
- Annotations can now be hidden.
|
|
||||||
- It is now possible to create annotations from time range limits.
|
|
||||||
- Expanded available tests.
|
|
||||||
- The previously existing "test" program now lives in "tests/tracy".
|
|
||||||
- The tests directory also contains some repro cases for solved problems.
|
|
||||||
- Expanded examples showing how to use the profiler.
|
|
||||||
- Includes "Dyna.net", a small 2D game that is a variation of Bomberman.
|
|
||||||
- Made numeric input field for trace parameters usable again.
|
|
||||||
- Added new type of trace parameter that exposes a trigger button.
|
|
||||||
- Added "sections" feature for marking high-level sections of program
|
|
||||||
execution.
|
|
||||||
- For example, a game with multiple levels may have each of the levels
|
|
||||||
marked as a section.
|
|
||||||
- Each section is a distinct region not restricted by other sections.
|
|
||||||
- Multiple sections can overlap.
|
|
||||||
- Sections can be assigned to user-defined categories.
|
|
||||||
- Can be used to set time range limits.
|
|
||||||
- Exposed via Lua bindings.
|
|
||||||
- Find zone window now also displays coefficient of variation, in addition
|
|
||||||
to standard deviation.
|
|
||||||
- Moved frame statistics from trace information window to its own window.
|
|
||||||
- Added various statistical data to bring frame statistics up to par with
|
|
||||||
the find zone window.
|
|
||||||
- Added a new time range limit for frame statistics. This accompanies the
|
|
||||||
previously existing "limit to view" option.
|
|
||||||
- More robust statistics for functions that recursively call themselves.
|
|
||||||
- General uplift of the compare traces window.
|
|
||||||
- Added all the statistical parameters displayed in find zone window that
|
|
||||||
were missing here.
|
|
||||||
- Frame comparison now also displays FPS values.
|
|
||||||
- The "more" and "less" indicators are now color coded to show their
|
|
||||||
importance. The amount of color these keywords are printed with is now
|
|
||||||
dependant on how divergent the numeric values are.
|
|
||||||
- It is now possible to limit time range for both traces that are compared
|
|
||||||
to their respective annotations or sections.
|
|
||||||
- Frame image playback improvements.
|
|
||||||
- Playback can be looped.
|
|
||||||
- The range of played frame images can be limited, either manually, or by
|
|
||||||
copying annotation, section, or time range limit. There's an "require
|
|
||||||
frame coverage" option to only include frames that are at least 75%
|
|
||||||
within the source region.
|
|
||||||
- Most windows now have a minimum width, in order to make sure all controls
|
|
||||||
provided in the window are visible. Previously it was easy to oversee some
|
|
||||||
button that was out of the window draw area.
|
|
||||||
- The ignore memory faults functionality (enabled by default if profiling
|
|
||||||
programs on Apple platforms) will now also ignore allocating memory at the
|
|
||||||
same location twice, without an intermediate free. Thanks, Tim Apple!
|
|
||||||
|
|
||||||
|
|
||||||
v0.13.1 (2025-12-11)
|
v0.13.1 (2025-12-11)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
### A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications.
|
### A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications.
|
||||||
|
|
||||||
Tracy supports profiling CPU (Direct support is provided for C, C++, Lua, Python and Fortran integration. At the same time, third-party bindings to many other languages exist on the internet, such as [Rust](https://github.com/nagisa/rust_tracy_client), [Zig](https://github.com/tealsnow/zig-tracy), [C#](https://github.com/clibequilibrium/Tracy-CSharp), [OCaml](https://github.com/imandra-ai/ocaml-tracy), [Odin](https://github.com/oskarnp/odin-tracy), etc.), GPU (All major graphics/compute APIs: OpenGL, Vulkan, Direct3D 11/12, Metal, OpenCL, CUDA, WebGPU.), memory allocations, locks, context switches, automatically attribute screenshots to captured frames, and much more.
|
Tracy supports profiling CPU (Direct support is provided for C, C++, Lua, Python and Fortran integration. At the same time, third-party bindings to many other languages exist on the internet, such as [Rust](https://github.com/nagisa/rust_tracy_client), [Zig](https://github.com/tealsnow/zig-tracy), [C#](https://github.com/clibequilibrium/Tracy-CSharp), [OCaml](https://github.com/imandra-ai/ocaml-tracy), [Odin](https://github.com/oskarnp/odin-tracy), etc.), GPU (All major graphic APIs: OpenGL, Vulkan, Direct3D 11/12, Metal, OpenCL, CUDA.), memory allocations, locks, context switches, automatically attribute screenshots to captured frames, and much more.
|
||||||
|
|
||||||
- [Documentation](https://github.com/wolfpld/tracy/releases/latest/download/tracy.pdf) for usage and build process instructions
|
- [Documentation](https://github.com/wolfpld/tracy/releases/latest/download/tracy.pdf) for usage and build process instructions
|
||||||
- [Releases](https://github.com/wolfpld/tracy/releases) containing the documentation (`tracy.pdf`) and compiled Windows x64 binaries (`Tracy-<version>.7z`) as assets
|
- [Releases](https://github.com/wolfpld/tracy/releases) containing the documentation (`tracy.pdf`) and compiled Windows x64 binaries (`Tracy-<version>.7z`) as assets
|
||||||
|
|||||||
@@ -16,21 +16,21 @@ include(${CMAKE_CURRENT_LIST_DIR}/../cmake/config.cmake)
|
|||||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/vendor.cmake)
|
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/vendor.cmake)
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/server.cmake)
|
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/server.cmake)
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/GitRef.cmake)
|
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/GitRef.cmake)
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/util.cmake)
|
|
||||||
|
|
||||||
set(PROGRAM_FILES
|
set(PROGRAM_FILES
|
||||||
src/capture.cpp
|
src/capture.cpp
|
||||||
|
src/CaptureOutput.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} ${PROGRAM_FILES})
|
add_executable(${PROJECT_NAME} ${PROGRAM_FILES} ${COMMON_FILES} ${SERVER_FILES})
|
||||||
add_git_ref(${PROJECT_NAME})
|
add_git_ref(${PROJECT_NAME})
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE TracyServer TracyUtil TracyGetOpt)
|
target_link_libraries(${PROJECT_NAME} PRIVATE TracyServer TracyGetOpt)
|
||||||
set_property(DIRECTORY ${CMAKE_CURRENT_LIST_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
|
set_property(DIRECTORY ${CMAKE_CURRENT_LIST_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
add_executable(tracy-capture-daemon src/capturedaemon.cpp)
|
add_executable(tracy-capture-daemon src/capturedaemon.cpp src/CaptureOutput.cpp ${COMMON_FILES} ${SERVER_FILES})
|
||||||
add_git_ref(tracy-capture-daemon)
|
add_git_ref(tracy-capture-daemon)
|
||||||
target_link_libraries(tracy-capture-daemon PRIVATE TracyServer TracyUtil TracyGetOpt)
|
target_link_libraries(tracy-capture-daemon PRIVATE TracyServer TracyGetOpt)
|
||||||
|
|
||||||
install(TARGETS tracy-capture-daemon DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS tracy-capture-daemon DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 10,
|
|
||||||
"configurePresets": [
|
|
||||||
{
|
|
||||||
"name": "common",
|
|
||||||
"hidden": true,
|
|
||||||
"binaryDir": "${sourceDir}/build-${presetName}",
|
|
||||||
"generator": "Ninja",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "debug",
|
|
||||||
"displayName": "Debug configuration",
|
|
||||||
"inherits": "common",
|
|
||||||
"binaryDir": "${sourceDir}/build",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_BUILD_TYPE": "Debug"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "release",
|
|
||||||
"displayName": "Release configuration",
|
|
||||||
"inherits": "common",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_BUILD_TYPE": "Release"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,60 @@
|
|||||||
|
#ifdef _WIN32
|
||||||
|
# include <io.h>
|
||||||
|
# include <windows.h>
|
||||||
|
#else
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "CaptureOutput.hpp"
|
#include "CaptureOutput.hpp"
|
||||||
#include "../public/common/TracyProtocol.hpp"
|
#include "../../public/common/TracyProtocol.hpp"
|
||||||
#include "../public/common/TracyStackFrames.hpp"
|
#include "../../public/common/TracyStackFrames.hpp"
|
||||||
#include "../server/TracyWorker.hpp"
|
#include "../../server/TracyMemory.hpp"
|
||||||
|
#include "../../server/TracyPrint.hpp"
|
||||||
|
#include "../../server/TracyWorker.hpp"
|
||||||
|
|
||||||
|
static bool s_isTerminal = false;
|
||||||
|
|
||||||
|
void InitTerminalDetection()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
s_isTerminal = _isatty( fileno( stdout ) );
|
||||||
|
#else
|
||||||
|
s_isTerminal = isatty( fileno( stdout ) );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsTerminal()
|
||||||
|
{
|
||||||
|
return s_isTerminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnsiPrintf( const char* ansiEscape, const char* format, ... )
|
||||||
|
{
|
||||||
|
if( IsTerminal() )
|
||||||
|
{
|
||||||
|
char buf[256];
|
||||||
|
va_list args;
|
||||||
|
va_start( args, format );
|
||||||
|
vsnprintf( buf, sizeof buf, format, args );
|
||||||
|
va_end( args );
|
||||||
|
printf( "%s%s" ANSI_RESET, ansiEscape, buf );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start( args, format );
|
||||||
|
vfprintf( stdout, format, args );
|
||||||
|
va_end( args );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int WaitForConnection( tracy::Worker& worker )
|
int WaitForConnection( tracy::Worker& worker )
|
||||||
{
|
{
|
||||||
@@ -112,3 +159,44 @@ void PrintWorkerFailure( tracy::Worker& worker )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrintCaptureProgress( tracy::Worker& worker, int64_t firstTime, int64_t memoryLimit )
|
||||||
|
{
|
||||||
|
if( !IsTerminal() ) return;
|
||||||
|
|
||||||
|
auto& lock = worker.GetMbpsDataLock();
|
||||||
|
lock.lock();
|
||||||
|
const auto mbps = worker.GetMbpsData().back();
|
||||||
|
const auto compRatio = worker.GetCompRatio();
|
||||||
|
const auto netTotal = worker.GetDataTransferred();
|
||||||
|
const auto queueSize = worker.GetSendQueueSize();
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
const char* unit = "Mbps";
|
||||||
|
float unitsPerMbps = 1.f;
|
||||||
|
if( mbps < 0.1f )
|
||||||
|
{
|
||||||
|
unit = "Kbps";
|
||||||
|
unitsPerMbps = 1000.f;
|
||||||
|
}
|
||||||
|
AnsiPrintf( ANSI_ERASE_LINE ANSI_CYAN ANSI_BOLD, "\r%7.2f %s", mbps * unitsPerMbps, unit );
|
||||||
|
printf( " /" );
|
||||||
|
AnsiPrintf( ANSI_CYAN ANSI_BOLD, "%5.1f%%", compRatio * 100.f );
|
||||||
|
printf( " =" );
|
||||||
|
AnsiPrintf( ANSI_YELLOW ANSI_BOLD, "%7.2f Mbps", mbps / compRatio );
|
||||||
|
printf( " | " );
|
||||||
|
AnsiPrintf( ANSI_YELLOW, "Tx: " );
|
||||||
|
AnsiPrintf( ANSI_GREEN, "%s", tracy::MemSizeToString( netTotal ) );
|
||||||
|
printf( " | " );
|
||||||
|
AnsiPrintf( ANSI_RED ANSI_BOLD, "%s", tracy::MemSizeToString( tracy::memUsage.load( std::memory_order_relaxed ) ) );
|
||||||
|
if( memoryLimit > 0 )
|
||||||
|
{
|
||||||
|
printf( " / " );
|
||||||
|
AnsiPrintf( ANSI_BLUE ANSI_BOLD, "%s", tracy::MemSizeToString( memoryLimit ) );
|
||||||
|
}
|
||||||
|
printf( " | " );
|
||||||
|
AnsiPrintf( ANSI_RED, "%s", tracy::TimeToString( worker.GetLastTime() - firstTime ) );
|
||||||
|
printf( " | " );
|
||||||
|
AnsiPrintf( ANSI_RED ANSI_BOLD, "%s query backlog", tracy::RealToString( queueSize ) );
|
||||||
|
fflush( stdout );
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef __CAPTURECONSOLE_HPP__
|
#ifndef __CAPTUREOUTPUT_HPP__
|
||||||
#define __CAPTURECONSOLE_HPP__
|
#define __CAPTUREOUTPUT_HPP__
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@@ -24,9 +24,10 @@ bool IsTerminal();
|
|||||||
#endif
|
#endif
|
||||||
void AnsiPrintf( const char* ansiEscape, const char* format, ... );
|
void AnsiPrintf( const char* ansiEscape, const char* format, ... );
|
||||||
|
|
||||||
// Renders the live capture status line (rate / compression / transferred /
|
int WaitForConnection( tracy::Worker& worker );
|
||||||
// memory / time / query backlog) on the current line. Does nothing when
|
|
||||||
// stdout is not a terminal or until the worker reports its first rate sample.
|
void PrintWorkerFailure( tracy::Worker& worker );
|
||||||
|
|
||||||
void PrintCaptureProgress( tracy::Worker& worker, int64_t firstTime, int64_t memoryLimit );
|
void PrintCaptureProgress( tracy::Worker& worker, int64_t firstTime, int64_t memoryLimit );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "../../server/TracyFileWrite.hpp"
|
#include "../../server/TracyFileWrite.hpp"
|
||||||
#include "../../server/TracyPrint.hpp"
|
#include "../../server/TracyPrint.hpp"
|
||||||
@@ -18,8 +19,6 @@
|
|||||||
#include "../../server/TracyWorker.hpp"
|
#include "../../server/TracyWorker.hpp"
|
||||||
#include "../../public/common/TracyVersion.hpp"
|
#include "../../public/common/TracyVersion.hpp"
|
||||||
#include "GitRef.hpp"
|
#include "GitRef.hpp"
|
||||||
#include "CaptureFileBackup.hpp"
|
|
||||||
#include "CaptureConsole.hpp"
|
|
||||||
|
|
||||||
#include "CaptureOutput.hpp"
|
#include "CaptureOutput.hpp"
|
||||||
|
|
||||||
@@ -97,18 +96,21 @@ int main( int argc, char** argv )
|
|||||||
|
|
||||||
if( !address || !output ) Usage();
|
if( !address || !output ) Usage();
|
||||||
|
|
||||||
const char* prepError = nullptr;
|
struct stat st;
|
||||||
const auto prep = tracy::PrepareOutputFile( output, overwrite, &prepError );
|
if( stat( output, &st ) == 0 && !overwrite )
|
||||||
if( prep == tracy::OutputPrep::Exists )
|
|
||||||
{
|
{
|
||||||
printf( "Output file %s already exists! Use -f to force overwrite.\n", output );
|
printf( "Output file %s already exists! Use -f to force overwrite.\n", output );
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
if( prep == tracy::OutputPrep::Unusable )
|
|
||||||
|
FILE* test = fopen( output, "wb" );
|
||||||
|
if( !test )
|
||||||
{
|
{
|
||||||
printf( "Cannot use output file: %s!\n", prepError );
|
printf( "Cannot open output file %s for writing!\n", output );
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
fclose( test );
|
||||||
|
unlink( output );
|
||||||
|
|
||||||
printf( "Connecting to %s:%i...", address, port );
|
printf( "Connecting to %s:%i...", address, port );
|
||||||
fflush( stdout );
|
fflush( stdout );
|
||||||
@@ -135,7 +137,6 @@ int main( int argc, char** argv )
|
|||||||
{
|
{
|
||||||
worker.Disconnect();
|
worker.Disconnect();
|
||||||
s_disconnect.store(false, std::memory_order_relaxed );
|
s_disconnect.store(false, std::memory_order_relaxed );
|
||||||
while( worker.IsConnected() ) std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,12 +168,10 @@ int main( int argc, char** argv )
|
|||||||
f->Finish();
|
f->Finish();
|
||||||
const auto stats = f->GetCompressionStatistics();
|
const auto stats = f->GetCompressionStatistics();
|
||||||
printf( "Trace size %s (%.2f%% ratio)\n", tracy::MemSizeToString( stats.second ), 100.f * stats.second / stats.first );
|
printf( "Trace size %s (%.2f%% ratio)\n", tracy::MemSizeToString( stats.second ), 100.f * stats.second / stats.first );
|
||||||
tracy::DiscardOutputBackup();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AnsiPrintf( ANSI_RED ANSI_BOLD, " failed!\n");
|
AnsiPrintf( ANSI_RED ANSI_BOLD, " failed!\n");
|
||||||
tracy::RestoreOutputBackup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <process.h>
|
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#else
|
#else
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
@@ -8,10 +7,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cerrno>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -32,7 +28,6 @@
|
|||||||
#include "../../server/TracyWorker.hpp"
|
#include "../../server/TracyWorker.hpp"
|
||||||
#include "GitRef.hpp"
|
#include "GitRef.hpp"
|
||||||
|
|
||||||
#include "CaptureConsole.hpp"
|
|
||||||
#include "CaptureOutput.hpp"
|
#include "CaptureOutput.hpp"
|
||||||
|
|
||||||
static std::atomic<bool> g_shutdown{false};
|
static std::atomic<bool> g_shutdown{false};
|
||||||
@@ -41,29 +36,12 @@ static uint16_t g_listenPort = 8086;
|
|||||||
static std::string g_filterName;
|
static std::string g_filterName;
|
||||||
static int g_filterPort = 0;
|
static int g_filterPort = 0;
|
||||||
static int64_t g_memoryLimit = -1;
|
static int64_t g_memoryLimit = -1;
|
||||||
static std::string g_lockFile;
|
|
||||||
|
|
||||||
void SigInt( int )
|
void SigInt( int )
|
||||||
{
|
{
|
||||||
g_shutdown.store( true, std::memory_order_relaxed );
|
g_shutdown.store( true, std::memory_order_relaxed );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RemoveLockFile()
|
|
||||||
{
|
|
||||||
std::error_code ec;
|
|
||||||
std::filesystem::remove( g_lockFile, ec );
|
|
||||||
}
|
|
||||||
|
|
||||||
static long ReadLockPid( const std::string& path )
|
|
||||||
{
|
|
||||||
FILE* in = fopen( path.c_str(), "r" );
|
|
||||||
if( !in ) return -1;
|
|
||||||
long pid = -1;
|
|
||||||
const int result = fscanf( in, "%li", &pid );
|
|
||||||
fclose( in );
|
|
||||||
return ( result == 1 && pid > 0 ) ? pid : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ClientStats
|
struct ClientStats
|
||||||
{
|
{
|
||||||
std::atomic<float> mbps{0};
|
std::atomic<float> mbps{0};
|
||||||
@@ -180,24 +158,17 @@ void CaptureThread( ClientSession* session, const std::string& address, uint16_t
|
|||||||
{
|
{
|
||||||
auto& lock = worker.GetMbpsDataLock();
|
auto& lock = worker.GetMbpsDataLock();
|
||||||
lock.lock();
|
lock.lock();
|
||||||
const auto& mbpsData = worker.GetMbpsData();
|
float mbps = worker.GetMbpsData().back();
|
||||||
if( !mbpsData.empty() )
|
int64_t txTotal = worker.GetDataTransferred();
|
||||||
{
|
|
||||||
session->stats.mbps = mbpsData.back();
|
|
||||||
session->stats.txBytes = worker.GetDataTransferred();
|
|
||||||
}
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
|
session->stats.mbps = mbps;
|
||||||
|
session->stats.txBytes = txTotal;
|
||||||
session->stats.memUsage = tracy::memUsage.load( std::memory_order_relaxed );
|
session->stats.memUsage = tracy::memUsage.load( std::memory_order_relaxed );
|
||||||
|
|
||||||
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
|
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( worker.IsConnected() )
|
|
||||||
{
|
|
||||||
worker.Disconnect();
|
|
||||||
while( worker.IsConnected() ) std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
printf( "\nSaving %s...", outputFile.c_str() );
|
printf( "\nSaving %s...", outputFile.c_str() );
|
||||||
fflush( stdout );
|
fflush( stdout );
|
||||||
|
|
||||||
@@ -358,45 +329,7 @@ int main( int argc, char** argv )
|
|||||||
std::filesystem::create_directories( outputDir );
|
std::filesystem::create_directories( outputDir );
|
||||||
|
|
||||||
InitTerminalDetection();
|
InitTerminalDetection();
|
||||||
#ifndef _WIN32
|
|
||||||
g_lockFile = outputDir + "/.tracy-capture-daemon.lock";
|
|
||||||
{
|
|
||||||
int lockFd = -1;
|
|
||||||
for( int attempt = 0; attempt < 5; attempt++ )
|
|
||||||
{
|
|
||||||
lockFd = open( g_lockFile.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644 );
|
|
||||||
if( lockFd >= 0 ) break;
|
|
||||||
if( errno != EEXIST )
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Error: Cannot create the lock file %s: %s\n", g_lockFile.c_str(), strerror( errno ) );
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
const long owner = ReadLockPid( g_lockFile );
|
|
||||||
if( owner <= 0 )
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Error: The lock file %s has no readable owner PID; remove it by hand if no daemon is running\n", g_lockFile.c_str() );
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// EPERM: the process exists under another user; treat it as alive.
|
|
||||||
if( kill( owner, 0 ) == 0 || errno == EPERM )
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Error: Another capture daemon (pid %ld) is already using the output directory %s\n", owner, outputDir.c_str() );
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
std::error_code ec;
|
|
||||||
std::filesystem::remove( g_lockFile, ec );
|
|
||||||
}
|
|
||||||
if( lockFd < 0 )
|
|
||||||
{
|
|
||||||
fprintf( stderr, "Error: Another capture daemon is already using the output directory %s\n", outputDir.c_str() );
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
const std::string pid = std::to_string( getpid() );
|
|
||||||
write( lockFd, pid.c_str(), pid.size() );
|
|
||||||
close( lockFd );
|
|
||||||
}
|
|
||||||
atexit( RemoveLockFile );
|
|
||||||
#endif
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
signal( SIGINT, SigInt );
|
signal( SIGINT, SigInt );
|
||||||
#else
|
#else
|
||||||
|
|||||||
152
cmake/CPM.cmake
@@ -42,11 +42,7 @@ if(NOT COMMAND cpm_message)
|
|||||||
endfunction()
|
endfunction()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(DEFINED EXTRACTED_CPM_VERSION)
|
set(CURRENT_CPM_VERSION 0.40.2)
|
||||||
set(CURRENT_CPM_VERSION "${EXTRACTED_CPM_VERSION}${CPM_DEVELOPMENT}")
|
|
||||||
else()
|
|
||||||
set(CURRENT_CPM_VERSION 0.43.1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH)
|
get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH)
|
||||||
if(CPM_DIRECTORY)
|
if(CPM_DIRECTORY)
|
||||||
@@ -166,7 +162,7 @@ set(CPM_SOURCE_CACHE
|
|||||||
CACHE PATH "Directory to download CPM dependencies"
|
CACHE PATH "Directory to download CPM dependencies"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT CPM_DONT_UPDATE_MODULE_PATH AND NOT DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR)
|
if(NOT CPM_DONT_UPDATE_MODULE_PATH)
|
||||||
set(CPM_MODULE_PATH
|
set(CPM_MODULE_PATH
|
||||||
"${CMAKE_BINARY_DIR}/CPM_modules"
|
"${CMAKE_BINARY_DIR}/CPM_modules"
|
||||||
CACHE INTERNAL ""
|
CACHE INTERNAL ""
|
||||||
@@ -202,65 +198,9 @@ function(cpm_package_name_from_git_uri URI RESULT)
|
|||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Find the shortest hash that can be used eg, if origin_hash is
|
|
||||||
# cccb77ae9609d2768ed80dd42cec54f77b1f1455 the following files will be checked, until one is found
|
|
||||||
# that is either empty (allowing us to assign origin_hash), or whose contents matches ${origin_hash}
|
|
||||||
#
|
|
||||||
# * .../cccb.hash
|
|
||||||
# * .../cccb77ae.hash
|
|
||||||
# * .../cccb77ae9609.hash
|
|
||||||
# * .../cccb77ae9609d276.hash
|
|
||||||
# * etc
|
|
||||||
#
|
|
||||||
# We will be able to use a shorter path with very high probability, but in the (rare) event that the
|
|
||||||
# first couple characters collide, we will check longer and longer substrings.
|
|
||||||
function(cpm_get_shortest_hash source_cache_dir origin_hash short_hash_output_var)
|
|
||||||
# for compatibility with caches populated by a previous version of CPM, check if a directory using
|
|
||||||
# the full hash already exists
|
|
||||||
if(EXISTS "${source_cache_dir}/${origin_hash}")
|
|
||||||
set(${short_hash_output_var}
|
|
||||||
"${origin_hash}"
|
|
||||||
PARENT_SCOPE
|
|
||||||
)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
foreach(len RANGE 4 40 4)
|
|
||||||
string(SUBSTRING "${origin_hash}" 0 ${len} short_hash)
|
|
||||||
set(hash_lock ${source_cache_dir}/${short_hash}.lock)
|
|
||||||
set(hash_fp ${source_cache_dir}/${short_hash}.hash)
|
|
||||||
# Take a lock, so we don't have a race condition with another instance of cmake. We will release
|
|
||||||
# this lock when we can, however, if there is an error, we want to ensure it gets released on
|
|
||||||
# it's own on exit from the function.
|
|
||||||
file(LOCK ${hash_lock} GUARD FUNCTION)
|
|
||||||
|
|
||||||
# Load the contents of .../${short_hash}.hash
|
|
||||||
file(TOUCH ${hash_fp})
|
|
||||||
file(READ ${hash_fp} hash_fp_contents)
|
|
||||||
|
|
||||||
if(hash_fp_contents STREQUAL "")
|
|
||||||
# Write the origin hash
|
|
||||||
file(WRITE ${hash_fp} ${origin_hash})
|
|
||||||
file(LOCK ${hash_lock} RELEASE)
|
|
||||||
break()
|
|
||||||
elseif(hash_fp_contents STREQUAL origin_hash)
|
|
||||||
file(LOCK ${hash_lock} RELEASE)
|
|
||||||
break()
|
|
||||||
else()
|
|
||||||
file(LOCK ${hash_lock} RELEASE)
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
set(${short_hash_output_var}
|
|
||||||
"${short_hash}"
|
|
||||||
PARENT_SCOPE
|
|
||||||
)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Try to infer package name and version from a url
|
# Try to infer package name and version from a url
|
||||||
function(cpm_package_name_and_ver_from_url url outName outVer)
|
function(cpm_package_name_and_ver_from_url url outName outVer)
|
||||||
if(url MATCHES
|
if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)")
|
||||||
"[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|tar\\.xz|tar\\.zst|zip|ZIP)(\\?|/|$)"
|
|
||||||
)
|
|
||||||
# We matched an archive
|
# We matched an archive
|
||||||
set(filename "${CMAKE_MATCH_1}")
|
set(filename "${CMAKE_MATCH_1}")
|
||||||
|
|
||||||
@@ -329,25 +269,10 @@ endfunction()
|
|||||||
# finding the system library
|
# finding the system library
|
||||||
function(cpm_create_module_file Name)
|
function(cpm_create_module_file Name)
|
||||||
if(NOT CPM_DONT_UPDATE_MODULE_PATH)
|
if(NOT CPM_DONT_UPDATE_MODULE_PATH)
|
||||||
if(DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR)
|
# erase any previous modules
|
||||||
# Redirect find_package calls to the CPM package. This is what FetchContent does when you set
|
file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake
|
||||||
# OVERRIDE_FIND_PACKAGE. The CMAKE_FIND_PACKAGE_REDIRECTS_DIR works for find_package in CONFIG
|
"include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)"
|
||||||
# mode, unlike the Find${Name}.cmake fallback. CMAKE_FIND_PACKAGE_REDIRECTS_DIR is not defined
|
)
|
||||||
# in script mode, or in CMake < 3.24.
|
|
||||||
# https://cmake.org/cmake/help/latest/module/FetchContent.html#fetchcontent-find-package-integration-examples
|
|
||||||
string(TOLOWER ${Name} NameLower)
|
|
||||||
file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config.cmake
|
|
||||||
"include(\"\${CMAKE_CURRENT_LIST_DIR}/${NameLower}-extra.cmake\" OPTIONAL)\n"
|
|
||||||
"include(\"\${CMAKE_CURRENT_LIST_DIR}/${Name}Extra.cmake\" OPTIONAL)\n"
|
|
||||||
)
|
|
||||||
file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config-version.cmake
|
|
||||||
"set(PACKAGE_VERSION_COMPATIBLE TRUE)\n" "set(PACKAGE_VERSION_EXACT TRUE)\n"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake
|
|
||||||
"include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
@@ -550,7 +475,7 @@ function(cpm_add_patches)
|
|||||||
|
|
||||||
# Find the patch program.
|
# Find the patch program.
|
||||||
find_program(PATCH_EXECUTABLE patch)
|
find_program(PATCH_EXECUTABLE patch)
|
||||||
if(CMAKE_HOST_WIN32 AND NOT PATCH_EXECUTABLE)
|
if(WIN32 AND NOT PATCH_EXECUTABLE)
|
||||||
# The Windows git executable is distributed with patch.exe. Find the path to the executable, if
|
# The Windows git executable is distributed with patch.exe. Find the path to the executable, if
|
||||||
# it exists, then search `../usr/bin` and `../../usr/bin` for patch.exe.
|
# it exists, then search `../usr/bin` and `../../usr/bin` for patch.exe.
|
||||||
find_package(Git QUIET)
|
find_package(Git QUIET)
|
||||||
@@ -650,6 +575,14 @@ endfunction()
|
|||||||
function(CPMAddPackage)
|
function(CPMAddPackage)
|
||||||
cpm_set_policies()
|
cpm_set_policies()
|
||||||
|
|
||||||
|
list(LENGTH ARGN argnLength)
|
||||||
|
if(argnLength EQUAL 1)
|
||||||
|
cpm_parse_add_package_single_arg("${ARGN}" ARGN)
|
||||||
|
|
||||||
|
# The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM
|
||||||
|
set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;")
|
||||||
|
endif()
|
||||||
|
|
||||||
set(oneValueArgs
|
set(oneValueArgs
|
||||||
NAME
|
NAME
|
||||||
FORCE
|
FORCE
|
||||||
@@ -672,26 +605,10 @@ function(CPMAddPackage)
|
|||||||
|
|
||||||
set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES)
|
set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES)
|
||||||
|
|
||||||
list(LENGTH ARGN argnLength)
|
|
||||||
|
|
||||||
# Parse single shorthand argument
|
|
||||||
if(argnLength EQUAL 1)
|
|
||||||
cpm_parse_add_package_single_arg("${ARGN}" ARGN)
|
|
||||||
|
|
||||||
# The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM
|
|
||||||
set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;")
|
|
||||||
|
|
||||||
# Parse URI shorthand argument
|
|
||||||
elseif(argnLength GREATER 1 AND "${ARGV0}" STREQUAL "URI")
|
|
||||||
list(REMOVE_AT ARGN 0 1) # remove "URI gh:<...>@version#tag"
|
|
||||||
cpm_parse_add_package_single_arg("${ARGV1}" ARGV0)
|
|
||||||
|
|
||||||
set(ARGN "${ARGV0};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;${ARGN}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
|
cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
|
||||||
|
|
||||||
# Set default values for arguments
|
# Set default values for arguments
|
||||||
|
|
||||||
if(NOT DEFINED CPM_ARGS_VERSION)
|
if(NOT DEFINED CPM_ARGS_VERSION)
|
||||||
if(DEFINED CPM_ARGS_GIT_TAG)
|
if(DEFINED CPM_ARGS_GIT_TAG)
|
||||||
cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION)
|
cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION)
|
||||||
@@ -768,23 +685,10 @@ function(CPMAddPackage)
|
|||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT DEFINED CPM_${CPM_ARGS_NAME}_SOURCE AND DEFINED ENV{CPM_${CPM_ARGS_NAME}_SOURCE})
|
|
||||||
# Normalize separators to support Windows paths when reading from environment variables.
|
|
||||||
file(TO_CMAKE_PATH "$ENV{CPM_${CPM_ARGS_NAME}_SOURCE}" CPM_${CPM_ARGS_NAME}_SOURCE)
|
|
||||||
message(WARNING "${CPM_INDENT} '${CPM_ARGS_NAME}' version overridden by environment variable "
|
|
||||||
"CPM_${CPM_ARGS_NAME}_SOURCE='${CPM_${CPM_ARGS_NAME}_SOURCE}'"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Check for manual overrides
|
# Check for manual overrides
|
||||||
if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "")
|
if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "")
|
||||||
set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE})
|
set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE})
|
||||||
set(CPM_${CPM_ARGS_NAME}_SOURCE "")
|
set(CPM_${CPM_ARGS_NAME}_SOURCE "")
|
||||||
if(NOT DEFINED ENV{CPM_${CPM_ARGS_NAME}_SOURCE})
|
|
||||||
message(WARNING "${CPM_INDENT} '${CPM_ARGS_NAME}' version overridden by CMake variable "
|
|
||||||
"CPM_${CPM_ARGS_NAME}_SOURCE='${PACKAGE_SOURCE}'"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME "${CPM_ARGS_NAME}"
|
NAME "${CPM_ARGS_NAME}"
|
||||||
SOURCE_DIR "${PACKAGE_SOURCE}"
|
SOURCE_DIR "${PACKAGE_SOURCE}"
|
||||||
@@ -875,19 +779,9 @@ function(CPMAddPackage)
|
|||||||
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${CPM_ARGS_CUSTOM_CACHE_KEY})
|
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${CPM_ARGS_CUSTOM_CACHE_KEY})
|
||||||
elseif(CPM_USE_NAMED_CACHE_DIRECTORIES)
|
elseif(CPM_USE_NAMED_CACHE_DIRECTORIES)
|
||||||
string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG")
|
string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG")
|
||||||
cpm_get_shortest_hash(
|
|
||||||
"${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory
|
|
||||||
"${origin_hash}" # Input hash
|
|
||||||
origin_hash # Computed hash
|
|
||||||
)
|
|
||||||
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME})
|
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME})
|
||||||
else()
|
else()
|
||||||
string(SHA1 origin_hash "${origin_parameters}")
|
string(SHA1 origin_hash "${origin_parameters}")
|
||||||
cpm_get_shortest_hash(
|
|
||||||
"${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory
|
|
||||||
"${origin_hash}" # Input hash
|
|
||||||
origin_hash # Computed hash
|
|
||||||
)
|
|
||||||
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash})
|
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash})
|
||||||
endif()
|
endif()
|
||||||
# Expand `download_directory` relative path. This is important because EXISTS doesn't work for
|
# Expand `download_directory` relative path. This is important because EXISTS doesn't work for
|
||||||
@@ -954,9 +848,7 @@ function(CPMAddPackage)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT "${DOWNLOAD_ONLY}")
|
cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")")
|
||||||
cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CPM_PACKAGE_LOCK_ENABLED)
|
if(CPM_PACKAGE_LOCK_ENABLED)
|
||||||
if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK)
|
if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK)
|
||||||
@@ -977,9 +869,8 @@ function(CPMAddPackage)
|
|||||||
# Calling FetchContent_MakeAvailable will then internally forward these options to
|
# Calling FetchContent_MakeAvailable will then internally forward these options to
|
||||||
# add_subdirectory. Up until these changes, we had to call FetchContent_Populate and
|
# add_subdirectory. Up until these changes, we had to call FetchContent_Populate and
|
||||||
# add_subdirectory separately, which is no longer necessary and has been deprecated as of 3.30.
|
# add_subdirectory separately, which is no longer necessary and has been deprecated as of 3.30.
|
||||||
# A Bug in CMake prevents us to use the non-deprecated functions until 3.30.3.
|
|
||||||
set(fetchContentDeclareExtraArgs "")
|
set(fetchContentDeclareExtraArgs "")
|
||||||
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3")
|
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.28.0")
|
||||||
if(${CPM_ARGS_EXCLUDE_FROM_ALL})
|
if(${CPM_ARGS_EXCLUDE_FROM_ALL})
|
||||||
list(APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL)
|
list(APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL)
|
||||||
endif()
|
endif()
|
||||||
@@ -1005,7 +896,7 @@ function(CPMAddPackage)
|
|||||||
if(CPM_SOURCE_CACHE AND download_directory)
|
if(CPM_SOURCE_CACHE AND download_directory)
|
||||||
file(LOCK ${download_directory}/../cmake.lock RELEASE)
|
file(LOCK ${download_directory}/../cmake.lock RELEASE)
|
||||||
endif()
|
endif()
|
||||||
if(${populated} AND ${CMAKE_VERSION} VERSION_LESS "3.30.3")
|
if(${populated} AND ${CMAKE_VERSION} VERSION_LESS "3.28.0")
|
||||||
cpm_add_subdirectory(
|
cpm_add_subdirectory(
|
||||||
"${CPM_ARGS_NAME}"
|
"${CPM_ARGS_NAME}"
|
||||||
"${DOWNLOAD_ONLY}"
|
"${DOWNLOAD_ONLY}"
|
||||||
@@ -1207,7 +1098,7 @@ function(cpm_fetch_package PACKAGE DOWNLOAD_ONLY populated)
|
|||||||
string(TOLOWER "${PACKAGE}" lower_case_name)
|
string(TOLOWER "${PACKAGE}" lower_case_name)
|
||||||
|
|
||||||
if(NOT ${lower_case_name}_POPULATED)
|
if(NOT ${lower_case_name}_POPULATED)
|
||||||
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3")
|
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.28.0")
|
||||||
if(DOWNLOAD_ONLY)
|
if(DOWNLOAD_ONLY)
|
||||||
# MakeAvailable will call add_subdirectory internally which is not what we want when
|
# MakeAvailable will call add_subdirectory internally which is not what we want when
|
||||||
# DOWNLOAD_ONLY is set. Populate will only download the dependency without adding it to the
|
# DOWNLOAD_ONLY is set. Populate will only download the dependency without adding it to the
|
||||||
@@ -1256,7 +1147,6 @@ function(cpm_parse_option OPTION)
|
|||||||
else()
|
else()
|
||||||
math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1")
|
math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1")
|
||||||
string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE)
|
string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE)
|
||||||
string(STRIP "${OPTION_VALUE}" OPTION_VALUE)
|
|
||||||
endif()
|
endif()
|
||||||
set(OPTION_KEY
|
set(OPTION_KEY
|
||||||
"${OPTION_KEY}"
|
"${OPTION_KEY}"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function(add_git_ref target)
|
|||||||
if(Git_FOUND)
|
if(Git_FOUND)
|
||||||
add_custom_target(git-ref
|
add_custom_target(git-ref
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo "#pragma once" > GitRef.hpp.tmp
|
COMMAND ${CMAKE_COMMAND} -E echo "#pragma once" > GitRef.hpp.tmp
|
||||||
COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} log -1 "--format=namespace tracy { static inline const char* GitRef = %x22%h%x22; }" ${GIT_REV} >> GitRef.hpp.tmp || ${CMAKE_COMMAND} -E echo "namespace tracy { static inline const char* GitRef = \"unknown\"; }" >> GitRef.hpp.tmp
|
COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} log -1 "--format=namespace tracy { static inline const char* GitRef = %x22%h%x22; }" ${GIT_REV} >> GitRef.hpp.tmp || echo "namespace tracy { static inline const char* GitRef = \"unknown\"; }" >> GitRef.hpp.tmp
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different GitRef.hpp.tmp GitRef.hpp
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different GitRef.hpp.tmp GitRef.hpp
|
||||||
BYPRODUCTS GitRef.hpp GitRef.hpp.tmp
|
BYPRODUCTS GitRef.hpp GitRef.hpp.tmp
|
||||||
VERBATIM
|
VERBATIM
|
||||||
|
|||||||
@@ -30,19 +30,15 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
if(NOT CMAKE_OSX_DEPLOYMENT_TARGET)
|
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0")
|
|
||||||
endif()
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "15")
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "15")
|
||||||
message(FATAL_ERROR "Apple Clang 15 or newer is required.")
|
message(FATAL_ERROR "Apple Clang 15 or newer is required.")
|
||||||
elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "16" AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "13.3")
|
elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "16")
|
||||||
# AppleClang 15 has issues with to_chars in <chrono> if target is too old
|
# AppleClang 15 has issues with to_chars in <chrono> if target is too old
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.3")
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-mmacosx-version-min=13.3>)
|
||||||
endif()
|
endif()
|
||||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fexperimental-library>)
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fexperimental-library>)
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "macOS deployment target: ${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
|||||||
12
cmake/gl3w-extra-symbols.patch
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
diff --git a/extra_symbols.txt b/extra_symbols.txt
|
||||||
|
index b95bb58..6b8f616 100644
|
||||||
|
--- a/extra_symbols.txt
|
||||||
|
+++ b/extra_symbols.txt
|
||||||
|
@@ -1,3 +1,7 @@
|
||||||
|
+glCompressedTexImage2D
|
||||||
|
+GL_LINEAR_MIPMAP_LINEAR
|
||||||
|
+GL_TEXTURE_WRAP_S
|
||||||
|
+GL_TEXTURE_WRAP_T
|
||||||
|
glReadPixels
|
||||||
|
glClearColor
|
||||||
|
glClear
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
diff --git i/imconfig.h w/imconfig.h
|
|
||||||
index b40db3898..561ab7782 100644
|
|
||||||
--- i/imconfig.h
|
|
||||||
+++ w/imconfig.h
|
|
||||||
@@ -42,7 +42,7 @@
|
|
||||||
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
|
||||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
|
||||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
|
||||||
-//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
|
||||||
+#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
|
||||||
//#define IMGUI_DISABLE_TIME_FUNCTIONS // Don't setup default platform_io.Platform_SessionDate value using time(), localtime_r().
|
|
||||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
|
||||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
diff --git i/imconfig.h w/imconfig.h
|
|
||||||
index b40db3898..f7f864394 100644
|
|
||||||
--- i/imconfig.h
|
|
||||||
+++ w/imconfig.h
|
|
||||||
@@ -50,7 +50,7 @@
|
|
||||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
|
||||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
|
||||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
|
||||||
-//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean + ProggyForever). Remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert.
|
|
||||||
+#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean + ProggyForever). Remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert.
|
|
||||||
//#define IMGUI_DISABLE_DEFAULT_FONT_BITMAP // Disable default embedded bitmap font (ProggyClean). Remove ~9 KB from output binary. AddFontDefaultBitmap() will assert.
|
|
||||||
//#define IMGUI_DISABLE_DEFAULT_FONT_VECTOR // Disable default embedded vector font (ProggyForever), Remove ~14 KB from output binary. AddFontDefaultVector() will assert.
|
|
||||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
|
|
||||||
index a9e32b7ac..2cdbc4812 100644
|
|
||||||
--- a/backends/imgui_impl_opengl3.cpp
|
|
||||||
+++ b/backends/imgui_impl_opengl3.cpp
|
|
||||||
@@ -1069,7 +1069,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
|
||||||
bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3);
|
|
||||||
#endif
|
|
||||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
|
||||||
- bd->HasBindSampler = (bd->GlVersion >= 330 || bd->GlProfileIsES3);
|
|
||||||
+ //bd->HasBindSampler = (bd->GlVersion >= 330 || bd->GlProfileIsES3);
|
|
||||||
#endif
|
|
||||||
bd->HasClipOrigin = (bd->GlVersion >= 450);
|
|
||||||
#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
|
|
||||||
--- a/imgui_tables.cpp
|
|
||||||
+++ b/imgui_tables.cpp
|
|
||||||
@@ -4082,7 +4082,9 @@
|
|
||||||
}
|
|
||||||
if (load_flags & ImGuiTableFlags_Reorderable)
|
|
||||||
column->DisplayOrder = column_settings->DisplayOrder;
|
|
||||||
- else
|
|
||||||
+ else if (column_settings->Index != -1)
|
|
||||||
+ // An Index of -1 means this column had no "Column N" line in the .ini file (columns without saved
|
|
||||||
+ // data are not written). Keep the display order as initialized (identity for non-reorderable tables). (#9519)
|
|
||||||
column->DisplayOrder = column_settings->Index; // Because default depends on previous Index, we need to set that up and cannot rely on TableInitColumnDefaults()
|
|
||||||
if ((load_flags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
|
|
||||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = (column_settings->IsEnabled == 1);
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
set(PANDOC_VERSION 3.11)
|
|
||||||
|
|
||||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
|
||||||
|
|
||||||
find_program(TRACY_PANDOC pandoc)
|
|
||||||
if(TRACY_PANDOC)
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${TRACY_PANDOC} --version
|
|
||||||
OUTPUT_VARIABLE _pandoc_version
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
RESULT_VARIABLE _pandoc_rc
|
|
||||||
ERROR_QUIET)
|
|
||||||
string(REGEX MATCH "pandoc [0-9.]+" _pandoc_version "${_pandoc_version}")
|
|
||||||
if(NOT _pandoc_rc EQUAL 0 OR NOT _pandoc_version STREQUAL "pandoc ${PANDOC_VERSION}")
|
|
||||||
set(TRACY_PANDOC "")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if(TRACY_PANDOC)
|
|
||||||
message(STATUS "Using system pandoc ${PANDOC_VERSION}: ${TRACY_PANDOC}")
|
|
||||||
else()
|
|
||||||
if(WIN32)
|
|
||||||
if(NOT CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
|
|
||||||
message(FATAL_ERROR "No pinned pandoc ${PANDOC_VERSION} binary for ${CMAKE_HOST_SYSTEM_PROCESSOR}; install pandoc ${PANDOC_VERSION}")
|
|
||||||
endif()
|
|
||||||
set(_pandoc_asset pandoc-${PANDOC_VERSION}-windows-x86_64.zip)
|
|
||||||
set(_pandoc_hash 2ab72baf2399450e148ddf7a2a8689806c42e1bba71862b57e220fd9b8456d3d)
|
|
||||||
set(_pandoc_exe pandoc-${PANDOC_VERSION}/pandoc.exe)
|
|
||||||
elseif(APPLE)
|
|
||||||
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)$")
|
|
||||||
set(_pandoc_asset pandoc-${PANDOC_VERSION}-arm64-macOS.zip)
|
|
||||||
set(_pandoc_hash 15806bedf9517bfead72e88fe6a6696635c3691efbb6e152173440e9c5bb50b4)
|
|
||||||
set(_pandoc_exe pandoc-${PANDOC_VERSION}-arm64/bin/pandoc)
|
|
||||||
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
|
|
||||||
set(_pandoc_asset pandoc-${PANDOC_VERSION}-x86_64-macOS.zip)
|
|
||||||
set(_pandoc_hash 3b1c1b57f160112c821d02f23d946ede8b7f57a6ccf4632a25a512d334a9291f)
|
|
||||||
set(_pandoc_exe pandoc-${PANDOC_VERSION}-x86_64/bin/pandoc)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "No pinned pandoc ${PANDOC_VERSION} binary for ${CMAKE_HOST_SYSTEM_PROCESSOR}; install pandoc ${PANDOC_VERSION}")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)$")
|
|
||||||
set(_pandoc_asset pandoc-${PANDOC_VERSION}-linux-arm64.tar.gz)
|
|
||||||
set(_pandoc_hash 56ed5566ec41d22ec9ee0704e6ac0b98ba102e92384efd5306173a22d314c79a)
|
|
||||||
set(_pandoc_exe pandoc-${PANDOC_VERSION}/bin/pandoc)
|
|
||||||
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
|
|
||||||
set(_pandoc_asset pandoc-${PANDOC_VERSION}-linux-amd64.tar.gz)
|
|
||||||
set(_pandoc_hash 37edb3bbcf722f921a009941bf5874e2e0c09263226c9b4a2d980788cb062ab6)
|
|
||||||
set(_pandoc_exe pandoc-${PANDOC_VERSION}/bin/pandoc)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "No pinned pandoc ${PANDOC_VERSION} binary for ${CMAKE_HOST_SYSTEM_PROCESSOR}; install pandoc ${PANDOC_VERSION}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Shared cache: $PANDOC_CACHE_DIR, else the platform user cache directory.
|
|
||||||
if(DEFINED ENV{PANDOC_CACHE_DIR})
|
|
||||||
set(_pandoc_cache $ENV{PANDOC_CACHE_DIR})
|
|
||||||
elseif(WIN32)
|
|
||||||
set(_pandoc_cache $ENV{LOCALAPPDATA}/tracy/pandoc)
|
|
||||||
elseif(APPLE)
|
|
||||||
set(_pandoc_cache $ENV{HOME}/Library/Caches/tracy/pandoc)
|
|
||||||
elseif(DEFINED ENV{XDG_CACHE_HOME})
|
|
||||||
set(_pandoc_cache $ENV{XDG_CACHE_HOME}/tracy/pandoc)
|
|
||||||
else()
|
|
||||||
set(_pandoc_cache $ENV{HOME}/.cache/tracy/pandoc)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(TRACY_PANDOC ${_pandoc_cache}/${_pandoc_exe})
|
|
||||||
if(NOT EXISTS ${TRACY_PANDOC})
|
|
||||||
# Concurrent configures on a cold cache can collide; worst case the
|
|
||||||
# hash check fails and a rerun succeeds.
|
|
||||||
message(STATUS "Downloading pandoc ${PANDOC_VERSION} (${_pandoc_asset})")
|
|
||||||
file(MAKE_DIRECTORY ${_pandoc_cache})
|
|
||||||
file(DOWNLOAD
|
|
||||||
https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/${_pandoc_asset}
|
|
||||||
${_pandoc_cache}/pandoc.archive
|
|
||||||
EXPECTED_HASH SHA256=${_pandoc_hash}
|
|
||||||
STATUS _pandoc_download)
|
|
||||||
list(GET _pandoc_download 0 _pandoc_download_rc)
|
|
||||||
if(NOT _pandoc_download_rc STREQUAL "0")
|
|
||||||
file(REMOVE ${_pandoc_cache}/pandoc.archive)
|
|
||||||
message(FATAL_ERROR "Failed to download pandoc ${PANDOC_VERSION}: ${_pandoc_download}")
|
|
||||||
endif()
|
|
||||||
file(ARCHIVE_EXTRACT
|
|
||||||
INPUT ${_pandoc_cache}/pandoc.archive
|
|
||||||
DESTINATION ${_pandoc_cache})
|
|
||||||
file(REMOVE ${_pandoc_cache}/pandoc.archive)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
|
|
||||||
index d6a9edc..abef8d7 100644
|
|
||||||
--- a/src/CMakeLists.txt
|
|
||||||
+++ b/src/CMakeLists.txt
|
|
||||||
@@ -32,18 +32,18 @@ if(nfd_PLATFORM STREQUAL PLATFORM_LINUX)
|
|
||||||
if(NFD_WAYLAND)
|
|
||||||
pkg_check_modules(WAYLAND REQUIRED wayland-client)
|
|
||||||
message(STATUS "Using Wayland version: ${WAYLAND_VERSION}")
|
|
||||||
- set(NFD_WAYLAND_PROTOCOL_XDG_FOREIGN ${CMAKE_CURRENT_SOURCE_DIR}/../3ps/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v1.xml)
|
|
||||||
+ set(NFD_WAYLAND_PROTOCOL_XDG_FOREIGN ${CMAKE_CURRENT_SOURCE_DIR}/../3ps/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v2.xml)
|
|
||||||
add_custom_command(
|
|
||||||
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.h
|
|
||||||
- COMMAND wayland-scanner client-header < ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN} > ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.h
|
|
||||||
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v2.h
|
|
||||||
+ COMMAND wayland-scanner client-header < ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN} > ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v2.h
|
|
||||||
MAIN_DEPENDENCY ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN}
|
|
||||||
)
|
|
||||||
add_custom_command(
|
|
||||||
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.c
|
|
||||||
- COMMAND wayland-scanner private-code < ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN} > ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.c
|
|
||||||
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v2.c
|
|
||||||
+ COMMAND wayland-scanner private-code < ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN} > ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v2.c
|
|
||||||
MAIN_DEPENDENCY ${NFD_WAYLAND_PROTOCOL_XDG_FOREIGN}
|
|
||||||
)
|
|
||||||
- list(APPEND SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.h ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v1.c)
|
|
||||||
+ list(APPEND SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v2.h ${CMAKE_CURRENT_BINARY_DIR}/xdg-foreign-unstable-v2.c)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
diff --git a/src/nfd_gtk.cpp b/src/nfd_gtk.cpp
|
|
||||||
index 76fdcd7..cec691d 100644
|
|
||||||
--- a/src/nfd_gtk.cpp
|
|
||||||
+++ b/src/nfd_gtk.cpp
|
|
||||||
@@ -433,17 +433,17 @@ gint RunDialogWithFocus(GtkDialog* dialog) {
|
|
||||||
|
|
||||||
#if defined(NFD_WAYLAND)
|
|
||||||
void DestroyXdgExported(void* context) {
|
|
||||||
- zxdg_exported_v1_destroy(static_cast<struct zxdg_exported_v1*>(context));
|
|
||||||
+ zxdg_exported_v2_destroy(static_cast<struct zxdg_exported_v2*>(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
-void zxdg_exported_v1_handle(void* context, struct zxdg_exported_v1*, const char* handle) {
|
|
||||||
+void zxdg_exported_v2_handle(void* context, struct zxdg_exported_v2*, const char* handle) {
|
|
||||||
GdkWindow* childWindow = static_cast<GdkWindow*>(context);
|
|
||||||
// GDK doesn't modify the string, even though it takes char*, so we can cast away the constness
|
|
||||||
gdk_wayland_window_set_transient_for_exported(childWindow, const_cast<char*>(handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
-constexpr struct zxdg_exported_v1_listener wayland_xdg_exported_v1_listener{
|
|
||||||
- &zxdg_exported_v1_handle};
|
|
||||||
+constexpr struct zxdg_exported_v2_listener wayland_xdg_exported_v2_listener{
|
|
||||||
+ &zxdg_exported_v2_handle};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// This is an RAII class that wraps the parenting of a GtkWidget (the file dialog).
|
|
||||||
@@ -645,17 +645,17 @@ struct NativeWindowParenter {
|
|
||||||
|
|
||||||
#if defined(NFD_WAYLAND)
|
|
||||||
void SetParentWayland(GdkWindow* childWindow) {
|
|
||||||
- if (wayland_display && wayland_xdg_exporter_v1) {
|
|
||||||
- struct zxdg_exported_v1* exported = zxdg_exporter_v1_export(
|
|
||||||
- wayland_xdg_exporter_v1, static_cast<struct wl_surface*>(parentWindowHandle));
|
|
||||||
+ if (wayland_display && wayland_xdg_exporter_v2) {
|
|
||||||
+ struct zxdg_exported_v2* exported = zxdg_exporter_v2_export_toplevel(
|
|
||||||
+ wayland_xdg_exporter_v2, static_cast<struct wl_surface*>(parentWindowHandle));
|
|
||||||
if (!exported) {
|
|
||||||
// if we fail to export the wl_surface, act as if the window has no parent
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
- zxdg_exported_v1_add_listener(
|
|
||||||
- exported, &wayland_xdg_exported_v1_listener, static_cast<void*>(childWindow));
|
|
||||||
+ zxdg_exported_v2_add_listener(
|
|
||||||
+ exported, &wayland_xdg_exported_v2_listener, static_cast<void*>(childWindow));
|
|
||||||
wl_display_roundtrip(wayland_display);
|
|
||||||
- zxdg_exported_v1_set_user_data(exported, nullptr);
|
|
||||||
+ zxdg_exported_v2_set_user_data(exported, nullptr);
|
|
||||||
destroy.fn = &DestroyXdgExported;
|
|
||||||
destroy.context = static_cast<void*>(exported);
|
|
||||||
}
|
|
||||||
diff --git a/src/nfd_linux_shared.hpp b/src/nfd_linux_shared.hpp
|
|
||||||
index 1413bac..e826f1e 100644
|
|
||||||
--- a/src/nfd_linux_shared.hpp
|
|
||||||
+++ b/src/nfd_linux_shared.hpp
|
|
||||||
@@ -14,7 +14,7 @@
|
|
||||||
|
|
||||||
#ifdef NFD_WAYLAND
|
|
||||||
#include <wayland-client.h>
|
|
||||||
-#include "xdg-foreign-unstable-v1.h"
|
|
||||||
+#include "xdg-foreign-unstable-v2.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
@@ -81,9 +81,9 @@ nfdnchar_t* emit_case_insensitive_glob(const nfdnchar_t* begin,
|
|
||||||
#ifdef NFD_WAYLAND
|
|
||||||
struct wl_display* wayland_display;
|
|
||||||
struct wl_registry* wayland_registry;
|
|
||||||
-uint32_t wayland_xdg_exporter_v1_name;
|
|
||||||
-struct zxdg_exporter_v1* wayland_xdg_exporter_v1;
|
|
||||||
-constexpr const char* XDG_EXPORTER_V1 = "zxdg_exporter_v1";
|
|
||||||
+uint32_t wayland_xdg_exporter_v2_name;
|
|
||||||
+struct zxdg_exporter_v2* wayland_xdg_exporter_v2;
|
|
||||||
+constexpr const char* XDG_EXPORTER_V2 = "zxdg_exporter_v2";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void EmptyFn(void*) {}
|
|
||||||
@@ -103,19 +103,19 @@ void registry_handle_global(void* context,
|
|
||||||
uint32_t version) {
|
|
||||||
(void)context;
|
|
||||||
(void)version;
|
|
||||||
- if (strcmp(interface, XDG_EXPORTER_V1) == 0) {
|
|
||||||
- wayland_xdg_exporter_v1_name = name;
|
|
||||||
- wayland_xdg_exporter_v1 = static_cast<struct zxdg_exporter_v1*>(wl_registry_bind(
|
|
||||||
- registry, name, &zxdg_exporter_v1_interface, zxdg_exporter_v1_interface.version));
|
|
||||||
+ if (strcmp(interface, XDG_EXPORTER_V2) == 0) {
|
|
||||||
+ wayland_xdg_exporter_v2_name = name;
|
|
||||||
+ wayland_xdg_exporter_v2 = static_cast<struct zxdg_exporter_v2*>(wl_registry_bind(
|
|
||||||
+ registry, name, &zxdg_exporter_v2_interface, zxdg_exporter_v2_interface.version));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void registry_handle_global_remove(void* context, struct wl_registry* registry, uint32_t name) {
|
|
||||||
(void)context;
|
|
||||||
(void)registry;
|
|
||||||
- if (wayland_xdg_exporter_v1 && name == wayland_xdg_exporter_v1_name) {
|
|
||||||
- zxdg_exporter_v1_destroy(wayland_xdg_exporter_v1);
|
|
||||||
- wayland_xdg_exporter_v1 = nullptr;
|
|
||||||
+ if (wayland_xdg_exporter_v2 && name == wayland_xdg_exporter_v2_name) {
|
|
||||||
+ zxdg_exporter_v2_destroy(wayland_xdg_exporter_v2);
|
|
||||||
+ wayland_xdg_exporter_v2 = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ void NFD_Wayland_Init(void) {
|
|
||||||
|
|
||||||
void NFD_Wayland_Quit(void) {
|
|
||||||
if (wayland_display) {
|
|
||||||
- if (wayland_xdg_exporter_v1) zxdg_exporter_v1_destroy(wayland_xdg_exporter_v1);
|
|
||||||
+ if (wayland_xdg_exporter_v2) zxdg_exporter_v2_destroy(wayland_xdg_exporter_v2);
|
|
||||||
wl_registry_destroy(wayland_registry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -143,7 +143,7 @@ nfdresult_t NFD_SetWaylandDisplay(wl_display* display) {
|
|
||||||
wayland_display = display;
|
|
||||||
if (wayland_display) {
|
|
||||||
wayland_registry = wl_display_get_registry(wayland_display);
|
|
||||||
- wayland_xdg_exporter_v1 = nullptr;
|
|
||||||
+ wayland_xdg_exporter_v2 = nullptr;
|
|
||||||
// seems like registry can't be null
|
|
||||||
wl_registry_add_listener(wayland_registry, &wayland_registry_listener, nullptr);
|
|
||||||
wl_display_roundtrip(wayland_display);
|
|
||||||
diff --git a/src/nfd_portal.cpp b/src/nfd_portal.cpp
|
|
||||||
index df840e0..e66ca7f 100644
|
|
||||||
--- a/src/nfd_portal.cpp
|
|
||||||
+++ b/src/nfd_portal.cpp
|
|
||||||
@@ -148,10 +148,10 @@ constexpr const char* DBUS_REQUEST_IFACE = "org.freedesktop.portal.Request";
|
|
||||||
constexpr const char* WAYLAND_PREFIX = "wayland:";
|
|
||||||
|
|
||||||
void DestroyXdgExported(void* context) {
|
|
||||||
- zxdg_exported_v1_destroy(static_cast<struct zxdg_exported_v1*>(context));
|
|
||||||
+ zxdg_exported_v2_destroy(static_cast<struct zxdg_exported_v2*>(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
-void zxdg_exported_v1_handle(void* context, struct zxdg_exported_v1*, const char* handle) {
|
|
||||||
+void zxdg_exported_v2_handle(void* context, struct zxdg_exported_v2*, const char* handle) {
|
|
||||||
DBusMessageIter& iter = *static_cast<DBusMessageIter*>(context);
|
|
||||||
const size_t handle_len = strlen(handle);
|
|
||||||
const size_t prefix_len = strlen(WAYLAND_PREFIX);
|
|
||||||
@@ -163,8 +163,8 @@ void zxdg_exported_v1_handle(void* context, struct zxdg_exported_v1*, const char
|
|
||||||
NFDi_Free(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
-constexpr struct zxdg_exported_v1_listener wayland_xdg_exported_v1_listener{
|
|
||||||
- &zxdg_exported_v1_handle};
|
|
||||||
+constexpr struct zxdg_exported_v2_listener wayland_xdg_exported_v2_listener{
|
|
||||||
+ &zxdg_exported_v2_handle};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void AppendOpenFileQueryParentWindow(DBusMessageIter& iter,
|
|
||||||
@@ -194,18 +194,18 @@ void AppendOpenFileQueryParentWindow(DBusMessageIter& iter,
|
|
||||||
#endif
|
|
||||||
#ifdef NFD_WAYLAND
|
|
||||||
case NFD_WINDOW_HANDLE_TYPE_WAYLAND: {
|
|
||||||
- if (wayland_display && wayland_xdg_exporter_v1) {
|
|
||||||
- struct zxdg_exported_v1* exported = zxdg_exporter_v1_export(
|
|
||||||
- wayland_xdg_exporter_v1, static_cast<struct wl_surface*>(parentWindow.handle));
|
|
||||||
+ if (wayland_display && wayland_xdg_exporter_v2) {
|
|
||||||
+ struct zxdg_exported_v2* exported = zxdg_exporter_v2_export_toplevel(
|
|
||||||
+ wayland_xdg_exporter_v2, static_cast<struct wl_surface*>(parentWindow.handle));
|
|
||||||
if (!exported) {
|
|
||||||
// if we fail to export the wl_surface, act as if the window has no parent
|
|
||||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &STR_EMPTY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
- zxdg_exported_v1_add_listener(
|
|
||||||
- exported, &wayland_xdg_exported_v1_listener, static_cast<void*>(&iter));
|
|
||||||
+ zxdg_exported_v2_add_listener(
|
|
||||||
+ exported, &wayland_xdg_exported_v2_listener, static_cast<void*>(&iter));
|
|
||||||
wl_display_roundtrip(wayland_display);
|
|
||||||
- zxdg_exported_v1_set_user_data(exported, nullptr);
|
|
||||||
+ zxdg_exported_v2_set_user_data(exported, nullptr);
|
|
||||||
destroy.fn = &DestroyXdgExported;
|
|
||||||
destroy.context = static_cast<void*>(exported);
|
|
||||||
return;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
set(TRACY_UTIL_DIR ${CMAKE_CURRENT_LIST_DIR}/../util)
|
|
||||||
|
|
||||||
set(TRACY_UTIL_SOURCES
|
|
||||||
CaptureConsole.cpp
|
|
||||||
CaptureFileBackup.cpp
|
|
||||||
CaptureOutput.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
list(TRANSFORM TRACY_UTIL_SOURCES PREPEND "${TRACY_UTIL_DIR}/")
|
|
||||||
|
|
||||||
add_library(TracyUtil STATIC EXCLUDE_FROM_ALL ${TRACY_UTIL_SOURCES})
|
|
||||||
target_link_libraries(TracyUtil PUBLIC TracyServer)
|
|
||||||
target_include_directories(TracyUtil PUBLIC ${TRACY_UTIL_DIR})
|
|
||||||
@@ -8,20 +8,11 @@ set (ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../")
|
|||||||
include(FindPkgConfig)
|
include(FindPkgConfig)
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake)
|
include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake)
|
||||||
|
|
||||||
if(APPLE AND BUNDLE)
|
|
||||||
set(DLOPT ON)
|
|
||||||
else()
|
|
||||||
set(DLOPT OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(DOWNLOAD_CAPSTONE "Force download capstone" ON)
|
option(DOWNLOAD_CAPSTONE "Force download capstone" ON)
|
||||||
|
option(DOWNLOAD_GLFW "Force download glfw" OFF)
|
||||||
if(VENDOR_GUI)
|
option(DOWNLOAD_FREETYPE "Force download freetype" OFF)
|
||||||
option(DOWNLOAD_GLFW "Force download glfw" ${DLOPT})
|
option(DOWNLOAD_LIBCURL "Force download libcURL" OFF)
|
||||||
option(DOWNLOAD_FREETYPE "Force download freetype" ${DLOPT})
|
option(DOWNLOAD_PUGIXML "Force download pugixml" OFF)
|
||||||
option(DOWNLOAD_LIBCURL "Force download libcURL" OFF)
|
|
||||||
option(DOWNLOAD_PUGIXML "Force download pugixml" ${DLOPT})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# capstone
|
# capstone
|
||||||
|
|
||||||
@@ -35,7 +26,7 @@ else()
|
|||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME capstone
|
NAME capstone
|
||||||
GITHUB_REPOSITORY capstone-engine/capstone
|
GITHUB_REPOSITORY capstone-engine/capstone
|
||||||
GIT_TAG 6.0.0-Alpha10
|
GIT_TAG 6.0.0-Alpha9
|
||||||
OPTIONS
|
OPTIONS
|
||||||
"CAPSTONE_X86_ATT_DISABLE ON"
|
"CAPSTONE_X86_ATT_DISABLE ON"
|
||||||
"CAPSTONE_ALPHA_SUPPORT OFF"
|
"CAPSTONE_ALPHA_SUPPORT OFF"
|
||||||
@@ -67,6 +58,52 @@ else()
|
|||||||
target_link_libraries(TracyCapstone INTERFACE capstone_static)
|
target_link_libraries(TracyCapstone INTERFACE capstone_static)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# GLFW
|
||||||
|
|
||||||
|
if(NOT USE_WAYLAND AND NOT EMSCRIPTEN)
|
||||||
|
pkg_check_modules(GLFW glfw3)
|
||||||
|
if (GLFW_FOUND AND NOT DOWNLOAD_GLFW)
|
||||||
|
add_library(TracyGlfw3 INTERFACE)
|
||||||
|
target_include_directories(TracyGlfw3 INTERFACE ${GLFW_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(TracyGlfw3 INTERFACE ${GLFW_LINK_LIBRARIES})
|
||||||
|
else()
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME glfw
|
||||||
|
GITHUB_REPOSITORY glfw/glfw
|
||||||
|
GIT_TAG 3.4
|
||||||
|
OPTIONS
|
||||||
|
"GLFW_BUILD_EXAMPLES OFF"
|
||||||
|
"GLFW_BUILD_TESTS OFF"
|
||||||
|
"GLFW_BUILD_DOCS OFF"
|
||||||
|
"GLFW_INSTALL OFF"
|
||||||
|
EXCLUDE_FROM_ALL TRUE
|
||||||
|
)
|
||||||
|
add_library(TracyGlfw3 INTERFACE)
|
||||||
|
target_link_libraries(TracyGlfw3 INTERFACE glfw)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# freetype
|
||||||
|
|
||||||
|
pkg_check_modules(FREETYPE freetype2)
|
||||||
|
if (FREETYPE_FOUND AND NOT DOWNLOAD_FREETYPE)
|
||||||
|
add_library(TracyFreetype INTERFACE)
|
||||||
|
target_include_directories(TracyFreetype INTERFACE ${FREETYPE_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(TracyFreetype INTERFACE ${FREETYPE_LINK_LIBRARIES})
|
||||||
|
else()
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME freetype
|
||||||
|
GITHUB_REPOSITORY freetype/freetype
|
||||||
|
GIT_TAG VER-2-14-3
|
||||||
|
OPTIONS
|
||||||
|
"FT_DISABLE_HARFBUZZ ON"
|
||||||
|
"FT_WITH_HARFBUZZ OFF"
|
||||||
|
EXCLUDE_FROM_ALL TRUE
|
||||||
|
)
|
||||||
|
add_library(TracyFreetype INTERFACE)
|
||||||
|
target_link_libraries(TracyFreetype INTERFACE freetype)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Zstd
|
# Zstd
|
||||||
|
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
@@ -95,6 +132,65 @@ set(GETOPT_HEADERS ${GETOPT_DIR}/getopt.h)
|
|||||||
add_library(TracyGetOpt STATIC EXCLUDE_FROM_ALL ${GETOPT_SOURCES} ${GETOPT_HEADERS})
|
add_library(TracyGetOpt STATIC EXCLUDE_FROM_ALL ${GETOPT_SOURCES} ${GETOPT_HEADERS})
|
||||||
target_include_directories(TracyGetOpt PUBLIC ${GETOPT_DIR})
|
target_include_directories(TracyGetOpt PUBLIC ${GETOPT_DIR})
|
||||||
|
|
||||||
|
# ImGui
|
||||||
|
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME ImGui
|
||||||
|
GITHUB_REPOSITORY ocornut/imgui
|
||||||
|
GIT_TAG v1.92.8-docking
|
||||||
|
DOWNLOAD_ONLY TRUE
|
||||||
|
PATCHES
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/imgui-emscripten.patch"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/imgui-loader.patch"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(IMGUI_SOURCES
|
||||||
|
imgui_widgets.cpp
|
||||||
|
imgui_draw.cpp
|
||||||
|
imgui_demo.cpp
|
||||||
|
imgui.cpp
|
||||||
|
imgui_tables.cpp
|
||||||
|
misc/freetype/imgui_freetype.cpp
|
||||||
|
backends/imgui_impl_opengl3.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
list(TRANSFORM IMGUI_SOURCES PREPEND "${ImGui_SOURCE_DIR}/")
|
||||||
|
|
||||||
|
add_library(TracyImGui STATIC EXCLUDE_FROM_ALL ${IMGUI_SOURCES})
|
||||||
|
target_include_directories(TracyImGui PUBLIC ${ImGui_SOURCE_DIR})
|
||||||
|
target_link_libraries(TracyImGui PUBLIC TracyFreetype)
|
||||||
|
target_compile_definitions(TracyImGui PRIVATE "IMGUI_ENABLE_FREETYPE")
|
||||||
|
target_compile_definitions(TracyImGui PUBLIC "IMGUI_USE_WCHAR32")
|
||||||
|
#target_compile_definitions(TracyImGui PUBLIC "IMGUI_DISABLE_OBSOLETE_FUNCTIONS")
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND LEGACY)
|
||||||
|
find_package(X11 REQUIRED)
|
||||||
|
target_link_libraries(TracyImGui PUBLIC ${X11_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
target_compile_definitions(TracyImGui PRIVATE "IMGUI_DISABLE_DEBUG_TOOLS" "IMGUI_DISABLE_DEMO_WINDOWS")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# NFD
|
||||||
|
|
||||||
|
if(NOT NO_FILESELECTOR AND NOT EMSCRIPTEN)
|
||||||
|
if(GTK_FILESELECTOR)
|
||||||
|
set(NFD_PORTAL OFF)
|
||||||
|
else()
|
||||||
|
set(NFD_PORTAL ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME nfd
|
||||||
|
GITHUB_REPOSITORY btzy/nativefiledialog-extended
|
||||||
|
GIT_TAG v1.3.0
|
||||||
|
EXCLUDE_FROM_ALL TRUE
|
||||||
|
OPTIONS
|
||||||
|
"NFD_PORTAL ${NFD_PORTAL}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# PPQSort
|
# PPQSort
|
||||||
|
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
@@ -116,226 +212,93 @@ CPMAddPackage(
|
|||||||
EXCLUDE_FROM_ALL TRUE
|
EXCLUDE_FROM_ALL TRUE
|
||||||
)
|
)
|
||||||
|
|
||||||
if(VENDOR_GUI)
|
# md4c
|
||||||
|
|
||||||
# GLFW
|
CPMAddPackage(
|
||||||
|
NAME md4c
|
||||||
|
GITHUB_REPOSITORY mity/md4c
|
||||||
|
GIT_TAG 755ce49acdc7cd682d4502b4796db5ed6a1230fb
|
||||||
|
EXCLUDE_FROM_ALL TRUE
|
||||||
|
)
|
||||||
|
|
||||||
if(NOT USE_WAYLAND AND NOT EMSCRIPTEN)
|
if(NOT EMSCRIPTEN)
|
||||||
pkg_check_modules(GLFW glfw3)
|
|
||||||
if (GLFW_FOUND AND NOT DOWNLOAD_GLFW)
|
|
||||||
add_library(TracyGlfw3 INTERFACE)
|
|
||||||
target_include_directories(TracyGlfw3 INTERFACE ${GLFW_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(TracyGlfw3 INTERFACE ${GLFW_LINK_LIBRARIES})
|
|
||||||
else()
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME glfw
|
|
||||||
GITHUB_REPOSITORY glfw/glfw
|
|
||||||
GIT_TAG 3.5.1
|
|
||||||
OPTIONS
|
|
||||||
"GLFW_BUILD_EXAMPLES OFF"
|
|
||||||
"GLFW_BUILD_TESTS OFF"
|
|
||||||
"GLFW_BUILD_DOCS OFF"
|
|
||||||
"GLFW_INSTALL OFF"
|
|
||||||
EXCLUDE_FROM_ALL TRUE
|
|
||||||
)
|
|
||||||
add_library(TracyGlfw3 INTERFACE)
|
|
||||||
target_link_libraries(TracyGlfw3 INTERFACE glfw)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# freetype
|
# base64
|
||||||
|
|
||||||
pkg_check_modules(FREETYPE freetype2)
|
|
||||||
if (FREETYPE_FOUND AND NOT DOWNLOAD_FREETYPE)
|
|
||||||
add_library(TracyFreetype INTERFACE)
|
|
||||||
target_include_directories(TracyFreetype INTERFACE ${FREETYPE_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(TracyFreetype INTERFACE ${FREETYPE_LINK_LIBRARIES})
|
|
||||||
else()
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME freetype
|
|
||||||
GITHUB_REPOSITORY freetype/freetype
|
|
||||||
GIT_TAG VER-2-14-3
|
|
||||||
OPTIONS
|
|
||||||
"FT_DISABLE_HARFBUZZ ON"
|
|
||||||
"FT_WITH_HARFBUZZ OFF"
|
|
||||||
"FT_DISABLE_ZLIB ON"
|
|
||||||
"FT_DISABLE_BZIP2 ON"
|
|
||||||
"FT_DISABLE_PNG ON"
|
|
||||||
"FT_DISABLE_BROTLI ON"
|
|
||||||
EXCLUDE_FROM_ALL TRUE
|
|
||||||
)
|
|
||||||
add_library(TracyFreetype INTERFACE)
|
|
||||||
target_link_libraries(TracyFreetype INTERFACE freetype)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ImGui
|
|
||||||
|
|
||||||
|
set(BUILD_SHARED_LIBS_SAVE ${BUILD_SHARED_LIBS})
|
||||||
|
set(BUILD_SHARED_LIBS OFF)
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME ImGui
|
NAME base64
|
||||||
GITHUB_REPOSITORY ocornut/imgui
|
GITHUB_REPOSITORY aklomp/base64
|
||||||
GIT_TAG v1.92.9b-docking
|
GIT_TAG v0.5.2
|
||||||
DOWNLOAD_ONLY TRUE
|
|
||||||
PATCHES
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/imgui-emscripten.patch"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/imgui-loader.patch"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/imgui-no-samplers.patch"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/imgui-no-default-font.patch"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/imgui-macos-clipboard.patch"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/imgui-phantom-column.patch"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(IMGUI_SOURCES
|
|
||||||
imgui_widgets.cpp
|
|
||||||
imgui_draw.cpp
|
|
||||||
imgui_demo.cpp
|
|
||||||
imgui.cpp
|
|
||||||
imgui_tables.cpp
|
|
||||||
misc/freetype/imgui_freetype.cpp
|
|
||||||
backends/imgui_impl_opengl3.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
list(TRANSFORM IMGUI_SOURCES PREPEND "${ImGui_SOURCE_DIR}/")
|
|
||||||
|
|
||||||
add_library(TracyImGui STATIC EXCLUDE_FROM_ALL ${IMGUI_SOURCES})
|
|
||||||
target_include_directories(TracyImGui PUBLIC ${ImGui_SOURCE_DIR})
|
|
||||||
target_link_libraries(TracyImGui PUBLIC
|
|
||||||
TracyFreetype
|
|
||||||
${CMAKE_DL_LIBS}
|
|
||||||
)
|
|
||||||
target_compile_definitions(TracyImGui PRIVATE "IMGUI_ENABLE_FREETYPE")
|
|
||||||
target_compile_definitions(TracyImGui PUBLIC "IMGUI_USE_WCHAR32")
|
|
||||||
#target_compile_definitions(TracyImGui PUBLIC "IMGUI_DISABLE_OBSOLETE_FUNCTIONS")
|
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND LEGACY)
|
|
||||||
find_package(X11 REQUIRED)
|
|
||||||
target_link_libraries(TracyImGui PUBLIC ${X11_LIBRARIES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
||||||
target_compile_definitions(TracyImGui PRIVATE "IMGUI_DISABLE_DEBUG_TOOLS" "IMGUI_DISABLE_DEMO_WINDOWS")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
target_link_libraries(TracyImGui PUBLIC "-framework ApplicationServices")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# NFD
|
|
||||||
|
|
||||||
if(NOT NO_FILESELECTOR AND NOT EMSCRIPTEN)
|
|
||||||
if(GTK_FILESELECTOR)
|
|
||||||
set(NFD_PORTAL OFF)
|
|
||||||
else()
|
|
||||||
set(NFD_PORTAL ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME nfd
|
|
||||||
GITHUB_REPOSITORY btzy/nativefiledialog-extended
|
|
||||||
GIT_TAG 7bbbd9fe6b1d1549b41df138f614d1a44df9ba08
|
|
||||||
EXCLUDE_FROM_ALL TRUE
|
|
||||||
PATCHES
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/nfd-xdg-foreign-v2.patch"
|
|
||||||
OPTIONS
|
|
||||||
"BUILD_SHARED_LIBS OFF"
|
|
||||||
"NFD_PORTAL ${NFD_PORTAL}"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# md4c
|
|
||||||
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME md4c
|
|
||||||
GITHUB_REPOSITORY mity/md4c
|
|
||||||
GIT_TAG 00788b157b2b6ef3efd8659b66f6521229f382d7
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
"BUILD_SHARED_LIBS OFF"
|
"BASE64_BUILD_CLI OFF"
|
||||||
|
"BASE64_WITH_OpenMP OFF"
|
||||||
|
EXCLUDE_FROM_ALL TRUE
|
||||||
|
)
|
||||||
|
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVE})
|
||||||
|
|
||||||
|
# tidy
|
||||||
|
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME tidy
|
||||||
|
GITHUB_REPOSITORY htacg/tidy-html5
|
||||||
|
GIT_TAG 5.8.0
|
||||||
|
PATCHES
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/tidy-cmake.patch"
|
||||||
EXCLUDE_FROM_ALL TRUE
|
EXCLUDE_FROM_ALL TRUE
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT EMSCRIPTEN)
|
# usearch
|
||||||
|
|
||||||
# base64
|
CPMAddPackage(
|
||||||
|
NAME usearch
|
||||||
|
GITHUB_REPOSITORY unum-cloud/usearch
|
||||||
|
GIT_TAG v2.25.2
|
||||||
|
EXCLUDE_FROM_ALL TRUE
|
||||||
|
)
|
||||||
|
|
||||||
set(BUILD_SHARED_LIBS_SAVE ${BUILD_SHARED_LIBS})
|
# pugixml
|
||||||
set(BUILD_SHARED_LIBS OFF)
|
|
||||||
|
pkg_check_modules(PUGIXML pugixml)
|
||||||
|
if (PUGIXML_FOUND AND NOT DOWNLOAD_PUGIXML)
|
||||||
|
add_library(TracyPugixml INTERFACE)
|
||||||
|
target_include_directories(TracyPugixml INTERFACE ${PUGIXML_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(TracyPugixml INTERFACE ${PUGIXML_LINK_LIBRARIES})
|
||||||
|
else()
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME base64
|
NAME pugixml
|
||||||
GITHUB_REPOSITORY aklomp/base64
|
GITHUB_REPOSITORY zeux/pugixml
|
||||||
GIT_TAG v0.5.2
|
GIT_TAG v1.15
|
||||||
|
EXCLUDE_FROM_ALL TRUE
|
||||||
|
)
|
||||||
|
add_library(TracyPugixml INTERFACE)
|
||||||
|
target_link_libraries(TracyPugixml INTERFACE pugixml)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# libcurl
|
||||||
|
|
||||||
|
pkg_check_modules(LIBCURL libcurl>=7.87.0)
|
||||||
|
if (LIBCURL_FOUND AND NOT DOWNLOAD_LIBCURL)
|
||||||
|
add_library(TracyLibcurl INTERFACE)
|
||||||
|
target_include_directories(TracyLibcurl INTERFACE ${LIBCURL_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(TracyLibcurl INTERFACE ${LIBCURL_LINK_LIBRARIES})
|
||||||
|
else()
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME libcurl
|
||||||
|
GITHUB_REPOSITORY curl/curl
|
||||||
|
GIT_TAG curl-8_20_0
|
||||||
OPTIONS
|
OPTIONS
|
||||||
"BASE64_BUILD_CLI OFF"
|
"BUILD_STATIC_LIBS ON"
|
||||||
"BASE64_WITH_OpenMP OFF"
|
"BUILD_SHARED_LIBS OFF"
|
||||||
|
"HTTP_ONLY ON"
|
||||||
|
"CURL_ZSTD OFF"
|
||||||
|
"CURL_USE_LIBPSL OFF"
|
||||||
EXCLUDE_FROM_ALL TRUE
|
EXCLUDE_FROM_ALL TRUE
|
||||||
)
|
)
|
||||||
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVE})
|
add_library(TracyLibcurl INTERFACE)
|
||||||
|
target_link_libraries(TracyLibcurl INTERFACE libcurl_static)
|
||||||
# tidy
|
target_include_directories(TracyLibcurl INTERFACE ${libcurl_SOURCE_DIR}/include)
|
||||||
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME tidy
|
|
||||||
GITHUB_REPOSITORY htacg/tidy-html5
|
|
||||||
GIT_TAG 5.8.0
|
|
||||||
PATCHES
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/tidy-cmake.patch"
|
|
||||||
EXCLUDE_FROM_ALL TRUE
|
|
||||||
)
|
|
||||||
|
|
||||||
# usearch
|
|
||||||
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME usearch
|
|
||||||
GITHUB_REPOSITORY unum-cloud/usearch
|
|
||||||
GIT_TAG v2.26.2
|
|
||||||
EXCLUDE_FROM_ALL TRUE
|
|
||||||
)
|
|
||||||
|
|
||||||
# pugixml
|
|
||||||
|
|
||||||
pkg_check_modules(PUGIXML pugixml)
|
|
||||||
if (PUGIXML_FOUND AND NOT DOWNLOAD_PUGIXML)
|
|
||||||
add_library(TracyPugixml INTERFACE)
|
|
||||||
target_include_directories(TracyPugixml INTERFACE ${PUGIXML_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(TracyPugixml INTERFACE ${PUGIXML_LINK_LIBRARIES})
|
|
||||||
else()
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME pugixml
|
|
||||||
GITHUB_REPOSITORY zeux/pugixml
|
|
||||||
GIT_TAG v1.16
|
|
||||||
EXCLUDE_FROM_ALL TRUE
|
|
||||||
)
|
|
||||||
add_library(TracyPugixml INTERFACE)
|
|
||||||
target_link_libraries(TracyPugixml INTERFACE pugixml)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# libcurl
|
|
||||||
|
|
||||||
pkg_check_modules(LIBCURL libcurl>=7.87.0)
|
|
||||||
if (LIBCURL_FOUND AND NOT DOWNLOAD_LIBCURL)
|
|
||||||
add_library(TracyLibcurl INTERFACE)
|
|
||||||
target_include_directories(TracyLibcurl INTERFACE ${LIBCURL_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(TracyLibcurl INTERFACE ${LIBCURL_LINK_LIBRARIES})
|
|
||||||
else()
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME libcurl
|
|
||||||
GITHUB_REPOSITORY curl/curl
|
|
||||||
GIT_TAG curl-8_22_0
|
|
||||||
OPTIONS
|
|
||||||
"BUILD_STATIC_LIBS ON"
|
|
||||||
"BUILD_SHARED_LIBS OFF"
|
|
||||||
"HTTP_ONLY ON"
|
|
||||||
"CURL_ZSTD OFF"
|
|
||||||
"CURL_USE_LIBPSL OFF"
|
|
||||||
"CURL_USE_LIBSSH2 OFF"
|
|
||||||
"CURL_BROTLI OFF"
|
|
||||||
"USE_NGHTTP2 OFF"
|
|
||||||
"USE_LIBIDN2 OFF"
|
|
||||||
EXCLUDE_FROM_ALL TRUE
|
|
||||||
)
|
|
||||||
add_library(TracyLibcurl INTERFACE)
|
|
||||||
target_link_libraries(TracyLibcurl INTERFACE libcurl_static)
|
|
||||||
target_include_directories(TracyLibcurl INTERFACE ${libcurl_SOURCE_DIR}/include)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 10,
|
|
||||||
"configurePresets": [
|
|
||||||
{
|
|
||||||
"name": "common",
|
|
||||||
"hidden": true,
|
|
||||||
"binaryDir": "${sourceDir}/build-${presetName}",
|
|
||||||
"generator": "Ninja",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "debug",
|
|
||||||
"displayName": "Debug configuration",
|
|
||||||
"inherits": "common",
|
|
||||||
"binaryDir": "${sourceDir}/build",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_BUILD_TYPE": "Debug"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "release",
|
|
||||||
"displayName": "Release configuration",
|
|
||||||
"inherits": "common",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_BUILD_TYPE": "Release"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -26,17 +26,17 @@ void print_usage_exit(int e)
|
|||||||
fprintf(stderr, "Usage:\n");
|
fprintf(stderr, "Usage:\n");
|
||||||
fprintf(stderr, " extract [OPTION...] <trace file>\n");
|
fprintf(stderr, " extract [OPTION...] <trace file>\n");
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
fprintf(stderr, " -h, --help Print usage\n");
|
fprintf(stderr, " -h, --help Print usage\n");
|
||||||
fprintf(stderr, " -V, --version Show version information\n");
|
fprintf(stderr, " -V, --version Show version information\n");
|
||||||
fprintf(stderr, " -f, --filter arg Filter zone names (default: "")\n");
|
fprintf(stderr, " -f, --filter arg Filter zone names (default: "")\n");
|
||||||
fprintf(stderr, " -s, --sep arg CSV separator (default: ,)\n");
|
fprintf(stderr, " -s, --sep arg CSV separator (default: ,)\n");
|
||||||
fprintf(stderr, " -c, --case Case sensitive filtering\n");
|
fprintf(stderr, " -c, --case Case sensitive filtering\n");
|
||||||
fprintf(stderr, " -e, --self Get self times\n");
|
fprintf(stderr, " -e, --self Get self times\n");
|
||||||
fprintf(stderr, " -u, --unwrap Report each cpu zone event\n");
|
fprintf(stderr, " -u, --unwrap Report each cpu zone event\n");
|
||||||
fprintf(stderr, " -g, --gpu Report each gpu zone event\n" );
|
fprintf(stderr, " -g, --gpu Report each gpu zone event\n" );
|
||||||
fprintf(stderr, " -m, --messages Report only messages\n");
|
fprintf(stderr, " -m, --messages Report only messages\n");
|
||||||
fprintf(stderr, " -p, --plot Report plot data (only with -u)\n");
|
fprintf(stderr, " -p, --plot Report plot data (only with -u)\n");
|
||||||
fprintf(stderr, " -t, --truncated_mean[=arg] Report truncated mean (arg is the percentile. Default is 90)\n");
|
fprintf(stderr, " -t, --truncated_mean arg Report truncated mean (arg is the percentile. Default is 90)\n");
|
||||||
|
|
||||||
exit(e);
|
exit(e);
|
||||||
}
|
}
|
||||||
@@ -66,8 +66,8 @@ Args parse_args(int argc, char** argv)
|
|||||||
struct option long_opts[] = {
|
struct option long_opts[] = {
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ "version", no_argument, NULL, 'V' },
|
{ "version", no_argument, NULL, 'V' },
|
||||||
{ "filter", required_argument, NULL, 'f' },
|
{ "filter", optional_argument, NULL, 'f' },
|
||||||
{ "sep", required_argument, NULL, 's' },
|
{ "sep", optional_argument, NULL, 's' },
|
||||||
{ "case", no_argument, NULL, 'c' },
|
{ "case", no_argument, NULL, 'c' },
|
||||||
{ "self", no_argument, NULL, 'e' },
|
{ "self", no_argument, NULL, 'e' },
|
||||||
{ "unwrap", no_argument, NULL, 'u' },
|
{ "unwrap", no_argument, NULL, 'u' },
|
||||||
@@ -79,7 +79,7 @@ Args parse_args(int argc, char** argv)
|
|||||||
};
|
};
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long(argc, argv, "hf:s:t:ceugmpV", long_opts, NULL)) != -1)
|
while ((c = getopt_long(argc, argv, "hf:s:ceugmpV", long_opts, NULL)) != -1)
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.29)
|
|
||||||
project(dyna LANGUAGES C CXX)
|
|
||||||
|
|
||||||
option(TRACY_ENABLE "Enable Tracy" ON)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
||||||
set(CMAKE_COLOR_DIAGNOSTICS ON)
|
|
||||||
|
|
||||||
include(cmake/CPM.cmake)
|
|
||||||
|
|
||||||
if(TRACY_ENABLE)
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
|
||||||
add_compile_options(-fno-omit-frame-pointer)
|
|
||||||
else()
|
|
||||||
message(WARNING "Tracy builds should be RelWithDebInfo!")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME glad
|
|
||||||
VERSION 2.0.8
|
|
||||||
GIT_REPOSITORY https://github.com/Dav1dde/glad.git
|
|
||||||
GIT_TAG v2.0.8
|
|
||||||
)
|
|
||||||
|
|
||||||
add_subdirectory(${glad_SOURCE_DIR}/cmake ${CMAKE_CURRENT_BINARY_DIR}/glad)
|
|
||||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. client/)
|
|
||||||
|
|
||||||
glad_add_library(glad_gl_core_33 STATIC API gl:core=3.3)
|
|
||||||
|
|
||||||
find_package(SDL3 REQUIRED)
|
|
||||||
find_package(SDL3_image REQUIRED)
|
|
||||||
|
|
||||||
add_executable(dyna
|
|
||||||
src/main.cpp
|
|
||||||
src/datapath.cpp
|
|
||||||
src/timer.cpp
|
|
||||||
src/gfx.cpp
|
|
||||||
src/texture.cpp
|
|
||||||
src/entity.cpp
|
|
||||||
src/world.cpp
|
|
||||||
src/map.cpp
|
|
||||||
src/player.cpp
|
|
||||||
src/monster.cpp
|
|
||||||
src/bomb.cpp
|
|
||||||
src/bonus.cpp
|
|
||||||
src/game.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(dyna
|
|
||||||
PRIVATE
|
|
||||||
glad_gl_core_33
|
|
||||||
SDL3::SDL3
|
|
||||||
SDL3_image::SDL3_image
|
|
||||||
Tracy::TracyClient
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(dyna PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
|
||||||
|
|
||||||
# Mirror the data/ tree next to the executable so the game finds its assets
|
|
||||||
# when launched from the build directory (paths are resolved via SDL_GetBasePath).
|
|
||||||
add_custom_command(TARGET dyna POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/data
|
|
||||||
$<TARGET_FILE_DIR:dyna>/data
|
|
||||||
COMMENT "Copying data/ next to dyna executable"
|
|
||||||
)
|
|
||||||
|
|
||||||
file(GENERATE OUTPUT .gitignore CONTENT "*")
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
Dyna.net copyright 2005 by Bartosz Taudul and Ralf Wrześniewski.
|
|
||||||
|
|
||||||
This program (including source code and the asset it uses) is NOT licensed
|
|
||||||
for any use other than being an example of how to integrate Tracy Profiler.
|
|
||||||
|
|
||||||
The license terms written in other parts of this repository DO NOT apply
|
|
||||||
here.
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# SPDX-License-Identifier: MIT
|
|
||||||
#
|
|
||||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
|
|
||||||
|
|
||||||
set(CPM_DOWNLOAD_VERSION 0.43.1)
|
|
||||||
set(CPM_HASH_SUM "1c40fc102ce9625d7de7eb14f541cab30cc3138dca627f0b0ec40293ce6c2934")
|
|
||||||
|
|
||||||
if(CPM_SOURCE_CACHE)
|
|
||||||
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
|
||||||
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
|
|
||||||
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
|
||||||
else()
|
|
||||||
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Expand relative path. This is important if the provided path contains a tilde (~)
|
|
||||||
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
|
|
||||||
|
|
||||||
file(DOWNLOAD
|
|
||||||
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
|
|
||||||
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
|
|
||||||
)
|
|
||||||
|
|
||||||
include(${CPM_DOWNLOAD_LOCATION})
|
|
||||||
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 768 B |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 667 B |
|
Before Width: | Height: | Size: 490 B |
@@ -1,12 +0,0 @@
|
|||||||
10 1 0 0
|
|
||||||
@............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
20 4 0 0
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#@#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
40 3 2 0
|
|
||||||
@............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
40 3 3 0
|
|
||||||
@............
|
|
||||||
.###.#.#.###.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.###.#.#.###.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.###.#.#.###.
|
|
||||||
.............
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
40 2 4 1
|
|
||||||
@............
|
|
||||||
.###.#.#.###.
|
|
||||||
.#.........#.
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.#.........#.
|
|
||||||
.###.#.#.###.
|
|
||||||
.............
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
50 2 2 3
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.....#.#.....
|
|
||||||
.#.###.###.#.
|
|
||||||
.............
|
|
||||||
.#.###.###.#.
|
|
||||||
.....#.#.....
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#@
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
60 3 3 3
|
|
||||||
@............
|
|
||||||
.#.#.###.#.#.
|
|
||||||
.............
|
|
||||||
.###.#.#.###.
|
|
||||||
.............
|
|
||||||
.#.#.###.#.#.
|
|
||||||
.............
|
|
||||||
.###.#.#.###.
|
|
||||||
.............
|
|
||||||
.#.#.###.#.#.
|
|
||||||
.............
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
60 5 3 3
|
|
||||||
@............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#...#.#...#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#...#.#...#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
90 5 5 5
|
|
||||||
@............
|
|
||||||
.#.........#.
|
|
||||||
.............
|
|
||||||
.............
|
|
||||||
.............
|
|
||||||
.............
|
|
||||||
.............
|
|
||||||
.............
|
|
||||||
.............
|
|
||||||
.#.........#.
|
|
||||||
.............
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
30 4 4 4
|
|
||||||
@............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#...#.#...#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
.#...#.#...#.
|
|
||||||
.............
|
|
||||||
.#.#.#.#.#.#.
|
|
||||||
.............
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
#include "bomb.hpp"
|
|
||||||
|
|
||||||
#include "gfx.hpp"
|
|
||||||
#include "map.hpp"
|
|
||||||
#include "texture.hpp"
|
|
||||||
#include "timer.hpp"
|
|
||||||
#include "world.hpp"
|
|
||||||
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
Bomb::Bomb( int x_, int y_ )
|
|
||||||
: x( x_ )
|
|
||||||
, y( y_ )
|
|
||||||
, left( 9 )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bomb::draw()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
if( stage == Stage::exploding )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( stage == Stage::appear )
|
|
||||||
{
|
|
||||||
Textures::bomb_appear.bind( 9 - left );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int frame = static_cast<int>( ( time - left ) / static_cast<float>( time ) * 8 );
|
|
||||||
if( Timer::get_timestamp() / 100 % 2 == 0 )
|
|
||||||
frame++;
|
|
||||||
Textures::bomb.bind( frame );
|
|
||||||
}
|
|
||||||
|
|
||||||
Gfx::draw_square( x, y );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bomb::tick( World& world )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
delta += Timer::delta;
|
|
||||||
|
|
||||||
while( delta > 10 )
|
|
||||||
{
|
|
||||||
delta -= 10;
|
|
||||||
|
|
||||||
if( stage == Stage::appear )
|
|
||||||
{
|
|
||||||
if( left > 0 )
|
|
||||||
{
|
|
||||||
delta -= 10; // the fade-in advances at double speed
|
|
||||||
left--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stage = Stage::ticking;
|
|
||||||
left = time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if( left > 0 )
|
|
||||||
{
|
|
||||||
left--;
|
|
||||||
}
|
|
||||||
else if( stage == Stage::ticking )
|
|
||||||
{
|
|
||||||
explode( world );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
die( world );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bomb::explode( World& world )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
stage = Stage::exploding;
|
|
||||||
left = 200;
|
|
||||||
|
|
||||||
Map& map = world.map();
|
|
||||||
map.at( x, y ) = Field::explosion( Field::ExplosionType::center );
|
|
||||||
|
|
||||||
struct Dir
|
|
||||||
{
|
|
||||||
int dx, dy;
|
|
||||||
Field::ExplosionType through, tip;
|
|
||||||
};
|
|
||||||
const Dir dirs[4] = {
|
|
||||||
{ -1, 0, Field::ExplosionType::horizontal, Field::ExplosionType::left },
|
|
||||||
{ 1, 0, Field::ExplosionType::horizontal, Field::ExplosionType::right },
|
|
||||||
{ 0, -1, Field::ExplosionType::vertical, Field::ExplosionType::up },
|
|
||||||
{ 0, 1, Field::ExplosionType::vertical, Field::ExplosionType::down },
|
|
||||||
};
|
|
||||||
|
|
||||||
for( const Dir& d : dirs )
|
|
||||||
{
|
|
||||||
for( int i = 1; i <= maxrange; i++ )
|
|
||||||
{
|
|
||||||
int tx = x + d.dx * i;
|
|
||||||
int ty = y + d.dy * i;
|
|
||||||
|
|
||||||
if( tx < 0 || tx > map.getx() - 1 || ty < 0 || ty > map.gety() - 1 )
|
|
||||||
break;
|
|
||||||
|
|
||||||
Destruction destr = map.at( tx, ty ).destructible();
|
|
||||||
if( destr == Destruction::none )
|
|
||||||
break;
|
|
||||||
|
|
||||||
etiles.emplace_back( tx, ty );
|
|
||||||
|
|
||||||
if( map.at( tx, ty ).kind == Field::Kind::crate )
|
|
||||||
world.crates_left--;
|
|
||||||
|
|
||||||
if( i == maxrange || destr == Destruction::single )
|
|
||||||
{
|
|
||||||
map.at( tx, ty ) = Field::explosion( d.tip );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
map.at( tx, ty ) = Field::explosion( d.through );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bomb::die( World& world )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
dead = true;
|
|
||||||
|
|
||||||
Map& map = world.map();
|
|
||||||
map.at( x, y ) = Field::floor();
|
|
||||||
for( const auto& [tx, ty] : etiles )
|
|
||||||
map.at( tx, ty ) = Field::floor();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
class World;
|
|
||||||
|
|
||||||
// A bomb on the grid: fades in, counts down, then paints a cross-shaped
|
|
||||||
// explosion onto the map and clears it again. Ported from bomb.cs.
|
|
||||||
class Bomb
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Bomb( int x, int y );
|
|
||||||
|
|
||||||
void draw();
|
|
||||||
void tick( World& world );
|
|
||||||
|
|
||||||
bool is_dead() const { return dead; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void explode( World& world );
|
|
||||||
void die( World& world );
|
|
||||||
|
|
||||||
enum class Stage
|
|
||||||
{
|
|
||||||
appear,
|
|
||||||
ticking,
|
|
||||||
exploding
|
|
||||||
};
|
|
||||||
|
|
||||||
int x, y; // grid coordinates
|
|
||||||
Stage stage = Stage::appear;
|
|
||||||
int left;
|
|
||||||
int delta = 0;
|
|
||||||
static constexpr int time = 150;
|
|
||||||
static constexpr int maxrange = 1;
|
|
||||||
std::vector<std::pair<int, int>> etiles; // tiles to revert to floor
|
|
||||||
bool dead = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
#include "bonus.hpp"
|
|
||||||
|
|
||||||
#include "gfx.hpp"
|
|
||||||
#include "texture.hpp"
|
|
||||||
#include "timer.hpp"
|
|
||||||
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
Vortex::Vortex( int gx, int gy )
|
|
||||||
{
|
|
||||||
x = gx; // stored in grid units, drawn via draw_square
|
|
||||||
y = gy;
|
|
||||||
set_action( Action::appear );
|
|
||||||
left = 79;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Vortex::draw()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
int frame = static_cast<int>( ( Timer::get_timestamp() - action_start ) / 40 );
|
|
||||||
|
|
||||||
switch( action )
|
|
||||||
{
|
|
||||||
case Action::appear:
|
|
||||||
Textures::vortex_appear.bind( frame );
|
|
||||||
break;
|
|
||||||
case Action::wait:
|
|
||||||
Textures::vortex.bind( frame );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Gfx::draw_square( x, y );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Vortex::tick( World& )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
delta += Timer::delta;
|
|
||||||
|
|
||||||
while( delta > 10 )
|
|
||||||
{
|
|
||||||
delta -= 10;
|
|
||||||
|
|
||||||
if( left > 0 )
|
|
||||||
left--;
|
|
||||||
else if( action == Action::appear )
|
|
||||||
set_action( Action::wait );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Vortex::die( World& ) {}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "entity.hpp"
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
// The level-exit portal. Unlike the other entities its coordinates are stored in
|
|
||||||
// grid units (it draws via draw_square), matching bonus.cs.
|
|
||||||
class Vortex : public Entity
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Vortex( int gx, int gy );
|
|
||||||
|
|
||||||
void draw() override;
|
|
||||||
void tick( World& world ) override;
|
|
||||||
void die( World& world ) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#include "datapath.hpp"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
std::string data_path( const std::string& rel )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
ZoneText( rel.c_str(), rel.size() );
|
|
||||||
|
|
||||||
// SDL_GetBasePath returns the executable's directory (with a trailing
|
|
||||||
// separator) and is owned by SDL, so cache it for the program's lifetime.
|
|
||||||
static const std::string base = []
|
|
||||||
{
|
|
||||||
const char* p = SDL_GetBasePath();
|
|
||||||
return std::string( p ? p : "" );
|
|
||||||
}();
|
|
||||||
return base + rel;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
// Resolve a path relative to the directory containing the executable, so the
|
|
||||||
// game finds its data files regardless of the current working directory (e.g.
|
|
||||||
// when launched from the build tree). The data/ tree is copied next to the
|
|
||||||
// binary at build time; see CMakeLists.txt.
|
|
||||||
std::string data_path( const std::string& rel );
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#include "entity.hpp"
|
|
||||||
|
|
||||||
#include "map.hpp"
|
|
||||||
#include "timer.hpp"
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
void Entity::set_action( Action a )
|
|
||||||
{
|
|
||||||
action = a;
|
|
||||||
action_start = Timer::get_timestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Entity::can_move( Action a, const Map& map ) const
|
|
||||||
{
|
|
||||||
switch( a )
|
|
||||||
{
|
|
||||||
case Action::up:
|
|
||||||
return y > 0 && !map.at( x / 64, y / 64 - 1 ).solid();
|
|
||||||
case Action::down:
|
|
||||||
return y / 64 < map.gety() - 1 && !map.at( x / 64, y / 64 + 1 ).solid();
|
|
||||||
case Action::left:
|
|
||||||
return x > 0 && !map.at( x / 64 - 1, y / 64 ).solid();
|
|
||||||
case Action::right:
|
|
||||||
return x / 64 < map.getx() - 1 && !map.at( x / 64 + 1, y / 64 ).solid();
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Entity::killed( const Map& map ) const
|
|
||||||
{
|
|
||||||
int tx = ( x + 32 ) / 64;
|
|
||||||
int ty = ( y + 32 ) / 64;
|
|
||||||
return map.at( tx, ty ).kind == Field::Kind::explosion;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
class Map;
|
|
||||||
class World;
|
|
||||||
|
|
||||||
// Movement/state verbs shared by the player and monsters. In the C# source this
|
|
||||||
// lived as Entity.Action; promoted to namespace scope so Game can refer to it.
|
|
||||||
enum class Action
|
|
||||||
{
|
|
||||||
wait,
|
|
||||||
up,
|
|
||||||
down,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
death,
|
|
||||||
place_bomb,
|
|
||||||
appear
|
|
||||||
};
|
|
||||||
|
|
||||||
// Base for everything that moves on the grid. Coordinates are in pixels
|
|
||||||
// (64 per tile) and laid out top-left origin, matching entity.cs.
|
|
||||||
class Entity
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~Entity() = default;
|
|
||||||
|
|
||||||
virtual void set_action( Action a );
|
|
||||||
|
|
||||||
int getx() const { return x; }
|
|
||||||
int gety() const { return y; }
|
|
||||||
|
|
||||||
virtual void draw() = 0;
|
|
||||||
virtual void tick( World& world ) = 0;
|
|
||||||
virtual void die( World& world ) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool can_move( Action a, const Map& map ) const;
|
|
||||||
virtual bool killed( const Map& map ) const;
|
|
||||||
|
|
||||||
int x = 0, y = 0;
|
|
||||||
std::int64_t action_start = 0;
|
|
||||||
int delta = 0;
|
|
||||||
Action action = Action::wait;
|
|
||||||
int left = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
#include "game.hpp"
|
|
||||||
|
|
||||||
#include "datapath.hpp"
|
|
||||||
#include "gfx.hpp"
|
|
||||||
#include "map.hpp"
|
|
||||||
#include "player.hpp"
|
|
||||||
#include "timer.hpp"
|
|
||||||
#include "world.hpp"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace Game
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
struct TracySection
|
|
||||||
{
|
|
||||||
explicit TracySection( const char* name ) { Enter( name ); }
|
|
||||||
explicit TracySection( const char* name, uint16_t category ) : idx( TracySectionEnterCategory( category, "%s", name ) ) {}
|
|
||||||
~TracySection() { Leave(); }
|
|
||||||
|
|
||||||
void Enter( const char* name )
|
|
||||||
{
|
|
||||||
idx = TracySectionEnter( "%s", name );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Leave()
|
|
||||||
{
|
|
||||||
if( idx > 0 )
|
|
||||||
{
|
|
||||||
TracySectionLeave( idx );
|
|
||||||
idx = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t idx;
|
|
||||||
};
|
|
||||||
|
|
||||||
SDL_Keycode key = 0; // most recently pressed movement key
|
|
||||||
bool help = false;
|
|
||||||
|
|
||||||
// Run one level to completion. Returns true if the player asked to quit the
|
|
||||||
// whole application (window close), false if the level simply ended (death,
|
|
||||||
// escape, or reaching the exit) and control should return to the caller.
|
|
||||||
bool level_loop( World& world )
|
|
||||||
{
|
|
||||||
TracySection section( ( std::string( "Level " ) + world.name() ).c_str(), 1 );
|
|
||||||
|
|
||||||
Player* p = world.player();
|
|
||||||
|
|
||||||
for( ;; )
|
|
||||||
{
|
|
||||||
SDL_Event ev;
|
|
||||||
while( SDL_PollEvent( &ev ) )
|
|
||||||
{
|
|
||||||
if( ev.type == SDL_EVENT_QUIT )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if( ev.type == SDL_EVENT_KEY_DOWN && !ev.key.repeat )
|
|
||||||
{
|
|
||||||
switch( ev.key.key )
|
|
||||||
{
|
|
||||||
case SDLK_ESCAPE:
|
|
||||||
world.killed = true;
|
|
||||||
return false;
|
|
||||||
case SDLK_LEFT:
|
|
||||||
key = SDLK_LEFT;
|
|
||||||
p->move( Action::left );
|
|
||||||
break;
|
|
||||||
case SDLK_RIGHT:
|
|
||||||
key = SDLK_RIGHT;
|
|
||||||
p->move( Action::right );
|
|
||||||
break;
|
|
||||||
case SDLK_UP:
|
|
||||||
key = SDLK_UP;
|
|
||||||
p->move( Action::up );
|
|
||||||
break;
|
|
||||||
case SDLK_DOWN:
|
|
||||||
key = SDLK_DOWN;
|
|
||||||
p->move( Action::down );
|
|
||||||
break;
|
|
||||||
case SDLK_SPACE:
|
|
||||||
world.map().place_bomb( ( p->getx() + 32 ) / 64, ( p->gety() + 32 ) / 64 );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ev.type == SDL_EVENT_KEY_UP )
|
|
||||||
{
|
|
||||||
switch( ev.key.key )
|
|
||||||
{
|
|
||||||
case SDLK_LEFT:
|
|
||||||
if( key == SDLK_LEFT ) p->move( Action::wait );
|
|
||||||
break;
|
|
||||||
case SDLK_RIGHT:
|
|
||||||
if( key == SDLK_RIGHT ) p->move( Action::wait );
|
|
||||||
break;
|
|
||||||
case SDLK_UP:
|
|
||||||
if( key == SDLK_UP ) p->move( Action::wait );
|
|
||||||
break;
|
|
||||||
case SDLK_DOWN:
|
|
||||||
if( key == SDLK_DOWN ) p->move( Action::wait );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gfx::clear();
|
|
||||||
|
|
||||||
Timer::tick();
|
|
||||||
|
|
||||||
world.tick();
|
|
||||||
world.draw();
|
|
||||||
|
|
||||||
Gfx::swap();
|
|
||||||
|
|
||||||
if( world.killed || world.next_level )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Play through the levels in order. Returns true if the application should quit.
|
|
||||||
bool new_game()
|
|
||||||
{
|
|
||||||
TracySection section( "In-game" );
|
|
||||||
|
|
||||||
int level = 1;
|
|
||||||
|
|
||||||
for( ;; )
|
|
||||||
{
|
|
||||||
World world( data_path( "data/levels/" + std::to_string( level ) ), true );
|
|
||||||
|
|
||||||
if( level_loop( world ) )
|
|
||||||
return true; // window closed
|
|
||||||
|
|
||||||
if( world.killed )
|
|
||||||
return false; // died or escaped to the menu
|
|
||||||
if( ++level >= 10 )
|
|
||||||
return false; // cleared the last level
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void menu_loop()
|
|
||||||
{
|
|
||||||
constexpr const char* sectionName = "Main menu";
|
|
||||||
TracySection section( sectionName );
|
|
||||||
|
|
||||||
World world( data_path( "data/levels/menu" ), false );
|
|
||||||
|
|
||||||
for( ;; )
|
|
||||||
{
|
|
||||||
SDL_Event ev;
|
|
||||||
while( SDL_PollEvent( &ev ) )
|
|
||||||
{
|
|
||||||
if( ev.type == SDL_EVENT_QUIT )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( ev.type == SDL_EVENT_KEY_DOWN && !ev.key.repeat )
|
|
||||||
{
|
|
||||||
switch( ev.key.key )
|
|
||||||
{
|
|
||||||
case SDLK_ESCAPE:
|
|
||||||
return;
|
|
||||||
case SDLK_SPACE:
|
|
||||||
section.Leave();
|
|
||||||
if( new_game() )
|
|
||||||
return; // window closed during play
|
|
||||||
section.Enter( sectionName );
|
|
||||||
break;
|
|
||||||
case SDLK_H:
|
|
||||||
help = !help;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gfx::clear();
|
|
||||||
|
|
||||||
Timer::tick();
|
|
||||||
world.tick();
|
|
||||||
world.draw();
|
|
||||||
|
|
||||||
if( help )
|
|
||||||
Gfx::show_help();
|
|
||||||
else
|
|
||||||
Gfx::show_menu();
|
|
||||||
|
|
||||||
Gfx::swap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Game
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
// Top-level game flow, ported from game.cs. The C# original kept the running
|
|
||||||
// game's state (player, map, win/lose flags) in static fields; that state now
|
|
||||||
// lives in a World object owned by the loops below, so nothing leaks out here.
|
|
||||||
namespace Game
|
|
||||||
{
|
|
||||||
void menu_loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,517 +0,0 @@
|
|||||||
#include "gfx.hpp"
|
|
||||||
|
|
||||||
#include "texture.hpp"
|
|
||||||
#include "timer.hpp"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
SDL_Window* g_window = nullptr;
|
|
||||||
SDL_GLContext g_gl_context = nullptr;
|
|
||||||
|
|
||||||
GLuint g_program = 0;
|
|
||||||
GLuint g_vao = 0;
|
|
||||||
GLuint g_vbo = 0;
|
|
||||||
|
|
||||||
// Current draw state, applied to every quad appended to the batch.
|
|
||||||
GLuint g_current_tex = 0;
|
|
||||||
int g_current_layer = 0;
|
|
||||||
float g_alpha = 1.0f;
|
|
||||||
|
|
||||||
// One vertex of the streaming batch: screen position, atlas-array texcoord,
|
|
||||||
// the array layer to sample and a per-vertex alpha multiplier.
|
|
||||||
struct GlVert
|
|
||||||
{
|
|
||||||
float px, py, tx, ty, layer, a;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A run of consecutive vertices that share one texture, drawn in a single call.
|
|
||||||
struct DrawCmd
|
|
||||||
{
|
|
||||||
GLuint tex;
|
|
||||||
GLsizei count;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<GlVert> g_verts;
|
|
||||||
std::vector<DrawCmd> g_cmds;
|
|
||||||
|
|
||||||
const char* VERT_SRC = R"(
|
|
||||||
#version 330 core
|
|
||||||
uniform mat4 uProjection;
|
|
||||||
layout(location = 0) in vec2 aPosition;
|
|
||||||
layout(location = 1) in vec2 aTexCoord;
|
|
||||||
layout(location = 2) in float aLayer;
|
|
||||||
layout(location = 3) in float aAlpha;
|
|
||||||
out vec3 vTexCoord;
|
|
||||||
out float vAlpha;
|
|
||||||
void main() {
|
|
||||||
gl_Position = uProjection * vec4(aPosition, 0.0, 1.0);
|
|
||||||
vTexCoord = vec3(aTexCoord, aLayer);
|
|
||||||
vAlpha = aAlpha;
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
const char* FRAG_SRC = R"(
|
|
||||||
#version 330 core
|
|
||||||
uniform sampler2DArray uTexture;
|
|
||||||
in vec3 vTexCoord;
|
|
||||||
in float vAlpha;
|
|
||||||
out vec4 fragColor;
|
|
||||||
void main() {
|
|
||||||
fragColor = texture(uTexture, vTexCoord) * vec4(1.0, 1.0, 1.0, vAlpha);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
GLuint compile_shader( GLenum type, const char* src )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
GLuint s = glCreateShader( type );
|
|
||||||
glShaderSource( s, 1, &src, nullptr );
|
|
||||||
glCompileShader( s );
|
|
||||||
GLint ok = 0;
|
|
||||||
glGetShaderiv( s, GL_COMPILE_STATUS, &ok );
|
|
||||||
if( !ok )
|
|
||||||
{
|
|
||||||
char log[512];
|
|
||||||
glGetShaderInfoLog( s, 512, nullptr, log );
|
|
||||||
std::fprintf( stderr, "Shader compile error: %s\n", log );
|
|
||||||
glDeleteShader( s );
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init_shaders()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
GLuint vs = compile_shader( GL_VERTEX_SHADER, VERT_SRC );
|
|
||||||
if( !vs ) return false;
|
|
||||||
GLuint fs = compile_shader( GL_FRAGMENT_SHADER, FRAG_SRC );
|
|
||||||
if( !fs )
|
|
||||||
{
|
|
||||||
glDeleteShader( vs );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_program = glCreateProgram();
|
|
||||||
glAttachShader( g_program, vs );
|
|
||||||
glAttachShader( g_program, fs );
|
|
||||||
glLinkProgram( g_program );
|
|
||||||
glDeleteShader( vs );
|
|
||||||
glDeleteShader( fs );
|
|
||||||
|
|
||||||
GLint ok = 0;
|
|
||||||
glGetProgramiv( g_program, GL_LINK_STATUS, &ok );
|
|
||||||
if( !ok )
|
|
||||||
{
|
|
||||||
char log[512];
|
|
||||||
glGetProgramInfoLog( g_program, 512, nullptr, log );
|
|
||||||
std::fprintf( stderr, "Program link error: %s\n", log );
|
|
||||||
glDeleteProgram( g_program );
|
|
||||||
g_program = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bottom-left origin orthographic projection, matching the original
|
|
||||||
// gluOrtho2D(0, w, 0, h) so the ported draw code carries over verbatim.
|
|
||||||
float l = 0.0f, r = static_cast<float>( Gfx::w );
|
|
||||||
float b = 0.0f, t = static_cast<float>( Gfx::h );
|
|
||||||
float proj[16] = {
|
|
||||||
2.0f / ( r - l ), 0.0f, 0.0f, 0.0f,
|
|
||||||
0.0f, 2.0f / ( t - b ), 0.0f, 0.0f,
|
|
||||||
0.0f, 0.0f, -1.0f, 0.0f,
|
|
||||||
-( r + l ) / ( r - l ), -( t + b ) / ( t - b ), 0.0f, 1.0f };
|
|
||||||
|
|
||||||
glUseProgram( g_program );
|
|
||||||
glUniformMatrix4fv( glGetUniformLocation( g_program, "uProjection" ), 1, GL_FALSE, proj );
|
|
||||||
glUniform1i( glGetUniformLocation( g_program, "uTexture" ), 0 );
|
|
||||||
glUseProgram( 0 );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_quad_vao()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
glGenVertexArrays( 1, &g_vao );
|
|
||||||
glGenBuffers( 1, &g_vbo );
|
|
||||||
|
|
||||||
glBindVertexArray( g_vao );
|
|
||||||
glBindBuffer( GL_ARRAY_BUFFER, g_vbo );
|
|
||||||
|
|
||||||
const GLsizei stride = sizeof( GlVert );
|
|
||||||
glEnableVertexAttribArray( 0 );
|
|
||||||
glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, stride, (void*)0 );
|
|
||||||
glEnableVertexAttribArray( 1 );
|
|
||||||
glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, stride, (void*)8 );
|
|
||||||
glEnableVertexAttribArray( 2 );
|
|
||||||
glVertexAttribPointer( 2, 1, GL_FLOAT, GL_FALSE, stride, (void*)16 );
|
|
||||||
glEnableVertexAttribArray( 3 );
|
|
||||||
glVertexAttribPointer( 3, 1, GL_FLOAT, GL_FALSE, stride, (void*)20 );
|
|
||||||
|
|
||||||
glBindVertexArray( 0 );
|
|
||||||
glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw and clear everything accumulated since the last flush, in submission
|
|
||||||
// order. Consecutive quads that share a texture collapse into one draw call.
|
|
||||||
void flush_batch()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
if( g_verts.empty() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
glBindBuffer( GL_ARRAY_BUFFER, g_vbo );
|
|
||||||
glBufferData( GL_ARRAY_BUFFER,
|
|
||||||
static_cast<GLsizeiptr>( g_verts.size() * sizeof( GlVert ) ),
|
|
||||||
g_verts.data(), GL_STREAM_DRAW );
|
|
||||||
|
|
||||||
glUseProgram( g_program );
|
|
||||||
glBindVertexArray( g_vao );
|
|
||||||
|
|
||||||
GLint offset = 0;
|
|
||||||
for( const DrawCmd& cmd : g_cmds )
|
|
||||||
{
|
|
||||||
glBindTexture( GL_TEXTURE_2D_ARRAY, cmd.tex );
|
|
||||||
glDrawArrays( GL_TRIANGLES, offset, cmd.count );
|
|
||||||
offset += cmd.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray( 0 );
|
|
||||||
glUseProgram( 0 );
|
|
||||||
glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
|
||||||
|
|
||||||
g_verts.clear();
|
|
||||||
g_cmds.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frame image capture, following the OpenGL example in the Tracy manual. The
|
|
||||||
// backbuffer is downscaled on the GPU to a small fixed size and read back
|
|
||||||
// asynchronously, so a screenshot can be attached to every frame without
|
|
||||||
// stalling the CPU on the GPU. Several buffer sets are cycled because rendering
|
|
||||||
// runs a few frames ahead of the GPU.
|
|
||||||
// Half the render resolution, preserving its aspect ratio; both dimensions
|
|
||||||
// stay divisible by 4 as FrameImage requires.
|
|
||||||
constexpr int FI_W = Gfx::w / 2;
|
|
||||||
constexpr int FI_H = Gfx::h / 2;
|
|
||||||
constexpr int FI_COUNT = 4;
|
|
||||||
|
|
||||||
GLuint g_fi_texture[FI_COUNT];
|
|
||||||
GLuint g_fi_framebuffer[FI_COUNT];
|
|
||||||
GLuint g_fi_pbo[FI_COUNT];
|
|
||||||
GLsync g_fi_fence[FI_COUNT] = {};
|
|
||||||
int g_fi_idx = 0;
|
|
||||||
std::vector<int> g_fi_queue;
|
|
||||||
|
|
||||||
void init_frame_images()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
glGenTextures( FI_COUNT, g_fi_texture );
|
|
||||||
glGenFramebuffers( FI_COUNT, g_fi_framebuffer );
|
|
||||||
glGenBuffers( FI_COUNT, g_fi_pbo );
|
|
||||||
for( int i = 0; i < FI_COUNT; i++ )
|
|
||||||
{
|
|
||||||
glBindTexture( GL_TEXTURE_2D, g_fi_texture[i] );
|
|
||||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
|
||||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
|
||||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, FI_W, FI_H, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
|
|
||||||
|
|
||||||
glBindFramebuffer( GL_FRAMEBUFFER, g_fi_framebuffer[i] );
|
|
||||||
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, g_fi_texture[i], 0 );
|
|
||||||
|
|
||||||
glBindBuffer( GL_PIXEL_PACK_BUFFER, g_fi_pbo[i] );
|
|
||||||
glBufferData( GL_PIXEL_PACK_BUFFER, FI_W * FI_H * 4, nullptr, GL_STREAM_READ );
|
|
||||||
}
|
|
||||||
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
|
|
||||||
glBindBuffer( GL_PIXEL_PACK_BUFFER, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
void shutdown_frame_images()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
glDeleteTextures( FI_COUNT, g_fi_texture );
|
|
||||||
glDeleteFramebuffers( FI_COUNT, g_fi_framebuffer );
|
|
||||||
glDeleteBuffers( FI_COUNT, g_fi_pbo );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send any captures the GPU has already finished, then queue a capture of the
|
|
||||||
// frame just rendered. Call after the batch is flushed but before swapping.
|
|
||||||
void capture_frame_image()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
|
|
||||||
// Hand finished captures from earlier frames to the profiler. The queue
|
|
||||||
// size is the number of frames we are still ahead of the GPU, which is the
|
|
||||||
// frame lag Tracy needs as the FrameImage offset.
|
|
||||||
while( !g_fi_queue.empty() )
|
|
||||||
{
|
|
||||||
const int idx = g_fi_queue.front();
|
|
||||||
if( glClientWaitSync( g_fi_fence[idx], 0, 0 ) == GL_TIMEOUT_EXPIRED ) break;
|
|
||||||
glDeleteSync( g_fi_fence[idx] );
|
|
||||||
glBindBuffer( GL_PIXEL_PACK_BUFFER, g_fi_pbo[idx] );
|
|
||||||
void* ptr = glMapBufferRange( GL_PIXEL_PACK_BUFFER, 0, FI_W * FI_H * 4, GL_MAP_READ_BIT );
|
|
||||||
FrameImage( ptr, FI_W, FI_H, g_fi_queue.size(), true );
|
|
||||||
glUnmapBuffer( GL_PIXEL_PACK_BUFFER );
|
|
||||||
g_fi_queue.erase( g_fi_queue.begin() );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Downscale the current backbuffer into the next buffer set and start an
|
|
||||||
// asynchronous read-back, signalled by a fence.
|
|
||||||
assert( g_fi_queue.empty() || g_fi_queue.front() != g_fi_idx ); // buffer overrun
|
|
||||||
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, g_fi_framebuffer[g_fi_idx] );
|
|
||||||
glBlitFramebuffer( 0, 0, Gfx::w, Gfx::h, 0, 0, FI_W, FI_H, GL_COLOR_BUFFER_BIT, GL_LINEAR );
|
|
||||||
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
|
|
||||||
glBindFramebuffer( GL_READ_FRAMEBUFFER, g_fi_framebuffer[g_fi_idx] );
|
|
||||||
glBindBuffer( GL_PIXEL_PACK_BUFFER, g_fi_pbo[g_fi_idx] );
|
|
||||||
glReadPixels( 0, 0, FI_W, FI_H, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
|
|
||||||
glBindFramebuffer( GL_READ_FRAMEBUFFER, 0 );
|
|
||||||
g_fi_fence[g_fi_idx] = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );
|
|
||||||
g_fi_queue.emplace_back( g_fi_idx );
|
|
||||||
g_fi_idx = ( g_fi_idx + 1 ) % FI_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace Render
|
|
||||||
{
|
|
||||||
|
|
||||||
bool init()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
if( !init_shaders() ) return false;
|
|
||||||
init_quad_vao();
|
|
||||||
init_frame_images();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shutdown()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
shutdown_frame_images();
|
|
||||||
if( g_vbo ) glDeleteBuffers( 1, &g_vbo );
|
|
||||||
if( g_vao ) glDeleteVertexArrays( 1, &g_vao );
|
|
||||||
if( g_program ) glDeleteProgram( g_program );
|
|
||||||
g_vbo = g_vao = g_program = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void use_texture( GLuint tex, int layer )
|
|
||||||
{
|
|
||||||
g_current_tex = tex;
|
|
||||||
g_current_layer = layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLuint make_texture( int w, int h, int layers, const void* rgba )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
GLuint tex = 0;
|
|
||||||
glGenTextures( 1, &tex );
|
|
||||||
glBindTexture( GL_TEXTURE_2D_ARRAY, tex );
|
|
||||||
glTexParameteri( GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
|
||||||
glTexParameteri( GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
|
||||||
glTexParameteri( GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
|
|
||||||
glTexParameteri( GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
|
|
||||||
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
|
|
||||||
glTexImage3D( GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, layers, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba );
|
|
||||||
return tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Render
|
|
||||||
|
|
||||||
namespace Gfx
|
|
||||||
{
|
|
||||||
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
glClear( GL_COLOR_BUFFER_BIT );
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
flush_batch();
|
|
||||||
capture_frame_image();
|
|
||||||
SDL_GL_SwapWindow( g_window );
|
|
||||||
FrameMark;
|
|
||||||
}
|
|
||||||
|
|
||||||
void alpha( float a )
|
|
||||||
{
|
|
||||||
g_alpha = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_quad( const Vertex corners[4] )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
// Two triangles, vertices appended in submission order so painter ordering
|
|
||||||
// (and the transient per-monster alpha) is preserved by the batch.
|
|
||||||
const int idx[6] = { 0, 1, 2, 0, 2, 3 };
|
|
||||||
for( int i : idx )
|
|
||||||
{
|
|
||||||
const Vertex& c = corners[i];
|
|
||||||
g_verts.push_back( { c.x, c.y, c.u, c.v,
|
|
||||||
static_cast<float>( g_current_layer ), g_alpha } );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !g_cmds.empty() && g_cmds.back().tex == g_current_tex )
|
|
||||||
g_cmds.back().count += 6;
|
|
||||||
else
|
|
||||||
g_cmds.push_back( { g_current_tex, 6 } );
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_sprite( int x, int y )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
float fx = static_cast<float>( x );
|
|
||||||
float fy = static_cast<float>( y );
|
|
||||||
float top = static_cast<float>( h ) - fy;
|
|
||||||
float bottom = static_cast<float>( h ) - ( fy + 64.0f );
|
|
||||||
Vertex corners[4] = {
|
|
||||||
{ fx, top, 0.0f, 0.0f },
|
|
||||||
{ fx + 64.0f, top, 1.0f, 0.0f },
|
|
||||||
{ fx + 64.0f, bottom, 1.0f, 1.0f },
|
|
||||||
{ fx, bottom, 0.0f, 1.0f },
|
|
||||||
};
|
|
||||||
draw_quad( corners );
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_square( int x, int y )
|
|
||||||
{
|
|
||||||
draw_sprite( x * 64, y * 64 );
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_help()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
Textures::menu.bind();
|
|
||||||
|
|
||||||
const float fw = static_cast<float>( w );
|
|
||||||
const float fh = static_cast<float>( h );
|
|
||||||
Vertex bg[4] = {
|
|
||||||
{ 0.0f, fh, 0.0f, 0.0f },
|
|
||||||
{ fw, fh, 832.0f / 1024, 0.0f },
|
|
||||||
{ fw, 0.0f, 832.0f / 1024, 704.0f / 1024 },
|
|
||||||
{ 0.0f, 0.0f, 0.0f, 704.0f / 1024 },
|
|
||||||
};
|
|
||||||
draw_quad( bg );
|
|
||||||
|
|
||||||
int t = static_cast<int>( Timer::get_timestamp() / 40 );
|
|
||||||
|
|
||||||
Textures::p_r.bind( t );
|
|
||||||
draw_sprite( 150, 85 );
|
|
||||||
Textures::m1_r.bind( t );
|
|
||||||
draw_sprite( 75, 160 );
|
|
||||||
Textures::m2_r.bind( t );
|
|
||||||
draw_sprite( 150, 160 );
|
|
||||||
Textures::m3_r.bind( t );
|
|
||||||
draw_sprite( 225, 160 );
|
|
||||||
Textures::bomb.bind( static_cast<int>( Timer::get_timestamp() / 100 % 2 ) );
|
|
||||||
draw_sprite( 150, 235 );
|
|
||||||
Textures::wall.bind();
|
|
||||||
draw_sprite( 150, 310 );
|
|
||||||
Textures::crate.bind();
|
|
||||||
draw_sprite( 150, 385 );
|
|
||||||
Textures::vortex.bind( t );
|
|
||||||
draw_sprite( 150, 460 );
|
|
||||||
Textures::bonus1.bind( t );
|
|
||||||
draw_sprite( 112, 535 );
|
|
||||||
Textures::bonus2.bind( t );
|
|
||||||
draw_sprite( 187, 535 );
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_menu()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
Textures::menu.bind();
|
|
||||||
|
|
||||||
Vertex logo[4] = {
|
|
||||||
{ float( ( w - 594 ) / 2 ), float( h - 50 ), 1.0f, 0.0f },
|
|
||||||
{ float( ( w + 594 ) / 2 ), float( h - 50 ), 1.0f, 594.0f / 1024 },
|
|
||||||
{ float( ( w + 594 ) / 2 ), float( h - 50 - 180 ), 1.0f - 180.0f / 1024, 594.0f / 1024 },
|
|
||||||
{ float( ( w - 594 ) / 2 ), float( h - 50 - 180 ), 1.0f - 180.0f / 1024, 0.0f },
|
|
||||||
};
|
|
||||||
draw_quad( logo );
|
|
||||||
|
|
||||||
Vertex prompt[4] = {
|
|
||||||
{ float( ( w - 527 ) / 2 ), 335.0f, 0.0f, 704.0f / 1024 },
|
|
||||||
{ float( ( w + 527 ) / 2 ), 335.0f, 527.0f / 1024, 704.0f / 1024 },
|
|
||||||
{ float( ( w + 527 ) / 2 ), 20.0f, 527.0f / 1024, 1019.0f / 1024 },
|
|
||||||
{ float( ( w - 527 ) / 2 ), 20.0f, 0.0f, 1019.0f / 1024 },
|
|
||||||
};
|
|
||||||
draw_quad( prompt );
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Gfx
|
|
||||||
|
|
||||||
namespace Init
|
|
||||||
{
|
|
||||||
|
|
||||||
bool all()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
if( !SDL_Init( SDL_INIT_VIDEO ) )
|
|
||||||
{
|
|
||||||
std::fprintf( stderr, "SDL_Init failed: %s\n", SDL_GetError() );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
|
|
||||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
|
|
||||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
|
|
||||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
|
|
||||||
|
|
||||||
g_window = SDL_CreateWindow( "Dyna.net", Gfx::w, Gfx::h, SDL_WINDOW_OPENGL );
|
|
||||||
if( !g_window )
|
|
||||||
{
|
|
||||||
std::fprintf( stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError() );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_gl_context = SDL_GL_CreateContext( g_window );
|
|
||||||
if( !g_gl_context )
|
|
||||||
{
|
|
||||||
std::fprintf( stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int version = gladLoadGL( (GLADloadfunc)SDL_GL_GetProcAddress );
|
|
||||||
if( version == 0 )
|
|
||||||
{
|
|
||||||
std::fprintf( stderr, "gladLoadGL failed\n" );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SetSwapInterval( 1 ); // vsync; the game is time-based so speed is unaffected
|
|
||||||
|
|
||||||
glViewport( 0, 0, Gfx::w, Gfx::h );
|
|
||||||
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
|
|
||||||
glEnable( GL_BLEND );
|
|
||||||
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
|
||||||
|
|
||||||
if( !Render::init() ) return false;
|
|
||||||
|
|
||||||
Timer::reset();
|
|
||||||
Textures::preload();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shutdown()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
Render::shutdown();
|
|
||||||
if( g_gl_context ) SDL_GL_DestroyContext( g_gl_context );
|
|
||||||
if( g_window ) SDL_DestroyWindow( g_window );
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Init
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <glad/gl.h>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
// Screen dimensions, matching the original 13x11 grid of 64px tiles.
|
|
||||||
namespace Gfx
|
|
||||||
{
|
|
||||||
constexpr int w = 832;
|
|
||||||
constexpr int h = 704;
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
void swap();
|
|
||||||
|
|
||||||
// Drawing primitives ported from gfx.cs. They render with the currently
|
|
||||||
// bound texture (see Texture::bind) and the current alpha. The coordinate
|
|
||||||
// system is bottom-left origin with y growing upward, exactly as the C#
|
|
||||||
// gluOrtho2D setup; draw_sprite/draw_square take y measured from the top
|
|
||||||
// and flip internally, so game-side coordinates stay top-left based.
|
|
||||||
void alpha( float a );
|
|
||||||
void draw_sprite( int x, int y ); // pixel position of the top-left corner
|
|
||||||
void draw_square( int x, int y ); // grid position (multiplied by 64)
|
|
||||||
|
|
||||||
// A single textured quad given four explicit (position, texcoord) corners,
|
|
||||||
// used by the menu/help screens which sample rotated regions of the atlas.
|
|
||||||
struct Vertex
|
|
||||||
{
|
|
||||||
float x, y, u, v;
|
|
||||||
};
|
|
||||||
void draw_quad( const Vertex corners[4] );
|
|
||||||
|
|
||||||
void show_help();
|
|
||||||
void show_menu();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Renderer back end shared by the texture loaders.
|
|
||||||
namespace Render
|
|
||||||
{
|
|
||||||
bool init(); // shaders + streaming VBO/VAO
|
|
||||||
void shutdown(); // delete the program and buffers
|
|
||||||
|
|
||||||
// Select the array texture (and layer within it) used by subsequent draws.
|
|
||||||
void use_texture( GLuint tex, int layer );
|
|
||||||
|
|
||||||
// Upload `layers` tightly packed RGBA8 images of size w*h as one
|
|
||||||
// GL_TEXTURE_2D_ARRAY and return its name (0 on failure).
|
|
||||||
GLuint make_texture( int w, int h, int layers, const void* rgba );
|
|
||||||
}
|
|
||||||
|
|
||||||
// One-time startup/shutdown, ported from the Init class in gfx.cs.
|
|
||||||
namespace Init
|
|
||||||
{
|
|
||||||
bool all(); // SDL, GL context, renderer, textures, timer
|
|
||||||
void shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#include "game.hpp"
|
|
||||||
#include "gfx.hpp"
|
|
||||||
|
|
||||||
#include <SDL3/SDL_main.h>
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <new>
|
|
||||||
|
|
||||||
// Route every heap allocation through Tracy so the profiler can track memory
|
|
||||||
// usage. The default array forms (operator new[]/delete[]) and the nothrow
|
|
||||||
// forms forward to these, so overriding the scalar operators covers them too.
|
|
||||||
void* operator new( std::size_t count )
|
|
||||||
{
|
|
||||||
void* ptr = std::malloc( count );
|
|
||||||
if( !ptr ) throw std::bad_alloc();
|
|
||||||
TracyAlloc( ptr, count );
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator delete( void* ptr ) noexcept
|
|
||||||
{
|
|
||||||
TracyFree( ptr );
|
|
||||||
std::free( ptr );
|
|
||||||
}
|
|
||||||
|
|
||||||
int main( int /*argc*/, char* /*argv*/[] )
|
|
||||||
{
|
|
||||||
TracyNoop;
|
|
||||||
|
|
||||||
TracySectionSetup( 0, "Game state" );
|
|
||||||
TracySectionSetup( 1, "Level progression" );
|
|
||||||
|
|
||||||
if( !dyna::Init::all() )
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
dyna::Game::menu_loop();
|
|
||||||
|
|
||||||
dyna::Init::shutdown();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,334 +0,0 @@
|
|||||||
#include "map.hpp"
|
|
||||||
|
|
||||||
#include "bomb.hpp"
|
|
||||||
#include "bonus.hpp"
|
|
||||||
#include "gfx.hpp"
|
|
||||||
#include "monster.hpp"
|
|
||||||
#include "player.hpp"
|
|
||||||
#include "texture.hpp"
|
|
||||||
#include "timer.hpp"
|
|
||||||
#include "world.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
// ---- Field --------------------------------------------------------------
|
|
||||||
|
|
||||||
Field Field::explosion( ExplosionType t )
|
|
||||||
{
|
|
||||||
Field f;
|
|
||||||
f.kind = Kind::explosion;
|
|
||||||
f.etype = t;
|
|
||||||
f.tstart = Timer::get_timestamp();
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Field::solid() const
|
|
||||||
{
|
|
||||||
switch( kind )
|
|
||||||
{
|
|
||||||
case Kind::wall:
|
|
||||||
case Kind::crate:
|
|
||||||
case Kind::bomb:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Destruction Field::destructible() const
|
|
||||||
{
|
|
||||||
switch( kind )
|
|
||||||
{
|
|
||||||
case Kind::floor:
|
|
||||||
return Destruction::multi;
|
|
||||||
case Kind::crate:
|
|
||||||
return Destruction::single;
|
|
||||||
default:
|
|
||||||
return Destruction::none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Field::draw( int x, int y ) const
|
|
||||||
{
|
|
||||||
switch( kind )
|
|
||||||
{
|
|
||||||
case Kind::wall:
|
|
||||||
Textures::wall.bind();
|
|
||||||
Gfx::draw_square( x, y );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Kind::crate:
|
|
||||||
Textures::sand.bind();
|
|
||||||
Gfx::draw_square( x, y );
|
|
||||||
Textures::crate.bind();
|
|
||||||
Gfx::draw_square( x, y );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Kind::explosion: {
|
|
||||||
Textures::sand.bind();
|
|
||||||
Gfx::draw_square( x, y );
|
|
||||||
|
|
||||||
int frame = static_cast<int>( ( Timer::get_timestamp() - tstart ) / 40 % 8 );
|
|
||||||
if( frame > 4 ) frame = 8 - frame;
|
|
||||||
|
|
||||||
switch( etype )
|
|
||||||
{
|
|
||||||
case ExplosionType::center: Textures::e_c.bind( frame ); break;
|
|
||||||
case ExplosionType::vertical: Textures::e_v.bind( frame ); break;
|
|
||||||
case ExplosionType::horizontal: Textures::e_h.bind( frame ); break;
|
|
||||||
case ExplosionType::left: Textures::e_le.bind( frame ); break;
|
|
||||||
case ExplosionType::right: Textures::e_re.bind( frame ); break;
|
|
||||||
case ExplosionType::up: Textures::e_ue.bind( frame ); break;
|
|
||||||
case ExplosionType::down: Textures::e_de.bind( frame ); break;
|
|
||||||
}
|
|
||||||
Gfx::draw_square( x, y );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// floor, bomb and vortex tiles all show plain sand; the bomb and vortex
|
|
||||||
// sprites themselves are drawn by their entities.
|
|
||||||
case Kind::floor:
|
|
||||||
case Kind::bomb:
|
|
||||||
case Kind::vortex:
|
|
||||||
default:
|
|
||||||
Textures::sand.bind();
|
|
||||||
Gfx::draw_square( x, y );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Map ----------------------------------------------------------------
|
|
||||||
|
|
||||||
Map::Map( const std::string& fn )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
ZoneText( fn.c_str(), fn.size() );
|
|
||||||
|
|
||||||
load( fn );
|
|
||||||
generate_destructibles();
|
|
||||||
populate_map();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map::~Map() = default;
|
|
||||||
|
|
||||||
void Map::load( const std::string& fn )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
std::ifstream f( fn );
|
|
||||||
if( !f )
|
|
||||||
{
|
|
||||||
std::fprintf( stderr, "Cannot open level %s\n", fn.c_str() );
|
|
||||||
grid.assign( X * Y, Field::floor() );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream buf;
|
|
||||||
buf << f.rdbuf();
|
|
||||||
std::string content = buf.str();
|
|
||||||
|
|
||||||
size_t nl = content.find( '\n' );
|
|
||||||
std::string header = ( nl == std::string::npos ) ? content : content.substr( 0, nl );
|
|
||||||
std::sscanf( header.c_str(), "%d %d %d %d", &destructibles, &m1, &m2, &m3 );
|
|
||||||
|
|
||||||
grid.assign( X * Y, Field::floor() );
|
|
||||||
px = -1;
|
|
||||||
|
|
||||||
size_t p = ( nl == std::string::npos ) ? content.size() : nl + 1;
|
|
||||||
for( int ry = 0; ry < Y; ry++ )
|
|
||||||
{
|
|
||||||
for( int rx = 0; rx < X; rx++ )
|
|
||||||
{
|
|
||||||
char c = ( p < content.size() ) ? content[p++] : '\0';
|
|
||||||
switch( c )
|
|
||||||
{
|
|
||||||
case '.':
|
|
||||||
at( rx, ry ) = Field::floor();
|
|
||||||
break;
|
|
||||||
case '#':
|
|
||||||
at( rx, ry ) = Field::wall();
|
|
||||||
break;
|
|
||||||
case '@':
|
|
||||||
at( rx, ry ) = Field::floor();
|
|
||||||
px = rx;
|
|
||||||
py = ry;
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
rx--; // newlines don't consume a grid cell
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Map::monster_ok( int rx, int ry, int pxx, int pyy, int r ) const
|
|
||||||
{
|
|
||||||
const Field& f = at( rx, ry );
|
|
||||||
return f.is_floor_family() && f.kind != Field::Kind::crate &&
|
|
||||||
( std::abs( rx - pxx ) > r || std::abs( ry - pyy ) > r );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Map::generate_destructibles()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
int i = destructibles;
|
|
||||||
while( i != 0 )
|
|
||||||
{
|
|
||||||
int rx = RNG::next( X );
|
|
||||||
int ry = RNG::next( Y );
|
|
||||||
if( monster_ok( rx, ry, px, py, 1 ) )
|
|
||||||
{
|
|
||||||
at( rx, ry ) = Field::crate();
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Map::populate_map()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
for( int type = 1; type <= 3; type++ )
|
|
||||||
{
|
|
||||||
int count = ( type == 1 ) ? m1 : ( type == 2 ) ? m2
|
|
||||||
: m3;
|
|
||||||
while( count != 0 )
|
|
||||||
{
|
|
||||||
int rx = RNG::next( X );
|
|
||||||
int ry = RNG::next( Y );
|
|
||||||
if( monster_ok( rx, ry, px, py, 2 ) )
|
|
||||||
{
|
|
||||||
monsters.push_back( std::make_unique<Monster>( type, rx, ry ) );
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Map::draw()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
for( int ry = 0; ry < Y; ry++ )
|
|
||||||
for( int rx = 0; rx < X; rx++ )
|
|
||||||
at( rx, ry ).draw( rx, ry );
|
|
||||||
|
|
||||||
for( auto& b : bombs ) b->draw();
|
|
||||||
for( auto& e : monsters ) e->draw();
|
|
||||||
for( auto& e : bonuses ) e->draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Map::tick( World& world )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
// Bombs.
|
|
||||||
for( auto& b : bombs ) b->tick( world );
|
|
||||||
bombs.erase( std::remove_if( bombs.begin(), bombs.end(),
|
|
||||||
[]( const std::unique_ptr<Bomb>& b ) { return b->is_dead(); } ),
|
|
||||||
bombs.end() );
|
|
||||||
|
|
||||||
// Monsters: tick, then retire the dead and queue their respawn timers.
|
|
||||||
for( auto& e : monsters ) e->tick( world );
|
|
||||||
for( auto& e : monsters )
|
|
||||||
{
|
|
||||||
if( e->is_dead() )
|
|
||||||
{
|
|
||||||
int delay = ( e->type() == 1 ) ? 10000 : ( e->type() == 2 ) ? 20000
|
|
||||||
: 30000;
|
|
||||||
mwait.push_back( { e->type(), Timer::get_timestamp() + delay } );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
monsters.erase( std::remove_if( monsters.begin(), monsters.end(),
|
|
||||||
[]( const std::unique_ptr<Monster>& e ) { return e->is_dead(); } ),
|
|
||||||
monsters.end() );
|
|
||||||
|
|
||||||
// The respawn and exit-portal placement below need the player's position;
|
|
||||||
// they only fire during gameplay (a monster died, or every crate is gone),
|
|
||||||
// never on the player-less menu screen.
|
|
||||||
Player* player = world.player();
|
|
||||||
|
|
||||||
// Respawn monsters whose wait has elapsed.
|
|
||||||
std::int64_t now = Timer::get_timestamp();
|
|
||||||
std::vector<MWait> still_waiting;
|
|
||||||
for( const MWait& m : mwait )
|
|
||||||
{
|
|
||||||
if( m.time < now && player )
|
|
||||||
{
|
|
||||||
int rx = 0, ry = 0;
|
|
||||||
bool ok = false;
|
|
||||||
while( !ok )
|
|
||||||
{
|
|
||||||
rx = RNG::next( X );
|
|
||||||
ry = RNG::next( Y );
|
|
||||||
if( monster_ok( rx, ry, player->getx() / 64, player->gety() / 64, 3 ) )
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
auto monster = std::make_unique<Monster>( m.type, rx, ry );
|
|
||||||
monster->set_action( Action::appear );
|
|
||||||
monsters.push_back( std::move( monster ) );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
still_waiting.push_back( m );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mwait = std::move( still_waiting );
|
|
||||||
|
|
||||||
// Bonuses.
|
|
||||||
for( auto& e : bonuses ) e->tick( world );
|
|
||||||
|
|
||||||
// Once every crate is gone, open the exit portal somewhere clear.
|
|
||||||
if( world.crates_left == 0 && player )
|
|
||||||
{
|
|
||||||
world.crates_left--;
|
|
||||||
|
|
||||||
int rx = 0, ry = 0;
|
|
||||||
bool ok = false;
|
|
||||||
while( !ok )
|
|
||||||
{
|
|
||||||
rx = RNG::next( X );
|
|
||||||
ry = RNG::next( Y );
|
|
||||||
if( monster_ok( rx, ry, player->getx() / 64, player->gety() / 64, 4 ) )
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
at( rx, ry ) = Field::vortex();
|
|
||||||
bonuses.push_back( std::make_unique<Vortex>( rx, ry ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Player> Map::create_player() const
|
|
||||||
{
|
|
||||||
return std::make_unique<Player>( px, py );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Map::place_bomb( int x, int y )
|
|
||||||
{
|
|
||||||
Field& f = at( x, y );
|
|
||||||
if( f.is_floor_family() && f.kind != Field::Kind::bomb )
|
|
||||||
{
|
|
||||||
f = Field::bomb();
|
|
||||||
bombs.push_back( std::make_unique<Bomb>( x, y ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Map::monster_collide( int tx, int ty ) const
|
|
||||||
{
|
|
||||||
for( const auto& e : monsters )
|
|
||||||
{
|
|
||||||
if( ( e->getx() + 32 ) / 64 == ( tx + 32 ) / 64 &&
|
|
||||||
( e->gety() + 32 ) / 64 == ( ty + 32 ) / 64 )
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
class Player;
|
|
||||||
class Bomb;
|
|
||||||
class Monster;
|
|
||||||
class Vortex;
|
|
||||||
class World;
|
|
||||||
|
|
||||||
// How a tile reacts to an explosion sweeping through it.
|
|
||||||
enum class Destruction
|
|
||||||
{
|
|
||||||
none, // blocks the blast (wall, bomb, existing explosion, vortex)
|
|
||||||
single, // destroyed and stops the blast (crate)
|
|
||||||
multi // passable, blast continues (floor)
|
|
||||||
};
|
|
||||||
|
|
||||||
// A grid cell. The C# version used a small class hierarchy rooted at a Field
|
|
||||||
// interface; since the variants differ only in a couple of flags and how they
|
|
||||||
// draw, this collapses them into one value type tagged by Kind. Note that in
|
|
||||||
// the original everything except Wall derived from Floor, so the "is Floor"
|
|
||||||
// checks there map to "kind != Wall" here.
|
|
||||||
struct Field
|
|
||||||
{
|
|
||||||
enum class Kind
|
|
||||||
{
|
|
||||||
floor,
|
|
||||||
wall,
|
|
||||||
crate,
|
|
||||||
bomb, // tile occupied by a live bomb (solid, indestructible)
|
|
||||||
explosion, // transient blast tile
|
|
||||||
vortex // level exit portal
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ExplosionType
|
|
||||||
{
|
|
||||||
center,
|
|
||||||
vertical,
|
|
||||||
horizontal,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
down,
|
|
||||||
up
|
|
||||||
};
|
|
||||||
|
|
||||||
Kind kind = Kind::floor;
|
|
||||||
ExplosionType etype = ExplosionType::center;
|
|
||||||
std::int64_t tstart = 0; // explosion animation start, set on creation
|
|
||||||
|
|
||||||
static Field floor() { return Field{}; }
|
|
||||||
static Field wall() { return Field{ Kind::wall, {}, 0 }; }
|
|
||||||
static Field crate() { return Field{ Kind::crate, {}, 0 }; }
|
|
||||||
static Field bomb() { return Field{ Kind::bomb, {}, 0 }; }
|
|
||||||
static Field vortex() { return Field{ Kind::vortex, {}, 0 }; }
|
|
||||||
static Field explosion( ExplosionType t );
|
|
||||||
|
|
||||||
bool solid() const;
|
|
||||||
Destruction destructible() const;
|
|
||||||
void draw( int x, int y ) const;
|
|
||||||
|
|
||||||
bool is_floor_family() const { return kind != Kind::wall; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class Map
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Map( const std::string& fn );
|
|
||||||
~Map(); // defined in map.cpp where the entity types are complete
|
|
||||||
|
|
||||||
Field& at( int x, int y ) { return grid[index( x, y )]; }
|
|
||||||
const Field& at( int x, int y ) const { return grid[index( x, y )]; }
|
|
||||||
|
|
||||||
void draw();
|
|
||||||
void tick( World& world );
|
|
||||||
|
|
||||||
int getx() const { return X; }
|
|
||||||
int gety() const { return Y; }
|
|
||||||
int get_crates() const { return destructibles; }
|
|
||||||
|
|
||||||
std::unique_ptr<Player> create_player() const;
|
|
||||||
|
|
||||||
void place_bomb( int x, int y );
|
|
||||||
bool monster_collide( int tx, int ty ) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr int X = 13, Y = 11;
|
|
||||||
|
|
||||||
// Deferred monster respawn timer, mirroring Map.MWait.
|
|
||||||
struct MWait
|
|
||||||
{
|
|
||||||
int type; // 1, 2 or 3
|
|
||||||
std::int64_t time; // timestamp at which it respawns
|
|
||||||
};
|
|
||||||
|
|
||||||
static int index( int x, int y ) { return x * Y + y; }
|
|
||||||
|
|
||||||
void load( const std::string& fn );
|
|
||||||
void generate_destructibles();
|
|
||||||
void populate_map();
|
|
||||||
bool monster_ok( int rx, int ry, int px, int py, int r ) const;
|
|
||||||
|
|
||||||
std::vector<Field> grid;
|
|
||||||
int px = -10, py = -10;
|
|
||||||
int destructibles = 0;
|
|
||||||
int m1 = 0, m2 = 0, m3 = 0;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Bomb>> bombs;
|
|
||||||
std::vector<std::unique_ptr<Monster>> monsters;
|
|
||||||
std::vector<std::unique_ptr<Vortex>> bonuses;
|
|
||||||
std::vector<MWait> mwait;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
#include "monster.hpp"
|
|
||||||
|
|
||||||
#include "gfx.hpp"
|
|
||||||
#include "map.hpp"
|
|
||||||
#include "texture.hpp"
|
|
||||||
#include "timer.hpp"
|
|
||||||
#include "world.hpp"
|
|
||||||
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
bool is_opposite( Action a, Action b )
|
|
||||||
{
|
|
||||||
return ( a == Action::up && b == Action::down ) ||
|
|
||||||
( a == Action::down && b == Action::up ) ||
|
|
||||||
( a == Action::left && b == Action::right ) ||
|
|
||||||
( a == Action::right && b == Action::left );
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Monster::Monster( int type, int gx, int gy )
|
|
||||||
: mtype( type )
|
|
||||||
, t( type == 1 ? 14 : type == 2 ? 11
|
|
||||||
: 7 )
|
|
||||||
{
|
|
||||||
x = gx * 64;
|
|
||||||
y = gy * 64;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Monster::set_action( Action a )
|
|
||||||
{
|
|
||||||
Entity::set_action( a );
|
|
||||||
if( action == Action::appear )
|
|
||||||
left = 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Action> Monster::possible_dirs( const Map& map ) const
|
|
||||||
{
|
|
||||||
std::vector<Action> dirs;
|
|
||||||
|
|
||||||
if( x > 0 && !map.at( x / 64 - 1, y / 64 ).solid() )
|
|
||||||
dirs.push_back( Action::left );
|
|
||||||
if( x / 64 < map.getx() - 1 && !map.at( x / 64 + 1, y / 64 ).solid() )
|
|
||||||
dirs.push_back( Action::right );
|
|
||||||
if( y > 0 && !map.at( x / 64, y / 64 - 1 ).solid() )
|
|
||||||
dirs.push_back( Action::up );
|
|
||||||
if( y / 64 < map.gety() - 1 && !map.at( x / 64, y / 64 + 1 ).solid() )
|
|
||||||
dirs.push_back( Action::down );
|
|
||||||
|
|
||||||
return dirs;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Monster::straight( const std::vector<Action>& dirs )
|
|
||||||
{
|
|
||||||
return is_opposite( dirs[0], dirs[1] );
|
|
||||||
}
|
|
||||||
|
|
||||||
Action Monster::any_dir( const Map& map )
|
|
||||||
{
|
|
||||||
std::vector<Action> dirs = possible_dirs( map );
|
|
||||||
if( dirs.empty() )
|
|
||||||
return Action::wait;
|
|
||||||
return dirs[RNG::next( static_cast<int>( dirs.size() ) )];
|
|
||||||
}
|
|
||||||
|
|
||||||
Action Monster::rand_dir( const Map& map )
|
|
||||||
{
|
|
||||||
Action tmp = any_dir( map );
|
|
||||||
if( is_opposite( action, tmp ) )
|
|
||||||
tmp = any_dir( map );
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Monster::think( const Map& map )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
if( action == Action::wait || action == Action::appear )
|
|
||||||
{
|
|
||||||
set_action( rand_dir( map ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Action> dirs = possible_dirs( map );
|
|
||||||
|
|
||||||
if( dirs.size() == 2 && straight( dirs ) )
|
|
||||||
{
|
|
||||||
left = 64;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Action tmp = rand_dir( map );
|
|
||||||
|
|
||||||
if( tmp == action )
|
|
||||||
{
|
|
||||||
left = 64;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
set_action( tmp );
|
|
||||||
if( tmp != Action::wait )
|
|
||||||
left = 64;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Monster::tick( World& world )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
Map& map = world.map();
|
|
||||||
|
|
||||||
delta += Timer::delta;
|
|
||||||
|
|
||||||
while( delta > t )
|
|
||||||
{
|
|
||||||
delta -= t;
|
|
||||||
|
|
||||||
if( action == Action::wait )
|
|
||||||
{
|
|
||||||
think( map );
|
|
||||||
}
|
|
||||||
else if( left > 0 )
|
|
||||||
{
|
|
||||||
left--;
|
|
||||||
|
|
||||||
switch( action )
|
|
||||||
{
|
|
||||||
case Action::down: y++; break;
|
|
||||||
case Action::up: y--; break;
|
|
||||||
case Action::left: x--; break;
|
|
||||||
case Action::right: x++; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if( action == Action::death )
|
|
||||||
die( world );
|
|
||||||
else
|
|
||||||
think( map );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( action != Action::death && killed( map ) )
|
|
||||||
{
|
|
||||||
set_action( Action::death );
|
|
||||||
left = 790 / t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Monster::die( World& )
|
|
||||||
{
|
|
||||||
dead = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AnimTexture& Monster::texture_for( Action a ) const
|
|
||||||
{
|
|
||||||
struct Set
|
|
||||||
{
|
|
||||||
const AnimTexture* wait;
|
|
||||||
const AnimTexture* up;
|
|
||||||
const AnimTexture* down;
|
|
||||||
const AnimTexture* left;
|
|
||||||
const AnimTexture* right;
|
|
||||||
const AnimTexture* death;
|
|
||||||
};
|
|
||||||
|
|
||||||
Set s;
|
|
||||||
if( mtype == 1 )
|
|
||||||
s = { &Textures::m1_d, &Textures::m1_u, &Textures::m1_d, &Textures::m1_l, &Textures::m1_r, &Textures::m1_death };
|
|
||||||
else if( mtype == 2 )
|
|
||||||
s = { &Textures::m2_d, &Textures::m2_u, &Textures::m2_d, &Textures::m2_l, &Textures::m2_r, &Textures::m2_death };
|
|
||||||
else
|
|
||||||
s = { &Textures::m3_d, &Textures::m3_u, &Textures::m3_d, &Textures::m3_l, &Textures::m3_r, &Textures::m3_death };
|
|
||||||
|
|
||||||
switch( a )
|
|
||||||
{
|
|
||||||
case Action::up: return *s.up;
|
|
||||||
case Action::down: return *s.down;
|
|
||||||
case Action::left: return *s.left;
|
|
||||||
case Action::right: return *s.right;
|
|
||||||
case Action::death: return *s.death;
|
|
||||||
case Action::wait:
|
|
||||||
case Action::appear:
|
|
||||||
default: return *s.wait; // wait/appear use the "down" sprite
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Monster::draw()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
// The original returns without drawing for unexpected actions; monsters only
|
|
||||||
// ever hold the actions handled by texture_for, so always draw.
|
|
||||||
generic_draw( texture_for( action ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Monster::generic_draw( const AnimTexture& tex )
|
|
||||||
{
|
|
||||||
int frame;
|
|
||||||
|
|
||||||
if( action == Action::wait )
|
|
||||||
{
|
|
||||||
frame = 0;
|
|
||||||
}
|
|
||||||
else if( action == Action::appear )
|
|
||||||
{
|
|
||||||
frame = 0;
|
|
||||||
Gfx::alpha( static_cast<float>( 200 - left ) / 200.0f );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
frame = static_cast<int>( ( Timer::get_timestamp() - action_start ) / 40 );
|
|
||||||
}
|
|
||||||
|
|
||||||
tex.bind( frame );
|
|
||||||
Gfx::draw_sprite( x, y );
|
|
||||||
|
|
||||||
if( action == Action::appear )
|
|
||||||
Gfx::alpha( 1.0f );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "entity.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
class AnimTexture;
|
|
||||||
|
|
||||||
// The three monster variants from monster.cs differed only in speed, sprite set
|
|
||||||
// and respawn delay, so they fold into one class parameterised by `type` (1-3).
|
|
||||||
class Monster : public Entity
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Monster( int type, int gx, int gy );
|
|
||||||
|
|
||||||
void set_action( Action a ) override;
|
|
||||||
void tick( World& world ) override;
|
|
||||||
void draw() override;
|
|
||||||
void die( World& world ) override;
|
|
||||||
|
|
||||||
bool is_dead() const { return dead; }
|
|
||||||
int type() const { return mtype; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Action> possible_dirs( const Map& map ) const;
|
|
||||||
static bool straight( const std::vector<Action>& dirs );
|
|
||||||
Action rand_dir( const Map& map );
|
|
||||||
Action any_dir( const Map& map ); // __rand_dir in the original
|
|
||||||
void think( const Map& map );
|
|
||||||
void generic_draw( const AnimTexture& tex );
|
|
||||||
const AnimTexture& texture_for( Action a ) const;
|
|
||||||
|
|
||||||
int mtype; // 1, 2 or 3
|
|
||||||
int t; // ms per movement sub-step (per-type speed)
|
|
||||||
bool dead = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
#include "player.hpp"
|
|
||||||
|
|
||||||
#include "gfx.hpp"
|
|
||||||
#include "map.hpp"
|
|
||||||
#include "texture.hpp"
|
|
||||||
#include "timer.hpp"
|
|
||||||
#include "world.hpp"
|
|
||||||
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
Player::Player( int gx, int gy )
|
|
||||||
{
|
|
||||||
x = gx * 64;
|
|
||||||
y = gy * 64;
|
|
||||||
set_action( Action::wait );
|
|
||||||
queue = Action::wait;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::tick( World& world )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
Map& map = world.map();
|
|
||||||
|
|
||||||
delta += Timer::delta;
|
|
||||||
|
|
||||||
while( delta > t )
|
|
||||||
{
|
|
||||||
delta -= t;
|
|
||||||
|
|
||||||
if( left > 0 )
|
|
||||||
{
|
|
||||||
left--;
|
|
||||||
|
|
||||||
switch( action )
|
|
||||||
{
|
|
||||||
case Action::down: y++; break;
|
|
||||||
case Action::up: y--; break;
|
|
||||||
case Action::left: x--; break;
|
|
||||||
case Action::right: x++; break;
|
|
||||||
case Action::place_bomb:
|
|
||||||
if( left == 0 )
|
|
||||||
map.place_bomb( x / 64, y / 64 );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if( action == Action::death )
|
|
||||||
{
|
|
||||||
die( world );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if( map.at( x / 64, y / 64 ).kind == Field::Kind::vortex )
|
|
||||||
{
|
|
||||||
world.next_level = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if( !can_move( queue, map ) )
|
|
||||||
queue = Action::wait;
|
|
||||||
|
|
||||||
if( action != queue )
|
|
||||||
set_action( queue );
|
|
||||||
|
|
||||||
if( action != Action::wait )
|
|
||||||
left = 64;
|
|
||||||
if( action == Action::place_bomb )
|
|
||||||
left = 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( action != Action::death && killed( map ) )
|
|
||||||
{
|
|
||||||
set_action( Action::death );
|
|
||||||
left = 1140 / t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::draw()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
const AnimTexture* tex = nullptr;
|
|
||||||
|
|
||||||
switch( action )
|
|
||||||
{
|
|
||||||
case Action::wait: tex = &Textures::p_wait; break;
|
|
||||||
case Action::up: tex = &Textures::p_u; break;
|
|
||||||
case Action::down: tex = &Textures::p_d; break;
|
|
||||||
case Action::left: tex = &Textures::p_l; break;
|
|
||||||
case Action::right: tex = &Textures::p_r; break;
|
|
||||||
case Action::death: tex = &Textures::p_death; break;
|
|
||||||
case Action::place_bomb: tex = &Textures::p_wait; break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int frame = static_cast<int>( Timer::get_timestamp() - action_start );
|
|
||||||
frame /= ( action == Action::death ) ? 60 : 40;
|
|
||||||
tex->bind( frame );
|
|
||||||
|
|
||||||
Gfx::draw_sprite( x, y );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::move( Action a )
|
|
||||||
{
|
|
||||||
queue = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::die( World& world )
|
|
||||||
{
|
|
||||||
world.killed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Player::killed( const Map& map ) const
|
|
||||||
{
|
|
||||||
if( Entity::killed( map ) )
|
|
||||||
return true;
|
|
||||||
if( map.monster_collide( x, y ) )
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "entity.hpp"
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
class Player : public Entity
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Player( int gx, int gy );
|
|
||||||
|
|
||||||
void tick( World& world ) override;
|
|
||||||
void draw() override;
|
|
||||||
void die( World& world ) override;
|
|
||||||
|
|
||||||
void move( Action a ); // queues the next direction; applied between tiles
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool killed( const Map& map ) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr int t = 6; // ms per movement sub-step
|
|
||||||
Action queue = Action::wait;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
#include "texture.hpp"
|
|
||||||
|
|
||||||
#include "datapath.hpp"
|
|
||||||
#include "gfx.hpp"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
#include <SDL3_image/SDL_image.h>
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
void GlTexture::reset()
|
|
||||||
{
|
|
||||||
if( id_ )
|
|
||||||
{
|
|
||||||
// The texture globals outlive main(), so their destructors can run after
|
|
||||||
// the GL context is already gone (which frees its textures anyway). Only
|
|
||||||
// call into GL while a context is current; otherwise just drop the name.
|
|
||||||
if( SDL_GL_GetCurrentContext() )
|
|
||||||
glDeleteTextures( 1, &id_ );
|
|
||||||
id_ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
struct SurfaceDeleter
|
|
||||||
{
|
|
||||||
void operator()( SDL_Surface* s ) const { SDL_DestroySurface( s ); }
|
|
||||||
};
|
|
||||||
using SurfacePtr = std::unique_ptr<SDL_Surface, SurfaceDeleter>;
|
|
||||||
|
|
||||||
// Convert an arbitrary surface to tightly addressable RGBA8. Returns null on
|
|
||||||
// failure; the result owns its pixels.
|
|
||||||
SurfacePtr to_rgba( SDL_Surface* src )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
if( !src ) return nullptr;
|
|
||||||
return SurfacePtr{ SDL_ConvertSurface( src, SDL_PIXELFORMAT_RGBA32 ) };
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
bool Texture::load( const char* fn )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
ZoneText( fn, strlen( fn ) );
|
|
||||||
|
|
||||||
SurfacePtr image{ IMG_Load( fn ) };
|
|
||||||
if( !image )
|
|
||||||
{
|
|
||||||
std::fprintf( stderr, "Cannot open texture %s: %s\n", fn, SDL_GetError() );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SurfacePtr rgba = to_rgba( image.get() );
|
|
||||||
if( !rgba )
|
|
||||||
{
|
|
||||||
std::fprintf( stderr, "Cannot convert texture %s: %s\n", fn, SDL_GetError() );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pack the surface into a tight RGBA8 block, skipping any per-row padding.
|
|
||||||
const int w = rgba->w, h = rgba->h;
|
|
||||||
std::vector<std::uint8_t> packed( static_cast<size_t>( w ) * h * 4 );
|
|
||||||
const auto* pixels = static_cast<const std::uint8_t*>( rgba->pixels );
|
|
||||||
for( int row = 0; row < h; row++ )
|
|
||||||
{
|
|
||||||
std::memcpy( &packed[static_cast<size_t>( row ) * w * 4],
|
|
||||||
pixels + static_cast<size_t>( row ) * rgba->pitch,
|
|
||||||
static_cast<size_t>( w ) * 4 );
|
|
||||||
}
|
|
||||||
|
|
||||||
tex_ = GlTexture{ Render::make_texture( w, h, 1, packed.data() ) };
|
|
||||||
return static_cast<bool>( tex_ );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Texture::bind() const
|
|
||||||
{
|
|
||||||
Render::use_texture( tex_.get(), 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimTexture::load( SDL_Surface* sheet, int tilex, int tiley, int n )
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
|
|
||||||
SurfacePtr rgba = to_rgba( sheet );
|
|
||||||
if( !rgba )
|
|
||||||
{
|
|
||||||
std::fprintf( stderr, "Cannot convert sprite sheet: %s\n", SDL_GetError() );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* pixels = static_cast<const std::uint8_t*>( rgba->pixels );
|
|
||||||
const int pitch = rgba->pitch;
|
|
||||||
|
|
||||||
// Lay the n frames out back to back as the layers of an array texture.
|
|
||||||
constexpr int frame_bytes = 64 * 64 * 4;
|
|
||||||
std::vector<std::uint8_t> frames( static_cast<size_t>( n ) * frame_bytes );
|
|
||||||
for( int i = 0; i < n; i++ )
|
|
||||||
{
|
|
||||||
for( int fy = 0; fy < 64; fy++ )
|
|
||||||
{
|
|
||||||
int srcy = 64 * ( tiley + i ) + fy;
|
|
||||||
int srcx = 64 * tilex;
|
|
||||||
std::memcpy( &frames[static_cast<size_t>( i ) * frame_bytes + static_cast<size_t>( fy ) * 64 * 4],
|
|
||||||
pixels + static_cast<size_t>( srcy ) * pitch + static_cast<size_t>( srcx ) * 4,
|
|
||||||
static_cast<size_t>( 64 ) * 4 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tex_ = GlTexture{ Render::make_texture( 64, 64, n, frames.data() ) };
|
|
||||||
frames_ = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimTexture::bind( int frame ) const
|
|
||||||
{
|
|
||||||
if( frames_ <= 0 ) return;
|
|
||||||
int layer = frame % frames_;
|
|
||||||
if( layer < 0 ) layer += frames_;
|
|
||||||
Render::use_texture( tex_.get(), layer );
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Textures
|
|
||||||
{
|
|
||||||
Texture menu, sand, wall, crate;
|
|
||||||
|
|
||||||
AnimTexture p_wait, p_u, p_d, p_l, p_r, p_death;
|
|
||||||
|
|
||||||
AnimTexture bomb, bomb_appear, e_c, e_h, e_v, e_le, e_re, e_de, e_ue;
|
|
||||||
|
|
||||||
AnimTexture m1_death, m1_l, m1_r, m1_d, m1_u;
|
|
||||||
AnimTexture m2_death, m2_l, m2_r, m2_d, m2_u;
|
|
||||||
AnimTexture m3_death, m3_l, m3_r, m3_d, m3_u;
|
|
||||||
|
|
||||||
AnimTexture bonus1, bonus2;
|
|
||||||
|
|
||||||
AnimTexture vortex_appear, vortex;
|
|
||||||
|
|
||||||
void preload()
|
|
||||||
{
|
|
||||||
ZoneScoped;
|
|
||||||
|
|
||||||
menu.load( data_path( "data/gfx/menu.png" ).c_str() );
|
|
||||||
sand.load( data_path( "data/gfx/sand.png" ).c_str() );
|
|
||||||
wall.load( data_path( "data/gfx/wall.png" ).c_str() );
|
|
||||||
crate.load( data_path( "data/gfx/crate.png" ).c_str() );
|
|
||||||
|
|
||||||
{
|
|
||||||
SurfacePtr img{ IMG_Load( data_path( "data/gfx/Player.png" ).c_str() ) };
|
|
||||||
p_wait.load( img.get(), 0, 0, 20 );
|
|
||||||
p_d.load( img.get(), 1, 0, 20 );
|
|
||||||
p_u.load( img.get(), 2, 0, 20 );
|
|
||||||
p_l.load( img.get(), 3, 0, 20 );
|
|
||||||
p_r.load( img.get(), 4, 0, 20 );
|
|
||||||
p_death.load( img.get(), 5, 0, 20 );
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
SurfacePtr img{ IMG_Load( data_path( "data/gfx/Bomb.png" ).c_str() ) };
|
|
||||||
bomb.load( img.get(), 0, 0, 10 );
|
|
||||||
bomb_appear.load( img.get(), 5, 0, 10 );
|
|
||||||
e_c.load( img.get(), 1, 0, 5 );
|
|
||||||
e_h.load( img.get(), 2, 0, 5 );
|
|
||||||
e_v.load( img.get(), 1, 5, 5 );
|
|
||||||
e_le.load( img.get(), 3, 0, 5 );
|
|
||||||
e_re.load( img.get(), 2, 5, 5 );
|
|
||||||
e_de.load( img.get(), 4, 0, 5 );
|
|
||||||
e_ue.load( img.get(), 3, 5, 5 );
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
SurfacePtr img{ IMG_Load( data_path( "data/gfx/monster1.png" ).c_str() ) };
|
|
||||||
m1_death.load( img.get(), 0, 0, 20 );
|
|
||||||
m1_u.load( img.get(), 1, 0, 10 );
|
|
||||||
m1_l.load( img.get(), 2, 0, 10 );
|
|
||||||
m1_d.load( img.get(), 1, 10, 10 );
|
|
||||||
m1_r.load( img.get(), 2, 10, 10 );
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
SurfacePtr img{ IMG_Load( data_path( "data/gfx/monster2.png" ).c_str() ) };
|
|
||||||
m2_death.load( img.get(), 0, 0, 20 );
|
|
||||||
m2_d.load( img.get(), 1, 0, 20 );
|
|
||||||
m2_u.load( img.get(), 2, 0, 20 );
|
|
||||||
m2_l.load( img.get(), 3, 0, 20 );
|
|
||||||
m2_r.load( img.get(), 4, 0, 20 );
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
SurfacePtr img{ IMG_Load( data_path( "data/gfx/monster3.png" ).c_str() ) };
|
|
||||||
m3_death.load( img.get(), 0, 0, 20 );
|
|
||||||
m3_d.load( img.get(), 1, 0, 9 );
|
|
||||||
m3_u.load( img.get(), 2, 0, 9 );
|
|
||||||
m3_l.load( img.get(), 1, 10, 9 );
|
|
||||||
m3_r.load( img.get(), 2, 10, 9 );
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
SurfacePtr img{ IMG_Load( data_path( "data/gfx/bonusy.png" ).c_str() ) };
|
|
||||||
bonus1.load( img.get(), 0, 0, 20 );
|
|
||||||
bonus2.load( img.get(), 1, 0, 20 );
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
SurfacePtr img{ IMG_Load( data_path( "data/gfx/portal.png" ).c_str() ) };
|
|
||||||
vortex_appear.load( img.get(), 0, 0, 20 );
|
|
||||||
vortex.load( img.get(), 1, 0, 20 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <glad/gl.h>
|
|
||||||
|
|
||||||
struct SDL_Surface;
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
// Move-only RAII owner of a GL texture name. Every texture in the game is a
|
|
||||||
// GL_TEXTURE_2D_ARRAY (static images use a single layer, animations use one
|
|
||||||
// layer per frame) so the renderer only ever has to deal with one sampler type.
|
|
||||||
class GlTexture
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GlTexture() = default;
|
|
||||||
explicit GlTexture( GLuint id ) noexcept : id_( id ) {}
|
|
||||||
~GlTexture() { reset(); }
|
|
||||||
|
|
||||||
GlTexture( GlTexture&& o ) noexcept : id_( o.id_ ) { o.id_ = 0; }
|
|
||||||
GlTexture& operator=( GlTexture&& o ) noexcept
|
|
||||||
{
|
|
||||||
if( this != &o )
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
id_ = o.id_;
|
|
||||||
o.id_ = 0;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GlTexture( const GlTexture& ) = delete;
|
|
||||||
GlTexture& operator=( const GlTexture& ) = delete;
|
|
||||||
|
|
||||||
GLuint get() const { return id_; }
|
|
||||||
explicit operator bool() const { return id_ != 0; }
|
|
||||||
|
|
||||||
void reset(); // glDeleteTextures; safe on an empty handle
|
|
||||||
|
|
||||||
private:
|
|
||||||
GLuint id_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A single static texture loaded from a whole image file. Ported from
|
|
||||||
// texture.cs; binding just records the texture for the next draw call.
|
|
||||||
class Texture
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool load( const char* fn );
|
|
||||||
void bind() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
GlTexture tex_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A vertical strip of 64x64 animation frames cut out of a sprite sheet, stored
|
|
||||||
// as the layers of one array texture. Mirrors AnimTexture in texture.cs.
|
|
||||||
class AnimTexture
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Extract n frames from column `tilex`, starting at row `tiley`, where each
|
|
||||||
// coordinate is in 64px tile units. Mirrors AnimTexture.load in texture.cs.
|
|
||||||
void load( SDL_Surface* sheet, int tilex, int tiley, int n );
|
|
||||||
void bind( int frame ) const; // frame is taken modulo the frame count
|
|
||||||
|
|
||||||
private:
|
|
||||||
GlTexture tex_;
|
|
||||||
int frames_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// All game textures, loaded once at startup. Mirrors the Textures class.
|
|
||||||
namespace Textures
|
|
||||||
{
|
|
||||||
extern Texture menu, sand, wall, crate;
|
|
||||||
|
|
||||||
extern AnimTexture p_wait, p_u, p_d, p_l, p_r, p_death;
|
|
||||||
|
|
||||||
extern AnimTexture bomb, bomb_appear, e_c, e_h, e_v, e_le, e_re, e_de, e_ue;
|
|
||||||
|
|
||||||
extern AnimTexture m1_death, m1_l, m1_r, m1_d, m1_u;
|
|
||||||
extern AnimTexture m2_death, m2_l, m2_r, m2_d, m2_u;
|
|
||||||
extern AnimTexture m3_death, m3_l, m3_r, m3_d, m3_u;
|
|
||||||
|
|
||||||
extern AnimTexture bonus1, bonus2;
|
|
||||||
|
|
||||||
extern AnimTexture vortex_appear, vortex;
|
|
||||||
|
|
||||||
void preload();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#include "timer.hpp"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace Timer
|
|
||||||
{
|
|
||||||
int delta = 0;
|
|
||||||
static std::int64_t timestamp = 0;
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
delta = 0;
|
|
||||||
timestamp = static_cast<std::int64_t>( SDL_GetTicks() );
|
|
||||||
}
|
|
||||||
|
|
||||||
int tick()
|
|
||||||
{
|
|
||||||
std::int64_t tmp = timestamp;
|
|
||||||
timestamp = static_cast<std::int64_t>( SDL_GetTicks() );
|
|
||||||
delta = static_cast<int>( timestamp - tmp );
|
|
||||||
return delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::int64_t get_timestamp()
|
|
||||||
{
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace RNG
|
|
||||||
{
|
|
||||||
static std::mt19937& engine()
|
|
||||||
{
|
|
||||||
static std::mt19937 e{ std::random_device{}() };
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
int next( int n )
|
|
||||||
{
|
|
||||||
if( n <= 0 ) return 0;
|
|
||||||
std::uniform_int_distribution<int> dist( 0, n - 1 );
|
|
||||||
return dist( engine() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
// Frame timing, ported from timer.cs. Timestamps are milliseconds since
|
|
||||||
// Timer::reset(); kept 64-bit so the modulo arithmetic the animation code
|
|
||||||
// relies on never overflows during a session.
|
|
||||||
namespace Timer
|
|
||||||
{
|
|
||||||
void reset();
|
|
||||||
int tick(); // advances the clock, returns delta in ms
|
|
||||||
std::int64_t get_timestamp();
|
|
||||||
extern int delta; // ms elapsed during the last tick()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thin wrapper over a single global PRNG, mirroring the C# RNG helper.
|
|
||||||
namespace RNG
|
|
||||||
{
|
|
||||||
int next( int n ); // uniform in [0, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#include "world.hpp"
|
|
||||||
|
|
||||||
#include "map.hpp"
|
|
||||||
#include "player.hpp"
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
World::World( const std::string& level_fn, bool with_player )
|
|
||||||
: map_( std::make_unique<Map>( level_fn ) )
|
|
||||||
, name_( level_fn.substr( level_fn.rfind( '/' ) + 1 ) )
|
|
||||||
{
|
|
||||||
if( with_player )
|
|
||||||
{
|
|
||||||
player_ = map_->create_player();
|
|
||||||
crates_left = map_->get_crates();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
crates_left = -1; // the menu never opens an exit portal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
World::~World() = default;
|
|
||||||
|
|
||||||
void World::tick()
|
|
||||||
{
|
|
||||||
map_->tick( *this );
|
|
||||||
if( player_ )
|
|
||||||
player_->tick( *this );
|
|
||||||
}
|
|
||||||
|
|
||||||
void World::draw()
|
|
||||||
{
|
|
||||||
map_->draw();
|
|
||||||
if( player_ )
|
|
||||||
player_->draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace dyna
|
|
||||||
{
|
|
||||||
|
|
||||||
class Map;
|
|
||||||
class Player;
|
|
||||||
|
|
||||||
// Owns the state for one running level: the map, the player (absent on the
|
|
||||||
// menu screen), and the flags the gameplay code used to reach through global
|
|
||||||
// variables. Passing a World& into the tick path replaces the old Game::p /
|
|
||||||
// Game::current_map / Game::killed globals, so there are no non-owning pointers
|
|
||||||
// to outlive the objects they point at.
|
|
||||||
class World
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Loads `level_fn`; spawns a player from the map's '@' marker when
|
|
||||||
// with_player is set (gameplay) and leaves it null otherwise (menu).
|
|
||||||
World( const std::string& level_fn, bool with_player );
|
|
||||||
~World();
|
|
||||||
|
|
||||||
World( const World& ) = delete;
|
|
||||||
World& operator=( const World& ) = delete;
|
|
||||||
|
|
||||||
Map& map() { return *map_; }
|
|
||||||
const Map& map() const { return *map_; }
|
|
||||||
Player* player() { return player_.get(); } // null on the menu screen
|
|
||||||
const std::string& name() const { return name_; }
|
|
||||||
|
|
||||||
void tick();
|
|
||||||
void draw();
|
|
||||||
|
|
||||||
bool killed = false;
|
|
||||||
bool next_level = false;
|
|
||||||
int crates_left = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<Map> map_;
|
|
||||||
std::unique_ptr<Player> player_;
|
|
||||||
std::string name_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
# CMakeLists.txt — OpenGL spinning triangle demo
|
|
||||||
#
|
|
||||||
# macOS:
|
|
||||||
# cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja .
|
|
||||||
# cmake --build build/ninja
|
|
||||||
#
|
|
||||||
# Linux (requires libsdl3-dev libgl1-mesa-dev):
|
|
||||||
# cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja .
|
|
||||||
# cmake --build build/ninja
|
|
||||||
#
|
|
||||||
# Windows:
|
|
||||||
# cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja .
|
|
||||||
# cmake --build build/ninja
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.16)
|
|
||||||
project(gl_spinning_triangle LANGUAGES C CXX)
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Tracy root — defaults to three directories above this CMakeLists.txt.
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
set(TRACY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../..")
|
|
||||||
option(TRACY_ENABLE "Enable Tracy profiling" ON)
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Platform — SDL3 (cross-platform windowing, must be installed on the system)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
find_package(SDL3 REQUIRED)
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# GL extension loader — GLEW (Windows + Linux, fetched automatically)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
if(NOT APPLE)
|
|
||||||
include(FetchContent)
|
|
||||||
set(glew-cmake_BUILD_SHARED OFF CACHE BOOL "" FORCE)
|
|
||||||
set(ONLY_LIBS ON CACHE BOOL "" FORCE)
|
|
||||||
FetchContent_Declare(glew
|
|
||||||
GIT_REPOSITORY https://github.com/Perlmint/glew-cmake.git
|
|
||||||
GIT_TAG glew-cmake-2.3.1
|
|
||||||
GIT_SHALLOW TRUE
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(glew)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(PLATFORM_SOURCES platform/platform_sdl3.cpp)
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
set(PLATFORM_LIBS SDL3::SDL3 "-framework OpenGL")
|
|
||||||
elseif(WIN32)
|
|
||||||
set(PLATFORM_LIBS SDL3::SDL3 opengl32 libglew_static)
|
|
||||||
else()
|
|
||||||
set(PLATFORM_LIBS SDL3::SDL3 GL libglew_static)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Target
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
add_executable(gl_spinning_triangle
|
|
||||||
spinning_triangle.cpp
|
|
||||||
"${TRACY_DIR}/public/TracyClient.cpp"
|
|
||||||
${PLATFORM_SOURCES}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Suppress upstream warnings from TracyClient.cpp
|
|
||||||
if(MSVC)
|
|
||||||
set_source_files_properties("${TRACY_DIR}/public/TracyClient.cpp"
|
|
||||||
PROPERTIES COMPILE_FLAGS "/w"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
set_source_files_properties("${TRACY_DIR}/public/TracyClient.cpp"
|
|
||||||
PROPERTIES COMPILE_FLAGS "-w"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_features(gl_spinning_triangle PRIVATE cxx_std_17)
|
|
||||||
|
|
||||||
if(TRACY_ENABLE)
|
|
||||||
target_compile_definitions(gl_spinning_triangle PRIVATE TRACY_ENABLE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_include_directories(gl_spinning_triangle PRIVATE
|
|
||||||
"${TRACY_DIR}/public"
|
|
||||||
)
|
|
||||||
target_link_libraries(gl_spinning_triangle PRIVATE ${PLATFORM_LIBS})
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// platform.h — interface between platform-agnostic code and platform backends
|
|
||||||
//
|
|
||||||
// Each platform_*.mm / platform_*.cpp file implements these four functions.
|
|
||||||
// Exactly one backend must be linked into the final binary.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
# include <OpenGL/gl3.h>
|
|
||||||
#else
|
|
||||||
# include <GL/glew.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Initialize the windowing system, create a window, and make an OpenGL 3.3
|
|
||||||
// Core Profile context current on the calling thread.
|
|
||||||
// Returns true on success.
|
|
||||||
bool platformInit(int width, int height, const char* title);
|
|
||||||
|
|
||||||
// Load OpenGL function pointers (no-op on macOS where the framework exports them directly).
|
|
||||||
// Must be called after platformInit() while the GL context is current.
|
|
||||||
// Returns true on success.
|
|
||||||
bool platformInitGL();
|
|
||||||
|
|
||||||
// Elapsed wall-clock time in seconds since platformInit().
|
|
||||||
double platformGetTime();
|
|
||||||
|
|
||||||
// Swap front and back buffers (present the rendered frame).
|
|
||||||
void platformSwapBuffers();
|
|
||||||
|
|
||||||
// Pixel scaling factor relative to the logical window size (1.0 on non-HiDPI displays).
|
|
||||||
// Must be called after platformInit().
|
|
||||||
void platformGetPixelDensityScale(float* x, float* y);
|
|
||||||
|
|
||||||
// Enter the platform event/render loop.
|
|
||||||
// Calls render() each frame at ~60 fps.
|
|
||||||
// Calls shutdown() exactly once before returning.
|
|
||||||
void platformRunLoop(void (*render)(), void (*shutdown)());
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
// platform_sdl3.cpp — SDL3 windowing backend (cross-platform)
|
|
||||||
#include "platform.h" // GL headers first (gl3.h / glew.h) so SDL sees guards set
|
|
||||||
|
|
||||||
#define SDL_MAIN_HANDLED // we don't want SDL_main
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
static SDL_Window* sWin = nullptr;
|
|
||||||
static SDL_GLContext sCtx = nullptr;
|
|
||||||
static std::chrono::steady_clock::time_point sStartTime;
|
|
||||||
|
|
||||||
bool platformInit(int width, int height, const char* title) {
|
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
|
||||||
fprintf(stderr, "ERROR: SDL_Init failed: %s\n", SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
||||||
|
|
||||||
sWin = SDL_CreateWindow(title, width, height, SDL_WINDOW_OPENGL);
|
|
||||||
if (!sWin) {
|
|
||||||
fprintf(stderr, "ERROR: SDL_CreateWindow failed: %s\n", SDL_GetError());
|
|
||||||
SDL_Quit();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SDL_SetWindowPosition(sWin, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
||||||
|
|
||||||
sCtx = SDL_GL_CreateContext(sWin);
|
|
||||||
if (!sCtx) {
|
|
||||||
fprintf(stderr, "ERROR: SDL_GL_CreateContext failed: %s\n", SDL_GetError());
|
|
||||||
SDL_DestroyWindow(sWin);
|
|
||||||
SDL_Quit();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SetSwapInterval(1);
|
|
||||||
sStartTime = std::chrono::steady_clock::now();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool platformInitGL() {
|
|
||||||
#ifndef __APPLE__
|
|
||||||
glewExperimental = GL_TRUE;
|
|
||||||
if (glewInit() != GLEW_OK) {
|
|
||||||
fprintf(stderr, "Failed to initialize GLEW\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
double platformGetTime() {
|
|
||||||
return std::chrono::duration<double>(
|
|
||||||
std::chrono::steady_clock::now() - sStartTime).count();
|
|
||||||
}
|
|
||||||
|
|
||||||
void platformSwapBuffers() { SDL_GL_SwapWindow(sWin); }
|
|
||||||
|
|
||||||
void platformGetPixelDensityScale(float* x, float* y) {
|
|
||||||
int pw, ph, ww, wh;
|
|
||||||
SDL_GetWindowSizeInPixels(sWin, &pw, &ph);
|
|
||||||
SDL_GetWindowSize(sWin, &ww, &wh);
|
|
||||||
*x = (ww > 0) ? (float)pw / (float)ww : 1.0f;
|
|
||||||
*y = (wh > 0) ? (float)ph / (float)wh : 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void platformRunLoop(void (*render)(), void (*shutdown)()) {
|
|
||||||
bool running = true;
|
|
||||||
while (running) {
|
|
||||||
SDL_Event e;
|
|
||||||
while (SDL_PollEvent(&e)) {
|
|
||||||
if (e.type == SDL_EVENT_QUIT) running = false;
|
|
||||||
if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) running = false;
|
|
||||||
}
|
|
||||||
if (running) render();
|
|
||||||
}
|
|
||||||
shutdown();
|
|
||||||
SDL_GL_DestroyContext(sCtx);
|
|
||||||
SDL_DestroyWindow(sWin);
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
// spinning_triangle.cpp — OpenGL spinning triangle demo with Tracy GPU profiling.
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
// NOTE: OpenGL is only available on MacOS (no iOS support)
|
|
||||||
// Including and using anything related to OpenGL on Apple (like <OpenGL/gl3.h>)
|
|
||||||
// will emit deprecation warnings, unless GL_SILENCE_DEPRECATION is defined
|
|
||||||
#define GL_SILENCE_DEPRECATION
|
|
||||||
// NOTE: TracyOpenGL.hpp will not work as expected even on Apple devices that
|
|
||||||
// support OpenGL, because the OpenGL drivers do not implement ARB_timer_query
|
|
||||||
// properly (querying GL_TIMESTAMP always resolves to 0). TracyOpenGL.hpp will
|
|
||||||
// emit a compiler warning, and a Tracy message to the trace/profiler, but the
|
|
||||||
// program will still run.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "platform/platform.h" // also includes OpenGL headers
|
|
||||||
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
// NOTE: opt-in toggle for periodic recalibrations during Collect()
|
|
||||||
#define TRACY_OPENGL_AUTO_CALIBRATION
|
|
||||||
#include <tracy/TracyOpenGL.hpp>
|
|
||||||
|
|
||||||
static const int kWidth = 800;
|
|
||||||
static const int kHeight = 600;
|
|
||||||
|
|
||||||
static GLuint gProgram = 0;
|
|
||||||
static GLuint gVao = 0;
|
|
||||||
static GLint gAngleLoc = -1;
|
|
||||||
|
|
||||||
// Vertex colors and positions are baked in; rotation is driven by a uniform.
|
|
||||||
static const char* kVertSrc = R"(
|
|
||||||
#version 150 core
|
|
||||||
uniform float uAngle;
|
|
||||||
const vec2 kPos[3] = vec2[3](
|
|
||||||
vec2( 0.0, 0.5 ),
|
|
||||||
vec2(-0.433, -0.25 ),
|
|
||||||
vec2( 0.433, -0.25 )
|
|
||||||
);
|
|
||||||
const vec3 kCol[3] = vec3[3](
|
|
||||||
vec3(1.0, 0.0, 0.0),
|
|
||||||
vec3(0.0, 1.0, 0.0),
|
|
||||||
vec3(0.0, 0.0, 1.0)
|
|
||||||
);
|
|
||||||
out vec3 vColor;
|
|
||||||
void main() {
|
|
||||||
float c = cos(uAngle);
|
|
||||||
float s = sin(uAngle);
|
|
||||||
vec2 p = kPos[gl_VertexID];
|
|
||||||
gl_Position = vec4(p.x*c - p.y*s, p.x*s + p.y*c, 0.0, 1.0);
|
|
||||||
vColor = kCol[gl_VertexID];
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
static const char* kFragSrc = R"(
|
|
||||||
#version 150 core
|
|
||||||
in vec3 vColor;
|
|
||||||
out vec4 fragColor;
|
|
||||||
void main() { fragColor = vec4(vColor, 1.0); }
|
|
||||||
)";
|
|
||||||
|
|
||||||
static GLuint compileShader(GLenum type, const char* src) {
|
|
||||||
GLuint s = glCreateShader(type);
|
|
||||||
glShaderSource(s, 1, &src, nullptr);
|
|
||||||
glCompileShader(s);
|
|
||||||
GLint ok = 0;
|
|
||||||
glGetShaderiv(s, GL_COMPILE_STATUS, &ok);
|
|
||||||
if (!ok) {
|
|
||||||
char log[512];
|
|
||||||
glGetShaderInfoLog(s, sizeof(log), nullptr, log);
|
|
||||||
fprintf(stderr, "Shader compile error: %s\n", log);
|
|
||||||
glDeleteShader(s);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int initGL() {
|
|
||||||
if (!platformInitGL()) return 1;
|
|
||||||
|
|
||||||
TracyGpuContext;
|
|
||||||
TracyGpuContextName("OpenGL", 6);
|
|
||||||
|
|
||||||
GLuint vert = compileShader(GL_VERTEX_SHADER, kVertSrc);
|
|
||||||
GLuint frag = compileShader(GL_FRAGMENT_SHADER, kFragSrc);
|
|
||||||
if (!vert || !frag) return 1;
|
|
||||||
|
|
||||||
gProgram = glCreateProgram();
|
|
||||||
glAttachShader(gProgram, vert);
|
|
||||||
glAttachShader(gProgram, frag);
|
|
||||||
glLinkProgram(gProgram);
|
|
||||||
glDeleteShader(vert);
|
|
||||||
glDeleteShader(frag);
|
|
||||||
|
|
||||||
GLint ok = 0;
|
|
||||||
glGetProgramiv(gProgram, GL_LINK_STATUS, &ok);
|
|
||||||
if (!ok) {
|
|
||||||
char log[512];
|
|
||||||
glGetProgramInfoLog(gProgram, sizeof(log), nullptr, log);
|
|
||||||
fprintf(stderr, "Program link error: %s\n", log);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
gAngleLoc = glGetUniformLocation(gProgram, "uAngle");
|
|
||||||
|
|
||||||
// Core profile requires a bound VAO even with no vertex attributes.
|
|
||||||
glGenVertexArrays(1, &gVao);
|
|
||||||
glBindVertexArray(gVao);
|
|
||||||
|
|
||||||
glClearColor(0.05f, 0.05f, 0.08f, 1.0f);
|
|
||||||
float scaleX, scaleY;
|
|
||||||
platformGetPixelDensityScale(&scaleX, &scaleY);
|
|
||||||
glViewport(0, 0, (int)(kWidth * scaleX), (int)(kHeight * scaleY));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void renderFrame() {
|
|
||||||
ZoneScoped;
|
|
||||||
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
glUseProgram(gProgram);
|
|
||||||
|
|
||||||
{
|
|
||||||
TracyGpuZone("triangle draw");
|
|
||||||
glUniform1f(gAngleLoc, (float)platformGetTime());
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
platformSwapBuffers();
|
|
||||||
TracyGpuCollect;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void shutdown() {
|
|
||||||
fprintf(stderr, "application is shutting down...\n");
|
|
||||||
glDeleteVertexArrays(1, &gVao);
|
|
||||||
glDeleteProgram(gProgram);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
if (!platformInit(kWidth, kHeight, "OpenGL Spinning Triangle"))
|
|
||||||
return 1;
|
|
||||||
if (initGL() != 0)
|
|
||||||
return 2;
|
|
||||||
platformRunLoop(renderFrame, shutdown);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
21
examples/seq/Makefile
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
TRACY_PUBLIC := ../../public
|
||||||
|
CXX := g++
|
||||||
|
|
||||||
|
TRACY_SRCS := $(TRACY_PUBLIC)/TracyClient.cpp
|
||||||
|
INCLUDES := -I$(TRACY_PUBLIC) -I$(TRACY_PUBLIC)/tracy
|
||||||
|
LIBS := -lpthread -ldl
|
||||||
|
|
||||||
|
CXXFLAGS := -std=c++17 -O2 -DTRACY_ENABLE
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: seq
|
||||||
|
|
||||||
|
seq: seq.cpp tracy_client.o
|
||||||
|
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ $< tracy_client.o $(LIBS)
|
||||||
|
|
||||||
|
tracy_client.o: $(TRACY_SRCS)
|
||||||
|
$(CXX) $(CXXFLAGS) $(INCLUDES) -c -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f seq tracy_client.o
|
||||||
349
examples/seq/seq.cpp
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
// Demonstrates Tracy's sequence (async-continuation) feature.
|
||||||
|
//
|
||||||
|
// Submits a mix of four "recipes" — different async pipelines with varying
|
||||||
|
// chain lengths and per-stage work — onto a thread pool. Each pipeline kicks
|
||||||
|
// off on the main thread (via TracySeqCreate + a tiny setup stage) and then
|
||||||
|
// migrates through 2-7 continuations on worker threads. The profiler renders
|
||||||
|
// arrows between suspend/resume points so the cross-thread chain of any one
|
||||||
|
// pipeline is visible by hovering its zones.
|
||||||
|
//
|
||||||
|
// Build:
|
||||||
|
// make
|
||||||
|
//
|
||||||
|
// Run:
|
||||||
|
// ./seq # then connect tracy-profiler
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Tracy.hpp"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
class ThreadPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ThreadPool( int n )
|
||||||
|
{
|
||||||
|
for( int i = 0; i < n; ++i )
|
||||||
|
{
|
||||||
|
m_workers.emplace_back( [this, i]
|
||||||
|
{
|
||||||
|
char name[16];
|
||||||
|
std::snprintf( name, sizeof( name ), "worker-%d", i );
|
||||||
|
tracy::SetThreadName( name );
|
||||||
|
Run();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ThreadPool()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk( m_mu );
|
||||||
|
m_stop = true;
|
||||||
|
}
|
||||||
|
m_cv.notify_all();
|
||||||
|
for( auto& w : m_workers ) w.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Submit( std::function<void()> task )
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk( m_mu );
|
||||||
|
m_queue.push( std::move( task ) );
|
||||||
|
}
|
||||||
|
m_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Run()
|
||||||
|
{
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
std::function<void()> task;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk( m_mu );
|
||||||
|
m_cv.wait( lk, [this]{ return m_stop || !m_queue.empty(); } );
|
||||||
|
if( m_stop && m_queue.empty() ) return;
|
||||||
|
task = std::move( m_queue.front() );
|
||||||
|
m_queue.pop();
|
||||||
|
}
|
||||||
|
task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mutex m_mu;
|
||||||
|
std::condition_variable m_cv;
|
||||||
|
std::queue<std::function<void()>> m_queue;
|
||||||
|
std::vector<std::thread> m_workers;
|
||||||
|
bool m_stop = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Chain
|
||||||
|
{
|
||||||
|
uint64_t seq;
|
||||||
|
int value;
|
||||||
|
ThreadPool* pool;
|
||||||
|
std::atomic<int>* remaining;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Each stage opens its own ZoneScoped, brackets simulated work with
|
||||||
|
// TracySeqResume/Suspend, then either submits the next stage to the pool or
|
||||||
|
// (for terminal stages) calls TracySeqRetire and bumps the completion counter.
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// query: 2 stages — short pipeline, tight chain
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static void query_exec( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "query/exec" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 35ms );
|
||||||
|
c.value *= 3;
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
TracySeqRetire( c.seq );
|
||||||
|
c.remaining->fetch_sub( 1, std::memory_order_release );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void query_parse( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "query/parse" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 8ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ query_exec( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ingest: 4 stages — IO-heavy
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static void ingest_store( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "ingest/store" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 18ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
TracySeqRetire( c.seq );
|
||||||
|
c.remaining->fetch_sub( 1, std::memory_order_release );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ingest_validate( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "ingest/validate" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 22ms );
|
||||||
|
c.value += 1;
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ ingest_store( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ingest_parse( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "ingest/parse" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 28ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ ingest_validate( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ingest_fetch( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "ingest/fetch" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 60ms ); // slow IO
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ ingest_parse( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// render: 5 stages — frame-like workload
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static void render_present( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "render/present" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 10ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
TracySeqRetire( c.seq );
|
||||||
|
c.remaining->fetch_sub( 1, std::memory_order_release );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render_compose( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "render/compose" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 35ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ render_present( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render_shadows( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "render/shadows" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 45ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ render_compose( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render_geometry( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "render/geometry" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 55ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ render_shadows( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render_setup( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "render/setup" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 12ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ render_geometry( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// compile: 7 stages — long pipeline
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static void compile_emit( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "compile/emit" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 12ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
TracySeqRetire( c.seq );
|
||||||
|
c.remaining->fetch_sub( 1, std::memory_order_release );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compile_codegen( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "compile/codegen" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 40ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ compile_emit( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compile_optimize( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "compile/optimize" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 70ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ compile_codegen( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compile_ir( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "compile/ir" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 25ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ compile_optimize( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compile_typecheck( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "compile/typecheck" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 30ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ compile_ir( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compile_parse( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "compile/parse" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 18ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ compile_typecheck( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compile_lex( Chain c )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "compile/lex" );
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 8ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
c.pool->Submit( [c]{ compile_parse( c ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Kick off a chain from the main thread. The "kickoff" zone holds the
|
||||||
|
// TracySeqCreate plus a small setup window on main; the first worker stage
|
||||||
|
// fires when its Submit lands on a worker.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
enum class Recipe { Query, Ingest, Render, Compile };
|
||||||
|
|
||||||
|
static void Kickoff( Recipe r, int value, ThreadPool& pool, std::atomic<int>& remaining )
|
||||||
|
{
|
||||||
|
ZoneScopedN( "kickoff" );
|
||||||
|
Chain c {
|
||||||
|
.seq = TracySeqCreate(),
|
||||||
|
.value = value,
|
||||||
|
.pool = &pool,
|
||||||
|
.remaining = &remaining,
|
||||||
|
};
|
||||||
|
TracySeqResume( c.seq );
|
||||||
|
std::this_thread::sleep_for( 4ms );
|
||||||
|
TracySeqSuspend( c.seq );
|
||||||
|
|
||||||
|
switch( r )
|
||||||
|
{
|
||||||
|
case Recipe::Query: pool.Submit( [c]{ query_parse( c ); } ); break;
|
||||||
|
case Recipe::Ingest: pool.Submit( [c]{ ingest_fetch( c ); } ); break;
|
||||||
|
case Recipe::Render: pool.Submit( [c]{ render_setup( c ); } ); break;
|
||||||
|
case Recipe::Compile: pool.Submit( [c]{ compile_lex( c ); } ); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
tracy::SetThreadName( "main" );
|
||||||
|
|
||||||
|
ThreadPool pool( 6 );
|
||||||
|
|
||||||
|
// Mixed schedule: short and long pipelines interleaved so workers stay
|
||||||
|
// saturated and continuations naturally bounce across threads.
|
||||||
|
static constexpr Recipe schedule[] = {
|
||||||
|
Recipe::Compile, Recipe::Query, Recipe::Ingest, Recipe::Render,
|
||||||
|
Recipe::Query, Recipe::Compile, Recipe::Render, Recipe::Ingest,
|
||||||
|
Recipe::Render, Recipe::Query, Recipe::Compile, Recipe::Ingest,
|
||||||
|
Recipe::Query, Recipe::Render, Recipe::Ingest, Recipe::Compile,
|
||||||
|
Recipe::Query, Recipe::Compile, Recipe::Render, Recipe::Query,
|
||||||
|
};
|
||||||
|
constexpr int kChains = sizeof( schedule ) / sizeof( schedule[0] );
|
||||||
|
std::atomic<int> remaining{ kChains };
|
||||||
|
|
||||||
|
for( int i = 0; i < kChains; ++i )
|
||||||
|
{
|
||||||
|
FrameMarkNamed( "submit" );
|
||||||
|
Kickoff( schedule[i], i * 100, pool, remaining );
|
||||||
|
std::this_thread::sleep_for( 6ms );
|
||||||
|
}
|
||||||
|
|
||||||
|
while( remaining.load( std::memory_order_acquire ) > 0 )
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for( 10ms );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::printf( "%d chains done\n", kChains );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
# CMakeLists.txt — WebGPU spinning triangle demo
|
|
||||||
#
|
|
||||||
# macOS:
|
|
||||||
# clang++ -std=c++17 -ObjC++ spinning_triangle.cpp platform/platform_macos.mm \
|
|
||||||
# -I/path/to/wgpu/include -L/path/to/wgpu/lib -lwgpu_native \
|
|
||||||
# -Wl,-rpath,@executable_path \
|
|
||||||
# -framework Cocoa -framework Metal -framework QuartzCore \
|
|
||||||
# -framework Foundation -framework IOKit -framework IOSurface \
|
|
||||||
# -o spinning_triangle
|
|
||||||
#
|
|
||||||
# Windows (MSVC):
|
|
||||||
# cl /std:c++17 spinning_triangle.cpp platform/platform_windows.cpp \
|
|
||||||
# /I\path\to\wgpu\include \path\to\wgpu\lib\wgpu_native.lib \
|
|
||||||
# user32.lib gdi32.lib /Fe:spinning_triangle.exe
|
|
||||||
#
|
|
||||||
# Linux (requires libsdl3-dev):
|
|
||||||
# g++ -std=c++17 spinning_triangle.cpp platform/platform_wayland.cpp \
|
|
||||||
# xdg-shell-protocol.c \
|
|
||||||
# -I/path/to/wgpu/include -L/path/to/wgpu/lib -lwgpu_native \
|
|
||||||
# -lwayland-client -o spinning_triangle
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.16)
|
|
||||||
project(spinning_triangle LANGUAGES C CXX)
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# WebGPU backend — set WGPU_PATH to your wgpu-native or Dawn installation.
|
|
||||||
# The library name differs between backends:
|
|
||||||
# wgpu-native → wgpu_native
|
|
||||||
# Dawn → webgpu_dawn
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
set(WGPU_PATH "" CACHE PATH "Root of the WebGPU native installation (contains include/ and lib/)")
|
|
||||||
set(WGPU_LIB "" CACHE STRING "WebGPU library name (wgpu_native or webgpu_dawn); auto-detected if empty")
|
|
||||||
|
|
||||||
if(NOT WGPU_PATH)
|
|
||||||
message(FATAL_ERROR "Set WGPU_PATH to the root of your WebGPU native installation.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# When WGPU_PATH changes, discard any previously auto-detected WGPU_LIB so
|
|
||||||
# detection re-runs against the new path.
|
|
||||||
if(NOT "${WGPU_PATH}" STREQUAL "${_WGPU_PATH_LAST}")
|
|
||||||
unset(WGPU_LIB CACHE)
|
|
||||||
set(WGPU_LIB "" CACHE STRING "WebGPU library name (wgpu_native or webgpu_dawn); auto-detected if empty")
|
|
||||||
endif()
|
|
||||||
set(_WGPU_PATH_LAST "${WGPU_PATH}" CACHE INTERNAL "")
|
|
||||||
|
|
||||||
if(NOT WGPU_LIB)
|
|
||||||
unset(_WGPU_NATIVE_LIB CACHE)
|
|
||||||
unset(_WEBGPU_DAWN_LIB CACHE)
|
|
||||||
find_library(_WGPU_NATIVE_LIB NAMES wgpu_native wgpu_native.dll PATHS "${WGPU_PATH}/lib" NO_DEFAULT_PATH)
|
|
||||||
find_library(_WEBGPU_DAWN_LIB NAMES webgpu_dawn PATHS "${WGPU_PATH}/lib" NO_DEFAULT_PATH)
|
|
||||||
if(_WGPU_NATIVE_LIB)
|
|
||||||
set(WGPU_LIB "wgpu_native" CACHE STRING "WebGPU library name (wgpu_native or webgpu_dawn); auto-detected if empty" FORCE)
|
|
||||||
elseif(_WEBGPU_DAWN_LIB)
|
|
||||||
set(WGPU_LIB "webgpu_dawn" CACHE STRING "WebGPU library name (wgpu_native or webgpu_dawn); auto-detected if empty" FORCE)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Could not detect a WebGPU library in ${WGPU_PATH}/lib. Set WGPU_LIB explicitly (wgpu_native or webgpu_dawn).")
|
|
||||||
endif()
|
|
||||||
message(STATUS "WebGPU library auto-detected: ${WGPU_LIB}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Tracy root — defaults to two directories above this CMakeLists.txt.
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
set(TRACY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../..")
|
|
||||||
option(TRACY_ENABLE "Enable Tracy profiling" ON)
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# macOS quarantine — pre-built WebGPU binaries downloaded from the internet
|
|
||||||
# carry a com.apple.quarantine extended attribute that prevents dyld from
|
|
||||||
# loading them ("damaged or incomplete" / Gatekeeper block). Strip it once
|
|
||||||
# at configure time so the linker and the runtime loader can both access the
|
|
||||||
# library directory without further user intervention.
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
if(APPLE)
|
|
||||||
execute_process(
|
|
||||||
COMMAND xattr -dr com.apple.quarantine "${WGPU_PATH}/lib"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Platform — SDL3 (cross-platform windowing, must be installed on the system)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
find_package(SDL3 REQUIRED)
|
|
||||||
|
|
||||||
set(PLATFORM_SOURCES platform/platform_sdl3.cpp)
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
set(PLATFORM_LIBS
|
|
||||||
SDL3::SDL3
|
|
||||||
"-framework Cocoa"
|
|
||||||
"-framework Metal"
|
|
||||||
"-framework QuartzCore"
|
|
||||||
"-framework Foundation"
|
|
||||||
"-framework IOKit"
|
|
||||||
"-framework IOSurface"
|
|
||||||
)
|
|
||||||
elseif(WIN32)
|
|
||||||
# wgpu-native (Rust stdlib) pull-ins: NtReadFile, GetUserProfileDirectoryW, ...
|
|
||||||
set(WGPU_NATIVE_WIN32_LIBS ntdll userenv)
|
|
||||||
# Dawn pull-ins: WKPDID_D3DDebugObjectName GUID, CompareObjectHandles, ...
|
|
||||||
set(WEBGPU_DAWN_WIN32_LIBS dxguid onecore)
|
|
||||||
set(PLATFORM_LIBS SDL3::SDL3 ${WGPU_NATIVE_WIN32_LIBS} ${WEBGPU_DAWN_WIN32_LIBS})
|
|
||||||
else()
|
|
||||||
set(PLATFORM_LIBS SDL3::SDL3)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Target
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
add_executable(spinning_triangle
|
|
||||||
spinning_triangle.cpp
|
|
||||||
"${TRACY_DIR}/public/TracyClient.cpp"
|
|
||||||
${PLATFORM_SOURCES}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Treat TracyClient.cpp as third-party code — suppress all warnings so that
|
|
||||||
# upstream changes don't pollute our build output.
|
|
||||||
if(MSVC)
|
|
||||||
set_source_files_properties("${TRACY_DIR}/public/TracyClient.cpp"
|
|
||||||
PROPERTIES COMPILE_FLAGS "/w"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
set_source_files_properties("${TRACY_DIR}/public/TracyClient.cpp"
|
|
||||||
PROPERTIES COMPILE_FLAGS "-w"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_features(spinning_triangle PRIVATE cxx_std_17)
|
|
||||||
|
|
||||||
if(TRACY_ENABLE)
|
|
||||||
target_compile_definitions(spinning_triangle PRIVATE TRACY_ENABLE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_include_directories(spinning_triangle PRIVATE
|
|
||||||
"${WGPU_PATH}/include"
|
|
||||||
"${TRACY_DIR}/public"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_directories(spinning_triangle PRIVATE "${WGPU_PATH}/lib")
|
|
||||||
|
|
||||||
target_link_libraries(spinning_triangle PRIVATE
|
|
||||||
${WGPU_LIB}
|
|
||||||
${PLATFORM_LIBS}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Embed the rpath so the binary finds the WebGPU dylib/so next to itself.
|
|
||||||
if(APPLE)
|
|
||||||
set_target_properties(spinning_triangle PROPERTIES
|
|
||||||
BUILD_RPATH "${WGPU_PATH}/lib"
|
|
||||||
INSTALL_RPATH "@executable_path"
|
|
||||||
)
|
|
||||||
elseif(UNIX)
|
|
||||||
set_target_properties(spinning_triangle PROPERTIES
|
|
||||||
BUILD_RPATH "${WGPU_PATH}/lib"
|
|
||||||
INSTALL_RPATH "$ORIGIN"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// platform.h — interface between platform-agnostic code and platform backends
|
|
||||||
//
|
|
||||||
// Each platform_*.mm / platform_*.cpp file implements these five functions.
|
|
||||||
// Exactly one backend must be linked into the final binary.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <webgpu/webgpu.h>
|
|
||||||
|
|
||||||
// Initialize the windowing system and create a window of the given dimensions.
|
|
||||||
// Returns true on success.
|
|
||||||
bool platformInit(int width, int height, const char* title);
|
|
||||||
|
|
||||||
// Create a WebGPU surface backed by the platform window.
|
|
||||||
// Must be called after wgpuCreateInstance() and platformInit().
|
|
||||||
WGPUSurface platformCreateSurface(WGPUInstance instance);
|
|
||||||
|
|
||||||
// Elapsed wall-clock time in seconds since platformInit().
|
|
||||||
double platformGetTime();
|
|
||||||
|
|
||||||
// Enter the platform event/render loop.
|
|
||||||
// Calls render() each frame at ~60 fps.
|
|
||||||
// Calls shutdown() exactly once before returning.
|
|
||||||
void platformRunLoop(void (*render)(), void (*shutdown)());
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
// platform_sdl3.cpp — SDL3 windowing backend for the WebGPU example
|
|
||||||
#include "platform.h" // webgpu/webgpu.h first
|
|
||||||
|
|
||||||
#define SDL_MAIN_HANDLED // we don't want SDL_main
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
# include <SDL3/SDL_metal.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
static SDL_Window* sWin = nullptr;
|
|
||||||
static std::chrono::steady_clock::time_point sStartTime;
|
|
||||||
#ifdef __APPLE__
|
|
||||||
static SDL_MetalView sMetalView = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool platformInit(int width, int height, const char* title) {
|
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
|
||||||
fprintf(stderr, "ERROR: SDL_Init failed: %s\n", SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_WindowFlags flags = 0;
|
|
||||||
#ifdef __APPLE__
|
|
||||||
flags |= SDL_WINDOW_METAL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sWin = SDL_CreateWindow(title, width, height, flags);
|
|
||||||
if (!sWin) {
|
|
||||||
fprintf(stderr, "ERROR: SDL_CreateWindow failed: %s\n", SDL_GetError());
|
|
||||||
SDL_Quit();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SDL_SetWindowPosition(sWin, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
||||||
|
|
||||||
sStartTime = std::chrono::steady_clock::now();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUSurface platformCreateSurface(WGPUInstance instance) {
|
|
||||||
WGPUSurfaceDescriptor desc = {};
|
|
||||||
SDL_PropertiesID props = SDL_GetWindowProperties(sWin);
|
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
sMetalView = SDL_Metal_CreateView(sWin);
|
|
||||||
if (!sMetalView) {
|
|
||||||
fprintf(stderr, "ERROR: SDL_Metal_CreateView failed\n");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
WGPUSurfaceSourceMetalLayer metalDesc = {};
|
|
||||||
metalDesc.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
|
|
||||||
metalDesc.layer = SDL_Metal_GetLayer(sMetalView);
|
|
||||||
desc.nextInChain = &metalDesc.chain;
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
WGPUSurfaceSourceWindowsHWND hwndDesc = {};
|
|
||||||
hwndDesc.chain.sType = WGPUSType_SurfaceSourceWindowsHWND;
|
|
||||||
hwndDesc.hinstance = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER, nullptr);
|
|
||||||
hwndDesc.hwnd = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
|
|
||||||
desc.nextInChain = &hwndDesc.chain;
|
|
||||||
#else // Linux / X11
|
|
||||||
WGPUSurfaceSourceXlibWindow x11Desc = {};
|
|
||||||
x11Desc.chain.sType = WGPUSType_SurfaceSourceXlibWindow;
|
|
||||||
x11Desc.display = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr);
|
|
||||||
x11Desc.window = (uint32_t)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
|
||||||
desc.nextInChain = &x11Desc.chain;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return wgpuInstanceCreateSurface(instance, &desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
double platformGetTime() {
|
|
||||||
return std::chrono::duration<double>(
|
|
||||||
std::chrono::steady_clock::now() - sStartTime).count();
|
|
||||||
}
|
|
||||||
|
|
||||||
void platformRunLoop(void (*render)(), void (*shutdown)()) {
|
|
||||||
bool running = true;
|
|
||||||
while (running) {
|
|
||||||
SDL_Event e;
|
|
||||||
while (SDL_PollEvent(&e)) {
|
|
||||||
if (e.type == SDL_EVENT_QUIT) running = false;
|
|
||||||
if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) running = false;
|
|
||||||
}
|
|
||||||
if (running) render();
|
|
||||||
}
|
|
||||||
shutdown();
|
|
||||||
#ifdef __APPLE__
|
|
||||||
SDL_Metal_DestroyView(sMetalView);
|
|
||||||
#endif
|
|
||||||
SDL_DestroyWindow(sWin);
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
|
||||||
@@ -1,352 +0,0 @@
|
|||||||
// spinning_triangle.cpp — platform-agnostic WebGPU spinning triangle demo.
|
|
||||||
|
|
||||||
#include "platform/platform.h"
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <webgpu/webgpu.h>
|
|
||||||
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
#include <tracy/TracyWebGPU.hpp>
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Globals
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static const int kWidth = 800;
|
|
||||||
static const int kHeight = 600;
|
|
||||||
|
|
||||||
static WGPUInstance gInstance = nullptr;
|
|
||||||
static WGPUSurface gSurface = nullptr;
|
|
||||||
static WGPUAdapter gAdapter = nullptr;
|
|
||||||
static WGPUDevice gDevice = nullptr;
|
|
||||||
static WGPUQueue gQueue = nullptr;
|
|
||||||
static WGPURenderPipeline gPipeline = nullptr;
|
|
||||||
static WGPUBuffer gUniformBuf = nullptr;
|
|
||||||
static WGPUBindGroup gBindGroup = nullptr;
|
|
||||||
|
|
||||||
static TracyWebGPUCtx gTracyCtx = nullptr;
|
|
||||||
|
|
||||||
static WGPUTextureFormat gSurfaceFormat = WGPUTextureFormat_BGRA8Unorm;
|
|
||||||
|
|
||||||
// TODO: this can become platformError() instead
|
|
||||||
int error(int code, const char* message) {
|
|
||||||
fprintf(stderr, "ERROR: %s (code: %d)\n", message, code);
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// WGSL shader — vertex colours baked in, rotation via a uniform float.
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static const char* kShaderSource = R"(
|
|
||||||
struct Uniforms {
|
|
||||||
angle: f32,
|
|
||||||
};
|
|
||||||
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
||||||
|
|
||||||
struct VSOut {
|
|
||||||
@builtin(position) pos: vec4f,
|
|
||||||
@location(0) color: vec3f,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {
|
|
||||||
var positions = array<vec2f, 3>(
|
|
||||||
vec2f( 0.0, 0.5),
|
|
||||||
vec2f(-0.433, -0.25),
|
|
||||||
vec2f( 0.433, -0.25),
|
|
||||||
);
|
|
||||||
var colors = array<vec3f, 3>(
|
|
||||||
vec3f(1.0, 0.0, 0.0),
|
|
||||||
vec3f(0.0, 1.0, 0.0),
|
|
||||||
vec3f(0.0, 0.0, 1.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let c = cos(u.angle);
|
|
||||||
let s = sin(u.angle);
|
|
||||||
let p = positions[vi];
|
|
||||||
let rotated = vec2f(p.x * c - p.y * s, p.x * s + p.y * c);
|
|
||||||
|
|
||||||
var out: VSOut;
|
|
||||||
out.pos = vec4f(rotated, 0.0, 1.0);
|
|
||||||
out.color = colors[vi];
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(@location(0) color: vec3f) -> @location(0) vec4f {
|
|
||||||
return vec4f(color, 1.0);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Adapter / Device request callbacks (current wgpu-native API)
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static void onAdapterReady(WGPURequestAdapterStatus status,
|
|
||||||
WGPUAdapter adapter,
|
|
||||||
WGPUStringView message,
|
|
||||||
void* userdata1, void* /*userdata2*/) {
|
|
||||||
if (status == WGPURequestAdapterStatus_Success) {
|
|
||||||
*(WGPUAdapter*)userdata1 = adapter;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Adapter request failed: %.*s\n",
|
|
||||||
(int)message.length, message.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void onDeviceReady(WGPURequestDeviceStatus status,
|
|
||||||
WGPUDevice device,
|
|
||||||
WGPUStringView message,
|
|
||||||
void* userdata1, void* /*userdata2*/) {
|
|
||||||
if (status == WGPURequestDeviceStatus_Success) {
|
|
||||||
*(WGPUDevice*)userdata1 = device;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Device request failed: %.*s\n",
|
|
||||||
(int)message.length, message.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// WebGPU init
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static int initWebGPU() {
|
|
||||||
// Adapter
|
|
||||||
WGPURequestAdapterOptions adapterOpts = {};
|
|
||||||
adapterOpts.compatibleSurface = gSurface;
|
|
||||||
|
|
||||||
WGPURequestAdapterCallbackInfo adapterCB = {};
|
|
||||||
adapterCB.mode = WGPUCallbackMode_AllowProcessEvents;
|
|
||||||
adapterCB.callback = onAdapterReady;
|
|
||||||
adapterCB.userdata1 = &gAdapter;
|
|
||||||
wgpuInstanceRequestAdapter(gInstance, &adapterOpts, adapterCB);
|
|
||||||
while (!gAdapter) { wgpuInstanceProcessEvents(gInstance); }
|
|
||||||
if (!gAdapter) return error(11, "No adapter");
|
|
||||||
|
|
||||||
WGPUUncapturedErrorCallbackInfo errorCB = {};
|
|
||||||
errorCB.callback = [](WGPUDevice const*, WGPUErrorType type,
|
|
||||||
WGPUStringView message, void*, void*) {
|
|
||||||
fprintf(stderr, "[WGPU ERROR] type=%d %.*s\n",
|
|
||||||
(int)type, (int)message.length, message.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
WGPUDeviceDescriptor deviceDesc = {};
|
|
||||||
deviceDesc.uncapturedErrorCallbackInfo = errorCB;
|
|
||||||
|
|
||||||
TracyWebGPUSetupDeviceDescriptor(deviceDesc);
|
|
||||||
|
|
||||||
WGPURequestDeviceCallbackInfo deviceCB = {};
|
|
||||||
deviceCB.mode = WGPUCallbackMode_AllowProcessEvents;
|
|
||||||
deviceCB.callback = onDeviceReady;
|
|
||||||
deviceCB.userdata1 = &gDevice;
|
|
||||||
wgpuAdapterRequestDevice(gAdapter, &deviceDesc, deviceCB);
|
|
||||||
while (!gDevice) { wgpuInstanceProcessEvents(gInstance); }
|
|
||||||
if (!gDevice) return error(12, "No device");
|
|
||||||
|
|
||||||
gQueue = wgpuDeviceGetQueue(gDevice);
|
|
||||||
gTracyCtx = TracyWebGPUContext(gInstance, gDevice, gQueue);
|
|
||||||
TracyWebGPUContextName(gTracyCtx, "WebGPU", 6);
|
|
||||||
|
|
||||||
// Configure surface
|
|
||||||
WGPUSurfaceConfiguration config = {};
|
|
||||||
config.device = gDevice;
|
|
||||||
config.format = gSurfaceFormat;
|
|
||||||
config.usage = WGPUTextureUsage_RenderAttachment;
|
|
||||||
config.alphaMode = WGPUCompositeAlphaMode_Opaque;
|
|
||||||
config.width = kWidth;
|
|
||||||
config.height = kHeight;
|
|
||||||
config.presentMode = WGPUPresentMode_Fifo;
|
|
||||||
wgpuSurfaceConfigure(gSurface, &config);
|
|
||||||
|
|
||||||
// Shader module
|
|
||||||
WGPUShaderSourceWGSL wgslSrc = {};
|
|
||||||
wgslSrc.chain.sType = WGPUSType_ShaderSourceWGSL;
|
|
||||||
wgslSrc.code = { kShaderSource, WGPU_STRLEN };
|
|
||||||
|
|
||||||
WGPUShaderModuleDescriptor smDesc = {};
|
|
||||||
smDesc.nextInChain = (WGPUChainedStruct*)&wgslSrc;
|
|
||||||
WGPUShaderModule shaderMod = wgpuDeviceCreateShaderModule(gDevice, &smDesc);
|
|
||||||
|
|
||||||
// Uniform buffer (one f32 for rotation angle)
|
|
||||||
WGPUBufferDescriptor bufDesc = {};
|
|
||||||
bufDesc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
|
|
||||||
bufDesc.size = sizeof(float);
|
|
||||||
gUniformBuf = wgpuDeviceCreateBuffer(gDevice, &bufDesc);
|
|
||||||
|
|
||||||
// Bind group layout + bind group
|
|
||||||
WGPUBindGroupLayoutEntry bglEntry = {};
|
|
||||||
bglEntry.binding = 0;
|
|
||||||
bglEntry.visibility = WGPUShaderStage_Vertex;
|
|
||||||
bglEntry.buffer.type = WGPUBufferBindingType_Uniform;
|
|
||||||
bglEntry.buffer.minBindingSize = sizeof(float);
|
|
||||||
|
|
||||||
WGPUBindGroupLayoutDescriptor bglDesc = {};
|
|
||||||
bglDesc.entryCount = 1;
|
|
||||||
bglDesc.entries = &bglEntry;
|
|
||||||
WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(gDevice, &bglDesc);
|
|
||||||
|
|
||||||
WGPUBindGroupEntry bgEntry = {};
|
|
||||||
bgEntry.binding = 0;
|
|
||||||
bgEntry.buffer = gUniformBuf;
|
|
||||||
bgEntry.size = sizeof(float);
|
|
||||||
|
|
||||||
WGPUBindGroupDescriptor bgDesc = {};
|
|
||||||
bgDesc.layout = bgl;
|
|
||||||
bgDesc.entryCount = 1;
|
|
||||||
bgDesc.entries = &bgEntry;
|
|
||||||
gBindGroup = wgpuDeviceCreateBindGroup(gDevice, &bgDesc);
|
|
||||||
|
|
||||||
// Pipeline layout
|
|
||||||
WGPUPipelineLayoutDescriptor plDesc = {};
|
|
||||||
plDesc.bindGroupLayoutCount = 1;
|
|
||||||
plDesc.bindGroupLayouts = &bgl;
|
|
||||||
WGPUPipelineLayout pipelineLayout = wgpuDeviceCreatePipelineLayout(gDevice, &plDesc);
|
|
||||||
|
|
||||||
// Render pipeline
|
|
||||||
WGPUColorTargetState colorTarget = {};
|
|
||||||
colorTarget.format = gSurfaceFormat;
|
|
||||||
colorTarget.writeMask = WGPUColorWriteMask_All;
|
|
||||||
|
|
||||||
WGPUFragmentState fragState = {};
|
|
||||||
fragState.module = shaderMod;
|
|
||||||
fragState.entryPoint = { "fs_main", WGPU_STRLEN };
|
|
||||||
fragState.targetCount = 1;
|
|
||||||
fragState.targets = &colorTarget;
|
|
||||||
|
|
||||||
WGPURenderPipelineDescriptor rpDesc = {};
|
|
||||||
rpDesc.layout = pipelineLayout;
|
|
||||||
rpDesc.vertex.module = shaderMod;
|
|
||||||
rpDesc.vertex.entryPoint = { "vs_main", WGPU_STRLEN };
|
|
||||||
rpDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
|
|
||||||
rpDesc.multisample.count = 1;
|
|
||||||
rpDesc.multisample.mask = 0xFFFFFFFF;
|
|
||||||
rpDesc.fragment = &fragState;
|
|
||||||
|
|
||||||
gPipeline = wgpuDeviceCreateRenderPipeline(gDevice, &rpDesc);
|
|
||||||
|
|
||||||
// Cleanup intermediates
|
|
||||||
wgpuShaderModuleRelease(shaderMod);
|
|
||||||
wgpuPipelineLayoutRelease(pipelineLayout);
|
|
||||||
wgpuBindGroupLayoutRelease(bgl);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Frame rendering
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Returns the surface texture for the current frame, or {.texture=nullptr} on
|
|
||||||
// a skippable condition (timeout, occlusion) or an error.
|
|
||||||
static WGPUSurfaceTexture getWindowSurface() {
|
|
||||||
WGPUSurfaceTexture surfTex = {};
|
|
||||||
wgpuSurfaceGetCurrentTexture(gSurface, &surfTex);
|
|
||||||
if (surfTex.status == WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal ||
|
|
||||||
surfTex.status == WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal)
|
|
||||||
return surfTex;
|
|
||||||
|
|
||||||
// Timeout and Occluded are normal OS events (window covered / on a different Space).
|
|
||||||
bool silent = surfTex.status == WGPUSurfaceGetCurrentTextureStatus_Timeout;
|
|
||||||
#ifdef WGPU_H_
|
|
||||||
silent = silent || surfTex.status == (WGPUSurfaceGetCurrentTextureStatus)WGPUSurfaceGetCurrentTextureStatus_Occluded;
|
|
||||||
#endif
|
|
||||||
if (!silent)
|
|
||||||
fprintf(stderr, "Failed to get surface texture (status %d)\n", surfTex.status);
|
|
||||||
if (surfTex.texture) wgpuTextureRelease(surfTex.texture);
|
|
||||||
surfTex.texture = nullptr;
|
|
||||||
return surfTex;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void renderFrame() {
|
|
||||||
ZoneScoped;
|
|
||||||
|
|
||||||
// Update rotation angle
|
|
||||||
float angle = (float)platformGetTime();
|
|
||||||
wgpuQueueWriteBuffer(gQueue, gUniformBuf, 0, &angle, sizeof(float));
|
|
||||||
|
|
||||||
WGPUSurfaceTexture surfTex = getWindowSurface();
|
|
||||||
if (!surfTex.texture) return;
|
|
||||||
|
|
||||||
WGPUTextureView view = wgpuTextureCreateView(surfTex.texture, nullptr);
|
|
||||||
|
|
||||||
// Command encoder
|
|
||||||
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(gDevice, nullptr);
|
|
||||||
|
|
||||||
// Render pass
|
|
||||||
WGPURenderPassColorAttachment colorAtt = {};
|
|
||||||
colorAtt.view = view;
|
|
||||||
colorAtt.loadOp = WGPULoadOp_Clear;
|
|
||||||
colorAtt.storeOp = WGPUStoreOp_Store;
|
|
||||||
colorAtt.clearValue = { 0.05, 0.05, 0.08, 1.0 };
|
|
||||||
colorAtt.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
|
||||||
|
|
||||||
WGPURenderPassDescriptor passDesc = {};
|
|
||||||
passDesc.colorAttachmentCount = 1;
|
|
||||||
passDesc.colorAttachments = &colorAtt;
|
|
||||||
|
|
||||||
{
|
|
||||||
ZoneScopedN("render-pass");
|
|
||||||
TracyWebGPUNamedZone(gTracyCtx, tracyZone, encoder, passDesc, "triangle draw", true);
|
|
||||||
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &passDesc);
|
|
||||||
wgpuRenderPassEncoderSetPipeline(pass, gPipeline);
|
|
||||||
wgpuRenderPassEncoderSetBindGroup(pass, 0, gBindGroup, 0, nullptr);
|
|
||||||
wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
|
|
||||||
wgpuRenderPassEncoderEnd(pass);
|
|
||||||
wgpuRenderPassEncoderRelease(pass);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Submit
|
|
||||||
WGPUCommandBuffer cmdBuf = wgpuCommandEncoderFinish(encoder, nullptr);
|
|
||||||
wgpuQueueSubmit(gQueue, 1, &cmdBuf);
|
|
||||||
|
|
||||||
// Present
|
|
||||||
wgpuSurfacePresent(gSurface);
|
|
||||||
|
|
||||||
// Process Events
|
|
||||||
wgpuInstanceProcessEvents(gInstance);
|
|
||||||
TracyWebGPUCollect(gTracyCtx);
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
wgpuCommandBufferRelease(cmdBuf);
|
|
||||||
wgpuCommandEncoderRelease(encoder);
|
|
||||||
wgpuTextureViewRelease(view);
|
|
||||||
wgpuTextureRelease(surfTex.texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Shutdown
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static void shutdown() {
|
|
||||||
fprintf(stderr, "application is shutting down...\n");
|
|
||||||
TracyWebGPUDestroy(gTracyCtx);
|
|
||||||
if (gBindGroup) wgpuBindGroupRelease(gBindGroup);
|
|
||||||
if (gUniformBuf) wgpuBufferRelease(gUniformBuf);
|
|
||||||
if (gPipeline) wgpuRenderPipelineRelease(gPipeline);
|
|
||||||
if (gQueue) wgpuQueueRelease(gQueue);
|
|
||||||
if (gDevice) wgpuDeviceRelease(gDevice);
|
|
||||||
if (gAdapter) wgpuAdapterRelease(gAdapter);
|
|
||||||
if (gSurface) wgpuSurfaceRelease(gSurface);
|
|
||||||
if (gInstance) wgpuInstanceRelease(gInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// main
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
if (!platformInit(kWidth, kHeight, "WebGPU Spinning Triangle"))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
gInstance = wgpuCreateInstance(nullptr);
|
|
||||||
if (!gInstance) return error(2, "Failed to create WebGPU instance.");
|
|
||||||
|
|
||||||
gSurface = platformCreateSurface(gInstance);
|
|
||||||
if (!gSurface) return error(3, "Failed to create surface.");
|
|
||||||
|
|
||||||
if (initWebGPU() != 0) return 4;
|
|
||||||
|
|
||||||
platformRunLoop(renderFrame, shutdown);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||