From 3fc458c4bdfc5eac6f5475fb4e4490ca1c778a08 Mon Sep 17 00:00:00 2001 From: orng Date: Sat, 28 Mar 2026 22:50:30 -0500 Subject: [PATCH 01/71] ci: make `build-linux` use `flake.nix` --- .github/workflows/build-linux.yml | 132 +++--------------------------- 1 file changed, 13 insertions(+), 119 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 7d73c103d..acec27a69 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -1,132 +1,26 @@ -name: Build (Linux, x86_64) +name: Build (Linux x86/64) on: push: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**/meson.build' - - 'meson.build' - - '**/CMakeLists.txt' - - 'CMakeLists.txt' - - '.github/workflows/build-linux.yml' - pull_request: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**/meson.build' - - 'meson.build' - - '**/CMakeLists.txt' - - 'CMakeLists.txt' - - '.github/workflows/build-linux.yml' + - "**.cpp" + - "**.h" + - "**.meson.build" + - ".github/workflows/*" jobs: build-linux: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential ccache python3 python3-pip ninja-build libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev - python -m pip install meson - # Set a reasonable ccache size - ccache -M 5G || true - - - name: Restore ccache - uses: actions/cache@v4 + - name: Install Nix + uses: cachix/install-nix-action@v31 with: - path: ~/.ccache - key: ${{ runner.os }}-ccache-${{ hashFiles('**/meson.build') }} + nix_path: nixpkgs=channel:nixos-unstable - - name: Restore meson cache - uses: actions/cache@v4 - with: - path: ~/.cache/meson - key: ${{ runner.os }}-meson-${{ hashFiles('**/meson.build') }} + - name: Build + run: nix build - - name: Configure Meson - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - mkdir -p "$CCACHE_DIR" - export CCACHE_DIR="$CCACHE_DIR" - meson setup build_release --wipe --buildtype=release --native-file=./scripts/llvm_native.txt - - - name: Build with Meson - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - export CCACHE_DIR="${{ runner.temp }}/ccache" - # Use all available cores for faster parallel builds - meson compile -C build_release -j $(nproc) -v Minecraft.Client - - - name: Install patchelf - run: sudo apt-get install -y patchelf - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: minecraft-client-linux-release_exe-${{ github.sha }} - path: build_release/Minecraft.Client/Minecraft.Client - retention-days: 7 - build-linux-debug: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential ccache python3 python3-pip ninja-build libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev - python -m pip install meson - # Set a reasonable ccache size - ccache -M 5G || true - - - name: Restore ccache - uses: actions/cache@v4 - with: - path: ~/.ccache - key: ${{ runner.os }}-ccache-debug-${{ hashFiles('**/meson.build') }} - - - name: Restore meson cache - uses: actions/cache@v4 - with: - path: ~/.cache/meson - key: ${{ runner.os }}-meson-debug-${{ hashFiles('**/meson.build') }} - - - name: Configure Meson (debug) - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - mkdir -p "$CCACHE_DIR" - export CCACHE_DIR="$CCACHE_DIR" - meson setup build_debug --wipe --buildtype=debug --native-file=./scripts/llvm_native.txt - - - name: Build Debug with Meson - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - export CCACHE_DIR="${{ runner.temp }}/ccache" - # Use all available cores for faster parallel builds - meson compile -C build_debug -j $(nproc) -v Minecraft.Client - - - name: Upload debug executable - uses: actions/upload-artifact@v4 - with: - name: minecraft-client-linux-debug_exe-${{ github.sha }} - path: build_debug/Minecraft.Client/Minecraft.Client - retention-days: 7 + - name: Flake integrity + run: nix flake check From 3a736777fd168a15e1eb89363818205fa72a6a77 Mon Sep 17 00:00:00 2001 From: orng Date: Sat, 28 Mar 2026 22:50:45 -0500 Subject: [PATCH 02/71] ci: simplify `clang-format` --- .github/workflows/clang-format.yml | 72 ++++++------------------------ 1 file changed, 13 insertions(+), 59 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 4965d4b15..ed5f9a402 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,69 +1,23 @@ -name: Clang Format +name: Clang-Format (Linux x86/64) on: push: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**.cc' - - '**.cxx' - - '**.hh' - - '**.hpp' - - '**.hxx' - - '**.inl' - - '.clang-format' - - '.github/workflows/clang-format.yml' - - '.github/scripts/check-clang-format.sh' - pull_request: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**.cc' - - '**.cxx' - - '**.hh' - - '**.hpp' - - '**.hxx' - - '**.inl' - - '.clang-format' - - '.github/workflows/clang-format.yml' - - '.github/scripts/check-clang-format.sh' + - "**.cpp" + - "**.h" + - "**.meson.build" + - ".github/workflows/*" jobs: - clang-format: - runs-on: ubuntu-24.04 - concurrency: - group: clang-format-${{ github.ref }} - cancel-in-progress: true + formatting-check: + runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Install clang-format - run: | - sudo apt-get update - sudo apt-get install -y clang-format-19 - - - name: Check changed files - env: - CLANG_FORMAT_BIN: clang-format-19 - EVENT_NAME: ${{ github.event_name }} - PR_BASE_REF: ${{ github.event.pull_request.base.ref }} - PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} - BEFORE_SHA: ${{ github.event.before }} - CURRENT_SHA: ${{ github.sha }} - run: | - set -euo pipefail - - BASE_SHA="" - if [ "$EVENT_NAME" = "pull_request" ]; then - git fetch --no-tags origin "$PR_BASE_REF" - BASE_SHA="$(git merge-base "origin/$PR_BASE_REF" "$CURRENT_SHA")" - elif [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then - BASE_SHA="$BEFORE_SHA" - fi - - bash ./.github/scripts/check-clang-format.sh "$BASE_SHA" "$CURRENT_SHA" + - name: Run clang-format style check + uses: jidicula/clang-format-action@v4.18.0 + with: + # keep this in line with the flake + clang-format-version: "21" From 09f3a33eddf82beacbb981eb46a5ba987d66a252 Mon Sep 17 00:00:00 2001 From: orng Date: Sat, 28 Mar 2026 22:59:03 -0500 Subject: [PATCH 03/71] test: push dummy file --- Minecraft.Client/really_good_cpp.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Minecraft.Client/really_good_cpp.cpp diff --git a/Minecraft.Client/really_good_cpp.cpp b/Minecraft.Client/really_good_cpp.cpp new file mode 100644 index 000000000..d4244cd5b --- /dev/null +++ b/Minecraft.Client/really_good_cpp.cpp @@ -0,0 +1,15 @@ +// This is literally the best c++ code ever. I hope everyone reading this has a fantastic day. + +struct Foo { + Bar bar, baz, + + Foo() noexcept : bar(),baz() { delete Foo; } + + void qux() const { + print("Hello, world!") + } + + private: + + bool bob() { hi }; +}; From a954f3f61699184390c23365cbf58c6f492848a2 Mon Sep 17 00:00:00 2001 From: orng Date: Sat, 28 Mar 2026 23:01:06 -0500 Subject: [PATCH 04/71] chore: remove dummy file --- Minecraft.Client/really_good_cpp.cpp | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 Minecraft.Client/really_good_cpp.cpp diff --git a/Minecraft.Client/really_good_cpp.cpp b/Minecraft.Client/really_good_cpp.cpp deleted file mode 100644 index d4244cd5b..000000000 --- a/Minecraft.Client/really_good_cpp.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// This is literally the best c++ code ever. I hope everyone reading this has a fantastic day. - -struct Foo { - Bar bar, baz, - - Foo() noexcept : bar(),baz() { delete Foo; } - - void qux() const { - print("Hello, world!") - } - - private: - - bool bob() { hi }; -}; From ceea5c356c696f27c13070fbba053d029200ddad Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:09:50 -0500 Subject: [PATCH 05/71] guh 1 --- .github/workflows/build-linux.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index acec27a69..83a05bb33 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -1,11 +1,12 @@ -name: Build (Linux x86/64) +name: Build (Linux, x86-64) on: push: - "**.cpp" - "**.h" - "**.meson.build" - - ".github/workflows/*" + - '.github/workflows/build-linux.yml' + jobs: build-linux: From d669174ecdd22de1627844178d0bcbb1fefac98a Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:11:32 -0500 Subject: [PATCH 06/71] guh 2 --- .github/workflows/build-linux.yml | 11 ++++++----- .github/workflows/clang-format.yml | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 83a05bb33..14697e934 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -2,10 +2,11 @@ name: Build (Linux, x86-64) on: push: - - "**.cpp" - - "**.h" - - "**.meson.build" - - '.github/workflows/build-linux.yml' + paths: + - "**.cpp" + - "**.h" + - "**.meson.build" + - '.github/workflows/*' jobs: @@ -13,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install Nix uses: cachix/install-nix-action@v31 diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index ed5f9a402..befc1e792 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,11 +1,12 @@ -name: Clang-Format (Linux x86/64) +name: Clang-Format (Linux, x86-64) on: push: - - "**.cpp" - - "**.h" - - "**.meson.build" - - ".github/workflows/*" + paths: + - "**.cpp" + - "**.h" + - "**.meson.build" + - ".github/workflows/*" jobs: formatting-check: From 80ca9142ef9081584a7a8a591c9cfc9da7131338 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:14:35 -0500 Subject: [PATCH 07/71] guh 3 --- .github/workflows/build-linux.yml | 19 +++++++++++++------ .github/workflows/clang-format.yml | 18 +++++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 14697e934..5729f6e2e 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -3,10 +3,17 @@ name: Build (Linux, x86-64) on: push: paths: - - "**.cpp" - - "**.h" + - '**.cpp' + - '**.h' + - '**.c' + - '**.cc' + - '**.cxx' + - '**.hh' + - '**.hpp' + - '**.hxx' + - '**.inl' - "**.meson.build" - - '.github/workflows/*' + - '.github/workflows/build-linux.yml*' jobs: @@ -21,8 +28,8 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - - name: Build - run: nix build - - name: Flake integrity run: nix flake check + + - name: Build + run: nix build diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index befc1e792..d51d1ee74 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,12 +1,20 @@ -name: Clang-Format (Linux, x86-64) +name: Check Format (Linux, x86-64) on: push: paths: - - "**.cpp" - - "**.h" - - "**.meson.build" - - ".github/workflows/*" + - '**.cpp' + - '**.h' + - '**.c' + - '**.cc' + - '**.cxx' + - '**.hh' + - '**.hpp' + - '**.hxx' + - '**.inl' + - '.clang-format' + - '.github/workflows/clang-format.yml' + - '.github/scripts/check-clang-format.sh' jobs: formatting-check: From 23452bf1292888dd72791949018bfe4e39497985 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:15:53 -0500 Subject: [PATCH 08/71] guh 4 --- .github/workflows/build-linux.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 5729f6e2e..2fb11be51 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -14,7 +14,19 @@ on: - '**.inl' - "**.meson.build" - '.github/workflows/build-linux.yml*' - + pull_request: + paths: + - '**.cpp' + - '**.h' + - '**.c' + - '**.cc' + - '**.cxx' + - '**.hh' + - '**.hpp' + - '**.hxx' + - '**.inl' + - "**.meson.build" + - '.github/workflows/build-linux.yml*' jobs: build-linux: From ec2a2b8d1362470e1a19132582ee605feaffa5cd Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:16:51 -0500 Subject: [PATCH 09/71] guhhhhhh --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index d51d1ee74..01da33f84 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 From a66a391225e0e209cb9ffa122a808c82cbc4460d Mon Sep 17 00:00:00 2001 From: orng Date: Sun, 29 Mar 2026 19:45:20 -0500 Subject: [PATCH 10/71] fix: `flake.nix` should include glm --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index fd8af4473..2db0000d7 100644 --- a/flake.nix +++ b/flake.nix @@ -100,6 +100,7 @@ openssl.dev libGL libGLU + glm SDL2 zlib ]; From 715f5e1dae3ba2df780c645fd870f280ec8d8b8e Mon Sep 17 00:00:00 2001 From: orng Date: Sun, 29 Mar 2026 19:50:20 -0500 Subject: [PATCH 11/71] fix: `clang-format` should have an `on: pull_request` --- .github/workflows/clang-format.yml | 37 ++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 01da33f84..4908ef1f9 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -3,18 +3,31 @@ name: Check Format (Linux, x86-64) on: push: paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**.cc' - - '**.cxx' - - '**.hh' - - '**.hpp' - - '**.hxx' - - '**.inl' - - '.clang-format' - - '.github/workflows/clang-format.yml' - - '.github/scripts/check-clang-format.sh' + - "**.cpp" + - "**.h" + - "**.c" + - "**.cc" + - "**.cxx" + - "**.hh" + - "**.hpp" + - "**.hxx" + - "**.inl" + - ".clang-format" + - ".github/workflows/clang-format.yml" + - ".github/scripts/check-clang-format.sh" + pull_request: + paths: + - "**.cpp" + - "**.h" + - "**.c" + - "**.cc" + - "**.cxx" + - "**.hh" + - "**.hpp" + - "**.hxx" + - "**.inl" + - "**.meson.build" + - ".github/workflows/clang-format.yml*" jobs: formatting-check: From aafc823c75cdc3bbaffa48b30417748dc28b293f Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:52:44 -0500 Subject: [PATCH 12/71] fix: --print-build-logs --- .github/workflows/build-linux.yml | 47 +++++++++++------------------- .github/workflows/clang-format.yml | 29 +++++++++--------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 2fb11be51..11e6eed3b 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -1,32 +1,19 @@ name: Build (Linux, x86-64) -on: - push: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**.cc' - - '**.cxx' - - '**.hh' - - '**.hpp' - - '**.hxx' - - '**.inl' - - "**.meson.build" - - '.github/workflows/build-linux.yml*' - pull_request: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**.cc' - - '**.cxx' - - '**.hh' - - '**.hpp' - - '**.hxx' - - '**.inl' - - "**.meson.build" - - '.github/workflows/build-linux.yml*' +on: [push, pull_request] + paths: + - '**.cpp' + - '**.h' + - '**.c' + - '**.cc' + - '**.cxx' + - '**.hh' + - '**.hpp' + - '**.hxx' + - '**.inl' + - "**meson.build" + - "flake.nix" + - '.github/workflows/build-linux.yml' jobs: build-linux: @@ -40,8 +27,8 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable + - name: Build + run: nix build --print-build-logs + - name: Flake integrity run: nix flake check - - - name: Build - run: nix build diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 01da33f84..6aab5fd76 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,20 +1,19 @@ name: Check Format (Linux, x86-64) -on: - push: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**.cc' - - '**.cxx' - - '**.hh' - - '**.hpp' - - '**.hxx' - - '**.inl' - - '.clang-format' - - '.github/workflows/clang-format.yml' - - '.github/scripts/check-clang-format.sh' +on: [push, pull_request] + paths: + - '**.cpp' + - '**.h' + - '**.c' + - '**.cc' + - '**.cxx' + - '**.hh' + - '**.hpp' + - '**.hxx' + - '**.inl' + - "**meson.build" + - "flake.nix" + - '.github/workflows/build-linux.yml' jobs: formatting-check: From 43908b1de581668f52b289af9d7cf45a20b6451b Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:54:51 -0500 Subject: [PATCH 13/71] Update clang-format.yml --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 4a01c2676..c099cd110 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,4 +1,4 @@ -name: Check Format (Linux, x86-64) +name: Format Check (Linux, x86-64) on: [push, pull_request] paths: From 39ebddbafcac7182de58dedeebff8cc1727b2fac Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:04:19 -0500 Subject: [PATCH 14/71] update format workflow --- .github/workflows/clang-format.yml | 49 +++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index c099cd110..2bf8ec762 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,4 +1,4 @@ -name: Format Check (Linux, x86-64) +name: Format on: [push, pull_request] paths: @@ -11,23 +11,42 @@ on: [push, pull_request] - '**.hpp' - '**.hxx' - '**.inl' - - "**meson.build" - - "flake.nix" - - ".clang-format" - - '.github/workflows/build-linux.yml' - - ".github/scripts/check-clang-format.sh" + - '.clang-format' + - '.github/workflows/clang-format.yml' + - '.github/scripts/check-clang-format.sh' jobs: - formatting-check: + clang-format: runs-on: ubuntu-latest + concurrency: + group: clang-format-${{ github.ref }} + cancel-in-progress: true steps: - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 + uses: actions/checkout@v4 - - name: Run clang-format style check - uses: jidicula/clang-format-action@v4.18.0 - with: - # keep this in line with the flake - clang-format-version: "21" + - name: Install clang-format + run: | + sudo apt-get update + sudo apt-get install -y clang-format-21 + + - name: Check changed files + env: + CLANG_FORMAT_BIN: clang-format + EVENT_NAME: ${{ github.event_name }} + PR_BASE_REF: ${{ github.event.pull_request.base.ref }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + BEFORE_SHA: ${{ github.event.before }} + CURRENT_SHA: ${{ github.sha }} + run: | + set -euo pipefail + + BASE_SHA="" + if [ "$EVENT_NAME" = "pull_request" ]; then + git fetch --no-tags origin "$PR_BASE_REF" + BASE_SHA="$(git merge-base "origin/$PR_BASE_REF" "$CURRENT_SHA")" + elif [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then + BASE_SHA="$BEFORE_SHA" + fi + + bash ./.github/scripts/check-clang-format.sh "$BASE_SHA" "$CURRENT_SHA" From 327addddaefb3d928f62f1eab379bb04be9332b3 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:06:38 -0500 Subject: [PATCH 15/71] yaml and its consequences for the human race --- .github/workflows/build-linux.yml | 32 +++++++++++++++++------------- .github/workflows/clang-format.yml | 32 +++++++++++++++++------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 11e6eed3b..e1ff0b0fd 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -1,19 +1,23 @@ name: Build (Linux, x86-64) -on: [push, pull_request] - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**.cc' - - '**.cxx' - - '**.hh' - - '**.hpp' - - '**.hxx' - - '**.inl' - - "**meson.build" - - "flake.nix" - - '.github/workflows/build-linux.yml' +on: + push: + paths: &workflow_paths + - '**.cpp' + - '**.h' + - '**.c' + - '**.cc' + - '**.cxx' + - '**.hh' + - '**.hpp' + - '**.hxx' + - '**.inl' + - "**meson.build" + - "flake.nix" + - '.github/workflows/build-linux.yml' + + pull_request: + paths: *workflow_paths jobs: build-linux: diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 2bf8ec762..9a4b43905 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,19 +1,23 @@ name: Format -on: [push, pull_request] - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**.cc' - - '**.cxx' - - '**.hh' - - '**.hpp' - - '**.hxx' - - '**.inl' - - '.clang-format' - - '.github/workflows/clang-format.yml' - - '.github/scripts/check-clang-format.sh' +on: + push: + paths: &workflow_paths + - '**.cpp' + - '**.h' + - '**.c' + - '**.cc' + - '**.cxx' + - '**.hh' + - '**.hpp' + - '**.hxx' + - '**.inl' + - '.clang-format' + - '.github/workflows/clang-format.yml' + - '.github/scripts/check-clang-format.sh' + + pull_request: + paths: *workflow_paths jobs: clang-format: From dcebd64557b96a9080432d882e36d7f5d1f2a374 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:07:16 -0500 Subject: [PATCH 16/71] rename to format check --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 9a4b43905..7626686b9 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,4 +1,4 @@ -name: Format +name: Format Check on: push: From 6010fa184801118c4fa54816fdd27d651b274af2 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:35:03 -0500 Subject: [PATCH 17/71] test --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 2db0000d7..05ceb4fef 100644 --- a/flake.nix +++ b/flake.nix @@ -54,6 +54,7 @@ version = "0.1.0"; src = ./.; + dontFixup = true; dontUseCmakeConfigure = true; # 4jcraft - Meson expects this subprojects structure From 0e9c1403bff1530200b89a58734a643c2ed9ad54 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:36:42 -0500 Subject: [PATCH 18/71] hate you ubuntu --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 7626686b9..e1bdc4b84 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -32,7 +32,7 @@ jobs: - name: Install clang-format run: | sudo apt-get update - sudo apt-get install -y clang-format-21 + sudo apt-get install -y clang-format - name: Check changed files env: From 7b5c2dfe257cd6dffc3a115fcbed0982c3826c09 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:43:14 -0500 Subject: [PATCH 19/71] use fetch-depth 0 for format workflow --- .github/workflows/clang-format.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index e1bdc4b84..297144427 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -27,7 +27,9 @@ jobs: cancel-in-progress: true steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 + with: + fetch-depth: 0 - name: Install clang-format run: | From 9d40fceecec0a736fab34fa7b06898f8b4d4d73e Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:51:08 -0500 Subject: [PATCH 20/71] cache clang-format installation --- .github/workflows/clang-format.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 297144427..84820ad88 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -29,12 +29,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 with: - fetch-depth: 0 + fetch-depth: 2 - name: Install clang-format - run: | - sudo apt-get update - sudo apt-get install -y clang-format + uses: daaku/gh-action-apt-install@v4 + with: + packages: clang-format - name: Check changed files env: From fbb7b304c8a86c4c171b618cbaaa5f0f0a42562a Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Thu, 26 Mar 2026 00:12:59 +0100 Subject: [PATCH 21/71] first batch & fix with new files (very hacky & uncleaned state); state: game runs but broken rendering every chunks is at 0,0,0 @n@ --- .../Platform/Linux/Iggy/gdraw/gdraw.c | 818 ++++++++++++++++++ .../Linux/Iggy/gdraw/{gdraw_sdl.h => gdraw.h} | 9 +- .../Platform/Linux/Iggy/gdraw/gdraw_sdl.c | 240 ----- Minecraft.Client/Platform/Linux/LinuxGL.cpp | 200 +++-- .../Platform/Linux/Linux_UIController.cpp | 52 +- Minecraft.Client/Platform/stdafx.h | 251 +++--- Minecraft.Client/Platform/stubs.h | 334 ++++--- 7 files changed, 1250 insertions(+), 654 deletions(-) create mode 100644 Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c rename Minecraft.Client/Platform/Linux/Iggy/gdraw/{gdraw_sdl.h => gdraw.h} (94%) delete mode 100644 Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.c diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c new file mode 100644 index 000000000..0c2cea4d5 --- /dev/null +++ b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c @@ -0,0 +1,818 @@ +#define GDRAW_ASSERTS + +#include "../../../Windows64/Iggy/include/iggy.h" +#include "../../../Windows64/Iggy/include/gdraw.h" +#include "gdraw.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define true 1 +#define false 0 + + +#ifndef _ENABLEIGGY +void* IggyGDrawMallocAnnotated(SINTa size, const char* file, int line) { + (void)file; + (void)line; + return malloc((size_t)size); +} + +void IggyGDrawFree(void* ptr) { free(ptr); } + +void IggyGDrawSendWarning(Iggy* f, char const* message, ...) { + (void)f; + va_list args; + va_start(args, message); + fprintf(stderr, "[Iggy GDraw Warning] "); + vfprintf(stderr, message, args); + fprintf(stderr, "\n"); + va_end(args); +} + +void IggyDiscardVertexBufferCallback(void* owner, void* buf) { + (void)owner; + (void)buf; +} +#endif + +// We track all the extensions we use. +#define GDRAW_GL_EXTENSION_LIST \ + /* identifier import procname */ \ + /* GL_ARB_vertex_buffer_object */ \ + GLE(GenBuffers, "GenBuffersARB", GENBUFFERSARB) \ + GLE(DeleteBuffers, "DeleteBuffersARB", DELETEBUFFERSARB) \ + GLE(BindBuffer, "BindBufferARB", BINDBUFFERARB) \ + GLE(BufferData, "BufferDataARB", BUFFERDATAARB) \ + GLE(MapBuffer, "MapBufferARB", MAPBUFFERARB) \ + GLE(UnmapBuffer, "UnmapBufferARB", UNMAPBUFFERARB) \ + GLE(VertexAttribPointer, "VertexAttribPointerARB", VERTEXATTRIBPOINTERARB) \ + GLE(EnableVertexAttribArray, "EnableVertexAttribArrayARB", \ + ENABLEVERTEXATTRIBARRAYARB) \ + GLE(DisableVertexAttribArray, "DisableVertexAttribArrayARB", \ + DISABLEVERTEXATTRIBARRAYARB) \ + /* GL_ARB_shader_objects */ \ + GLE(CreateShader, "CreateShaderObjectARB", CREATESHADEROBJECTARB) \ + GLE(DeleteShader, "DeleteObjectARB", DELETEOBJECTARB) \ + GLE(ShaderSource, "ShaderSourceARB", SHADERSOURCEARB) \ + GLE(CompileShader, "CompileShaderARB", COMPILESHADERARB) \ + GLE(GetShaderiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ + GLE(GetShaderInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ + GLE(CreateProgram, "CreateProgramObjectARB", CREATEPROGRAMOBJECTARB) \ + GLE(DeleteProgram, "DeleteObjectARB", DELETEOBJECTARB) \ + GLE(AttachShader, "AttachObjectARB", ATTACHOBJECTARB) \ + GLE(LinkProgram, "LinkProgramARB", LINKPROGRAMARB) \ + GLE(GetUniformLocation, "GetUniformLocationARB", GETUNIFORMLOCATIONARB) \ + GLE(UseProgram, "UseProgramObjectARB", USEPROGRAMOBJECTARB) \ + GLE(GetProgramiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ + GLE(GetProgramInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ + GLE(Uniform1i, "Uniform1iARB", UNIFORM1IARB) \ + GLE(Uniform4f, "Uniform4fARB", UNIFORM4FARB) \ + GLE(Uniform4fv, "Uniform4fvARB", UNIFORM4FVARB) \ + /* GL_ARB_vertex_shader */ \ + GLE(BindAttribLocation, "BindAttribLocationARB", BINDATTRIBLOCATIONARB) \ + /* Missing from WGL but needed by shared code */ \ + GLE(Uniform1f, "Uniform1fARB", UNIFORM1FARB) \ + /* GL_EXT_framebuffer_object */ \ + GLE(GenRenderbuffers, "GenRenderbuffersEXT", GENRENDERBUFFERSEXT) \ + GLE(DeleteRenderbuffers, "DeleteRenderbuffersEXT", DELETERENDERBUFFERSEXT) \ + GLE(BindRenderbuffer, "BindRenderbufferEXT", BINDRENDERBUFFEREXT) \ + GLE(RenderbufferStorage, "RenderbufferStorageEXT", RENDERBUFFERSTORAGEEXT) \ + GLE(GenFramebuffers, "GenFramebuffersEXT", GENFRAMEBUFFERSEXT) \ + GLE(DeleteFramebuffers, "DeleteFramebuffersEXT", DELETEFRAMEBUFFERSEXT) \ + GLE(BindFramebuffer, "BindFramebufferEXT", BINDFRAMEBUFFEREXT) \ + GLE(CheckFramebufferStatus, "CheckFramebufferStatusEXT", \ + CHECKFRAMEBUFFERSTATUSEXT) \ + GLE(FramebufferRenderbuffer, "FramebufferRenderbufferEXT", \ + FRAMEBUFFERRENDERBUFFEREXT) \ + GLE(FramebufferTexture2D, "FramebufferTexture2DEXT", \ + FRAMEBUFFERTEXTURE2DEXT) \ + GLE(GenerateMipmap, "GenerateMipmapEXT", GENERATEMIPMAPEXT) \ + /* GL_EXT_framebuffer_blit */ \ + GLE(BlitFramebuffer, "BlitFramebufferEXT", BLITFRAMEBUFFEREXT) \ + /* GL_EXT_framebuffer_multisample */ \ + GLE(RenderbufferStorageMultisample, "RenderbufferStorageMultisampleEXT", \ + RENDERBUFFERSTORAGEMULTISAMPLEEXT) \ + /* */ + + +// Shared .inl +#define gdraw_GLx_(id) gdraw_GL_##id +#define GDRAW_GLx_(id) GDRAW_GL_##id +#define GDRAW_SHADERS "gdraw_gl_shaders.inl" + +// GLhandleARB is void* but shader functions use GLuint values. +// homework stolen from gdraw_gl_shared.inl. +#define GDrawGLProgram GLuint +typedef GLuint GLhandle; +typedef gdraw_gl_resourcetype gdraw_resourcetype; + +#define GLE(id, import, procname) static PFNGL##procname##PROC gl##id; +GDRAW_GL_EXTENSION_LIST +#undef GLE + + +typedef const GLubyte*(APIENTRYP PFNGLGETSTRINGIPROC_)(GLenum name, + GLuint index); +static PFNGLGETSTRINGIPROC_ gdraw_glGetStringi = NULL; + +typedef void(APIENTRYP PFNGLGENVERTEXARRAYSPROC_)(GLsizei n, GLuint* arrays); +typedef void(APIENTRYP PFNGLBINDVERTEXARRAYPROC_)(GLuint array); +static PFNGLGENVERTEXARRAYSPROC_ gdraw_glGenVertexArrays = NULL; +static PFNGLBINDVERTEXARRAYPROC_ gdraw_glBindVertexArray = NULL; +static GLuint gdraw_vao = 0; + +typedef void(APIENTRYP gdraw_vtxattrib_fn)(GLuint, GLint, GLenum, GLboolean, + GLsizei, const void*); +static gdraw_vtxattrib_fn gdraw_real_vtxattrib = NULL; +static GLuint gdraw_screenvbo = 0; +static const void* gdraw_screenvbo_base = NULL; +static size_t gdraw_expected_vbo_size = 0; + + +typedef void(APIENTRYP gdraw_drawelements_fn)(GLenum mode, GLsizei count, + GLenum type, const void* indices); +static gdraw_drawelements_fn gdraw_real_drawelements = NULL; +static GLuint gdraw_screenibo = 0; + + +typedef GLuint(APIENTRYP gdraw_createshader_fn)(GLenum); +typedef void(APIENTRYP gdraw_shadersource_fn)(GLuint, GLsizei, const GLchar**, + const GLint*); +typedef void(APIENTRYP gdraw_compileshader_fn)(GLuint); +typedef void(APIENTRYP gdraw_linkprogram_fn)(GLuint); +static gdraw_createshader_fn gdraw_real_createshader = NULL; +static gdraw_shadersource_fn gdraw_real_shadersource = NULL; +static gdraw_compileshader_fn gdraw_real_compileshader = NULL; +static gdraw_linkprogram_fn gdraw_real_linkprogram = NULL; + +// some core reject p0 + +typedef void(APIENTRYP gdraw_useprogram_fn)(GLuint); +static gdraw_useprogram_fn gdraw_real_useprogram = NULL; +static GLuint gdraw_null_program = 0; + + +typedef void(APIENTRYP gdraw_teximage2d_fn)(GLenum, GLint, GLint, GLsizei, + GLsizei, GLint, GLenum, GLenum, + const void*); +typedef void(APIENTRYP gdraw_texsubimage2d_fn)(GLenum, GLint, GLint, GLint, + GLsizei, GLsizei, GLenum, GLenum, + const void*); +static gdraw_teximage2d_fn gdraw_real_teximage2d = NULL; +static gdraw_texsubimage2d_fn gdraw_real_texsubimage2d = NULL; + +#define TRY(ptr, arb, core) \ + do { \ + void* _p = SDL_GL_GetProcAddress(core); \ + if (!_p) _p = SDL_GL_GetProcAddress(arb); \ + *(void**)&(ptr) = _p; \ + } while (0) + +static void load_extensions(void) { +// gl_shared requires ts shit ugh +#define GLE(id, import, procname) \ + gl##id = (PFNGL##procname##PROC)SDL_GL_GetProcAddress("gl" import); + GDRAW_GL_EXTENSION_LIST +#undef GLE + + TRY(glCreateShader, "glCreateShaderObjectARB", "glCreateShader"); + TRY(glDeleteShader, "glDeleteObjectARB", "glDeleteShader"); + TRY(glShaderSource, "glShaderSourceARB", "glShaderSource"); + TRY(glCompileShader, "glCompileShaderARB", "glCompileShader"); + TRY(glGetShaderiv, "glGetObjectParameterivARB", "glGetShaderiv"); + TRY(glGetShaderInfoLog, "glGetInfoLogARB", "glGetShaderInfoLog"); + TRY(glCreateProgram, "glCreateProgramObjectARB", "glCreateProgram"); + TRY(glDeleteProgram, "glDeleteObjectARB", "glDeleteProgram"); + TRY(glAttachShader, "glAttachObjectARB", "glAttachShader"); + TRY(glLinkProgram, "glLinkProgramARB", "glLinkProgram"); + TRY(glGetUniformLocation, "glGetUniformLocationARB", + "glGetUniformLocation"); + TRY(glUseProgram, "glUseProgramObjectARB", "glUseProgram"); + TRY(glGetProgramiv, "glGetObjectParameterivARB", "glGetProgramiv"); + TRY(glGetProgramInfoLog, "glGetInfoLogARB", "glGetProgramInfoLog"); + TRY(glUniform1i, "glUniform1iARB", "glUniform1i"); + TRY(glUniform4f, "glUniform4fARB", "glUniform4f"); + TRY(glUniform4fv, "glUniform4fvARB", "glUniform4fv"); + TRY(glUniform1f, "glUniform1fARB", "glUniform1f"); + TRY(glBindAttribLocation, "glBindAttribLocationARB", + "glBindAttribLocation"); + + TRY(glGenBuffers, "glGenBuffersARB", "glGenBuffers"); + TRY(glDeleteBuffers, "glDeleteBuffersARB", "glDeleteBuffers"); + TRY(glBindBuffer, "glBindBufferARB", "glBindBuffer"); + TRY(glBufferData, "glBufferDataARB", "glBufferData"); + TRY(glMapBuffer, "glMapBufferARB", "glMapBuffer"); + TRY(glUnmapBuffer, "glUnmapBufferARB", "glUnmapBuffer"); + TRY(glVertexAttribPointer, "glVertexAttribPointerARB", + "glVertexAttribPointer"); + TRY(glEnableVertexAttribArray, "glEnableVertexAttribArrayARB", + "glEnableVertexAttribArray"); + TRY(glDisableVertexAttribArray, "glDisableVertexAttribArrayARB", + "glDisableVertexAttribArray"); + + TRY(glGenRenderbuffers, "glGenRenderbuffersEXT", "glGenRenderbuffers"); + TRY(glDeleteRenderbuffers, "glDeleteRenderbuffersEXT", + "glDeleteRenderbuffers"); + TRY(glBindRenderbuffer, "glBindRenderbufferEXT", "glBindRenderbuffer"); + TRY(glRenderbufferStorage, "glRenderbufferStorageEXT", + "glRenderbufferStorage"); + TRY(glGenFramebuffers, "glGenFramebuffersEXT", "glGenFramebuffers"); + TRY(glDeleteFramebuffers, "glDeleteFramebuffersEXT", + "glDeleteFramebuffers"); + TRY(glBindFramebuffer, "glBindFramebufferEXT", "glBindFramebuffer"); + TRY(glCheckFramebufferStatus, "glCheckFramebufferStatusEXT", + "glCheckFramebufferStatus"); + TRY(glFramebufferRenderbuffer, "glFramebufferRenderbufferEXT", + "glFramebufferRenderbuffer"); + TRY(glFramebufferTexture2D, "glFramebufferTexture2DEXT", + "glFramebufferTexture2D"); + TRY(glGenerateMipmap, "glGenerateMipmapEXT", "glGenerateMipmap"); + TRY(glBlitFramebuffer, "glBlitFramebufferEXT", "glBlitFramebuffer"); + TRY(glRenderbufferStorageMultisample, "glRenderbufferStorageMultisampleEXT", + "glRenderbufferStorageMultisample"); + + // Save raw pointers before we #define over the names below + gdraw_real_vtxattrib = (gdraw_vtxattrib_fn)(void*)glVertexAttribPointer; + gdraw_real_createshader = (gdraw_createshader_fn)(void*)glCreateShader; + gdraw_real_shadersource = (gdraw_shadersource_fn)(void*)glShaderSource; + gdraw_real_compileshader = (gdraw_compileshader_fn)(void*)glCompileShader; + gdraw_real_linkprogram = (gdraw_linkprogram_fn)(void*)glLinkProgram; + gdraw_real_teximage2d = (gdraw_teximage2d_fn)(void*)glTexImage2D; + gdraw_real_texsubimage2d = (gdraw_texsubimage2d_fn)(void*)glTexSubImage2D; + gdraw_real_useprogram = (gdraw_useprogram_fn)(void*)glUseProgram; + gdraw_real_drawelements = + (gdraw_drawelements_fn)SDL_GL_GetProcAddress("glDrawElements"); + + gdraw_glGetStringi = + (PFNGLGETSTRINGIPROC_)SDL_GL_GetProcAddress("glGetStringi"); + // VAO + gdraw_glGenVertexArrays = + (PFNGLGENVERTEXARRAYSPROC_)SDL_GL_GetProcAddress("glGenVertexArrays"); + gdraw_glBindVertexArray = + (PFNGLBINDVERTEXARRAYPROC_)SDL_GL_GetProcAddress("glBindVertexArray"); + + if (gdraw_glGenVertexArrays && gdraw_glBindVertexArray && gdraw_vao == 0) { + gdraw_glGenVertexArrays(1, &gdraw_vao); + gdraw_glBindVertexArray(gdraw_vao); + } +} + +#undef TRY + +// rebind vbo + +static void clear_renderstate_platform_specific(void) { + if (gdraw_glBindVertexArray && gdraw_vao) + gdraw_glBindVertexArray(gdraw_vao); +} + +static void error_msg_platform_specific(const char* msg) { + fprintf(stderr, "[GDraw] %s\n", msg); +} + +#define GDRAW_MULTISAMPLING + +// i wish i could improve this function +#ifdef RR_BREAK +#undef RR_BREAK +#endif +#define RR_BREAK() \ + do { \ + fprintf(stderr, "[GDraw] GL error at %s:%d\n", __FILE__, __LINE__); \ + } while (0) + + +// the magic number that tropical told me +#define GDRAW_MAX_SHADERS 64 +static struct { + GLuint handle; + GLenum type; +} gdraw_shader_types[GDRAW_MAX_SHADERS]; +static int gdraw_shader_type_count = 0; + +static GLenum gdraw_get_shader_type(GLuint shader) { + int i; + for (i = 0; i < gdraw_shader_type_count; i++) + if (gdraw_shader_types[i].handle == shader) + return gdraw_shader_types[i].type; + return GL_FRAGMENT_SHADER; +} + +static GLuint gdraw_CreateShaderTracked(GLenum type) { + GLuint h = gdraw_real_createshader(type); + if (h && gdraw_shader_type_count < GDRAW_MAX_SHADERS) { + gdraw_shader_types[gdraw_shader_type_count].handle = h; + gdraw_shader_types[gdraw_shader_type_count].type = type; + gdraw_shader_type_count++; + } + return h; +} + + + +static void gdraw_CompileShaderAndLog(GLuint shader) { + GLint status = 0; + gdraw_real_compileshader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (!status) { + char log[2048]; + GLint len = 0; + glGetShaderInfoLog(shader, (GLsizei)sizeof(log) - 1, &len, log); + log[len] = '\0'; + fprintf(stderr, "[GDraw GLSL] compile FAILED shader=%u:\n%s\n", shader, + log); + fflush(stderr); + } +} + +static void gdraw_LinkProgramAndLog(GLuint program) { + GLint status = 0; + gdraw_real_linkprogram(program); + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (!status) { + char log[2048]; + GLint len = 0; + glGetProgramInfoLog(program, (GLsizei)sizeof(log) - 1, &len, log); + log[len] = '\0'; + fprintf(stderr, "[GDraw GLSL] link FAILED program=%u:\n%s\n", program, + log); + fflush(stderr); + } +} + +#undef glCreateShader +#define glCreateShader gdraw_CreateShaderTracked + +// This is the part that turns the old ugly shaders to 330 +static char* gdraw_strreplace(char* src, const char* find, const char* rep) { + char* result; + char* pos; + char* base = src; + size_t find_len = strlen(find); + size_t rep_len = strlen(rep); + size_t count = 0; + char* tmp = src; + + while ((tmp = strstr(tmp, find))) { + count++; + tmp += find_len; + } + if (!count) return src; + + result = (char*)malloc(strlen(src) + count * (rep_len + 1) + 1); + if (!result) return src; + + tmp = result; + while ((pos = strstr(src, find))) { + size_t before = (size_t)(pos - src); + memcpy(tmp, src, before); + tmp += before; + memcpy(tmp, rep, rep_len); + tmp += rep_len; + src = pos + find_len; + } + strcpy(tmp, src); + free(base); + return result; +} + +static void gdraw_ShaderSourceUpgraded(GLuint shader, GLsizei count, + const GLchar** strings, + const GLint* lengths) { + int i; + size_t total = 0; + char* src; + char* patched; + int is_vert; + const GLchar* patched_ptr; + + for (i = 0; i < count; i++) + total += lengths ? (lengths[i] >= 0 ? (size_t)lengths[i] + : strlen(strings[i])) + : strlen(strings[i]); + + src = (char*)malloc(total + 1); + if (!src) { + gdraw_real_shadersource(shader, count, strings, lengths); + return; + } + + src[0] = '\0'; + for (i = 0; i < count; i++) { + size_t len = lengths ? (lengths[i] >= 0 ? (size_t)lengths[i] + : strlen(strings[i])) + : strlen(strings[i]); + strncat(src, strings[i], len); + } + + is_vert = (gdraw_get_shader_type(shader) == GL_VERTEX_SHADER); + + // Strip any existing #version directive as i'll add our own + { + char* vp = strstr(src, "#version"); + if (vp) { + char* nl = strchr(vp, '\n'); + if (nl) + memmove(vp, nl + 1, strlen(nl + 1) + 1); + else + *vp = '\0'; + } + } + + // Texture built-ins + src = gdraw_strreplace(src, "texture2DRect", "texture"); + src = gdraw_strreplace(src, "texture2D", "texture"); + + // Attribute -> in + src = gdraw_strreplace(src, "attribute ", "in "); + src = gdraw_strreplace(src, "attribute\t", "in\t"); + src = gdraw_strreplace(src, "attribute\n", "in\n"); + + // Varying -> out (vert) / in (frag) + if (is_vert) { + src = gdraw_strreplace(src, "varying ", "out "); + src = gdraw_strreplace(src, "varying\t", "out\t"); + src = gdraw_strreplace(src, "varying\n", "out\n"); + } else { + src = gdraw_strreplace(src, "varying ", "in "); + src = gdraw_strreplace(src, "varying\t", "in\t"); + src = gdraw_strreplace(src, "varying\n", "in\n"); + } + if (!is_vert) { + src = gdraw_strreplace(src, "gl_FragData[0]", "_gdraw_frag_out"); + src = gdraw_strreplace(src, "gl_FragColor", "_gdraw_frag_out"); + } + + // finally turn it 330 + { + const char* vert_header = "#version 330 core\n"; + const char* frag_header = + "#version 330 core\nout vec4 _gdraw_frag_out;\n"; + const char* header = is_vert ? vert_header : frag_header; + patched = (char*)malloc(strlen(header) + strlen(src) + 2); + if (!patched) { + free(src); + gdraw_real_shadersource(shader, count, strings, lengths); + return; + } + strcpy(patched, header); + strcat(patched, src); + free(src); + } + + patched_ptr = (const GLchar*)patched; + gdraw_real_shadersource(shader, 1, &patched_ptr, NULL); + free(patched); +} + +#undef glShaderSource +#define glShaderSource gdraw_ShaderSourceUpgraded + + +// Remap all the deprecated internal formats to their modern equivalents +// (idk why but just the word "swizzle" is cracking me up) +static void gdraw_apply_swizzle(GLenum internal_fmt) { + if (internal_fmt == 0x1906 /* GL_ALPHA */ || internal_fmt == GL_RED) { + GLint sw[4] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, sw); + } else if (internal_fmt == 0x1909 /* GL_LUMINANCE */) { + GLint sw[4] = {GL_RED, GL_RED, GL_RED, GL_ONE}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, sw); + } else if (internal_fmt == 0x190A /* GL_LUMINANCE_ALPHA */) { + GLint sw[4] = {GL_RED, GL_RED, GL_RED, GL_GREEN}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, sw); + } +} + +static GLenum gdraw_remap_fmt(GLenum fmt) { + switch (fmt) { + case 0x1906: + return GL_RED; // GL_ALPHA + case 0x1909: + return GL_RED; // GL_LUMINANCE + case 0x190A: + return GL_RG; // GL_LUMINANCE_ALPHA + case 0x8033: + return GL_RG; // GL_LUMINANCE4_ALPHA4 + case 0x8045: + return GL_R8; // GL_LUMINANCE8 + case 0x8048: + return GL_RG8; // GL_LUMINANCE8_ALPHA8 + case 0x804F: + return GL_R8; // GL_INTENSITY4 + case 0x8050: + return GL_R8; // GL_INTENSITY8 + default: + return fmt; + } +} + +static void gdraw_TexImage2D(GLenum target, GLint level, GLint ifmt, GLsizei w, + GLsizei h, GLint border, GLenum fmt, GLenum type, + const void* data) { + GLenum new_ifmt = gdraw_remap_fmt((GLenum)ifmt); + GLenum new_fmt = gdraw_remap_fmt(fmt); + gdraw_real_teximage2d(target, level, (GLint)new_ifmt, w, h, border, new_fmt, + type, data); + if (new_ifmt != (GLenum)ifmt) gdraw_apply_swizzle((GLenum)ifmt); +} + +static void gdraw_TexSubImage2D(GLenum target, GLint level, GLint xoff, + GLint yoff, GLsizei w, GLsizei h, GLenum fmt, + GLenum type, const void* data) { + GLenum new_fmt = gdraw_remap_fmt(fmt); + gdraw_real_texsubimage2d(target, level, xoff, yoff, w, h, new_fmt, type, + data); +} + +#undef glTexImage2D +#define glTexImage2D gdraw_TexImage2D +#undef glTexSubImage2D +#define glTexSubImage2D gdraw_TexSubImage2D + +// vbo emu +static void gdraw_ClientVertexAttribPointer(GLuint index, GLint size, + GLenum type, GLboolean normalized, + GLsizei stride, + const void* pointer) { + if (gdraw_glBindVertexArray && gdraw_vao) { + GLint current_vao = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, ¤t_vao); + if ((GLuint)current_vao != gdraw_vao) + gdraw_glBindVertexArray(gdraw_vao); + } + + { + GLint current_vbo = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_vbo); + + if (current_vbo != 0 && current_vbo != (GLint)gdraw_screenvbo) { + gdraw_real_vtxattrib(index, size, type, normalized, stride, + pointer); + return; + } + } + + if (pointer == NULL) { + gdraw_real_vtxattrib(index, size, type, normalized, stride, pointer); + return; + } + + if (gdraw_screenvbo_base == NULL) { + if (!gdraw_screenvbo) glGenBuffers(1, &gdraw_screenvbo); + glBindBuffer(GL_ARRAY_BUFFER, gdraw_screenvbo); + + size_t upload_size = + gdraw_expected_vbo_size > 0 ? gdraw_expected_vbo_size : 256; + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)upload_size, pointer, + GL_STREAM_DRAW); + + gdraw_screenvbo_base = pointer; + gdraw_real_vtxattrib(index, size, type, normalized, stride, + (const void*)0); + } else { + glBindBuffer(GL_ARRAY_BUFFER, gdraw_screenvbo); + ptrdiff_t offset = + (const char*)pointer - (const char*)gdraw_screenvbo_base; + gdraw_real_vtxattrib(index, size, type, normalized, stride, + (const void*)offset); + } +} + +#undef glVertexAttribPointer +#define glVertexAttribPointer gdraw_ClientVertexAttribPointer + +// fake ibo +static void hooked_glDrawElements(GLenum mode, GLsizei count, GLenum type, + const void* indices) { + GLint current_ibo = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ¤t_ibo); + + if (current_ibo == 0 && indices != NULL) { + if (!gdraw_screenibo) glGenBuffers(1, &gdraw_screenibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gdraw_screenibo); + + size_t index_size = (type == GL_UNSIGNED_SHORT) ? 2 + : (type == GL_UNSIGNED_BYTE) ? 1 + : 4; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(count * index_size), + indices, GL_STREAM_DRAW); + + gdraw_real_drawelements(mode, count, type, (const void*)0); + } else { + gdraw_real_drawelements(mode, count, type, indices); + } +} + +#define glDrawElements hooked_glDrawElements + +// dummy shader for glUseProgram(0) safety +static void gdraw_UseProgramSafe(GLuint program) { + if (!program) { + if (!gdraw_null_program && gdraw_real_useprogram) { + const char* vs = + "#version 330 core\nvoid main(){gl_Position=vec4(0);}"; + const char* fs = + "#version 330 core\nout vec4 c;\nvoid main(){c=vec4(0);}"; + GLuint v = gdraw_real_createshader(GL_VERTEX_SHADER); + GLuint f = gdraw_real_createshader(GL_FRAGMENT_SHADER); + gdraw_real_shadersource(v, 1, &vs, NULL); + gdraw_real_shadersource(f, 1, &fs, NULL); + gdraw_real_compileshader(v); + gdraw_real_compileshader(f); + gdraw_null_program = glCreateProgram(); + glAttachShader(gdraw_null_program, v); + glAttachShader(gdraw_null_program, f); + gdraw_real_linkprogram(gdraw_null_program); + glDeleteShader(v); + glDeleteShader(f); + } + gdraw_real_useprogram(0); + return; + } + gdraw_real_useprogram(program); +} + +#undef glUseProgram +#define glUseProgram gdraw_UseProgramSafe + +#undef glCompileShader +#define glCompileShader gdraw_CompileShaderAndLog + +#undef glLinkProgram +#define glLinkProgram gdraw_LinkProgramAndLog + +// v ugh fuck you windows +#include "../../../Windows64/Iggy/gdraw/gdraw_gl_shared.inl" +#undef glVertexAttribPointer +#define glVertexAttribPointer gdraw_real_vtxattrib + +// 3.3 c extension +static int hasext_core(const char* name) { + GLint n = 0; + GLint i; + if (!gdraw_glGetStringi) return 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &n); + for (i = 0; i < n; i++) { + const char* e = + (const char*)gdraw_glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (e && strcmp(e, name) == 0) return 1; + } + return 0; +} + +static gdraw_draw_indexed_triangles* real_DrawIndexedTriangles = NULL; + +static void RADLINK hooked_DrawIndexedTriangles(GDrawRenderState* r, + GDrawPrimitive* prim, + GDrawVertexBuffer* buf, + GDrawStats* stats) { + if (buf == NULL && prim != NULL && prim->vertices != NULL) { + size_t stride = 8; + if (prim->vertex_format == GDRAW_vformat_v2aa) + stride = 16; + else if (prim->vertex_format == GDRAW_vformat_v2tc2) + stride = 16; + else if (prim->vertex_format == GDRAW_vformat_ihud1) + stride = 20; + gdraw_expected_vbo_size = prim->num_vertices * stride; + } else { + gdraw_expected_vbo_size = 0; + } + gdraw_screenvbo_base = NULL; // Force VBO re-upload for each primitive + real_DrawIndexedTriangles(r, prim, buf, stats); +} + +static gdraw_filter_quad* real_FilterQuad = NULL; + +static void RADLINK hooked_FilterQuad(GDrawRenderState* r, S32 x0, S32 y0, + S32 x1, S32 y1, GDrawStats* stats) { + gdraw_expected_vbo_size = 4 * 20; // 4 vertices, max stride + gdraw_screenvbo_base = NULL; + real_FilterQuad(r, x0, y0, x1, y1, stats); +} + +static gdraw_rendering_begin* real_RenderingBegin = NULL; + +// stupid hack +static void RADLINK hooked_RenderingBegin(void) { + if (real_RenderingBegin) real_RenderingBegin(); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); +} + +// Creating the context +GDrawFunctions* gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) { + static const TextureFormatDesc tex_formats[] = { + {IFT_FORMAT_rgba_8888, 1, 1, 4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, + {IFT_FORMAT_rgba_4444_LE, 1, 1, 2, GL_RGBA4, GL_RGBA, + GL_UNSIGNED_SHORT_4_4_4_4}, + {IFT_FORMAT_rgba_5551_LE, 1, 1, 2, GL_RGB5_A1, GL_RGBA, + GL_UNSIGNED_SHORT_5_5_5_1}, + {IFT_FORMAT_la_88, 1, 1, 2, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_la_44, 1, 1, 1, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_i_8, 1, 1, 1, GL_INTENSITY8, GL_ALPHA, GL_UNSIGNED_BYTE}, + {IFT_FORMAT_i_4, 1, 1, 1, GL_INTENSITY4, GL_ALPHA, GL_UNSIGNED_BYTE}, + {IFT_FORMAT_l_8, 1, 1, 1, GL_LUMINANCE8, GL_LUMINANCE, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_l_4, 1, 1, 1, GL_LUMINANCE4, GL_LUMINANCE, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_DXT1, 4, 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_DXT3, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_DXT5, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, + GL_UNSIGNED_BYTE}, + {0, 0, 0, 0, 0, 0, 0}, + }; + + GDrawFunctions* funcs; + GLint n; + GLint major = 0, minor = 0; + + // incase everything goes wrong and it goes like 1.1 or smh + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + if (major < 3 || (major == 3 && minor < 3)) { + fprintf(stderr, "[GDraw] GL 3.3 or higher required (got %d.%d)\n", + major, minor); + return NULL; + } + + // FBO is core since 3.0 + if (!hasext_core("GL_EXT_framebuffer_object")) { + fprintf(stderr, + "[GDraw] GL_EXT_framebuffer_object not listed, " + "fuck it, let's continue.\n"); + } + + if (!hasext_core("GL_EXT_framebuffer_multisample") && msaa_samples > 1) + return NULL; + + load_extensions(); + + if (gdraw_glBindVertexArray && gdraw_vao) + gdraw_glBindVertexArray(gdraw_vao); + + funcs = create_context(w, h); + if (!funcs) return NULL; + + // hook the vtable entries for VBO reset and render state + real_DrawIndexedTriangles = funcs->DrawIndexedTriangles; + funcs->DrawIndexedTriangles = hooked_DrawIndexedTriangles; + + real_FilterQuad = funcs->FilterQuad; + funcs->FilterQuad = hooked_FilterQuad; + + real_RenderingBegin = funcs->RenderingBegin; + funcs->RenderingBegin = hooked_RenderingBegin; + + gdraw->tex_formats = tex_formats; + gdraw->has_mapbuffer = true; + gdraw->has_depth24 = true; + gdraw->has_texture_max_level = true; + + if (hasext_core("GL_EXT_packed_depth_stencil")) + gdraw->has_packed_depth_stencil = true; + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n); + gdraw->has_conditional_non_power_of_two = (n < 8192); + + if (msaa_samples > 1) { + glGetIntegerv(GL_MAX_SAMPLES, &n); + gdraw->multisampling = RR_MIN(msaa_samples, n); + } + + opengl_check(); + + fprintf(stderr, + "[GDraw] Context created successfully (%dx%d, msaa=%d)\n", w, h, + msaa_samples); + return funcs; +} + +// Custom draw callbacks +void gdraw_GL_BeginCustomDraw_4J(IggyCustomDrawCallbackRegion* region, + F32* matrix) { + // rebind vbo + if (gdraw_glBindVertexArray && gdraw_vao) + gdraw_glBindVertexArray(gdraw_vao); + clear_renderstate(); + gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, + depth_from_id(0), 0); +} + +void gdraw_GL_CalculateCustomDraw_4J(IggyCustomDrawCallbackRegion* region, + F32* matrix) { + gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, 0.0f, 0); +} \ No newline at end of file diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.h b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.h similarity index 94% rename from Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.h rename to Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.h index 063660297..326d5c5a5 100644 --- a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.h +++ b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.h @@ -1,7 +1,9 @@ -#ifndef __RAD_INCLUDE_GDRAW_SDL_H__ -#define __RAD_INCLUDE_GDRAW_SDL_H__ +#ifndef __LINUX_IGGY_GDRAW_H__ +#define __LINUX_IGGY_GDRAW_H__ + #include "../../../Windows64/Iggy/include/gdraw.h" #include "../../../Windows64/Iggy/include/iggy.h" + #ifdef __cplusplus extern "C" { #endif @@ -34,4 +36,5 @@ extern void gdraw_GL_DestroyTextureFromResource(GDrawTexture *tex); #ifdef __cplusplus } #endif -#endif // __RAD_INCLUDE_GDRAW_SDL_H__ \ No newline at end of file + +#endif // __LINUX_IGGY_GDRAW_H__ \ No newline at end of file diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.c b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.c deleted file mode 100644 index 7e3506367..000000000 --- a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.c +++ /dev/null @@ -1,240 +0,0 @@ -// Rewrite of gdraw_GLFW to gdraw_SDL -// I hope iggy gets fully implemented rrlllly quickly <3 -#define GDRAW_ASSERTS - -#include "../../../Windows64/Iggy/include/iggy.h" -#include "../../../Windows64/Iggy/include/gdraw.h" -#include "gdraw_sdl.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define true 1 -#define false 0 - -// Say hi to sdl - -#ifndef _ENABLEIGGY -void *IggyGDrawMallocAnnotated(SINTa size, const char *file, int line) -{ - (void)file; (void)line; - return malloc((size_t)size); -} - -void IggyGDrawFree(void *ptr) -{ - free(ptr); -} - -void IggyGDrawSendWarning(Iggy *f, char const *message, ...) -{ - (void)f; - va_list args; - va_start(args, message); - fprintf(stderr, "[Iggy GDraw Warning] "); - vfprintf(stderr, message, args); - fprintf(stderr, "\n"); - va_end(args); -} - -void IggyDiscardVertexBufferCallback(void *owner, void *buf) -{ - (void)owner; (void)buf; -} -#endif - -// glActiveTexture and glCompressedTexImage2D are core GL 1.3+ on Linux and -// are declared directly in , so they are omitted from this list. -#define GDRAW_GL_EXTENSION_LIST \ -/* identifier import procname */ \ -/* GL_ARB_vertex_buffer_object */ \ -GLE(GenBuffers, "GenBuffersARB", GENBUFFERSARB) \ -GLE(DeleteBuffers, "DeleteBuffersARB", DELETEBUFFERSARB) \ -GLE(BindBuffer, "BindBufferARB", BINDBUFFERARB) \ -GLE(BufferData, "BufferDataARB", BUFFERDATAARB) \ -GLE(MapBuffer, "MapBufferARB", MAPBUFFERARB) \ -GLE(UnmapBuffer, "UnmapBufferARB", UNMAPBUFFERARB) \ -GLE(VertexAttribPointer, "VertexAttribPointerARB", VERTEXATTRIBPOINTERARB) \ -GLE(EnableVertexAttribArray, "EnableVertexAttribArrayARB", ENABLEVERTEXATTRIBARRAYARB) \ -GLE(DisableVertexAttribArray, "DisableVertexAttribArrayARB", DISABLEVERTEXATTRIBARRAYARB) \ -/* GL_ARB_shader_objects */ \ -GLE(CreateShader, "CreateShaderObjectARB", CREATESHADEROBJECTARB) \ -GLE(DeleteShader, "DeleteObjectARB", DELETEOBJECTARB) \ -GLE(ShaderSource, "ShaderSourceARB", SHADERSOURCEARB) \ -GLE(CompileShader, "CompileShaderARB", COMPILESHADERARB) \ -GLE(GetShaderiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ -GLE(GetShaderInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ -GLE(CreateProgram, "CreateProgramObjectARB", CREATEPROGRAMOBJECTARB) \ -GLE(DeleteProgram, "DeleteObjectARB", DELETEOBJECTARB) \ -GLE(AttachShader, "AttachObjectARB", ATTACHOBJECTARB) \ -GLE(LinkProgram, "LinkProgramARB", LINKPROGRAMARB) \ -GLE(GetUniformLocation, "GetUniformLocationARB", GETUNIFORMLOCATIONARB) \ -GLE(UseProgram, "UseProgramObjectARB", USEPROGRAMOBJECTARB) \ -GLE(GetProgramiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ -GLE(GetProgramInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ -GLE(Uniform1i, "Uniform1iARB", UNIFORM1IARB) \ -GLE(Uniform4f, "Uniform4fARB", UNIFORM4FARB) \ -GLE(Uniform4fv, "Uniform4fvARB", UNIFORM4FVARB) \ -/* GL_ARB_vertex_shader */ \ -GLE(BindAttribLocation, "BindAttribLocationARB", BINDATTRIBLOCATIONARB) \ -/* Missing from WGL but needed by shared code */ \ -GLE(Uniform1f, "Uniform1fARB", UNIFORM1FARB) \ -/* GL_EXT_framebuffer_object */ \ -GLE(GenRenderbuffers, "GenRenderbuffersEXT", GENRENDERBUFFERSEXT) \ -GLE(DeleteRenderbuffers, "DeleteRenderbuffersEXT", DELETERENDERBUFFERSEXT) \ -GLE(BindRenderbuffer, "BindRenderbufferEXT", BINDRENDERBUFFEREXT) \ -GLE(RenderbufferStorage, "RenderbufferStorageEXT", RENDERBUFFERSTORAGEEXT) \ -GLE(GenFramebuffers, "GenFramebuffersEXT", GENFRAMEBUFFERSEXT) \ -GLE(DeleteFramebuffers, "DeleteFramebuffersEXT", DELETEFRAMEBUFFERSEXT) \ -GLE(BindFramebuffer, "BindFramebufferEXT", BINDFRAMEBUFFEREXT) \ -GLE(CheckFramebufferStatus, "CheckFramebufferStatusEXT", CHECKFRAMEBUFFERSTATUSEXT) \ -GLE(FramebufferRenderbuffer, "FramebufferRenderbufferEXT", FRAMEBUFFERRENDERBUFFEREXT) \ -GLE(FramebufferTexture2D, "FramebufferTexture2DEXT", FRAMEBUFFERTEXTURE2DEXT) \ -GLE(GenerateMipmap, "GenerateMipmapEXT", GENERATEMIPMAPEXT) \ -/* GL_EXT_framebuffer_blit */ \ -GLE(BlitFramebuffer, "BlitFramebufferEXT", BLITFRAMEBUFFEREXT) \ -/* GL_EXT_framebuffer_multisample */ \ -GLE(RenderbufferStorageMultisample, "RenderbufferStorageMultisampleEXT",RENDERBUFFERSTORAGEMULTISAMPLEEXT) \ -/* */ - -#define gdraw_GLx_(id) gdraw_GL_##id -#define GDRAW_GLx_(id) GDRAW_GL_##id -#define GDRAW_SHADERS "gdraw_gl_shaders.inl" - -// On Linux, GLhandleARB is void* but shader functions use GLuint values. -// Use GLuint as our handle type, matching the Mac pattern from gdraw_gl_shared.inl. -#define GDrawGLProgram GLuint -typedef GLuint GLhandle; -typedef gdraw_gl_resourcetype gdraw_resourcetype; - -// Declare extension function pointers -#define GLE(id, import, procname) static PFNGL##procname##PROC gl##id; -GDRAW_GL_EXTENSION_LIST -#undef GLE - -static void load_extensions(void) -{ - #define GLE(id, import, procname) \ - gl##id = (PFNGL##procname##PROC) SDL_GL_GetProcAddress("gl" import); - GDRAW_GL_EXTENSION_LIST - #undef GLE -} - -static void clear_renderstate_platform_specific(void) -{ - glDisable(GL_ALPHA_TEST); -} - -static void error_msg_platform_specific(const char *msg) -{ - fprintf(stderr, "[GDraw SDL] %s\n", msg); -} - -#define GDRAW_MULTISAMPLING - -// Suppress SIGTRAP from GL debug assertions on Linux -#ifdef RR_BREAK -#undef RR_BREAK -#endif -#define RR_BREAK() \ - do { fprintf(stderr, "[GDraw] RR_BREAK suppressed (GL error)\n"); } while(0) - -#include "../../../Windows64/Iggy/gdraw/gdraw_gl_shared.inl" - -// Context creation and management - -GDrawFunctions *gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) -{ - static const TextureFormatDesc tex_formats[] = { - { IFT_FORMAT_rgba_8888, 1, 1, 4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_rgba_4444_LE, 1, 1, 2, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 }, - { IFT_FORMAT_rgba_5551_LE, 1, 1, 2, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 }, - { IFT_FORMAT_la_88, 1, 1, 2, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_la_44, 1, 1, 1, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_i_8, 1, 1, 1, GL_INTENSITY8, GL_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_i_4, 1, 1, 1, GL_INTENSITY4, GL_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_l_8, 1, 1, 1, GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_l_4, 1, 1, 1, GL_LUMINANCE4, GL_LUMINANCE, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_DXT1, 4, 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_DXT3, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_DXT5, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_UNSIGNED_BYTE }, - { 0, 0, 0, 0, 0, 0, 0 }, - }; - - GDrawFunctions *funcs; - const char *s; - GLint n; - - // A current SDL2 GL context must be active before calling this, if it doesn't exist, just throw an warning- - s = (const char *) glGetString(GL_EXTENSIONS); - if (!s) { - fprintf(stderr, "[GDraw SDL] glGetString(GL_EXTENSIONS) returned NULL - " - "SDL GL context not current?\n"); - assert(s != NULL); - return NULL; - } - - // Verify required extensions - if (!hasext(s, "GL_ARB_multitexture") || - !hasext(s, "GL_ARB_texture_compression") || - !hasext(s, "GL_ARB_texture_mirrored_repeat") || - !hasext(s, "GL_ARB_texture_non_power_of_two") || - !hasext(s, "GL_ARB_vertex_buffer_object") || - !hasext(s, "GL_EXT_framebuffer_object") || - !hasext(s, "GL_ARB_shader_objects") || - !hasext(s, "GL_ARB_vertex_shader") || - !hasext(s, "GL_ARB_fragment_shader")) - { - fprintf(stderr, "[GDraw SDL] Required GL extensions not available\n"); - return NULL; - } - - if (!hasext(s, "GL_EXT_framebuffer_multisample") && msaa_samples > 1) - return NULL; - - load_extensions(); - funcs = create_context(w, h); - if (!funcs) - return NULL; - - gdraw->tex_formats = tex_formats; - - gdraw->has_mapbuffer = true; // core in ARB_vertex_buffer_object - gdraw->has_depth24 = true; - gdraw->has_texture_max_level = true; // core GL - - if (hasext(s, "GL_EXT_packed_depth_stencil")) - gdraw->has_packed_depth_stencil = true; - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n); - gdraw->has_conditional_non_power_of_two = (n < 8192); - - if (msaa_samples > 1) { - glGetIntegerv(GL_MAX_SAMPLES, &n); - gdraw->multisampling = RR_MIN(msaa_samples, n); - } - - opengl_check(); - - fprintf(stderr, "[GDraw SDL] Context created successfully (%dx%d, msaa=%d)\n", - w, h, msaa_samples); - return funcs; -} - -void gdraw_GL_BeginCustomDraw_4J(IggyCustomDrawCallbackRegion *region, F32 *matrix) -{ - clear_renderstate(); - gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, depth_from_id(0), 0); -} - -void gdraw_GL_CalculateCustomDraw_4J(IggyCustomDrawCallbackRegion *region, F32 *matrix) -{ - gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, 0.0f, 0); -} diff --git a/Minecraft.Client/Platform/Linux/LinuxGL.cpp b/Minecraft.Client/Platform/Linux/LinuxGL.cpp index 351bb5d24..1333b5114 100644 --- a/Minecraft.Client/Platform/Linux/LinuxGL.cpp +++ b/Minecraft.Client/Platform/Linux/LinuxGL.cpp @@ -1,7 +1,6 @@ #ifdef __linux__ #include "../stdafx.h" - #include #include #include @@ -11,125 +10,138 @@ #include "../../Minecraft.World/IO/Streams/FloatBuffer.h" #include "../../Minecraft.World/IO/Streams/ByteBuffer.h" -void LinuxGLLogLightmapState(const char* stage, int textureId, bool scaleLight) { - static int logCount = 0; - if (logCount >= 16) return; +#undef glGenTextures +#undef glDeleteTextures +#undef glLight +#undef glLightModel +#undef glGetFloat +#undef glTexGen +#undef glFog +#undef glTexCoordPointer +#undef glNormalPointer +#undef glColorPointer +#undef glVertexPointer +#undef glEndList +#undef glTexImage2D +#undef glCallLists +#undef glReadPixels +#undef glActiveTexture - ++logCount; - - static bool loggedSymbols = false; - if (!loggedSymbols) { - loggedSymbols = true; - app.DebugPrintf( - "[linux-lightmap] linuxgl symbols glActiveTexture=%p " - "glClientActiveTexture=%p glMultiTexCoord2f=%p\n", - reinterpret_cast(::glActiveTexture), - reinterpret_cast(::glClientActiveTexture), - reinterpret_cast(::glMultiTexCoord2f)); - } - - GLint activeTexture = 0; - GLint matrixMode = 0; - ::glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture); - ::glGetIntegerv(GL_MATRIX_MODE, &matrixMode); - - const GLint restoreTexture = activeTexture; - ::glActiveTexture(GL_TEXTURE1); - - GLint unit1Binding = 0; - ::glGetIntegerv(GL_TEXTURE_BINDING_2D, &unit1Binding); - const bool unit1Enabled = (::glIsEnabled(GL_TEXTURE_2D) == GL_TRUE); - - GLfloat textureMatrix[16]; - ::glGetFloatv(GL_TEXTURE_MATRIX, textureMatrix); - - ::glActiveTexture(restoreTexture); - - app.DebugPrintf( - "[linux-lightmap] %s tex=%d scale=%d active=%#x matrixMode=%#x " - "unit1Bound=%d unit1Enabled=%d texMatrix=[%.4f %.4f %.4f %.4f]\n", - stage, textureId, scaleLight ? 1 : 0, activeTexture, matrixMode, - unit1Binding, unit1Enabled ? 1 : 0, textureMatrix[0], textureMatrix[5], - textureMatrix[12], textureMatrix[13]); -} - -int glGenTextures() { - GLuint id = 0; - ::glGenTextures(1, &id); - return (int)id; -} - -void glGenTextures(IntBuffer* buf) { +// _4j suffix shit (todo: make ts better) +void glGenTextures_4J(IntBuffer* buf) { GLuint id = 0; ::glGenTextures(1, &id); buf->put((int)id); buf->flip(); } -void glDeleteTextures(int id) { - GLuint uid = (GLuint)id; - ::glDeleteTextures(1, &uid); +void glDeleteTextures_4J(IntBuffer* buf) { + if (buf && buf->limit() > 0) { + int id = buf->get(0); + GLuint uid = (GLuint)id; + ::glDeleteTextures(1, &uid); + } } -void glDeleteTextures(IntBuffer* buf) { - int id = buf->get(0); - GLuint uid = (GLuint)id; - ::glDeleteTextures(1, &uid); -} - -void glLight(int light, int pname, FloatBuffer* params) { +void glLight_4J(int light, int pname, FloatBuffer* params) { ::glLightfv((GLenum)light, (GLenum)pname, params->_getDataPointer()); } -void glLightModel(int pname, FloatBuffer* params) { +void glLightModel_4J(int pname, FloatBuffer* params) { ::glLightModelfv((GLenum)pname, params->_getDataPointer()); } -void glGetFloat(int pname, FloatBuffer* params) { +void glGetFloat_4J(int pname, FloatBuffer* params) { ::glGetFloatv((GLenum)pname, params->_getDataPointer()); } -void glTexGen(int coord, int pname, FloatBuffer* params) { +void glTexGen_4J(int coord, int pname, FloatBuffer* params) { ::glTexGenfv((GLenum)coord, (GLenum)pname, params->_getDataPointer()); } -void glFog(int pname, FloatBuffer* params) { +void glFog_4J(int pname, FloatBuffer* params) { ::glFogfv((GLenum)pname, params->_getDataPointer()); } -void glTexCoordPointer(int size, int type, FloatBuffer* pointer) { +void glTexCoordPointer_4J(int size, int type, FloatBuffer* pointer) { ::glTexCoordPointer(size, (GLenum)type, 0, pointer->_getDataPointer()); } -void glNormalPointer(int type, ByteBuffer* pointer) { +void glNormalPointer_4J(int type, ByteBuffer* pointer) { ::glNormalPointer((GLenum)type, 0, pointer->getBuffer()); } -void glColorPointer(int size, bool normalized, int stride, - ByteBuffer* pointer) { +void glColorPointer_4J(int size, bool normalized, int stride, + ByteBuffer* pointer) { (void)normalized; ::glColorPointer(size, GL_UNSIGNED_BYTE, stride, pointer->getBuffer()); } -void glVertexPointer(int size, int type, FloatBuffer* pointer) { +void glVertexPointer_4J(int size, int type, FloatBuffer* pointer) { ::glVertexPointer(size, (GLenum)type, 0, pointer->_getDataPointer()); } -void glEndList(int) { ::glEndList(); } +void glEndList_4J(int dummy) { + (void)dummy; + ::glEndList(); +} -void glTexImage2D(int target, int level, int internalformat, int width, - int height, int border, int format, int type, - ByteBuffer* pixels) { +void glTexImage2D_4J(int target, int level, int internalformat, int width, + int height, int border, int format, int type, + ByteBuffer* pixels) { void* data = pixels ? pixels->getBuffer() : nullptr; ::glTexImage2D((GLenum)target, level, internalformat, width, height, border, (GLenum)format, (GLenum)type, data); } -void glCallLists(IntBuffer* lists) { +void glCallLists_4J(IntBuffer* lists) { int count = lists->limit() - lists->position(); ::glCallLists(count, GL_INT, lists->getBuffer()); } +void glReadPixels_4J(int x, int y, int width, int height, int format, int type, + ByteBuffer* pixels) { + ::glReadPixels(x, y, width, height, (GLenum)format, (GLenum)type, + pixels->getBuffer()); +} + +void glGetFloat(int pname, FloatBuffer* params) { + glGetFloat_4J(pname, params); +} +void glGenTextures(IntBuffer* buf) { glGenTextures_4J(buf); } +void glDeleteTextures(IntBuffer* buf) { glDeleteTextures_4J(buf); } +void glLight(int light, int pname, FloatBuffer* params) { + glLight_4J(light, pname, params); +} +void glLightModel(int pname, FloatBuffer* params) { + glLightModel_4J(pname, params); +} +void glTexGen(int coord, int pname, FloatBuffer* params) { + glTexGen_4J(coord, pname, params); +} +void glFog(int pname, FloatBuffer* params) { glFog_4J(pname, params); } +void glTexCoordPointer(int size, int type, FloatBuffer* pointer) { + glTexCoordPointer_4J(size, type, pointer); +} +void glNormalPointer(int type, ByteBuffer* pointer) { + glNormalPointer_4J(type, pointer); +} +void glColorPointer(int size, bool normalized, int stride, + ByteBuffer* pointer) { + glColorPointer_4J(size, normalized, stride, pointer); +} +void glVertexPointer(int size, int type, FloatBuffer* pointer) { + glVertexPointer_4J(size, type, pointer); +} +void glTexImage2D(int t, int l, int i, int w, int h, int b, int f, int ty, + ByteBuffer* p) { + glTexImage2D_4J(t, l, i, w, h, b, f, ty, p); +} +void glCallLists(IntBuffer* lists) { glCallLists_4J(lists); } +void glReadPixels(int x, int y, int w, int h, int f, int t, ByteBuffer* p) { + glReadPixels_4J(x, y, w, h, f, t, p); +} + static PFNGLGENQUERIESARBPROC _glGenQueriesARB = nullptr; static PFNGLBEGINQUERYARBPROC _glBeginQueryARB = nullptr; static PFNGLENDQUERYARBPROC _glEndQueryARB = nullptr; @@ -148,7 +160,7 @@ static void initQueryFuncs() { RTLD_DEFAULT, "glGetQueryObjectuivARB"); } -void glGenQueriesARB(IntBuffer* buf) { +void glGenQueriesARB_4J(IntBuffer* buf) { initQueryFuncs(); if (_glGenQueriesARB) { GLuint id = 0; @@ -157,18 +169,21 @@ void glGenQueriesARB(IntBuffer* buf) { buf->flip(); } } +void glGenQueriesARB(IntBuffer* buf) { glGenQueriesARB_4J(buf); } -void glBeginQueryARB(int target, int id) { +void glBeginQueryARB_4J(int target, int id) { initQueryFuncs(); if (_glBeginQueryARB) _glBeginQueryARB((GLenum)target, (GLuint)id); } +void glBeginQueryARB(int target, int id) { glBeginQueryARB_4J(target, id); } -void glEndQueryARB(int target) { +void glEndQueryARB_4J(int target) { initQueryFuncs(); if (_glEndQueryARB) _glEndQueryARB((GLenum)target); } +void glEndQueryARB(int target) { glEndQueryARB_4J(target); } -void glGetQueryObjectuARB(int id, int pname, IntBuffer* params) { +void glGetQueryObjectuARB_4J(int id, int pname, IntBuffer* params) { initQueryFuncs(); if (_glGetQueryObjectuivARB) { GLuint val = 0; @@ -177,11 +192,30 @@ void glGetQueryObjectuARB(int id, int pname, IntBuffer* params) { params->flip(); } } - -void glReadPixels(int x, int y, int width, int height, int format, int type, - ByteBuffer* pixels) { - ::glReadPixels(x, y, width, height, (GLenum)format, (GLenum)type, - pixels->getBuffer()); +void glGetQueryObjectuARB(int id, int pname, IntBuffer* params) { + glGetQueryObjectuARB_4J(id, pname, params); } -#endif +void LinuxGLLogLightmapState(const char* stage, int textureId, + bool scaleLight) { + static int logCount = 0; + if (logCount >= 16) return; + ++logCount; + + GLint activeTexture = 0; + ::glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture); + const GLint restoreTexture = activeTexture; + ::glActiveTexture(GL_TEXTURE1); + GLint unit1Binding = 0; + ::glGetIntegerv(GL_TEXTURE_BINDING_2D, &unit1Binding); + const bool unit1Enabled = (::glIsEnabled(GL_TEXTURE_2D) == GL_TRUE); + ::glActiveTexture(restoreTexture); + + app.DebugPrintf( + "[linux-lightmap] %s tex=%d scale=%d active=%#x unit1Bound=%d " + "unit1Enabled=%d\n", + stage, textureId, scaleLight ? 1 : 0, activeTexture, unit1Binding, + unit1Enabled ? 1 : 0); +} + +#endif \ No newline at end of file diff --git a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp index 4a39e85ef..445fd5d9d 100644 --- a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp @@ -1,3 +1,4 @@ +// Linux_UIController.cpp #include "../../../Minecraft.World/Platform/stdafx.h" #include "Linux_UIController.h" @@ -6,34 +7,34 @@ #include "../../Textures/Textures.h" // GDraw GL backend for Linux -#include "Iggy/gdraw/gdraw_sdl.h" +#include "Iggy/gdraw/gdraw.h" +#include "4J_Render.h" ConsoleUIController ui; static void restoreFixedFunctionStateAfterIggy() { - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.1f); - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); + RenderManager.StateSetColour(1.0f, 1.0f, 1.0f, 1.0f); + RenderManager.StateSetAlphaTestEnable(true); + RenderManager.StateSetAlphaFunc(GL_GREATER, 0.1f); - glClientActiveTexture(GL_TEXTURE1); - glActiveTexture(GL_TEXTURE1); - glDisable(GL_TEXTURE_2D); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); + RenderManager.StateSetDepthTestEnable(true); + RenderManager.StateSetDepthFunc(GL_LEQUAL); + RenderManager.StateSetDepthMask(true); - glClientActiveTexture(GL_TEXTURE0); - glActiveTexture(GL_TEXTURE0); - glEnable(GL_TEXTURE_2D); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); + RenderManager.StateSetFaceCull(true); + RenderManager.StateSetActiveTexture(GL_TEXTURE1); + RenderManager.StateSetTextureEnable(false); + RenderManager.MatrixMode(GL_TEXTURE); + RenderManager.MatrixSetIdentity(); - glMatrixMode(GL_MODELVIEW); + RenderManager.StateSetActiveTexture(GL_TEXTURE0); + RenderManager.StateSetTextureEnable(true); + RenderManager.MatrixMode(GL_TEXTURE); + + RenderManager.MatrixSetIdentity(); + RenderManager.MatrixMode(GL_MODELVIEW); + + RenderManager.Set_matrixDirty(); } void ConsoleUIController::init(S32 w, S32 h) { @@ -66,8 +67,11 @@ void ConsoleUIController::render() { if (!gdraw_funcs) return; gdraw_GL_SetTileOrigin(0, 0, 0); - if (!app.GetGameStarted() && gdraw_funcs->ClearID) { - gdraw_funcs->ClearID(); + if (!app.GetGameStarted()) { + glDisable(GL_SCISSOR_TEST); + glClearDepth(1.0); + glDepthMask(GL_TRUE); + glClear(GL_DEPTH_BUFFER_BIT); } // render @@ -138,4 +142,4 @@ void ConsoleUIController::shutdown() { gdraw_funcs = nullptr; } #endif -} +} \ No newline at end of file diff --git a/Minecraft.Client/Platform/stdafx.h b/Minecraft.Client/Platform/stdafx.h index aa113c2c0..e305669a7 100644 --- a/Minecraft.Client/Platform/stdafx.h +++ b/Minecraft.Client/Platform/stdafx.h @@ -5,8 +5,8 @@ #pragma once -//#include -//#include +// #include +// #include #define __STR2__(x) #x #define __STR1__(x) __STR2__(x) @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include #include "../Platform/PSVita/PSVitaExtras/PSVitaTypes.h" #include "../Platform/PSVita/PSVitaExtras/PSVitaStubs.h" @@ -71,8 +71,8 @@ typedef unsigned __int64 __uint64; #endif -#ifdef _WINDOWS64 -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#ifdef _WINDOWS64 +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include #include @@ -80,14 +80,12 @@ typedef unsigned __int64 __uint64; // TODO: reference additional headers your program requires here #include #include -using namespace DirectX; +using namespace DirectX; #define HRESULT_SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) #endif - - #ifdef _DURANGO #include #include @@ -95,14 +93,12 @@ using namespace DirectX; #include #include #include -using namespace DirectX; +using namespace DirectX; #include #include "../Platform/Durango/DurangoExtras/DurangoStubs.h" #define HRESULT_SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) #endif - - #ifdef _XBOX #include #include @@ -166,42 +162,42 @@ typedef XUID GameSessionUID; #include "../../Minecraft.World/Util/PerformanceTimer.h" #ifdef _XBOX - #include "xbox/4JLibs/inc/4J_Input.h" - #include "xbox/4JLibs/inc/4J_Profile.h" - #include "xbox/4JLibs/inc/4J_Render.h" - #include "xbox/4JLibs/inc/4J_XTMS.h" - #include "xbox/4JLibs/inc/4J_Storage.h" -#elif defined (__PS3__) +#include "xbox/4JLibs/inc/4J_Input.h" +#include "xbox/4JLibs/inc/4J_Profile.h" +#include "xbox/4JLibs/inc/4J_Render.h" +#include "xbox/4JLibs/inc/4J_XTMS.h" +#include "xbox/4JLibs/inc/4J_Storage.h" +#elif defined(__PS3__) - #include "../Platform/PS3/4JLibs/inc/4J_Input.h" - #include "../Platform/PS3/4JLibs/inc/4J_Profile.h" - #include "../Platform/PS3/4JLibs/inc/4J_Render.h" - #include "../Platform/PS3/4JLibs/inc/4J_Storage.h" +#include "../Platform/PS3/4JLibs/inc/4J_Input.h" +#include "../Platform/PS3/4JLibs/inc/4J_Profile.h" +#include "../Platform/PS3/4JLibs/inc/4J_Render.h" +#include "../Platform/PS3/4JLibs/inc/4J_Storage.h" #elif defined _DURANGO - #include "../Platform/Durango/4JLibs/inc/4J_Input.h" - #include "../Platform/Durango/4JLibs/inc/4J_Profile.h" - #include "../Platform/Durango/4JLibs/inc/4J_Render.h" - #include "../Platform/Durango/4JLibs/inc/4J_Storage.h" +#include "../Platform/Durango/4JLibs/inc/4J_Input.h" +#include "../Platform/Durango/4JLibs/inc/4J_Profile.h" +#include "../Platform/Durango/4JLibs/inc/4J_Render.h" +#include "../Platform/Durango/4JLibs/inc/4J_Storage.h" #elif defined _WINDOWS64 - #include "../Platform/Windows64/4JLibs/inc/4J_Input.h" - #include "../Platform/Windows64/4JLibs/inc/4J_Profile.h" - #include "../Platform/Windows64/4JLibs/inc/4J_Render.h" - #include "../Platform/Windows64/4JLibs/inc/4J_Storage.h" +#include "../Platform/Windows64/4JLibs/inc/4J_Input.h" +#include "../Platform/Windows64/4JLibs/inc/4J_Profile.h" +#include "../Platform/Windows64/4JLibs/inc/4J_Render.h" +#include "../Platform/Windows64/4JLibs/inc/4J_Storage.h" #elif defined __linux__ - #include "4J_Input.h" - #include "4J_Profile.h" - #include "4J_Render.h" - #include "4J_Storage.h" +#include "4J_Input.h" +#include "4J_Profile.h" +#include "4J_Render.h" +#include "4J_Storage.h" #elif defined __PSVITA__ - #include "../Platform/PSVita/4JLibs/inc/4J_Input.h" - #include "../Platform/PSVita/4JLibs/inc/4J_Profile.h" - #include "../Platform/PSVita/4JLibs/inc/4J_Render.h" - #include "../Platform/PSVita/4JLibs/inc/4J_Storage.h" +#include "../Platform/PSVita/4JLibs/inc/4J_Input.h" +#include "../Platform/PSVita/4JLibs/inc/4J_Profile.h" +#include "../Platform/PSVita/4JLibs/inc/4J_Render.h" +#include "../Platform/PSVita/4JLibs/inc/4J_Storage.h" #else - #include "../Platform/Orbis/4JLibs/inc/4J_Input.h" - #include "../Platform/Orbis/4JLibs/inc/4J_Profile.h" - #include "../Platform/Orbis/4JLibs/inc/4J_Render.h" - #include "../Platform/Orbis/4JLibs/inc/4J_Storage.h" +#include "../Platform/Orbis/4JLibs/inc/4J_Input.h" +#include "../Platform/Orbis/4JLibs/inc/4J_Profile.h" +#include "../Platform/Orbis/4JLibs/inc/4J_Render.h" +#include "../Platform/Orbis/4JLibs/inc/4J_Storage.h" #endif #include "../Textures/Textures.h" @@ -229,7 +225,7 @@ typedef XUID GameSessionUID; #include "../Platform/Common/App_enums.h" #include "../Platform/Common/Tutorial/TutorialEnum.h" #include "../Platform/Common/App_structs.h" -//#endif +// #endif #include "../Platform/Common/Consoles_App.h" #include "../Platform/Common/Minecraft_Macros.h" @@ -242,95 +238,94 @@ typedef XUID GameSessionUID; #include "strings.h" #ifdef _XBOX - #include "../Platform/Xbox/Xbox_App.h" - #include "../Platform/Xbox/Sentient/MinecraftTelemetry.h" - #include "../Platform/Xbox/Sentient/DynamicConfigurations.h" - #include "../Platform/Xbox/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Xbox/Sentient/Include/SenClientStats.h" - #include "../Platform/Xbox/GameConfig/Minecraft.spa.h" - #include "../../Minecraft.Assets/XboxMedia/4J_strings.h" - #include "../Platform/Xbox/XML/ATGXmlParser.h" - #include "../Platform/Xbox/Leaderboards/XboxLeaderboardManager.h" - #include "../Platform/Xbox/Social/SocialManager.h" - #include "../Platform/Xbox/Audio/SoundEngine.h" - #include "../Platform/Xbox/Xbox_UIController.h" +#include "../Platform/Xbox/Xbox_App.h" +#include "../Platform/Xbox/Sentient/MinecraftTelemetry.h" +#include "../Platform/Xbox/Sentient/DynamicConfigurations.h" +#include "../Platform/Xbox/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Xbox/Sentient/Include/SenClientStats.h" +#include "../Platform/Xbox/GameConfig/Minecraft.spa.h" +#include "../../Minecraft.Assets/XboxMedia/4J_strings.h" +#include "../Platform/Xbox/XML/ATGXmlParser.h" +#include "../Platform/Xbox/Leaderboards/XboxLeaderboardManager.h" +#include "../Platform/Xbox/Social/SocialManager.h" +#include "../Platform/Xbox/Audio/SoundEngine.h" +#include "../Platform/Xbox/Xbox_UIController.h" -#elif defined (__PS3__) - #include "extraX64client.h" - #include "../Platform/PS3/Sentient/MinecraftTelemetry.h" - #include "../Platform/PS3/Sentient/DynamicConfigurations.h" - #include "../Platform/PS3/Sentient/SentientTelemetryCommon.h" - #include "../Platform/PS3/PS3_App.h" - #include "../Platform/PS3/GameConfig/Minecraft.spa.h" - #include "../../Minecraft.Assets/PS3Media/4J_strings.h" - #include "../Platform/PS3/XML/ATGXmlParser.h" - #include "../Platform/PS3/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/PS3/Iggy/include/iggy.h" - #include "../Platform/PS3/Iggy/gdraw/gdraw_ps3gcm.h" - #include "../Platform/PS3/PS3_UIController.h" +#elif defined(__PS3__) +#include "extraX64client.h" +#include "../Platform/PS3/Sentient/MinecraftTelemetry.h" +#include "../Platform/PS3/Sentient/DynamicConfigurations.h" +#include "../Platform/PS3/Sentient/SentientTelemetryCommon.h" +#include "../Platform/PS3/PS3_App.h" +#include "../Platform/PS3/GameConfig/Minecraft.spa.h" +#include "../../Minecraft.Assets/PS3Media/4J_strings.h" +#include "../Platform/PS3/XML/ATGXmlParser.h" +#include "../Platform/PS3/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/PS3/Iggy/include/iggy.h" +#include "../Platform/PS3/Iggy/gdraw/gdraw_ps3gcm.h" +#include "../Platform/PS3/PS3_UIController.h" #elif defined _DURANGO - #include "../Platform/Durango/Sentient/MinecraftTelemetry.h" - #include "../Platform/Durango/Durango_App.h" - #include "../Platform/Durango/Sentient/DynamicConfigurations.h" - #include "../Platform/Durango/Sentient/TelemetryEnum.h" - #include "../Platform/Durango/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Durango/PresenceIds.h" - #include "../../Minecraft.Assets/DurangoMedia/4J_strings.h" - #include "../Platform/Durango/XML/ATGXmlParser.h" - #include "../Platform/Durango/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/Durango/Iggy/include/iggy.h" - #include "../Platform/Durango/Iggy/gdraw/gdraw_d3d11.h" - #include "../Platform/Durango/Durango_UIController.h" +#include "../Platform/Durango/Sentient/MinecraftTelemetry.h" +#include "../Platform/Durango/Durango_App.h" +#include "../Platform/Durango/Sentient/DynamicConfigurations.h" +#include "../Platform/Durango/Sentient/TelemetryEnum.h" +#include "../Platform/Durango/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Durango/PresenceIds.h" +#include "../../Minecraft.Assets/DurangoMedia/4J_strings.h" +#include "../Platform/Durango/XML/ATGXmlParser.h" +#include "../Platform/Durango/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/Durango/Iggy/include/iggy.h" +#include "../Platform/Durango/Iggy/gdraw/gdraw_d3d11.h" +#include "../Platform/Durango/Durango_UIController.h" #elif defined _WINDOWS64 - #include "../Platform/Windows64/Sentient/MinecraftTelemetry.h" - #include "../Platform/Windows64/Windows64_App.h" - #include "../Platform/Windows64/Sentient/DynamicConfigurations.h" - #include "../Platform/Windows64/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Windows64/GameConfig/Minecraft.spa.h" - #include "../Platform/Windows64/XML/ATGXmlParser.h" - #include "../Platform/Windows64/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/Windows64/Iggy/include/iggy.h" - #include "../Platform/Windows64/Iggy/gdraw/gdraw_d3d11.h" - #include "../Platform/Windows64/Windows64_UIController.h" +#include "../Platform/Windows64/Sentient/MinecraftTelemetry.h" +#include "../Platform/Windows64/Windows64_App.h" +#include "../Platform/Windows64/Sentient/DynamicConfigurations.h" +#include "../Platform/Windows64/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Windows64/GameConfig/Minecraft.spa.h" +#include "../Platform/Windows64/XML/ATGXmlParser.h" +#include "../Platform/Windows64/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/Windows64/Iggy/include/iggy.h" +#include "../Platform/Windows64/Iggy/gdraw/gdraw_d3d11.h" +#include "../Platform/Windows64/Windows64_UIController.h" #elif defined __linux__ - // Linux build: avoid pulling in Windows64 platform headers (they cause - // symbol/class redefinitions). Use Orbis-compatible stubs and Linux controller. - #include "../Platform/Linux/Linux_App.h" - #include "../Platform/Linux/Iggy/include/iggy.h" - #include "../Platform/Linux/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Linux/Sentient/DynamicConfigurations.h" - #include "../Platform/Orbis/GameConfig/Minecraft.spa.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/Linux/Linux_UIController.h" - #include "../Platform/Linux/Social/SocialManager.h" +// todo: cleanup ts +#include "../Platform/Linux/Linux_App.h" +#include "../Platform/Linux/Iggy/include/iggy.h" +#include "../Platform/Linux/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Linux/Sentient/DynamicConfigurations.h" +#include "../Platform/Orbis/GameConfig/Minecraft.spa.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/Linux/Linux_UIController.h" +#include "../Platform/Linux/Social/SocialManager.h" #elif defined __PSVITA__ - #include "../Platform/PSVita/PSVita_App.h" - #include "../Platform/PSVita/Sentient/SentientManager.h" - #include "../Platform/PSVita/Sentient/MinecraftTelemetry.h" - #include "../Platform/PSVita/Sentient/DynamicConfigurations.h" - #include "../Platform/PSVita/GameConfig/Minecraft.spa.h" - #include "../Platform/PSVita/XML/ATGXmlParser.h" - #include "../Platform/PSVita/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/PSVita/Iggy/include/iggy.h" - #include "../Platform/PSVita/Iggy/gdraw/gdraw_psp2.h" - #include "../Platform/PSVita/PSVita_UIController.h" +#include "../Platform/PSVita/PSVita_App.h" +#include "../Platform/PSVita/Sentient/SentientManager.h" +#include "../Platform/PSVita/Sentient/MinecraftTelemetry.h" +#include "../Platform/PSVita/Sentient/DynamicConfigurations.h" +#include "../Platform/PSVita/GameConfig/Minecraft.spa.h" +#include "../Platform/PSVita/XML/ATGXmlParser.h" +#include "../Platform/PSVita/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/PSVita/Iggy/include/iggy.h" +#include "../Platform/PSVita/Iggy/gdraw/gdraw_psp2.h" +#include "../Platform/PSVita/PSVita_UIController.h" #else - #include "../Platform/Orbis/Sentient/MinecraftTelemetry.h" - #include "../Platform/Orbis/Orbis_App.h" - #include "../Platform/Orbis/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Orbis/Sentient/DynamicConfigurations.h" - #include "../Platform/Orbis/GameConfig/Minecraft.spa.h" - #include "../../Minecraft.Assets/OrbisMedia/4J_strings.h" - #include "../Platform/Orbis/XML/ATGXmlParser.h" - #include "../Platform/Windows64/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/Orbis/Iggy/include/iggy.h" - #include "../Platform/Orbis/Iggy/gdraw/gdraw_orbis.h" - #include "../Platform/Orbis/Orbis_UIController.h" +#include "../Platform/Orbis/Sentient/MinecraftTelemetry.h" +#include "../Platform/Orbis/Orbis_App.h" +#include "../Platform/Orbis/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Orbis/Sentient/DynamicConfigurations.h" +#include "../Platform/Orbis/GameConfig/Minecraft.spa.h" +#include "../../Minecraft.Assets/OrbisMedia/4J_strings.h" +#include "../Platform/Orbis/XML/ATGXmlParser.h" +#include "../Platform/Windows64/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/Orbis/Iggy/include/iggy.h" +#include "../Platform/Orbis/Iggy/gdraw/gdraw_orbis.h" +#include "../Platform/Orbis/Orbis_UIController.h" #endif #ifdef _XBOX @@ -358,13 +353,11 @@ typedef XUID GameSessionUID; #include "../Platform/Common/Telemetry/TelemetryManager.h" #ifdef _XBOX -//#include "../Platform/Xbox/Xbox_App.h" +// #include "../Platform/Xbox/Xbox_App.h" #elif !defined(__PS3__) #include "extraX64client.h" #endif - - #ifdef _FINAL_BUILD #define printf BREAKTHECOMPILE #define wprintf BREAKTHECOMPILE diff --git a/Minecraft.Client/Platform/stubs.h b/Minecraft.Client/Platform/stubs.h index 91464c6a8..9841637ca 100644 --- a/Minecraft.Client/Platform/stubs.h +++ b/Minecraft.Client/Platform/stubs.h @@ -13,30 +13,32 @@ class FloatBuffer; class IntBuffer; class ByteBuffer; -void glGenTextures(IntBuffer *); +void glGenTextures(IntBuffer*); int glGenTextures(); -void glDeleteTextures(IntBuffer *); +void glDeleteTextures(IntBuffer*); void glDeleteTextures(int); -void glLight(int, int, FloatBuffer *); -void glLightModel(int, FloatBuffer *); -void glGetFloat(int, FloatBuffer *); -void glTexGen(int, int, FloatBuffer *); -void glFog(int, FloatBuffer *); -void glTexCoordPointer(int, int, FloatBuffer *); -void glNormalPointer(int, ByteBuffer *); -void glColorPointer(int, bool, int, ByteBuffer *); -void glVertexPointer(int, int, FloatBuffer *); -void glEndList(int); -void glTexImage2D(int, int, int, int, int, int, int, int, ByteBuffer *); -void glCallLists(IntBuffer *); -void glGenQueriesARB(IntBuffer *); +void glLight(int, int, FloatBuffer*); +void glLightModel(int, FloatBuffer*); +void glGetFloat(int, FloatBuffer*); +void glTexGen(int, int, FloatBuffer*); +void glFog(int, FloatBuffer*); +void glTexCoordPointer(int, int, FloatBuffer*); +void glNormalPointer(int, ByteBuffer*); +void glColorPointer(int, bool, int, ByteBuffer*); +void glVertexPointer(int, int, FloatBuffer*); + +void glEndList_4J(int vertexCount = 0); + +void glTexImage2D(int, int, int, int, int, int, int, int, ByteBuffer*); +void glCallLists(IntBuffer*); +void glGenQueriesARB(IntBuffer*); void glBeginQueryARB(int, int); void glEndQueryARB(int); -void glGetQueryObjectuARB(int, int, IntBuffer *); -void glReadPixels(int, int, int, int, int, int, ByteBuffer *); +void glGetQueryObjectuARB(int, int, IntBuffer*); +void glReadPixels(int, int, int, int, int, int, ByteBuffer*); + void LinuxGLLogLightmapState(const char* stage, int textureId, bool scaleLight); void LinuxLogStubLightmapProbe(); - #else const int GL_BYTE = 0; @@ -54,13 +56,9 @@ const int GL_NORMALIZE = 0; const int GL_RESCALE_NORMAL = 0; - - const int GL_SMOOTH = 0; const int GL_FLAT = 0; - - const int GL_RGBA = 0; const int GL_BGRA = 1; const int GL_BGR = 0; @@ -83,73 +81,73 @@ const int GL_TEXTURE1 = 0; const int GL_TEXTURE0 = 1; void glFlush(); -void glTexGeni(int,int,int); -void glTexGen(int,int,FloatBuffer *); -void glReadPixels(int,int, int, int, int, int, ByteBuffer *); +void glTexGeni(int, int, int); +void glTexGen(int, int, FloatBuffer*); +void glReadPixels(int, int, int, int, int, int, ByteBuffer*); void glClearDepth(double); void glCullFace(int); -void glDeleteLists(int,int); -void glGenTextures(IntBuffer *); +void glDeleteLists(int, int); +void glGenTextures(IntBuffer*); int glGenTextures(); int glGenLists(int); -void glLight(int, int,FloatBuffer *); -void glLightModel(int, FloatBuffer *); -void glGetFloat(int a, FloatBuffer *b); +void glLight(int, int, FloatBuffer*); +void glLightModel(int, FloatBuffer*); +void glGetFloat(int a, FloatBuffer* b); void glTexCoordPointer(int, int, int, int); -void glTexCoordPointer(int, int, FloatBuffer *); +void glTexCoordPointer(int, int, FloatBuffer*); void glNormalPointer(int, int, int); -void glNormalPointer(int, ByteBuffer *); +void glNormalPointer(int, ByteBuffer*); void glEnableClientState(int); void glDisableClientState(int); -void glColorPointer(int, bool, int, ByteBuffer *); +void glColorPointer(int, bool, int, ByteBuffer*); void glColorPointer(int, int, int, int); void glVertexPointer(int, int, int, int); -void glVertexPointer(int, int, FloatBuffer *); -void glDrawArrays(int,int,int); -void glTranslatef(float,float,float); -void glRotatef(float,float,float,float); -void glNewList(int,int); +void glVertexPointer(int, int, FloatBuffer*); +void glDrawArrays(int, int, int); +void glTranslatef(float, float, float); +void glRotatef(float, float, float, float); +void glNewList(int, int); void glEndList(int vertexCount = 0); void glCallList(int); void glPopMatrix(); void glPushMatrix(); -void glColor3f(float,float,float); -void glScalef(float,float,float); -void glMultMatrixf(float *); -void glColor4f(float,float,float,float); +void glColor3f(float, float, float); +void glScalef(float, float, float); +void glMultMatrixf(float*); +void glColor4f(float, float, float, float); void glDisable(int); void glEnable(int); -void glBlendFunc(int,int); +void glBlendFunc(int, int); void glDepthMask(bool); -void glNormal3f(float,float,float); +void glNormal3f(float, float, float); void glDepthFunc(int); void glMatrixMode(int); void glLoadIdentity(); -void glBindTexture(int,int); -void glTexParameteri(int,int,int); -void glTexImage2D(int,int,int,int,int,int,int,int,ByteBuffer *); -void glDeleteTextures(IntBuffer *); +void glBindTexture(int, int); +void glTexParameteri(int, int, int); +void glTexImage2D(int, int, int, int, int, int, int, int, ByteBuffer*); +void glDeleteTextures(IntBuffer*); void glDeleteTextures(int); -void glCallLists(IntBuffer *); -void glGenQueriesARB(IntBuffer *); -void glColorMask(bool,bool,bool,bool); -void glBeginQueryARB(int,int); +void glCallLists(IntBuffer*); +void glGenQueriesARB(IntBuffer*); +void glColorMask(bool, bool, bool, bool); +void glBeginQueryARB(int, int); void glEndQueryARB(int); -void glGetQueryObjectuARB(int,int,IntBuffer *); +void glGetQueryObjectuARB(int, int, IntBuffer*); void glShadeModel(int); -void glPolygonOffset(float,float); +void glPolygonOffset(float, float); void glLineWidth(float); -void glScaled(double,double,double); -void gluPerspective(float,float,float,float); +void glScaled(double, double, double); +void gluPerspective(float, float, float, float); void glClear(int); -void glViewport(int,int,int,int); -void glAlphaFunc(int,float); -void glOrtho(float,float,float,float,float,float); -void glClearColor(float,float,float,float); -void glFogi(int,int); -void glFogf(int,float); -void glFog(int,FloatBuffer *); -void glColorMaterial(int,int); +void glViewport(int, int, int, int); +void glAlphaFunc(int, float); +void glOrtho(float, float, float, float, float, float); +void glClearColor(float, float, float, float); +void glFogi(int, int); +void glFogf(int, float); +void glFog(int, FloatBuffer*); +void glColorMaterial(int, int); void glMultiTexCoord2f(int, float, float); void glClientActiveTexture(int); @@ -158,164 +156,150 @@ void glActiveTexture(int); #endif #ifdef __linux__ -class GL11 -{ -public: - static const int GL_SMOOTH = 0x1D01; - static const int GL_FLAT = 0x1D00; - static void glShadeModel(int mode) { ::glShadeModel(mode); } -}; +class GL11 { +public: + static const int GL_SMOOTH = 0x1D01; + static const int GL_FLAT = 0x1D00; +#undef glShadeModel +#define GL_SHADEMODEL_IS_FUNCTION + static void glShadeModel(int mode) { ::glShadeModel(mode); } +}; #undef GL_ARRAY_BUFFER_ARB #undef GL_STREAM_DRAW_ARB -class ARBVertexBufferObject -{ +class ARBVertexBufferObject { public: - static const int GL_ARRAY_BUFFER_ARB = 0x8892; - static const int GL_STREAM_DRAW_ARB = 0x88E0; - static void glBindBufferARB(int, int) {} - static void glBufferDataARB(int, ByteBuffer *, int) {} - static void glGenBuffersARB(IntBuffer *) {} + static const int GL_ARRAY_BUFFER_ARB = 0x8892; + static const int GL_STREAM_DRAW_ARB = 0x88E0; + static void glBindBufferARB(int, int) {} + static void glBufferDataARB(int, ByteBuffer*, int) {} + static void glGenBuffersARB(IntBuffer*) {} }; #else -class GL11 -{ +class GL11 { public: - static const int GL_SMOOTH = 0; - static const int GL_FLAT = 0; - static void glShadeModel(int) {}; + static const int GL_SMOOTH = 0; + static const int GL_FLAT = 0; + static void glShadeModel(int) {}; }; -class ARBVertexBufferObject -{ +class ARBVertexBufferObject { public: - static const int GL_ARRAY_BUFFER_ARB = 0; - static const int GL_STREAM_DRAW_ARB = 0; - static void glBindBufferARB(int, int) {} - static void glBufferDataARB(int, ByteBuffer *, int) {} - static void glGenBuffersARB(IntBuffer *) {} + static const int GL_ARRAY_BUFFER_ARB = 0; + static const int GL_STREAM_DRAW_ARB = 0; + static void glBindBufferARB(int, int) {} + static void glBufferDataARB(int, ByteBuffer*, int) {} + static void glGenBuffersARB(IntBuffer*) {} }; #endif - class Level; class Player; class Textures; class Font; class MapItemSavedData; class Mob; -class Particles -{ +class Particles { public: - void render(float) {} - void tick() {} + void render(float) {} + void tick() {} }; class BufferedImage; -class Graphics -{ +class Graphics { public: - void drawImage(BufferedImage *, int, int, void *) {} - void dispose() {} + void drawImage(BufferedImage*, int, int, void*) {} + void dispose() {} }; -class ZipEntry -{ -}; +class ZipEntry {}; class InputStream; class File; -class ZipFile -{ +class ZipFile { public: - ZipFile(File *file) {} - InputStream *getInputStream(ZipEntry *entry) { return NULL; } - ZipEntry *getEntry(const std::wstring& name) {return NULL;} - void close() {} + ZipFile(File* file) {} + InputStream* getInputStream(ZipEntry* entry) { return NULL; } + ZipEntry* getEntry(const std::wstring& name) { return NULL; } + void close() {} }; -class ImageIO -{ +class ImageIO { public: - static BufferedImage *read(InputStream *in) { return NULL; } + static BufferedImage* read(InputStream* in) { return NULL; } }; -class Keyboard -{ +class Keyboard { public: - static void create() {} - static void destroy() {} - static bool isKeyDown(int) {return false;} - static std::wstring getKeyName(int) { return L"KEYNAME"; } - static void enableRepeatEvents(bool) {} - static const int KEY_A = 0; - static const int KEY_B = 1; - static const int KEY_C = 2; - static const int KEY_D = 3; - static const int KEY_E = 4; - static const int KEY_F = 5; - static const int KEY_G = 6; - static const int KEY_H = 7; - static const int KEY_I = 8; - static const int KEY_J = 9; - static const int KEY_K = 10; - static const int KEY_L = 11; - static const int KEY_M = 12; - static const int KEY_N = 13; - static const int KEY_O = 14; - static const int KEY_P = 15; - static const int KEY_Q = 16; - static const int KEY_R = 17; - static const int KEY_S = 18; - static const int KEY_T = 19; - static const int KEY_U = 20; - static const int KEY_V = 21; - static const int KEY_W = 22; - static const int KEY_X = 23; - static const int KEY_Y = 24; - static const int KEY_Z = 25; - static const int KEY_SPACE = 26; - static const int KEY_LSHIFT = 27; - static const int KEY_ESCAPE = 28; - static const int KEY_BACK = 29; - static const int KEY_RETURN = 30; - static const int KEY_RSHIFT = 31; - static const int KEY_UP = 32; - static const int KEY_DOWN = 33; - static const int KEY_TAB = 34; + static void create() {} + static void destroy() {} + static bool isKeyDown(int) { return false; } + static std::wstring getKeyName(int) { return L"KEYNAME"; } + static void enableRepeatEvents(bool) {} + static const int KEY_A = 0; + static const int KEY_B = 1; + static const int KEY_C = 2; + static const int KEY_D = 3; + static const int KEY_E = 4; + static const int KEY_F = 5; + static const int KEY_G = 6; + static const int KEY_H = 7; + static const int KEY_I = 8; + static const int KEY_J = 9; + static const int KEY_K = 10; + static const int KEY_L = 11; + static const int KEY_M = 12; + static const int KEY_N = 13; + static const int KEY_O = 14; + static const int KEY_P = 15; + static const int KEY_Q = 16; + static const int KEY_R = 17; + static const int KEY_S = 18; + static const int KEY_T = 19; + static const int KEY_U = 20; + static const int KEY_V = 21; + static const int KEY_W = 22; + static const int KEY_X = 23; + static const int KEY_Y = 24; + static const int KEY_Z = 25; + static const int KEY_SPACE = 26; + static const int KEY_LSHIFT = 27; + static const int KEY_ESCAPE = 28; + static const int KEY_BACK = 29; + static const int KEY_RETURN = 30; + static const int KEY_RSHIFT = 31; + static const int KEY_UP = 32; + static const int KEY_DOWN = 33; + static const int KEY_TAB = 34; }; -class Mouse -{ +class Mouse { public: - static void create() {} - static void destroy() {} - static int getX() { return 0; } - static int getY() { return 0; } - static bool isButtonDown(int) { return false; } + static void create() {} + static void destroy() {} + static int getX() { return 0; } + static int getY() { return 0; } + static bool isButtonDown(int) { return false; } }; -class Display -{ +class Display { public: - static bool isActive() {return true;} - static void update(); - static void swapBuffers(); - static void destroy() {} + static bool isActive() { return true; } + static void update(); + static void swapBuffers(); + static void destroy() {} }; -class BackgroundDownloader -{ +class BackgroundDownloader { public: - BackgroundDownloader(File workDir, Minecraft* minecraft) {} - void start() {} - void halt() {} - void forceReload() {} + BackgroundDownloader(File workDir, Minecraft* minecraft) {} + void start() {} + void halt() {} + void forceReload() {} }; -class Color -{ +class Color { public: - static int HSBtoRGB(float,float,float) {return 0;} + static int HSBtoRGB(float, float, float) { return 0; } }; From 09e8a8f9819475291a2e755a277337e7d6b21b0e Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 27 Mar 2026 00:36:18 +0100 Subject: [PATCH 22/71] batch 2: fixing chunk rendering & frustum culling; rewrote most of it, but now it works. next step is to fix the "highlight" effect & add the optimizations i wrote on optifromhell.txt --- .../Platform/Linux/Linux_UIController.cpp | 2 +- Minecraft.Client/Rendering/Camera.cpp | 6 +- Minecraft.Client/Rendering/Frustum.cpp | 81 +++++++------------ Minecraft.Client/Rendering/FrustumData.cpp | 15 ++-- Minecraft.Client/Rendering/LevelRenderer.cpp | 21 ++++- Minecraft.Client/Rendering/Tesselator.cpp | 13 ++- 6 files changed, 69 insertions(+), 69 deletions(-) diff --git a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp index 445fd5d9d..f4a1c033a 100644 --- a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp @@ -1,4 +1,4 @@ -// Linux_UIController.cpp +// should we keep Linux_UIController.cpp?* #include "../../../Minecraft.World/Platform/stdafx.h" #include "Linux_UIController.h" diff --git a/Minecraft.Client/Rendering/Camera.cpp b/Minecraft.Client/Rendering/Camera.cpp index f728f3f09..137108089 100644 --- a/Minecraft.Client/Rendering/Camera.cpp +++ b/Minecraft.Client/Rendering/Camera.cpp @@ -22,8 +22,10 @@ float Camera::xa2 = 0.0f; float Camera::za2 = 0.0f; void Camera::prepare(std::shared_ptr player, bool mirror) { - glGetFloat(GL_MODELVIEW_MATRIX, modelview); - glGetFloat(GL_PROJECTION_MATRIX, projection); + memcpy(modelview->_getDataPointer(), + RenderManager.MatrixGet(GL_MODELVIEW_MATRIX), 16 * sizeof(float)); + memcpy(projection->_getDataPointer(), + RenderManager.MatrixGet(GL_PROJECTION_MATRIX), 16 * sizeof(float)); /* Original java code for reference glGetInteger(GL_VIEWPORT, viewport); diff --git a/Minecraft.Client/Rendering/Frustum.cpp b/Minecraft.Client/Rendering/Frustum.cpp index 1144a7d5f..3d2e562ea 100644 --- a/Minecraft.Client/Rendering/Frustum.cpp +++ b/Minecraft.Client/Rendering/Frustum.cpp @@ -1,9 +1,11 @@ #include "../Platform/stdafx.h" #include "../../Minecraft.World/IO/Streams/FloatBuffer.h" #include "Frustum.h" +#include "Camera.h" Frustum* Frustum::frustum = new Frustum(); +// those are now unused but i still gotta do testing. Frustum::Frustum() { _proj = MemoryTracker::createFloatBuffer(16); _modl = MemoryTracker::createFloatBuffer(16); @@ -43,63 +45,38 @@ void Frustum::normalizePlane(float** frustum, int side) { } void Frustum::calculateFrustum() { - _proj->clear(); - _modl->clear(); - _clip->clear(); + // 4jcraft: GL 3.3 core removed GL_MODELVIEW_MATRIX / GL_PROJECTION_MATRIX + // queries. + // Camera::prepare() already captures both matrices every frame :) + // i spent an ungodly amount of time on this simple fix. + memcpy(proj.data, RenderManager.MatrixGet(GL_PROJECTION_MATRIX), + 16 * sizeof(float)); + memcpy(modl.data, RenderManager.MatrixGet(GL_MODELVIEW_MATRIX), + 16 * sizeof(float)); - // glGetFloatv() is used to extract information about our OpenGL world. - // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix. - // It then stores the matrix into an array of [16]. - glGetFloat(GL_PROJECTION_MATRIX, _proj); + float* p = proj.data; + float* m = modl.data; + float* c = clip.data; - // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix. - // This also stores it in an array of [16]. - glGetFloat(GL_MODELVIEW_MATRIX, _modl); + c[0] = m[0] * p[0] + m[1] * p[4] + m[2] * p[8] + m[3] * p[12]; + c[1] = m[0] * p[1] + m[1] * p[5] + m[2] * p[9] + m[3] * p[13]; + c[2] = m[0] * p[2] + m[1] * p[6] + m[2] * p[10] + m[3] * p[14]; + c[3] = m[0] * p[3] + m[1] * p[7] + m[2] * p[11] + m[3] * p[15]; - _proj->flip()->limit(16); - _proj->get(&proj); - _modl->flip()->limit(16); - _modl->get(&modl); + c[4] = m[4] * p[0] + m[5] * p[4] + m[6] * p[8] + m[7] * p[12]; + c[5] = m[4] * p[1] + m[5] * p[5] + m[6] * p[9] + m[7] * p[13]; + c[6] = m[4] * p[2] + m[5] * p[6] + m[6] * p[10] + m[7] * p[14]; + c[7] = m[4] * p[3] + m[5] * p[7] + m[6] * p[11] + m[7] * p[15]; - // Now that we have our modelview and projection matrix, if we combine these - // 2 matrices, it will give us our clipping planes. To combine 2 matrices, - // we multiply them. + c[8] = m[8] * p[0] + m[9] * p[4] + m[10] * p[8] + m[11] * p[12]; + c[9] = m[8] * p[1] + m[9] * p[5] + m[10] * p[9] + m[11] * p[13]; + c[10] = m[8] * p[2] + m[9] * p[6] + m[10] * p[10] + m[11] * p[14]; + c[11] = m[8] * p[3] + m[9] * p[7] + m[10] * p[11] + m[11] * p[15]; - clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + - modl[3] * proj[12]; - clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + - modl[3] * proj[13]; - clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + - modl[3] * proj[14]; - clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + - modl[3] * proj[15]; - - clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + - modl[7] * proj[12]; - clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + - modl[7] * proj[13]; - clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + - modl[7] * proj[14]; - clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + - modl[7] * proj[15]; - - clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + - modl[11] * proj[12]; - clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + - modl[11] * proj[13]; - clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + - modl[11] * proj[14]; - clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + - modl[11] * proj[15]; - - clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + - modl[15] * proj[12]; - clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + - modl[15] * proj[13]; - clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + - modl[15] * proj[14]; - clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + - modl[15] * proj[15]; + c[12] = m[12] * p[0] + m[13] * p[4] + m[14] * p[8] + m[15] * p[12]; + c[13] = m[12] * p[1] + m[13] * p[5] + m[14] * p[9] + m[15] * p[13]; + c[14] = m[12] * p[2] + m[13] * p[6] + m[14] * p[10] + m[15] * p[14]; + c[15] = m[12] * p[3] + m[13] * p[7] + m[14] * p[11] + m[15] * p[15]; // Now we actually want to get the sides of the frustum. To do this we take // the clipping planes we received above and extract the sides from them. diff --git a/Minecraft.Client/Rendering/FrustumData.cpp b/Minecraft.Client/Rendering/FrustumData.cpp index a5f11b768..eb46045e3 100644 --- a/Minecraft.Client/Rendering/FrustumData.cpp +++ b/Minecraft.Client/Rendering/FrustumData.cpp @@ -1,21 +1,22 @@ #include "../Platform/stdafx.h" #include "FrustumData.h" -float** m_Frustum; +// float** m_Frustum; FrustumData::FrustumData() { - m_Frustum = new float*[16]; - for (int i = 0; i < 16; i++) m_Frustum[i] = new float[16]; + this->m_Frustum = new float*[6]; + for (int i = 0; i < 6; i++) { + this->m_Frustum[i] = new float[4]; + } proj = floatArray(16); modl = floatArray(16); clip = floatArray(16); } FrustumData::~FrustumData() { - delete[] proj.data; - delete[] modl.data; - delete[] clip.data; - for (int i = 0; i < 16; i++) delete[] m_Frustum[i]; + for (int i = 0; i < 6; i++) { + delete[] m_Frustum[i]; + } delete[] m_Frustum; } diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 0d5e9afa7..f64a9b284 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -856,6 +856,10 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == emptyFlag) continue; // Check that this particular layer isn't empty + glPushMatrix(); + + glTranslatef((float)pClipChunk->chunk->x, (float)pClipChunk->chunk->y, + (float)pClipChunk->chunk->z); // List can be calculated directly from the chunk's global idex int list = pClipChunk->globalIdx * 2 + layer; list += chunkLists; @@ -863,6 +867,8 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { if (RenderManager.CBuffCall(list, first)) { first = false; } + + glPopMatrix(); count++; } @@ -2248,9 +2254,13 @@ bool LevelRenderer::updateDirtyChunks() { // int64_t startTime = System::currentTimeMillis(); // app.DebugPrintf("Rebuilding permaChunk %d\n", index); - + Tesselator::getInstance()->offset(-permaChunk[index].x, + -permaChunk[index].y, + -permaChunk[index].z); permaChunk[index].rebuild(); + Tesselator::getInstance()->offset(0, 0, 0); + if (index != 0) s_rebuildCompleteEvents->Set( index - 1); // MGH - this rebuild happening on the main @@ -2303,7 +2313,10 @@ bool LevelRenderer::updateDirtyChunks() { // static int64_t totalTime = 0; // static int64_t countTime = 0; // int64_t startTime = System::currentTimeMillis(); + Tesselator::getInstance()->offset( + -permaChunk[index].x, -permaChunk[index].y, -permaChunk[index].z); permaChunk.rebuild(); + Tesselator::getInstance()->offset(0, 0, 0); // int64_t endTime = System::currentTimeMillis(); // totalTime += (endTime - startTime); // countTime++; @@ -4039,10 +4052,12 @@ int LevelRenderer::rebuildChunkThreadProc(void* lpParam) { while (true) { s_activationEventA[index]->WaitForSignal(INFINITE); - + Tesselator* t = Tesselator::getInstance(); + Tesselator::getInstance()->offset( + -permaChunk[index].x, -permaChunk[index].y, -permaChunk[index].z); // app.DebugPrintf("Rebuilding permaChunk %d\n", index + 1); permaChunk[index + 1].rebuild(); - + Tesselator::getInstance()->offset(0, 0, 0); // Inform the producer thread that we are done with this chunk s_rebuildCompleteEvents->Set(index); } diff --git a/Minecraft.Client/Rendering/Tesselator.cpp b/Minecraft.Client/Rendering/Tesselator.cpp index 49e6f5669..76e13d996 100644 --- a/Minecraft.Client/Rendering/Tesselator.cpp +++ b/Minecraft.Client/Rendering/Tesselator.cpp @@ -215,10 +215,15 @@ void Tesselator::end() { } #endif } - glDisableClientState(GL_VERTEX_ARRAY); - if (hasTexture) glDisableClientState(GL_TEXTURE_COORD_ARRAY); - if (hasColor) glDisableClientState(GL_COLOR_ARRAY); - if (hasNormal) glDisableClientState(GL_NORMAL_ARRAY); + // 4jcraft: gldisableclientstate breaks gl compat, commenting those lead + // to some weird glitches with input but.. somehow stopped one day so.. + // just keep an eye on these incase mouse locking stops working outta + // nowhere (i blame opengl not me) + // + // glDisableClientState(GL_VERTEX_ARRAY); if (hasTexture) + // glDisableClientState(GL_TEXTURE_COORD_ARRAY); if (hasColor) + // glDisableClientState(GL_COLOR_ARRAY); if (hasNormal) + // glDisableClientState(GL_NORMAL_ARRAY); } clear(); From e71ff8699a78b6f3908d3a4fd42590f8f2341fe4 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 27 Mar 2026 09:12:07 +0100 Subject: [PATCH 23/71] batch 2.1; entity lightfix --- .../EntityRenderers/ItemRenderer.cpp | 30 ++++++++++++------- Minecraft.Client/Rendering/Lighting.cpp | 28 +++++++++++++---- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp index bf94c18ab..35cfa3e3f 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp @@ -358,16 +358,22 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, Tile* tile = Tile::tiles[itemId]; glPushMatrix(); + // 4J - original code left here for reference // 4jcraft: original code reused for proper lighting + // force normalize + glEnable(GL_NORMALIZE); + glDisable(GL_RESCALE_NORMAL); + #if 1 - glTranslatef((float)(x), (float)(y), 0.0f); - glScalef(fScaleX, fScaleY, 1.0f); - glTranslatef(-2.0f,3.0f, -3.0f + blitOffset); - glScalef(10.0f, 10.0f, 10.0f); + // does it work? too lazy to find out + glTranslatef((float)(x), (float)(y), 0.0f); + glScalef(fScaleX, fScaleY, 1.0f); + glTranslatef(-2.0f, 3.0f, -3.0f + blitOffset); + glScalef(10.0f, 10.0f, 10.0f); glTranslatef(1.0f, 0.5f, 8.0f); - glScalef(1.0f, 1.0f, -1.0f); - glRotatef(180.0f + 30.0f, 1.0f, 0.0f, 0.0f); + glScalef(1.0f, -1.0f, 1.0f); + glRotatef(30.0f, 1.0f, 0.0f, 0.0f); glRotatef(45.0f, 0.0f, 1.0f, 0.0f); #else glTranslatef(x, y, 0.0f); // Translate to screen coords @@ -385,12 +391,16 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, glRotatef(45.0f, 0.0f, 1.0f, 0.0f); // Rotate round y axis (centre at origin) #endif - // 4J-PB - pass the alpha value in - the grass block - // render has the top surface coloured differently to - // the rest of the block - glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); + // 4J-PB - pass the alpha value in - the grass block + // render has the top surface coloured differently to + // the rest of the block + glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); + // glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); + tileRenderer->renderTile(tile, itemAuxValue, 1, fAlpha, useCompiled); + // glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); + glDisable(GL_NORMALIZE); glPopMatrix(); PIXEndNamedEvent(); } else if (Item::items[itemId]->hasMultipleSpriteLayers()) { diff --git a/Minecraft.Client/Rendering/Lighting.cpp b/Minecraft.Client/Rendering/Lighting.cpp index 4182a9fe3..ada92606a 100644 --- a/Minecraft.Client/Rendering/Lighting.cpp +++ b/Minecraft.Client/Rendering/Lighting.cpp @@ -52,9 +52,27 @@ FloatBuffer* Lighting::getBuffer(float a, float b, float c, float d) { } void Lighting::turnOnGui() { - glPushMatrix(); - glRotatef(-30, 0, 1, 0); - glRotatef(165, 1, 0, 0); - turnOn(); - glPopMatrix(); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + + float a = 0.4f; + float d = 0.6f; + float s = 0.0f; + // big john machine + // (copied from turnon) + RenderManager.StateSetLightDirection(0, 0.129f, 0.965f, 0.224f); + glLight(GL_LIGHT0, GL_DIFFUSE, getBuffer(d, d, d, 1)); + glLight(GL_LIGHT0, GL_AMBIENT, getBuffer(0.0f, 0.0f, 0.0f, 1.0f)); + glLight(GL_LIGHT0, GL_SPECULAR, getBuffer(s, s, s, 1.0f)); + + RenderManager.StateSetLightDirection(1, -0.129f, 0.965f, -0.224f); + glLight(GL_LIGHT1, GL_DIFFUSE, getBuffer(d, d, d, 1)); + glLight(GL_LIGHT1, GL_AMBIENT, getBuffer(0.0f, 0.0f, 0.0f, 1.0f)); + glLight(GL_LIGHT1, GL_SPECULAR, getBuffer(s, s, s, 1.0f)); + + glShadeModel(GL_FLAT); + glLightModel(GL_LIGHT_MODEL_AMBIENT, getBuffer(a, a, a, 1)); } From 97b068260a399751e9be4de4cf828f382284c34e Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 27 Mar 2026 09:33:32 +0100 Subject: [PATCH 24/71] batch 2.2, clamp be gone --- Minecraft.Client/Textures/Textures.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 907f51927..37f99f1ca 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -696,8 +696,8 @@ void Textures::loadTexture(BufferedImage* img, int id, bool blur, bool clamp) { } if (clamp) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); From b7ac84ca79cf3965bc471f25e65b62e7f1d53cf1 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Fri, 27 Mar 2026 04:21:18 -0500 Subject: [PATCH 25/71] fix: revert changes to lighting direction --- Minecraft.Client/Platform/Common/UI/UIScene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/Platform/Common/UI/UIScene.cpp b/Minecraft.Client/Platform/Common/UI/UIScene.cpp index c50a82230..863e0f951 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIScene.cpp @@ -712,7 +712,7 @@ void UIScene::_customDrawSlotControl(CustomDrawData* region, int iPad, } glEnable(GL_RESCALE_NORMAL); glPushMatrix(); - Lighting::turnOnGui(); + Lighting::turnOn(); glRotatef(120, 1, 0, 0); glPopMatrix(); From 08ed9861b9bac78751b8ff761a38d8a7f671e546 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 27 Mar 2026 11:00:36 +0100 Subject: [PATCH 26/71] batch 2.3 enabling back optimizations --- Minecraft.Client/Platform/Linux/Linux_UIController.cpp | 2 ++ Minecraft.Client/Rendering/GameRenderer.cpp | 2 +- Minecraft.Client/Textures/Textures.cpp | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp index f4a1c033a..a8da75b1d 100644 --- a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp @@ -35,6 +35,8 @@ static void restoreFixedFunctionStateAfterIggy() { RenderManager.MatrixMode(GL_MODELVIEW); RenderManager.Set_matrixDirty(); + Minecraft::GetInstance()->textures->clearLastBoundId(); + // 4jcraft: clears the last bound id, dumb fix but fine } void ConsoleUIController::init(S32 w, S32 h) { diff --git a/Minecraft.Client/Rendering/GameRenderer.cpp b/Minecraft.Client/Rendering/GameRenderer.cpp index 3e5763087..9b9759704 100644 --- a/Minecraft.Client/Rendering/GameRenderer.cpp +++ b/Minecraft.Client/Rendering/GameRenderer.cpp @@ -1778,7 +1778,7 @@ void GameRenderer::renderSnowAndRain(float a) { yy1 * s / 4.0f + ra * s); #endif t->offset(0, 0, 0); - t->end(); + } else { if (mode != 1) { if (mode >= 0) t->end(); diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 37f99f1ca..cad870918 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -541,11 +541,11 @@ void Textures::bindTextureLayers(ResourceLocation* resource) { } void Textures::bind(int id) { - // if (id != lastBoundId) + if (id != lastBoundId) { if (id < 0) return; glBindTexture(GL_TEXTURE_2D, id); - // lastBoundId = id; + lastBoundId = id; } } From e0aa739aacfb5943dd7e92e5eb4e3626f32e829d Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 27 Mar 2026 11:22:23 +0100 Subject: [PATCH 27/71] batch 2.4 | highlight fix --- .../Platform/Linux/Linux_UIController.cpp | 2 +- Minecraft.Client/Rendering/LevelRenderer.cpp | 83 +++++++++++-------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp index a8da75b1d..5b6524c9f 100644 --- a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp @@ -33,7 +33,7 @@ static void restoreFixedFunctionStateAfterIggy() { RenderManager.MatrixSetIdentity(); RenderManager.MatrixMode(GL_MODELVIEW); - + glDisable(GL_SCISSOR_TEST); // iggy is mean RenderManager.Set_matrixDirty(); Minecraft::GetInstance()->textures->clearLastBoundId(); // 4jcraft: clears the last bound id, dumb fix but fine diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index f64a9b284..f2ed64ceb 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -2469,7 +2469,6 @@ void LevelRenderer::renderDestroyAnimation(Tesselator* t, glPopMatrix(); } } - void LevelRenderer::renderHitOutline(std::shared_ptr player, HitResult* h, int mode, float a) { if (mode == 0 && h->type == HitResult::TILE) { @@ -2477,14 +2476,18 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, // 4J-PB - If Display HUD is false, don't render the hit outline if (app.GetGameSettings(iPad, eGameSetting_DisplayHUD) == 0) return; + RenderManager.StateSetLightingEnable(false); + RenderManager.StateSetTextureEnable(false); + + // draw hit outline + RenderManager.StateSetColour(0.0f, 0.0f, 0.0f, 0.4f); + RenderManager.StateSetLineWidth(2.0f); + + // hack + glDepthFunc(GL_LEQUAL); + glEnable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(-2.0f, -2.0f); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(0, 0, 0, 0.4f); - glLineWidth(2.0f); - glDisable(GL_TEXTURE_2D); - glDepthMask(false); - float ss = 0.002f; int tileId = level[iPad]->getTile(h->x, h->y, h->z); if (tileId > 0) { @@ -2495,46 +2498,56 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, AABB bb = Tile::tiles[tileId] ->getTileAABB(level[iPad], h->x, h->y, h->z) - .grow(ss, ss, ss) + .grow(0.002f, 0.002f, 0.002f) .move(-xo, -yo, -zo); render(&bb); } - glDepthMask(true); - glEnable(GL_TEXTURE_2D); - glDisable(GL_BLEND); + + // restore + glDisable(GL_POLYGON_OFFSET_LINE); + RenderManager.StateSetColour(1.0f, 1.0f, 1.0f, 1.0f); + RenderManager.StateSetTextureEnable(true); + RenderManager.StateSetLightingEnable(true); } } void LevelRenderer::render(AABB* b) { Tesselator* t = Tesselator::getInstance(); + RenderManager.StateSetLightingEnable(false); + RenderManager.StateSetTextureEnable(false); + RenderManager.StateSetColour(0.0f, 0.0f, 0.0f, 0.4f); - t->begin(GL_LINE_STRIP); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y0), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y0), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z0)); - t->end(); + // prevent zfight + glEnable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(-2.0f, -2.0f); - t->begin(GL_LINE_STRIP); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y1), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y1), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z0)); - t->end(); + // One call please! + t->begin(GL_LINES); + + // Bottom + t->vertex(b->x0, b->y0, b->z0); t->vertex(b->x1, b->y0, b->z0); + t->vertex(b->x1, b->y0, b->z0); t->vertex(b->x1, b->y0, b->z1); + t->vertex(b->x1, b->y0, b->z1); t->vertex(b->x0, b->y0, b->z1); + t->vertex(b->x0, b->y0, b->z1); t->vertex(b->x0, b->y0, b->z0); + + // Top + t->vertex(b->x0, b->y1, b->z0); t->vertex(b->x1, b->y1, b->z0); + t->vertex(b->x1, b->y1, b->z0); t->vertex(b->x1, b->y1, b->z1); + t->vertex(b->x1, b->y1, b->z1); t->vertex(b->x0, b->y1, b->z1); + t->vertex(b->x0, b->y1, b->z1); t->vertex(b->x0, b->y1, b->z0); + + // Vertical + t->vertex(b->x0, b->y0, b->z0); t->vertex(b->x0, b->y1, b->z0); + t->vertex(b->x1, b->y0, b->z0); t->vertex(b->x1, b->y1, b->z0); + t->vertex(b->x1, b->y0, b->z1); t->vertex(b->x1, b->y1, b->z1); + t->vertex(b->x0, b->y0, b->z1); t->vertex(b->x0, b->y1, b->z1); - t->begin(GL_LINES); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z0)); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y0), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y1), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y0), (float)(b->z1)); - t->vertex((float)(b->x1), (float)(b->y1), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z1)); t->end(); + glDisable(GL_POLYGON_OFFSET_LINE); + RenderManager.StateSetLightingEnable(true); + RenderManager.StateSetTextureEnable(true); + RenderManager.StateSetColour(1.0f, 1.0f, 1.0f, 1.0f); } void LevelRenderer::setDirty(int x0, int y0, int z0, int x1, int y1, int z1, From 98c8d49eaff20c9394578390b3c88587650f55ab Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Fri, 27 Mar 2026 10:24:44 -0500 Subject: [PATCH 28/71] fix: make hit outline width 1 pixel --- Minecraft.Client/Rendering/LevelRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index f2ed64ceb..9471f43cc 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -2481,7 +2481,7 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, // draw hit outline RenderManager.StateSetColour(0.0f, 0.0f, 0.0f, 0.4f); - RenderManager.StateSetLineWidth(2.0f); + RenderManager.StateSetLineWidth(1.0f); // hack glDepthFunc(GL_LEQUAL); From ca601c8205b297cc4efc03b371f58c5e742a72ed Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Fri, 27 Mar 2026 11:37:37 -0500 Subject: [PATCH 29/71] restore original code for renderGuiItem --- Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp index 35cfa3e3f..769ece8e8 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp @@ -365,7 +365,7 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, glEnable(GL_NORMALIZE); glDisable(GL_RESCALE_NORMAL); -#if 1 +#if 0 // does it work? too lazy to find out glTranslatef((float)(x), (float)(y), 0.0f); glScalef(fScaleX, fScaleY, 1.0f); From dbd7ce40cd68ff165583a1416eaca0089e816c13 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Fri, 27 Mar 2026 11:52:28 -0500 Subject: [PATCH 30/71] fix: don't GL_NORMALIZE/GL_RESCALE_NORMAL when rendering items --- .../Rendering/EntityRenderers/ItemRenderer.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp index 769ece8e8..e5eba0d25 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp @@ -360,12 +360,7 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, glPushMatrix(); // 4J - original code left here for reference - // 4jcraft: original code reused for proper lighting - // force normalize - glEnable(GL_NORMALIZE); - glDisable(GL_RESCALE_NORMAL); - -#if 0 +#if 1 // does it work? too lazy to find out glTranslatef((float)(x), (float)(y), 0.0f); glScalef(fScaleX, fScaleY, 1.0f); @@ -400,7 +395,6 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, tileRenderer->renderTile(tile, itemAuxValue, 1, fAlpha, useCompiled); // glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); - glDisable(GL_NORMALIZE); glPopMatrix(); PIXEndNamedEvent(); } else if (Item::items[itemId]->hasMultipleSpriteLayers()) { From a4db31ee64a1d484ef133a88f68ac6ec181031a1 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Fri, 27 Mar 2026 11:56:36 -0500 Subject: [PATCH 31/71] add back ItemRenderer comment --- Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp index e5eba0d25..069de61af 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp @@ -360,6 +360,8 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, glPushMatrix(); // 4J - original code left here for reference + // 4jcraft: original code reused for proper lighting + // force normalize #if 1 // does it work? too lazy to find out glTranslatef((float)(x), (float)(y), 0.0f); From 23af1b0a727e1766eb408c98809c58c7de0fd972 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:08:14 -0500 Subject: [PATCH 32/71] revert unintentional turnOnGui --- Minecraft.Client/Rendering/Lighting.cpp | 28 +++++-------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/Minecraft.Client/Rendering/Lighting.cpp b/Minecraft.Client/Rendering/Lighting.cpp index ada92606a..4182a9fe3 100644 --- a/Minecraft.Client/Rendering/Lighting.cpp +++ b/Minecraft.Client/Rendering/Lighting.cpp @@ -52,27 +52,9 @@ FloatBuffer* Lighting::getBuffer(float a, float b, float c, float d) { } void Lighting::turnOnGui() { - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_LIGHT1); - glEnable(GL_COLOR_MATERIAL); - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - - float a = 0.4f; - float d = 0.6f; - float s = 0.0f; - // big john machine - // (copied from turnon) - RenderManager.StateSetLightDirection(0, 0.129f, 0.965f, 0.224f); - glLight(GL_LIGHT0, GL_DIFFUSE, getBuffer(d, d, d, 1)); - glLight(GL_LIGHT0, GL_AMBIENT, getBuffer(0.0f, 0.0f, 0.0f, 1.0f)); - glLight(GL_LIGHT0, GL_SPECULAR, getBuffer(s, s, s, 1.0f)); - - RenderManager.StateSetLightDirection(1, -0.129f, 0.965f, -0.224f); - glLight(GL_LIGHT1, GL_DIFFUSE, getBuffer(d, d, d, 1)); - glLight(GL_LIGHT1, GL_AMBIENT, getBuffer(0.0f, 0.0f, 0.0f, 1.0f)); - glLight(GL_LIGHT1, GL_SPECULAR, getBuffer(s, s, s, 1.0f)); - - glShadeModel(GL_FLAT); - glLightModel(GL_LIGHT_MODEL_AMBIENT, getBuffer(a, a, a, 1)); + glPushMatrix(); + glRotatef(-30, 0, 1, 0); + glRotatef(165, 1, 0, 0); + turnOn(); + glPopMatrix(); } From fa248125824a877c9f418416970641002a1cdb39 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 00:29:37 -0500 Subject: [PATCH 33/71] revert some seemingly unneeded changes --- Minecraft.Client/Rendering/FrustumData.cpp | 7 ++++--- Minecraft.Client/Rendering/GameRenderer.cpp | 2 +- Minecraft.Client/Rendering/LevelRenderer.cpp | 9 --------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Minecraft.Client/Rendering/FrustumData.cpp b/Minecraft.Client/Rendering/FrustumData.cpp index eb46045e3..098243a75 100644 --- a/Minecraft.Client/Rendering/FrustumData.cpp +++ b/Minecraft.Client/Rendering/FrustumData.cpp @@ -14,9 +14,10 @@ FrustumData::FrustumData() { } FrustumData::~FrustumData() { - for (int i = 0; i < 6; i++) { - delete[] m_Frustum[i]; - } + delete[] proj.data; + delete[] modl.data; + delete[] clip.data; + for (int i = 0; i < 6; i++) delete[] m_Frustum[i]; delete[] m_Frustum; } diff --git a/Minecraft.Client/Rendering/GameRenderer.cpp b/Minecraft.Client/Rendering/GameRenderer.cpp index 9b9759704..3e5763087 100644 --- a/Minecraft.Client/Rendering/GameRenderer.cpp +++ b/Minecraft.Client/Rendering/GameRenderer.cpp @@ -1778,7 +1778,7 @@ void GameRenderer::renderSnowAndRain(float a) { yy1 * s / 4.0f + ra * s); #endif t->offset(0, 0, 0); - + t->end(); } else { if (mode != 1) { if (mode >= 0) t->end(); diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 9471f43cc..9cf3dfdd3 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -2254,13 +2254,8 @@ bool LevelRenderer::updateDirtyChunks() { // int64_t startTime = System::currentTimeMillis(); // app.DebugPrintf("Rebuilding permaChunk %d\n", index); - Tesselator::getInstance()->offset(-permaChunk[index].x, - -permaChunk[index].y, - -permaChunk[index].z); permaChunk[index].rebuild(); - Tesselator::getInstance()->offset(0, 0, 0); - if (index != 0) s_rebuildCompleteEvents->Set( index - 1); // MGH - this rebuild happening on the main @@ -4065,12 +4060,8 @@ int LevelRenderer::rebuildChunkThreadProc(void* lpParam) { while (true) { s_activationEventA[index]->WaitForSignal(INFINITE); - Tesselator* t = Tesselator::getInstance(); - Tesselator::getInstance()->offset( - -permaChunk[index].x, -permaChunk[index].y, -permaChunk[index].z); // app.DebugPrintf("Rebuilding permaChunk %d\n", index + 1); permaChunk[index + 1].rebuild(); - Tesselator::getInstance()->offset(0, 0, 0); // Inform the producer thread that we are done with this chunk s_rebuildCompleteEvents->Set(index); } From 9680753f5e5d81476447c454e49bf899a6f786e2 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Sat, 28 Mar 2026 18:00:47 +1100 Subject: [PATCH 34/71] fix(linux): restore UI GL state handoff for core renderer --- .../Common/UI/UIScene_EnchantingMenu.cpp | 2 - .../Platform/Linux/Iggy/gdraw/gdraw.c | 3 +- Minecraft.Client/Platform/Linux/LinuxGL.cpp | 13 ++++- .../Platform/Linux/Linux_UIController.cpp | 56 ++++++++----------- Minecraft.Client/Rendering/FrustumData.cpp | 2 +- Minecraft.Client/Rendering/GameRenderer.cpp | 1 + Minecraft.Client/Rendering/LevelRenderer.cpp | 8 +-- 7 files changed, 43 insertions(+), 42 deletions(-) diff --git a/Minecraft.Client/Platform/Common/UI/UIScene_EnchantingMenu.cpp b/Minecraft.Client/Platform/Common/UI/UIScene_EnchantingMenu.cpp index 78fca71e5..0affa127f 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene_EnchantingMenu.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIScene_EnchantingMenu.cpp @@ -5,8 +5,6 @@ #include "../../Minecraft.Client/Minecraft.h" #include "UIScene_EnchantingMenu.h" -#include - UIScene_EnchantingMenu::UIScene_EnchantingMenu(int iPad, void* _initData, UILayer* parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) { diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c index 0c2cea4d5..fc037dcbf 100644 --- a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c +++ b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c @@ -776,6 +776,7 @@ GDrawFunctions* gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) { real_RenderingBegin = funcs->RenderingBegin; funcs->RenderingBegin = hooked_RenderingBegin; + funcs->ClearID = gdraw_ClearID; gdraw->tex_formats = tex_formats; gdraw->has_mapbuffer = true; @@ -815,4 +816,4 @@ void gdraw_GL_BeginCustomDraw_4J(IggyCustomDrawCallbackRegion* region, void gdraw_GL_CalculateCustomDraw_4J(IggyCustomDrawCallbackRegion* region, F32* matrix) { gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, 0.0f, 0); -} \ No newline at end of file +} diff --git a/Minecraft.Client/Platform/Linux/LinuxGL.cpp b/Minecraft.Client/Platform/Linux/LinuxGL.cpp index 1333b5114..a1d992ee3 100644 --- a/Minecraft.Client/Platform/Linux/LinuxGL.cpp +++ b/Minecraft.Client/Platform/Linux/LinuxGL.cpp @@ -28,6 +28,12 @@ #undef glActiveTexture // _4j suffix shit (todo: make ts better) +int glGenTextures() { + GLuint id = 0; + ::glGenTextures(1, &id); + return (int)id; +} + void glGenTextures_4J(IntBuffer* buf) { GLuint id = 0; ::glGenTextures(1, &id); @@ -35,6 +41,11 @@ void glGenTextures_4J(IntBuffer* buf) { buf->flip(); } +void glDeleteTextures(int id) { + GLuint uid = (GLuint)id; + ::glDeleteTextures(1, &uid); +} + void glDeleteTextures_4J(IntBuffer* buf) { if (buf && buf->limit() > 0) { int id = buf->get(0); @@ -218,4 +229,4 @@ void LinuxGLLogLightmapState(const char* stage, int textureId, unit1Enabled ? 1 : 0); } -#endif \ No newline at end of file +#endif diff --git a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp index 5b6524c9f..e2c1c17c7 100644 --- a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp @@ -1,42 +1,35 @@ -// should we keep Linux_UIController.cpp?* #include "../../../Minecraft.World/Platform/stdafx.h" #include "Linux_UIController.h" -// Temp -#include "../../Minecraft.h" -#include "../../Textures/Textures.h" - // GDraw GL backend for Linux #include "Iggy/gdraw/gdraw.h" -#include "4J_Render.h" ConsoleUIController ui; static void restoreFixedFunctionStateAfterIggy() { - RenderManager.StateSetColour(1.0f, 1.0f, 1.0f, 1.0f); - RenderManager.StateSetAlphaTestEnable(true); - RenderManager.StateSetAlphaFunc(GL_GREATER, 0.1f); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.1f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); - RenderManager.StateSetDepthTestEnable(true); - RenderManager.StateSetDepthFunc(GL_LEQUAL); - RenderManager.StateSetDepthMask(true); + glClientActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); - RenderManager.StateSetFaceCull(true); - RenderManager.StateSetActiveTexture(GL_TEXTURE1); - RenderManager.StateSetTextureEnable(false); - RenderManager.MatrixMode(GL_TEXTURE); - RenderManager.MatrixSetIdentity(); + glClientActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); - RenderManager.StateSetActiveTexture(GL_TEXTURE0); - RenderManager.StateSetTextureEnable(true); - RenderManager.MatrixMode(GL_TEXTURE); - - RenderManager.MatrixSetIdentity(); - RenderManager.MatrixMode(GL_MODELVIEW); - glDisable(GL_SCISSOR_TEST); // iggy is mean - RenderManager.Set_matrixDirty(); - Minecraft::GetInstance()->textures->clearLastBoundId(); - // 4jcraft: clears the last bound id, dumb fix but fine + glMatrixMode(GL_MODELVIEW); } void ConsoleUIController::init(S32 w, S32 h) { @@ -69,11 +62,8 @@ void ConsoleUIController::render() { if (!gdraw_funcs) return; gdraw_GL_SetTileOrigin(0, 0, 0); - if (!app.GetGameStarted()) { - glDisable(GL_SCISSOR_TEST); - glClearDepth(1.0); - glDepthMask(GL_TRUE); - glClear(GL_DEPTH_BUFFER_BIT); + if (!app.GetGameStarted() && gdraw_funcs->ClearID) { + gdraw_funcs->ClearID(); } // render @@ -144,4 +134,4 @@ void ConsoleUIController::shutdown() { gdraw_funcs = nullptr; } #endif -} \ No newline at end of file +} diff --git a/Minecraft.Client/Rendering/FrustumData.cpp b/Minecraft.Client/Rendering/FrustumData.cpp index 098243a75..140fa4772 100644 --- a/Minecraft.Client/Rendering/FrustumData.cpp +++ b/Minecraft.Client/Rendering/FrustumData.cpp @@ -130,4 +130,4 @@ bool FrustumData::cubeInFrustum(double x1, double y1, double z1, double x2, bool FrustumData::isVisible(AABB* aabb) { return cubeInFrustum(aabb->x0, aabb->y0, aabb->z0, aabb->x1, aabb->y1, aabb->z1); -} \ No newline at end of file +} diff --git a/Minecraft.Client/Rendering/GameRenderer.cpp b/Minecraft.Client/Rendering/GameRenderer.cpp index 3e5763087..61171598e 100644 --- a/Minecraft.Client/Rendering/GameRenderer.cpp +++ b/Minecraft.Client/Rendering/GameRenderer.cpp @@ -1779,6 +1779,7 @@ void GameRenderer::renderSnowAndRain(float a) { #endif t->offset(0, 0, 0); t->end(); + } else { if (mode != 1) { if (mode >= 0) t->end(); diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 9cf3dfdd3..627a46f47 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -2472,7 +2472,7 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, // 4J-PB - If Display HUD is false, don't render the hit outline if (app.GetGameSettings(iPad, eGameSetting_DisplayHUD) == 0) return; RenderManager.StateSetLightingEnable(false); - RenderManager.StateSetTextureEnable(false); + glDisable(GL_TEXTURE_2D); // draw hit outline RenderManager.StateSetColour(0.0f, 0.0f, 0.0f, 0.4f); @@ -2502,7 +2502,7 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, // restore glDisable(GL_POLYGON_OFFSET_LINE); RenderManager.StateSetColour(1.0f, 1.0f, 1.0f, 1.0f); - RenderManager.StateSetTextureEnable(true); + glEnable(GL_TEXTURE_2D); RenderManager.StateSetLightingEnable(true); } } @@ -2510,7 +2510,7 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, void LevelRenderer::render(AABB* b) { Tesselator* t = Tesselator::getInstance(); RenderManager.StateSetLightingEnable(false); - RenderManager.StateSetTextureEnable(false); + glDisable(GL_TEXTURE_2D); RenderManager.StateSetColour(0.0f, 0.0f, 0.0f, 0.4f); // prevent zfight @@ -2541,7 +2541,7 @@ void LevelRenderer::render(AABB* b) { t->end(); glDisable(GL_POLYGON_OFFSET_LINE); RenderManager.StateSetLightingEnable(true); - RenderManager.StateSetTextureEnable(true); + glEnable(GL_TEXTURE_2D); RenderManager.StateSetColour(1.0f, 1.0f, 1.0f, 1.0f); } From c60a71581d327995595365ae446b27354f70fa01 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 02:48:12 -0500 Subject: [PATCH 35/71] revert ItemRenderer changes --- Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp | 4 ++-- subprojects/4jlibs.wrap | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp index 069de61af..6b36e130e 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp @@ -369,8 +369,8 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, glTranslatef(-2.0f, 3.0f, -3.0f + blitOffset); glScalef(10.0f, 10.0f, 10.0f); glTranslatef(1.0f, 0.5f, 8.0f); - glScalef(1.0f, -1.0f, 1.0f); - glRotatef(30.0f, 1.0f, 0.0f, 0.0f); + glScalef(1.0f, 1.0f, -1.0f); + glRotatef(180.0f + 30.0f, 1.0f, 0.0f, 0.0f); glRotatef(45.0f, 0.0f, 1.0f, 0.0f); #else glTranslatef(x, y, 0.0f); // Translate to screen coords diff --git a/subprojects/4jlibs.wrap b/subprojects/4jlibs.wrap index e9e21334c..ab584a950 100644 --- a/subprojects/4jlibs.wrap +++ b/subprojects/4jlibs.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://github.com/4jcraft/4jlibs.git -revision = main +revision = immediategone-v2 [provide] dependency_names = 4j-render, 4j-input, 4j-profile, 4j-storage From 8dfd9e50448c4fdf56e571e0525734ee20213927 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Sat, 28 Mar 2026 18:57:19 +1100 Subject: [PATCH 36/71] fix(gui): always rebind textures for java font rendering --- Minecraft.Client/Textures/Textures.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index cad870918..7fc2dabcf 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -541,11 +541,11 @@ void Textures::bindTextureLayers(ResourceLocation* resource) { } void Textures::bind(int id) { - if (id != lastBoundId) + // Classic GUI code still performs some raw glBindTexture calls, so this + // path must always rebind rather than trusting lastBoundId to be in sync. { if (id < 0) return; glBindTexture(GL_TEXTURE_2D, id); - lastBoundId = id; } } From 69e9db9edc25689b891d2dbc341aad1b6af70f4e Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 03:06:32 -0500 Subject: [PATCH 37/71] chore: undo some comments --- Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp | 4 ---- Minecraft.Client/Rendering/GameRenderer.cpp | 1 - 2 files changed, 5 deletions(-) diff --git a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp index 6b36e130e..d5925a3ec 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp @@ -358,12 +358,9 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, Tile* tile = Tile::tiles[itemId]; glPushMatrix(); - // 4J - original code left here for reference // 4jcraft: original code reused for proper lighting - // force normalize #if 1 - // does it work? too lazy to find out glTranslatef((float)(x), (float)(y), 0.0f); glScalef(fScaleX, fScaleY, 1.0f); glTranslatef(-2.0f, 3.0f, -3.0f + blitOffset); @@ -396,7 +393,6 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, tileRenderer->renderTile(tile, itemAuxValue, 1, fAlpha, useCompiled); - // glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); glPopMatrix(); PIXEndNamedEvent(); } else if (Item::items[itemId]->hasMultipleSpriteLayers()) { diff --git a/Minecraft.Client/Rendering/GameRenderer.cpp b/Minecraft.Client/Rendering/GameRenderer.cpp index 61171598e..3e5763087 100644 --- a/Minecraft.Client/Rendering/GameRenderer.cpp +++ b/Minecraft.Client/Rendering/GameRenderer.cpp @@ -1779,7 +1779,6 @@ void GameRenderer::renderSnowAndRain(float a) { #endif t->offset(0, 0, 0); t->end(); - } else { if (mode != 1) { if (mode >= 0) t->end(); From fb745767f26579bb35659d8803694658d1030c92 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 03:10:19 -0500 Subject: [PATCH 38/71] undo changes to Camera.cpp --- Minecraft.Client/Rendering/Camera.cpp | 8 +++----- Minecraft.Client/Textures/Textures.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Minecraft.Client/Rendering/Camera.cpp b/Minecraft.Client/Rendering/Camera.cpp index 137108089..aac62892a 100644 --- a/Minecraft.Client/Rendering/Camera.cpp +++ b/Minecraft.Client/Rendering/Camera.cpp @@ -22,11 +22,9 @@ float Camera::xa2 = 0.0f; float Camera::za2 = 0.0f; void Camera::prepare(std::shared_ptr player, bool mirror) { - memcpy(modelview->_getDataPointer(), - RenderManager.MatrixGet(GL_MODELVIEW_MATRIX), 16 * sizeof(float)); - memcpy(projection->_getDataPointer(), - RenderManager.MatrixGet(GL_PROJECTION_MATRIX), 16 * sizeof(float)); - + glGetFloat(GL_MODELVIEW_MATRIX, modelview); + glGetFloat(GL_PROJECTION_MATRIX, projection); + /* Original java code for reference glGetInteger(GL_VIEWPORT, viewport); diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 7fc2dabcf..13c76e46c 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -541,11 +541,13 @@ void Textures::bindTextureLayers(ResourceLocation* resource) { } void Textures::bind(int id) { - // Classic GUI code still performs some raw glBindTexture calls, so this - // path must always rebind rather than trusting lastBoundId to be in sync. + // 4jcraft: Classic GUI code still performs some raw glBindTexture calls, so + // this path must always rebind rather than trusting lastBoundId to be in sync. + // if (id != lastBoundId) { if (id < 0) return; glBindTexture(GL_TEXTURE_2D, id); + // lastBoundId = id; } } From fc6e4e32b14eab4913458d1e21357fb69a7f11f7 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 03:17:41 -0500 Subject: [PATCH 39/71] undo further LevelRenderer changes --- Minecraft.Client/Rendering/LevelRenderer.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 627a46f47..47702a12a 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -2308,10 +2308,7 @@ bool LevelRenderer::updateDirtyChunks() { // static int64_t totalTime = 0; // static int64_t countTime = 0; // int64_t startTime = System::currentTimeMillis(); - Tesselator::getInstance()->offset( - -permaChunk[index].x, -permaChunk[index].y, -permaChunk[index].z); permaChunk.rebuild(); - Tesselator::getInstance()->offset(0, 0, 0); // int64_t endTime = System::currentTimeMillis(); // totalTime += (endTime - startTime); // countTime++; @@ -4060,8 +4057,10 @@ int LevelRenderer::rebuildChunkThreadProc(void* lpParam) { while (true) { s_activationEventA[index]->WaitForSignal(INFINITE); + // app.DebugPrintf("Rebuilding permaChunk %d\n", index + 1); permaChunk[index + 1].rebuild(); + // Inform the producer thread that we are done with this chunk s_rebuildCompleteEvents->Set(index); } From 485edf06798b8783a7bcd97f4fc82cd061338fc6 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 03:22:37 -0500 Subject: [PATCH 40/71] clean up the diff further --- Minecraft.Client/Rendering/Camera.cpp | 2 +- Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Minecraft.Client/Rendering/Camera.cpp b/Minecraft.Client/Rendering/Camera.cpp index aac62892a..f728f3f09 100644 --- a/Minecraft.Client/Rendering/Camera.cpp +++ b/Minecraft.Client/Rendering/Camera.cpp @@ -24,7 +24,7 @@ float Camera::za2 = 0.0f; void Camera::prepare(std::shared_ptr player, bool mirror) { glGetFloat(GL_MODELVIEW_MATRIX, modelview); glGetFloat(GL_PROJECTION_MATRIX, projection); - + /* Original java code for reference glGetInteger(GL_VIEWPORT, viewport); diff --git a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp index d5925a3ec..2b3faece9 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp @@ -389,7 +389,6 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, // render has the top surface coloured differently to // the rest of the block glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); - // glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); tileRenderer->renderTile(tile, itemAuxValue, 1, fAlpha, useCompiled); From d53831186e3fda37f7d4c2c5e756b7512fa0b682 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Sat, 28 Mar 2026 20:22:26 +1100 Subject: [PATCH 41/71] fix(linux): stop core-profile GL state leakage in iggy title rendering --- .../Platform/Linux/Iggy/gdraw/gdraw.c | 7 +++ .../Platform/Linux/Linux_UIController.cpp | 2 - .../Windows64/Iggy/gdraw/gdraw_gl_shared.inl | 46 +++++++++++++------ Minecraft.Client/Rendering/GameRenderer.cpp | 28 +++++++++++ 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c index fc037dcbf..c56cf237b 100644 --- a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c +++ b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c @@ -278,6 +278,12 @@ static void error_msg_platform_specific(const char* msg) { fprintf(stderr, "[GDraw] %s\n", msg); } +#define GDRAW_PLATFORM_REPORT_GL_SITE(site) \ + do { \ + if ((site) != NULL) \ + fprintf(stderr, "[GDraw] GL error site: %s\n", (site)); \ + } while (0) + #define GDRAW_MULTISAMPLING // i wish i could improve this function @@ -707,6 +713,7 @@ static void RADLINK hooked_RenderingBegin(void) { if (real_RenderingBegin) real_RenderingBegin(); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); + OPENGL_CHECK_SITE("hooked_RenderingBegin:post_state"); } // Creating the context diff --git a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp index e2c1c17c7..4d99f518f 100644 --- a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp @@ -18,14 +18,12 @@ static void restoreFixedFunctionStateAfterIggy() { glClientActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glClientActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glMatrixMode(GL_TEXTURE); glLoadIdentity(); diff --git a/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl b/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl index c22517041..a7a965d19 100644 --- a/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl +++ b/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl @@ -57,6 +57,10 @@ static RADINLINE void break_on_err(GLint e) #endif } +#ifndef GDRAW_PLATFORM_REPORT_GL_SITE +#define GDRAW_PLATFORM_REPORT_GL_SITE(site) ((void)0) +#endif + static void report_err(GLint e) { break_on_err(e); @@ -74,17 +78,31 @@ static void eat_gl_err(void) while (glGetError() != GL_NO_ERROR); } +static void opengl_check_site(const char *site); + static void opengl_check(void) +{ + opengl_check_site(NULL); +} + +static void opengl_check_site(const char *site) { #ifdef _DEBUG GLint e = glGetError(); if (e != GL_NO_ERROR) { + GDRAW_PLATFORM_REPORT_GL_SITE(site); report_err(e); eat_gl_err(); } +#else + (void) site; #endif } +#ifndef OPENGL_CHECK_SITE +#define OPENGL_CHECK_SITE(site) opengl_check_site(site) +#endif + static U32 is_pow2(S32 n) { return ((U32) n & (U32) (n-1)) == 0; @@ -1355,18 +1373,18 @@ static int set_render_state(GDrawRenderState *r, S32 vformat, const int **ovvars } use_lazy_shader(prg); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:use_lazy_shader"); fvars = prg->vars[0]; vvars = prg->vars[1]; if (vformat == GDRAW_vformat_ihud1) { F32 wv[2][4] = { 1.0f/960,0,0,-1.0, 0,-1.0f/540,0,+1.0 }; glUniform4fv(vvars[VAR_ihudv_worldview], 2, wv[0]); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:ihud_worldview"); glUniform4fv(vvars[VAR_ihudv_material], p->uniform_count, p->uniforms); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:ihud_material"); glUniform1f(vvars[VAR_ihudv_textmode], p->drawprim_mode ? 0.0f : 1.0f); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:ihud_textmode"); } else { // set vertex shader constants @@ -1393,7 +1411,7 @@ static int set_render_state(GDrawRenderState *r, S32 vformat, const int **ovvars // texture stuff set_texture(0, r->tex[0]); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:set_texture0"); if (r->tex[0] && gdraw->has_conditional_non_power_of_two && ((GDrawHandle*) r->tex[0])->handle.tex.nonpow2) { // only wrap mode allowed in conditional nonpow2 is clamp; this should @@ -1490,7 +1508,7 @@ static int set_render_state(GDrawRenderState *r, S32 vformat, const int **ovvars else glDepthMask(GL_FALSE); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:final"); if (ovvars) *ovvars = vvars; @@ -1611,7 +1629,7 @@ static void RADLINK gdraw_DrawIndexedTriangles(GDrawRenderState *r, GDrawPrimiti glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - opengl_check(); + OPENGL_CHECK_SITE("gdraw_DrawIndexedTriangles:final"); tag_resources(vb, r->tex[0], r->tex[1]); } @@ -1628,33 +1646,33 @@ static void do_screen_quad(gswf_recti *s, F32 *tc, const int *vvars, GDrawStats F32 vert[4][4]; F32 world[2*4]; - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:begin"); vert[0][0] = px0; vert[0][1] = py0; vert[0][2] = s0; vert[0][3] = t0; vert[1][0] = px1; vert[1][1] = py0; vert[1][2] = s1; vert[1][3] = t0; vert[2][0] = px1; vert[2][1] = py1; vert[2][2] = s1; vert[2][3] = t1; vert[3][0] = px0; vert[3][1] = py1; vert[3][2] = s0; vert[3][3] = t1; - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:after_vertices"); gdraw_PixelSpace(world); world[2] = depth; set_world_projection(vvars, world); - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:after_projection"); set_vertex_format(GDRAW_vformat_v2tc2, vert[0]); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:before_draw"); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); reset_vertex_format(GDRAW_vformat_v2tc2); - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:after_draw"); gstats->nonzero_flags |= GDRAW_STATS_batches; gstats->num_batches += 1; gstats->drawn_vertices += 4; gstats->drawn_indices += 6; - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:final"); } #ifdef GDRAW_FEWER_CLEARS @@ -1805,7 +1823,7 @@ static void RADLINK gdraw_FilterQuad(GDrawRenderState *r, S32 x0, S32 y0, S32 x1 glColorMask(1,1,1,1); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); - opengl_check(); + OPENGL_CHECK_SITE("gdraw_FilterQuad:pre_filter"); if (r->blend_mode == GDRAW_BLEND_filter) { switch (r->filter) { diff --git a/Minecraft.Client/Rendering/GameRenderer.cpp b/Minecraft.Client/Rendering/GameRenderer.cpp index 3e5763087..6a1bbc614 100644 --- a/Minecraft.Client/Rendering/GameRenderer.cpp +++ b/Minecraft.Client/Rendering/GameRenderer.cpp @@ -1845,6 +1845,34 @@ void GameRenderer::setupGuiScreen(int forceScale /*=-1*/) { // 4jcraft: use actual framebuffer dimensions instead of mc->width/height // to ensure GUI scales correctly after a window resize. ScreenSizeCalculator ssc(mc->options, fbw, fbh, forceScale); + + // 4jcraft: Java GUI screens still assume a clean 2D fixed-function style state. + RenderManager.StateSetFaceCull(false); + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + glColor4f(1, 1, 1, 1); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.1f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(true); + + RenderManager.TextureBindVertex(-1); + + glClientActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + glClientActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_2D); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); From 35d92fe504b15ce4ffbeb06e6e8bfc34f6efae2e Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 04:25:24 -0500 Subject: [PATCH 42/71] docs: add FIXME for clipChunk translation --- Minecraft.Client/Rendering/LevelRenderer.cpp | 47 +++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 47702a12a..ad1c0642e 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -858,6 +858,9 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { glPushMatrix(); + // 4jcraft: added this. On OpenGL 2.1 this breaks, but the 3.3 renderer + // requires it. + // FIXME: find a way to get rid of this. glTranslatef((float)pClipChunk->chunk->x, (float)pClipChunk->chunk->y, (float)pClipChunk->chunk->z); // List can be calculated directly from the chunk's global idex @@ -2470,11 +2473,11 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, if (app.GetGameSettings(iPad, eGameSetting_DisplayHUD) == 0) return; RenderManager.StateSetLightingEnable(false); glDisable(GL_TEXTURE_2D); - + // draw hit outline RenderManager.StateSetColour(0.0f, 0.0f, 0.0f, 0.4f); RenderManager.StateSetLineWidth(1.0f); - + // hack glDepthFunc(GL_LEQUAL); glEnable(GL_POLYGON_OFFSET_LINE); @@ -2515,25 +2518,37 @@ void LevelRenderer::render(AABB* b) { glPolygonOffset(-2.0f, -2.0f); // One call please! - t->begin(GL_LINES); + t->begin(GL_LINES); // Bottom - t->vertex(b->x0, b->y0, b->z0); t->vertex(b->x1, b->y0, b->z0); - t->vertex(b->x1, b->y0, b->z0); t->vertex(b->x1, b->y0, b->z1); - t->vertex(b->x1, b->y0, b->z1); t->vertex(b->x0, b->y0, b->z1); - t->vertex(b->x0, b->y0, b->z1); t->vertex(b->x0, b->y0, b->z0); + t->vertex(b->x0, b->y0, b->z0); + t->vertex(b->x1, b->y0, b->z0); + t->vertex(b->x1, b->y0, b->z0); + t->vertex(b->x1, b->y0, b->z1); + t->vertex(b->x1, b->y0, b->z1); + t->vertex(b->x0, b->y0, b->z1); + t->vertex(b->x0, b->y0, b->z1); + t->vertex(b->x0, b->y0, b->z0); // Top - t->vertex(b->x0, b->y1, b->z0); t->vertex(b->x1, b->y1, b->z0); - t->vertex(b->x1, b->y1, b->z0); t->vertex(b->x1, b->y1, b->z1); - t->vertex(b->x1, b->y1, b->z1); t->vertex(b->x0, b->y1, b->z1); - t->vertex(b->x0, b->y1, b->z1); t->vertex(b->x0, b->y1, b->z0); + t->vertex(b->x0, b->y1, b->z0); + t->vertex(b->x1, b->y1, b->z0); + t->vertex(b->x1, b->y1, b->z0); + t->vertex(b->x1, b->y1, b->z1); + t->vertex(b->x1, b->y1, b->z1); + t->vertex(b->x0, b->y1, b->z1); + t->vertex(b->x0, b->y1, b->z1); + t->vertex(b->x0, b->y1, b->z0); // Vertical - t->vertex(b->x0, b->y0, b->z0); t->vertex(b->x0, b->y1, b->z0); - t->vertex(b->x1, b->y0, b->z0); t->vertex(b->x1, b->y1, b->z0); - t->vertex(b->x1, b->y0, b->z1); t->vertex(b->x1, b->y1, b->z1); - t->vertex(b->x0, b->y0, b->z1); t->vertex(b->x0, b->y1, b->z1); + t->vertex(b->x0, b->y0, b->z0); + t->vertex(b->x0, b->y1, b->z0); + t->vertex(b->x1, b->y0, b->z0); + t->vertex(b->x1, b->y1, b->z0); + t->vertex(b->x1, b->y0, b->z1); + t->vertex(b->x1, b->y1, b->z1); + t->vertex(b->x0, b->y0, b->z1); + t->vertex(b->x0, b->y1, b->z1); t->end(); glDisable(GL_POLYGON_OFFSET_LINE); @@ -4060,7 +4075,7 @@ int LevelRenderer::rebuildChunkThreadProc(void* lpParam) { // app.DebugPrintf("Rebuilding permaChunk %d\n", index + 1); permaChunk[index + 1].rebuild(); - + // Inform the producer thread that we are done with this chunk s_rebuildCompleteEvents->Set(index); } From dc8c0b04c038a67110f087ace42e136e4b30f0dd Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 11:25:58 +0100 Subject: [PATCH 43/71] apply setchuckoffset to direct shader matrix --- Minecraft.Client/Rendering/LevelRenderer.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index ad1c0642e..7990e1737 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -856,24 +856,22 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == emptyFlag) continue; // Check that this particular layer isn't empty - glPushMatrix(); - - // 4jcraft: added this. On OpenGL 2.1 this breaks, but the 3.3 renderer - // requires it. - // FIXME: find a way to get rid of this. - glTranslatef((float)pClipChunk->chunk->x, (float)pClipChunk->chunk->y, - (float)pClipChunk->chunk->z); // List can be calculated directly from the chunk's global idex int list = pClipChunk->globalIdx * 2 + layer; list += chunkLists; + // 4jcraft: replaced glPushMatrix/glTranslatef/glPopMatrix per chunk + // no more full MVP upload per chunk, can also be bkwards compat + RenderManager.SetChunkOffset((float)pClipChunk->chunk->x, + (float)pClipChunk->chunk->y, + (float)pClipChunk->chunk->z); + if (RenderManager.CBuffCall(list, first)) { first = false; } - - glPopMatrix(); count++; } + RenderManager.SetChunkOffset(0.f, 0.f, 0.f); #ifdef __PSVITA__ // AP - alpha cut out is expensive on vita. Now we render all the alpha cut From 31acc463089db730d2160e585dc4d715d4bb24c1 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Sat, 28 Mar 2026 21:09:35 +1100 Subject: [PATCH 44/71] fix(linux): localise chunk transforms and guard optional iggy paths --- .../Platform/Common/UI/UIControl.cpp | 50 +++++++++++++++---- .../Platform/Common/UI/UIControl.h | 2 + .../Platform/Common/UI/UIController.cpp | 11 +++- .../Platform/Common/UI/UIScene.cpp | 12 +++++ Minecraft.Client/Platform/Common/UI/UIScene.h | 1 + Minecraft.Client/Platform/Linux/LinuxGL.cpp | 7 +-- Minecraft.Client/Rendering/Chunk.cpp | 25 ---------- Minecraft.Client/Textures/Textures.cpp | 3 ++ 8 files changed, 71 insertions(+), 40 deletions(-) diff --git a/Minecraft.Client/Platform/Common/UI/UIControl.cpp b/Minecraft.Client/Platform/Common/UI/UIControl.cpp index 234d9bb99..9a9426be3 100644 --- a/Minecraft.Client/Platform/Common/UI/UIControl.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIControl.cpp @@ -10,7 +10,12 @@ UIControl::UIControl() { m_controlName = ""; m_isVisible = true; m_bHidden = false; + m_isValid = false; m_eControlType = eNoControl; + m_x = 0; + m_y = 0; + m_width = 0; + m_height = 0; } bool UIControl::setupControl(UIScene* scene, IggyValuePath* parent, @@ -20,6 +25,7 @@ bool UIControl::setupControl(UIScene* scene, IggyValuePath* parent, rrbool res = IggyValuePathMakeNameRef(&m_iggyPath, parent, controlName.c_str()); + m_isValid = res ? true : false; m_nameXPos = registerFastName(L"x"); m_nameYPos = registerFastName(L"y"); @@ -28,16 +34,32 @@ bool UIControl::setupControl(UIScene* scene, IggyValuePath* parent, m_funcSetAlpha = registerFastName(L"SetControlAlpha"); m_nameVisible = registerFastName(L"visible"); - F64 fx, fy, fwidth, fheight; - IggyValueGetF64RS(getIggyValuePath(), m_nameXPos, NULL, &fx); - IggyValueGetF64RS(getIggyValuePath(), m_nameYPos, NULL, &fy); - IggyValueGetF64RS(getIggyValuePath(), m_nameWidth, NULL, &fwidth); - IggyValueGetF64RS(getIggyValuePath(), m_nameHeight, NULL, &fheight); + if (m_isValid) { + IggyDatatype controlType = IGGY_DATATYPE__invalid_request; + IggyResult typeResult = + IggyValueGetTypeRS(getIggyValuePath(), 0, NULL, &controlType); + m_isValid = typeResult == IGGY_RESULT_SUCCESS && + controlType != IGGY_DATATYPE__invalid_request && + controlType != IGGY_DATATYPE_undefined; + } - m_x = (S32)fx; - m_y = (S32)fy; - m_width = (S32)Math::round(fwidth); - m_height = (S32)Math::round(fheight); + if (m_isValid) { + F64 fx, fy, fwidth, fheight; + IggyValueGetF64RS(getIggyValuePath(), m_nameXPos, NULL, &fx); + IggyValueGetF64RS(getIggyValuePath(), m_nameYPos, NULL, &fy); + IggyValueGetF64RS(getIggyValuePath(), m_nameWidth, NULL, &fwidth); + IggyValueGetF64RS(getIggyValuePath(), m_nameHeight, NULL, &fheight); + + m_x = (S32)fx; + m_y = (S32)fy; + m_width = (S32)Math::round(fwidth); + m_height = (S32)Math::round(fheight); + } else { + m_x = 0; + m_y = 0; + m_width = 0; + m_height = 0; + } return res; } @@ -57,6 +79,8 @@ void UIControl::UpdateControl() { #endif // __PSVITA__ void UIControl::ReInit() { + if (!m_isValid) return; + if (m_lastOpacity != 1.0f) { IggyDataValue result; IggyDataValue value[2]; @@ -91,6 +115,7 @@ S32 UIControl::getHeight() { return m_height; } void UIControl::setOpacity(float percent) { if (percent != m_lastOpacity) { m_lastOpacity = percent; + if (!m_isValid) return; IggyDataValue result; IggyDataValue value[2]; @@ -112,6 +137,11 @@ void UIControl::setOpacity(float percent) { void UIControl::setVisible(bool visible) { if (visible != m_isVisible) { + if (!m_isValid) { + m_isVisible = visible; + return; + } + rrbool succ = IggyValueSetBooleanRS(getIggyValuePath(), m_nameVisible, NULL, visible); if (succ) @@ -122,6 +152,8 @@ void UIControl::setVisible(bool visible) { } bool UIControl::getVisible() { + if (!m_isValid) return m_isVisible; + rrbool bVisible = false; IggyResult result = IggyValueGetBooleanRS(getIggyValuePath(), m_nameVisible, diff --git a/Minecraft.Client/Platform/Common/UI/UIControl.h b/Minecraft.Client/Platform/Common/UI/UIControl.h index 19cdb9ad4..807daa5dd 100644 --- a/Minecraft.Client/Platform/Common/UI/UIControl.h +++ b/Minecraft.Client/Platform/Common/UI/UIControl.h @@ -34,6 +34,7 @@ protected: eUIControlType m_eControlType; int m_id; bool m_bHidden; // set by the Remove call + bool m_isValid; public: void setControlType(eUIControlType eType) { m_eControlType = eType; } @@ -83,6 +84,7 @@ public: void setVisible(bool visible); bool getVisible(); bool isVisible() { return m_isVisible; } + bool isValid() { return m_isValid; } virtual bool hasFocus() { return false; } diff --git a/Minecraft.Client/Platform/Common/UI/UIController.cpp b/Minecraft.Client/Platform/Common/UI/UIController.cpp index 912e827df..3807ec7e0 100644 --- a/Minecraft.Client/Platform/Common/UI/UIController.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIController.cpp @@ -77,6 +77,15 @@ static void RADLINK WarningCallback(void* user_callback_data, Iggy* player, // IGGY_RESULT_Error_UndefinedEntity = 504, // IGGY_RESULT_Error_OutOfMemory = 1001,}; + if (message != NULL) { + // Some Linux movie variants do not ship these optional hooks/controls. + // We guard the call sites, so drop the residual Iggy warning noise. + if (strstr(message, "LabelGamertag") != NULL || + strstr(message, "Method SetSafeZone was not a function") != NULL) { + return; + } + } + switch (code) { case IGGY_RESULT_Warning_CannotSustainFrameRate: // Ignore warning @@ -3233,4 +3242,4 @@ void UIController::SendTouchInput(unsigned int iPad, unsigned int key, } } -#endif \ No newline at end of file +#endif diff --git a/Minecraft.Client/Platform/Common/UI/UIScene.cpp b/Minecraft.Client/Platform/Common/UI/UIScene.cpp index 863e0f951..d46b97b37 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIScene.cpp @@ -19,6 +19,7 @@ UIScene::UIScene(int iPad, UILayer* parentLayer) { m_bVisible = true; m_bCanHandleInput = false; m_bIsReloading = false; + m_hasSetSafeZoneMethod = false; m_iFocusControl = -1; m_iFocusChild = 0; @@ -53,6 +54,7 @@ void UIScene::destroyMovie() { /* Destroy the Iggy player. */ IggyPlayerDestroy(swf); swf = NULL; + m_hasSetSafeZoneMethod = false; // Clear out the controls collection (doesn't delete the controls, and they // get re-setup later) @@ -76,6 +78,7 @@ void UIScene::reloadMovie(bool force) { // Clear out the controls collection (doesn't delete the controls, and // they get re-setup later) m_controls.clear(); + m_hasSetSafeZoneMethod = false; // Clear out all the fast names for the current movie m_fastNames.clear(); @@ -194,6 +197,8 @@ void UIScene::updateSafeZone() { void UIScene::setSafeZone(S32 safeTop, S32 safeBottom, S32 safeLeft, S32 safeRight) { + if (!m_hasSetSafeZoneMethod) return; + IggyDataValue result; IggyDataValue value[4]; @@ -251,6 +256,13 @@ bool UIScene::mapElementsAndNames() { m_funcSetAlpha = registerFastName(L"SetAlpha"); m_funcSetFocus = registerFastName(L"SetFocus"); m_funcHorizontalResizeCheck = registerFastName(L"DoHorizontalResizeCheck"); + + IggyDatatype safeZoneType = IGGY_DATATYPE__invalid_request; + IggyResult safeZoneResult = IggyValueGetTypeRS( + m_rootPath, m_funcSetSafeZone, NULL, &safeZoneType); + m_hasSetSafeZoneMethod = + safeZoneResult == IGGY_RESULT_SUCCESS && + safeZoneType == IGGY_DATATYPE_function; return true; } diff --git a/Minecraft.Client/Platform/Common/UI/UIScene.h b/Minecraft.Client/Platform/Common/UI/UIScene.h index b4948bfce..e7d746865 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene.h +++ b/Minecraft.Client/Platform/Common/UI/UIScene.h @@ -72,6 +72,7 @@ private: bool m_bUpdateOpacity; bool m_bVisible; bool m_bCanHandleInput; + bool m_hasSetSafeZoneMethod; UIScene* m_backScene; size_t m_callbackUniqueId; diff --git a/Minecraft.Client/Platform/Linux/LinuxGL.cpp b/Minecraft.Client/Platform/Linux/LinuxGL.cpp index a1d992ee3..a6ccce7cc 100644 --- a/Minecraft.Client/Platform/Linux/LinuxGL.cpp +++ b/Minecraft.Client/Platform/Linux/LinuxGL.cpp @@ -219,14 +219,11 @@ void LinuxGLLogLightmapState(const char* stage, int textureId, ::glActiveTexture(GL_TEXTURE1); GLint unit1Binding = 0; ::glGetIntegerv(GL_TEXTURE_BINDING_2D, &unit1Binding); - const bool unit1Enabled = (::glIsEnabled(GL_TEXTURE_2D) == GL_TRUE); ::glActiveTexture(restoreTexture); app.DebugPrintf( - "[linux-lightmap] %s tex=%d scale=%d active=%#x unit1Bound=%d " - "unit1Enabled=%d\n", - stage, textureId, scaleLight ? 1 : 0, activeTexture, unit1Binding, - unit1Enabled ? 1 : 0); + "[linux-lightmap] %s tex=%d scale=%d active=%#x unit1Bound=%d\n", + stage, textureId, scaleLight ? 1 : 0, activeTexture, unit1Binding); } #endif diff --git a/Minecraft.Client/Rendering/Chunk.cpp b/Minecraft.Client/Rendering/Chunk.cpp index a52ac7459..4df99432b 100644 --- a/Minecraft.Client/Rendering/Chunk.cpp +++ b/Minecraft.Client/Rendering/Chunk.cpp @@ -410,20 +410,8 @@ void Chunk::rebuild() { MemSect(31); glNewList(lists + currentLayer, GL_COMPILE); MemSect(0); - glPushMatrix(); glDepthMask(true); // 4J added t->useCompactVertices(true); // 4J added - translateToPos(); - float ss = 1.000001f; - // 4J - have removed this scale as I don't think we - // should need it, and have now optimised the vertex - // shader so it doesn't do anything other than - // translate with this matrix anyway -#if 0 - glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); - glScalef(ss, ss, ss); - glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); -#endif t->begin(); t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); @@ -471,7 +459,6 @@ void Chunk::rebuild() { #endif t->end(); bounds.addBounds(t->bounds); // 4J MGH - added - glPopMatrix(); glEndList(); t->useCompactVertices(false); // 4J added t->offset(0, 0, 0); @@ -734,19 +721,8 @@ void Chunk::rebuild_SPU() { { glNewList(lists + currentLayer, GL_COMPILE); MemSect(0); - glPushMatrix(); glDepthMask(true); // 4J added t->useCompactVertices(true); // 4J added - translateToPos(); - float ss = 1.000001f; - // 4J - have removed this scale as I don't think we should need it, - // and have now optimised the vertex shader so it doesn't do - // anything other than translate with this matrix anyway -#if 0 - glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); - glScalef(ss, ss, ss); - glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); -#endif t->begin(); t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); } @@ -806,7 +782,6 @@ void Chunk::rebuild_SPU() { { t->end(); bounds.addBounds(t->bounds); - glPopMatrix(); glEndList(); t->useCompactVertices(false); // 4J added t->offset(0, 0, 0); diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 13c76e46c..a9927a1bf 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -543,6 +543,9 @@ void Textures::bindTextureLayers(ResourceLocation* resource) { void Textures::bind(int id) { // 4jcraft: Classic GUI code still performs some raw glBindTexture calls, so // this path must always rebind rather than trusting lastBoundId to be in sync. + // TODO(4jcraft): Long term, route all texture binds through one synchronized + // path or invalidate lastBoundId at every raw glBindTexture call so this can + // safely use cached binds again without breaking font/UI rendering. // if (id != lastBoundId) { if (id < 0) return; From e2a6c3f92cc877b8ad270e56dc0d79542c2fc2bd Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 13:58:50 +0100 Subject: [PATCH 45/71] ss not here --- Minecraft.Client/Rendering/LevelRenderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 7990e1737..911bc5aa7 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -2467,6 +2467,8 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, if (mode == 0 && h->type == HitResult::TILE) { int iPad = mc->player->GetXboxPad(); // 4J added + const float ss = 0.002f; + // 4J-PB - If Display HUD is false, don't render the hit outline if (app.GetGameSettings(iPad, eGameSetting_DisplayHUD) == 0) return; RenderManager.StateSetLightingEnable(false); From 4aa8106e52a35037b9d9498379c94aa0e108b1d7 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 15:21:52 +0100 Subject: [PATCH 46/71] rewrote & formatted meson, also added gles to renderer, no need to touch, it works, and it'll help for future support. basically a neat lil touch --- Minecraft.Client/Platform/Linux/LinuxGL.cpp | 67 +++++++------ Minecraft.Client/meson.build | 103 ++++++++++---------- meson.build | 66 +++++++++---- meson.options | 7 ++ 4 files changed, 146 insertions(+), 97 deletions(-) diff --git a/Minecraft.Client/Platform/Linux/LinuxGL.cpp b/Minecraft.Client/Platform/Linux/LinuxGL.cpp index a6ccce7cc..4b41d2961 100644 --- a/Minecraft.Client/Platform/Linux/LinuxGL.cpp +++ b/Minecraft.Client/Platform/Linux/LinuxGL.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "../../Minecraft.World/IO/Streams/IntBuffer.h" #include "../../Minecraft.World/IO/Streams/FloatBuffer.h" @@ -27,7 +28,15 @@ #undef glReadPixels #undef glActiveTexture -// _4j suffix shit (todo: make ts better) +// Helper functions & stuff +inline GLuint* getIntPtr(IntBuffer* buf) { + return buf ? (GLuint*)((int*)buf->getBuffer() + buf->position()) : nullptr; +} +inline GLvoid* getBytePtr(ByteBuffer* buf) { + return buf ? (GLvoid*)((char*)buf->getBuffer() + buf->position()) : nullptr; +} + +// _4j suffix shit int glGenTextures() { GLuint id = 0; ::glGenTextures(1, &id); @@ -35,10 +44,11 @@ int glGenTextures() { } void glGenTextures_4J(IntBuffer* buf) { - GLuint id = 0; - ::glGenTextures(1, &id); - buf->put((int)id); - buf->flip(); + if (!buf) return; + int n = buf->limit() - buf->position(); + if (n > 0) { + ::glGenTextures(n, getIntPtr(buf)); + } } void glDeleteTextures(int id) { @@ -47,10 +57,10 @@ void glDeleteTextures(int id) { } void glDeleteTextures_4J(IntBuffer* buf) { - if (buf && buf->limit() > 0) { - int id = buf->get(0); - GLuint uid = (GLuint)id; - ::glDeleteTextures(1, &uid); + if (!buf) return; + int n = buf->limit() - buf->position(); + if (n > 0) { + ::glDeleteTextures(n, getIntPtr(buf)); } } @@ -79,13 +89,13 @@ void glTexCoordPointer_4J(int size, int type, FloatBuffer* pointer) { } void glNormalPointer_4J(int type, ByteBuffer* pointer) { - ::glNormalPointer((GLenum)type, 0, pointer->getBuffer()); + ::glNormalPointer((GLenum)type, 0, getBytePtr(pointer)); } void glColorPointer_4J(int size, bool normalized, int stride, ByteBuffer* pointer) { (void)normalized; - ::glColorPointer(size, GL_UNSIGNED_BYTE, stride, pointer->getBuffer()); + ::glColorPointer(size, GL_UNSIGNED_BYTE, stride, getBytePtr(pointer)); } void glVertexPointer_4J(int size, int type, FloatBuffer* pointer) { @@ -100,20 +110,22 @@ void glEndList_4J(int dummy) { void glTexImage2D_4J(int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer* pixels) { - void* data = pixels ? pixels->getBuffer() : nullptr; ::glTexImage2D((GLenum)target, level, internalformat, width, height, border, - (GLenum)format, (GLenum)type, data); + (GLenum)format, (GLenum)type, getBytePtr(pixels)); } void glCallLists_4J(IntBuffer* lists) { + if (!lists) return; int count = lists->limit() - lists->position(); - ::glCallLists(count, GL_INT, lists->getBuffer()); + if (count > 0) { + ::glCallLists(count, GL_INT, getIntPtr(lists)); + } } void glReadPixels_4J(int x, int y, int width, int height, int format, int type, ByteBuffer* pixels) { ::glReadPixels(x, y, width, height, (GLenum)format, (GLenum)type, - pixels->getBuffer()); + getBytePtr(pixels)); } void glGetFloat(int pname, FloatBuffer* params) { @@ -173,11 +185,11 @@ static void initQueryFuncs() { void glGenQueriesARB_4J(IntBuffer* buf) { initQueryFuncs(); - if (_glGenQueriesARB) { - GLuint id = 0; - _glGenQueriesARB(1, &id); - buf->put((int)id); - buf->flip(); + if (_glGenQueriesARB && buf) { + int n = buf->limit() - buf->position(); + if (n > 0) { + _glGenQueriesARB(n, getIntPtr(buf)); + } } } void glGenQueriesARB(IntBuffer* buf) { glGenQueriesARB_4J(buf); } @@ -196,11 +208,10 @@ void glEndQueryARB(int target) { glEndQueryARB_4J(target); } void glGetQueryObjectuARB_4J(int id, int pname, IntBuffer* params) { initQueryFuncs(); - if (_glGetQueryObjectuivARB) { - GLuint val = 0; - _glGetQueryObjectuivARB((GLuint)id, (GLenum)pname, &val); - params->put((int)val); - params->flip(); + if (_glGetQueryObjectuivARB && params) { + // LWJGL does not change limits/positions during these calls, it + // reads/writes exactly at pointer!! + _glGetQueryObjectuivARB((GLuint)id, (GLenum)pname, getIntPtr(params)); } } void glGetQueryObjectuARB(int id, int pname, IntBuffer* params) { @@ -222,8 +233,8 @@ void LinuxGLLogLightmapState(const char* stage, int textureId, ::glActiveTexture(restoreTexture); app.DebugPrintf( - "[linux-lightmap] %s tex=%d scale=%d active=%#x unit1Bound=%d\n", - stage, textureId, scaleLight ? 1 : 0, activeTexture, unit1Binding); + "[linux-lightmap] %s tex=%d scale=%d active=%#x unit1Bound=%d\n", stage, + textureId, scaleLight ? 1 : 0, activeTexture, unit1Binding); } -#endif +#endif \ No newline at end of file diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index 9bb614fc4..f31407b0d 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -8,21 +8,21 @@ exclude_sources = [ # all sources except ./Platform/* client_sources = run_command( - 'sh', '-c', - 'find "' - + meson.current_source_dir() - + '" \\( -name "*.cpp" -o -name "*.c" \\)' - + ' '.join(exclude_sources), - check : true, + 'sh', + '-c', 'find "' + + meson.current_source_dir() + + '" \\( -name "*.cpp" -o -name "*.c" \\)' + + ' '.join(exclude_sources), + check: true, ).stdout().strip().split('\n') # all sources in ./Platform (top-level files only) platform_sources = run_command( - 'sh', '-c', - 'find "' - + meson.current_source_dir() / 'Platform' - + '" -maxdepth 1 \\( -name "*.cpp" -o -name "*.c" \\)', - check : true, + 'sh', + '-c', 'find "' + + meson.current_source_dir() / 'Platform' + + '" -maxdepth 1 \\( -name "*.cpp" -o -name "*.c" \\)', + check: true, ).stdout().strip().split('\n') # some platform-specific sources that are for some stupid reason in Common @@ -32,47 +32,47 @@ exclude_platform_common_sources = [ # we use system zlib instead, since this one is old as hell and isn't configured for linux correctly ' ! -path "*/zlib/*"', ' ! -name "SonyLeaderboardManager.cpp"', - ' ! -name "UIScene_InGameSaveManagementMenu.cpp"' + ' ! -name "UIScene_InGameSaveManagementMenu.cpp"', ] # all sources in in ./Platform/Common platform_sources += run_command( - 'sh', '-c', - 'find "' - + meson.current_source_dir() / 'Platform/Common' - + '" \\( -name "*.cpp" -o -name "*.c" \\)' - + ' '.join(exclude_platform_common_sources), - check : true, + 'sh', + '-c', 'find "' + + meson.current_source_dir() / 'Platform/Common' + + '" \\( -name "*.cpp" -o -name "*.c" \\)' + + ' '.join(exclude_platform_common_sources), + check: true, ).stdout().strip().split('\n') # linux-specific files (everything in Platform/Linux) if host_machine.system() == 'linux' platform_sources += run_command( - 'sh', '-c', - 'find "' - + meson.current_source_dir() / 'Platform/Linux' - + '" \\( -name "*.cpp" -o -name "*.c" \\) ', - check : true, + 'sh', + '-c', 'find "' + + meson.current_source_dir() / 'Platform/Linux' + + '" \\( -name "*.cpp" -o -name "*.c" \\) ', + check: true, ).stdout().strip().split('\n') endif client_dependencies = [ - render_dep, - input_dep, - profile_dep, - storage_dep, - assets_localisation_dep, - world_dep, - gl_dep, - glu_dep, - thread_dep, - thread_dep, - dependency('zlib'), - miniaudio_dep + render_dep, + input_dep, + profile_dep, + storage_dep, + assets_localisation_dep, + world_dep, + gl_dep, + glu_dep, + thread_dep, + dl_dep, + dependency('zlib'), + miniaudio_dep, ] if get_option('enable_vsync') - global_cpp_defs += '-DENABLE_VSYNC' + global_cpp_defs += ['-DENABLE_VSYNC'] endif if get_option('classic_panorama') @@ -85,8 +85,8 @@ if get_option('ui_backend') == 'shiggy' fallback : ['shiggy', 'shiggy_dep'], ) - global_cpp_defs += '-D_ENABLEIGGY' - client_dependencies += shiggy_dep + global_cpp_defs += ['-D_ENABLEIGGY'] + client_dependencies += shiggy_dep endif if get_option('ui_backend') == 'java' @@ -95,25 +95,30 @@ endif client = executable('Minecraft.Client', client_sources + platform_sources + localisation[1], - include_directories : [include_directories('Platform', 'Platform/Linux/Iggy/include'),stb], - dependencies : client_dependencies, - cpp_args : global_cpp_args + global_cpp_defs + [ - '-DUNICODE', '-D_UNICODE', + include_directories: [include_directories('Platform', 'Platform/Linux/Iggy/include'), stb], + dependencies: client_dependencies, + cpp_args: global_cpp_args + + global_cpp_defs + + [ + '-DUNICODE', + '-D_UNICODE', '-include', meson.current_source_dir() / 'Platform/stdafx.h', ], - c_args : global_cpp_defs + ['-DUNICODE', '-D_UNICODE'], - install : true, - install_dir : '' + c_args: global_cpp_defs + ['-DUNICODE', '-D_UNICODE'], + install: true, + install_dir: '', ) # To support actually running the client from the build folder, we need to # copy the generated assets from Minecraft.Assets into the working directory # of the client. -custom_target('copy_assets_to_client', +custom_target( + 'copy_assets_to_client', input: [client, media_archive], output: 'assets.stamp', # using a stamp file to avoid copying assets every time - command : [ - python, meson.project_source_root() / 'scripts/copy_assets_to_client.py', + command: [ + python, + meson.project_source_root() / 'scripts/copy_assets_to_client.py', meson.project_source_root(), meson.project_build_root(), meson.current_build_dir(), @@ -121,4 +126,4 @@ custom_target('copy_assets_to_client', '@OUTPUT@', ], build_by_default: true, -) +) \ No newline at end of file diff --git a/meson.build b/meson.build index cd2b7daa8..7d08e7faf 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,9 @@ -project('4jcraft-chucklegrounds', ['cpp', 'c'], - version : '0.1.0', +project( + '4jcraft', + ['cpp', 'c'], + version: '0.1.0', meson_version: '>= 1.7', - default_options : [ + default_options: [ 'cpp_std=c++23', 'warning_level=0', 'buildtype=debug', # for now @@ -16,22 +18,6 @@ python = pymod.find_installation('python3', required: true) cc = meson.get_compiler('cpp') -# system deps -gl_dep = dependency('gl') -glu_dep = dependency('glu') -sdl2_dep = dependency('sdl2') # Yes.. i know sdl3 is out, but there's not point upgrading right now except when - # someone is gonna ask me "Hey juicey can you make it SDL3?" and i'd be like fuck you and still do it. -thread_dep = dependency('threads') -miniaudio_dep = dependency('miniaudio') -stb = subproject('stb').get_variable('stb_inc') -# compile flags (chagne ts juicey) -global_cpp_args = [ - '-fpermissive', - '-Wshift-count-overflow', - '-pipe', # use pipes instead of temp files between compiler stages -] - -# global ccp defs type shi global_cpp_defs = [ '-DSPLIT_SAVES', '-D_LARGE_WORLDS', @@ -49,11 +35,51 @@ if host_machine.system() == 'linux' ] endif +add_project_arguments(global_cpp_defs, language: ['cpp', 'c']) + +global_cpp_args = [ + '-fpermissive', + '-Wshift-count-overflow', + '-pipe', +] +add_project_arguments(global_cpp_args, language: 'cpp') + +sdl2_dep = dependency('sdl2') +thread_dep = dependency('threads') +dl_dep = cc.find_library('dl', required: true) + +if '-DGLES' in global_cpp_defs + gl_dep = dependency('glesv2') + glu_dep = dependency('', required: false) +else + gl_dep = dependency('gl') + glu_dep = dependency('glu') +endif + +stb = subproject('stb').get_variable('stb_inc') +stb_dep = declare_dependency(include_directories: stb) + +miniaudio_dep = dependency('miniaudio') + render_dep = dependency('4j-render', fallback: ['4jlibs', 'render_dep']) input_dep = dependency('4j-input', fallback: ['4jlibs', 'input_dep']) profile_dep = dependency('4j-profile', fallback: ['4jlibs', 'profile_dep']) storage_dep = dependency('4j-storage', fallback: ['4jlibs', 'storage_dep']) +all_deps = [ + gl_dep, + glu_dep, + sdl2_dep, + thread_dep, + dl_dep, + stb_dep, + miniaudio_dep, + render_dep, + input_dep, + profile_dep, + storage_dep, +] + subdir('Minecraft.Assets') subdir('Minecraft.World') -subdir('Minecraft.Client') +subdir('Minecraft.Client') \ No newline at end of file diff --git a/meson.options b/meson.options index 68eb92598..1121b453a 100644 --- a/meson.options +++ b/meson.options @@ -13,3 +13,10 @@ option('enable_vsync', type : 'boolean', value : true, description : 'Toggles V-Sync and adds options to unlock maximum in-game framerate.') + +option( + 'gles', + type: 'boolean', + value: true, + description: 'Use OpenGL ES instead of Desktop GL', +) From a2ec6610a5d21cc900a2412c1fa1e673617bf625 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 15:22:42 +0100 Subject: [PATCH 47/71] mistake while commit, gles was turned on by default. --- meson.options | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.options b/meson.options index 1121b453a..6f4f4808b 100644 --- a/meson.options +++ b/meson.options @@ -17,6 +17,6 @@ option('enable_vsync', option( 'gles', type: 'boolean', - value: true, + value: false, description: 'Use OpenGL ES instead of Desktop GL', ) From d2e77c63ad2db763edae7f86b81da34c35de63e8 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 17:04:39 +0100 Subject: [PATCH 48/71] refactor meson and final gles support, cleaner gdraw --- .../Platform/Linux/Iggy/gdraw/gdraw.c | 233 +++++++++--------- Minecraft.Client/Platform/Linux/LinuxGL.cpp | 26 ++ Minecraft.Client/meson.build | 3 +- meson.build | 43 +++- 4 files changed, 179 insertions(+), 126 deletions(-) diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c index c56cf237b..4fde9a91f 100644 --- a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c +++ b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c @@ -13,11 +13,11 @@ #include #include #include +#include #define true 1 #define false 0 - #ifndef _ENABLEIGGY void* IggyGDrawMallocAnnotated(SINTa size, const char* file, int line) { (void)file; @@ -43,9 +43,26 @@ void IggyDiscardVertexBufferCallback(void* owner, void* buf) { } #endif -// We track all the extensions we use. +static void* get_gl_proc(const char* name) { + void* p = SDL_GL_GetProcAddress(name); + if (!p) p = dlsym(RTLD_DEFAULT, name); + if (!p) { + char buf[256]; + strncpy(buf, name, sizeof(buf) - 1); + buf[255] = '\0'; + char* ext = strstr(buf, "ARB"); + if (!ext) ext = strstr(buf, "EXT"); + if (ext && ext == buf + strlen(buf) - 3) { + *ext = '\0'; + p = SDL_GL_GetProcAddress(buf); + if (!p) p = dlsym(RTLD_DEFAULT, buf); + } + } + return p; +} + #define GDRAW_GL_EXTENSION_LIST \ - /* identifier import procname */ \ + /* identifier import procname */ \ /* GL_ARB_vertex_buffer_object */ \ GLE(GenBuffers, "GenBuffersARB", GENBUFFERSARB) \ GLE(DeleteBuffers, "DeleteBuffersARB", DELETEBUFFERSARB) \ @@ -102,7 +119,6 @@ void IggyDiscardVertexBufferCallback(void* owner, void* buf) { RENDERBUFFERSTORAGEMULTISAMPLEEXT) \ /* */ - // Shared .inl #define gdraw_GLx_(id) gdraw_GL_##id #define GDRAW_GLx_(id) GDRAW_GL_##id @@ -118,7 +134,6 @@ typedef gdraw_gl_resourcetype gdraw_resourcetype; GDRAW_GL_EXTENSION_LIST #undef GLE - typedef const GLubyte*(APIENTRYP PFNGLGETSTRINGIPROC_)(GLenum name, GLuint index); static PFNGLGETSTRINGIPROC_ gdraw_glGetStringi = NULL; @@ -136,13 +151,11 @@ static GLuint gdraw_screenvbo = 0; static const void* gdraw_screenvbo_base = NULL; static size_t gdraw_expected_vbo_size = 0; - typedef void(APIENTRYP gdraw_drawelements_fn)(GLenum mode, GLsizei count, GLenum type, const void* indices); static gdraw_drawelements_fn gdraw_real_drawelements = NULL; static GLuint gdraw_screenibo = 0; - typedef GLuint(APIENTRYP gdraw_createshader_fn)(GLenum); typedef void(APIENTRYP gdraw_shadersource_fn)(GLuint, GLsizei, const GLchar**, const GLint*); @@ -159,7 +172,6 @@ typedef void(APIENTRYP gdraw_useprogram_fn)(GLuint); static gdraw_useprogram_fn gdraw_real_useprogram = NULL; static GLuint gdraw_null_program = 0; - typedef void(APIENTRYP gdraw_teximage2d_fn)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*); @@ -169,17 +181,17 @@ typedef void(APIENTRYP gdraw_texsubimage2d_fn)(GLenum, GLint, GLint, GLint, static gdraw_teximage2d_fn gdraw_real_teximage2d = NULL; static gdraw_texsubimage2d_fn gdraw_real_texsubimage2d = NULL; -#define TRY(ptr, arb, core) \ - do { \ - void* _p = SDL_GL_GetProcAddress(core); \ - if (!_p) _p = SDL_GL_GetProcAddress(arb); \ - *(void**)&(ptr) = _p; \ +#define TRY(ptr, arb, core) \ + do { \ + void* _p = get_gl_proc(core); \ + if (!_p) _p = get_gl_proc(arb); \ + *(void**)&(ptr) = _p; \ } while (0) static void load_extensions(void) { // gl_shared requires ts shit ugh #define GLE(id, import, procname) \ - gl##id = (PFNGL##procname##PROC)SDL_GL_GetProcAddress("gl" import); + gl##id = (PFNGL##procname##PROC)get_gl_proc("gl" import); GDRAW_GL_EXTENSION_LIST #undef GLE @@ -240,24 +252,27 @@ static void load_extensions(void) { "glRenderbufferStorageMultisample"); // Save raw pointers before we #define over the names below - gdraw_real_vtxattrib = (gdraw_vtxattrib_fn)(void*)glVertexAttribPointer; - gdraw_real_createshader = (gdraw_createshader_fn)(void*)glCreateShader; - gdraw_real_shadersource = (gdraw_shadersource_fn)(void*)glShaderSource; - gdraw_real_compileshader = (gdraw_compileshader_fn)(void*)glCompileShader; - gdraw_real_linkprogram = (gdraw_linkprogram_fn)(void*)glLinkProgram; - gdraw_real_teximage2d = (gdraw_teximage2d_fn)(void*)glTexImage2D; - gdraw_real_texsubimage2d = (gdraw_texsubimage2d_fn)(void*)glTexSubImage2D; - gdraw_real_useprogram = (gdraw_useprogram_fn)(void*)glUseProgram; + gdraw_real_vtxattrib = + (gdraw_vtxattrib_fn)get_gl_proc("glVertexAttribPointer"); + gdraw_real_createshader = + (gdraw_createshader_fn)get_gl_proc("glCreateShader"); + gdraw_real_shadersource = + (gdraw_shadersource_fn)get_gl_proc("glShaderSource"); + gdraw_real_compileshader = + (gdraw_compileshader_fn)get_gl_proc("glCompileShader"); + gdraw_real_linkprogram = (gdraw_linkprogram_fn)get_gl_proc("glLinkProgram"); + gdraw_real_teximage2d = (gdraw_teximage2d_fn)get_gl_proc("glTexImage2D"); + gdraw_real_texsubimage2d = + (gdraw_texsubimage2d_fn)get_gl_proc("glTexSubImage2D"); + gdraw_real_useprogram = (gdraw_useprogram_fn)get_gl_proc("glUseProgram"); gdraw_real_drawelements = - (gdraw_drawelements_fn)SDL_GL_GetProcAddress("glDrawElements"); + (gdraw_drawelements_fn)get_gl_proc("glDrawElements"); - gdraw_glGetStringi = - (PFNGLGETSTRINGIPROC_)SDL_GL_GetProcAddress("glGetStringi"); - // VAO + gdraw_glGetStringi = (PFNGLGETSTRINGIPROC_)get_gl_proc("glGetStringi"); gdraw_glGenVertexArrays = - (PFNGLGENVERTEXARRAYSPROC_)SDL_GL_GetProcAddress("glGenVertexArrays"); + (PFNGLGENVERTEXARRAYSPROC_)get_gl_proc("glGenVertexArrays"); gdraw_glBindVertexArray = - (PFNGLBINDVERTEXARRAYPROC_)SDL_GL_GetProcAddress("glBindVertexArray"); + (PFNGLBINDVERTEXARRAYPROC_)get_gl_proc("glBindVertexArray"); if (gdraw_glGenVertexArrays && gdraw_glBindVertexArray && gdraw_vao == 0) { gdraw_glGenVertexArrays(1, &gdraw_vao); @@ -278,15 +293,15 @@ static void error_msg_platform_specific(const char* msg) { fprintf(stderr, "[GDraw] %s\n", msg); } -#define GDRAW_PLATFORM_REPORT_GL_SITE(site) \ - do { \ - if ((site) != NULL) \ - fprintf(stderr, "[GDraw] GL error site: %s\n", (site)); \ +#define GDRAW_PLATFORM_REPORT_GL_SITE(site) \ + do { \ + if ((site) != NULL) \ + fprintf(stderr, "[GDraw] GL error site: %s\n", (site)); \ } while (0) #define GDRAW_MULTISAMPLING -// i wish i could improve this function +// i wish i could improve this function #ifdef RR_BREAK #undef RR_BREAK #endif @@ -295,7 +310,6 @@ static void error_msg_platform_specific(const char* msg) { fprintf(stderr, "[GDraw] GL error at %s:%d\n", __FILE__, __LINE__); \ } while (0) - // the magic number that tropical told me #define GDRAW_MAX_SHADERS 64 static struct { @@ -305,8 +319,7 @@ static struct { static int gdraw_shader_type_count = 0; static GLenum gdraw_get_shader_type(GLuint shader) { - int i; - for (i = 0; i < gdraw_shader_type_count; i++) + for (int i = 0; i < gdraw_shader_type_count; i++) if (gdraw_shader_types[i].handle == shader) return gdraw_shader_types[i].type; return GL_FRAGMENT_SHADER; @@ -322,8 +335,6 @@ static GLuint gdraw_CreateShaderTracked(GLenum type) { return h; } - - static void gdraw_CompileShaderAndLog(GLuint shader) { GLint status = 0; gdraw_real_compileshader(shader); @@ -335,7 +346,6 @@ static void gdraw_CompileShaderAndLog(GLuint shader) { log[len] = '\0'; fprintf(stderr, "[GDraw GLSL] compile FAILED shader=%u:\n%s\n", shader, log); - fflush(stderr); } } @@ -350,7 +360,6 @@ static void gdraw_LinkProgramAndLog(GLuint program) { log[len] = '\0'; fprintf(stderr, "[GDraw GLSL] link FAILED program=%u:\n%s\n", program, log); - fflush(stderr); } } @@ -393,33 +402,27 @@ static char* gdraw_strreplace(char* src, const char* find, const char* rep) { static void gdraw_ShaderSourceUpgraded(GLuint shader, GLsizei count, const GLchar** strings, const GLint* lengths) { - int i; size_t total = 0; - char* src; - char* patched; - int is_vert; - const GLchar* patched_ptr; - - for (i = 0; i < count; i++) + for (int i = 0; i < count; i++) total += lengths ? (lengths[i] >= 0 ? (size_t)lengths[i] : strlen(strings[i])) : strlen(strings[i]); - src = (char*)malloc(total + 1); + char* src = (char*)malloc(total + 1); if (!src) { gdraw_real_shadersource(shader, count, strings, lengths); return; } src[0] = '\0'; - for (i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { size_t len = lengths ? (lengths[i] >= 0 ? (size_t)lengths[i] : strlen(strings[i])) : strlen(strings[i]); strncat(src, strings[i], len); } - is_vert = (gdraw_get_shader_type(shader) == GL_VERTEX_SHADER); + int is_vert = (gdraw_get_shader_type(shader) == GL_VERTEX_SHADER); // Strip any existing #version directive as i'll add our own { @@ -451,30 +454,24 @@ static void gdraw_ShaderSourceUpgraded(GLuint shader, GLsizei count, src = gdraw_strreplace(src, "varying ", "in "); src = gdraw_strreplace(src, "varying\t", "in\t"); src = gdraw_strreplace(src, "varying\n", "in\n"); - } - if (!is_vert) { src = gdraw_strreplace(src, "gl_FragData[0]", "_gdraw_frag_out"); src = gdraw_strreplace(src, "gl_FragColor", "_gdraw_frag_out"); } - // finally turn it 330 - { - const char* vert_header = "#version 330 core\n"; - const char* frag_header = - "#version 330 core\nout vec4 _gdraw_frag_out;\n"; - const char* header = is_vert ? vert_header : frag_header; - patched = (char*)malloc(strlen(header) + strlen(src) + 2); - if (!patched) { - free(src); - gdraw_real_shadersource(shader, count, strings, lengths); - return; - } - strcpy(patched, header); - strcat(patched, src); + const char* header = is_vert + ? "#version 330 core\n" + : "#version 330 core\nout vec4 _gdraw_frag_out;\n"; + char* patched = (char*)malloc(strlen(header) + strlen(src) + 2); + if (!patched) { free(src); + gdraw_real_shadersource(shader, count, strings, lengths); + return; } + strcpy(patched, header); + strcat(patched, src); + free(src); - patched_ptr = (const GLchar*)patched; + const GLchar* patched_ptr = (const GLchar*)patched; gdraw_real_shadersource(shader, 1, &patched_ptr, NULL); free(patched); } @@ -482,7 +479,6 @@ static void gdraw_ShaderSourceUpgraded(GLuint shader, GLsizei count, #undef glShaderSource #define glShaderSource gdraw_ShaderSourceUpgraded - // Remap all the deprecated internal formats to their modern equivalents // (idk why but just the word "swizzle" is cracking me up) static void gdraw_apply_swizzle(GLenum internal_fmt) { @@ -524,6 +520,9 @@ static GLenum gdraw_remap_fmt(GLenum fmt) { static void gdraw_TexImage2D(GLenum target, GLint level, GLint ifmt, GLsizei w, GLsizei h, GLint border, GLenum fmt, GLenum type, const void* data) { + // ES strictly requires explicitly sized formats & stuff + if (ifmt == GL_RGBA && data == NULL) ifmt = GL_RGBA8; + GLenum new_ifmt = gdraw_remap_fmt((GLenum)ifmt); GLenum new_fmt = gdraw_remap_fmt(fmt); gdraw_real_teximage2d(target, level, (GLint)new_ifmt, w, h, border, new_fmt, @@ -556,15 +555,13 @@ static void gdraw_ClientVertexAttribPointer(GLuint index, GLint size, gdraw_glBindVertexArray(gdraw_vao); } - { - GLint current_vbo = 0; - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_vbo); + GLint current_vbo = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_vbo); - if (current_vbo != 0 && current_vbo != (GLint)gdraw_screenvbo) { - gdraw_real_vtxattrib(index, size, type, normalized, stride, - pointer); - return; - } + if (current_vbo != 0 && current_vbo != (GLint)gdraw_screenvbo) { + // no touchies + gdraw_real_vtxattrib(index, size, type, normalized, stride, pointer); + return; } if (pointer == NULL) { @@ -572,12 +569,19 @@ static void gdraw_ClientVertexAttribPointer(GLuint index, GLint size, return; } - if (gdraw_screenvbo_base == NULL) { + ptrdiff_t offset = + gdraw_screenvbo_base + ? ((const char*)pointer - (const char*)gdraw_screenvbo_base) + : -1; + + if (gdraw_screenvbo_base == NULL || offset < 0 || + offset >= (ptrdiff_t)gdraw_expected_vbo_size) { if (!gdraw_screenvbo) glGenBuffers(1, &gdraw_screenvbo); glBindBuffer(GL_ARRAY_BUFFER, gdraw_screenvbo); - size_t upload_size = - gdraw_expected_vbo_size > 0 ? gdraw_expected_vbo_size : 256; + size_t upload_size = gdraw_expected_vbo_size > 0 + ? (gdraw_expected_vbo_size + 256) + : 65536; glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)upload_size, pointer, GL_STREAM_DRAW); @@ -586,8 +590,6 @@ static void gdraw_ClientVertexAttribPointer(GLuint index, GLint size, (const void*)0); } else { glBindBuffer(GL_ARRAY_BUFFER, gdraw_screenvbo); - ptrdiff_t offset = - (const char*)pointer - (const char*)gdraw_screenvbo_base; gdraw_real_vtxattrib(index, size, type, normalized, stride, (const void*)offset); } @@ -641,7 +643,7 @@ static void gdraw_UseProgramSafe(GLuint program) { glDeleteShader(v); glDeleteShader(f); } - gdraw_real_useprogram(0); + gdraw_real_useprogram(gdraw_null_program); return; } gdraw_real_useprogram(program); @@ -649,25 +651,48 @@ static void gdraw_UseProgramSafe(GLuint program) { #undef glUseProgram #define glUseProgram gdraw_UseProgramSafe - #undef glCompileShader #define glCompileShader gdraw_CompileShaderAndLog - #undef glLinkProgram #define glLinkProgram gdraw_LinkProgramAndLog -// v ugh fuck you windows +static void gdraw_FramebufferRenderbufferSafe(GLenum target, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) { + static GLuint last_depth_rb = 0; + + if (attachment == GL_DEPTH_ATTACHMENT) { + last_depth_rb = renderbuffer; + (glFramebufferRenderbuffer)(target, attachment, renderbuffertarget, + renderbuffer); + } else if (attachment == GL_STENCIL_ATTACHMENT) { + if (renderbuffer == last_depth_rb && renderbuffer != 0) { + // If identical, bind as packed depth-stencil to satisfy strict GLES + // ^ how greedy -n- + (glFramebufferRenderbuffer)( + target, 0x821A /* GL_DEPTH_STENCIL_ATTACHMENT */, + renderbuffertarget, renderbuffer); + } else { + (glFramebufferRenderbuffer)(target, attachment, renderbuffertarget, + renderbuffer); + } + } else { + (glFramebufferRenderbuffer)(target, attachment, renderbuffertarget, + renderbuffer); + } +} +#define glFramebufferRenderbuffer_SAFE gdraw_FramebufferRenderbufferSafe +#define glFramebufferRenderbuffer glFramebufferRenderbuffer_SAFE + #include "../../../Windows64/Iggy/gdraw/gdraw_gl_shared.inl" #undef glVertexAttribPointer #define glVertexAttribPointer gdraw_real_vtxattrib -// 3.3 c extension static int hasext_core(const char* name) { GLint n = 0; - GLint i; if (!gdraw_glGetStringi) return 0; glGetIntegerv(GL_NUM_EXTENSIONS, &n); - for (i = 0; i < n; i++) { + for (GLint i = 0; i < n; i++) { const char* e = (const char*)gdraw_glGetStringi(GL_EXTENSIONS, (GLuint)i); if (e && strcmp(e, name) == 0) return 1; @@ -743,35 +768,21 @@ GDrawFunctions* gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) { {0, 0, 0, 0, 0, 0, 0}, }; - GDrawFunctions* funcs; - GLint n; GLint major = 0, minor = 0; - - // incase everything goes wrong and it goes like 1.1 or smh glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MINOR_VERSION, &minor); - if (major < 3 || (major == 3 && minor < 3)) { - fprintf(stderr, "[GDraw] GL 3.3 or higher required (got %d.%d)\n", + if (major < 3) { + fprintf(stderr, "[GDraw] GL 3.0 or higher required (got %d.%d)\n", major, minor); return NULL; } - // FBO is core since 3.0 - if (!hasext_core("GL_EXT_framebuffer_object")) { - fprintf(stderr, - "[GDraw] GL_EXT_framebuffer_object not listed, " - "fuck it, let's continue.\n"); - } - - if (!hasext_core("GL_EXT_framebuffer_multisample") && msaa_samples > 1) - return NULL; - load_extensions(); if (gdraw_glBindVertexArray && gdraw_vao) gdraw_glBindVertexArray(gdraw_vao); - funcs = create_context(w, h); + GDrawFunctions* funcs = create_context(w, h); if (!funcs) return NULL; // hook the vtable entries for VBO reset and render state @@ -786,13 +797,13 @@ GDrawFunctions* gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) { funcs->ClearID = gdraw_ClearID; gdraw->tex_formats = tex_formats; - gdraw->has_mapbuffer = true; + gdraw->has_mapbuffer = false; gdraw->has_depth24 = true; gdraw->has_texture_max_level = true; - if (hasext_core("GL_EXT_packed_depth_stencil")) - gdraw->has_packed_depth_stencil = true; + gdraw->has_packed_depth_stencil = true; + GLint n = 0; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n); gdraw->has_conditional_non_power_of_two = (n < 8192); @@ -802,10 +813,8 @@ GDrawFunctions* gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) { } opengl_check(); - - fprintf(stderr, - "[GDraw] Context created successfully (%dx%d, msaa=%d)\n", w, h, - msaa_samples); + fprintf(stderr, "[GDraw] Context created successfully (%dx%d, msaa=%d)\n", + w, h, msaa_samples); return funcs; } diff --git a/Minecraft.Client/Platform/Linux/LinuxGL.cpp b/Minecraft.Client/Platform/Linux/LinuxGL.cpp index 4b41d2961..17f25fbcb 100644 --- a/Minecraft.Client/Platform/Linux/LinuxGL.cpp +++ b/Minecraft.Client/Platform/Linux/LinuxGL.cpp @@ -28,6 +28,32 @@ #undef glReadPixels #undef glActiveTexture +#ifdef GLES +extern "C" { +// TELL IT TO USE GLEW +extern void glClearDepthf(float depth); + +void glClearDepth(double depth) { glClearDepthf((float)depth); } + +void glTexGeni(unsigned int coord, unsigned int pname, int param) {} +void glTexGenfv(unsigned int coord, unsigned int pname, const float* params) {} +void glTexCoordPointer(int size, unsigned int type, int stride, + const void* pointer) {} +void glNormalPointer(unsigned int type, int stride, const void* pointer) {} +void glColorPointer(int size, unsigned int type, int stride, + const void* pointer) {} +void glVertexPointer(int size, unsigned int type, int stride, + const void* pointer) {} +void glEndList(void) {} +void glCallLists(int n, unsigned int type, const void* lists) {} +// Did you nose? +// Did you know if you sniff pizza too much your nose +// might think it. It's pizza? +// This. This is because pizza smells trick your nose into pizza thinking. +} + +#endif + // Helper functions & stuff inline GLuint* getIntPtr(IntBuffer* buf) { return buf ? (GLuint*)((int*)buf->getBuffer() + buf->position()) : nullptr; diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index f31407b0d..39bc97d2a 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -69,6 +69,7 @@ client_dependencies = [ dl_dep, dependency('zlib'), miniaudio_dep, + stb_dep, ] if get_option('enable_vsync') @@ -95,7 +96,7 @@ endif client = executable('Minecraft.Client', client_sources + platform_sources + localisation[1], - include_directories: [include_directories('Platform', 'Platform/Linux/Iggy/include'), stb], + include_directories: include_directories('Platform', 'Platform/Linux/Iggy/include'), dependencies: client_dependencies, cpp_args: global_cpp_args + global_cpp_defs diff --git a/meson.build b/meson.build index 7d08e7faf..a62becd63 100644 --- a/meson.build +++ b/meson.build @@ -18,6 +18,8 @@ python = pymod.find_installation('python3', required: true) cc = meson.get_compiler('cpp') +use_gles = get_option('gles') + global_cpp_defs = [ '-DSPLIT_SAVES', '-D_LARGE_WORLDS', @@ -28,11 +30,23 @@ global_cpp_defs = [ ] if host_machine.system() == 'linux' - global_cpp_defs += [ - '-Dlinux', - '-D__linux', - '-D__linux__', - ] + global_cpp_defs += ['-Dlinux', '-D__linux', '-D__linux__'] +endif + +if use_gles + global_cpp_defs += ['-DGLES'] +endif + +if get_option('enable_vsync') + global_cpp_defs += ['-DENABLE_VSYNC'] +endif + +if get_option('enable_shiggy') + global_cpp_defs += ['-D_ENABLEIGGY'] +endif + +if get_option('enable_java_guis') + global_cpp_defs += ['-DENABLE_JAVA_GUIS'] endif add_project_arguments(global_cpp_defs, language: ['cpp', 'c']) @@ -48,12 +62,13 @@ sdl2_dep = dependency('sdl2') thread_dep = dependency('threads') dl_dep = cc.find_library('dl', required: true) -if '-DGLES' in global_cpp_defs - gl_dep = dependency('glesv2') +# GLES vs Desktop GL +if use_gles + gl_dep = dependency('glesv2', required: true) glu_dep = dependency('', required: false) else - gl_dep = dependency('gl') - glu_dep = dependency('glu') + gl_dep = dependency('gl', required: true) + glu_dep = dependency('glu', required: true) endif stb = subproject('stb').get_variable('stb_inc') @@ -61,10 +76,12 @@ stb_dep = declare_dependency(include_directories: stb) miniaudio_dep = dependency('miniaudio') -render_dep = dependency('4j-render', fallback: ['4jlibs', 'render_dep']) -input_dep = dependency('4j-input', fallback: ['4jlibs', 'input_dep']) -profile_dep = dependency('4j-profile', fallback: ['4jlibs', 'profile_dep']) -storage_dep = dependency('4j-storage', fallback: ['4jlibs', 'storage_dep']) +sub_opts = ['gles=' + use_gles.to_string()] + +render_dep = dependency('4j-render', fallback: ['4jlibs', 'render_dep'], default_options: sub_opts) +input_dep = dependency('4j-input', fallback: ['4jlibs', 'input_dep'], default_options: sub_opts) +profile_dep = dependency('4j-profile', fallback: ['4jlibs', 'profile_dep'], default_options: sub_opts) +storage_dep = dependency('4j-storage', fallback: ['4jlibs', 'storage_dep'], default_options: sub_opts) all_deps = [ gl_dep, From 73a74f4872265401c8703229547cfc9d4eaea5ff Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 11:27:33 -0500 Subject: [PATCH 49/71] chore: clean up LevelRenderer diffs --- Minecraft.Client/Rendering/LevelRenderer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 911bc5aa7..03bb23057 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -2255,6 +2255,7 @@ bool LevelRenderer::updateDirtyChunks() { // int64_t startTime = System::currentTimeMillis(); // app.DebugPrintf("Rebuilding permaChunk %d\n", index); + permaChunk[index].rebuild(); if (index != 0) @@ -2493,7 +2494,7 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, AABB bb = Tile::tiles[tileId] ->getTileAABB(level[iPad], h->x, h->y, h->z) - .grow(0.002f, 0.002f, 0.002f) + .grow(ss, ss, ss) .move(-xo, -yo, -zo); render(&bb); From b963a61caf7f209fee922639b93959707cd2929c Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 11:35:47 -0500 Subject: [PATCH 50/71] refactor: use combo option for renderer selection --- meson.build | 2 +- meson.options | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index a62becd63..bee78f958 100644 --- a/meson.build +++ b/meson.build @@ -18,7 +18,7 @@ python = pymod.find_installation('python3', required: true) cc = meson.get_compiler('cpp') -use_gles = get_option('gles') +use_gles = get_option('renderer') == 'gles' global_cpp_defs = [ '-DSPLIT_SAVES', diff --git a/meson.options b/meson.options index 6f4f4808b..bc73c0516 100644 --- a/meson.options +++ b/meson.options @@ -9,14 +9,13 @@ option('classic_panorama', value : false, description : 'Enable classic java edition panorama (ui_backend=java ONLY).') -option('enable_vsync', +option('renderer', + type : 'combo', + choices: ['gl2', 'gl3', 'gles'], + value : 'gl3', + description : 'Specifies a rendering implementation for the game.') + +option('enable_vsync', type : 'boolean', value : true, description : 'Toggles V-Sync and adds options to unlock maximum in-game framerate.') - -option( - 'gles', - type: 'boolean', - value: false, - description: 'Use OpenGL ES instead of Desktop GL', -) From 19d0af460ea828b0f350765a5fac9ec90d8fb7b5 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 11:38:49 -0500 Subject: [PATCH 51/71] fix: remove gl2 option --- meson.options | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.options b/meson.options index bc73c0516..3171bc298 100644 --- a/meson.options +++ b/meson.options @@ -11,7 +11,7 @@ option('classic_panorama', option('renderer', type : 'combo', - choices: ['gl2', 'gl3', 'gles'], + choices: ['gl3', 'gles'], value : 'gl3', description : 'Specifies a rendering implementation for the game.') From cb144f3debb80225eb3ac5c519bf4d147e4dfdce Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 18:07:19 +0100 Subject: [PATCH 52/71] temp removal --- meson.build | 8 -------- 1 file changed, 8 deletions(-) diff --git a/meson.build b/meson.build index bee78f958..6ad549cbb 100644 --- a/meson.build +++ b/meson.build @@ -41,14 +41,6 @@ if get_option('enable_vsync') global_cpp_defs += ['-DENABLE_VSYNC'] endif -if get_option('enable_shiggy') - global_cpp_defs += ['-D_ENABLEIGGY'] -endif - -if get_option('enable_java_guis') - global_cpp_defs += ['-DENABLE_JAVA_GUIS'] -endif - add_project_arguments(global_cpp_defs, language: ['cpp', 'c']) global_cpp_args = [ From d6394de9004d9d7211546bc5ed16426ed08d034c Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 12:09:20 -0500 Subject: [PATCH 53/71] fix: get rid of redundant option check --- Minecraft.Client/Platform/Common/UI/UIController.cpp | 5 +++-- meson.build | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Minecraft.Client/Platform/Common/UI/UIController.cpp b/Minecraft.Client/Platform/Common/UI/UIController.cpp index 3807ec7e0..6ee65e8ec 100644 --- a/Minecraft.Client/Platform/Common/UI/UIController.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIController.cpp @@ -78,8 +78,9 @@ static void RADLINK WarningCallback(void* user_callback_data, Iggy* player, // IGGY_RESULT_Error_OutOfMemory = 1001,}; if (message != NULL) { - // Some Linux movie variants do not ship these optional hooks/controls. - // We guard the call sites, so drop the residual Iggy warning noise. + // 4jcraft: Some Linux movie variants do not ship these optional + // hooks/controls. We guard the call sites, so drop the residual Iggy + // warning noise. if (strstr(message, "LabelGamertag") != NULL || strstr(message, "Method SetSafeZone was not a function") != NULL) { return; diff --git a/meson.build b/meson.build index 6ad549cbb..1939a88ad 100644 --- a/meson.build +++ b/meson.build @@ -37,10 +37,6 @@ if use_gles global_cpp_defs += ['-DGLES'] endif -if get_option('enable_vsync') - global_cpp_defs += ['-DENABLE_VSYNC'] -endif - add_project_arguments(global_cpp_defs, language: ['cpp', 'c']) global_cpp_args = [ From bb50db7d9b55da1760451bebd0b5dc9d53b6b645 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 18:29:47 +0100 Subject: [PATCH 54/71] Cleaned up linuxGL.cpp --- Minecraft.Client/Platform/Linux/LinuxGL.cpp | 250 ++++++-------------- 1 file changed, 75 insertions(+), 175 deletions(-) diff --git a/Minecraft.Client/Platform/Linux/LinuxGL.cpp b/Minecraft.Client/Platform/Linux/LinuxGL.cpp index 17f25fbcb..c7fff72d5 100644 --- a/Minecraft.Client/Platform/Linux/LinuxGL.cpp +++ b/Minecraft.Client/Platform/Linux/LinuxGL.cpp @@ -1,196 +1,113 @@ #ifdef __linux__ #include "../stdafx.h" -#include -#include -#include -#include -#include +#include "4J_Render.h" #include "../../Minecraft.World/IO/Streams/IntBuffer.h" #include "../../Minecraft.World/IO/Streams/FloatBuffer.h" #include "../../Minecraft.World/IO/Streams/ByteBuffer.h" -#undef glGenTextures -#undef glDeleteTextures -#undef glLight -#undef glLightModel -#undef glGetFloat -#undef glTexGen -#undef glFog -#undef glTexCoordPointer -#undef glNormalPointer -#undef glColorPointer -#undef glVertexPointer -#undef glEndList -#undef glTexImage2D -#undef glCallLists -#undef glReadPixels -#undef glActiveTexture +extern C4JRender RenderManager; #ifdef GLES extern "C" { -// TELL IT TO USE GLEW extern void glClearDepthf(float depth); - void glClearDepth(double depth) { glClearDepthf((float)depth); } - -void glTexGeni(unsigned int coord, unsigned int pname, int param) {} -void glTexGenfv(unsigned int coord, unsigned int pname, const float* params) {} -void glTexCoordPointer(int size, unsigned int type, int stride, - const void* pointer) {} -void glNormalPointer(unsigned int type, int stride, const void* pointer) {} -void glColorPointer(int size, unsigned int type, int stride, - const void* pointer) {} -void glVertexPointer(int size, unsigned int type, int stride, - const void* pointer) {} +void glTexGeni(unsigned int, unsigned int, int) {} +void glTexGenfv(unsigned int, unsigned int, const float*) {} +void glTexCoordPointer(int, unsigned int, int, const void*) {} +void glNormalPointer(unsigned int, int, const void*) {} +void glColorPointer(int, unsigned int, int, const void*) {} +void glVertexPointer(int, unsigned int, int, const void*) {} void glEndList(void) {} -void glCallLists(int n, unsigned int type, const void* lists) {} -// Did you nose? -// Did you know if you sniff pizza too much your nose -// might think it. It's pizza? -// This. This is because pizza smells trick your nose into pizza thinking. +void glCallLists(int, unsigned int, const void*) {} } - #endif -// Helper functions & stuff -inline GLuint* getIntPtr(IntBuffer* buf) { - return buf ? (GLuint*)((int*)buf->getBuffer() + buf->position()) : nullptr; +inline int* getIntPtr(IntBuffer* buf) { + return buf ? (int*)buf->getBuffer() + buf->position() : nullptr; } -inline GLvoid* getBytePtr(ByteBuffer* buf) { - return buf ? (GLvoid*)((char*)buf->getBuffer() + buf->position()) : nullptr; -} - -// _4j suffix shit -int glGenTextures() { - GLuint id = 0; - ::glGenTextures(1, &id); - return (int)id; +inline void* getBytePtr(ByteBuffer* buf) { + return buf ? (char*)buf->getBuffer() + buf->position() : nullptr; } void glGenTextures_4J(IntBuffer* buf) { if (!buf) return; int n = buf->limit() - buf->position(); - if (n > 0) { - ::glGenTextures(n, getIntPtr(buf)); - } -} - -void glDeleteTextures(int id) { - GLuint uid = (GLuint)id; - ::glDeleteTextures(1, &uid); + int* dst = getIntPtr(buf); + for (int i = 0; i < n; i++) dst[i] = RenderManager.TextureCreate(); } void glDeleteTextures_4J(IntBuffer* buf) { if (!buf) return; int n = buf->limit() - buf->position(); - if (n > 0) { - ::glDeleteTextures(n, getIntPtr(buf)); - } -} - -void glLight_4J(int light, int pname, FloatBuffer* params) { - ::glLightfv((GLenum)light, (GLenum)pname, params->_getDataPointer()); -} - -void glLightModel_4J(int pname, FloatBuffer* params) { - ::glLightModelfv((GLenum)pname, params->_getDataPointer()); -} - -void glGetFloat_4J(int pname, FloatBuffer* params) { - ::glGetFloatv((GLenum)pname, params->_getDataPointer()); -} - -void glTexGen_4J(int coord, int pname, FloatBuffer* params) { - ::glTexGenfv((GLenum)coord, (GLenum)pname, params->_getDataPointer()); -} - -void glFog_4J(int pname, FloatBuffer* params) { - ::glFogfv((GLenum)pname, params->_getDataPointer()); -} - -void glTexCoordPointer_4J(int size, int type, FloatBuffer* pointer) { - ::glTexCoordPointer(size, (GLenum)type, 0, pointer->_getDataPointer()); -} - -void glNormalPointer_4J(int type, ByteBuffer* pointer) { - ::glNormalPointer((GLenum)type, 0, getBytePtr(pointer)); -} - -void glColorPointer_4J(int size, bool normalized, int stride, - ByteBuffer* pointer) { - (void)normalized; - ::glColorPointer(size, GL_UNSIGNED_BYTE, stride, getBytePtr(pointer)); -} - -void glVertexPointer_4J(int size, int type, FloatBuffer* pointer) { - ::glVertexPointer(size, (GLenum)type, 0, pointer->_getDataPointer()); -} - -void glEndList_4J(int dummy) { - (void)dummy; - ::glEndList(); + int* src = getIntPtr(buf); + for (int i = 0; i < n; i++) RenderManager.TextureFree(src[i]); } void glTexImage2D_4J(int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer* pixels) { - ::glTexImage2D((GLenum)target, level, internalformat, width, height, border, - (GLenum)format, (GLenum)type, getBytePtr(pixels)); + (void)target; + (void)internalformat; + (void)border; + (void)format; + (void)type; + RenderManager.TextureData(width, height, getBytePtr(pixels), level, + C4JRender::TEXTURE_FORMAT_RxGyBzAw); +} + +void glLight_4J(int light, int pname, FloatBuffer* params) { + const float* p = params->_getDataPointer(); + int idx = (light == 0x4001) ? 1 : 0; + if (pname == 0x1203) + RenderManager.StateSetLightDirection(idx, p[0], p[1], p[2]); + else if (pname == 0x1201) + RenderManager.StateSetLightColour(idx, p[0], p[1], p[2]); + else if (pname == 0x1200) + RenderManager.StateSetLightAmbientColour(p[0], p[1], p[2]); +} + +void glLightModel_4J(int pname, FloatBuffer* params) { + if (pname == 0x0B53) { + const float* p = params->_getDataPointer(); + RenderManager.StateSetLightAmbientColour(p[0], p[1], p[2]); + } +} + +void glFog_4J(int pname, FloatBuffer* params) { + const float* p = params->_getDataPointer(); + if (pname == 0x0B66) RenderManager.StateSetFogColour(p[0], p[1], p[2]); +} + +void glGetFloat_4J(int pname, FloatBuffer* params) { + const float* m = RenderManager.MatrixGet(pname); + if (m) memcpy(params->_getDataPointer(), m, 16 * sizeof(float)); } void glCallLists_4J(IntBuffer* lists) { if (!lists) return; int count = lists->limit() - lists->position(); - if (count > 0) { - ::glCallLists(count, GL_INT, getIntPtr(lists)); - } + int* ids = getIntPtr(lists); + for (int i = 0; i < count; i++) RenderManager.CBuffCall(ids[i], false); } -void glReadPixels_4J(int x, int y, int width, int height, int format, int type, - ByteBuffer* pixels) { - ::glReadPixels(x, y, width, height, (GLenum)format, (GLenum)type, - getBytePtr(pixels)); +void glReadPixels_4J(int x, int y, int w, int h, int f, int t, ByteBuffer* p) { + (void)f; + (void)t; + RenderManager.ReadPixels(x, y, w, h, getBytePtr(p)); } -void glGetFloat(int pname, FloatBuffer* params) { - glGetFloat_4J(pname, params); -} -void glGenTextures(IntBuffer* buf) { glGenTextures_4J(buf); } -void glDeleteTextures(IntBuffer* buf) { glDeleteTextures_4J(buf); } -void glLight(int light, int pname, FloatBuffer* params) { - glLight_4J(light, pname, params); -} -void glLightModel(int pname, FloatBuffer* params) { - glLightModel_4J(pname, params); -} -void glTexGen(int coord, int pname, FloatBuffer* params) { - glTexGen_4J(coord, pname, params); -} -void glFog(int pname, FloatBuffer* params) { glFog_4J(pname, params); } -void glTexCoordPointer(int size, int type, FloatBuffer* pointer) { - glTexCoordPointer_4J(size, type, pointer); -} -void glNormalPointer(int type, ByteBuffer* pointer) { - glNormalPointer_4J(type, pointer); -} -void glColorPointer(int size, bool normalized, int stride, - ByteBuffer* pointer) { - glColorPointer_4J(size, normalized, stride, pointer); -} -void glVertexPointer(int size, int type, FloatBuffer* pointer) { - glVertexPointer_4J(size, type, pointer); -} -void glTexImage2D(int t, int l, int i, int w, int h, int b, int f, int ty, - ByteBuffer* p) { - glTexImage2D_4J(t, l, i, w, h, b, f, ty, p); -} -void glCallLists(IntBuffer* lists) { glCallLists_4J(lists); } -void glReadPixels(int x, int y, int w, int h, int f, int t, ByteBuffer* p) { - glReadPixels_4J(x, y, w, h, f, t, p); -} +// dead stubs +void glTexCoordPointer_4J(int, int, FloatBuffer*) {} +void glNormalPointer_4J(int, ByteBuffer*) {} +void glColorPointer_4J(int, bool, int, ByteBuffer*) {} +void glVertexPointer_4J(int, int, FloatBuffer*) {} +void glEndList_4J(int) {} +void glTexGen_4J(int, int, FloatBuffer*) {} +// query objects +#include static PFNGLGENQUERIESARBPROC _glGenQueriesARB = nullptr; static PFNGLBEGINQUERYARBPROC _glBeginQueryARB = nullptr; static PFNGLENDQUERYARBPROC _glEndQueryARB = nullptr; @@ -213,54 +130,37 @@ void glGenQueriesARB_4J(IntBuffer* buf) { initQueryFuncs(); if (_glGenQueriesARB && buf) { int n = buf->limit() - buf->position(); - if (n > 0) { - _glGenQueriesARB(n, getIntPtr(buf)); - } + if (n > 0) _glGenQueriesARB(n, (GLuint*)getIntPtr(buf)); } } -void glGenQueriesARB(IntBuffer* buf) { glGenQueriesARB_4J(buf); } void glBeginQueryARB_4J(int target, int id) { initQueryFuncs(); if (_glBeginQueryARB) _glBeginQueryARB((GLenum)target, (GLuint)id); } -void glBeginQueryARB(int target, int id) { glBeginQueryARB_4J(target, id); } void glEndQueryARB_4J(int target) { initQueryFuncs(); if (_glEndQueryARB) _glEndQueryARB((GLenum)target); } -void glEndQueryARB(int target) { glEndQueryARB_4J(target); } void glGetQueryObjectuARB_4J(int id, int pname, IntBuffer* params) { initQueryFuncs(); - if (_glGetQueryObjectuivARB && params) { + if (_glGetQueryObjectuivARB && params) // LWJGL does not change limits/positions during these calls, it // reads/writes exactly at pointer!! - _glGetQueryObjectuivARB((GLuint)id, (GLenum)pname, getIntPtr(params)); - } + _glGetQueryObjectuivARB((GLuint)id, (GLenum)pname, + (GLuint*)getIntPtr(params)); } -void glGetQueryObjectuARB(int id, int pname, IntBuffer* params) { - glGetQueryObjectuARB_4J(id, pname, params); +void glGetFloat(int pname, FloatBuffer* params) { + glGetFloat_4J(pname, params); } - void LinuxGLLogLightmapState(const char* stage, int textureId, bool scaleLight) { static int logCount = 0; if (logCount >= 16) return; ++logCount; - - GLint activeTexture = 0; - ::glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture); - const GLint restoreTexture = activeTexture; - ::glActiveTexture(GL_TEXTURE1); - GLint unit1Binding = 0; - ::glGetIntegerv(GL_TEXTURE_BINDING_2D, &unit1Binding); - ::glActiveTexture(restoreTexture); - - app.DebugPrintf( - "[linux-lightmap] %s tex=%d scale=%d active=%#x unit1Bound=%d\n", stage, - textureId, scaleLight ? 1 : 0, activeTexture, unit1Binding); + fprintf(stderr, "[linux-lightmap] %s tex=%d scale=%d\n", stage, textureId, + scaleLight ? 1 : 0); } - #endif \ No newline at end of file From 5357e75037f285bb6f3e149d6fb15408e767955c Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 19:45:28 +0100 Subject: [PATCH 55/71] gamerenderer bit more optimized --- Minecraft.Client/Rendering/GameRenderer.cpp | 248 +++++++++++--------- 1 file changed, 131 insertions(+), 117 deletions(-) diff --git a/Minecraft.Client/Rendering/GameRenderer.cpp b/Minecraft.Client/Rendering/GameRenderer.cpp index 6a1bbc614..617e67dc2 100644 --- a/Minecraft.Client/Rendering/GameRenderer.cpp +++ b/Minecraft.Client/Rendering/GameRenderer.cpp @@ -72,6 +72,9 @@ ResourceLocation GameRenderer::RAIN_LOCATION = ResourceLocation GameRenderer::SNOW_LOCATION = ResourceLocation(TN_ENVIRONMENT_SNOW); +// dirty light tracking +static bool s_lightTexDirty[XUSER_MAX_COUNT] = {true, true, true, true}; + GameRenderer::GameRenderer(Minecraft* mc) { // 4J - added this block of initialisers renderDistance = 0; @@ -871,6 +874,10 @@ void GameRenderer::tickLightTexture() { blr += (blrt - blr) * 1; blg += (blgt - blg) * 1; _updateLightTexture = true; + + // Mark all players dirty so updateLightTexture() knows when it actually + // needs to tick, preventz unessesary player recompute + for (int j = 0; j < XUSER_MAX_COUNT; j++) s_lightTexDirty[j] = true; } void GameRenderer::updateLightTexture(float a) { @@ -883,8 +890,10 @@ void GameRenderer::updateLightTexture(float a) { Minecraft::GetInstance()->localplayers[j]; if (player == NULL) continue; - Level* level = player->level; // 4J - was mc->level when it was just to - // update the one light texture + if (!s_lightTexDirty[j]) continue; + s_lightTexDirty[j] = false; + + Level* level = player->level; float skyDarken1 = level->getSkyDarken((float)1); for (int i = 0; i < 256; i++) { @@ -964,10 +973,10 @@ void GameRenderer::updateLightTexture(float a) { _b = _b * 0.96f + 0.03f; if (_r > 1) _r = 1; - if (_g > 1) _g = 1; - if (_b > 1) _b = 1; if (_r < 0) _r = 0; + if (_g > 1) _g = 1; if (_g < 0) _g = 0; + if (_b > 1) _b = 1; if (_b < 0) _b = 0; int alpha = 255; @@ -1180,24 +1189,19 @@ int GameRenderer::runUpdate(void* lpParam) { // We've got stacks for things that can only safely be deleted whilst // this thread isn't updating things - delete those things now EnterCriticalSection(&m_csDeleteStack); - for (unsigned int i = 0; i < m_deleteStackByte.size(); i++) { + for (unsigned int i = 0; i < m_deleteStackByte.size(); i++) delete m_deleteStackByte[i]; - } m_deleteStackByte.clear(); for (unsigned int i = 0; i < m_deleteStackSparseLightStorage.size(); - i++) { + i++) delete m_deleteStackSparseLightStorage[i]; - } m_deleteStackSparseLightStorage.clear(); for (unsigned int i = 0; i < m_deleteStackCompressedTileStorage.size(); - i++) { + i++) delete m_deleteStackCompressedTileStorage[i]; - } m_deleteStackCompressedTileStorage.clear(); - for (unsigned int i = 0; i < m_deleteStackSparseDataStorage.size(); - i++) { + for (unsigned int i = 0; i < m_deleteStackSparseDataStorage.size(); i++) delete m_deleteStackSparseDataStorage[i]; - } m_deleteStackSparseDataStorage.clear(); LeaveCriticalSection(&m_csDeleteStack); @@ -1700,135 +1704,144 @@ void GameRenderer::renderSnowAndRain(float a) { glColor4f(1, 1, 1, 1); - for (int x = x0 - r; x <= x0 + r; x++) + // two snow/rain rendering + mc->textures->bindTexture(&RAIN_LOCATION); + t->begin(); + for (int x = x0 - r; x <= x0 + r; x++) { for (int z = z0 - r; z <= z0 + r; z++) { int rainSlot = (z - z0 + 16) * 32 + (x - x0 + 16); float xa = rainXa[rainSlot] * 0.5f; float za = rainZa[rainSlot] * 0.5f; - // 4J - changes here brought forward from 1.8.2 Biome* b = level->getBiome(x, z); if (!b->hasRain() && !b->hasSnow()) continue; int floor = level->getTopRainBlock(x, z); - int yy0 = y0 - r; int yy1 = y0 + r; - if (yy0 < floor) yy0 = floor; if (yy1 < floor) yy1 = floor; - float s = 1; + if (yy0 == yy1) continue; int yl = floor; if (yl < yMin) yl = yMin; - if (yy0 != yy1) { - random->setSeed((x * x * 3121 + x * 45238971) ^ - (z * z * 418711 + z * 13761)); + float temp = b->getTemperature(); + if (level->getBiomeSource()->scaleTemp(temp, floor) < 0.15f) + continue; - // 4J - changes here brought forward from 1.8.2 - float temp = b->getTemperature(); - if (level->getBiomeSource()->scaleTemp(temp, floor) >= 0.15f) { - if (mode != 0) { - if (mode >= 0) t->end(); - mode = 0; - mc->textures->bindTexture(&RAIN_LOCATION); - t->begin(); - } + random->setSeed((x * x * 3121 + x * 45238971) ^ + (z * z * 418711 + z * 13761)); - float ra = (((_tick + x * x * 3121 + x * 45238971 + - z * z * 418711 + z * 13761) & - 31) + - a) / - 32.0f * (3 + random->nextFloat()); + float ra = (((_tick + x * x * 3121 + x * 45238971 + z * z * 418711 + + z * 13761) & + 31) + + a) / + 32.0f * (3 + random->nextFloat()); - double xd = (x + 0.5f) - player->x; - double zd = (z + 0.5f) - player->z; - float dd = (float)Mth::sqrt(xd * xd + zd * zd) / r; + double xd = (x + 0.5f) - player->x; + double zd = (z + 0.5f) - player->z; + float dd = (float)Mth::sqrt(xd * xd + zd * zd) / r; - float br = 1; - t->offset(-xo * 1, -yo * 1, -zo * 1); + float br = 1.0f; + float s = 1.0f; + t->offset(-xo, -yo, -zo); #ifdef __PSVITA__ - // AP - this will set up the 4 vertices in half the time - float Alpha = ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel; - int tex2 = - (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; - t->tileRainQuad( - x - xa + 0.5, yy0, z - za + 0.5, 0 * s, - yy0 * s / 4.0f + ra * s, x + xa + 0.5, yy0, - z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s, - x + xa + 0.5, yy1, z + za + 0.5, 1 * s, - yy1 * s / 4.0f + ra * s, x - xa + 0.5, yy1, - z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s, br, br, - br, Alpha, br, br, br, 0, tex2); + float Alpha = ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel; + int tex2 = (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; + t->tileRainQuad( + x - xa + 0.5, yy0, z - za + 0.5, 0 * s, yy0 * s / 4.0f + ra * s, + x + xa + 0.5, yy0, z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s, + x + xa + 0.5, yy1, z + za + 0.5, 1 * s, yy1 * s / 4.0f + ra * s, + x - xa + 0.5, yy1, z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s, + br, br, br, Alpha, br, br, br, 0, tex2); #else - t->tex2(level->getLightColor(x, yl, z, 0)); - t->color(br, br, br, - ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel); - t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s, - yy0 * s / 4.0f + ra * s); - t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s, - yy0 * s / 4.0f + ra * s); - // 4jcraft: this color call made rain invisible - // t->color(br, br, br, 0.0f); - // // 4J - added to soften the top visible edge of the rain - t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s, - yy1 * s / 4.0f + ra * s); - t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s, - yy1 * s / 4.0f + ra * s); + t->tex2(level->getLightColor(x, yl, z, 0)); + t->color(br, br, br, ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel); + t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s, + yy0 * s / 4.0f + ra * s); + t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s, + yy0 * s / 4.0f + ra * s); + t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s, + yy1 * s / 4.0f + ra * s); + t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s, + yy1 * s / 4.0f + ra * s); #endif - t->offset(0, 0, 0); - t->end(); - } else { - if (mode != 1) { - if (mode >= 0) t->end(); - mode = 1; - mc->textures->bindTexture(&SNOW_LOCATION); - t->begin(); - } - float ra = (((_tick) & 511) + a) / 512.0f; - float uo = random->nextFloat() + - time * 0.01f * (float)random->nextGaussian(); - float vo = random->nextFloat() + - time * (float)random->nextGaussian() * 0.001f; - double xd = (x + 0.5f) - player->x; - double zd = (z + 0.5f) - player->z; - float dd = (float)sqrt(xd * xd + zd * zd) / r; - float br = 1; - t->offset(-xo * 1, -yo * 1, -zo * 1); -#ifdef __PSVITA__ - // AP - this will set up the 4 vertices in half the time - float Alpha = ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel; - int tex2 = - (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; - t->tileRainQuad( - x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, - yy0 * s / 4.0f + ra * s + vo, x + xa + 0.5, yy0, - z + za + 0.5, 1 * s + uo, yy0 * s / 4.0f + ra * s + vo, - x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, - yy1 * s / 4.0f + ra * s + vo, x - xa + 0.5, yy1, - z - za + 0.5, 0 * s + uo, yy1 * s / 4.0f + ra * s + vo, - br, br, br, Alpha, br, br, br, Alpha, tex2); -#else - t->tex2((level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / - 4); - t->color(br, br, br, - ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel); - t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, - yy0 * s / 4.0f + ra * s + vo); - t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s + uo, - yy0 * s / 4.0f + ra * s + vo); - t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, - yy1 * s / 4.0f + ra * s + vo); - t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, - yy1 * s / 4.0f + ra * s + vo); -#endif - t->offset(0, 0, 0); - } - } + t->offset(0, 0, 0); } + } + t->end(); // single submit for all rain geometry + // sno time + mc->textures->bindTexture(&SNOW_LOCATION); + t->begin(); + for (int x = x0 - r; x <= x0 + r; x++) { + for (int z = z0 - r; z <= z0 + r; z++) { + int rainSlot = (z - z0 + 16) * 32 + (x - x0 + 16); + float xa = rainXa[rainSlot] * 0.5f; + float za = rainZa[rainSlot] * 0.5f; + + Biome* b = level->getBiome(x, z); + if (!b->hasRain() && !b->hasSnow()) continue; + + int floor = level->getTopRainBlock(x, z); + int yy0 = y0 - r; + int yy1 = y0 + r; + if (yy0 < floor) yy0 = floor; + if (yy1 < floor) yy1 = floor; + if (yy0 == yy1) continue; + + int yl = floor; + if (yl < yMin) yl = yMin; + + float temp = b->getTemperature(); + // only draw snow (not rain) in this pass + if (level->getBiomeSource()->scaleTemp(temp, floor) >= 0.15f) + continue; + + random->setSeed((x * x * 3121 + x * 45238971) ^ + (z * z * 418711 + z * 13761)); + + float ra = (((_tick) & 511) + a) / 512.0f; + float uo = random->nextFloat() + + time * 0.01f * (float)random->nextGaussian(); + float vo = random->nextFloat() + + time * (float)random->nextGaussian() * 0.001f; + + double xd = (x + 0.5f) - player->x; + double zd = (z + 0.5f) - player->z; + float dd = (float)sqrt(xd * xd + zd * zd) / r; + + float br = 1.0f; + float s = 1.0f; + t->offset(-xo, -yo, -zo); +#ifdef __PSVITA__ + float Alpha = ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel; + int tex2 = (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; + t->tileRainQuad( + x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, + yy0 * s / 4.0f + ra * s + vo, x + xa + 0.5, yy0, z + za + 0.5, + 1 * s + uo, yy0 * s / 4.0f + ra * s + vo, x + xa + 0.5, yy1, + z + za + 0.5, 1 * s + uo, yy1 * s / 4.0f + ra * s + vo, + x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, + yy1 * s / 4.0f + ra * s + vo, br, br, br, Alpha, br, br, br, + Alpha, tex2); +#else + t->tex2((level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4); + t->color(br, br, br, ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel); + t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, + yy0 * s / 4.0f + ra * s + vo); + t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s + uo, + yy0 * s / 4.0f + ra * s + vo); + t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, + yy1 * s / 4.0f + ra * s + vo); + t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, + yy1 * s / 4.0f + ra * s + vo); +#endif + t->offset(0, 0, 0); + } + } + t->end(); // single submit for all snow geometry - if (mode >= 0) t->end(); glEnable(GL_CULL_FACE); glDisable(GL_BLEND); glAlphaFunc(GL_GREATER, 0.1f); @@ -1846,7 +1859,8 @@ void GameRenderer::setupGuiScreen(int forceScale /*=-1*/) { // to ensure GUI scales correctly after a window resize. ScreenSizeCalculator ssc(mc->options, fbw, fbh, forceScale); - // 4jcraft: Java GUI screens still assume a clean 2D fixed-function style state. + // 4jcraft: Java GUI screens still assume a clean 2D fixed-function style + // state. RenderManager.StateSetFaceCull(false); glDisable(GL_LIGHTING); glDisable(GL_FOG); From 48935be0fa723b9ede60dfa5562063834c13eff8 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 19:55:49 +0100 Subject: [PATCH 56/71] format meson.options --- meson.options | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/meson.options b/meson.options index 3171bc298..cdf6457be 100644 --- a/meson.options +++ b/meson.options @@ -1,21 +1,27 @@ -option('ui_backend', - type : 'combo', +option( + 'ui_backend', + type: 'combo', choices: ['shiggy', 'java'], - value : 'shiggy', - description : 'Specifies a backend implementation for the game UI.') + value: 'shiggy', + description: 'Specifies a backend implementation for the game UI.', +) option('classic_panorama', type : 'boolean', value : false, description : 'Enable classic java edition panorama (ui_backend=java ONLY).') -option('renderer', - type : 'combo', +option( + 'renderer', + type: 'combo', choices: ['gl3', 'gles'], - value : 'gl3', - description : 'Specifies a rendering implementation for the game.') + value: 'gl3', + description: 'Specifies a rendering implementation for the game.', +) -option('enable_vsync', - type : 'boolean', - value : true, - description : 'Toggles V-Sync and adds options to unlock maximum in-game framerate.') +option( + 'enable_vsync', + type: 'boolean', + value: true, + description: 'Toggles V-Sync and adds options to unlock maximum in-game framerate.', +) \ No newline at end of file From 5e7bba862cd22c3519128fbd40e716230a3fab9a Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sat, 28 Mar 2026 21:00:30 +0100 Subject: [PATCH 57/71] perf fix culler thing --- Minecraft.Client/Rendering/LevelRenderer.cpp | 63 ++++++++++++++------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 03bb23057..22ac311fb 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -846,6 +846,11 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { int count = 0; ClipChunk* pClipChunk = chunks[playerIndex].data; unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; + static thread_local std::vector sortList; + sortList.clear(); + if (sortList.capacity() < (size_t)chunks[playerIndex].length) { + sortList.reserve(chunks[playerIndex].length); + } for (int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++) { if (!pClipChunk->visible) continue; // This will be set if the chunk isn't visible, or isn't @@ -854,17 +859,47 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { continue; // Not sure if we should ever encounter this... TODO // check if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == emptyFlag) - continue; // Check that this particular layer isn't empty + continue; - // List can be calculated directly from the chunk's global idex - int list = pClipChunk->globalIdx * 2 + layer; + sortList.push_back(pClipChunk); + } + // he sorts me till i + std::sort(sortList.begin(), sortList.end(), + [xOff, yOff, zOff, layer](ClipChunk* a, ClipChunk* b) { + float dxA = (float)((a->chunk->x + 8.0f) - xOff); + float dyA = (float)((a->chunk->y + 8.0f) - yOff); + float dzA = (float)((a->chunk->z + 8.0f) - zOff); + float distSqA = dxA * dxA + dyA * dyA + dzA * dzA; + + float dxB = (float)((b->chunk->x + 8.0f) - xOff); + float dyB = (float)((b->chunk->y + 8.0f) - yOff); + float dzB = (float)((b->chunk->z + 8.0f) - zOff); + float distSqB = dxB * dxB + dyB * dyB + dzB * dzB; + + if (layer == 0) + return distSqA < distSqB; // Opaque: Closest first + return distSqA > distSqB; // Transparent: Furthest first + }); + + for (ClipChunk* chunk : sortList) { + // ugly occluder + float dx = (chunk->chunk->x + 8.0f) - (float)xOff; + float dy = (chunk->chunk->y + 8.0f) - (float)yOff; + float dz = (chunk->chunk->z + 8.0f) - (float)zOff; + bool isVeryNear = (dx * dx + dy * dy + dz * dz) < (16.0f * 16.0f); + + if (!isVeryNear && layer == 0) { + // todo: occlusion flag + } + + int list = chunk->globalIdx * 2 + layer; list += chunkLists; // 4jcraft: replaced glPushMatrix/glTranslatef/glPopMatrix per chunk // no more full MVP upload per chunk, can also be bkwards compat - RenderManager.SetChunkOffset((float)pClipChunk->chunk->x, - (float)pClipChunk->chunk->y, - (float)pClipChunk->chunk->z); + RenderManager.SetChunkOffset((float)chunk->chunk->x, + (float)chunk->chunk->y, + (float)chunk->chunk->z); if (RenderManager.CBuffCall(list, first)) { first = false; @@ -945,31 +980,23 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { double yOff = player->yOld + (player->y - player->yOld) * alpha; double zOff = player->zOld + (player->z - player->zOld) * alpha; + for (int l = 0; l < RENDERLISTS_LENGTH; l++) renderLists[l].clear(); int lists = 0; - for (int l = 0; l < RENDERLISTS_LENGTH; l++) { - renderLists[l].clear(); - } - - AUTO_VAR(itEnd, _renderChunks.end()); - for (AUTO_VAR(it, _renderChunks.begin()); it != itEnd; it++) { - Chunk* chunk = *it; //_renderChunks[i]; - + for (auto it = _renderChunks.begin(); it != _renderChunks.end(); it++) { + Chunk* chunk = *it; int list = -1; for (int l = 0; l < lists; l++) { if (renderLists[l].isAt(chunk->xRender, chunk->yRender, - chunk->zRender)) { + chunk->zRender)) list = l; - } } if (list < 0) { list = lists++; renderLists[list].init(chunk->xRender, chunk->yRender, chunk->zRender, xOff, yOff, zOff); } - renderLists[list].add(chunk->getList(layer)); } - renderSameAsLast(layer, alpha); #endif From 21092cb686c9518b52263fec31b95b49270e4ef1 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 18:08:27 -0500 Subject: [PATCH 58/71] fix: stub IggyValueGetTypeRS --- Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h b/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h index 701ad994f..a86f7a0ae 100644 --- a/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h +++ b/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h @@ -254,6 +254,14 @@ RADEXPFUNC inline void* RADEXPLINK IggyPerfmonCreate( } RADEXPFUNC inline void RADEXPLINK IggyInstallPerfmon(void* perfmon) { STUBBED; } +RADEXPFUNC IggyResult RADEXPLINK IggyValueGetTypeRS(IggyValuePath* var, + IggyName sub_name, + char const* sub_name_utf8, + IggyDatatype* result) { + STUBBED; + return IGGY_RESULT_SUCCESS; +} + // GDraw memory/warning functions are defined in gdraw_glfw.c (C linkage) // Juicey you stupid idiot do NOT define them here From 94a67d41b678ed934be49bf1391dd9e9e8c9ce80 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 28 Mar 2026 18:10:55 -0500 Subject: [PATCH 59/71] inline that --- Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h b/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h index a86f7a0ae..ca0c47ff1 100644 --- a/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h +++ b/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h @@ -254,7 +254,7 @@ RADEXPFUNC inline void* RADEXPLINK IggyPerfmonCreate( } RADEXPFUNC inline void RADEXPLINK IggyInstallPerfmon(void* perfmon) { STUBBED; } -RADEXPFUNC IggyResult RADEXPLINK IggyValueGetTypeRS(IggyValuePath* var, +RADEXPFUNC inline IggyResult RADEXPLINK IggyValueGetTypeRS(IggyValuePath* var, IggyName sub_name, char const* sub_name_utf8, IggyDatatype* result) { From f499eaee9c34b4ff0d12ec05101b31a6e22d2c53 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Sun, 29 Mar 2026 10:42:12 +1100 Subject: [PATCH 60/71] chore(linux): add diagnostics for iggy render-target exhaustion --- .../Windows64/Iggy/gdraw/gdraw_gl_shared.inl | 106 +++++++++++++++++- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl b/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl index a7a965d19..e24dd7202 100644 --- a/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl +++ b/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl @@ -653,6 +653,90 @@ static void RADLINK gdraw_DescribeVertexBuffer(GDrawVertexBuffer *vbuf, GDraw_Ve // Create/free (or cache) render targets // +#ifdef __linux__ +typedef struct +{ + S32 free_count; + S32 live_count; + S32 locked_count; + S32 dead_count; + S32 pinned_count; + S32 user_owned_count; + S32 alloc_count; +} GDrawHandleStateCounts; + +enum +{ + GDRAW_RT_DIAG_color_memory = 1 << 0, + GDRAW_RT_DIAG_color_handles = 1 << 1, + GDRAW_RT_DIAG_cache_bitmap = 1 << 2, + GDRAW_RT_DIAG_stack_depth = 1 << 3, + GDRAW_RT_DIAG_empty_request = 1 << 4, +}; + +static U32 gdraw_rt_diag_emitted = 0; + +static void gdraw_CountHandleStates(GDrawHandleCache *cache, + GDrawHandleStateCounts *counts) +{ + S32 i; + counts->free_count = 0; + counts->live_count = 0; + counts->locked_count = 0; + counts->dead_count = 0; + counts->pinned_count = 0; + counts->user_owned_count = 0; + counts->alloc_count = 0; + + if (!cache) + return; + + for (i = 0; i < cache->max_handles; ++i) { + switch (cache->handle[i].state) { + case GDRAW_HANDLE_STATE_free: ++counts->free_count; break; + case GDRAW_HANDLE_STATE_live: ++counts->live_count; break; + case GDRAW_HANDLE_STATE_locked: ++counts->locked_count; break; + case GDRAW_HANDLE_STATE_dead: ++counts->dead_count; break; + case GDRAW_HANDLE_STATE_pinned: ++counts->pinned_count; break; + case GDRAW_HANDLE_STATE_user_owned: ++counts->user_owned_count; break; + case GDRAW_HANDLE_STATE_alloc: ++counts->alloc_count; break; + default: break; + } + } +} + +static void gdraw_ReportHandleCacheDiag(char const *label, + GDrawHandleCache *cache, + U32 once_bit, + S32 req_w, + S32 req_h) +{ + GDrawHandleStateCounts counts; + + if (gdraw_rt_diag_emitted & once_bit) + return; + gdraw_rt_diag_emitted |= once_bit; + + gdraw_CountHandleStates(cache, &counts); + IggyGDrawSendWarning(NULL, + "GDraw[%s] diag: frame=%dx%d tile=%dx%d padded=%dx%d req=%dx%d depth=%d bytes_free=%d total_bytes=%d handles free=%d live=%d locked=%d dead=%d pinned=%d user=%d alloc=%d", + label, + gdraw->frametex_width, gdraw->frametex_height, + gdraw->tw, gdraw->th, + gdraw->tpw, gdraw->tph, + req_w, req_h, + (int) (gdraw->cur - gdraw->frame), + cache ? cache->bytes_free : 0, + cache ? cache->total_bytes : 0, + counts.free_count, counts.live_count, counts.locked_count, + counts.dead_count, counts.pinned_count, counts.user_owned_count, + counts.alloc_count); +} +#else +#define gdraw_ReportHandleCacheDiag(label, cache, once_bit, req_w, req_h) \ + ((void) 0) +#endif + static GDrawHandle *get_rendertarget_texture(int width, int height, void *owner, GDrawStats *gstats) { S32 size; @@ -696,13 +780,17 @@ static GDrawHandle *get_color_rendertarget(GDrawStats *gstats) // ran out of RTs, allocate a new one size = gdraw->frametex_width * gdraw->frametex_height * 4; if (gdraw->rendertargets.bytes_free < size) { - IggyGDrawSendWarning(NULL, "GDraw exceeded available rendertarget memory"); + gdraw_ReportHandleCacheDiag("rt-color-memory", &gdraw->rendertargets, + GDRAW_RT_DIAG_color_memory, gdraw->tpw, gdraw->tph); + IggyGDrawSendWarning(NULL, "GDraw[rt-color] exceeded available rendertarget memory"); return NULL; } t = gdraw_HandleCacheAllocateBegin(&gdraw->rendertargets); if (!t) { - IggyGDrawSendWarning(NULL, "GDraw exceeded available rendertarget handles"); + gdraw_ReportHandleCacheDiag("rt-color-handles", &gdraw->rendertargets, + GDRAW_RT_DIAG_color_handles, gdraw->tpw, gdraw->tph); + IggyGDrawSendWarning(NULL, "GDraw[rt-color] exceeded available rendertarget handles"); return t; } @@ -1010,25 +1098,31 @@ static rrbool RADLINK gdraw_TextureDrawBufferBegin(gswf_recti *region, gdraw_tex GDrawHandle *t; int k; if (gdraw->tw == 0 || gdraw->th == 0) { - IggyGDrawSendWarning(NULL, "GDraw got a request for an empty rendertarget"); + gdraw_ReportHandleCacheDiag("rt-empty", &gdraw->rendertargets, + GDRAW_RT_DIAG_empty_request, region->x1 - region->x0, region->y1 - region->y0); + IggyGDrawSendWarning(NULL, "GDraw[rt-stack] got a request for an empty rendertarget"); return false; } if (n >= &gdraw->frame[MAX_RENDER_STACK_DEPTH]) { - IggyGDrawSendWarning(NULL, "GDraw rendertarget nesting exceeded MAX_RENDER_STACK_DEPTH"); + gdraw_ReportHandleCacheDiag("rt-stack-depth", &gdraw->rendertargets, + GDRAW_RT_DIAG_stack_depth, region->x1 - region->x0, region->y1 - region->y0); + IggyGDrawSendWarning(NULL, "GDraw[rt-stack] rendertarget nesting exceeded MAX_RENDER_STACK_DEPTH"); return false; } if (owner) { t = get_rendertarget_texture(region->x1 - region->x0, region->y1 - region->y0, owner, gstats); if (!t) { - IggyGDrawSendWarning(NULL, "GDraw ran out of rendertargets for cacheAsBItmap"); + gdraw_ReportHandleCacheDiag("rt-cacheAsBitmap", gdraw->texturecache, + GDRAW_RT_DIAG_cache_bitmap, region->x1 - region->x0, region->y1 - region->y0); + IggyGDrawSendWarning(NULL, "GDraw[rt-cacheAsBitmap] ran out of rendertargets"); return false; } } else { t = get_color_rendertarget(gstats); if (!t) { - IggyGDrawSendWarning(NULL, "GDraw ran out of rendertargets"); + IggyGDrawSendWarning(NULL, "GDraw[rt-color] ran out of rendertargets"); return false; } } From b5b4fc34fec65608d567d256107bb224c78a7202 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Sun, 29 Mar 2026 11:19:28 +1100 Subject: [PATCH 61/71] fix(linux): increase iggy render-target budget to 64mb --- Minecraft.Client/Platform/Linux/Linux_UIController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp index 4d99f518f..855c10b9d 100644 --- a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp @@ -48,7 +48,7 @@ void ConsoleUIController::init(S32 w, S32 h) { gdraw_GL_SetResourceLimits(GDRAW_GL_RESOURCE_texture, 5000, 128 * 1024 * 1024); gdraw_GL_SetResourceLimits(GDRAW_GL_RESOURCE_rendertarget, 10, - 32 * 1024 * 1024); + 64 * 1024 * 1024); IggySetGDraw(gdraw_funcs); #endif From 47b7d90835d1368ca08140dd06fe0e133f764c71 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:10:19 +1100 Subject: [PATCH 62/71] feat(profiling): add low-overhead in-engine frame profiler --- Minecraft.Client/Rendering/GameRenderer.cpp | 73 +- Minecraft.Client/Rendering/LevelRenderer.cpp | 749 ++++++++++--------- Minecraft.Client/Utils/FrameProfiler.cpp | 260 +++++++ Minecraft.Client/Utils/FrameProfiler.h | 93 +++ Minecraft.Client/meson.build | 6 +- meson.options | 9 +- 6 files changed, 816 insertions(+), 374 deletions(-) create mode 100644 Minecraft.Client/Utils/FrameProfiler.cpp create mode 100644 Minecraft.Client/Utils/FrameProfiler.h diff --git a/Minecraft.Client/Rendering/GameRenderer.cpp b/Minecraft.Client/Rendering/GameRenderer.cpp index 617e67dc2..6a2272f7c 100644 --- a/Minecraft.Client/Rendering/GameRenderer.cpp +++ b/Minecraft.Client/Rendering/GameRenderer.cpp @@ -50,6 +50,7 @@ #include "../Textures/Packs/TexturePackRepository.h" #include "../Textures/Packs/TexturePack.h" #include "../Textures/TextureAtlas.h" +#include "../Utils/FrameProfiler.h" bool GameRenderer::anaglyph3d = false; int GameRenderer::anaglyphPass = 0; @@ -776,6 +777,7 @@ void GameRenderer::renderItemInHand(float a, int eye) { // 4J - change brought forward from 1.8.2 void GameRenderer::turnOffLightLayer(double alpha) { // 4J - TODO + FRAME_PROFILE_SCOPE(Lightmap); #ifdef __linux__ if (SharedConstants::TEXTURE_LIGHTING) { LinuxLogStubLightmapProbe(); @@ -807,6 +809,7 @@ void GameRenderer::turnOffLightLayer(double alpha) { // 4J - TODO void GameRenderer::turnOnLightLayer( double alpha, bool scaleLight) { // 4jcraft: added scaleLight for entity lighting + FRAME_PROFILE_SCOPE(Lightmap); #ifdef __linux__ if (!SharedConstants::TEXTURE_LIGHTING) return; @@ -881,6 +884,7 @@ void GameRenderer::tickLightTexture() { } void GameRenderer::updateLightTexture(float a) { + FRAME_PROFILE_SCOPE(Lightmap); // 4J-JEV: Now doing light textures on PER PLAYER basis. // 4J - we *had* added separate light textures for all dimensions, and this // loop to update them all here @@ -1025,6 +1029,8 @@ int GameRenderer::getLightTexture(int iPad, Level* level) { } void GameRenderer::render(float a, bool bFirst) { + FRAME_PROFILE_FRAME_SCOPE(); + if (_updateLightTexture && bFirst) updateLightTexture(a); if (Display::isActive()) { lastActiveTime = System::currentTimeMillis(); @@ -1083,6 +1089,7 @@ void GameRenderer::render(float a, bool bFirst) { lastNsTime = System::nanoTime(); if (!mc->options->hideGui || mc->screen != NULL) { + FRAME_PROFILE_SCOPE(UIHud); mc->gui->render(a, mc->screen != NULL, xMouse, yMouse); } } else { @@ -1097,6 +1104,7 @@ void GameRenderer::render(float a, bool bFirst) { } if (mc->screen != NULL) { + FRAME_PROFILE_SCOPE(UIHud); glClear(GL_DEPTH_BUFFER_BIT); mc->screen->render(xMouse, yMouse, a); if (mc->screen != NULL && mc->screen->particles != NULL) @@ -1244,6 +1252,8 @@ void GameRenderer::DisableUpdateThread() { } void GameRenderer::renderLevel(float a, int64_t until) { + FRAME_PROFILE_SCOPE(World); + // if (updateLightTexture) updateLightTexture(); // 4J - TODO - // Java 1.0.1 has this line enabled, should check why - don't want to put it // in now in case it breaks split-screen @@ -1296,9 +1306,12 @@ void GameRenderer::renderLevel(float a, int64_t until) { Frustum::getFrustum(); if (mc->options->viewDistance < 2) { setupFog(-1, a); - levelRenderer->renderSky(a); - if (mc->skins->getSelected()->getId() == 1026) - levelRenderer->renderHaloRing(a); + { + FRAME_PROFILE_SCOPE(WeatherSky); + levelRenderer->renderSky(a); + if (mc->skins->getSelected()->getId() == 1026) + levelRenderer->renderHaloRing(a); + } } // 4jcraft: needs to be enabled for proper transparent texturing on low // render dists this was done in renderSky() for the far and normal @@ -1320,7 +1333,10 @@ void GameRenderer::renderLevel(float a, int64_t until) { MemSect(0); frustum->prepare(xOff, yOff, zOff); - mc->levelRenderer->cull(frustum, a); + { + FRAME_PROFILE_SCOPE(ChunkCull); + mc->levelRenderer->cull(frustum, a); + } PIXEndNamedEvent(); #ifndef MULTITHREAD_ENABLE @@ -1347,6 +1363,7 @@ void GameRenderer::renderLevel(float a, int64_t until) { #endif if (cameraEntity->y < Level::genDepth) { + FRAME_PROFILE_SCOPE(WeatherSky); prepareAndRenderClouds(levelRenderer, a); } Frustum::getFrustum(); // 4J added - re-calculate frustum as rendering @@ -1385,7 +1402,10 @@ void GameRenderer::renderLevel(float a, int64_t until) { cameraPos.x = cameraPosTemp.x; cameraPos.y = cameraPosTemp.y; cameraPos.z = cameraPosTemp.z; - levelRenderer->renderEntities(&cameraPos, frustum, a); + { + FRAME_PROFILE_SCOPE(Entity); + levelRenderer->renderEntities(&cameraPos, frustum, a); + } #ifdef __PSVITA__ // AP - make sure we're using the Alpha cut out effect for particles glEnable(GL_ALPHA_TEST); @@ -1393,12 +1413,18 @@ void GameRenderer::renderLevel(float a, int64_t until) { PIXEndNamedEvent(); PIXBeginNamedEvent(0, "Particle render"); turnOnLightLayer(a); // 4J - brought forward from 1.8.2 - particleEngine->renderLit(cameraEntity, a, - ParticleEngine::OPAQUE_LIST); + { + FRAME_PROFILE_SCOPE(Particle); + particleEngine->renderLit(cameraEntity, a, + ParticleEngine::OPAQUE_LIST); + } Lighting::turnOff(); setupFog(0, a); - particleEngine->render(cameraEntity, a, - ParticleEngine::OPAQUE_LIST); + { + FRAME_PROFILE_SCOPE(Particle); + particleEngine->render(cameraEntity, a, + ParticleEngine::OPAQUE_LIST); + } PIXEndNamedEvent(); turnOffLightLayer(a); // 4J - brought forward from 1.8.2 @@ -1467,12 +1493,18 @@ void GameRenderer::renderLevel(float a, int64_t until) { PIXBeginNamedEvent(0, "Particle render (translucent)"); Lighting::turnOn(); turnOnLightLayer(a); // 4J - brought forward from 1.8.2 - particleEngine->renderLit(cameraEntity, a, - ParticleEngine::TRANSLUCENT_LIST); + { + FRAME_PROFILE_SCOPE(Particle); + particleEngine->renderLit(cameraEntity, a, + ParticleEngine::TRANSLUCENT_LIST); + } Lighting::turnOff(); setupFog(0, a); - particleEngine->render(cameraEntity, a, - ParticleEngine::TRANSLUCENT_LIST); + { + FRAME_PROFILE_SCOPE(Particle); + particleEngine->render(cameraEntity, a, + ParticleEngine::TRANSLUCENT_LIST); + } PIXEndNamedEvent(); turnOffLightLayer(a); // 4J - brought forward from 1.8.2 ////////////////////////// End of 4J added section @@ -1503,12 +1535,16 @@ void GameRenderer::renderLevel(float a, int64_t until) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); - levelRenderer->renderDestroyAnimation( - Tesselator::getInstance(), - std::dynamic_pointer_cast(cameraEntity), a); + { + FRAME_PROFILE_SCOPE(WeatherSky); + levelRenderer->renderDestroyAnimation( + Tesselator::getInstance(), + std::dynamic_pointer_cast(cameraEntity), a); + } glDisable(GL_BLEND); if (cameraEntity->y >= Level::genDepth) { + FRAME_PROFILE_SCOPE(WeatherSky); prepareAndRenderClouds(levelRenderer, a); } @@ -1517,7 +1553,10 @@ void GameRenderer::renderLevel(float a, int64_t until) { setupFog(0, a); glEnable(GL_FOG); PIXBeginNamedEvent(0, "Rendering snow and rain"); - renderSnowAndRain(a); + { + FRAME_PROFILE_SCOPE(WeatherSky); + renderSnowAndRain(a); + } PIXEndNamedEvent(); glDisable(GL_FOG); diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 22ac311fb..3da404243 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -63,6 +63,7 @@ #include "../Level/MultiPlayerLevel.h" #include "../../Minecraft.World/Util/SoundTypes.h" #include "FrustumCuller.h" +#include "../Utils/FrameProfiler.h" // #define DISABLE_SPU_CODE @@ -736,6 +737,8 @@ void LevelRenderer::resortChunks(int xc, int yc, int zc) { int LevelRenderer::render(std::shared_ptr player, int layer, double alpha, bool updateChunks) { + FRAME_PROFILE_SCOPE(Terrain); + int playerIndex = mc->player->GetXboxPad(); // 4J - added - if the number of players has changed, we need to rebuild @@ -851,49 +854,55 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { if (sortList.capacity() < (size_t)chunks[playerIndex].length) { sortList.reserve(chunks[playerIndex].length); } - for (int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++) { - if (!pClipChunk->visible) - continue; // This will be set if the chunk isn't visible, or isn't - // compiled, or has both empty flags set - if (pClipChunk->globalIdx == -1) - continue; // Not sure if we should ever encounter this... TODO - // check - if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == emptyFlag) - continue; + { + FRAME_PROFILE_SCOPE(ChunkCollect); + for (int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++) { + if (!pClipChunk->visible) + continue; // This will be set if the chunk isn't visible, or + // isn't compiled, or has both empty flags set + if (pClipChunk->globalIdx == -1) + continue; // Not sure if we should ever encounter this... + // TODO check + if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == + emptyFlag) + continue; - sortList.push_back(pClipChunk); - } - // he sorts me till i - std::sort(sortList.begin(), sortList.end(), - [xOff, yOff, zOff, layer](ClipChunk* a, ClipChunk* b) { - float dxA = (float)((a->chunk->x + 8.0f) - xOff); - float dyA = (float)((a->chunk->y + 8.0f) - yOff); - float dzA = (float)((a->chunk->z + 8.0f) - zOff); - float distSqA = dxA * dxA + dyA * dyA + dzA * dzA; - - float dxB = (float)((b->chunk->x + 8.0f) - xOff); - float dyB = (float)((b->chunk->y + 8.0f) - yOff); - float dzB = (float)((b->chunk->z + 8.0f) - zOff); - float distSqB = dxB * dxB + dyB * dyB + dzB * dzB; - - if (layer == 0) - return distSqA < distSqB; // Opaque: Closest first - return distSqA > distSqB; // Transparent: Furthest first - }); - - for (ClipChunk* chunk : sortList) { - // ugly occluder - float dx = (chunk->chunk->x + 8.0f) - (float)xOff; - float dy = (chunk->chunk->y + 8.0f) - (float)yOff; - float dz = (chunk->chunk->z + 8.0f) - (float)zOff; - bool isVeryNear = (dx * dx + dy * dy + dz * dz) < (16.0f * 16.0f); - - if (!isVeryNear && layer == 0) { - // todo: occlusion flag + sortList.push_back(pClipChunk); } + // he sorts me till i + std::sort(sortList.begin(), sortList.end(), + [xOff, yOff, zOff, layer](ClipChunk* a, ClipChunk* b) { + float dxA = (float)((a->chunk->x + 8.0f) - xOff); + float dyA = (float)((a->chunk->y + 8.0f) - yOff); + float dzA = (float)((a->chunk->z + 8.0f) - zOff); + float distSqA = dxA * dxA + dyA * dyA + dzA * dzA; - int list = chunk->globalIdx * 2 + layer; - list += chunkLists; + float dxB = (float)((b->chunk->x + 8.0f) - xOff); + float dyB = (float)((b->chunk->y + 8.0f) - yOff); + float dzB = (float)((b->chunk->z + 8.0f) - zOff); + float distSqB = dxB * dxB + dyB * dyB + dzB * dzB; + + if (layer == 0) + return distSqA < distSqB; // Opaque: Closest first + return distSqA > distSqB; // Transparent: Furthest + // first + }); + } + { + FRAME_PROFILE_SCOPE(ChunkPlayback); + for (ClipChunk* chunk : sortList) { + // ugly occluder + float dx = (chunk->chunk->x + 8.0f) - (float)xOff; + float dy = (chunk->chunk->y + 8.0f) - (float)yOff; + float dz = (chunk->chunk->z + 8.0f) - (float)zOff; + bool isVeryNear = (dx * dx + dy * dy + dz * dz) < (16.0f * 16.0f); + + if (!isVeryNear && layer == 0) { + // todo: occlusion flag + } + + int list = chunk->globalIdx * 2 + layer; + list += chunkLists; // 4jcraft: replaced glPushMatrix/glTranslatef/glPopMatrix per chunk // no more full MVP upload per chunk, can also be bkwards compat @@ -901,12 +910,13 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { (float)chunk->chunk->y, (float)chunk->chunk->z); - if (RenderManager.CBuffCall(list, first)) { - first = false; + if (RenderManager.CBuffCall(list, first)) { + first = false; + } + count++; } - count++; + RenderManager.SetChunkOffset(0.f, 0.f, 0.f); } - RenderManager.SetChunkOffset(0.f, 0.f, 0.f); #ifdef __PSVITA__ // AP - alpha cut out is expensive on vita. Now we render all the alpha cut @@ -917,25 +927,29 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { first = true; pClipChunk = chunks[playerIndex].data; emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; - for (int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++) { - if (!pClipChunk->visible) - continue; // This will be set if the chunk isn't visible, or isn't - // compiled, or has both empty flags set - if (pClipChunk->globalIdx == -1) - continue; // Not sure if we should ever encounter this... TODO - // check - if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == emptyFlag) - continue; // Check that this particular layer isn't empty - if (!(globalChunkFlags[pClipChunk->globalIdx] & - LevelRenderer::CHUNK_FLAG_CUT_OUT)) - continue; // Does this chunk contain any cut out geometry + { + FRAME_PROFILE_SCOPE(ChunkPlayback); + for (int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++) { + if (!pClipChunk->visible) + continue; // This will be set if the chunk isn't visible, or + // isn't compiled, or has both empty flags set + if (pClipChunk->globalIdx == -1) + continue; // Not sure if we should ever encounter this... TODO + // check + if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == + emptyFlag) + continue; // Check that this particular layer isn't empty + if (!(globalChunkFlags[pClipChunk->globalIdx] & + LevelRenderer::CHUNK_FLAG_CUT_OUT)) + continue; // Does this chunk contain any cut out geometry - // List can be calculated directly from the chunk's global idex - int list = pClipChunk->globalIdx * 2 + layer; - list += chunkLists; + // List can be calculated directly from the chunk's global idex + int list = pClipChunk->globalIdx * 2 + layer; + list += chunkLists; - if (RenderManager.CBuffCallCutOut(list, first)) { - first = false; + if (RenderManager.CBuffCallCutOut(list, first)) { + first = false; + } } } RenderManager.StateSetForceLOD(-1); // AP - back to normal mipmapping @@ -1959,312 +1973,319 @@ bool LevelRenderer::updateDirtyChunks() { // Set a flag if we should only rebuild existing chunks, not create anything // new - unsigned int memAlloc = RenderManager.CBuffSize(-1); - /* - static int throttle = 0; - if( ( throttle % 100 ) == 0 ) { - app.DebugPrintf("CBuffSize: %d\n",memAlloc/(1024*1024)); - } - throttle++; - */ - PIXAddNamedCounter(((float)memAlloc) / (1024.0f * 1024.0f), - "Command buffer allocations"); - bool onlyRebuild = (memAlloc >= MAX_COMMANDBUFFER_ALLOCATIONS); - EnterCriticalSection(&m_csDirtyChunks); + FRAME_PROFILE_SCOPE(ChunkDirtyScan); - // Move any dirty chunks stored in the lock free stack into global flags - int index = 0; - - do { - // See comment on dirtyChunksLockFreeStack.Push() regarding details of - // this casting/subtracting -2. - index = (size_t)dirtyChunksLockFreeStack.Pop(); -#ifdef _CRITICAL_CHUNKS - int oldIndex = index; - index &= 0x0fffffff; // remove the top bit that marked the chunk as - // non-critical -#endif - if (index == 1) - dirtyChunkPresent = - true; // 1 is a special value passed to let this thread know - // that a chunk which isn't on this stack has been set to - // dirty - else if (index > 1) { - int i2 = index - 2; - if (i2 >= DIMENSION_OFFSETS[2]) { - i2 -= DIMENSION_OFFSETS[2]; - int y2 = i2 & (CHUNK_Y_COUNT - 1); - i2 /= CHUNK_Y_COUNT; - int z2 = i2 / MAX_LEVEL_RENDER_SIZE[2]; - int x2 = i2 - z2 * MAX_LEVEL_RENDER_SIZE[2]; - x2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; - z2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; - } - setGlobalChunkFlag(index - 2, CHUNK_FLAG_DIRTY); - -#ifdef _CRITICAL_CHUNKS - if (!(oldIndex & 0x10000000)) // was this chunk not marked as - // non-critical. Ugh double negatives - { - setGlobalChunkFlag(index - 2, CHUNK_FLAG_CRITICAL); - } -#endif - - dirtyChunkPresent = true; + unsigned int memAlloc = RenderManager.CBuffSize(-1); + /* + static int throttle = 0; + if( ( throttle % 100 ) == 0 ) + { + app.DebugPrintf("CBuffSize: %d\n",memAlloc/(1024*1024)); } - } while (index); + throttle++; + */ + PIXAddNamedCounter(((float)memAlloc) / (1024.0f * 1024.0f), + "Command buffer allocations"); + bool onlyRebuild = (memAlloc >= MAX_COMMANDBUFFER_ALLOCATIONS); + EnterCriticalSection(&m_csDirtyChunks); - // Only bother searching round all the chunks if we have some dirty chunk(s) - if (dirtyChunkPresent) { - lastDirtyChunkFound = System::currentTimeMillis(); - PIXBeginNamedEvent(0, "Finding nearest chunk\n"); + // Move any dirty chunks stored in the lock free stack into global flags + int index = 0; + + do { + // See comment on dirtyChunksLockFreeStack.Push() regarding details of + // this casting/subtracting -2. + index = (size_t)dirtyChunksLockFreeStack.Pop(); +#ifdef _CRITICAL_CHUNKS + int oldIndex = index; + index &= 0x0fffffff; // remove the top bit that marked the chunk as + // non-critical +#endif + if (index == 1) + dirtyChunkPresent = + true; // 1 is a special value passed to let this thread know + // that a chunk which isn't on this stack has been set to + // dirty + else if (index > 1) { + int i2 = index - 2; + if (i2 >= DIMENSION_OFFSETS[2]) { + i2 -= DIMENSION_OFFSETS[2]; + int y2 = i2 & (CHUNK_Y_COUNT - 1); + i2 /= CHUNK_Y_COUNT; + int z2 = i2 / MAX_LEVEL_RENDER_SIZE[2]; + int x2 = i2 - z2 * MAX_LEVEL_RENDER_SIZE[2]; + x2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; + z2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; + } + setGlobalChunkFlag(index - 2, CHUNK_FLAG_DIRTY); + +#ifdef _CRITICAL_CHUNKS + if (!(oldIndex & 0x10000000)) // was this chunk not marked as + // non-critical. Ugh double negatives + { + setGlobalChunkFlag(index - 2, CHUNK_FLAG_CRITICAL); + } +#endif + + dirtyChunkPresent = true; + } + } while (index); + + // Only bother searching round all the chunks if we have some dirty chunk(s) + if (dirtyChunkPresent) { + lastDirtyChunkFound = System::currentTimeMillis(); + PIXBeginNamedEvent(0, "Finding nearest chunk\n"); #if defined __PS3__ && !defined DISABLE_SPU_CODE - // find the nearest chunk with a spu task, copy all the data over here - // for uploading to SPU - g_findNearestChunkDataIn.numGlobalChunks = getGlobalChunkCount(); - g_findNearestChunkDataIn.pGlobalChunkFlags = globalChunkFlags; - g_findNearestChunkDataIn.onlyRebuild = onlyRebuild; - g_findNearestChunkDataIn.lowerOffset = - (int)&((LevelChunk*)0) - ->lowerBlocks; // dodgy bit of class structure poking, as we - // don't want to try and get the whole of - // LevelChunk copmpiling on SPU - g_findNearestChunkDataIn.upperOffset = - (int)&((LevelChunk*)0)->upperBlocks; - g_findNearestChunkDataIn.xChunks = xChunks; - g_findNearestChunkDataIn.yChunks = yChunks; - g_findNearestChunkDataIn.zChunks = zChunks; + // find the nearest chunk with a spu task, copy all the data over here + // for uploading to SPU + g_findNearestChunkDataIn.numGlobalChunks = getGlobalChunkCount(); + g_findNearestChunkDataIn.pGlobalChunkFlags = globalChunkFlags; + g_findNearestChunkDataIn.onlyRebuild = onlyRebuild; + g_findNearestChunkDataIn.lowerOffset = + (int)&((LevelChunk*)0) + ->lowerBlocks; // dodgy bit of class structure poking, as we + // don't want to try and get the whole of + // LevelChunk copmpiling on SPU + g_findNearestChunkDataIn.upperOffset = + (int)&((LevelChunk*)0)->upperBlocks; + g_findNearestChunkDataIn.xChunks = xChunks; + g_findNearestChunkDataIn.yChunks = yChunks; + g_findNearestChunkDataIn.zChunks = zChunks; - for (int i = 0; i < 4; i++) { - g_findNearestChunkDataIn.chunks[i] = - (LevelRenderer_FindNearestChunk_DataIn::ClipChunk*)chunks[i] - .data; - g_findNearestChunkDataIn.chunkLengths[i] = chunks[i].length; - g_findNearestChunkDataIn.level[i] = level[i]; - g_findNearestChunkDataIn.playerData[i].bValid = - mc->localplayers[i] != NULL; - if (mc->localplayers[i] != NULL) { - g_findNearestChunkDataIn.playerData[i].x = - mc->localplayers[i]->x; - g_findNearestChunkDataIn.playerData[i].y = - mc->localplayers[i]->y; - g_findNearestChunkDataIn.playerData[i].z = - mc->localplayers[i]->z; + for (int i = 0; i < 4; i++) { + g_findNearestChunkDataIn.chunks[i] = + (LevelRenderer_FindNearestChunk_DataIn::ClipChunk*)chunks[i] + .data; + g_findNearestChunkDataIn.chunkLengths[i] = chunks[i].length; + g_findNearestChunkDataIn.level[i] = level[i]; + g_findNearestChunkDataIn.playerData[i].bValid = + mc->localplayers[i] != NULL; + if (mc->localplayers[i] != NULL) { + g_findNearestChunkDataIn.playerData[i].x = + mc->localplayers[i]->x; + g_findNearestChunkDataIn.playerData[i].y = + mc->localplayers[i]->y; + g_findNearestChunkDataIn.playerData[i].z = + mc->localplayers[i]->z; + } + if (level[i] != NULL) { + g_findNearestChunkDataIn.multiplayerChunkCache[i].XZOFFSET = + ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZOFFSET; + g_findNearestChunkDataIn.multiplayerChunkCache[i].XZSIZE = + ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZSIZE; + g_findNearestChunkDataIn.multiplayerChunkCache[i].cache = + (void**)((MultiPlayerChunkCache*)(level[i]->chunkSource)) + ->cache; + } } - if (level[i] != NULL) { - g_findNearestChunkDataIn.multiplayerChunkCache[i].XZOFFSET = - ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZOFFSET; - g_findNearestChunkDataIn.multiplayerChunkCache[i].XZSIZE = - ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZSIZE; - g_findNearestChunkDataIn.multiplayerChunkCache[i].cache = - (void**)((MultiPlayerChunkCache*)(level[i]->chunkSource)) - ->cache; - } - } - // assert(sizeof(LevelRenderer_FindNearestChunk_DataIn::Chunk) - // == sizeof(Chunk)); - C4JSpursJob_LevelRenderer_FindNearestChunk findJob( - &g_findNearestChunkDataIn); - m_jobPort_FindNearestChunk->submitJob(&findJob); - m_jobPort_FindNearestChunk->waitForCompletion(); - nearChunk = (ClipChunk*)g_findNearestChunkDataIn.nearChunk; - veryNearCount = g_findNearestChunkDataIn.veryNearCount; + // assert(sizeof(LevelRenderer_FindNearestChunk_DataIn::Chunk) + // == sizeof(Chunk)); + C4JSpursJob_LevelRenderer_FindNearestChunk findJob( + &g_findNearestChunkDataIn); + m_jobPort_FindNearestChunk->submitJob(&findJob); + m_jobPort_FindNearestChunk->waitForCompletion(); + nearChunk = (ClipChunk*)g_findNearestChunkDataIn.nearChunk; + veryNearCount = g_findNearestChunkDataIn.veryNearCount; #else // __PS3__ #ifdef _LARGE_WORLDS - int maxNearestChunks = MAX_CONCURRENT_CHUNK_REBUILDS; - // 4J Stu - On XboxOne we should cut this down if in a constrained state - // so the saving threads get more time + int maxNearestChunks = MAX_CONCURRENT_CHUNK_REBUILDS; + // 4J Stu - On XboxOne we should cut this down if in a constrained state + // so the saving threads get more time #endif - // Find nearest chunk that is dirty - for (int p = 0; p < XUSER_MAX_COUNT; p++) { - // It's possible that the localplayers member can be set to NULL on - // the main thread when a player chooses to exit the game So take a - // reference to the player object now. As it is a shared_ptr it - // should live as long as we need it - std::shared_ptr player = mc->localplayers[p]; - if (player == NULL) continue; - if (chunks[p].data == NULL) continue; - if (level[p] == NULL) continue; - if (chunks[p].length != xChunks * zChunks * CHUNK_Y_COUNT) continue; - int px = (int)player->x; - int py = (int)player->y; - int pz = (int)player->z; + // Find nearest chunk that is dirty + for (int p = 0; p < XUSER_MAX_COUNT; p++) { + // It's possible that the localplayers member can be set to NULL on + // the main thread when a player chooses to exit the game So take a + // reference to the player object now. As it is a shared_ptr it + // should live as long as we need it + std::shared_ptr player = mc->localplayers[p]; + if (player == NULL) continue; + if (chunks[p].data == NULL) continue; + if (level[p] == NULL) continue; + if (chunks[p].length != xChunks * zChunks * CHUNK_Y_COUNT) continue; + int px = (int)player->x; + int py = (int)player->y; + int pz = (int)player->z; - // app.DebugPrintf("!! %d %d %d, %d %d %d {%d,%d} - //",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild, - // xChunks, zChunks); + // app.DebugPrintf("!! %d %d %d, %d %d %d {%d,%d} + //",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild, + // xChunks, zChunks); - int considered = 0; - int wouldBeNearButEmpty = 0; - for (int x = 0; x < xChunks; x++) { - for (int z = 0; z < zChunks; z++) { - for (int y = 0; y < CHUNK_Y_COUNT; y++) { - ClipChunk* pClipChunk = - &chunks[p][(z * yChunks + y) * xChunks + x]; - // Get distance to this chunk - deliberately not calling - // the chunk's method of doing this to avoid overheads - // (passing entitie, type conversion etc.) that this - // involves - int xd = pClipChunk->xm - px; - int yd = pClipChunk->ym - py; - int zd = pClipChunk->zm - pz; - int distSq = xd * xd + yd * yd + zd * zd; - int distSqWeighted = - xd * xd + yd * yd * 4 + - zd * - zd; // Weighting against y to prioritise things - // in same x/z plane as player first + int considered = 0; + int wouldBeNearButEmpty = 0; + for (int x = 0; x < xChunks; x++) { + for (int z = 0; z < zChunks; z++) { + for (int y = 0; y < CHUNK_Y_COUNT; y++) { + ClipChunk* pClipChunk = + &chunks[p][(z * yChunks + y) * xChunks + x]; + // Get distance to this chunk - deliberately not calling + // the chunk's method of doing this to avoid overheads + // (passing entitie, type conversion etc.) that this + // involves + int xd = pClipChunk->xm - px; + int yd = pClipChunk->ym - py; + int zd = pClipChunk->zm - pz; + int distSq = xd * xd + yd * yd + zd * zd; + int distSqWeighted = + xd * xd + yd * yd * 4 + + zd * + zd; // Weighting against y to prioritise things + // in same x/z plane as player first - if (globalChunkFlags[pClipChunk->globalIdx] & - CHUNK_FLAG_DIRTY) { - if ((!onlyRebuild) || - globalChunkFlags[pClipChunk->globalIdx] & - CHUNK_FLAG_COMPILED || - (distSq < - 20 * 20)) // Always rebuild really near things - // or else building (say) at tower - // up into empty blocks when we are - // low on memory will not create - // render data - { - considered++; - // Is this chunk nearer than our nearest? -#ifdef _LARGE_WORLDS - bool isNearer = nearestClipChunks.empty(); - AUTO_VAR(itNearest, nearestClipChunks.begin()); - for (; itNearest != nearestClipChunks.end(); - ++itNearest) { - isNearer = - distSqWeighted < itNearest->second; - if (isNearer) break; - } - isNearer = - isNearer || (nearestClipChunks.size() < - maxNearestChunks); -#else - bool isNearer = distSqWeighted < minDistSq; -#endif - -#ifdef _CRITICAL_CHUNKS - // AP - this will make sure that if a deferred - // grouping has started, only critical chunks go - // into that grouping, even if a non-critical - // chunk is closer. - if ((!veryNearCount && isNearer) || - (distSq < 20 * 20 && - (globalChunkFlags[pClipChunk->globalIdx] & - CHUNK_FLAG_CRITICAL))) -#else - if (isNearer) -#endif + if (globalChunkFlags[pClipChunk->globalIdx] & + CHUNK_FLAG_DIRTY) { + if ((!onlyRebuild) || + globalChunkFlags[pClipChunk->globalIdx] & + CHUNK_FLAG_COMPILED || + (distSq < + 20 * 20)) // Always rebuild really near things + // or else building (say) at tower + // up into empty blocks when we are + // low on memory will not create + // render data { - // At this point we've got a chunk that we - // would like to consider for rendering, at - // least based on its proximity to the - // player(s). Its *quite* quick to generate - // empty render data for render chunks, but - // if we let the rebuilding do that then the - // after rebuilding we will have to start - // searching for the next nearest chunk from - // scratch again. Instead, its better to - // detect empty chunks at this stage, flag - // them up as not dirty (and empty), and - // carry on. The levelchunk's - // isRenderChunkEmpty method can be quite - // optimal as it can make use of the chunk's - // data compression to detect emptiness - // without actually testing as many data - // items as uncompressed data would. - Chunk* chunk = pClipChunk->chunk; - LevelChunk* lc = level[p]->getChunkAt( - chunk->x, chunk->z); - if (!lc->isRenderChunkEmpty(y * 16)) { - nearChunk = pClipChunk; - minDistSq = distSqWeighted; + considered++; + // Is this chunk nearer than our nearest? #ifdef _LARGE_WORLDS - nearestClipChunks.insert( - itNearest, - std::pair( - nearChunk, minDistSq)); - if (nearestClipChunks.size() > - maxNearestChunks) { - nearestClipChunks.pop_back(); - } -#endif - } else { - chunk->clearDirty(); - globalChunkFlags[pClipChunk - ->globalIdx] |= - CHUNK_FLAG_EMPTYBOTH; - wouldBeNearButEmpty++; + bool isNearer = nearestClipChunks.empty(); + AUTO_VAR(itNearest, nearestClipChunks.begin()); + for (; itNearest != nearestClipChunks.end(); + ++itNearest) { + isNearer = + distSqWeighted < itNearest->second; + if (isNearer) break; } - } + isNearer = + isNearer || (nearestClipChunks.size() < + maxNearestChunks); +#else + bool isNearer = distSqWeighted < minDistSq; +#endif #ifdef _CRITICAL_CHUNKS - // AP - is the chunk near and also critical - if (distSq < 20 * 20 && - ((globalChunkFlags[pClipChunk->globalIdx] & - CHUNK_FLAG_CRITICAL))) + // AP - this will make sure that if a deferred + // grouping has started, only critical chunks go + // into that grouping, even if a non-critical + // chunk is closer. + if ((!veryNearCount && isNearer) || + (distSq < 20 * 20 && + (globalChunkFlags[pClipChunk->globalIdx] & + CHUNK_FLAG_CRITICAL))) #else - if (distSq < 20 * 20) + if (isNearer) #endif - { - veryNearCount++; + { + // At this point we've got a chunk that we + // would like to consider for rendering, at + // least based on its proximity to the + // player(s). Its *quite* quick to generate + // empty render data for render chunks, but + // if we let the rebuilding do that then the + // after rebuilding we will have to start + // searching for the next nearest chunk from + // scratch again. Instead, its better to + // detect empty chunks at this stage, flag + // them up as not dirty (and empty), and + // carry on. The levelchunk's + // isRenderChunkEmpty method can be quite + // optimal as it can make use of the chunk's + // data compression to detect emptiness + // without actually testing as many data + // items as uncompressed data would. + Chunk* chunk = pClipChunk->chunk; + LevelChunk* lc = level[p]->getChunkAt( + chunk->x, chunk->z); + if (!lc->isRenderChunkEmpty(y * 16)) { + nearChunk = pClipChunk; + minDistSq = distSqWeighted; +#ifdef _LARGE_WORLDS + nearestClipChunks.insert( + itNearest, + std::pair( + nearChunk, minDistSq)); + if (nearestClipChunks.size() > + maxNearestChunks) { + nearestClipChunks.pop_back(); + } +#endif + } else { + chunk->clearDirty(); + globalChunkFlags[pClipChunk + ->globalIdx] |= + CHUNK_FLAG_EMPTYBOTH; + wouldBeNearButEmpty++; + } + } + +#ifdef _CRITICAL_CHUNKS + // AP - is the chunk near and also critical + if (distSq < 20 * 20 && + ((globalChunkFlags[pClipChunk->globalIdx] & + CHUNK_FLAG_CRITICAL))) +#else + if (distSq < 20 * 20) +#endif + { + veryNearCount++; + } } } } } } + // app.DebugPrintf("[%d,%d,%d]\n",nearestClipChunks.empty(),considered,wouldBeNearButEmpty); } - // app.DebugPrintf("[%d,%d,%d]\n",nearestClipChunks.empty(),considered,wouldBeNearButEmpty); - } #endif // __PS3__ - PIXEndNamedEvent(); + PIXEndNamedEvent(); + } } Chunk* chunk = NULL; #ifdef _LARGE_WORLDS if (!nearestClipChunks.empty()) { int index = 0; - for (AUTO_VAR(it, nearestClipChunks.begin()); - it != nearestClipChunks.end(); ++it) { - chunk = it->first->chunk; - // If this chunk is very near, then move the renderer into a - // deferred mode. This won't commit any command buffers for - // rendering until we call CBuffDeferredModeEnd(), allowing us to - // group any near changes into an atomic unit. This is essential so - // we don't temporarily create any holes in the environment whilst - // updating one chunk and not the neighbours. The "ver near" aspect - // of this is just a cosmetic nicety - exactly the same thing would - // happen further away, but we just don't care about it so much from - // terms of visual impact. - if (veryNearCount > 0) { - RenderManager.CBuffDeferredModeStart(); + { + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); + for (AUTO_VAR(it, nearestClipChunks.begin()); + it != nearestClipChunks.end(); ++it) { + chunk = it->first->chunk; + // If this chunk is very near, then move the renderer into a + // deferred mode. This won't commit any command buffers for + // rendering until we call CBuffDeferredModeEnd(), allowing us to + // group any near changes into an atomic unit. This is essential so + // we don't temporarily create any holes in the environment whilst + // updating one chunk and not the neighbours. The "ver near" aspect + // of this is just a cosmetic nicety - exactly the same thing would + // happen further away, but we just don't care about it so much from + // terms of visual impact. + if (veryNearCount > 0) { + RenderManager.CBuffDeferredModeStart(); + } + // Build this chunk & return false to continue processing + chunk->clearDirty(); + // Take a copy of the details that are required for chunk + // rebuilding, and rebuild That instead of the original chunk data. + // This is done within the m_csDirtyChunks critical section, which + // means that any chunks can't be repositioned whilst we are doing + // this copy. The copy will then be guaranteed to be consistent + // whilst rebuilding takes place outside of that critical section. + permaChunk[index].makeCopyForRebuild(chunk); + ++index; } - // Build this chunk & return false to continue processing - chunk->clearDirty(); - // Take a copy of the details that are required for chunk - // rebuilding, and rebuild That instead of the original chunk data. - // This is done within the m_csDirtyChunks critical section, which - // means that any chunks can't be repositioned whilst we are doing - // this copy. The copy will then be guaranteed to be consistent - // whilst rebuilding takes place outside of that critical section. - permaChunk[index].makeCopyForRebuild(chunk); - ++index; - } - LeaveCriticalSection(&m_csDirtyChunks); + LeaveCriticalSection(&m_csDirtyChunks); - --index; // Bring it back into 0 counted range + --index; // Bring it back into 0 counted range - for (int i = MAX_CHUNK_REBUILD_THREADS - 1; i >= 0; --i) { - // Set the events that won't run - if ((i + 1) > index) - s_rebuildCompleteEvents->Set(i); - else - break; + for (int i = MAX_CHUNK_REBUILD_THREADS - 1; i >= 0; --i) { + // Set the events that won't run + if ((i + 1) > index) + s_rebuildCompleteEvents->Set(i); + else + break; + } } for (; index >= 0; --index) { @@ -2283,13 +2304,18 @@ bool LevelRenderer::updateDirtyChunks() { // app.DebugPrintf("Rebuilding permaChunk %d\n", index); - permaChunk[index].rebuild(); + { + FRAME_PROFILE_SCOPE(ChunkRebuildBody); + permaChunk[index].rebuild(); + } - if (index != 0) + if (index != 0) { + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); s_rebuildCompleteEvents->Set( index - 1); // MGH - this rebuild happening on the main // thread instead, mark the thread it // should have been running on as complete + } // int64_t endTime = System::currentTimeMillis(); // totalTime += (endTime - startTime); @@ -2300,44 +2326,54 @@ bool LevelRenderer::updateDirtyChunks() { // 4J Stu - Ignore this path when in constrained mode on Xbox One else { // Activate thread to rebuild this chunk + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); s_activationEventA[index - 1]->Set(); } } // Wait for the other threads to be done as well - s_rebuildCompleteEvents->WaitForAll(INFINITE); + { + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); + s_rebuildCompleteEvents->WaitForAll(INFINITE); + } } #else if (nearChunk) { chunk = nearChunk->chunk; PIXBeginNamedEvent(0, "Rebuilding near chunk %d %d %d", chunk->x, chunk->y, chunk->z); - // If this chunk is very near, then move the renderer into a deferred - // mode. This won't commit any command buffers for rendering until we - // call CBuffDeferredModeEnd(), allowing us to group any near changes - // into an atomic unit. This is essential so we don't temporarily create - // any holes in the environment whilst updating one chunk and not the - // neighbours. The "ver near" aspect of this is just a cosmetic nicety - - // exactly the same thing would happen further away, but we just don't - // care about it so much from terms of visual impact. - if (veryNearCount > 0) { - RenderManager.CBuffDeferredModeStart(); - } - // Build this chunk & return false to continue processing - chunk->clearDirty(); - // Take a copy of the details that are required for chunk rebuilding, - // and rebuild That instead of the original chunk data. This is done - // within the m_csDirtyChunks critical section, which means that any - // chunks can't be repositioned whilst we are doing this copy. The copy - // will then be guaranteed to be consistent whilst rebuilding takes - // place outside of that critical section. static Chunk permaChunk; - permaChunk.makeCopyForRebuild(chunk); - LeaveCriticalSection(&m_csDirtyChunks); + { + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); + // If this chunk is very near, then move the renderer into a deferred + // mode. This won't commit any command buffers for rendering until we + // call CBuffDeferredModeEnd(), allowing us to group any near changes + // into an atomic unit. This is essential so we don't temporarily create + // any holes in the environment whilst updating one chunk and not the + // neighbours. The "ver near" aspect of this is just a cosmetic nicety - + // exactly the same thing would happen further away, but we just don't + // care about it so much from terms of visual impact. + if (veryNearCount > 0) { + RenderManager.CBuffDeferredModeStart(); + } + // Build this chunk & return false to continue processing + chunk->clearDirty(); + // Take a copy of the details that are required for chunk rebuilding, + // and rebuild That instead of the original chunk data. This is done + // within the m_csDirtyChunks critical section, which means that any + // chunks can't be repositioned whilst we are doing this copy. The copy + // will then be guaranteed to be consistent whilst rebuilding takes + // place outside of that critical section. + permaChunk.makeCopyForRebuild(chunk); + LeaveCriticalSection(&m_csDirtyChunks); + } // static int64_t totalTime = 0; // static int64_t countTime = 0; // int64_t startTime = System::currentTimeMillis(); - permaChunk.rebuild(); + { + FRAME_PROFILE_SCOPE(ChunkRebuildBody); + permaChunk.rebuild(); + } // int64_t endTime = System::currentTimeMillis(); // totalTime += (endTime - startTime); // countTime++; @@ -4102,7 +4138,10 @@ int LevelRenderer::rebuildChunkThreadProc(void* lpParam) { s_activationEventA[index]->WaitForSignal(INFINITE); // app.DebugPrintf("Rebuilding permaChunk %d\n", index + 1); - permaChunk[index + 1].rebuild(); + { + FRAME_PROFILE_SCOPE(ChunkRebuildBody); + permaChunk[index + 1].rebuild(); + } // Inform the producer thread that we are done with this chunk s_rebuildCompleteEvents->Set(index); diff --git a/Minecraft.Client/Utils/FrameProfiler.cpp b/Minecraft.Client/Utils/FrameProfiler.cpp new file mode 100644 index 000000000..d27889c44 --- /dev/null +++ b/Minecraft.Client/Utils/FrameProfiler.cpp @@ -0,0 +1,260 @@ +#include "../Platform/stdafx.h" +#include "FrameProfiler.h" + +#ifdef ENABLE_FRAME_PROFILER + +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#define FRAME_PROFILER_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) || defined(__clang__) +#define FRAME_PROFILER_NOINLINE __attribute__((noinline)) +#else +#define FRAME_PROFILER_NOINLINE +#endif + +namespace { + +using FrameProfilerClock = std::chrono::steady_clock; +using Bucket = FrameProfiler::Bucket; +constexpr std::uint64_t kNsPerMs = 1000ULL * 1000ULL; +constexpr std::uint64_t kReportIntervalNs = 1000ULL * 1000ULL * 1000ULL; +constexpr std::size_t kBucketCount = FrameProfiler::BucketCount(); +constexpr auto kFalseTokens = std::to_array({ + "0", "false", "False", "FALSE", "no", "No", "NO", "off", "Off", "OFF", +}); +constexpr std::array + kBucketDescriptors = {{ + {Bucket::Frame, "frame"}, + {Bucket::World, "world"}, + {Bucket::Terrain, "terrain"}, + {Bucket::ChunkCull, "chunkCull"}, + {Bucket::ChunkCollect, "chunkCollect"}, + {Bucket::ChunkPlayback, "chunkPlayback"}, + {Bucket::ChunkDirtyScan, "chunkDirtyScan"}, + {Bucket::ChunkRebuildSchedule, "chunkRebuildSchedule"}, + {Bucket::ChunkRebuildBody, "chunkRebuildBody"}, + {Bucket::Entity, "entities"}, + {Bucket::Particle, "particles"}, + {Bucket::WeatherSky, "weather"}, + {Bucket::UIHud, "ui"}, + {Bucket::Lightmap, "lightmap"}, + }}; + +struct BucketTotals { + std::uint64_t totalNs{}; + std::uint64_t maxNs{}; + std::uint64_t calls{}; + + void Record(std::uint64_t elapsedNs) noexcept { + totalNs += elapsedNs; + ++calls; + if (elapsedNs > maxNs) maxNs = elapsedNs; + } + + void Merge(const BucketTotals& other) noexcept { + totalNs += other.totalNs; + calls += other.calls; + if (other.maxNs > maxNs) maxNs = other.maxNs; + } +}; + +struct AtomicBucketTotals { + std::atomic totalNs{0}; + std::atomic maxNs{0}; + std::atomic calls{0}; +}; + +struct ProfilerState { + std::array workerBuckets{}; +}; + +struct ThreadState { + std::uint32_t frameScopeDepth{}; + std::uint64_t windowStartNs{}; + std::array localBuckets{}; +}; + +constinit ProfilerState g_profilerState{}; +constinit thread_local ThreadState t_threadState{}; + +static_assert(kBucketDescriptors.size() == kBucketCount); + +[[nodiscard]] inline std::uint64_t nowNs() noexcept { + return static_cast( + std::chrono::duration_cast( + FrameProfilerClock::now().time_since_epoch()) + .count()); +} + +[[nodiscard]] constexpr double nsToMs(std::uint64_t ns) noexcept { + return static_cast(ns) / static_cast(kNsPerMs); +} + +[[nodiscard]] constexpr bool envSaysDisabled( + std::string_view value) noexcept { + if (value.empty()) return false; + + for (std::string_view falseToken : kFalseTokens) { + if (value == falseToken) return true; + } + return false; +} + +inline void updateAtomicMax(std::atomic& value, + std::uint64_t candidate) noexcept { + std::uint64_t current = value.load(std::memory_order_relaxed); + while (current < candidate && + !value.compare_exchange_weak(current, candidate, + std::memory_order_relaxed, + std::memory_order_relaxed)) { + } +} + +inline void recordWorkerBucket(Bucket bucket, std::uint64_t elapsedNs) noexcept { + AtomicBucketTotals& state = + g_profilerState.workerBuckets[FrameProfiler::BucketIndex(bucket)]; + state.totalNs.fetch_add(elapsedNs, std::memory_order_relaxed); + state.calls.fetch_add(1, std::memory_order_relaxed); + updateAtomicMax(state.maxNs, elapsedNs); +} + +[[nodiscard]] inline bool isFrameThread() noexcept { + return t_threadState.frameScopeDepth != 0; +} + +FRAME_PROFILER_NOINLINE +[[nodiscard]] bool computeEnabled() noexcept { + const char* const envValue = std::getenv("C4J_FRAME_PROFILER"); + if (envValue == nullptr) return true; + return !envSaysDisabled(envValue); +} + +FRAME_PROFILER_NOINLINE void emitWindowReport( + const std::array& buckets) noexcept { + const std::uint64_t frames = + buckets[FrameProfiler::BucketIndex(Bucket::Frame)].calls; + if (frames == 0) return; + + std::fprintf(stderr, + "[frame-prof] avg/frame(ms) frames=%llu", + static_cast(frames)); + for (const auto& descriptor : kBucketDescriptors) { + const BucketTotals& bucket = + buckets[FrameProfiler::BucketIndex(descriptor.bucket)]; + const std::string_view label = descriptor.label; + std::fprintf(stderr, " %.*s=%.2f", static_cast(label.size()), + label.data(), nsToMs(bucket.totalNs) / frames); + } + std::fputc('\n', stderr); + + std::fputs("[frame-prof] max(ms)/calls", stderr); + for (const auto& descriptor : kBucketDescriptors) { + const BucketTotals& bucket = + buckets[FrameProfiler::BucketIndex(descriptor.bucket)]; + const std::string_view label = descriptor.label; + std::fprintf(stderr, " %.*s=%.2f/%llu", + static_cast(label.size()), label.data(), + nsToMs(bucket.maxNs), + static_cast(bucket.calls)); + } + std::fputc('\n', stderr); + std::fflush(stderr); +} + +[[nodiscard]] std::array snapshotAndResetWorkerBuckets() + noexcept { + std::array snapshot = {}; + for (std::size_t i = 0; i < kBucketCount; ++i) { + AtomicBucketTotals& workerBucket = g_profilerState.workerBuckets[i]; + snapshot[i].totalNs = + workerBucket.totalNs.exchange(0, std::memory_order_relaxed); + snapshot[i].maxNs = + workerBucket.maxNs.exchange(0, std::memory_order_relaxed); + snapshot[i].calls = + workerBucket.calls.exchange(0, std::memory_order_relaxed); + } + return snapshot; +} + +} // namespace + +bool FrameProfiler::IsEnabled() noexcept { + static const bool enabled = computeEnabled(); + return enabled; +} + +void FrameProfiler::Record(Bucket bucket, std::uint64_t elapsedNs) noexcept { + if (isFrameThread()) { + t_threadState.localBuckets[BucketIndex(bucket)].Record(elapsedNs); + return; + } + + recordWorkerBucket(bucket, elapsedNs); +} + +void FrameProfiler::EndFrame(std::uint64_t elapsedNs) noexcept { + Record(Bucket::Frame, elapsedNs); + + ThreadState& threadState = t_threadState; + const std::uint64_t now = nowNs(); + + if (threadState.windowStartNs == 0) { + threadState.windowStartNs = now; + return; + } + + if ((now - threadState.windowStartNs) < kReportIntervalNs) return; + + std::array combined = threadState.localBuckets; + const auto workerSnapshot = snapshotAndResetWorkerBuckets(); + + for (std::size_t i = 0; i < kBucketCount; ++i) { + combined[i].Merge(workerSnapshot[i]); + } + + emitWindowReport(combined); + + threadState.windowStartNs = now; + threadState.localBuckets = {}; +} + +FrameProfiler::Scope::Scope(Bucket bucket) noexcept + : m_startNs(0), m_bucket(bucket), m_enabled(FrameProfiler::IsEnabled()) { + if (m_enabled) m_startNs = nowNs(); +} + +FrameProfiler::Scope::~Scope() noexcept { + if (!m_enabled) return; + FrameProfiler::Record(m_bucket, nowNs() - m_startNs); +} + +FrameProfiler::FrameScope::FrameScope() noexcept + : m_startNs(0), m_enabled(false) { + if (!FrameProfiler::IsEnabled()) return; + + m_enabled = (t_threadState.frameScopeDepth++ == 0); + if (m_enabled) m_startNs = nowNs(); +} + +FrameProfiler::FrameScope::~FrameScope() noexcept { + if (!m_enabled) { + if (t_threadState.frameScopeDepth > 0) { + --t_threadState.frameScopeDepth; + } + return; + } + + FrameProfiler::EndFrame(nowNs() - m_startNs); + + if (t_threadState.frameScopeDepth > 0) { + --t_threadState.frameScopeDepth; + } +} + +#endif diff --git a/Minecraft.Client/Utils/FrameProfiler.h b/Minecraft.Client/Utils/FrameProfiler.h new file mode 100644 index 000000000..5fbb71b12 --- /dev/null +++ b/Minecraft.Client/Utils/FrameProfiler.h @@ -0,0 +1,93 @@ +#pragma once + +#ifdef ENABLE_FRAME_PROFILER + +#include +#include +#include + +class FrameProfiler { +public: + enum class Bucket : std::uint8_t { + Frame, + World, + Terrain, + ChunkCull, + ChunkCollect, + ChunkPlayback, + ChunkDirtyScan, + ChunkRebuildSchedule, + ChunkRebuildBody, + Entity, + Particle, + WeatherSky, + UIHud, + Lightmap, + Count, + }; + + struct BucketDescriptor { + Bucket bucket; + const char* label; + }; + + [[nodiscard]] static constexpr std::size_t BucketIndex( + Bucket bucket) noexcept { + return static_cast(std::to_underlying(bucket)); + } + + [[nodiscard]] static constexpr std::size_t BucketCount() noexcept { + return BucketIndex(Bucket::Count); + } + + [[nodiscard]] static bool IsEnabled() noexcept; + + class Scope { + public: + explicit Scope(Bucket bucket) noexcept; + Scope(const Scope&) = delete; + Scope& operator=(const Scope&) = delete; + Scope(Scope&&) = delete; + Scope& operator=(Scope&&) = delete; + ~Scope() noexcept; + + private: + std::uint64_t m_startNs; + Bucket m_bucket; + bool m_enabled; + }; + + class FrameScope { + public: + FrameScope() noexcept; + FrameScope(const FrameScope&) = delete; + FrameScope& operator=(const FrameScope&) = delete; + FrameScope(FrameScope&&) = delete; + FrameScope& operator=(FrameScope&&) = delete; + ~FrameScope() noexcept; + + private: + std::uint64_t m_startNs; + bool m_enabled; + }; + +private: + static void Record(Bucket bucket, std::uint64_t elapsedNs) noexcept; + static void EndFrame(std::uint64_t elapsedNs) noexcept; +}; + +#define FRAME_PROFILE_CONCAT_INNER(a, b) a##b +#define FRAME_PROFILE_CONCAT(a, b) FRAME_PROFILE_CONCAT_INNER(a, b) +#define FRAME_PROFILE_SCOPE(bucket_name) \ + FrameProfiler::Scope FRAME_PROFILE_CONCAT(frameProfileScope_, __LINE__)( \ + FrameProfiler::Bucket::bucket_name) +#define FRAME_PROFILE_FRAME_SCOPE() \ + FrameProfiler::FrameScope FRAME_PROFILE_CONCAT(frameProfileFrameScope_, \ + __LINE__) + +#else + +#define FRAME_PROFILE_SCOPE(bucket_name) ((void)0) +#define FRAME_PROFILE_FRAME_SCOPE() ((void)0) + +#endif diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index 39bc97d2a..abbb3b1ef 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -80,6 +80,10 @@ if get_option('classic_panorama') global_cpp_defs += '-DCLASSIC_PANORAMA' endif +if get_option('enable_frame_profiler') + global_cpp_defs += ['-DENABLE_FRAME_PROFILER'] +endif + if get_option('ui_backend') == 'shiggy' shiggy_dep = dependency( 'shiggy', @@ -127,4 +131,4 @@ custom_target( '@OUTPUT@', ], build_by_default: true, -) \ No newline at end of file +) diff --git a/meson.options b/meson.options index cdf6457be..0290c8005 100644 --- a/meson.options +++ b/meson.options @@ -24,4 +24,11 @@ option( type: 'boolean', value: true, description: 'Toggles V-Sync and adds options to unlock maximum in-game framerate.', -) \ No newline at end of file +) + +option( + 'enable_frame_profiler', + type: 'boolean', + value: false, + description: 'Enable the in-engine frame profiler for render hotspot discovery.', +) From a3f7f7d03cff7a8b10573e33f0de43cad79ea9e8 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Sun, 29 Mar 2026 15:52:55 +1100 Subject: [PATCH 63/71] perf(render): improve chunk scheduling and refine rebuild profiling --- Minecraft.Client/Rendering/Chunk.cpp | 176 +++++++++--------- .../EntityRenderers/TileRenderer.cpp | 27 ++- Minecraft.Client/Rendering/LevelRenderer.cpp | 80 ++++---- Minecraft.Client/Utils/FrameProfiler.cpp | 5 + Minecraft.Client/Utils/FrameProfiler.h | 5 + 5 files changed, 171 insertions(+), 122 deletions(-) diff --git a/Minecraft.Client/Rendering/Chunk.cpp b/Minecraft.Client/Rendering/Chunk.cpp index 4df99432b..7980f241e 100644 --- a/Minecraft.Client/Rendering/Chunk.cpp +++ b/Minecraft.Client/Rendering/Chunk.cpp @@ -7,6 +7,7 @@ #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" #include "LevelRenderer.h" +#include "../Utils/FrameProfiler.h" #ifdef __PS3__ #include "../Platform/PS3/SPU_Tasks/ChunkUpdate/ChunkRebuildData.h" @@ -252,94 +253,97 @@ void Chunk::rebuild() { // into this category. By far the largest category of these are tiles in // solid regions of rock. bool empty = true; - for (int yy = y0; yy < y1; yy++) { - for (int zz = 0; zz < 16; zz++) { - for (int xx = 0; xx < 16; xx++) { - // 4J Stu - tile data is ordered in 128 blocks of full width, - // lower 128 then upper 128 - int indexY = yy; - int offset = 0; - if (indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { - indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; - offset = Level::COMPRESSED_CHUNK_SECTION_TILES; - } - - unsigned char tileId = - tileIds[offset + (((xx + 0) << 11) | ((zz + 0) << 7) | - (indexY + 0))]; - if (tileId > 0) empty = false; - - // Don't bother trying to work out neighbours for this tile if - // we are at the edge of the chunk - apart from the very bottom - // of the world where we shouldn't ever be able to see - if (yy == (Level::maxBuildHeight - 1)) continue; - if ((xx == 0) || (xx == 15)) continue; - if ((zz == 0) || (zz == 15)) continue; - - // Establish whether this tile and its neighbours are all made - // of rock, dirt, unbreakable tiles, or have already been - // determined to meet this criteria themselves and have a tile - // of 255 set. - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - tileId = tileIds[offset + (((xx - 1) << 11) | ((zz + 0) << 7) | - (indexY + 0))]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - tileId = tileIds[offset + (((xx + 1) << 11) | ((zz + 0) << 7) | - (indexY + 0))]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - tileId = tileIds[offset + (((xx + 0) << 11) | ((zz - 1) << 7) | - (indexY + 0))]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - tileId = tileIds[offset + (((xx + 0) << 11) | ((zz + 1) << 7) | - (indexY + 0))]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - // Treat the bottom of the world differently - we shouldn't ever - // be able to look up at this, so consider tiles as invisible if - // they are surrounded on sides other than the bottom - if (yy > 0) { - int indexYMinusOne = yy - 1; - int yMinusOneOffset = 0; - if (indexYMinusOne >= - Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { - indexYMinusOne -= - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; - yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + { + FRAME_PROFILE_SCOPE(ChunkPrepass); + for (int yy = y0; yy < y1; yy++) { + for (int zz = 0; zz < 16; zz++) { + for (int xx = 0; xx < 16; xx++) { + // 4J Stu - tile data is ordered in 128 blocks of full width, + // lower 128 then upper 128 + int indexY = yy; + int offset = 0; + if (indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offset = Level::COMPRESSED_CHUNK_SECTION_TILES; } - tileId = tileIds[yMinusOneOffset + (((xx + 0) << 11) | - ((zz + 0) << 7) | - indexYMinusOne)]; - if (!((tileId == Tile::stone_Id) || - (tileId == Tile::dirt_Id) || + + unsigned char tileId = + tileIds[offset + (((xx + 0) << 11) | ((zz + 0) << 7) | + (indexY + 0))]; + if (tileId > 0) empty = false; + + // Don't bother trying to work out neighbours for this tile if + // we are at the edge of the chunk - apart from the very bottom + // of the world where we shouldn't ever be able to see + if (yy == (Level::maxBuildHeight - 1)) continue; + if ((xx == 0) || (xx == 15)) continue; + if ((zz == 0) || (zz == 15)) continue; + + // Establish whether this tile and its neighbours are all made + // of rock, dirt, unbreakable tiles, or have already been + // determined to meet this criteria themselves and have a tile + // of 255 set. + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + tileId = tileIds[offset + (((xx - 1) << 11) | ((zz + 0) << 7) | + (indexY + 0))]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + tileId = tileIds[offset + (((xx + 1) << 11) | ((zz + 0) << 7) | + (indexY + 0))]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + tileId = tileIds[offset + (((xx + 0) << 11) | ((zz - 1) << 7) | + (indexY + 0))]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + tileId = tileIds[offset + (((xx + 0) << 11) | ((zz + 1) << 7) | + (indexY + 0))]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + // Treat the bottom of the world differently - we shouldn't ever + // be able to look up at this, so consider tiles as invisible if + // they are surrounded on sides other than the bottom + if (yy > 0) { + int indexYMinusOne = yy - 1; + int yMinusOneOffset = 0; + if (indexYMinusOne >= + Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { + indexYMinusOne -= + Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + tileId = tileIds[yMinusOneOffset + (((xx + 0) << 11) | + ((zz + 0) << 7) | + indexYMinusOne)]; + if (!((tileId == Tile::stone_Id) || + (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + } + int indexYPlusOne = yy + 1; + int yPlusOneOffset = 0; + if (indexYPlusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { + indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + tileId = + tileIds[yPlusOneOffset + (((xx + 0) << 11) | + ((zz + 0) << 7) | indexYPlusOne)]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || (tileId == Tile::unbreakable_Id) || (tileId == 255))) continue; - } - int indexYPlusOne = yy + 1; - int yPlusOneOffset = 0; - if (indexYPlusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { - indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; - yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; - } - tileId = - tileIds[yPlusOneOffset + (((xx + 0) << 11) | - ((zz + 0) << 7) | indexYPlusOne)]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - // This tile is surrounded. Flag it as not requiring to be - // rendered by setting its id to 255. - tileIds[offset + (((xx + 0) << 11) | ((zz + 0) << 7) | - (indexY + 0))] = 0xff; + // This tile is surrounded. Flag it as not requiring to be + // rendered by setting its id to 255. + tileIds[offset + (((xx + 0) << 11) | ((zz + 0) << 7) | + (indexY + 0))] = 0xff; + } } } } @@ -752,7 +756,7 @@ void Chunk::rebuild_SPU() { if (currentLayer == 0 && Tile::tiles[tileId]->isEntityTile()) { std::shared_ptr et = - region.getTileEntity(x, y, z); + region->getTileEntity(x, y, z); if (TileEntityRenderDispatcher::instance ->hasRenderer(et)) { renderableTileEntities.push_back(et); @@ -770,7 +774,7 @@ void Chunk::rebuild_SPU() { } else if (renderLayer == currentLayer) { // if(currentLayer == 0) // numRenderedLayer0++; - rendered |= tileRenderer.tesselateInWorld( + rendered |= tileRenderer->tesselateInWorld( tile, x, y, z); } } diff --git a/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp index 3ee4ff71a..f1f91018c 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp @@ -11,6 +11,7 @@ #include "../../../Minecraft.World/Headers/net.minecraft.h" #include "../../../Minecraft.World/Headers/net.minecraft.world.h" #include "../Tesselator.h" +#include "../../Utils/FrameProfiler.h" #include "EntityTileRenderer.h" #include "../../GameState/Options.h" @@ -267,7 +268,12 @@ bool TileRenderer::tesselateInWorld( { Tesselator* t = Tesselator::getInstance(); int shape = tt->getRenderShape(); - tt->updateShape(level, x, y, z, forceData, forceEntity); + if (shape == Tile::SHAPE_BLOCK) { + FRAME_PROFILE_SCOPE(ChunkBlockShape); + tt->updateShape(level, x, y, z, forceData, forceEntity); + } else { + tt->updateShape(level, x, y, z, forceData, forceEntity); + } // AP - now that the culling is done earlier we don't need to call setShape // until later on (only for SHAPE_BLOCK) if (shape != Tile::SHAPE_BLOCK) { @@ -278,6 +284,11 @@ bool TileRenderer::tesselateInWorld( bool retVal = false; switch (shape) { case Tile::SHAPE_BLOCK: { + { + FRAME_PROFILE_SCOPE(ChunkBlockShape); + setShape(tt); + } + // 4J - added these faceFlags so we can detect whether this block is // going to have no visible faces and early out the original code // checked noCulling and shouldRenderFace directly where faceFlags @@ -290,6 +301,7 @@ bool TileRenderer::tesselateInWorld( if (noCulling) { faceFlags = 0x3f; } else { + FRAME_PROFILE_SCOPE(ChunkBlockFaceCull); // these block types can take advantage of a faster version of // shouldRenderFace there are others but this is an easy check // which covers the majority Note: This now covers rock, grass, @@ -319,9 +331,6 @@ bool TileRenderer::tesselateInWorld( break; } - // now we need to set the shape - setShape(tt); - retVal = tesselateBlockInWorld(tt, x, y, z, faceFlags); } break; case Tile::SHAPE_TREE: @@ -4828,9 +4837,11 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tt, int x, int y, int z) { if (Tile::lightEmission[tt->id] == 0) // 4J - TODO/remove (Minecraft::useAmbientOcclusion()) { + FRAME_PROFILE_SCOPE(ChunkBlockLighting); return tesselateBlockInWorldWithAmbienceOcclusionTexLighting( tt, x, y, z, r, g, b, 0, smoothShapeLighting); } else { + FRAME_PROFILE_SCOPE(ChunkBlockLighting); return tesselateBlockInWorld(tt, x, y, z, r, g, b); } } @@ -4856,9 +4867,11 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tt, int x, int y, int z, if (Tile::lightEmission[tt->id] == 0) // 4J - TODO/remove (Minecraft::useAmbientOcclusion()) { + FRAME_PROFILE_SCOPE(ChunkBlockLighting); return tesselateBlockInWorldWithAmbienceOcclusionTexLighting( tt, x, y, z, r, g, b, faceFlags, smoothShapeLighting); } else { + FRAME_PROFILE_SCOPE(ChunkBlockLighting); return tesselateBlockInWorld(tt, x, y, z, r, g, b); } } @@ -7049,6 +7062,7 @@ bool TileRenderer::tesselateDoorInWorld(Tile* tt, int x, int y, int z) { void TileRenderer::renderFaceDown(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7167,6 +7181,7 @@ void TileRenderer::renderFaceDown(Tile* tt, double x, double y, double z, void TileRenderer::renderFaceUp(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7286,6 +7301,7 @@ void TileRenderer::renderFaceUp(Tile* tt, double x, double y, double z, void TileRenderer::renderNorth(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7410,6 +7426,7 @@ void TileRenderer::renderNorth(Tile* tt, double x, double y, double z, void TileRenderer::renderSouth(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7534,6 +7551,7 @@ void TileRenderer::renderSouth(Tile* tt, double x, double y, double z, void TileRenderer::renderWest(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7658,6 +7676,7 @@ void TileRenderer::renderWest(Tile* tt, double x, double y, double z, void TileRenderer::renderEast(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 3da404243..177b8e021 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "../Platform/stdafx.h" #include "LevelRenderer.h" @@ -904,11 +905,12 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { int list = chunk->globalIdx * 2 + layer; list += chunkLists; - // 4jcraft: replaced glPushMatrix/glTranslatef/glPopMatrix per chunk - // no more full MVP upload per chunk, can also be bkwards compat - RenderManager.SetChunkOffset((float)chunk->chunk->x, - (float)chunk->chunk->y, - (float)chunk->chunk->z); + // 4jcraft: replaced glPushMatrix/glTranslatef/glPopMatrix per + // chunk no more full MVP upload per chunk, can also be bkwards + // compat + RenderManager.SetChunkOffset((float)chunk->chunk->x, + (float)chunk->chunk->y, + (float)chunk->chunk->z); if (RenderManager.CBuffCall(list, first)) { first = false; @@ -1964,7 +1966,41 @@ void LevelRenderer::renderAdvancedClouds(float alpha) { bool LevelRenderer::updateDirtyChunks() { #ifdef _LARGE_WORLDS - std::list > nearestClipChunks; + struct NearestClipChunkSet { + std::array, MAX_CONCURRENT_CHUNK_REBUILDS> + items; + int count = 0; + + bool empty() const noexcept { return count == 0; } + int size() const noexcept { return count; } + + bool wouldAccept(int distSqWeighted) const noexcept { + return (count < MAX_CONCURRENT_CHUNK_REBUILDS) || + (distSqWeighted < items[count - 1].second); + } + + void insert(ClipChunk* chunk, int distSqWeighted) noexcept { + int pos = 0; + while ((pos < count) && (items[pos].second <= distSqWeighted)) { + ++pos; + } + + if ((count == MAX_CONCURRENT_CHUNK_REBUILDS) && + (pos >= MAX_CONCURRENT_CHUNK_REBUILDS)) { + return; + } + + const int newCount = + (count < MAX_CONCURRENT_CHUNK_REBUILDS) + ? (count + 1) + : MAX_CONCURRENT_CHUNK_REBUILDS; + for (int i = newCount - 1; i > pos; --i) { + items[i] = items[i - 1]; + } + items[pos] = std::pair(chunk, distSqWeighted); + count = newCount; + } + } nearestClipChunks; #endif ClipChunk* nearChunk = NULL; // Nearest chunk that is dirty @@ -2090,11 +2126,6 @@ bool LevelRenderer::updateDirtyChunks() { veryNearCount = g_findNearestChunkDataIn.veryNearCount; #else // __PS3__ -#ifdef _LARGE_WORLDS - int maxNearestChunks = MAX_CONCURRENT_CHUNK_REBUILDS; - // 4J Stu - On XboxOne we should cut this down if in a constrained state - // so the saving threads get more time -#endif // Find nearest chunk that is dirty for (int p = 0; p < XUSER_MAX_COUNT; p++) { // It's possible that the localplayers member can be set to NULL on @@ -2150,17 +2181,9 @@ bool LevelRenderer::updateDirtyChunks() { considered++; // Is this chunk nearer than our nearest? #ifdef _LARGE_WORLDS - bool isNearer = nearestClipChunks.empty(); - AUTO_VAR(itNearest, nearestClipChunks.begin()); - for (; itNearest != nearestClipChunks.end(); - ++itNearest) { - isNearer = - distSqWeighted < itNearest->second; - if (isNearer) break; - } - isNearer = - isNearer || (nearestClipChunks.size() < - maxNearestChunks); + bool isNearer = + nearestClipChunks.wouldAccept( + distSqWeighted); #else bool isNearer = distSqWeighted < minDistSq; #endif @@ -2203,13 +2226,7 @@ bool LevelRenderer::updateDirtyChunks() { minDistSq = distSqWeighted; #ifdef _LARGE_WORLDS nearestClipChunks.insert( - itNearest, - std::pair( - nearChunk, minDistSq)); - if (nearestClipChunks.size() > - maxNearestChunks) { - nearestClipChunks.pop_back(); - } + nearChunk, minDistSq); #endif } else { chunk->clearDirty(); @@ -2249,9 +2266,8 @@ bool LevelRenderer::updateDirtyChunks() { int index = 0; { FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); - for (AUTO_VAR(it, nearestClipChunks.begin()); - it != nearestClipChunks.end(); ++it) { - chunk = it->first->chunk; + for (int i = 0; i < nearestClipChunks.size(); ++i) { + chunk = nearestClipChunks.items[i].first->chunk; // If this chunk is very near, then move the renderer into a // deferred mode. This won't commit any command buffers for // rendering until we call CBuffDeferredModeEnd(), allowing us to diff --git a/Minecraft.Client/Utils/FrameProfiler.cpp b/Minecraft.Client/Utils/FrameProfiler.cpp index d27889c44..89db484eb 100644 --- a/Minecraft.Client/Utils/FrameProfiler.cpp +++ b/Minecraft.Client/Utils/FrameProfiler.cpp @@ -39,6 +39,11 @@ constexpr std::array {Bucket::ChunkDirtyScan, "chunkDirtyScan"}, {Bucket::ChunkRebuildSchedule, "chunkRebuildSchedule"}, {Bucket::ChunkRebuildBody, "chunkRebuildBody"}, + {Bucket::ChunkPrepass, "chunkPrepass"}, + {Bucket::ChunkBlockShape, "chunkBlockShape"}, + {Bucket::ChunkBlockFaceCull, "chunkBlockFaceCull"}, + {Bucket::ChunkBlockLighting, "chunkBlockLighting"}, + {Bucket::ChunkBlockEmit, "chunkBlockEmit"}, {Bucket::Entity, "entities"}, {Bucket::Particle, "particles"}, {Bucket::WeatherSky, "weather"}, diff --git a/Minecraft.Client/Utils/FrameProfiler.h b/Minecraft.Client/Utils/FrameProfiler.h index 5fbb71b12..7abf56255 100644 --- a/Minecraft.Client/Utils/FrameProfiler.h +++ b/Minecraft.Client/Utils/FrameProfiler.h @@ -18,6 +18,11 @@ public: ChunkDirtyScan, ChunkRebuildSchedule, ChunkRebuildBody, + ChunkPrepass, + ChunkBlockShape, + ChunkBlockFaceCull, + ChunkBlockLighting, + ChunkBlockEmit, Entity, Particle, WeatherSky, From abba4b57ce7a341e0ca43c1920e3612a74171d51 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Mon, 30 Mar 2026 00:24:23 +0200 Subject: [PATCH 64/71] Fixed broken LevelRenderer.cpp and removed merge garbage --- Minecraft.Client/Rendering/Chunk.cpp | 154 ++++- Minecraft.Client/Rendering/Chunk.h | 2 +- Minecraft.Client/Rendering/LevelRenderer.cpp | 608 +++++++++++++------ Minecraft.Client/Rendering/LevelRenderer.h | 5 +- Minecraft.Client/meson.build | 21 +- meson.options | 9 + 6 files changed, 609 insertions(+), 190 deletions(-) diff --git a/Minecraft.Client/Rendering/Chunk.cpp b/Minecraft.Client/Rendering/Chunk.cpp index 7980f241e..ff0550717 100644 --- a/Minecraft.Client/Rendering/Chunk.cpp +++ b/Minecraft.Client/Rendering/Chunk.cpp @@ -73,6 +73,7 @@ void Chunk::setPos(int x, int y, int z) { clipChunk->globalIdx = LevelRenderer::getGlobalIndexForChunk(x, y, z, level); + levelRenderer->setGlobalChunkConnectivity(clipChunk->globalIdx, ~0ULL); #if 1 // 4J - we're not using offsetted renderlists anymore, so just set the full @@ -358,6 +359,12 @@ void Chunk::rebuild() { RenderManager.CBuffClear(lists + currentLayer); } + int globalIdx = levelRenderer->getGlobalIndexForChunk(this->x, this->y, + this->z, level); + levelRenderer->setGlobalChunkConnectivity(globalIdx, ~0ULL); + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, + LevelRenderer::CHUNK_FLAG_COMPILED); + delete region; delete tileRenderer; return; @@ -495,6 +502,11 @@ void Chunk::rebuild() { bb = {bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2], bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]}; + uint64_t conn = computeConnectivity(tileIds); // pass tileIds + int globalIdx = + levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); + levelRenderer->setGlobalChunkConnectivity(globalIdx, conn); + delete tileRenderer; delete region; @@ -958,6 +970,142 @@ float Chunk::squishedDistanceToSqr(std::shared_ptr player) { return xd * xd + yd * yd + zd * zd; } +uint64_t Chunk::computeConnectivity(const uint8_t* tileIds) { + const int W = 16; + const int H = 16; + const int VOLUME = W * H * W; + + auto idx = [&](int x, int y, int z) -> int { + return y * W * W + z * W + x; + }; + + auto isOpen = [&](int lx, int ly, int lz) -> bool { + int worldY = this->y + ly; + int offset = 0; + int indexY = worldY; + if (indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + + uint8_t tileId = tileIds[offset + ((lx << 11) | (lz << 7) | indexY)]; + + if (tileId == 0) return true; // air + if (tileId == 0xFF) return false; // hidden tile (yeah) + + Tile* t = Tile::tiles[tileId]; + return (t == nullptr) || !t->isSolidRender(); + }; + + uint8_t visited[6][512]; + memset(visited, 0, sizeof(visited)); + + static const int FX[6] = {1, -1, 0, 0, 0, 0}; + static const int FY[6] = {0, 0, 1, -1, 0, 0}; + static const int FZ[6] = {0, 0, 0, 0, 1, -1}; + + struct Cell { + int8_t x, y, z; + }; + static thread_local std::vector queue; + + uint64_t result = 0; + + for (int entryFace = 0; entryFace < 6; entryFace++) { + uint8_t* vis = visited[entryFace]; + queue.clear(); + int x0s, x1s, y0s, y1s, z0s, z1s; + switch (entryFace) { + case 0: + x0s = W - 1; + x1s = W - 1; + y0s = 0; + y1s = H - 1; + z0s = 0; + z1s = W - 1; + break; // +X + case 1: + x0s = 0; + x1s = 0; + y0s = 0; + y1s = H - 1; + z0s = 0; + z1s = W - 1; + break; // -X + case 2: + x0s = 0; + x1s = W - 1; + y0s = H - 1; + y1s = H - 1; + z0s = 0; + z1s = W - 1; + break; // +Y + case 3: + x0s = 0; + x1s = W - 1; + y0s = 0; + y1s = 0; + z0s = 0; + z1s = W - 1; + break; // -Y + case 4: + x0s = 0; + x1s = W - 1; + y0s = 0; + y1s = H - 1; + z0s = W - 1; + z1s = W - 1; + break; // +Z + case 5: + x0s = 0; + x1s = W - 1; + y0s = 0; + y1s = H - 1; + z0s = 0; + z1s = 0; + break; // -Z + default: + continue; + } + + for (int sy = y0s; sy <= y1s; sy++) + for (int sz = z0s; sz <= z1s; sz++) + for (int sx = x0s; sx <= x1s; sx++) { + if (!isOpen(sx, sy, sz)) continue; + int i = idx(sx, sy, sz); + if (vis[i >> 3] & (1 << (i & 7))) continue; + vis[i >> 3] |= (1 << (i & 7)); + queue.push_back({(int8_t)sx, (int8_t)sy, (int8_t)sz}); + } + + for (int qi = 0; qi < (int)queue.size(); qi++) { + Cell cur = queue[qi]; + + for (int nb = 0; nb < 6; nb++) { + int nx = cur.x + FX[nb]; + int ny = cur.y + FY[nb]; + int nz = cur.z + FZ[nb]; + + // entry exit conn + if (nx < 0 || nx >= W || ny < 0 || ny >= H || nz < 0 || + nz >= W) { + // nb IS the exit face because FX,FY,FZ are aligned + result |= ((uint64_t)1 << (entryFace * 6 + nb)); + continue; + } + + if (!isOpen(nx, ny, nz)) continue; + + int i = idx(nx, ny, nz); + if (vis[i >> 3] & (1 << (i & 7))) continue; + vis[i >> 3] |= (1 << (i & 7)); + queue.push_back({(int8_t)nx, (int8_t)ny, (int8_t)nz}); + } + } + } + + return result; +} void Chunk::reset() { if (assigned) { EnterCriticalSection(&levelRenderer->m_csDirtyChunks); @@ -1002,7 +1150,11 @@ int Chunk::getList(int layer) { return -1; } -void Chunk::cull(Culler* culler) { clipChunk->visible = culler->isVisible(&bb); } +void Chunk::cull(Culler* culler) { + if (clipChunk->visible) { + clipChunk->visible = culler->isVisible(&bb); + } +} void Chunk::renderBB() { // glCallList(lists + 2); // 4J - removed - TODO put back in diff --git a/Minecraft.Client/Rendering/Chunk.h b/Minecraft.Client/Rendering/Chunk.h index e6d214ab2..c524c69ce 100644 --- a/Minecraft.Client/Rendering/Chunk.h +++ b/Minecraft.Client/Rendering/Chunk.h @@ -48,7 +48,7 @@ public: int xm, ym, zm; AABB bb; ClipChunk* clipChunk; - + uint64_t computeConnectivity(const uint8_t* tileIds); int id; // public: // std::vector > renderableTileEntities; diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 177b8e021..b73ee025c 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -91,6 +91,9 @@ ResourceLocation LevelRenderer::END_SKY_LOCATION = const unsigned int HALO_RING_RADIUS = 100; +uint64_t* LevelRenderer::globalChunkConnectivity = + nullptr; // bad placement do bettr juicey + #ifdef _LARGE_WORLDS Chunk LevelRenderer::permaChunk[MAX_CONCURRENT_CHUNK_REBUILDS]; C4JThread* LevelRenderer::rebuildThreads[MAX_CHUNK_REBUILD_THREADS]; @@ -189,6 +192,10 @@ LevelRenderer::LevelRenderer(Minecraft* mc, Textures* textures) { globalChunkFlags = new unsigned char[getGlobalChunkCount()]; memset(globalChunkFlags, 0, getGlobalChunkCount()); + globalChunkConnectivity = new uint64_t[getGlobalChunkCount()]; + memset(globalChunkConnectivity, 0xFF, + getGlobalChunkCount() * sizeof(uint64_t)); // 0xFF >> Fully open + starList = MemoryTracker::genLists(4); glPushMatrix(); @@ -801,14 +808,14 @@ int compare(const void* a, const void* b) { #endif +// 4jcraft: removed the vita & ps3 versions because they were SEVERELY annoying +// me it looked so ugly, god. +// if you ever hate me for it, deal with it, the source code is STILL visible if +// you look at someone elses fork. like steamcmd int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { - int playerIndex = mc->player->GetXboxPad(); // 4J added + int playerIndex = mc->player->GetXboxPad(); + if (chunks[playerIndex].data == NULL) return 0; -#if 1 - // 4J - cut down version, we're not using offsetted render lists, or a - // sorted chunk list, anymore - mc->gameRenderer->turnOnLightLayer( - alpha); // 4J - brought forward from 1.8.2 std::shared_ptr player = mc->cameraTargetPlayer; double xOff = player->xOld + (player->x - player->xOld) * alpha; double yOff = player->yOld + (player->y - player->yOld) * alpha; @@ -817,14 +824,9 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { glPushMatrix(); glTranslatef((float)-xOff, (float)-yOff, (float)-zOff); -#ifdef __PSVITA__ - // AP - also set the camera position so we can work out if a chunk is fogged - // or not - RenderManager.SetCameraPosition((float)-xOff, (float)-yOff, (float)-zOff); -#endif - -#if defined __PS3__ && !defined DISABLE_SPU_CODE - // pre- calc'd on the SPU + ClipChunk* pClipChunk = chunks[playerIndex].data; + unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; + bool first = true; int count = 0; waitForCull_SPU(); if (layer == 0) { @@ -905,12 +907,11 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { int list = chunk->globalIdx * 2 + layer; list += chunkLists; - // 4jcraft: replaced glPushMatrix/glTranslatef/glPopMatrix per - // chunk no more full MVP upload per chunk, can also be bkwards - // compat - RenderManager.SetChunkOffset((float)chunk->chunk->x, - (float)chunk->chunk->y, - (float)chunk->chunk->z); + // 4jcraft: replaced glPushMatrix/glTranslatef/glPopMatrix per chunk + // no more full MVP upload per chunk, can also be bkwards compat + RenderManager.SetChunkOffset((float)pClipChunk->chunk->x, + (float)pClipChunk->chunk->y, + (float)pClipChunk->chunk->z); if (RenderManager.CBuffCall(list, first)) { first = false; @@ -960,61 +961,7 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { #endif // __PS3__ glPopMatrix(); - mc->gameRenderer->turnOffLightLayer( - alpha); // 4J - brought forward from 1.8.2 - -#else - _renderChunks.clear(); - // int p = 0; - int count = 0; - for (int i = from; i < to; i++) { - if (layer == 0) { - totalChunks++; - if (sortedChunks[playerIndex]->at(i)->emptyFlagSet(layer)) - emptyChunks++; - else if (!sortedChunks[playerIndex]->at(i)->visible) - offscreenChunks++; - else - renderedChunks++; - } - - // if (!sortedChunks[i].empty[layer] && - // sortedChunks[i].visible && - // (sortedChunks[i].occlusion_visible)) { - if (!(sortedChunks[playerIndex]->at(i)->emptyFlagSet(layer) && - sortedChunks[playerIndex]->at(i)->visible)) { - int list = sortedChunks[playerIndex]->at(i)->getList(layer); - if (list >= 0) { - _renderChunks.push_back(sortedChunks[playerIndex]->at(i)); - count++; - } - } - } - - std::shared_ptr player = mc->cameraTargetPlayer; - double xOff = player->xOld + (player->x - player->xOld) * alpha; - double yOff = player->yOld + (player->y - player->yOld) * alpha; - double zOff = player->zOld + (player->z - player->zOld) * alpha; - - for (int l = 0; l < RENDERLISTS_LENGTH; l++) renderLists[l].clear(); - int lists = 0; - for (auto it = _renderChunks.begin(); it != _renderChunks.end(); it++) { - Chunk* chunk = *it; - int list = -1; - for (int l = 0; l < lists; l++) { - if (renderLists[l].isAt(chunk->xRender, chunk->yRender, - chunk->zRender)) - list = l; - } - if (list < 0) { - list = lists++; - renderLists[list].init(chunk->xRender, chunk->yRender, - chunk->zRender, xOff, yOff, zOff); - } - renderLists[list].add(chunk->getList(layer)); - } - renderSameAsLast(layer, alpha); -#endif + mc->gameRenderer->turnOffLightLayer(alpha); return count; } @@ -1990,10 +1937,9 @@ bool LevelRenderer::updateDirtyChunks() { return; } - const int newCount = - (count < MAX_CONCURRENT_CHUNK_REBUILDS) - ? (count + 1) - : MAX_CONCURRENT_CHUNK_REBUILDS; + const int newCount = (count < MAX_CONCURRENT_CHUNK_REBUILDS) + ? (count + 1) + : MAX_CONCURRENT_CHUNK_REBUILDS; for (int i = newCount - 1; i > pos; --i) { items[i] = items[i - 1]; } @@ -2030,8 +1976,8 @@ bool LevelRenderer::updateDirtyChunks() { int index = 0; do { - // See comment on dirtyChunksLockFreeStack.Push() regarding details of - // this casting/subtracting -2. + // See comment on dirtyChunksLockFreeStack.Push() regarding details + // of this casting/subtracting -2. index = (size_t)dirtyChunksLockFreeStack.Pop(); #ifdef _CRITICAL_CHUNKS int oldIndex = index; @@ -2040,9 +1986,9 @@ bool LevelRenderer::updateDirtyChunks() { #endif if (index == 1) dirtyChunkPresent = - true; // 1 is a special value passed to let this thread know - // that a chunk which isn't on this stack has been set to - // dirty + true; // 1 is a special value passed to let this thread + // know that a chunk which isn't on this stack has + // been set to dirty else if (index > 1) { int i2 = index - 2; if (i2 >= DIMENSION_OFFSETS[2]) { @@ -2057,8 +2003,9 @@ bool LevelRenderer::updateDirtyChunks() { setGlobalChunkFlag(index - 2, CHUNK_FLAG_DIRTY); #ifdef _CRITICAL_CHUNKS - if (!(oldIndex & 0x10000000)) // was this chunk not marked as - // non-critical. Ugh double negatives + if (!(oldIndex & + 0x10000000)) // was this chunk not marked as + // non-critical. Ugh double negatives { setGlobalChunkFlag(index - 2, CHUNK_FLAG_CRITICAL); } @@ -2068,20 +2015,21 @@ bool LevelRenderer::updateDirtyChunks() { } } while (index); - // Only bother searching round all the chunks if we have some dirty chunk(s) + // Only bother searching round all the chunks if we have some dirty + // chunk(s) if (dirtyChunkPresent) { lastDirtyChunkFound = System::currentTimeMillis(); PIXBeginNamedEvent(0, "Finding nearest chunk\n"); #if defined __PS3__ && !defined DISABLE_SPU_CODE - // find the nearest chunk with a spu task, copy all the data over here - // for uploading to SPU + // find the nearest chunk with a spu task, copy all the data over + // here for uploading to SPU g_findNearestChunkDataIn.numGlobalChunks = getGlobalChunkCount(); g_findNearestChunkDataIn.pGlobalChunkFlags = globalChunkFlags; g_findNearestChunkDataIn.onlyRebuild = onlyRebuild; g_findNearestChunkDataIn.lowerOffset = (int)&((LevelChunk*)0) - ->lowerBlocks; // dodgy bit of class structure poking, as we - // don't want to try and get the whole of + ->lowerBlocks; // dodgy bit of class structure poking, as + // we don't want to try and get the whole of // LevelChunk copmpiling on SPU g_findNearestChunkDataIn.upperOffset = (int)&((LevelChunk*)0)->upperBlocks; @@ -2107,11 +2055,14 @@ bool LevelRenderer::updateDirtyChunks() { } if (level[i] != NULL) { g_findNearestChunkDataIn.multiplayerChunkCache[i].XZOFFSET = - ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZOFFSET; + ((MultiPlayerChunkCache*)(level[i]->chunkSource)) + ->XZOFFSET; g_findNearestChunkDataIn.multiplayerChunkCache[i].XZSIZE = - ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZSIZE; + ((MultiPlayerChunkCache*)(level[i]->chunkSource)) + ->XZSIZE; g_findNearestChunkDataIn.multiplayerChunkCache[i].cache = - (void**)((MultiPlayerChunkCache*)(level[i]->chunkSource)) + (void**)((MultiPlayerChunkCache*)(level[i] + ->chunkSource)) ->cache; } } @@ -2128,20 +2079,22 @@ bool LevelRenderer::updateDirtyChunks() { // Find nearest chunk that is dirty for (int p = 0; p < XUSER_MAX_COUNT; p++) { - // It's possible that the localplayers member can be set to NULL on - // the main thread when a player chooses to exit the game So take a - // reference to the player object now. As it is a shared_ptr it - // should live as long as we need it + // It's possible that the localplayers member can be set to NULL + // on the main thread when a player chooses to exit the game So + // take a reference to the player object now. As it is a + // shared_ptr it should live as long as we need it std::shared_ptr player = mc->localplayers[p]; if (player == NULL) continue; if (chunks[p].data == NULL) continue; if (level[p] == NULL) continue; - if (chunks[p].length != xChunks * zChunks * CHUNK_Y_COUNT) continue; + if (chunks[p].length != xChunks * zChunks * CHUNK_Y_COUNT) + continue; int px = (int)player->x; int py = (int)player->y; int pz = (int)player->z; - // app.DebugPrintf("!! %d %d %d, %d %d %d {%d,%d} + // app.DebugPrintf("!! %d %d %d, %d %d %d + //{%d,%d} //",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild, // xChunks, zChunks); @@ -2152,19 +2105,19 @@ bool LevelRenderer::updateDirtyChunks() { for (int y = 0; y < CHUNK_Y_COUNT; y++) { ClipChunk* pClipChunk = &chunks[p][(z * yChunks + y) * xChunks + x]; - // Get distance to this chunk - deliberately not calling - // the chunk's method of doing this to avoid overheads - // (passing entitie, type conversion etc.) that this - // involves + // Get distance to this chunk - deliberately not + // calling the chunk's method of doing this to avoid + // overheads (passing entitie, type conversion etc.) + // that this involves int xd = pClipChunk->xm - px; int yd = pClipChunk->ym - py; int zd = pClipChunk->zm - pz; int distSq = xd * xd + yd * yd + zd * zd; int distSqWeighted = xd * xd + yd * yd * 4 + - zd * - zd; // Weighting against y to prioritise things - // in same x/z plane as player first + zd * zd; // Weighting against y to prioritise + // things in same x/z plane as player + // first if (globalChunkFlags[pClipChunk->globalIdx] & CHUNK_FLAG_DIRTY) { @@ -2172,11 +2125,11 @@ bool LevelRenderer::updateDirtyChunks() { globalChunkFlags[pClipChunk->globalIdx] & CHUNK_FLAG_COMPILED || (distSq < - 20 * 20)) // Always rebuild really near things - // or else building (say) at tower - // up into empty blocks when we are - // low on memory will not create - // render data + 20 * 20)) // Always rebuild really near + // things or else building (say) + // at tower up into empty blocks + // when we are low on memory + // will not create render data { considered++; // Is this chunk nearer than our nearest? @@ -2189,33 +2142,36 @@ bool LevelRenderer::updateDirtyChunks() { #endif #ifdef _CRITICAL_CHUNKS - // AP - this will make sure that if a deferred - // grouping has started, only critical chunks go - // into that grouping, even if a non-critical - // chunk is closer. + // AP - this will make sure that if a + // deferred grouping has started, only + // critical chunks go into that grouping, + // even if a non-critical chunk is closer. if ((!veryNearCount && isNearer) || (distSq < 20 * 20 && - (globalChunkFlags[pClipChunk->globalIdx] & + (globalChunkFlags[pClipChunk + ->globalIdx] & CHUNK_FLAG_CRITICAL))) #else if (isNearer) #endif { - // At this point we've got a chunk that we - // would like to consider for rendering, at - // least based on its proximity to the - // player(s). Its *quite* quick to generate - // empty render data for render chunks, but - // if we let the rebuilding do that then the - // after rebuilding we will have to start - // searching for the next nearest chunk from - // scratch again. Instead, its better to - // detect empty chunks at this stage, flag - // them up as not dirty (and empty), and - // carry on. The levelchunk's - // isRenderChunkEmpty method can be quite - // optimal as it can make use of the chunk's - // data compression to detect emptiness + // At this point we've got a chunk that + // we would like to consider for + // rendering, at least based on its + // proximity to the player(s). Its + // *quite* quick to generate empty + // render data for render chunks, but if + // we let the rebuilding do that then + // the after rebuilding we will have to + // start searching for the next nearest + // chunk from scratch again. Instead, + // its better to detect empty chunks at + // this stage, flag them up as not dirty + // (and empty), and carry on. The + // levelchunk's isRenderChunkEmpty + // method can be quite optimal as it can + // make use of the chunk's data + // compression to detect emptiness // without actually testing as many data // items as uncompressed data would. Chunk* chunk = pClipChunk->chunk; @@ -2225,8 +2181,8 @@ bool LevelRenderer::updateDirtyChunks() { nearChunk = pClipChunk; minDistSq = distSqWeighted; #ifdef _LARGE_WORLDS - nearestClipChunks.insert( - nearChunk, minDistSq); + nearestClipChunks.insert(nearChunk, + minDistSq); #endif } else { chunk->clearDirty(); @@ -2240,7 +2196,8 @@ bool LevelRenderer::updateDirtyChunks() { #ifdef _CRITICAL_CHUNKS // AP - is the chunk near and also critical if (distSq < 20 * 20 && - ((globalChunkFlags[pClipChunk->globalIdx] & + ((globalChunkFlags[pClipChunk + ->globalIdx] & CHUNK_FLAG_CRITICAL))) #else if (distSq < 20 * 20) @@ -2270,24 +2227,25 @@ bool LevelRenderer::updateDirtyChunks() { chunk = nearestClipChunks.items[i].first->chunk; // If this chunk is very near, then move the renderer into a // deferred mode. This won't commit any command buffers for - // rendering until we call CBuffDeferredModeEnd(), allowing us to - // group any near changes into an atomic unit. This is essential so - // we don't temporarily create any holes in the environment whilst - // updating one chunk and not the neighbours. The "ver near" aspect - // of this is just a cosmetic nicety - exactly the same thing would - // happen further away, but we just don't care about it so much from - // terms of visual impact. + // rendering until we call CBuffDeferredModeEnd(), allowing us + // to group any near changes into an atomic unit. This is + // essential so we don't temporarily create any holes in the + // environment whilst updating one chunk and not the neighbours. + // The "ver near" aspect of this is just a cosmetic nicety - + // exactly the same thing would happen further away, but we just + // don't care about it so much from terms of visual impact. if (veryNearCount > 0) { RenderManager.CBuffDeferredModeStart(); } // Build this chunk & return false to continue processing chunk->clearDirty(); // Take a copy of the details that are required for chunk - // rebuilding, and rebuild That instead of the original chunk data. - // This is done within the m_csDirtyChunks critical section, which - // means that any chunks can't be repositioned whilst we are doing - // this copy. The copy will then be guaranteed to be consistent - // whilst rebuilding takes place outside of that critical section. + // rebuilding, and rebuild That instead of the original chunk + // data. This is done within the m_csDirtyChunks critical + // section, which means that any chunks can't be repositioned + // whilst we are doing this copy. The copy will then be + // guaranteed to be consistent whilst rebuilding takes place + // outside of that critical section. permaChunk[index].makeCopyForRebuild(chunk); ++index; } @@ -2361,25 +2319,26 @@ bool LevelRenderer::updateDirtyChunks() { static Chunk permaChunk; { FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); - // If this chunk is very near, then move the renderer into a deferred - // mode. This won't commit any command buffers for rendering until we - // call CBuffDeferredModeEnd(), allowing us to group any near changes - // into an atomic unit. This is essential so we don't temporarily create - // any holes in the environment whilst updating one chunk and not the - // neighbours. The "ver near" aspect of this is just a cosmetic nicety - - // exactly the same thing would happen further away, but we just don't - // care about it so much from terms of visual impact. + // If this chunk is very near, then move the renderer into a + // deferred mode. This won't commit any command buffers for + // rendering until we call CBuffDeferredModeEnd(), allowing us to + // group any near changes into an atomic unit. This is essential so + // we don't temporarily create any holes in the environment whilst + // updating one chunk and not the neighbours. The "ver near" aspect + // of this is just a cosmetic nicety - exactly the same thing would + // happen further away, but we just don't care about it so much from + // terms of visual impact. if (veryNearCount > 0) { RenderManager.CBuffDeferredModeStart(); } // Build this chunk & return false to continue processing chunk->clearDirty(); - // Take a copy of the details that are required for chunk rebuilding, - // and rebuild That instead of the original chunk data. This is done - // within the m_csDirtyChunks critical section, which means that any - // chunks can't be repositioned whilst we are doing this copy. The copy - // will then be guaranteed to be consistent whilst rebuilding takes - // place outside of that critical section. + // Take a copy of the details that are required for chunk + // rebuilding, and rebuild That instead of the original chunk data. + // This is done within the m_csDirtyChunks critical section, which + // means that any chunks can't be repositioned whilst we are doing + // this copy. The copy will then be guaranteed to be consistent + // whilst rebuilding takes place outside of that critical section. permaChunk.makeCopyForRebuild(chunk); LeaveCriticalSection(&m_csDirtyChunks); } @@ -2879,13 +2838,16 @@ void LevelRenderer::waitForCull_SPU() { } #endif // __PS3__ +// 4jcraft: optional occlusion culling system, i hope to upgrade it soon +// gives better performances but mostly breaks chunk rendering void LevelRenderer::cull(Culler* culler, float a) { - int playerIndex = mc->player->GetXboxPad(); // 4J added + int playerIndex = mc->player->GetXboxPad(); + if (chunks[playerIndex].data == nullptr) return; #if defined __PS3__ && !defined DISABLE_SPU_CODE cull_SPU(playerIndex, culler, a); return; -#endif // __PS3__ +#endif FrustumCuller* fc = (FrustumCuller*)culler; FrustumData* fd = fc->frustum; @@ -2901,28 +2863,297 @@ void LevelRenderer::cull(Culler* culler, float a) { (fy * -fc->yOff) + (fz * -fc->zOff)); } - ClipChunk* pClipChunk = chunks[playerIndex].data; - int vis = 0; - int total = 0; - int numWrong = 0; +#if defined(OCCLUSION_MODE_NONE) + // just check if chunk is compiled and non-empty for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { - unsigned char flags = pClipChunk->globalIdx == -1 - ? 0 - : globalChunkFlags[pClipChunk->globalIdx]; - - if ((flags & CHUNK_FLAG_COMPILED) && - ((flags & CHUNK_FLAG_EMPTYBOTH) != CHUNK_FLAG_EMPTYBOTH)) { - bool clipres = clip(pClipChunk->aabb, fdraw); - pClipChunk->visible = clipres; - if (pClipChunk->visible) vis++; - total++; - } else { - pClipChunk->visible = false; + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) { + cc->visible = false; + continue; } - pClipChunk++; - } -} + unsigned char flags = globalChunkFlags[cc->globalIdx]; + bool isCompiled = (flags & CHUNK_FLAG_COMPILED) != 0; + bool isEmptyBoth = + (flags & CHUNK_FLAG_EMPTYBOTH) == CHUNK_FLAG_EMPTYBOTH; + + cc->visible = isCompiled && !isEmptyBoth; + } + +#elif defined(OCCLUSION_MODE_FRUSTUM) + // Just ~~monika~~ frustum culling + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) { + cc->visible = false; + continue; + } + + unsigned char flags = globalChunkFlags[cc->globalIdx]; + bool isCompiled = (flags & CHUNK_FLAG_COMPILED) != 0; + bool isEmptyBoth = + (flags & CHUNK_FLAG_EMPTYBOTH) == CHUNK_FLAG_EMPTYBOTH; + + if (isCompiled && !isEmptyBoth) { + float cellBounds[6] = {(float)cc->chunk->x - 0.1f, + (float)cc->chunk->y - 0.1f, + (float)cc->chunk->z - 0.1f, + (float)cc->chunk->x + CHUNK_XZSIZE + 0.1f, + (float)cc->chunk->y + CHUNK_SIZE + 0.1f, + (float)cc->chunk->z + CHUNK_XZSIZE + 0.1f}; + cc->visible = clip(cellBounds, fdraw); + } else { + cc->visible = false; + } + } + +#elif defined(OCCLUSION_MODE_HARDWARE) +// TODO: Hardware occlusion culling using GPU queries +// For now, fall back to frustum culling +#warning \ + "OCCLUSION_MODE_HARDWARE is not implemented yet, falling back to frustum culling" + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) { + cc->visible = false; + continue; + } + + unsigned char flags = globalChunkFlags[cc->globalIdx]; + bool isCompiled = (flags & CHUNK_FLAG_COMPILED) != 0; + bool isEmptyBoth = + (flags & CHUNK_FLAG_EMPTYBOTH) == CHUNK_FLAG_EMPTYBOTH; + + if (isCompiled && !isEmptyBoth) { + float cellBounds[6] = {(float)cc->chunk->x - 0.1f, + (float)cc->chunk->y - 0.1f, + (float)cc->chunk->z - 0.1f, + (float)cc->chunk->x + CHUNK_XZSIZE + 0.1f, + (float)cc->chunk->y + CHUNK_SIZE + 0.1f, + (float)cc->chunk->z + CHUNK_XZSIZE + 0.1f}; + cc->visible = clip(cellBounds, fdraw); + } else { + cc->visible = false; + } + } + +#elif defined(OCCLUSION_MODE_BFS) + // Experimental BFS occlusion culling. + // Check https://tomcc.github.io/2014/08/31/visibility-1.html + // And https://tomcc.github.io/2014/08/31/visibility-2.html + // And finally https://en.wikipedia.org/wiki/Breadth-first_search + std::shared_ptr player = mc->cameraTargetPlayer; + float camX = (float)(player->xOld + (player->x - player->xOld) * a); + float camY = (float)(player->yOld + (player->y - player->yOld) * a); + float camZ = (float)(player->zOld + (player->z - player->zOld) * a); + + auto intFloorDiv = [](int v, int div) { + if (v < 0 && v % div != 0) return (v / div) - 1; + return v / div; + }; + + auto floatFloorDiv = [](float v, int div) { + int iv = (int)v; + if (v < 0 && v != iv) iv--; + if (iv < 0 && iv % div != 0) return (iv / div) - 1; + return iv / div; + }; + + int minCx = INT_MAX, minCy = INT_MAX, minCz = INT_MAX; + int maxCx = INT_MIN, maxCy = INT_MIN, maxCz = INT_MIN; + + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + cc->visible = false; + if (cc->globalIdx < 0) continue; + + int cx = intFloorDiv(cc->chunk->x, CHUNK_XZSIZE); + int cy = intFloorDiv(cc->chunk->y, CHUNK_SIZE); + int cz = intFloorDiv(cc->chunk->z, CHUNK_XZSIZE); + + if (cx < minCx) minCx = cx; + if (cy < minCy) minCy = cy; + if (cz < minCz) minCz = cz; + if (cx > maxCx) maxCx = cx; + if (cy > maxCy) maxCy = cy; + if (cz > maxCz) maxCz = cz; + } + + if (minCx > maxCx) return; + + int sizeX = maxCx - minCx + 1; + int sizeY = maxCy - minCy + 1; + int sizeZ = maxCz - minCz + 1; + + std::vector grid(sizeX * sizeY * sizeZ, nullptr); + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) continue; + int lx = intFloorDiv(cc->chunk->x, CHUNK_XZSIZE) - minCx; + int ly = intFloorDiv(cc->chunk->y, CHUNK_SIZE) - minCy; + int lz = intFloorDiv(cc->chunk->z, CHUNK_XZSIZE) - minCz; + grid[(lx * sizeY + ly) * sizeZ + lz] = cc; + } + + auto getChunkAt = [&](int cx, int cy, int cz) -> ClipChunk* { + int lx = cx - minCx; + int ly = cy - minCy; + int lz = cz - minCz; + if (lx >= 0 && lx < sizeX && ly >= 0 && ly < sizeY && lz >= 0 && + lz < sizeZ) { + return grid[(lx * sizeY + ly) * sizeZ + lz]; + } + return nullptr; + }; + + int startCx = floatFloorDiv(camX, CHUNK_XZSIZE); + int startCy = floatFloorDiv(camY, CHUNK_SIZE); + int startCz = floatFloorDiv(camZ, CHUNK_XZSIZE); + + if (startCx < minCx) + startCx = minCx; + else if (startCx > maxCx) + startCx = maxCx; + if (startCy < minCy) + startCy = minCy; + else if (startCy > maxCy) + startCy = maxCy; + if (startCz < minCz) + startCz = minCz; + else if (startCz > maxCz) + startCz = maxCz; + + ClipChunk* startChunk = getChunkAt(startCx, startCy, startCz); + + if (!startChunk) { + float minDist = 1e30f; + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) continue; + float midX = cc->chunk->x + CHUNK_XZSIZE * 0.5f; + float midY = cc->chunk->y + CHUNK_SIZE * 0.5f; + float midZ = cc->chunk->z + CHUNK_XZSIZE * 0.5f; + float dist = (camX - midX) * (camX - midX) + + (camY - midY) * (camY - midY) + + (camZ - midZ) * (camZ - midZ); + if (dist < minDist) { + minDist = dist; + startChunk = cc; + } + } + } + + if (!startChunk) return; + + struct BFSNode { + ClipChunk* cc; + int incomingFace; + }; + + std::vector q; + q.reserve(chunks[playerIndex].length * 2); + int qHead = 0; + + std::vector visitedFaces(chunks[playerIndex].length, 0); + + q.push_back({startChunk, -1}); + visitedFaces[startChunk - chunks[playerIndex].data] = 0x3F; + + static const int OFFSETS[6][3] = { + {0, -1, 0}, // 0: -Y + {0, 1, 0}, // 1: +Y + {0, 0, -1}, // 2: -Z + {0, 0, 1}, // 3: +Z + {-1, 0, 0}, // 4: -X + {1, 0, 0} // 5: +X + }; + + while (qHead < (int)q.size()) { + BFSNode node = q[qHead++]; + ClipChunk* curr = node.cc; + int incFace = node.incomingFace; + + unsigned char flags = globalChunkFlags[curr->globalIdx]; + bool isCompiled = (flags & CHUNK_FLAG_COMPILED) != 0; + bool isEmptyBoth = + (flags & CHUNK_FLAG_EMPTYBOTH) == CHUNK_FLAG_EMPTYBOTH; + + if (isCompiled && !isEmptyBoth) { + curr->visible = true; + } + + int cx = intFloorDiv(curr->chunk->x, CHUNK_XZSIZE); + int cy = intFloorDiv(curr->chunk->y, CHUNK_SIZE); + int cz = intFloorDiv(curr->chunk->z, CHUNK_XZSIZE); + + uint64_t conn = getGlobalChunkConnectivity(curr->globalIdx); + + for (int i = 0; i < 6; i++) { + int outFace = i; + + bool canGo = false; + float chkX = curr->chunk->x; + float chkY = curr->chunk->y; + float chkZ = curr->chunk->z; + switch (outFace) { + case 0: + canGo = camY >= chkY; + break; + case 1: + canGo = camY <= chkY + CHUNK_SIZE; + break; + case 2: + canGo = camZ >= chkZ; + break; + case 3: + canGo = camZ <= chkZ + CHUNK_XZSIZE; + break; + case 4: + canGo = camX >= chkX; + break; + case 5: + canGo = camX <= chkX + CHUNK_XZSIZE; + break; + } + if (!canGo) continue; + + if (incFace != -1) { + int shift = (incFace * 6) + outFace; + if ((conn & (1ULL << shift)) == 0) continue; + } + + int nx = cx + OFFSETS[i][0]; + int ny = cy + OFFSETS[i][1]; + int nz = cz + OFFSETS[i][2]; + + ClipChunk* neighbor = getChunkAt(nx, ny, nz); + if (!neighbor) continue; + + int nIdx = neighbor - chunks[playerIndex].data; + int nextIncFace = outFace ^ 1; + + if ((visitedFaces[nIdx] & (1 << nextIncFace)) != 0) continue; + + float cellBounds[6] = { + (float)neighbor->chunk->x - 0.1f, + (float)neighbor->chunk->y - 0.1f, + (float)neighbor->chunk->z - 0.1f, + (float)neighbor->chunk->x + CHUNK_XZSIZE + 0.1f, + (float)neighbor->chunk->y + CHUNK_SIZE + 0.1f, + (float)neighbor->chunk->z + CHUNK_XZSIZE + 0.1f}; + + if (!clip(cellBounds, fdraw)) continue; + + visitedFaces[nIdx] |= (1 << nextIncFace); + q.push_back({neighbor, nextIncFace}); + } + } + +#else +#error \ + "Unknown occlusion mode, this should NEVER happen, check meson.build for misconfiguration" +#endif +} void LevelRenderer::playStreamingMusic(const std::wstring& name, int x, int y, int z) { if (name != L"") { @@ -3891,6 +4122,19 @@ void LevelRenderer::setGlobalChunkFlag(int x, int y, int z, Level* level, } } +void LevelRenderer::setGlobalChunkConnectivity(int index, uint64_t conn) { + if (index >= 0 && index < getGlobalChunkCount()) { + globalChunkConnectivity[index] = conn; + } +} + +uint64_t LevelRenderer::getGlobalChunkConnectivity(int index) { + if (index >= 0 && index < getGlobalChunkCount()) { + return globalChunkConnectivity[index]; + } + return ~(uint64_t)0; // out of bounds +} + void LevelRenderer::clearGlobalChunkFlag(int x, int y, int z, Level* level, unsigned char flag, unsigned char shift) { diff --git a/Minecraft.Client/Rendering/LevelRenderer.h b/Minecraft.Client/Rendering/LevelRenderer.h index 58f194a3b..081aa98b9 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.h +++ b/Minecraft.Client/Rendering/LevelRenderer.h @@ -209,7 +209,8 @@ private: emptyChunks; static const int RENDERLISTS_LENGTH = 4; // 4J - added OffsettedRenderList renderLists[RENDERLISTS_LENGTH]; - + void setGlobalChunkConnectivity(int index, uint64_t conn); + uint64_t getGlobalChunkConnectivity(int index); std::unordered_map destroyingBlocks; Icon** breakingTextures; @@ -296,6 +297,8 @@ public: void clearGlobalChunkFlag(int x, int y, int z, Level* level, unsigned char flag, unsigned char shift = 0); + static uint64_t* globalChunkConnectivity; + // Get/set whole byte of flags unsigned char getGlobalChunkFlags(int x, int y, int z, Level* level); void setGlobalChunkFlags(int x, int y, int z, Level* level, diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index abbb3b1ef..123320903 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -85,10 +85,10 @@ if get_option('enable_frame_profiler') endif if get_option('ui_backend') == 'shiggy' - shiggy_dep = dependency( - 'shiggy', - fallback : ['shiggy', 'shiggy_dep'], - ) + shiggy_dep = dependency( + 'shiggy', + fallback: ['shiggy', 'shiggy_dep'], + ) global_cpp_defs += ['-D_ENABLEIGGY'] client_dependencies += shiggy_dep @@ -98,7 +98,18 @@ if get_option('ui_backend') == 'java' global_cpp_defs += '-DENABLE_JAVA_GUIS' endif -client = executable('Minecraft.Client', +occlusion_mode = get_option('occlusion_culling') +if occlusion_mode == 'off' + global_cpp_defs += ['-DOCCLUSION_MODE_NONE'] +elif occlusion_mode == 'frustum' + global_cpp_defs += ['-DOCCLUSION_MODE_FRUSTUM'] +elif occlusion_mode == 'bfs' + global_cpp_defs += ['-DOCCLUSION_MODE_BFS', '-DUSE_OCCLUSION_CULLING'] +elif occlusion_mode == 'hardware' + global_cpp_defs += ['-DOCCLUSION_MODE_HARDWARE', '-DUSE_OCCLUSION_CULLING'] +endif +client = executable( + 'Minecraft.Client', client_sources + platform_sources + localisation[1], include_directories: include_directories('Platform', 'Platform/Linux/Iggy/include'), dependencies: client_dependencies, diff --git a/meson.options b/meson.options index 0290c8005..f92698ac4 100644 --- a/meson.options +++ b/meson.options @@ -27,8 +27,17 @@ option( ) option( +<<<<<<< HEAD 'enable_frame_profiler', type: 'boolean', value: false, description: 'Enable the in-engine frame profiler for render hotspot discovery.', ) +======= + 'occlusion_culling', + type: 'combo', + choices: ['off', 'frustum', 'bfs', 'hardware'], + value: 'frustum', + description: 'Occlusion culling mode. Off disables ALL CULLING (debug only!), Frustum disables offscreen rendering (default), BFS is experimental connectivity culling, hardware uses GPU queries.', +) +>>>>>>> db062d4ba (new culler) From 51ad1434db4ad2730372b20dc9a033fcf07f7534 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Sun, 29 Mar 2026 23:29:16 +0200 Subject: [PATCH 65/71] new culler --- Minecraft.Client/Rendering/LevelRenderer.cpp | 24 +++----------------- meson.options | 5 ++-- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index b73ee025c..a91df96dd 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -807,11 +807,6 @@ int compare(const void* a, const void* b) { } #endif - -// 4jcraft: removed the vita & ps3 versions because they were SEVERELY annoying -// me it looked so ugly, god. -// if you ever hate me for it, deal with it, the source code is STILL visible if -// you look at someone elses fork. like steamcmd int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { int playerIndex = mc->player->GetXboxPad(); if (chunks[playerIndex].data == NULL) return 0; @@ -824,17 +819,13 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { glPushMatrix(); glTranslatef((float)-xOff, (float)-yOff, (float)-zOff); - ClipChunk* pClipChunk = chunks[playerIndex].data; - unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; - bool first = true; - int count = 0; +#ifdef __PS3__ waitForCull_SPU(); if (layer == 0) { count = g_cullDataIn[playerIndex].numToRender_layer0; RenderManager.CBuffCallMultiple( g_cullDataIn[playerIndex].listArray_layer0, count); - } else // layer == 1 - { + } else { // layer == 1 count = g_cullDataIn[playerIndex].numToRender_layer1; RenderManager.CBuffCallMultiple( g_cullDataIn[playerIndex].listArray_layer1, count); @@ -891,19 +882,10 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { // first }); } + { FRAME_PROFILE_SCOPE(ChunkPlayback); for (ClipChunk* chunk : sortList) { - // ugly occluder - float dx = (chunk->chunk->x + 8.0f) - (float)xOff; - float dy = (chunk->chunk->y + 8.0f) - (float)yOff; - float dz = (chunk->chunk->z + 8.0f) - (float)zOff; - bool isVeryNear = (dx * dx + dy * dy + dz * dz) < (16.0f * 16.0f); - - if (!isVeryNear && layer == 0) { - // todo: occlusion flag - } - int list = chunk->globalIdx * 2 + layer; list += chunkLists; diff --git a/meson.options b/meson.options index f92698ac4..4dc73f322 100644 --- a/meson.options +++ b/meson.options @@ -27,17 +27,16 @@ option( ) option( -<<<<<<< HEAD 'enable_frame_profiler', type: 'boolean', value: false, description: 'Enable the in-engine frame profiler for render hotspot discovery.', ) -======= + +option( 'occlusion_culling', type: 'combo', choices: ['off', 'frustum', 'bfs', 'hardware'], value: 'frustum', description: 'Occlusion culling mode. Off disables ALL CULLING (debug only!), Frustum disables offscreen rendering (default), BFS is experimental connectivity culling, hardware uses GPU queries.', ) ->>>>>>> db062d4ba (new culler) From c6fa51d5928b1c56ab7f637037237625bf6b21d0 Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:26:35 +1100 Subject: [PATCH 66/71] perf(render): optimise tile entity cleanup and profiling --- Minecraft.Client/Rendering/Chunk.cpp | 177 ++++++++---------- Minecraft.Client/Rendering/Chunk.h | 2 + Minecraft.Client/Rendering/LevelRenderer.cpp | 112 ++++++++--- Minecraft.Client/Rendering/LevelRenderer.h | 22 ++- Minecraft.Client/Utils/FrameProfiler.cpp | 5 +- Minecraft.Client/Utils/FrameProfiler.h | 2 + .../Blocks/TileEntities/TileEntity.cpp | 11 +- .../Blocks/TileEntities/TileEntity.h | 3 +- Minecraft.World/Level/Level.cpp | 17 +- Minecraft.World/Level/Level.h | 4 +- Minecraft.World/Level/LevelChunk.cpp | 10 +- 11 files changed, 213 insertions(+), 152 deletions(-) diff --git a/Minecraft.Client/Rendering/Chunk.cpp b/Minecraft.Client/Rendering/Chunk.cpp index ff0550717..9b294d5e8 100644 --- a/Minecraft.Client/Rendering/Chunk.cpp +++ b/Minecraft.Client/Rendering/Chunk.cpp @@ -8,6 +8,7 @@ #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" #include "LevelRenderer.h" #include "../Utils/FrameProfiler.h" +#include #ifdef __PS3__ #include "../Platform/PS3/SPU_Tasks/ChunkUpdate/ChunkRebuildData.h" @@ -36,6 +37,66 @@ Tesselator* Chunk::t = Tesselator::getInstance(); #endif LevelRenderer* Chunk::levelRenderer; +void Chunk::reconcileRenderableTileEntities( + const std::vector >& renderableTileEntities) { + int key = + levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if (!renderableTileEntities.empty()) { + std::unordered_set currentRenderableTileEntitySet; + currentRenderableTileEntitySet.reserve(renderableTileEntities.size()); + for (size_t i = 0; i < renderableTileEntities.size(); i++) { + currentRenderableTileEntitySet.insert(renderableTileEntities[i].get()); + } + + if (it != globalRenderableTileEntities->end()) { + LevelRenderer::RenderableTileEntityBucket& existingBucket = + it->second; + + for (AUTO_VAR(it2, existingBucket.tiles.begin()); + it2 != existingBucket.tiles.end(); it2++) { + TileEntity* tileEntity = (*it2).get(); + if (currentRenderableTileEntitySet.find(tileEntity) == + currentRenderableTileEntitySet.end()) { + (*it2)->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageFlaggedAtChunk); + levelRenderer->queueRenderableTileEntityForRemoval_Locked( + key, tileEntity); + } else { + (*it2)->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageKeep); + } + } + + for (size_t i = 0; i < renderableTileEntities.size(); i++) { + renderableTileEntities[i]->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageKeep); + if (existingBucket.indexByTile.find(renderableTileEntities[i].get()) == + existingBucket.indexByTile.end()) { + levelRenderer->addRenderableTileEntity_Locked( + key, renderableTileEntities[i]); + } + } + } else { + for (size_t i = 0; i < renderableTileEntities.size(); i++) { + renderableTileEntities[i]->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageKeep); + levelRenderer->addRenderableTileEntity_Locked( + key, renderableTileEntities[i]); + } + } + } else if (it != globalRenderableTileEntities->end()) { + for (AUTO_VAR(it2, it->second.tiles.begin()); + it2 != it->second.tiles.end(); + it2++) { + (*it2)->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageFlaggedAtChunk); + levelRenderer->queueRenderableTileEntityForRemoval_Locked(key, + (*it2).get()); + } + } +} + // TODO - 4J see how input entity vector is set up and decide what way is best // to pass this to the function Chunk::Chunk(Level* level, LevelRenderer::rteMap& globalRenderableTileEntities, @@ -519,57 +580,8 @@ void Chunk::rebuild() { // from the dimension and chunk position (using same index as is used for // global flags) #if 1 - int key = - levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); EnterCriticalSection(globalRenderableTileEntities_cs); - if (renderableTileEntities.size()) { - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - // We've got some renderable tile entities that we want associated - // with this chunk, and an existing list of things that used to be. - // We need to flag any that we don't need any more to be removed, - // keep those that we do, and add any new ones - - // First pass - flag everything already existing to be removed - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - - // Now go through the current list. If these are already in the - // list, then unflag the remove flag. If they aren't, then add - for (int i = 0; i < renderableTileEntities.size(); i++) { - AUTO_VAR(it2, find(it->second.begin(), it->second.end(), - renderableTileEntities[i])); - if (it2 == it->second.end()) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } else { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageKeep); - } - } - } else { - // Easy case - nothing already existing for this chunk. Add them all - // in. - for (int i = 0; i < renderableTileEntities.size(); i++) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } - } - } else { - // Another easy case - we don't want any renderable tile entities - // associated with this chunk. Flag all to be removed. - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - } - } + reconcileRenderableTileEntities(renderableTileEntities); LeaveCriticalSection(globalRenderableTileEntities_cs); PIXEndNamedEvent(); #else @@ -831,57 +843,8 @@ void Chunk::rebuild_SPU() { // from the dimension and chunk position (using same index as is used for // global flags) #if 1 - int key = - levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); EnterCriticalSection(globalRenderableTileEntities_cs); - if (renderableTileEntities.size()) { - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - // We've got some renderable tile entities that we want associated - // with this chunk, and an existing list of things that used to be. - // We need to flag any that we don't need any more to be removed, - // keep those that we do, and add any new ones - - // First pass - flag everything already existing to be removed - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - - // Now go through the current list. If these are already in the - // list, then unflag the remove flag. If they aren't, then add - for (int i = 0; i < renderableTileEntities.size(); i++) { - AUTO_VAR(it2, find(it->second.begin(), it->second.end(), - renderableTileEntities[i])); - if (it2 == it->second.end()) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } else { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageKeep); - } - } - } else { - // Easy case - nothing already existing for this chunk. Add them all - // in. - for (int i = 0; i < renderableTileEntities.size(); i++) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } - } - } else { - // Another easy case - we don't want any renderable tile entities - // associated with this chunk. Flag all to be removed. - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - } - } + reconcileRenderableTileEntities(renderableTileEntities); LeaveCriticalSection(globalRenderableTileEntities_cs); #else // Find the removed ones: @@ -1108,15 +1071,19 @@ uint64_t Chunk::computeConnectivity(const uint8_t* tileIds) { } void Chunk::reset() { if (assigned) { + int oldKey = -1; + bool retireRenderableTileEntities = false; + EnterCriticalSection(&levelRenderer->m_csDirtyChunks); + oldKey = levelRenderer->getGlobalIndexForChunk(x, y, z, level); unsigned char refCount = levelRenderer->decGlobalChunkRefCount(x, y, z, level); assigned = false; // printf("\t\t [dec] refcount %d at %d, %d, //%d\n",refCount,x,y,z); - if (refCount == 0) { - int lists = - levelRenderer->getGlobalIndexForChunk(x, y, z, level) * 2; + if (refCount == 0 && oldKey != -1) { + retireRenderableTileEntities = true; + int lists = oldKey * 2; if (lists >= 0) { lists += levelRenderer->chunkLists; for (int i = 0; i < 2; i++) { @@ -1128,6 +1095,10 @@ void Chunk::reset() { } } LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); + + if (retireRenderableTileEntities) { + levelRenderer->retireRenderableTileEntitiesForChunkKey(oldKey); + } } clipChunk->visible = false; diff --git a/Minecraft.Client/Rendering/Chunk.h b/Minecraft.Client/Rendering/Chunk.h index c524c69ce..fded430cf 100644 --- a/Minecraft.Client/Rendering/Chunk.h +++ b/Minecraft.Client/Rendering/Chunk.h @@ -69,6 +69,8 @@ public: private: void translateToPos(); + void reconcileRenderableTileEntities( + const std::vector >& renderableTileEntities); public: void makeCopyForRebuild(Chunk* source); diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index a91df96dd..763611cca 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -410,8 +410,12 @@ void LevelRenderer::setLevel(int playerIndex, MultiPlayerLevel* level) { // tile entities in the world dissappear We should only do this when // actually exiting the game, so only when the primary player sets there // level to NULL - if (playerIndex == ProfileManager.GetPrimaryPad()) + if (playerIndex == ProfileManager.GetPrimaryPad()) { + EnterCriticalSection(&m_csRenderableTileEntities); renderableTileEntities.clear(); + m_renderableTileEntitiesPendingRemoval.clear(); + LeaveCriticalSection(&m_csRenderableTileEntities); + } } } @@ -641,34 +645,13 @@ void LevelRenderer::renderEntities(Vec3* cam, Culler* culler, float a) { // Don't render if it isn't in the same dimension as this player if (!isGlobalIndexInSameDimension(idx, level[playerIndex])) continue; - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); + for (AUTO_VAR(it2, it->second.tiles.begin()); + it2 != it->second.tiles.end(); it2++) { TileEntityRenderDispatcher::instance->render(*it2, a); } } - // Now consider if any of these renderable tile entities have been flagged - // for removal, and if so, remove - for (AUTO_VAR(it, renderableTileEntities.begin()); - it != renderableTileEntities.end();) { - int idx = it->first; - - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end();) { - // If it has been flagged for removal, remove - if ((*it2)->shouldRemoveForRender()) { - it2 = it->second.erase(it2); - } else { - it2++; - } - } - - // If there aren't any entities left for this key, then delete the key - if (it->second.size() == 0) { - it = renderableTileEntities.erase(it); - } else { - it++; - } - } LeaveCriticalSection(&m_csRenderableTileEntities); @@ -4181,16 +4164,87 @@ unsigned char LevelRenderer::decGlobalChunkRefCount(int x, int y, int z, } } +void LevelRenderer::queueRenderableTileEntityForRemoval_Locked( + int key, TileEntity* tileEntity) { + m_renderableTileEntitiesPendingRemoval[key].insert(tileEntity); +} + +void LevelRenderer::addRenderableTileEntity_Locked( + int key, const std::shared_ptr& tileEntity) { + RenderableTileEntityBucket& bucket = renderableTileEntities[key]; + TileEntity* tileEntityPtr = tileEntity.get(); + if (bucket.indexByTile.find(tileEntityPtr) != bucket.indexByTile.end()) { + return; + } + + size_t index = bucket.tiles.size(); + bucket.tiles.push_back(tileEntity); + bucket.indexByTile.insert(std::make_pair(tileEntityPtr, index)); +} + +void LevelRenderer::eraseRenderableTileEntity_Locked( + RenderableTileEntityBucket& bucket, TileEntity* tileEntity) { + auto it = bucket.indexByTile.find(tileEntity); + if (it == bucket.indexByTile.end()) { + return; + } + + size_t index = it->second; + size_t lastIndex = bucket.tiles.size() - 1; + if (index != lastIndex) { + std::shared_ptr moved = bucket.tiles[lastIndex]; + bucket.tiles[index] = moved; + bucket.indexByTile[moved.get()] = index; + } + + bucket.tiles.pop_back(); + bucket.indexByTile.erase(it); +} + +void LevelRenderer::retireRenderableTileEntitiesForChunkKey(int key) { + if (key == -1) return; + + EnterCriticalSection(&m_csRenderableTileEntities); + renderableTileEntities.erase(key); + m_renderableTileEntitiesPendingRemoval.erase(key); + LeaveCriticalSection(&m_csRenderableTileEntities); +} + // 4J added void LevelRenderer::fullyFlagRenderableTileEntitiesToBeRemoved() { + FRAME_PROFILE_SCOPE(RenderableTileEntityCleanup); + EnterCriticalSection(&m_csRenderableTileEntities); - AUTO_VAR(itChunkEnd, renderableTileEntities.end()); - for (AUTO_VAR(it, renderableTileEntities.begin()); it != itChunkEnd; it++) { - AUTO_VAR(itTEEnd, it->second.end()); - for (AUTO_VAR(it2, it->second.begin()); it2 != itTEEnd; it2++) { - (*it2)->upgradeRenderRemoveStage(); + if (m_renderableTileEntitiesPendingRemoval.empty()) { + LeaveCriticalSection(&m_csRenderableTileEntities); + return; + } + + auto itKeyEnd = m_renderableTileEntitiesPendingRemoval.end(); + for (auto itKey = m_renderableTileEntitiesPendingRemoval.begin(); + itKey != itKeyEnd; itKey++) { + auto itChunk = renderableTileEntities.find(itKey->first); + if (itChunk == renderableTileEntities.end()) continue; + + RenderableTileEntityBucket& bucket = itChunk->second; + for (AUTO_VAR(itPending, itKey->second.begin()); + itPending != itKey->second.end(); itPending++) { + if (bucket.indexByTile.find(*itPending) == bucket.indexByTile.end()) { + continue; + } + + if (!(*itPending)->finalizeRenderRemoveStage()) { + continue; + } + + eraseRenderableTileEntity_Locked(bucket, *itPending); + } + + if (bucket.tiles.empty()) { + renderableTileEntities.erase(itChunk); } } + m_renderableTileEntitiesPendingRemoval.clear(); LeaveCriticalSection(&m_csRenderableTileEntities); } diff --git a/Minecraft.Client/Rendering/LevelRenderer.h b/Minecraft.Client/Rendering/LevelRenderer.h index 081aa98b9..fbafa7ce8 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.h +++ b/Minecraft.Client/Rendering/LevelRenderer.h @@ -10,6 +10,7 @@ #ifdef __PS3__ #include "../Platform/PS3/PS3Extras/C4JSpursJob.h" #endif +#include class MultiPlayerLevel; class Textures; class Chunk; @@ -163,8 +164,13 @@ public: void destroyTileProgress(int id, int x, int y, int z, int progress); void registerTextures(IconRegister* iconRegister); - typedef std::unordered_map >, - IntKeyHash, IntKeyEq> + struct RenderableTileEntityBucket { + std::vector > tiles; + std::unordered_map indexByTile; + }; + + typedef std::unordered_map rteMap; private: @@ -176,6 +182,10 @@ private: rteMap renderableTileEntities; // 4J - changed - was // std::vector, // now hashed by chunk so we can find them + typedef std::unordered_set rtePendingRemovalSet; + typedef std::unordered_map + rtePendingRemovalMap; + rtePendingRemovalMap m_renderableTileEntitiesPendingRemoval; CRITICAL_SECTION m_csRenderableTileEntities; MultiPlayerLevel* level[4]; // 4J - now one per player Textures* textures; @@ -214,6 +224,14 @@ private: std::unordered_map destroyingBlocks; Icon** breakingTextures; + void addRenderableTileEntity_Locked( + int key, const std::shared_ptr& tileEntity); + void eraseRenderableTileEntity_Locked( + RenderableTileEntityBucket& bucket, TileEntity* tileEntity); + void queueRenderableTileEntityForRemoval_Locked(int key, + TileEntity* tileEntity); + void retireRenderableTileEntitiesForChunkKey(int key); + public: void fullyFlagRenderableTileEntitiesToBeRemoved(); // 4J added diff --git a/Minecraft.Client/Utils/FrameProfiler.cpp b/Minecraft.Client/Utils/FrameProfiler.cpp index 89db484eb..53ddaa42f 100644 --- a/Minecraft.Client/Utils/FrameProfiler.cpp +++ b/Minecraft.Client/Utils/FrameProfiler.cpp @@ -44,6 +44,8 @@ constexpr std::array {Bucket::ChunkBlockFaceCull, "chunkBlockFaceCull"}, {Bucket::ChunkBlockLighting, "chunkBlockLighting"}, {Bucket::ChunkBlockEmit, "chunkBlockEmit"}, + {Bucket::RenderableTileEntityCleanup, "renderableTileEntityCleanup"}, + {Bucket::TileEntityUnloadCleanup, "tileEntityUnloadCleanup"}, {Bucket::Entity, "entities"}, {Bucket::Particle, "particles"}, {Bucket::WeatherSky, "weather"}, @@ -133,8 +135,7 @@ inline void recordWorkerBucket(Bucket bucket, std::uint64_t elapsedNs) noexcept return t_threadState.frameScopeDepth != 0; } -FRAME_PROFILER_NOINLINE -[[nodiscard]] bool computeEnabled() noexcept { +FRAME_PROFILER_NOINLINE bool computeEnabled() noexcept { const char* const envValue = std::getenv("C4J_FRAME_PROFILER"); if (envValue == nullptr) return true; return !envSaysDisabled(envValue); diff --git a/Minecraft.Client/Utils/FrameProfiler.h b/Minecraft.Client/Utils/FrameProfiler.h index 7abf56255..c22351f69 100644 --- a/Minecraft.Client/Utils/FrameProfiler.h +++ b/Minecraft.Client/Utils/FrameProfiler.h @@ -23,6 +23,8 @@ public: ChunkBlockFaceCull, ChunkBlockLighting, ChunkBlockEmit, + RenderableTileEntityCleanup, + TileEntityUnloadCleanup, Entity, Particle, WeatherSky, diff --git a/Minecraft.World/Blocks/TileEntities/TileEntity.cpp b/Minecraft.World/Blocks/TileEntities/TileEntity.cpp index 24986046f..66ae1afac 100644 --- a/Minecraft.World/Blocks/TileEntities/TileEntity.cpp +++ b/Minecraft.World/Blocks/TileEntities/TileEntity.cpp @@ -187,6 +187,15 @@ void TileEntity::upgradeRenderRemoveStage() { } } +bool TileEntity::finalizeRenderRemoveStage() { + if (renderRemoveStage == e_RenderRemoveStageFlaggedAtChunk) { + renderRemoveStage = e_RenderRemoveStageRemove; + return true; + } + + return renderRemoveStage == e_RenderRemoveStageRemove; +} + // 4J Added void TileEntity::clone(std::shared_ptr tileEntity) { tileEntity->level = this->level; @@ -195,4 +204,4 @@ void TileEntity::clone(std::shared_ptr tileEntity) { tileEntity->z = this->z; tileEntity->data = this->data; tileEntity->tile = this->tile; -} \ No newline at end of file +} diff --git a/Minecraft.World/Blocks/TileEntities/TileEntity.h b/Minecraft.World/Blocks/TileEntities/TileEntity.h index e640ae2e4..fc382b673 100644 --- a/Minecraft.World/Blocks/TileEntities/TileEntity.h +++ b/Minecraft.World/Blocks/TileEntities/TileEntity.h @@ -49,6 +49,7 @@ public: void setRenderRemoveStage(unsigned char stage); // 4J added void upgradeRenderRemoveStage(); // 4J added + bool finalizeRenderRemoveStage(); // 4J added bool shouldRemoveForRender(); // 4J added virtual Level* getLevel(); @@ -77,4 +78,4 @@ public: protected: void clone(std::shared_ptr tileEntity); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Level/Level.cpp b/Minecraft.World/Level/Level.cpp index ab251a833..06c99a2ae 100644 --- a/Minecraft.World/Level/Level.cpp +++ b/Minecraft.World/Level/Level.cpp @@ -38,6 +38,7 @@ #include "../../Minecraft.Client/Platform/Common/DLC/DLCPack.h" #include "../../Minecraft.Client/Platform/PS3/PS3Extras/ShutdownManager.h" #include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/Utils/FrameProfiler.h" #include #include #include @@ -2206,19 +2207,11 @@ void Level::tickEntities() { // 4J-PB - Stuart - check this is correct here if (!tileEntitiesToUnload.empty()) { - // tileEntityList.removeAll(tileEntitiesToUnload); + FRAME_PROFILE_SCOPE(TileEntityUnloadCleanup); for (AUTO_VAR(it, tileEntityList.begin()); it != tileEntityList.end();) { - bool found = false; - for (AUTO_VAR(it2, tileEntitiesToUnload.begin()); - it2 != tileEntitiesToUnload.end(); it2++) { - if ((*it) == (*it2)) { - found = true; - break; - } - } - if (found) { + if (tileEntitiesToUnload.find(*it) != tileEntitiesToUnload.end()) { if (isClientSide) { __debugbreak(); } @@ -2731,7 +2724,9 @@ void Level::removeTileEntity(int x, int y, int z) { } void Level::markForRemoval(std::shared_ptr entity) { - tileEntitiesToUnload.push_back(entity); + EnterCriticalSection(&m_tileEntityListCS); + tileEntitiesToUnload.insert(entity); + LeaveCriticalSection(&m_tileEntityListCS); } bool Level::isSolidRenderTile(int x, int y, int z) { diff --git a/Minecraft.World/Level/Level.h b/Minecraft.World/Level/Level.h index d28d1cda5..50910ada0 100644 --- a/Minecraft.World/Level/Level.h +++ b/Minecraft.World/Level/Level.h @@ -10,6 +10,7 @@ #include "../WorldGen/Biomes/Biome.h" #include "../Util/C4JThread.h" #include +#include #ifdef __PSVITA__ #include "../../Minecraft.Client/Platform/PSVita/PSVitaExtras/CustomSet.h" @@ -118,7 +119,7 @@ public: private: std::vector > pendingTileEntities; - std::vector > tileEntitiesToUnload; + std::unordered_set > tileEntitiesToUnload; bool updatingTileEntities; public: @@ -665,3 +666,4 @@ public: bool canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType); }; +#include diff --git a/Minecraft.World/Level/LevelChunk.cpp b/Minecraft.World/Level/LevelChunk.cpp index 5f95fd9d2..52193665f 100644 --- a/Minecraft.World/Level/LevelChunk.cpp +++ b/Minecraft.World/Level/LevelChunk.cpp @@ -1558,13 +1558,19 @@ void LevelChunk::unload(bool unloadTileEntities) // 4J - added parameter { loaded = false; if (unloadTileEntities) { + std::vector > tileEntitiesToRemove; EnterCriticalSection(&m_csTileEntities); for (AUTO_VAR(it, tileEntities.begin()); it != tileEntities.end(); it++) { - // 4J-PB -m 1.7.3 was it->second->setRemoved(); - level->markForRemoval(it->second); + tileEntitiesToRemove.push_back(it->second); } LeaveCriticalSection(&m_csTileEntities); + + AUTO_VAR(itEnd, tileEntitiesToRemove.end()); + for (AUTO_VAR(it, tileEntitiesToRemove.begin()); it != itEnd; it++) { + // 4J-PB -m 1.7.3 was it->second->setRemoved(); + level->markForRemoval(*it); + } } #ifdef _ENTITIES_RW_SECTION From 3986e4642ba7b977ed1883b28da110368b412125 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Mon, 30 Mar 2026 00:36:50 +0200 Subject: [PATCH 67/71] ghost segfault??????? --- Minecraft.Client/Rendering/LevelRenderer.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 763611cca..596a06055 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -646,13 +646,11 @@ void LevelRenderer::renderEntities(Vec3* cam, Culler* culler, float a) { if (!isGlobalIndexInSameDimension(idx, level[playerIndex])) continue; for (AUTO_VAR(it2, it->second.tiles.begin()); - it2 != it->second.tiles.end(); - it2++) { + it2 != it->second.tiles.end(); it2++) { TileEntityRenderDispatcher::instance->render(*it2, a); } } - LeaveCriticalSection(&m_csRenderableTileEntities); mc->gameRenderer->turnOffLightLayer(a); // 4J - brought forward from 1.8.2 @@ -874,9 +872,9 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { // 4jcraft: replaced glPushMatrix/glTranslatef/glPopMatrix per chunk // no more full MVP upload per chunk, can also be bkwards compat - RenderManager.SetChunkOffset((float)pClipChunk->chunk->x, - (float)pClipChunk->chunk->y, - (float)pClipChunk->chunk->z); + RenderManager.SetChunkOffset((float)chunk->chunk->x, + (float)chunk->chunk->y, + (float)chunk->chunk->z); if (RenderManager.CBuffCall(list, first)) { first = false; @@ -4229,7 +4227,8 @@ void LevelRenderer::fullyFlagRenderableTileEntitiesToBeRemoved() { RenderableTileEntityBucket& bucket = itChunk->second; for (AUTO_VAR(itPending, itKey->second.begin()); itPending != itKey->second.end(); itPending++) { - if (bucket.indexByTile.find(*itPending) == bucket.indexByTile.end()) { + if (bucket.indexByTile.find(*itPending) == + bucket.indexByTile.end()) { continue; } From 19e7386d125fd945be32d2239fcc9fd2cef76b9f Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Mon, 30 Mar 2026 01:25:35 +0200 Subject: [PATCH 68/71] light fix --- Minecraft.Client/Rendering/LevelRenderer.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 596a06055..c15b3027a 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -767,10 +767,10 @@ int LevelRenderer::render(std::shared_ptr player, int layer, } Lighting::turnOff(); glColor4f(1, 1, 1, 1); - glColor4f(1, 1, 1, 1); + mc->gameRenderer->turnOnLightLayer(alpha); int count = renderChunks(0, (int)chunks[playerIndex].length, layer, alpha); - + mc->gameRenderer->turnOffLightLayer(alpha); return count; } @@ -791,13 +791,14 @@ int compare(const void* a, const void* b) { int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { int playerIndex = mc->player->GetXboxPad(); if (chunks[playerIndex].data == NULL) return 0; - + mc->gameRenderer->turnOnLightLayer(alpha); std::shared_ptr player = mc->cameraTargetPlayer; double xOff = player->xOld + (player->x - player->xOld) * alpha; double yOff = player->yOld + (player->y - player->yOld) * alpha; double zOff = player->zOld + (player->z - player->zOld) * alpha; glPushMatrix(); + glTranslatef((float)-xOff, (float)-yOff, (float)-zOff); #ifdef __PS3__ From f25b105beed41734b060d72de74b9742e07f5c75 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Mon, 30 Mar 2026 01:46:49 +0200 Subject: [PATCH 69/71] slight optimizations --- Minecraft.Client/Rendering/LevelRenderer.cpp | 31 ++++++++++++++------ Minecraft.Client/Rendering/LevelRenderer.h | 6 ++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index c15b3027a..160b945c1 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -2948,15 +2948,20 @@ void LevelRenderer::cull(Culler* culler, float a) { int sizeX = maxCx - minCx + 1; int sizeY = maxCy - minCy + 1; int sizeZ = maxCz - minCz + 1; + int gridSize = sizeX * sizeY * sizeZ; - std::vector grid(sizeX * sizeY * sizeZ, nullptr); + if (m_bfsGrid.size() < gridSize) { + m_bfsGrid.resize(gridSize); + } + + memset(m_bfsGrid.data(), 0, gridSize * sizeof(ClipChunk*)); for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { ClipChunk* cc = &chunks[playerIndex][i]; if (cc->globalIdx < 0) continue; int lx = intFloorDiv(cc->chunk->x, CHUNK_XZSIZE) - minCx; int ly = intFloorDiv(cc->chunk->y, CHUNK_SIZE) - minCy; int lz = intFloorDiv(cc->chunk->z, CHUNK_XZSIZE) - minCz; - grid[(lx * sizeY + ly) * sizeZ + lz] = cc; + m_bfsGrid[(lx * sizeY + ly) * sizeZ + lz] = cc; } auto getChunkAt = [&](int cx, int cy, int cz) -> ClipChunk* { @@ -2965,7 +2970,7 @@ void LevelRenderer::cull(Culler* culler, float a) { int lz = cz - minCz; if (lx >= 0 && lx < sizeX && ly >= 0 && ly < sizeY && lz >= 0 && lz < sizeZ) { - return grid[(lx * sizeY + ly) * sizeZ + lz]; + return m_bfsGrid[(lx * sizeY + ly) * sizeZ + lz]; } return nullptr; }; @@ -3014,14 +3019,20 @@ void LevelRenderer::cull(Culler* culler, float a) { int incomingFace; }; - std::vector q; - q.reserve(chunks[playerIndex].length * 2); + static thread_local std::vector q; + q.clear(); + q.reserve(chunks[playerIndex].length); int qHead = 0; - std::vector visitedFaces(chunks[playerIndex].length, 0); + int visitedSize = chunks[playerIndex].length; + if (m_bfsVisitedFaces[playerIndex].size() < visitedSize) { + m_bfsVisitedFaces[playerIndex].resize(visitedSize, 0); + } + memset(m_bfsVisitedFaces[playerIndex].data(), 0, visitedSize); q.push_back({startChunk, -1}); - visitedFaces[startChunk - chunks[playerIndex].data] = 0x3F; + m_bfsVisitedFaces[playerIndex][startChunk - chunks[playerIndex].data] = + 0x3F; static const int OFFSETS[6][3] = { {0, -1, 0}, // 0: -Y @@ -3096,7 +3107,9 @@ void LevelRenderer::cull(Culler* culler, float a) { int nIdx = neighbor - chunks[playerIndex].data; int nextIncFace = outFace ^ 1; - if ((visitedFaces[nIdx] & (1 << nextIncFace)) != 0) continue; + if ((m_bfsVisitedFaces[playerIndex][nIdx] & (1 << nextIncFace)) != + 0) + continue; float cellBounds[6] = { (float)neighbor->chunk->x - 0.1f, @@ -3108,7 +3121,7 @@ void LevelRenderer::cull(Culler* culler, float a) { if (!clip(cellBounds, fdraw)) continue; - visitedFaces[nIdx] |= (1 << nextIncFace); + m_bfsVisitedFaces[playerIndex][nIdx] |= (1 << nextIncFace); q.push_back({neighbor, nextIncFace}); } } diff --git a/Minecraft.Client/Rendering/LevelRenderer.h b/Minecraft.Client/Rendering/LevelRenderer.h index fbafa7ce8..ec925ebbe 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.h +++ b/Minecraft.Client/Rendering/LevelRenderer.h @@ -221,13 +221,15 @@ private: OffsettedRenderList renderLists[RENDERLISTS_LENGTH]; void setGlobalChunkConnectivity(int index, uint64_t conn); uint64_t getGlobalChunkConnectivity(int index); + std::vector m_bfsGrid; + std::vector m_bfsVisitedFaces[4]; std::unordered_map destroyingBlocks; Icon** breakingTextures; void addRenderableTileEntity_Locked( int key, const std::shared_ptr& tileEntity); - void eraseRenderableTileEntity_Locked( - RenderableTileEntityBucket& bucket, TileEntity* tileEntity); + void eraseRenderableTileEntity_Locked(RenderableTileEntityBucket& bucket, + TileEntity* tileEntity); void queueRenderableTileEntityForRemoval_Locked(int key, TileEntity* tileEntity); void retireRenderableTileEntitiesForChunkKey(int key); From 4b8ebbb8240d65e24a12063ce59bb29a3d0ccd7d Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 21:02:24 -0500 Subject: [PATCH 70/71] what if i --- flake.nix | 60 ------------------------------------------------------- 1 file changed, 60 deletions(-) diff --git a/flake.nix b/flake.nix index 05ceb4fef..eed2e5ce2 100644 --- a/flake.nix +++ b/flake.nix @@ -4,36 +4,6 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; - - shiggy = { - url = "github:4jcraft/shiggy/main"; - flake = false; - }; - - "4jlibs" = { - url = "github:4jcraft/4jlibs/main"; - flake = false; - }; - - miniaudio = { - url = "https://github.com/mackron/miniaudio/archive/refs/tags/0.11.22.tar.gz"; - flake = false; - }; - - miniaudio-patch = { - url = "https://wrapdb.mesonbuild.com/v2/miniaudio_0.11.22-2/get_patch"; - flake = false; - }; - - stb = { - url = "github:nothings/stb/master"; - flake = false; - }; - - simdutf = { - url = "github:simdutf/simdutf"; - flake = false; - }; }; outputs = @@ -57,36 +27,6 @@ dontFixup = true; dontUseCmakeConfigure = true; - # 4jcraft - Meson expects this subprojects structure - postUnpack = '' - mkdir -p $sourceRoot/subprojects - - cp -r ${inputs.shiggy} $sourceRoot/subprojects/shiggy - cp -r ${inputs."4jlibs"} $sourceRoot/subprojects/4jlibs - cp -r ${inputs.stb} $sourceRoot/subprojects/stb - cp -r ${inputs.simdutf} $sourceRoot/subprojects/simdutf - cp -r ${inputs.miniaudio} $sourceRoot/subprojects/miniaudio - - chmod -R u+w $sourceRoot/subprojects - ''; - - # 4jcraft - `stb` and `simdutf` patches - postPatch = '' - cp subprojects/packagefiles/stb/meson.build subprojects/stb/meson.build - cp subprojects/packagefiles/simdutf/meson.build subprojects/simdutf/meson.build - cp subprojects/packagefiles/simdutf/meson.options subprojects/simdutf/meson.options - - unzip ${inputs.miniaudio-patch} -d miniaudio-patch-tmp - cp -r miniaudio-patch-tmp/*/. subprojects/miniaudio/ - - cat > subprojects/miniaudio.wrap < Date: Sun, 29 Mar 2026 21:06:57 -0500 Subject: [PATCH 71/71] THAT IS SO ANNOYING WHY NIX --- flake.nix | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/flake.nix b/flake.nix index eed2e5ce2..05ceb4fef 100644 --- a/flake.nix +++ b/flake.nix @@ -4,6 +4,36 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; + + shiggy = { + url = "github:4jcraft/shiggy/main"; + flake = false; + }; + + "4jlibs" = { + url = "github:4jcraft/4jlibs/main"; + flake = false; + }; + + miniaudio = { + url = "https://github.com/mackron/miniaudio/archive/refs/tags/0.11.22.tar.gz"; + flake = false; + }; + + miniaudio-patch = { + url = "https://wrapdb.mesonbuild.com/v2/miniaudio_0.11.22-2/get_patch"; + flake = false; + }; + + stb = { + url = "github:nothings/stb/master"; + flake = false; + }; + + simdutf = { + url = "github:simdutf/simdutf"; + flake = false; + }; }; outputs = @@ -27,6 +57,36 @@ dontFixup = true; dontUseCmakeConfigure = true; + # 4jcraft - Meson expects this subprojects structure + postUnpack = '' + mkdir -p $sourceRoot/subprojects + + cp -r ${inputs.shiggy} $sourceRoot/subprojects/shiggy + cp -r ${inputs."4jlibs"} $sourceRoot/subprojects/4jlibs + cp -r ${inputs.stb} $sourceRoot/subprojects/stb + cp -r ${inputs.simdutf} $sourceRoot/subprojects/simdutf + cp -r ${inputs.miniaudio} $sourceRoot/subprojects/miniaudio + + chmod -R u+w $sourceRoot/subprojects + ''; + + # 4jcraft - `stb` and `simdutf` patches + postPatch = '' + cp subprojects/packagefiles/stb/meson.build subprojects/stb/meson.build + cp subprojects/packagefiles/simdutf/meson.build subprojects/simdutf/meson.build + cp subprojects/packagefiles/simdutf/meson.options subprojects/simdutf/meson.options + + unzip ${inputs.miniaudio-patch} -d miniaudio-patch-tmp + cp -r miniaudio-patch-tmp/*/. subprojects/miniaudio/ + + cat > subprojects/miniaudio.wrap <