Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0ae4c76ba | ||
|
|
3caf18409b | ||
|
|
59261868e1 | ||
|
|
a7612878e5 | ||
|
|
bb3f8306ef | ||
|
|
7cb4d18308 | ||
|
|
b6d619254d | ||
|
|
702b4e29aa | ||
|
|
1d12e4e099 |
@@ -30,3 +30,4 @@ compile_commands.json
|
|||||||
# local data dumps / RE artifacts
|
# local data dumps / RE artifacts
|
||||||
re-data/
|
re-data/
|
||||||
*.log
|
*.log
|
||||||
|
*.log.[0-9]*
|
||||||
|
|||||||
+276
-46
@@ -4,61 +4,291 @@ stages:
|
|||||||
|
|
||||||
variables:
|
variables:
|
||||||
DOCKER_HOST: tcp://docker:2375
|
DOCKER_HOST: tcp://docker:2375
|
||||||
DOCKER_DRIVER: overlay2
|
|
||||||
DOCKER_TLS_CERTDIR: ""
|
DOCKER_TLS_CERTDIR: ""
|
||||||
# Where to push the dev image. Leave empty to not push at all.
|
DOCKER_BUILDKIT: "1"
|
||||||
# Examples: "192.168.1.11:9090/<group>/openra3" or "docker.io/<user>/openra3"
|
# apt mirror used inside both images (override per pipeline if needed)
|
||||||
IMAGE_NAME: ""
|
|
||||||
APT_MIRROR: "http://mirrors.tuna.tsinghua.edu.cn/ubuntu"
|
APT_MIRROR: "http://mirrors.tuna.tsinghua.edu.cn/ubuntu"
|
||||||
|
# Images are shared through Harbor (plain HTTP), never as artifacts.
|
||||||
|
HARBOR_HOST: "192.168.1.11:9090"
|
||||||
|
HARBOR_PROJECT: "openra3"
|
||||||
|
IMAGE: "$HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME"
|
||||||
|
|
||||||
image_build:
|
# dind: pull docker.io through the intranet Harbor proxy (plain HTTP ->
|
||||||
|
# --insecure-registry) and enable the containerd snapshotter so the *docker*
|
||||||
|
# driver can export the registry BuildKit cache (the classic overlay2 driver
|
||||||
|
# rejects it). Anchor is an array, so `services: *dind` stays an array.
|
||||||
|
.dind: &dind
|
||||||
|
- name: docker:dind
|
||||||
|
command:
|
||||||
|
- --registry-mirror=http://192.168.1.11:9090/dockerhub
|
||||||
|
- --insecure-registry=192.168.1.11:9090
|
||||||
|
- --feature=containerd-snapshotter=true
|
||||||
|
|
||||||
|
# Retry helper for transient network failures (login / build / push / pull).
|
||||||
|
.retry: &retry
|
||||||
|
- |
|
||||||
|
retry() {
|
||||||
|
n=0
|
||||||
|
until "$@"; do
|
||||||
|
n=$((n + 1))
|
||||||
|
[ "$n" -ge 5 ] && { echo "failed after $n tries: $*" >&2; return 1; }
|
||||||
|
echo "attempt $n failed, retrying: $*" >&2
|
||||||
|
sleep 15
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- build the two isolated toolchain images and push them to Harbor ----------
|
||||||
|
linux_image:
|
||||||
stage: image
|
stage: image
|
||||||
image: docker:latest
|
image: docker:latest
|
||||||
services:
|
services: *dind
|
||||||
- name: docker:dind
|
|
||||||
# Pull docker.io images through the intranet Harbor pull-through cache;
|
|
||||||
# plain HTTP registry, hence --insecure-registry.
|
|
||||||
command:
|
|
||||||
- --registry-mirror=http://192.168.1.11:9090/dockerhub
|
|
||||||
- --insecure-registry=192.168.1.11:9090
|
|
||||||
script:
|
|
||||||
- docker build --target dev -t openra3-dev:local --build-arg APT_MIRROR=$APT_MIRROR .
|
|
||||||
- docker save openra3-dev:local -o dev-image.tar
|
|
||||||
- |
|
|
||||||
if [ -n "$IMAGE_NAME" ]; then
|
|
||||||
# add `docker login` here if IMAGE_NAME points to a private registry
|
|
||||||
docker tag openra3-dev:local $IMAGE_NAME:$CI_COMMIT_SHORT_SHA
|
|
||||||
docker push $IMAGE_NAME:$CI_COMMIT_SHORT_SHA
|
|
||||||
fi
|
|
||||||
artifacts:
|
|
||||||
name: "dev-image-$CI_COMMIT_SHORT_SHA"
|
|
||||||
paths:
|
|
||||||
- dev-image.tar
|
|
||||||
expire_in: 1 day
|
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- |
|
||||||
|
retry docker build \
|
||||||
|
--cache-from type=registry,ref=$IMAGE:cache-linux,insecure=true \
|
||||||
|
--cache-to type=registry,ref=$IMAGE:cache-linux,mode=max,insecure=true \
|
||||||
|
--build-arg APT_MIRROR=$APT_MIRROR \
|
||||||
|
--target dev \
|
||||||
|
-t $IMAGE:$CI_COMMIT_SHORT_SHA-linux \
|
||||||
|
-f Dockerfile .
|
||||||
|
- retry docker push $IMAGE:$CI_COMMIT_SHORT_SHA-linux
|
||||||
|
|
||||||
build_test_package:
|
windows_image:
|
||||||
|
stage: image
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- |
|
||||||
|
retry docker build \
|
||||||
|
--cache-from type=registry,ref=$IMAGE:cache-windows,insecure=true \
|
||||||
|
--cache-to type=registry,ref=$IMAGE:cache-windows,mode=max,insecure=true \
|
||||||
|
--build-arg APT_MIRROR=$APT_MIRROR \
|
||||||
|
--target dev \
|
||||||
|
-t $IMAGE:$CI_COMMIT_SHORT_SHA-windows \
|
||||||
|
-f Dockerfile.win .
|
||||||
|
- retry docker push $IMAGE:$CI_COMMIT_SHORT_SHA-windows
|
||||||
|
|
||||||
|
# --- build and test each target ----------------------------------------------
|
||||||
|
build_linux:
|
||||||
stage: build
|
stage: build
|
||||||
image: docker:latest
|
image: docker:latest
|
||||||
services:
|
services: *dind
|
||||||
- name: docker:dind
|
|
||||||
command:
|
|
||||||
- --registry-mirror=http://192.168.1.11:9090/dockerhub
|
|
||||||
- --insecure-registry=192.168.1.11:9090
|
|
||||||
needs:
|
|
||||||
- image_build
|
|
||||||
before_script:
|
|
||||||
- docker load -i dev-image.tar
|
|
||||||
script:
|
|
||||||
- docker run --rm -v "$(pwd):/work" -w /work openra3-dev:local cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
|
||||||
- docker run --rm -v "$(pwd):/work" -w /work openra3-dev:local cmake --build build -j
|
|
||||||
- docker run --rm -v "$(pwd):/work" -w /work openra3-dev:local ctest --test-dir build --output-on-failure
|
|
||||||
- docker run --rm -v "$(pwd):/work" -w /work openra3-dev:local bash -c "mkdir -p artifacts_bin && cp build/bin/openra3 artifacts_bin/"
|
|
||||||
artifacts:
|
|
||||||
name: "$CI_PROJECT_NAME-executables-$CI_COMMIT_SHORT_SHA"
|
|
||||||
paths:
|
|
||||||
- artifacts_bin/
|
|
||||||
expire_in: 7 days
|
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
needs:
|
||||||
|
- linux_image
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- retry docker pull $IMAGE:$CI_COMMIT_SHORT_SHA-linux
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-linux cmake -S . -B build/linux -G Ninja -DCMAKE_BUILD_TYPE=Release -DOPENRA3_DISTRO_TAG=ubuntu26.04
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-linux cmake --build build/linux -j
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-linux ctest --test-dir build/linux --output-on-failure
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-linux bash -c "cd build/linux && cpack -G DEB"
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-linux bash -c "mkdir -p artifacts/linux && cp build/linux/bin/openra3 build/linux/*.deb artifacts/linux/"
|
||||||
|
artifacts:
|
||||||
|
name: "$CI_PROJECT_NAME-linux-$CI_COMMIT_SHORT_SHA"
|
||||||
|
paths:
|
||||||
|
- artifacts/linux/
|
||||||
|
expire_in: 7 days
|
||||||
|
|
||||||
|
build_windows:
|
||||||
|
stage: build
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
needs:
|
||||||
|
- windows_image
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- retry docker pull $IMAGE:$CI_COMMIT_SHORT_SHA-windows
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-windows
|
||||||
|
cmake -S . -B build/windows -G Ninja
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/llvm-mingw-x86_64.cmake
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-windows cmake --build build/windows -j
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-windows cmake --build build/windows --target openra3_msi
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-windows bash -c "cd build/windows && cpack -G ZIP"
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-windows bash -c "mkdir -p artifacts/windows && cp build/windows/bin/openra3.exe build/windows/bin/SDL3.dll build/windows/bin/*.msi build/windows/*.zip artifacts/windows/"
|
||||||
|
artifacts:
|
||||||
|
name: "$CI_PROJECT_NAME-windows-$CI_COMMIT_SHORT_SHA"
|
||||||
|
paths:
|
||||||
|
- artifacts/windows/
|
||||||
|
expire_in: 7 days
|
||||||
|
|
||||||
|
# --- arm64 + Debian toolchain images -----------------------------------------
|
||||||
|
linux_arm64_image:
|
||||||
|
stage: image
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- |
|
||||||
|
retry docker build \
|
||||||
|
--cache-from type=registry,ref=$IMAGE:cache-linux-arm64,insecure=true \
|
||||||
|
--cache-to type=registry,ref=$IMAGE:cache-linux-arm64,mode=max,insecure=true \
|
||||||
|
--build-arg APT_MIRROR=$APT_MIRROR \
|
||||||
|
--target dev \
|
||||||
|
-t $IMAGE:$CI_COMMIT_SHORT_SHA-linux-arm64 \
|
||||||
|
-f Dockerfile.linux-arm64 .
|
||||||
|
- retry docker push $IMAGE:$CI_COMMIT_SHORT_SHA-linux-arm64
|
||||||
|
|
||||||
|
windows_arm64_image:
|
||||||
|
stage: image
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- |
|
||||||
|
retry docker build \
|
||||||
|
--cache-from type=registry,ref=$IMAGE:cache-windows-arm64,insecure=true \
|
||||||
|
--cache-to type=registry,ref=$IMAGE:cache-windows-arm64,mode=max,insecure=true \
|
||||||
|
--build-arg APT_MIRROR=$APT_MIRROR \
|
||||||
|
--target dev \
|
||||||
|
-t $IMAGE:$CI_COMMIT_SHORT_SHA-windows-arm64 \
|
||||||
|
-f Dockerfile.win-arm64 .
|
||||||
|
- retry docker push $IMAGE:$CI_COMMIT_SHORT_SHA-windows-arm64
|
||||||
|
|
||||||
|
debian_image:
|
||||||
|
stage: image
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- |
|
||||||
|
retry docker build \
|
||||||
|
--cache-from type=registry,ref=$IMAGE:cache-debian,insecure=true \
|
||||||
|
--cache-to type=registry,ref=$IMAGE:cache-debian,mode=max,insecure=true \
|
||||||
|
--build-arg APT_MIRROR=$APT_MIRROR \
|
||||||
|
--target dev \
|
||||||
|
-t $IMAGE:$CI_COMMIT_SHORT_SHA-debian \
|
||||||
|
-f Dockerfile.debian .
|
||||||
|
- retry docker push $IMAGE:$CI_COMMIT_SHORT_SHA-debian
|
||||||
|
|
||||||
|
debian_arm64_image:
|
||||||
|
stage: image
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- |
|
||||||
|
retry docker build \
|
||||||
|
--cache-from type=registry,ref=$IMAGE:cache-debian-arm64,insecure=true \
|
||||||
|
--cache-to type=registry,ref=$IMAGE:cache-debian-arm64,mode=max,insecure=true \
|
||||||
|
--build-arg APT_MIRROR=$APT_MIRROR \
|
||||||
|
--target dev \
|
||||||
|
-t $IMAGE:$CI_COMMIT_SHORT_SHA-debian-arm64 \
|
||||||
|
-f Dockerfile.debian-arm64 .
|
||||||
|
- retry docker push $IMAGE:$CI_COMMIT_SHORT_SHA-debian-arm64
|
||||||
|
|
||||||
|
# --- build each target (binary + packages) -----------------------------------
|
||||||
|
build_linux_arm64:
|
||||||
|
stage: build
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
needs:
|
||||||
|
- linux_arm64_image
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- retry docker pull $IMAGE:$CI_COMMIT_SHORT_SHA-linux-arm64
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-linux-arm64 cmake -S . -B build/linux-arm64 -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-aarch64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -DOPENRA3_DISTRO_TAG=ubuntu26.04
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-linux-arm64 cmake --build build/linux-arm64 -j
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-linux-arm64 bash -c "cd build/linux-arm64 && cpack -G DEB"
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-linux-arm64 bash -c "mkdir -p artifacts/linux-arm64 && cp build/linux-arm64/bin/openra3 build/linux-arm64/*.deb artifacts/linux-arm64/"
|
||||||
|
artifacts:
|
||||||
|
name: "$CI_PROJECT_NAME-linux-arm64-$CI_COMMIT_SHORT_SHA"
|
||||||
|
paths:
|
||||||
|
- artifacts/linux-arm64/
|
||||||
|
expire_in: 7 days
|
||||||
|
|
||||||
|
build_windows_arm64:
|
||||||
|
stage: build
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
needs:
|
||||||
|
- windows_arm64_image
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- retry docker pull $IMAGE:$CI_COMMIT_SHORT_SHA-windows-arm64
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-windows-arm64 cmake -S . -B build/windows-arm64 -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/llvm-mingw-aarch64.cmake -DCMAKE_BUILD_TYPE=Release
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-windows-arm64 cmake --build build/windows-arm64 -j
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-windows-arm64 bash -c "cd build/windows-arm64 && cpack -G ZIP"
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-windows-arm64 bash -c "mkdir -p artifacts/windows-arm64 && cp build/windows-arm64/bin/openra3.exe build/windows-arm64/bin/SDL3.dll build/windows-arm64/*.zip artifacts/windows-arm64/"
|
||||||
|
artifacts:
|
||||||
|
name: "$CI_PROJECT_NAME-windows-arm64-$CI_COMMIT_SHORT_SHA"
|
||||||
|
paths:
|
||||||
|
- artifacts/windows-arm64/
|
||||||
|
expire_in: 7 days
|
||||||
|
|
||||||
|
build_debian:
|
||||||
|
stage: build
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
needs:
|
||||||
|
- debian_image
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- retry docker pull $IMAGE:$CI_COMMIT_SHORT_SHA-debian
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-debian cmake -S . -B build/debian -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STDLIB_MODULES_JSON=/usr/lib/llvm-19/lib/libc++.modules.json -DOPENRA3_DISTRO_TAG=debian13
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-debian cmake --build build/debian -j
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-debian ctest --test-dir build/debian --output-on-failure
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-debian bash -c "cd build/debian && cpack -G DEB"
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-debian bash -c "mkdir -p artifacts/debian && cp build/debian/bin/openra3 build/debian/*.deb artifacts/debian/"
|
||||||
|
artifacts:
|
||||||
|
name: "$CI_PROJECT_NAME-debian-$CI_COMMIT_SHORT_SHA"
|
||||||
|
paths:
|
||||||
|
- artifacts/debian/
|
||||||
|
expire_in: 7 days
|
||||||
|
|
||||||
|
build_debian_arm64:
|
||||||
|
stage: build
|
||||||
|
image: docker:latest
|
||||||
|
services: *dind
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
needs:
|
||||||
|
- debian_arm64_image
|
||||||
|
before_script: *retry
|
||||||
|
script:
|
||||||
|
- retry sh -c 'echo "$HARBOR_PASSWORD" | docker login "$HARBOR_HOST" -u "$HARBOR_USERNAME" --password-stdin'
|
||||||
|
- retry docker pull $IMAGE:$CI_COMMIT_SHORT_SHA-debian-arm64
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-debian-arm64 cmake -S . -B build/debian-arm64 -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-aarch64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -DOPENRA3_AARCH64_CC=clang-19 -DOPENRA3_AARCH64_CXX=clang++-19 -DOPENRA3_AARCH64_STDLIB_MODULES_JSON=/usr/lib/llvm-19/lib/libc++.modules.json -DOPENRA3_DISTRO_TAG=debian13
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-debian-arm64 cmake --build build/debian-arm64 -j
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-debian-arm64 bash -c "cd build/debian-arm64 && cpack -G DEB"
|
||||||
|
- docker run --rm -v "$(pwd):/work" -w /work $IMAGE:$CI_COMMIT_SHORT_SHA-debian-arm64 bash -c "mkdir -p artifacts/debian-arm64 && cp build/debian-arm64/bin/openra3 build/debian-arm64/*.deb artifacts/debian-arm64/"
|
||||||
|
artifacts:
|
||||||
|
name: "$CI_PROJECT_NAME-debian-arm64-$CI_COMMIT_SHORT_SHA"
|
||||||
|
paths:
|
||||||
|
- artifacts/debian-arm64/
|
||||||
|
expire_in: 7 days
|
||||||
+341
-70
@@ -1,10 +1,31 @@
|
|||||||
cmake_minimum_required(VERSION 3.28)
|
cmake_minimum_required(VERSION 3.30)
|
||||||
|
|
||||||
project(OpenRA3 VERSION 0.0.1 LANGUAGES CXX)
|
# --- C++26 modules + `import std;` (clang + libc++) -------------------------
|
||||||
|
# CMake gates `import std` behind an experimental value documented in
|
||||||
|
# Help/dev/experimental.rst for the CMake version in use.
|
||||||
|
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
|
||||||
|
|
||||||
|
# Path to the standard-library module description. A cross toolchain overrides
|
||||||
|
# this before the compiler is probed; this default targets the Linux LLVM
|
||||||
|
# package.
|
||||||
|
if(NOT CMAKE_CXX_STDLIB_MODULES_JSON)
|
||||||
|
set(CMAKE_CXX_STDLIB_MODULES_JSON "/usr/lib/llvm-21/lib/libc++.modules.json")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# libc++ must be selected before the compiler is probed: CMake builds its
|
||||||
|
# internal standard-library module target during `project()`, and that target
|
||||||
|
# only sees `CMAKE_CXX_FLAGS` (not later `add_compile_options`). The standard
|
||||||
|
# and extension settings must also be in place before `project()` so the
|
||||||
|
# internal `std.pcm` is built with the same configuration as our modules.
|
||||||
|
set(CMAKE_CXX_FLAGS_INIT "-stdlib=libc++")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_INIT "-stdlib=libc++")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-stdlib=libc++")
|
||||||
set(CMAKE_CXX_STANDARD 26)
|
set(CMAKE_CXX_STANDARD 26)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
|
||||||
|
|
||||||
|
project(OpenRA3 VERSION 0.5.0 LANGUAGES C CXX)
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
||||||
@@ -14,122 +35,372 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
|||||||
|
|
||||||
option(OPENRA3_WERROR "Treat warnings as errors" OFF)
|
option(OPENRA3_WERROR "Treat warnings as errors" OFF)
|
||||||
|
|
||||||
|
# Windows: statically link the C++ runtime (libc++, libunwind) so the .exe only
|
||||||
|
# needs SDL3.dll next to it at runtime.
|
||||||
|
if(WIN32)
|
||||||
|
add_link_options(-static)
|
||||||
|
endif()
|
||||||
|
|
||||||
function(openra3_target_defaults target)
|
function(openra3_target_defaults target)
|
||||||
target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic)
|
target_compile_options(${target} PRIVATE -Wall -Wextra)
|
||||||
|
set_property(TARGET ${target} PROPERTY CXX_MODULE_STD ON)
|
||||||
if(OPENRA3_WERROR)
|
if(OPENRA3_WERROR)
|
||||||
target_compile_options(${target} PRIVATE -Werror)
|
target_compile_options(${target} PRIVATE -Werror)
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# --- engine core: fundamental types, math, strings, random, message stream ---
|
# --- SDL3: pkg-config on Linux, or an explicit MinGW root for Windows --------
|
||||||
|
add_library(openra3_sdl3 INTERFACE)
|
||||||
|
set(OPENRA3_HAS_SDL3 OFF)
|
||||||
|
# The triple-named subdirectory inside a MinGW SDL3 development package
|
||||||
|
# (`<root>/<triple>/{include,lib,bin}`); x86_64 unless a toolchain overrides it.
|
||||||
|
set(OPENRA3_SDL3_MINGW_TRIPLE "x86_64-w64-mingw32" CACHE STRING "SDL3 MinGW triple subdirectory")
|
||||||
|
if(OPENRA3_SDL3_ROOT)
|
||||||
|
target_include_directories(openra3_sdl3 INTERFACE "${OPENRA3_SDL3_ROOT}/${OPENRA3_SDL3_MINGW_TRIPLE}/include")
|
||||||
|
target_link_libraries(openra3_sdl3 INTERFACE "${OPENRA3_SDL3_ROOT}/${OPENRA3_SDL3_MINGW_TRIPLE}/lib/libSDL3.dll.a")
|
||||||
|
set(OPENRA3_HAS_SDL3 ON)
|
||||||
|
else()
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
if(PkgConfig_FOUND)
|
||||||
|
pkg_check_modules(SDL3 QUIET IMPORTED_TARGET sdl3)
|
||||||
|
endif()
|
||||||
|
if(SDL3_FOUND)
|
||||||
|
target_link_libraries(openra3_sdl3 INTERFACE PkgConfig::SDL3)
|
||||||
|
set(OPENRA3_HAS_SDL3 ON)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(OPENRA3_HAS_SDL3)
|
||||||
|
message(STATUS "OpenRA3: SDL3 found - windowed viewer enabled")
|
||||||
|
else()
|
||||||
|
message(STATUS "OpenRA3: SDL3 not found - offscreen BMP output only")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- Vulkan: vendored volk + headers, so no Vulkan SDK is needed ---------------
|
||||||
|
# The loader is resolved at runtime (volk dlopen/LoadLibrary), which lets the
|
||||||
|
# same source build the Vulkan backend on Linux and on the MinGW Windows target.
|
||||||
|
option(OPENRA3_VULKAN "Build the Vulkan viewer" ON)
|
||||||
|
set(OPENRA3_HAS_VULKAN OFF)
|
||||||
|
if(OPENRA3_VULKAN AND OPENRA3_HAS_SDL3)
|
||||||
|
set(OPENRA3_HAS_VULKAN ON)
|
||||||
|
endif()
|
||||||
|
if(OPENRA3_HAS_VULKAN)
|
||||||
|
message(STATUS "OpenRA3: Vulkan viewer enabled (vendored volk)")
|
||||||
|
else()
|
||||||
|
message(STATUS "OpenRA3: Vulkan viewer disabled - offscreen image only")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(ra3_volk STATIC third_party/volk/volk.c)
|
||||||
|
target_include_directories(ra3_volk PUBLIC third_party/volk third_party/vulkan/include)
|
||||||
|
target_compile_definitions(ra3_volk PUBLIC VK_NO_PROTOTYPES)
|
||||||
|
|
||||||
|
# Embed the committed SPIR-V blobs into a generated header at configure time.
|
||||||
|
# Regenerate with shaders/compile.sh after editing a .vert/.frag.
|
||||||
|
function(openra3_embed_spirv out_header)
|
||||||
|
set(content "// Generated by CMake from shaders/generated/*.spv.\n")
|
||||||
|
string(APPEND content "#pragma once\n#include <cstddef>\n#include <cstdint>\n\nnamespace ra3_shaders {\n")
|
||||||
|
foreach(spv IN LISTS ARGN)
|
||||||
|
if(NOT EXISTS "${spv}")
|
||||||
|
message(FATAL_ERROR "Missing precompiled shader ${spv}; run shaders/compile.sh")
|
||||||
|
endif()
|
||||||
|
get_filename_component(base "${spv}" NAME)
|
||||||
|
string(MAKE_C_IDENTIFIER "${base}" ident)
|
||||||
|
file(READ "${spv}" hex HEX)
|
||||||
|
string(LENGTH "${hex}" hexlen)
|
||||||
|
math(EXPR words "${hexlen} / 8")
|
||||||
|
string(REGEX REPLACE "(..)(..)(..)(..)" "0x\\4\\3\\2\\1," words_list "${hex}")
|
||||||
|
string(APPEND content "inline constexpr std::uint32_t ${ident}[] = {${words_list}};\n")
|
||||||
|
string(APPEND content "inline constexpr std::size_t ${ident}_words = ${words};\n\n")
|
||||||
|
endforeach()
|
||||||
|
string(APPEND content "} // namespace ra3_shaders\n")
|
||||||
|
file(WRITE "${out_header}" "${content}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Embed the HLSL sources as string literals. The Direct3D backends compile them
|
||||||
|
# at runtime with d3dcompiler_47, so no HLSL compiler is needed at build time
|
||||||
|
# (the Windows target cross-compiles from Linux).
|
||||||
|
function(openra3_embed_hlsl out_header)
|
||||||
|
set(content "// Generated by CMake from shaders/*.hlsl.\n")
|
||||||
|
string(APPEND content "#pragma once\n\nnamespace ra3_shaders {\n")
|
||||||
|
foreach(src IN LISTS ARGN)
|
||||||
|
if(NOT EXISTS "${src}")
|
||||||
|
message(FATAL_ERROR "Missing HLSL shader ${src}")
|
||||||
|
endif()
|
||||||
|
get_filename_component(base "${src}" NAME_WE)
|
||||||
|
string(MAKE_C_IDENTIFIER "${base}" ident)
|
||||||
|
string(APPEND ident "_hlsl")
|
||||||
|
file(READ "${src}" text)
|
||||||
|
string(APPEND content "inline constexpr const char ${ident}[] = R\"RA3HLSL(${text})RA3HLSL\";\n\n")
|
||||||
|
endforeach()
|
||||||
|
string(APPEND content "} // namespace ra3_shaders\n")
|
||||||
|
file(WRITE "${out_header}" "${content}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# --- logging (vendored libenderlog, MIT) -------------------------------------
|
||||||
|
# A standalone C++26 module logger (`import ender.log;`) with a per-run
|
||||||
|
# archiving file sink. libc++ has no <stacktrace>, so on this toolchain the
|
||||||
|
# module records call sites but no stacks; it defaults ENDERLOG_HAS_STACKTRACE
|
||||||
|
# to 0 without any define.
|
||||||
|
add_library(ra3_enderlog STATIC)
|
||||||
|
target_sources(ra3_enderlog PUBLIC FILE_SET CXX_MODULES FILES third_party/libenderlog/src/ender.log.cppm)
|
||||||
|
target_compile_features(ra3_enderlog PUBLIC cxx_std_26)
|
||||||
|
openra3_target_defaults(ra3_enderlog)
|
||||||
|
|
||||||
|
# --- engine core -------------------------------------------------------------
|
||||||
add_library(ra3_core STATIC)
|
add_library(ra3_core STATIC)
|
||||||
target_sources(ra3_core
|
target_sources(ra3_core PUBLIC FILE_SET CXX_MODULES FILES src/core/ra3.core.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
|
||||||
src/core/ra3.core.cppm
|
|
||||||
)
|
|
||||||
openra3_target_defaults(ra3_core)
|
openra3_target_defaults(ra3_core)
|
||||||
|
|
||||||
# --- game logic: objects, players, teams, spatial partition, game loop ---
|
# --- game logic --------------------------------------------------------------
|
||||||
add_library(ra3_logic STATIC)
|
add_library(ra3_logic STATIC)
|
||||||
target_sources(ra3_logic
|
target_sources(ra3_logic PUBLIC FILE_SET CXX_MODULES FILES src/logic/ra3.logic.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
|
||||||
src/logic/ra3.logic.cppm
|
|
||||||
)
|
|
||||||
target_link_libraries(ra3_logic PUBLIC ra3_core)
|
target_link_libraries(ra3_logic PUBLIC ra3_core)
|
||||||
openra3_target_defaults(ra3_logic)
|
openra3_target_defaults(ra3_logic)
|
||||||
|
|
||||||
# --- client: headless display/input abstraction and the client facade ---
|
# --- RA3 gameplay data (real balance) ----------------------------------------
|
||||||
|
add_library(ra3_data STATIC)
|
||||||
|
target_sources(ra3_data PUBLIC FILE_SET CXX_MODULES FILES src/data/ra3.data.cppm)
|
||||||
|
target_link_libraries(ra3_data PUBLIC ra3_core)
|
||||||
|
openra3_target_defaults(ra3_data)
|
||||||
|
|
||||||
|
# --- client ------------------------------------------------------------------
|
||||||
add_library(ra3_client STATIC)
|
add_library(ra3_client STATIC)
|
||||||
target_sources(ra3_client
|
target_sources(ra3_client PUBLIC FILE_SET CXX_MODULES FILES src/client/ra3.client.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
|
||||||
src/client/ra3.client.cppm
|
|
||||||
)
|
|
||||||
target_link_libraries(ra3_client PUBLIC ra3_core ra3_logic)
|
target_link_libraries(ra3_client PUBLIC ra3_core ra3_logic)
|
||||||
openra3_target_defaults(ra3_client)
|
openra3_target_defaults(ra3_client)
|
||||||
|
|
||||||
# --- RA3 game definitions: factions, player templates, science, special powers ---
|
# --- RA3 game definitions ----------------------------------------------------
|
||||||
add_library(ra3_game STATIC)
|
add_library(ra3_game STATIC)
|
||||||
target_sources(ra3_game
|
target_sources(ra3_game PUBLIC FILE_SET CXX_MODULES FILES src/game/ra3.game.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
|
||||||
src/game/ra3.game.cppm
|
|
||||||
)
|
|
||||||
target_link_libraries(ra3_game PUBLIC ra3_core ra3_logic)
|
target_link_libraries(ra3_game PUBLIC ra3_core ra3_logic)
|
||||||
openra3_target_defaults(ra3_game)
|
openra3_target_defaults(ra3_game)
|
||||||
|
|
||||||
# --- filesystem: BIG4 archives, RefPack codec, local install locator ---
|
# --- filesystem: BIG4 + RefPack ---------------------------------------------
|
||||||
add_library(ra3_fs STATIC)
|
add_library(ra3_fs STATIC)
|
||||||
target_sources(ra3_fs
|
target_sources(ra3_fs PUBLIC FILE_SET CXX_MODULES FILES src/fs/ra3.fs.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
|
||||||
src/fs/ra3.fs.cppm
|
|
||||||
)
|
|
||||||
target_link_libraries(ra3_fs PUBLIC ra3_core)
|
target_link_libraries(ra3_fs PUBLIC ra3_core)
|
||||||
openra3_target_defaults(ra3_fs)
|
openra3_target_defaults(ra3_fs)
|
||||||
|
|
||||||
# --- map discovery/loading (real .big data, user's local install) ---
|
# --- map discovery/loading ---------------------------------------------------
|
||||||
add_library(ra3_map STATIC)
|
add_library(ra3_map STATIC)
|
||||||
target_sources(ra3_map
|
target_sources(ra3_map PUBLIC FILE_SET CXX_MODULES FILES src/map/ra3.map.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
|
||||||
src/map/ra3.map.cppm
|
|
||||||
)
|
|
||||||
target_link_libraries(ra3_map PUBLIC ra3_core ra3_fs)
|
target_link_libraries(ra3_map PUBLIC ra3_core ra3_fs)
|
||||||
openra3_target_defaults(ra3_map)
|
openra3_target_defaults(ra3_map)
|
||||||
|
|
||||||
# --- minimal headless skirmish simulation ---
|
# --- minimal skirmish simulation --------------------------------------------
|
||||||
add_library(ra3_skirmish STATIC)
|
add_library(ra3_skirmish STATIC)
|
||||||
target_sources(ra3_skirmish
|
target_sources(ra3_skirmish PUBLIC FILE_SET CXX_MODULES FILES src/skirmish/ra3.skirmish.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
target_link_libraries(ra3_skirmish PUBLIC ra3_core ra3_logic ra3_game ra3_data ra3_map)
|
||||||
src/skirmish/ra3.skirmish.cppm
|
|
||||||
)
|
|
||||||
target_link_libraries(ra3_skirmish PUBLIC ra3_core ra3_logic ra3_game ra3_map)
|
|
||||||
openra3_target_defaults(ra3_skirmish)
|
openra3_target_defaults(ra3_skirmish)
|
||||||
|
|
||||||
# --- software renderer: framebuffer, TGA decode, BMP encode, map compositing ---
|
# --- software renderer -------------------------------------------------------
|
||||||
add_library(ra3_render STATIC)
|
add_library(ra3_render STATIC)
|
||||||
target_sources(ra3_render
|
target_sources(ra3_render PUBLIC FILE_SET CXX_MODULES FILES src/render/ra3.render.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
|
||||||
src/render/ra3.render.cppm
|
|
||||||
)
|
|
||||||
target_link_libraries(ra3_render PUBLIC ra3_core)
|
target_link_libraries(ra3_render PUBLIC ra3_core)
|
||||||
openra3_target_defaults(ra3_render)
|
openra3_target_defaults(ra3_render)
|
||||||
|
|
||||||
# --- windowed viewer: SDL3 when available, otherwise a null backend ---
|
# --- real map terrain (HeightMapData / BlendTileData) ------------------------
|
||||||
find_package(PkgConfig QUIET)
|
add_library(ra3_terrain STATIC)
|
||||||
if(PkgConfig_FOUND)
|
target_sources(ra3_terrain PUBLIC FILE_SET CXX_MODULES FILES src/terrain/ra3.terrain.cppm)
|
||||||
pkg_check_modules(SDL3 QUIET IMPORTED_TARGET sdl3)
|
target_link_libraries(ra3_terrain PUBLIC ra3_core ra3_fs ra3_render)
|
||||||
endif()
|
openra3_target_defaults(ra3_terrain)
|
||||||
|
|
||||||
|
# The presentation facade imports render + terrain (for the terrain capability).
|
||||||
|
target_link_libraries(ra3_client PUBLIC ra3_render ra3_terrain)
|
||||||
|
|
||||||
|
# --- windowed viewer (SDL3 or null) -----------------------------------------
|
||||||
add_library(ra3_ui STATIC)
|
add_library(ra3_ui STATIC)
|
||||||
if(SDL3_FOUND)
|
if(OPENRA3_HAS_SDL3)
|
||||||
target_sources(ra3_ui
|
target_sources(ra3_ui PUBLIC FILE_SET CXX_MODULES FILES src/ui/ra3.ui.sdl.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
target_link_libraries(ra3_ui PUBLIC ra3_core ra3_render ra3_client openra3_sdl3)
|
||||||
src/ui/ra3.ui.sdl.cppm
|
|
||||||
)
|
|
||||||
target_link_libraries(ra3_ui PUBLIC ra3_core ra3_render PkgConfig::SDL3)
|
|
||||||
message(STATUS "OpenRA3: SDL3 ${SDL3_VERSION} found - windowed viewer enabled")
|
|
||||||
else()
|
else()
|
||||||
target_sources(ra3_ui
|
target_sources(ra3_ui PUBLIC FILE_SET CXX_MODULES FILES src/ui/ra3.ui.null.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
target_link_libraries(ra3_ui PUBLIC ra3_core ra3_render ra3_client)
|
||||||
src/ui/ra3.ui.null.cppm
|
|
||||||
)
|
|
||||||
target_link_libraries(ra3_ui PUBLIC ra3_core ra3_render)
|
|
||||||
message(STATUS "OpenRA3: SDL3 not found - offscreen BMP output only")
|
|
||||||
endif()
|
endif()
|
||||||
openra3_target_defaults(ra3_ui)
|
openra3_target_defaults(ra3_ui)
|
||||||
|
|
||||||
# --- umbrella module re-exporting the whole SDK ---
|
# --- Vulkan viewer (SDL3 surface + Vulkan, or null fallback) ------------------
|
||||||
|
add_library(ra3_vulkan STATIC)
|
||||||
|
if(OPENRA3_HAS_VULKAN AND OPENRA3_HAS_SDL3)
|
||||||
|
openra3_embed_spirv(
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/generated/shaders_embedded.hpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/scene.vert.spv"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/scene.frag.spv"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/terrain.vert.spv"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/terrain.frag.spv"
|
||||||
|
)
|
||||||
|
# The blobs are read at configure time, so re-run CMake when they change.
|
||||||
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/scene.vert.spv"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/scene.frag.spv"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/terrain.vert.spv"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/terrain.frag.spv")
|
||||||
|
target_sources(ra3_vulkan PUBLIC FILE_SET CXX_MODULES FILES src/vulkan/ra3.vulkan.sdl.cppm)
|
||||||
|
target_include_directories(ra3_vulkan PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||||
|
target_link_libraries(ra3_vulkan PUBLIC ra3_core ra3_render ra3_terrain ra3_client openra3_sdl3 ra3_volk)
|
||||||
|
else()
|
||||||
|
target_sources(ra3_vulkan PUBLIC FILE_SET CXX_MODULES FILES src/vulkan/ra3.vulkan.null.cppm)
|
||||||
|
target_link_libraries(ra3_vulkan PUBLIC ra3_core ra3_render ra3_client)
|
||||||
|
endif()
|
||||||
|
openra3_target_defaults(ra3_vulkan)
|
||||||
|
|
||||||
|
# --- Direct3D viewer (D3D11 + D3D12, or null fallback) ------------------------
|
||||||
|
# Direct3D is Windows-only, so the real backends build only for the MinGW target;
|
||||||
|
# every other platform links the `ra3.dx.null` fallback that fails `init` cleanly.
|
||||||
|
option(OPENRA3_DX "Build the Direct3D 11/12 viewer" ON)
|
||||||
|
set(OPENRA3_HAS_DX OFF)
|
||||||
|
if(OPENRA3_DX AND WIN32 AND OPENRA3_HAS_SDL3)
|
||||||
|
set(OPENRA3_HAS_DX ON)
|
||||||
|
endif()
|
||||||
|
if(OPENRA3_HAS_DX)
|
||||||
|
message(STATUS "OpenRA3: Direct3D 11/12 viewer enabled (runtime HLSL via d3dcompiler)")
|
||||||
|
else()
|
||||||
|
message(STATUS "OpenRA3: Direct3D viewer disabled - offscreen image only")
|
||||||
|
endif()
|
||||||
|
add_library(ra3_dx STATIC)
|
||||||
|
if(OPENRA3_HAS_DX)
|
||||||
|
openra3_embed_hlsl(
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/generated/dx_shaders_embedded.hpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/dx_scene.hlsl"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/dx_terrain.hlsl"
|
||||||
|
)
|
||||||
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/dx_scene.hlsl"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/dx_terrain.hlsl")
|
||||||
|
target_sources(ra3_dx PUBLIC FILE_SET CXX_MODULES FILES src/dx/ra3.dx.cppm)
|
||||||
|
target_include_directories(ra3_dx PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||||
|
target_link_libraries(ra3_dx PUBLIC ra3_core ra3_render ra3_terrain ra3_client ra3_enderlog openra3_sdl3 d3d11 d3d12 dxgi d3dcompiler)
|
||||||
|
else()
|
||||||
|
target_sources(ra3_dx PUBLIC FILE_SET CXX_MODULES FILES src/dx/ra3.dx.null.cppm)
|
||||||
|
target_link_libraries(ra3_dx PUBLIC ra3_core ra3_render ra3_client)
|
||||||
|
endif()
|
||||||
|
openra3_target_defaults(ra3_dx)
|
||||||
|
|
||||||
|
# --- display backend selection ------------------------------------------------
|
||||||
|
add_library(ra3_display STATIC)
|
||||||
|
target_sources(ra3_display PUBLIC FILE_SET CXX_MODULES FILES src/display/ra3.display.cppm)
|
||||||
|
target_link_libraries(ra3_display PUBLIC ra3_core ra3_render ra3_terrain ra3_client ra3_ui ra3_vulkan ra3_dx)
|
||||||
|
openra3_target_defaults(ra3_display)
|
||||||
|
|
||||||
|
# --- umbrella -----------------------------------------------------------------
|
||||||
add_library(ra3 STATIC)
|
add_library(ra3 STATIC)
|
||||||
target_sources(ra3
|
target_sources(ra3 PUBLIC FILE_SET CXX_MODULES FILES src/ra3.cppm)
|
||||||
PUBLIC FILE_SET CXX_MODULES FILES
|
target_link_libraries(ra3 PUBLIC ra3_core ra3_logic ra3_client ra3_data ra3_game ra3_fs ra3_map ra3_skirmish ra3_render ra3_terrain ra3_display
|
||||||
src/ra3.cppm
|
ra3_ui ra3_vulkan ra3_dx ra3_enderlog)
|
||||||
)
|
|
||||||
target_link_libraries(ra3 PUBLIC ra3_core ra3_logic ra3_client ra3_game ra3_fs ra3_map ra3_skirmish ra3_render ra3_ui)
|
|
||||||
openra3_target_defaults(ra3)
|
openra3_target_defaults(ra3)
|
||||||
|
|
||||||
# --- the headless game executable ---
|
# --- executable ---------------------------------------------------------------
|
||||||
add_executable(openra3 apps/openra3/main.cpp)
|
add_executable(openra3 apps/openra3/main.cpp)
|
||||||
target_link_libraries(openra3 PRIVATE ra3)
|
target_link_libraries(openra3 PRIVATE ra3)
|
||||||
openra3_target_defaults(openra3)
|
openra3_target_defaults(openra3)
|
||||||
|
|
||||||
# --- unit tests ---
|
if(WIN32 AND OPENRA3_SDL3_ROOT)
|
||||||
|
add_custom_command(TARGET openra3 POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"${OPENRA3_SDL3_ROOT}/${OPENRA3_SDL3_MINGW_TRIPLE}/bin/SDL3.dll"
|
||||||
|
"$<TARGET_FILE_DIR:openra3>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- packaging (.deb / .msi / portable archives) -----------------------------
|
||||||
|
# CPack produces the release artifacts in one shot:
|
||||||
|
# Linux -> DEB (dependencies derived from the binary with dpkg-shlibdeps)
|
||||||
|
# plus a portable .tar.gz
|
||||||
|
# Windows -> a Windows Installer .msi (built with wixl from msitools when the
|
||||||
|
# WiX tools are absent, so the Linux cross image can make it) plus a
|
||||||
|
# portable .zip with the exe and SDL3.dll
|
||||||
|
# OPENRA3_DISTRO_TAG (e.g. ubuntu26.04) is appended to the Linux file name so the
|
||||||
|
# per-distro images do not collide.
|
||||||
|
install(TARGETS openra3 RUNTIME DESTINATION bin)
|
||||||
|
if(WIN32 AND OPENRA3_SDL3_ROOT)
|
||||||
|
install(FILES "${OPENRA3_SDL3_ROOT}/${OPENRA3_SDL3_MINGW_TRIPLE}/bin/SDL3.dll" DESTINATION bin)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(OPENRA3_DISTRO_TAG "" CACHE STRING "Distro tag appended to package file names (e.g. ubuntu26.04)")
|
||||||
|
set(CPACK_PACKAGE_NAME "openra3")
|
||||||
|
set(CPACK_PACKAGE_VENDOR "OpenRA3")
|
||||||
|
set(CPACK_PACKAGE_CONTACT "OpenRA3 <noreply@openra3.invalid>")
|
||||||
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "OpenRA3 - a from-scratch reimplementation of Red Alert 3")
|
||||||
|
set(CPACK_PACKAGE_HOMEPAGE_URL "https://git.ender.cool/EnderTheCoder/openra3")
|
||||||
|
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
|
||||||
|
set(CPACK_PACKAGE_EXECUTABLES "openra3" "OpenRA3")
|
||||||
|
set(CPACK_STRIP_FILES ON)
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
||||||
|
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||||
|
set(OPENRA3_WIN_ARCH "arm64")
|
||||||
|
else()
|
||||||
|
set(OPENRA3_WIN_ARCH "x86_64")
|
||||||
|
endif()
|
||||||
|
set(CPACK_PACKAGE_FILE_NAME "openra3-${PROJECT_VERSION}-windows-${OPENRA3_WIN_ARCH}")
|
||||||
|
# Portable .zip (exe + SDL3.dll).
|
||||||
|
set(CPACK_GENERATOR "ZIP")
|
||||||
|
|
||||||
|
# A Windows Installer (.msi) straight from the Linux cross build via wixl
|
||||||
|
# (msitools). wixl 0.106 has no arm64 support, so the MSI is offered for
|
||||||
|
# x86_64 and ARM64 ships the portable zip.
|
||||||
|
find_program(OPENRA3_WIXL wixl)
|
||||||
|
if(OPENRA3_WIXL AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||||
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/packaging/openra3.wxs.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/openra3.wxs" @ONLY)
|
||||||
|
add_custom_target(openra3_msi
|
||||||
|
COMMAND "${OPENRA3_WIXL}" --arch x64
|
||||||
|
-D "exe=$<TARGET_FILE:openra3>"
|
||||||
|
-D "dll=${OPENRA3_SDL3_ROOT}/${OPENRA3_SDL3_MINGW_TRIPLE}/bin/SDL3.dll"
|
||||||
|
-o "$<TARGET_FILE_DIR:openra3>/${CPACK_PACKAGE_FILE_NAME}.msi"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/openra3.wxs"
|
||||||
|
DEPENDS openra3
|
||||||
|
COMMENT "Building Windows Installer (${CPACK_PACKAGE_FILE_NAME}.msi) with wixl"
|
||||||
|
VERBATIM)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "arm64")
|
||||||
|
else()
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
|
||||||
|
endif()
|
||||||
|
set(CPACK_PACKAGE_FILE_NAME "openra3_${PROJECT_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}${OPENRA3_DISTRO_TAG}")
|
||||||
|
set(CPACK_GENERATOR "TGZ;DEB")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_SECTION "games")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
|
||||||
|
endif()
|
||||||
|
include(CPack)
|
||||||
|
|
||||||
|
# --- extracted assets ---------------------------------------------------------
|
||||||
|
# At build time, extract the retail assets into <exe_dir>/assets so the
|
||||||
|
# executable is self-contained (no --game-dir at run time). This drives the
|
||||||
|
# ra3tools Python scripts (the game install + Python, no target executable), so
|
||||||
|
# it also runs for a cross-build when the install is mounted into the container
|
||||||
|
# and RA3_GAME_DIR points at it. The engine's own map/terrain reader is embedded
|
||||||
|
# in the exe (run `openra3 extract` on a native host if you prefer).
|
||||||
|
set(RA3_GAME_DIR "C:/Red Alert 3" CACHE PATH "Red Alert 3 installation used to extract assets")
|
||||||
|
set(RA3TOOLS_DIR "C:/Users/Ender/CLionProjects/ra3-headless/ra3tools" CACHE PATH "ra3tools scripts (asset extraction)")
|
||||||
|
find_program(OPENRA3_PYTHON NAMES python3 python)
|
||||||
|
set(PYTHON_EXECUTABLE "${OPENRA3_PYTHON}" CACHE FILEPATH "Python interpreter used to run ra3tools")
|
||||||
|
option(OPENRA3_EXTRACT_ALL "Extract every asset (models/textures/audio/movies) via ra3tools" ON)
|
||||||
|
|
||||||
|
if(EXISTS "${RA3_GAME_DIR}/Data" AND OPENRA3_PYTHON)
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${CMAKE_BINARY_DIR}/.assets_stamp"
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
"-DGAME_DIR=${RA3_GAME_DIR}"
|
||||||
|
"-DOUT=$<TARGET_FILE_DIR:openra3>/assets"
|
||||||
|
"-DPYTHON=${PYTHON_EXECUTABLE}"
|
||||||
|
"-DRA3TOOLS=${RA3TOOLS_DIR}"
|
||||||
|
"-DEXTRACT_ALL=$<IF:$<BOOL:${OPENRA3_EXTRACT_ALL}>,ON,OFF>"
|
||||||
|
"-DSTAMP=${CMAKE_BINARY_DIR}/.assets_stamp"
|
||||||
|
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/extract_assets.cmake"
|
||||||
|
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/extract_assets.cmake"
|
||||||
|
COMMENT "Extracting Red Alert 3 assets to $<TARGET_FILE_DIR:openra3>/assets"
|
||||||
|
VERBATIM)
|
||||||
|
add_custom_target(openra3_assets ALL DEPENDS "${CMAKE_BINARY_DIR}/.assets_stamp")
|
||||||
|
else()
|
||||||
|
message(STATUS "OpenRA3: asset extraction skipped (RA3_GAME_DIR '${RA3_GAME_DIR}' or Python not found)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- tests --------------------------------------------------------------------
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_executable(ra3_tests tests/ra3_tests.cpp)
|
add_executable(ra3_tests tests/ra3_tests.cpp)
|
||||||
target_link_libraries(ra3_tests PRIVATE ra3)
|
target_link_libraries(ra3_tests PRIVATE ra3)
|
||||||
|
|||||||
+25
-18
@@ -1,7 +1,9 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
# dev target: full toolchain + debug tools for development and debugging.
|
# Linux build environment. Isolated from the Windows cross image (Dockerfile.win)
|
||||||
# Base image and compiler are the latest stable (ubuntu:26.04 ships gcc/g++ 16).
|
# so the two toolchains and their standard libraries never interfere.
|
||||||
|
#
|
||||||
|
# dev target: clang + libc++ (for `import std;`) + SDL3 + debug tools.
|
||||||
FROM ubuntu:26.04 AS dev
|
FROM ubuntu:26.04 AS dev
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
@@ -23,44 +25,49 @@ RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
|||||||
sleep 5; \
|
sleep 5; \
|
||||||
done \
|
done \
|
||||||
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
build-essential \
|
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
|
clang-21 \
|
||||||
|
clang-tools-21 \
|
||||||
cmake \
|
cmake \
|
||||||
g++-16 \
|
dpkg-dev \
|
||||||
|
file \
|
||||||
gdb \
|
gdb \
|
||||||
git \
|
git \
|
||||||
|
libc++-21-dev \
|
||||||
|
libc++abi-21-dev \
|
||||||
libsdl3-dev \
|
libsdl3-dev \
|
||||||
|
libvulkan-dev \
|
||||||
ninja-build \
|
ninja-build \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Pin the toolchain to GCC 16 (the distro default is still GCC 15).
|
ENV CC=clang-21
|
||||||
ENV CC=gcc-16
|
ENV CXX=clang++-21
|
||||||
ENV CXX=g++-16
|
|
||||||
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-16 100 \
|
|
||||||
&& update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-16 100 \
|
|
||||||
&& update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-16 100
|
|
||||||
|
|
||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# C++26 modules need CMake >= 3.28, the Ninja generator and a module-aware gcc.
|
RUN cmake -S . -B build/linux -G Ninja -DCMAKE_BUILD_TYPE=Release -DOPENRA3_DISTRO_TAG=ubuntu26.04 \
|
||||||
RUN cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
&& cmake --build build/linux -j \
|
||||||
&& cmake --build build -j \
|
&& ctest --test-dir build/linux --output-on-failure \
|
||||||
&& ctest --test-dir build --output-on-failure
|
&& (cd build/linux && cpack -G DEB)
|
||||||
|
|
||||||
# deploy target: runtime dependencies + final binary, minimal and fast.
|
# deploy target: runtime dependencies + final binary, minimal and fast.
|
||||||
FROM ubuntu:26.04 AS deploy
|
FROM ubuntu:26.04 AS deploy
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get -o Acquire::Retries=5 update \
|
RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
apt-get -o Acquire::Retries=5 update \
|
||||||
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
|
libc++1 \
|
||||||
|
libc++abi1 \
|
||||||
libsdl3-0 \
|
libsdl3-0 \
|
||||||
libstdc++6 \
|
libvulkan1 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=dev /work/build/bin/openra3 /app/openra3
|
COPY --from=dev /work/build/linux/bin/openra3 /app/openra3
|
||||||
|
|
||||||
ENTRYPOINT ["/app/openra3"]
|
ENTRYPOINT ["/app/openra3"]
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Debian build environment (latest stable, Debian 13 "trixie") producing a .deb
|
||||||
|
# alongside the binary. Separate from Dockerfile (Ubuntu 26.04) because the
|
||||||
|
# distro and its libc++/SDL3 versions differ, and a .deb's dependencies must be
|
||||||
|
# generated against the distro it targets.
|
||||||
|
#
|
||||||
|
# Toolchain: clang-19 + libc++-19 (for `import std;`). Debian 13 ships CMake
|
||||||
|
# 3.31, whose experimental `import std` gate value the project does not target,
|
||||||
|
# so an upstream CMake is installed from the Kitware tarball.
|
||||||
|
|
||||||
|
FROM debian:stable AS dev
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
ARG APT_MIRROR=""
|
||||||
|
RUN if [ -n "$APT_MIRROR" ]; then \
|
||||||
|
sed -i -e "s|http://deb.debian.org/debian|${APT_MIRROR}|g" \
|
||||||
|
-e "s|http://security.debian.org/debian-security|${APT_MIRROR}|g" \
|
||||||
|
/etc/apt/sources.list /etc/apt/sources.list.d/*.sources 2>/dev/null || true; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
for attempt in 1 2 3 4 5; do \
|
||||||
|
apt-get -o Acquire::Retries=5 update && break; \
|
||||||
|
echo "apt-get update failed (attempt $attempt), retrying"; \
|
||||||
|
sleep 5; \
|
||||||
|
done \
|
||||||
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
clang-19 \
|
||||||
|
clang-tools-19 \
|
||||||
|
curl \
|
||||||
|
dpkg-dev \
|
||||||
|
file \
|
||||||
|
libc++-19-dev \
|
||||||
|
libc++abi-19-dev \
|
||||||
|
libsdl3-dev \
|
||||||
|
libunwind-19-dev \
|
||||||
|
lld-19 \
|
||||||
|
ninja-build \
|
||||||
|
pkg-config \
|
||||||
|
python3 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Upstream CMake, because Debian 13's 3.31 experimental gate does not enable the
|
||||||
|
# `import std;` support the project relies on.
|
||||||
|
ARG CMAKE_VERSION=4.2.3
|
||||||
|
RUN mkdir -p /opt/cmake \
|
||||||
|
&& curl -fL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.tar.gz" \
|
||||||
|
| tar -xz -C /opt/cmake --strip-components=1
|
||||||
|
ENV PATH="/opt/cmake/bin:${PATH}"
|
||||||
|
|
||||||
|
ENV CC=clang-19
|
||||||
|
ENV CXX=clang++-19
|
||||||
|
|
||||||
|
WORKDIR /work
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN cmake -S . -B build/debian -G Ninja \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_CXX_STDLIB_MODULES_JSON=/usr/lib/llvm-19/lib/libc++.modules.json \
|
||||||
|
-DOPENRA3_DISTRO_TAG=debian13 \
|
||||||
|
&& cmake --build build/debian -j \
|
||||||
|
&& ctest --test-dir build/debian --output-on-failure \
|
||||||
|
&& (cd build/debian && cpack -G DEB)
|
||||||
|
|
||||||
|
# deploy target: runtime dependencies + final binary, minimal and fast.
|
||||||
|
FROM debian:stable AS deploy
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
apt-get -o Acquire::Retries=5 update \
|
||||||
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
|
libc++1-19 \
|
||||||
|
libc++abi1-19 \
|
||||||
|
libsdl3-0 \
|
||||||
|
libvulkan1 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=dev /work/build/debian/bin/openra3 /app/openra3
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/openra3"]
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Debian build environment for Linux on ARM64 (aarch64-linux-gnu), producing an
|
||||||
|
# arm64 .deb. Cross-compiled from amd64 with Debian's clang-19 + libc++-19 and
|
||||||
|
# the arm64 packages installed via dpkg multiarch.
|
||||||
|
|
||||||
|
FROM debian:stable AS dev
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
ARG APT_MIRROR=""
|
||||||
|
RUN if [ -n "$APT_MIRROR" ]; then \
|
||||||
|
sed -i -e "s|http://deb.debian.org/debian|${APT_MIRROR}|g" \
|
||||||
|
-e "s|http://security.debian.org/debian-security|${APT_MIRROR}|g" \
|
||||||
|
/etc/apt/sources.list /etc/apt/sources.list.d/*.sources 2>/dev/null || true; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUN dpkg --add-architecture arm64
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
for attempt in 1 2 3 4 5; do \
|
||||||
|
apt-get -o Acquire::Retries=5 update && break; \
|
||||||
|
echo "apt-get update failed (attempt $attempt), retrying"; \
|
||||||
|
sleep 5; \
|
||||||
|
done \
|
||||||
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
clang-19 \
|
||||||
|
clang-tools-19 \
|
||||||
|
curl \
|
||||||
|
dpkg-dev \
|
||||||
|
file \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
libc++-19-dev:arm64 \
|
||||||
|
libc++abi-19-dev:arm64 \
|
||||||
|
libsdl3-dev:arm64 \
|
||||||
|
libunwind-19-dev:arm64 \
|
||||||
|
lld-19 \
|
||||||
|
ninja-build \
|
||||||
|
pkg-config \
|
||||||
|
python3 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ARG CMAKE_VERSION=4.2.3
|
||||||
|
RUN mkdir -p /opt/cmake \
|
||||||
|
&& curl -fL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.tar.gz" \
|
||||||
|
| tar -xz -C /opt/cmake --strip-components=1
|
||||||
|
ENV PATH="/opt/cmake/bin:${PATH}"
|
||||||
|
|
||||||
|
ENV CC=clang-19
|
||||||
|
ENV CXX=clang++-19
|
||||||
|
# Point pkg-config at the arm64 packages so SDL3 resolves to the aarch64 build.
|
||||||
|
ENV PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig
|
||||||
|
|
||||||
|
WORKDIR /work
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN cmake -S . -B build/debian-arm64 -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-aarch64-linux-gnu.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DOPENRA3_AARCH64_CC=clang-19 \
|
||||||
|
-DOPENRA3_AARCH64_CXX=clang++-19 \
|
||||||
|
-DOPENRA3_AARCH64_STDLIB_MODULES_JSON=/usr/lib/llvm-19/lib/libc++.modules.json \
|
||||||
|
-DOPENRA3_DISTRO_TAG=debian13 \
|
||||||
|
&& cmake --build build/debian-arm64 -j \
|
||||||
|
&& (cd build/debian-arm64 && cpack -G DEB)
|
||||||
|
|
||||||
|
FROM --platform=linux/arm64 debian:stable AS deploy
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
apt-get -o Acquire::Retries=5 update \
|
||||||
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
|
libc++1-19 \
|
||||||
|
libc++abi1-19 \
|
||||||
|
libsdl3-0 \
|
||||||
|
libvulkan1 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=dev /work/build/debian-arm64/bin/openra3 /app/openra3
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/openra3"]
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Linux on ARM64 cross-build environment (aarch64-linux-gnu), isolated from the
|
||||||
|
# x86_64 Linux image and the Windows images.
|
||||||
|
#
|
||||||
|
# Toolchain: clang-21 + libc++ with the arm64 libc++ (for `import std;`), the
|
||||||
|
# arm64 glibc from `gcc-aarch64-linux-gnu`, arm64 SDL3 from `libsdl3-dev:arm64`,
|
||||||
|
# and lld as the linker. The arm64 packages are installed with dpkg multiarch
|
||||||
|
# (`arm64` added to the sources); the native clang/cmake build tools stay amd64.
|
||||||
|
|
||||||
|
FROM ubuntu:26.04 AS dev
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
ARG APT_MIRROR=""
|
||||||
|
RUN if [ -n "$APT_MIRROR" ]; then \
|
||||||
|
sed -i -e "s|http://archive.ubuntu.com/ubuntu|${APT_MIRROR}|g" \
|
||||||
|
-e "s|http://security.ubuntu.com/ubuntu|${APT_MIRROR}|g" \
|
||||||
|
-e "s|http://ports.ubuntu.com/ubuntu-ports|${APT_MIRROR}|g" \
|
||||||
|
/etc/apt/sources.list /etc/apt/sources.list.d/*.sources 2>/dev/null || true; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable the arm64 architecture on top of the emulated amd64 userland.
|
||||||
|
RUN dpkg --add-architecture arm64
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
for attempt in 1 2 3 4 5; do \
|
||||||
|
apt-get -o Acquire::Retries=5 update && break; \
|
||||||
|
echo "apt-get update failed (attempt $attempt), retrying"; \
|
||||||
|
sleep 5; \
|
||||||
|
done \
|
||||||
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
clang-21 \
|
||||||
|
cmake \
|
||||||
|
dpkg-dev \
|
||||||
|
file \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
libc++-21-dev:arm64 \
|
||||||
|
libc++abi-21-dev:arm64 \
|
||||||
|
libsdl3-dev:arm64 \
|
||||||
|
libunwind-21-dev:arm64 \
|
||||||
|
lld-21 \
|
||||||
|
ninja-build \
|
||||||
|
pkg-config \
|
||||||
|
python3 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV CC=clang-21
|
||||||
|
ENV CXX=clang++-21
|
||||||
|
# Point pkg-config at the arm64 packages so SDL3 resolves to the aarch64 build.
|
||||||
|
ENV PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig
|
||||||
|
|
||||||
|
WORKDIR /work
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN cmake -S . -B build/linux-arm64 -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-aarch64-linux-gnu.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DOPENRA3_DISTRO_TAG=ubuntu26.04 \
|
||||||
|
&& cmake --build build/linux-arm64 -j \
|
||||||
|
&& (cd build/linux-arm64 && cpack -G DEB)
|
||||||
|
|
||||||
|
# deploy target: runtime dependencies + final binary, minimal and fast. This is
|
||||||
|
# an arm64 image; build it on (or with emulation for) an arm64 host.
|
||||||
|
FROM --platform=linux/arm64 ubuntu:26.04 AS deploy
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
apt-get -o Acquire::Retries=5 update \
|
||||||
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
|
libc++1 \
|
||||||
|
libc++abi1 \
|
||||||
|
libsdl3-0 \
|
||||||
|
libvulkan1 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=dev /work/build/linux-arm64/bin/openra3 /app/openra3
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/openra3"]
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Windows cross-build environment (x86_64), fully isolated from the Linux image
|
||||||
|
# in Dockerfile so the two toolchains and standard libraries never interfere.
|
||||||
|
#
|
||||||
|
# Toolchain: llvm-mingw (clang + libc++ + the libc++ `std` module, MSVCRT
|
||||||
|
# runtime) with the official SDL3 MinGW development package.
|
||||||
|
|
||||||
|
FROM ubuntu:26.04 AS dev
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
ARG APT_MIRROR=""
|
||||||
|
RUN if [ -n "$APT_MIRROR" ]; then \
|
||||||
|
sed -i -e "s|http://archive.ubuntu.com/ubuntu|${APT_MIRROR}|g" \
|
||||||
|
-e "s|http://security.ubuntu.com/ubuntu|${APT_MIRROR}|g" \
|
||||||
|
-e "s|http://ports.ubuntu.com/ubuntu-ports|${APT_MIRROR}|g" \
|
||||||
|
/etc/apt/sources.list /etc/apt/sources.list.d/*.sources 2>/dev/null || true; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
for attempt in 1 2 3 4 5; do \
|
||||||
|
apt-get -o Acquire::Retries=5 update && break; \
|
||||||
|
echo "apt-get update failed (attempt $attempt), retrying"; \
|
||||||
|
sleep 5; \
|
||||||
|
done \
|
||||||
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
cmake \
|
||||||
|
curl \
|
||||||
|
ninja-build \
|
||||||
|
python3 \
|
||||||
|
wixl \
|
||||||
|
xz-utils \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Pinned toolchain + SDL3 versions (override with --build-arg to bump).
|
||||||
|
ARG LLVM_MINGW_VERSION=20260908
|
||||||
|
ARG LLVM_MINGW_FLAVOR=msvcrt
|
||||||
|
ARG SDL3_VERSION=3.4.16
|
||||||
|
|
||||||
|
RUN mkdir -p /opt/llvm-mingw /opt/sdl3-mingw \
|
||||||
|
&& curl -fL "https://github.com/mstorsjo/llvm-mingw/releases/download/${LLVM_MINGW_VERSION}/llvm-mingw-${LLVM_MINGW_VERSION}-${LLVM_MINGW_FLAVOR}-ubuntu-22.04-x86_64.tar.xz" \
|
||||||
|
| tar -xJ -C /opt/llvm-mingw --strip-components=1 \
|
||||||
|
&& curl -fL "https://github.com/libsdl-org/SDL/releases/download/release-${SDL3_VERSION}/SDL3-devel-${SDL3_VERSION}-mingw.tar.gz" \
|
||||||
|
| tar -xz -C /opt/sdl3-mingw --strip-components=1
|
||||||
|
|
||||||
|
ENV OPENRA3_LLVM_MINGW_ROOT=/opt/llvm-mingw
|
||||||
|
ENV OPENRA3_SDL3_ROOT=/opt/sdl3-mingw
|
||||||
|
|
||||||
|
WORKDIR /work
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN cmake -S . -B build/windows -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/llvm-mingw-x86_64.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
&& cmake --build build/windows -j \
|
||||||
|
&& cmake --build build/windows --target openra3_msi \
|
||||||
|
&& (cd build/windows && cpack -G ZIP)
|
||||||
|
|
||||||
|
# package target: the .exe plus the SDL3 runtime DLL.
|
||||||
|
FROM ubuntu:26.04 AS package
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=dev /work/build/windows/bin/openra3.exe /app/
|
||||||
|
COPY --from=dev /work/build/windows/bin/SDL3.dll /app/
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Windows on ARM64 cross-build environment (aarch64-w64-mingw32), isolated from
|
||||||
|
# the x86_64 Windows image and the Linux images.
|
||||||
|
#
|
||||||
|
# Toolchain: the same multi-target llvm-mingw as Dockerfile.win (clang + libc++ +
|
||||||
|
# the libc++ `std` module, MSVCRT runtime), targeting aarch64. The official SDL
|
||||||
|
# release ships no MinGW ARM64 dev package, so SDL3 comes from MSYS2's clangarm64
|
||||||
|
# repository, staged into the `<triple>/{include,lib,bin}` layout the toolchain
|
||||||
|
# expects.
|
||||||
|
|
||||||
|
FROM ubuntu:26.04 AS dev
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
ARG APT_MIRROR=""
|
||||||
|
RUN if [ -n "$APT_MIRROR" ]; then \
|
||||||
|
sed -i -e "s|http://archive.ubuntu.com/ubuntu|${APT_MIRROR}|g" \
|
||||||
|
-e "s|http://security.ubuntu.com/ubuntu|${APT_MIRROR}|g" \
|
||||||
|
-e "s|http://ports.ubuntu.com/ubuntu-ports|${APT_MIRROR}|g" \
|
||||||
|
/etc/apt/sources.list /etc/apt/sources.list.d/*.sources 2>/dev/null || true; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
for attempt in 1 2 3 4 5; do \
|
||||||
|
apt-get -o Acquire::Retries=5 update && break; \
|
||||||
|
echo "apt-get update failed (attempt $attempt), retrying"; \
|
||||||
|
sleep 5; \
|
||||||
|
done \
|
||||||
|
&& apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
cmake \
|
||||||
|
curl \
|
||||||
|
ninja-build \
|
||||||
|
python3 \
|
||||||
|
xz-utils \
|
||||||
|
zstd \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Pinned toolchain + SDL3 versions (override with --build-arg to bump).
|
||||||
|
ARG LLVM_MINGW_VERSION=20260908
|
||||||
|
ARG LLVM_MINGW_FLAVOR=msvcrt
|
||||||
|
# SDL3 for aarch64-w64-mingw32 from MSYS2's clangarm64 repo (name includes the
|
||||||
|
# MSYS2 package release, hence the `-1`).
|
||||||
|
ARG MSYS2_SDL3_PKG=mingw-w64-clang-aarch64-sdl3-3.4.16-1-any.pkg.tar.zst
|
||||||
|
|
||||||
|
RUN mkdir -p /opt/llvm-mingw \
|
||||||
|
&& curl -fL "https://github.com/mstorsjo/llvm-mingw/releases/download/${LLVM_MINGW_VERSION}/llvm-mingw-${LLVM_MINGW_VERSION}-${LLVM_MINGW_FLAVOR}-ubuntu-22.04-x86_64.tar.xz" \
|
||||||
|
| tar -xJ -C /opt/llvm-mingw --strip-components=1
|
||||||
|
|
||||||
|
RUN mkdir -p /opt/sdl3-aarch64/aarch64-w64-mingw32/include /opt/sdl3-aarch64/aarch64-w64-mingw32/lib /opt/sdl3-aarch64/aarch64-w64-mingw32/bin \
|
||||||
|
&& curl -fL "https://repo.msys2.org/mingw/clangarm64/${MSYS2_SDL3_PKG}" -o /tmp/sdl3.pkg.tar.zst \
|
||||||
|
&& mkdir -p /tmp/sdl3 && tar --zstd -xf /tmp/sdl3.pkg.tar.zst -C /tmp/sdl3 \
|
||||||
|
&& cp -r /tmp/sdl3/clangarm64/include/SDL3 /opt/sdl3-aarch64/aarch64-w64-mingw32/include/ \
|
||||||
|
&& cp /tmp/sdl3/clangarm64/lib/libSDL3.dll.a /opt/sdl3-aarch64/aarch64-w64-mingw32/lib/ \
|
||||||
|
&& cp /tmp/sdl3/clangarm64/bin/SDL3.dll /opt/sdl3-aarch64/aarch64-w64-mingw32/bin/ \
|
||||||
|
&& rm -rf /tmp/sdl3 /tmp/sdl3.pkg.tar.zst
|
||||||
|
|
||||||
|
ENV OPENRA3_LLVM_MINGW_ROOT=/opt/llvm-mingw
|
||||||
|
ENV OPENRA3_SDL3_ROOT=/opt/sdl3-aarch64
|
||||||
|
|
||||||
|
WORKDIR /work
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN cmake -S . -B build/windows-arm64 -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/llvm-mingw-aarch64.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
&& cmake --build build/windows-arm64 -j \
|
||||||
|
&& (cd build/windows-arm64 && cpack -G ZIP)
|
||||||
|
|
||||||
|
# package target: the .exe plus the SDL3 runtime DLL.
|
||||||
|
FROM ubuntu:26.04 AS package
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=dev /work/build/windows-arm64/bin/openra3.exe /app/
|
||||||
|
COPY --from=dev /work/build/windows-arm64/bin/SDL3.dll /app/
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
# OpenRA3
|
# OpenRA3
|
||||||
|
|
||||||
A from-scratch, portable re-implementation of **Command & Conquer: Red Alert 3**
|
A from-scratch, portable re-implementation of **Command & Conquer: Red Alert 3**
|
||||||
in **C++26** using **C++ modules**, built with **GCC 16**.
|
in **C++26** using **C++ modules** and `import std;`, built with **Clang** for
|
||||||
|
both **Linux** and **Windows**.
|
||||||
|
|
||||||
Red Alert 3 runs SAGE 2.0. EA never released that engine's C++ source, but it
|
Red Alert 3 runs SAGE 2.0. EA never released that engine's C++ source, but it
|
||||||
did open-source the closely related SAGE 1.0 engine as
|
did open-source the closely related SAGE 1.0 engine as
|
||||||
@@ -17,11 +18,23 @@ Ghidra.
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
OpenRA3 is at **v0.0.1**. The engine compiles and runs headless, and a **minimal
|
OpenRA3 is at **v0.5.0**. The engine compiles and runs headless, and a **minimal
|
||||||
skirmish** is playable: it reads a real multiplayer map out of your install,
|
skirmish** is playable: it reads a real multiplayer map out of your install,
|
||||||
recovers the player start waypoints, and simulates two sides earning credits,
|
recovers the player start waypoints, and simulates two sides building a base,
|
||||||
training units and fighting until one base falls. The map and match state can be
|
extracting ore and fighting until one side is wiped out. The balance is the
|
||||||
**rendered** to a window (SDL3) or to an image.
|
**retail Red Alert 3 balance**, pinned in `ra3.data` from EA's open RA3 XML:
|
||||||
|
damage types, `ArmorTemplate` percentages, weapon target masks, build costs and
|
||||||
|
the ore economy. Presentation offers **Vulkan** (`ra3.vulkan`) and **Direct3D 11 / 12**
|
||||||
|
(`ra3.dx`) GPU backends with an SDL software blit and null fallbacks; the
|
||||||
|
backend is selectable from the in-game menu and the software renderer still
|
||||||
|
produces headless images. Terrain tiles
|
||||||
|
cross-fade the way the retail `Terrain.fx` does (a per-cell blend ramp plus a
|
||||||
|
gutter-padded atlas), so material boundaries are smooth instead of a grid of
|
||||||
|
hard lines. An in-window **menu** lists the maps by their localized name and
|
||||||
|
exposes every render/skirmish option for tweaking before launch. The whole tree
|
||||||
|
builds for **Linux** (clang + libc++) and cross-compiles to **Windows**
|
||||||
|
(`openra3.exe` + `SDL3.dll`) with llvm-mingw — both using C++26 modules and
|
||||||
|
`import std;`.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ openra3 skirmish --game-dir "/game" --map map_mp_2_feasel4
|
$ openra3 skirmish --game-dir "/game" --map map_mp_2_feasel4
|
||||||
@@ -29,9 +42,9 @@ OpenRA3 skirmish map=map_mp_2_feasel4 source=archive:map_mp_2_feasel4 seed=1
|
|||||||
start positions:
|
start positions:
|
||||||
P0 (1338, 1940)
|
P0 (1338, 1940)
|
||||||
P1 (1291, 1404)
|
P1 (1291, 1404)
|
||||||
result: decided winner=0 frames=3397 (113.2 s)
|
result: decided winner=0 frames=9293 (309.8 s)
|
||||||
P0 Commander Allied money= 129 units=19 kills=21 losses=3
|
P0 Commander Allied money= 700 units=15 kills=21 losses=15
|
||||||
P1 AI Soviet money= 840 units= 0 kills=3 losses=21
|
P1 AI Soviet money= 376 units= 0 kills=15 losses=21
|
||||||
```
|
```
|
||||||
|
|
||||||
## Offline only
|
## Offline only
|
||||||
@@ -44,15 +57,25 @@ tested without any external service.
|
|||||||
|
|
||||||
## Assets
|
## Assets
|
||||||
|
|
||||||
The engine reads the retail game from your local install:
|
The runtime reads a portable `assets/` folder **next to the executable** — there
|
||||||
|
is no `--game-dir` at run time. The build extracts it from your install:
|
||||||
|
|
||||||
- default path `C:\Red Alert 3`, or `--game-dir DIR`, or `$RA3_GAME_DIR`;
|
- `openra3_assets` (build target) runs `cmake/extract_assets.cmake`, which
|
||||||
- `Data\MapsMultiplayer.big` is read for the map catalog;
|
- extracts every map (double-unwrapped `CkMp`) and terrain TGA into `assets/`
|
||||||
- maps are unpacked from `BIG4` + RefPack and their start waypoints recovered.
|
via the engine's own `openra3 extract`;
|
||||||
|
- with `-DOPENRA3_EXTRACT_ALL=ON` (default) also dumps **every** `.big` entry
|
||||||
|
plus models, textures (`.png`), sound effects/voice and movie audio via the
|
||||||
|
[`ra3-headless/ra3tools`](https://github.com/) scripts.
|
||||||
|
- CMake cache vars: `RA3_GAME_DIR` (default `C:/Red Alert 3`), `RA3TOOLS_DIR`
|
||||||
|
(the `ra3tools` scripts), `PYTHON_EXECUTABLE`.
|
||||||
|
- The step is skipped when the exe cannot run on the build host (a Windows
|
||||||
|
cross-build in a Linux container); the exe then extracts maps/terrain on first
|
||||||
|
launch, and the full dump can be run explicitly:
|
||||||
|
`cmake --build <build> --target openra3_assets` on a native host, or
|
||||||
|
`openra3 extract --game-dir DIR --out DIR`.
|
||||||
|
|
||||||
**No game data is committed or distributed.** `reference/` (the GPLv3 Generals
|
**No game data is committed or distributed.** `reference/`, `assets/` and the
|
||||||
source) and any extracted assets are git-ignored; `tools/fetch_reference.sh`
|
ra3tools checkout are git-ignored. Without an install the engine falls back to a
|
||||||
fetches the former on demand. Without an install, the engine falls back to a
|
|
||||||
built-in test map so the project still builds and runs in CI.
|
built-in test map so the project still builds and runs in CI.
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
@@ -61,70 +84,195 @@ built-in test map so the project still builds and runs in CI.
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `src/core/ra3.core.cppm` | fundamental types, math, strings, random, message stream |
|
| `src/core/ra3.core.cppm` | fundamental types, math, strings, random, message stream |
|
||||||
| `src/logic/ra3.logic.cppm` | objects, players, teams, spatial partition, game loop |
|
| `src/logic/ra3.logic.cppm` | objects, players, teams, spatial partition, game loop |
|
||||||
| `src/client/ra3.client.cppm` | display abstraction + client facade / frame loop |
|
| `src/client/ra3.client.cppm` | `display` abstraction + shared interactive loops + client facade |
|
||||||
|
| `src/display/ra3.display.cppm` | picks the backend (Vulkan/D3D11/D3D12, then SDL) for the app |
|
||||||
| `src/game/ra3.game.cppm` | RA3 sides, player templates, skirmish defaults |
|
| `src/game/ra3.game.cppm` | RA3 sides, player templates, skirmish defaults |
|
||||||
|
| `src/data/ra3.data.cppm` | retail RA3 balance: damage types, armour, weapons, units, economy |
|
||||||
| `src/fs/ra3.fs.cppm` | `BIG4` archives, RefPack codec, local install locator |
|
| `src/fs/ra3.fs.cppm` | `BIG4` archives, RefPack codec, local install locator |
|
||||||
| `src/map/ra3.map.cppm` | map catalog, `EAR`/RefPack unwrap, start waypoints |
|
| `src/map/ra3.map.cppm` | map catalog, `EAR`/RefPack unwrap, start waypoints, `gamestrings.csf` display names |
|
||||||
| `src/skirmish/ra3.skirmish.cppm` | units, economy, AI, combat, win condition |
|
| `src/skirmish/ra3.skirmish.cppm` | base building, economy, AI, combat, win condition |
|
||||||
| `src/render/ra3.render.cppm` | ARGB framebuffer, TGA decode, BMP encode, map compositing |
|
| `src/terrain/ra3.terrain.cppm` | `CkMp` terrain: `HeightMapData`, `BlendTileData` (tiles + blends), `Terrain.big` tiles |
|
||||||
| `src/ui/ra3.ui.*.cppm` | SDL3 window viewer (null backend when SDL3 is absent) |
|
| `src/render/ra3.render.cppm` | ARGB framebuffer, TGA decode, BMP encode, map compositing, bitmap-font text |
|
||||||
|
| `src/ui/ra3.ui.*.cppm` | SDL3 window viewer and menu (null backend when SDL3 is absent) |
|
||||||
|
| `src/vulkan/ra3.vulkan.*.cppm` | Vulkan presentation backend and menu (null fallback without a loader) |
|
||||||
|
| `src/dx/ra3.dx.*.cppm` | Direct3D 11/12 presentation backend (runtime HLSL; null fallback off Windows) |
|
||||||
|
| `third_party/libenderlog/` | vendored C++26 module logger (`import ender.log;`, MIT) with a native stack fallback for libc++ |
|
||||||
| `src/ra3.cppm` | umbrella module re-exporting the SDK |
|
| `src/ra3.cppm` | umbrella module re-exporting the SDK |
|
||||||
| `apps/openra3/main.cpp` | `maps` / `skirmish` CLI |
|
| `apps/openra3/main.cpp` | `menu` / `maps` / `skirmish` / `render` CLI |
|
||||||
| `tests/ra3_tests.cpp` | smoke tests (run via `ctest`) |
|
| `tests/ra3_tests.cpp` | smoke tests (run via `ctest`) |
|
||||||
| `tools/` | reference fetch + Ghidra-driven reconstruction helpers |
|
| `tools/` | reference fetch + Ghidra-driven reconstruction helpers |
|
||||||
| `docs/` | architecture, reverse-engineering notes, roadmap |
|
| `docs/` | architecture & master plan, reverse-engineering notes |
|
||||||
| `Dockerfile` | `dev` (toolchain) and `deploy` (runtime) targets |
|
| `Dockerfile` | Linux build image (`dev` toolchain + `deploy` runtime) |
|
||||||
| `.gitlab-ci.yml` | build → test → package pipeline |
|
| `Dockerfile.win` | isolated Windows cross-build image (llvm-mingw + SDL3 MinGW) |
|
||||||
|
| `cmake/toolchains/` | `llvm-mingw-x86_64.cmake` cross toolchain |
|
||||||
|
| `scripts/` | `build-linux.sh` / `build-windows.sh` one-shot builders |
|
||||||
|
| `.gitlab-ci.yml` | build both targets → test → package pipeline |
|
||||||
|
|
||||||
|
## Why Clang + `import std;`
|
||||||
|
|
||||||
|
The engine never `#include`s the standard library: every module does
|
||||||
|
`import std;`. CMake's support for that (`CXX_MODULE_STD`) works today with
|
||||||
|
Clang + libc++. Linux uses the distro clang (LLVM 21); Windows cross-compiles
|
||||||
|
with [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) (clang 23 + libc++ +
|
||||||
|
the libc++ `std` module). The two toolchains live in **separate images** so
|
||||||
|
their compilers and standard libraries never interfere.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
The host needs no toolchain: build inside the container.
|
The host needs no toolchain: each target builds inside its own image.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build --target dev -t openra3-dev:local .
|
# Linux (amd64) -> build/linux/bin/openra3 + .deb
|
||||||
|
scripts/build-linux.sh
|
||||||
|
|
||||||
docker run --rm -v "$PWD:/work" -w /work openra3-dev:local cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
# Linux/arm64 -> build/linux-arm64/bin/openra3 + .deb (cross)
|
||||||
docker run --rm -v "$PWD:/work" -w /work openra3-dev:local cmake --build build -j
|
scripts/build-linux-arm64.sh
|
||||||
docker run --rm -v "$PWD:/work" -w /work openra3-dev:local ctest --test-dir build --output-on-failure
|
|
||||||
|
# Debian (amd64) -> build/debian/bin/openra3 + .deb
|
||||||
|
scripts/build-debian.sh
|
||||||
|
|
||||||
|
# Debian (arm64) -> build/debian-arm64/bin/openra3 + .deb (cross)
|
||||||
|
scripts/build-debian-arm64.sh
|
||||||
|
|
||||||
|
# Windows (x86_64) -> build/windows/bin/openra3.exe + SDL3.dll, .zip, .msi
|
||||||
|
scripts/build-windows.sh
|
||||||
|
|
||||||
|
# Windows (arm64) -> build/windows-arm64/bin/openra3.exe + SDL3.dll, .zip (cross)
|
||||||
|
scripts/build-windows-arm64.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Every target is cross-built from x86_64 Linux in its own image: llvm-mingw for
|
||||||
|
the Windows targets, clang + libc++ with dpkg multiarch for the arm64 targets,
|
||||||
|
and Debian/Ubuntu-specific images for the `.deb` packages.
|
||||||
|
|
||||||
|
Or drive Docker directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build --target dev -t openra3-linux:local .
|
||||||
|
docker run --rm -v "$PWD:/work" -w /work openra3-linux:local \
|
||||||
|
cmake -S . -B build/linux -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||||
|
docker run --rm -v "$PWD:/work" -w /work openra3-linux:local cmake --build build/linux -j
|
||||||
|
docker run --rm -v "$PWD:/work" -w /work openra3-linux:local ctest --test-dir build/linux --output-on-failure
|
||||||
|
|
||||||
|
docker build -f Dockerfile.win --target dev -t openra3-windows:local .
|
||||||
|
docker run --rm -v "$PWD:/work" -w /work openra3-windows:local \
|
||||||
|
cmake -S . -B build/windows -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/llvm-mingw-x86_64.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
docker run --rm -v "$PWD:/work" -w /work openra3-windows:local cmake --build build/windows -j
|
||||||
```
|
```
|
||||||
|
|
||||||
Behind a slow or blocked mirror, pass `--build-arg APT_MIRROR=<url>`.
|
Behind a slow or blocked mirror, pass `--build-arg APT_MIRROR=<url>`.
|
||||||
|
|
||||||
|
The two images are deliberately separate so the Linux (clang + libc++) and
|
||||||
|
Windows (llvm-mingw + MinGW SDL3) toolchains never interfere. The Windows demo
|
||||||
|
is `build/windows/bin/openra3.exe` next to `SDL3.dll`:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# show a real map in a Vulkan window
|
||||||
|
openra3.exe render --game-dir "C:\Red Alert 3" --vulkan
|
||||||
|
```
|
||||||
|
|
||||||
|
Vulkan is provided by **vendored volk + headers** (`third_party/`), resolved at
|
||||||
|
runtime, so neither image needs a Vulkan SDK.
|
||||||
|
|
||||||
|
## Packages
|
||||||
|
|
||||||
|
CPack produces the release artifacts; the Docker images and CI run it for every
|
||||||
|
target.
|
||||||
|
|
||||||
|
| Target | Portable | Installer |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Linux amd64 (Ubuntu 26.04) | `.tar.gz` | `openra3_<v>_amd64ubuntu26.04.deb` |
|
||||||
|
| Linux arm64 (Ubuntu 26.04) | `.tar.gz` | `openra3_<v>_arm64ubuntu26.04.deb` |
|
||||||
|
| Linux amd64 (Debian 13) | `.tar.gz` | `openra3_<v>_amd64debian13.deb` |
|
||||||
|
| Linux arm64 (Debian 13) | `.tar.gz` | `openra3_<v>_arm64debian13.deb` |
|
||||||
|
| Windows x86_64 | `openra3-<v>-windows-x86_64.zip` | `openra3-<v>-windows-x86_64.msi` |
|
||||||
|
| Windows arm64 | `openra3-<v>-windows-arm64.zip` | (WiX-only; see below) |
|
||||||
|
|
||||||
|
The `.deb` dependencies are derived from the binary with `dpkg-shlibdeps`. The
|
||||||
|
`.msi` is built with **wixl** (msitools) straight from the Linux cross image;
|
||||||
|
wixl 0.106 has no arm64 support, so Windows/ARM64 ships the portable `.zip`
|
||||||
|
(the MSI toolchain for ARM64 would be WiX v4 via the .NET SDK).
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Every run writes `openra3.log` next to the executable through the vendored
|
||||||
|
[`libenderlog`](third_party/libenderlog) module (`import ender.log;`). A file
|
||||||
|
sink archives the previous log to `openra3.log.<YYYYmmdd-HHMMSS>` on open, so
|
||||||
|
each run gets its own file; the active file rotates at 4 MiB and the last 10
|
||||||
|
archives are kept. Records at **`warn` and above** carry a call stack (Windows
|
||||||
|
`CaptureStackBackTrace` / POSIX `execinfo`, because libc++ has no
|
||||||
|
`<stacktrace>`). A hard crash also writes `openra3_crash.log` with the faulting
|
||||||
|
module and a raw backtrace.
|
||||||
|
|
||||||
## Running a skirmish
|
## Running a skirmish
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# list the maps in your install
|
# list the maps in your install
|
||||||
docker run --rm -v "/path/to/Red Alert 3:/game:ro" openra3-dev:local \
|
docker run --rm -v "/path/to/Red Alert 3:/game:ro" openra3-linux:local \
|
||||||
./build/bin/openra3 maps --game-dir /game
|
/work/build/linux/bin/openra3 maps --game-dir /game
|
||||||
|
|
||||||
# play a headless skirmish on a real map
|
# play a headless skirmish on a real map
|
||||||
docker run --rm -v "/path/to/Red Alert 3:/game:ro" openra3-dev:local \
|
docker run --rm -v "/path/to/Red Alert 3:/game:ro" openra3-linux:local \
|
||||||
./build/bin/openra3 skirmish --game-dir /game --map map_mp_2_feasel4 --seed 7
|
/work/build/linux/bin/openra3 skirmish --game-dir /game --map map_mp_2_feasel4 --seed 7
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The Windows build is a native `openra3.exe` — copy it next to `SDL3.dll` and run
|
||||||
|
it from `cmd`/PowerShell, e.g. `openra3.exe skirmish --game-dir "C:\Red Alert 3"`.
|
||||||
|
|
||||||
`--frames N` caps the simulation length (default 15 minutes of game time at
|
`--frames N` caps the simulation length (default 15 minutes of game time at
|
||||||
30 Hz). The result is deterministic for a given map and seed.
|
30 Hz). The result is deterministic for a given map and seed.
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
|
||||||
|
Run `openra3` with no arguments (or `openra3 menu`) to open a window listing the
|
||||||
|
maps by their **localized display name** (read from the install's
|
||||||
|
`gamestrings.csf`, e.g. `map_mp_2_feasel4` → "Battlebase Beta") with their id
|
||||||
|
below. The `Options` column exposes every `render`/`skirmish` parameter — mode
|
||||||
|
(3D terrain / top-down / skirmish), window size, camera pitch/yaw/height, FOV,
|
||||||
|
zoom, terrain scale, terrain pitch, world size, seed, frame cap, the overview
|
||||||
|
thumbnail toggle and a BMP output path. `Up`/`Down` selects, `Left`/`Right`
|
||||||
|
changes a value (or moves the text caret on the BMP field), `Tab` switches
|
||||||
|
between the map list and the options, `Enter` starts and `Esc` quits. Starting a
|
||||||
|
3D or top-down view opens the viewer; closing it returns to the menu. When no
|
||||||
|
window backend is available the same flow falls back to a console picker, and
|
||||||
|
`openra3 menu-preview` renders one menu frame to a BMP for inspection.
|
||||||
|
|
||||||
## Rendering the map
|
## Rendering the map
|
||||||
|
|
||||||
The renderer draws the map's own overview art, a world grid and the match state
|
`render` draws the **real terrain**: it parses the map's `HeightMapData`
|
||||||
(start markers in yellow, player 0 in blue, player 1 in red).
|
(elevation grid) and `BlendTileData` (per-cell tile index plus the per-cell
|
||||||
|
`Blends`/`ThreeWayBlends` and their `BlendDescription`s), loads the tile
|
||||||
|
textures from `Data\Terrain.big` (RefPack + TGA), and rasterises the map with an
|
||||||
|
elevation shade. Each cell cross-fades into its blend neighbour with the same
|
||||||
|
linear ramp the retail `Terrain.fx` uses, so material transitions are smooth; on
|
||||||
|
the GPU path the tile atlas is padded with a replicated gutter so filtering
|
||||||
|
never bleeds between tiles. Match state is overlaid (start markers in yellow,
|
||||||
|
player 0 in blue, player 1 in red). `--thumbnail` uses the old `<map>_art.tga`
|
||||||
|
overview instead.
|
||||||
|
|
||||||
**Offscreen image** (works anywhere, no display needed):
|
**Offscreen image** (works anywhere, no display needed):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm -v "/path/to/Red Alert 3:/game:ro" -v "$PWD/out:/out" openra3-dev:local \
|
docker run --rm -v "/path/to/Red Alert 3:/game:ro" -v "$PWD/out:/out" openra3-linux:local \
|
||||||
/work/build/bin/openra3 render --game-dir /game --map map_mp_2_feasel4 --out /out/map.bmp
|
/work/build/linux/bin/openra3 render --game-dir /game --map map_mp_2_feasel4 --out /out/map.bmp
|
||||||
```
|
```
|
||||||
|
|
||||||
**Interactive window** (SDL3; drag to pan, wheel to zoom, Esc to quit). In a
|
On Windows the same command runs natively: `openra3.exe render --game-dir
|
||||||
container you need an X server on the host — on Windows run
|
"C:\Red Alert 3" --map map_mp_2_feasel4` opens an SDL3 window.
|
||||||
|
|
||||||
|
**Interactive window** (Vulkan with `--vulkan`, otherwise SDL3). The camera
|
||||||
|
follows the retail tactical view: it opens centred on the first player's start,
|
||||||
|
the wheel zooms, pushing the cursor against a screen edge scrolls, dragging with
|
||||||
|
the left button pans, and Esc quits. `--zoom Z` sets the initial zoom (1 fits
|
||||||
|
the whole map). In a container you need an X server on the host — on Windows run
|
||||||
[VcXsrv](https://sourceforge.net/projects/vcxsrv/) and launch it with "Disable
|
[VcXsrv](https://sourceforge.net/projects/vcxsrv/) and launch it with "Disable
|
||||||
access control", then:
|
access control", then:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm -v "/path/to/Red Alert 3:/game:ro" -e DISPLAY=host.docker.internal:0.0 \
|
docker run --rm -v "/path/to/Red Alert 3:/game:ro" -e DISPLAY=host.docker.internal:0.0 \
|
||||||
openra3-dev:local /work/build/bin/openra3 render --game-dir /game --map map_mp_2_feasel4
|
openra3-linux:local /work/build/linux/bin/openra3 render --game-dir /game --map map_mp_2_feasel4
|
||||||
```
|
```
|
||||||
|
|
||||||
If no display is available the viewer falls back to writing `openra3_view.bmp`.
|
If no display is available the viewer falls back to writing `openra3_view.bmp`.
|
||||||
|
|||||||
+1161
-125
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
|||||||
|
# Extract the retail assets into OUT using the ra3tools scripts. Driven by the
|
||||||
|
# openra3_assets target and run with `cmake -P`, so it needs only Python and the
|
||||||
|
# game install - no target executable, and therefore works for a cross-build too
|
||||||
|
# (mount the install into the container and pass RA3_GAME_DIR).
|
||||||
|
#
|
||||||
|
# Variables (all required unless noted):
|
||||||
|
# GAME_DIR Red Alert 3 install root
|
||||||
|
# OUT destination folder (next to the executable)
|
||||||
|
# PYTHON python interpreter
|
||||||
|
# RA3TOOLS path to ra3-headless/ra3tools
|
||||||
|
# EXTRACT_ALL ON to also dump every asset (models/textures/audio/movies)
|
||||||
|
# STAMP stamp file to write on completion
|
||||||
|
|
||||||
|
function(run)
|
||||||
|
execute_process(COMMAND ${ARGN} RESULT_VARIABLE _rc)
|
||||||
|
if(NOT _rc EQUAL 0)
|
||||||
|
message(WARNING "extract step failed (${_rc}): ${ARGN}")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${OUT}")
|
||||||
|
|
||||||
|
if(NOT EXISTS "${RA3TOOLS}/ra3_big.py")
|
||||||
|
message(WARNING "ra3tools not found at '${RA3TOOLS}'; skipping asset extraction")
|
||||||
|
else()
|
||||||
|
# Maps + overview art: raw .big payloads; the engine unwraps EAR/RefPack.
|
||||||
|
run("${PYTHON}" "${RA3TOOLS}/ra3_big.py" extract "${GAME_DIR}/Data/MapsMultiplayer.big" --match "*.map" --out "${OUT}/maps")
|
||||||
|
run("${PYTHON}" "${RA3TOOLS}/ra3_big.py" extract "${GAME_DIR}/Data/MapsMultiplayer.big" --match "*_art.tga" --out "${OUT}/maps")
|
||||||
|
|
||||||
|
# Terrain tile textures (the engine samples these for the map).
|
||||||
|
foreach(archive Terrain.big Core11.big)
|
||||||
|
if(EXISTS "${GAME_DIR}/Data/${archive}")
|
||||||
|
run("${PYTHON}" "${RA3TOOLS}/ra3_big.py" extract "${GAME_DIR}/Data/${archive}" --match "*.tga" --out "${OUT}/terrain")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(EXTRACT_ALL)
|
||||||
|
message(STATUS "extracting every .big entry to ${OUT}/raw")
|
||||||
|
run("${PYTHON}" "${RA3TOOLS}/ra3_big.py" extract-all --game-dir "${GAME_DIR}" --out "${OUT}/raw" --match "*")
|
||||||
|
|
||||||
|
if(EXISTS "${RA3TOOLS}/ra3_binary.py")
|
||||||
|
message(STATUS "extracting models + textures (static stream)")
|
||||||
|
run("${PYTHON}" "${RA3TOOLS}/ra3_binary.py" export "${GAME_DIR}/Data/StaticStream.big" --models --textures --png --by-source --out "${OUT}/art")
|
||||||
|
|
||||||
|
message(STATUS "extracting audio (sound effects + voice)")
|
||||||
|
run("${PYTHON}" "${RA3TOOLS}/ra3_binary.py" export "${GAME_DIR}/Data/EnglishAudio.big" --stream audio --audio --out "${OUT}/audio")
|
||||||
|
|
||||||
|
message(STATUS "extracting movie audio")
|
||||||
|
run("${PYTHON}" "${RA3TOOLS}/ra3_big.py" extract "${GAME_DIR}/Data/EnglishMovieAudio.big" --match "*.snd" --out "${OUT}/movies")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(TOUCH "${STAMP}")
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<!--
|
||||||
|
Windows Installer source for OpenRA3, driven by wixl (msitools) so the Linux
|
||||||
|
cross-build image can produce the .msi. `exe` and `dll` are passed with
|
||||||
|
wixl -D; the version is substituted at configure time.
|
||||||
|
-->
|
||||||
|
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
|
||||||
|
<Product Id='*'
|
||||||
|
Name='OpenRA3'
|
||||||
|
Language='1033'
|
||||||
|
Version='@PROJECT_VERSION@'
|
||||||
|
Manufacturer='OpenRA3'
|
||||||
|
UpgradeCode='6C6B1F4E-0B9A-4C2E-9E2A-2E4D7B5C1A31'>
|
||||||
|
<Package Id='*' InstallerVersion='500' Compressed='yes' InstallScope='perMachine'/>
|
||||||
|
<Media Id='1' Cabinet='openra3.cab' EmbedCab='yes'/>
|
||||||
|
|
||||||
|
<Directory Id='TARGETDIR' Name='SourceDir'>
|
||||||
|
<Directory Id='ProgramFiles64Folder'>
|
||||||
|
<Directory Id='INSTALLFOLDER' Name='OpenRA3'>
|
||||||
|
<Component Id='MainExecutable' Guid='1F2E3D4C-5B6A-4798-8C1D-2E3F4A5B6C7D'>
|
||||||
|
<File Id='OpenRA3Exe' Name='openra3.exe' Source='$(var.exe)' KeyPath='yes'>
|
||||||
|
<Shortcut Id='StartMenuShortcut' Directory='ProgramMenuFolder' Name='OpenRA3'
|
||||||
|
WorkingDirectory='INSTALLFOLDER' Advertise='no'/>
|
||||||
|
</File>
|
||||||
|
</Component>
|
||||||
|
<Component Id='SdlRuntime' Guid='2A3B4C5D-6E7F-4809-9D2E-3F4A5B6C7D8E'>
|
||||||
|
<File Id='Sdl3Dll' Name='SDL3.dll' Source='$(var.dll)' KeyPath='yes'/>
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id='ProgramMenuFolder'/>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Feature Id='Main' Title='OpenRA3' Level='1'>
|
||||||
|
<ComponentRef Id='MainExecutable'/>
|
||||||
|
<ComponentRef Id='SdlRuntime'/>
|
||||||
|
</Feature>
|
||||||
|
</Product>
|
||||||
|
</Wix>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Cross-compile for Linux on ARM64 (aarch64-linux-gnu) from x86_64.
|
||||||
|
#
|
||||||
|
# clang-21 + libc++ (for `import std;`) with the arm64 libc++ and SDL3 taken
|
||||||
|
# from Ubuntu's arm64 packages (installed with dpkg multiarch), and the arm64
|
||||||
|
# glibc headers/libs from `gcc-aarch64-linux-gnu`. lld links the result; the C
|
||||||
|
# multiarch include directory is added explicitly because clang does not always
|
||||||
|
# infer Debian's `bits/` layout from the target triple alone.
|
||||||
|
|
||||||
|
# The `import std` gate must be set before the compiler is probed; a toolchain
|
||||||
|
# file is processed earlier than the project's own top-level settings.
|
||||||
|
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
|
||||||
|
|
||||||
|
set(CMAKE_SYSTEM_NAME Linux)
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||||
|
|
||||||
|
set(OPENRA3_AARCH64_TRIPLE "aarch64-linux-gnu")
|
||||||
|
# Multiarch paths the arm64 packages install into.
|
||||||
|
set(OPENRA3_AARCH64_INCLUDE "/usr/include/${OPENRA3_AARCH64_TRIPLE}")
|
||||||
|
set(OPENRA3_AARCH64_LIB "/usr/lib/${OPENRA3_AARCH64_TRIPLE}")
|
||||||
|
|
||||||
|
# Compiler + libc++ module description are cache vars so a distro with a
|
||||||
|
# differently-versioned clang (e.g. Debian's clang-19) can override on the
|
||||||
|
# command line.
|
||||||
|
set(OPENRA3_AARCH64_CC "clang-21" CACHE STRING "aarch64 C compiler")
|
||||||
|
set(OPENRA3_AARCH64_CXX "clang++-21" CACHE STRING "aarch64 C++ compiler")
|
||||||
|
set(OPENRA3_AARCH64_STDLIB_MODULES_JSON "/usr/lib/llvm-21/lib/libc++.modules.json" CACHE FILEPATH
|
||||||
|
"libc++ std module description for the aarch64 target")
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER ${OPENRA3_AARCH64_CC})
|
||||||
|
set(CMAKE_CXX_COMPILER ${OPENRA3_AARCH64_CXX})
|
||||||
|
set(CMAKE_C_COMPILER_TARGET ${OPENRA3_AARCH64_TRIPLE})
|
||||||
|
set(CMAKE_CXX_COMPILER_TARGET ${OPENRA3_AARCH64_TRIPLE})
|
||||||
|
# CMake's standard-library detection does not pass COMPILER_TARGET, so without
|
||||||
|
# this it probes the host standard library.
|
||||||
|
set(CMAKE_C_COMPILER_ARG1 "--target=${OPENRA3_AARCH64_TRIPLE}")
|
||||||
|
set(CMAKE_CXX_COMPILER_ARG1 "--target=${OPENRA3_AARCH64_TRIPLE}")
|
||||||
|
|
||||||
|
# libc++ standard-library module description for the aarch64 target. On a
|
||||||
|
# multiarch image only the arm64 libc++ is installed, so the standard path
|
||||||
|
# already resolves to the aarch64 module description.
|
||||||
|
set(CMAKE_CXX_STDLIB_MODULES_JSON "${OPENRA3_AARCH64_STDLIB_MODULES_JSON}")
|
||||||
|
|
||||||
|
# Strip with the cross binutils, so CPack can strip the arm64 binary.
|
||||||
|
set(CMAKE_STRIP aarch64-linux-gnu-strip)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS_INIT "-isystem ${OPENRA3_AARCH64_INCLUDE}")
|
||||||
|
set(CMAKE_CXX_FLAGS_INIT "-stdlib=libc++ -isystem ${OPENRA3_AARCH64_INCLUDE}")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_INIT "-stdlib=libc++ -fuse-ld=lld -L${OPENRA3_AARCH64_LIB}")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-stdlib=libc++ -fuse-ld=lld -L${OPENRA3_AARCH64_LIB}")
|
||||||
|
|
||||||
|
# Run arm64 test binaries through qemu when it is installed, so `ctest` works
|
||||||
|
# during the image build; harmless when the tests are only cross-compiled.
|
||||||
|
set(CMAKE_CROSSCOMPILING_EMULATOR qemu-aarch64-static)
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Cross-compile for Windows on ARM64 (aarch64-w64-mingw32) with llvm-mingw:
|
||||||
|
# clang + libc++ + the libc++ `std` module, targeting the MSVCRT MinGW runtime.
|
||||||
|
# The same llvm-mingw release is multi-target, so only the target triple, the
|
||||||
|
# SDL3 SDK and the sysroot paths differ from the x86_64 toolchain.
|
||||||
|
|
||||||
|
# The `import std` gate must be set before the compiler is probed; a toolchain
|
||||||
|
# file is processed earlier than the project's own top-level settings.
|
||||||
|
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
|
||||||
|
|
||||||
|
set(CMAKE_SYSTEM_NAME Windows)
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||||
|
|
||||||
|
set(OPENRA3_LLVM_MINGW_ROOT "/opt/llvm-mingw" CACHE PATH "llvm-mingw installation root")
|
||||||
|
if(NOT EXISTS "${OPENRA3_LLVM_MINGW_ROOT}/bin/clang++")
|
||||||
|
message(FATAL_ERROR "llvm-mingw not found at ${OPENRA3_LLVM_MINGW_ROOT} (expected bin/clang++)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# llvm-mingw's per-target prefix/sysroot for the ARM64 target.
|
||||||
|
set(OPENRA3_MINGW_TRIPLE "aarch64-w64-mingw32")
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER "${OPENRA3_LLVM_MINGW_ROOT}/bin/clang")
|
||||||
|
set(CMAKE_CXX_COMPILER "${OPENRA3_LLVM_MINGW_ROOT}/bin/clang++")
|
||||||
|
set(CMAKE_C_COMPILER_TARGET ${OPENRA3_MINGW_TRIPLE})
|
||||||
|
set(CMAKE_CXX_COMPILER_TARGET ${OPENRA3_MINGW_TRIPLE})
|
||||||
|
# CMake's standard-library detection does not pass COMPILER_TARGET, so without
|
||||||
|
# this it probes the host standard library. Putting the target in ARG1 makes
|
||||||
|
# the probe see libc++ (the MinGW target's library).
|
||||||
|
set(CMAKE_C_COMPILER_ARG1 "--target=${OPENRA3_MINGW_TRIPLE}")
|
||||||
|
set(CMAKE_CXX_COMPILER_ARG1 "--target=${OPENRA3_MINGW_TRIPLE}")
|
||||||
|
|
||||||
|
# libc++ standard-library module description for the MinGW target. Must be set
|
||||||
|
# before the compiler is probed so CMake can build `import std;`.
|
||||||
|
set(CMAKE_CXX_STDLIB_MODULES_JSON "${OPENRA3_LLVM_MINGW_ROOT}/${OPENRA3_MINGW_TRIPLE}/lib/libc++.modules.json")
|
||||||
|
|
||||||
|
# Select libc++ explicitly so CMake's `import std` detection recognises the
|
||||||
|
# standard library (llvm-mingw defaults to it, but CMake keys off -stdlib=).
|
||||||
|
set(CMAKE_CXX_FLAGS_INIT "-stdlib=libc++")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_INIT "-stdlib=libc++")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-stdlib=libc++")
|
||||||
|
|
||||||
|
# SDL3 for aarch64-w64-mingw32. The official SDL release ships no MinGW ARM64
|
||||||
|
# dev package, so the image stages one from MSYS2's clangarm64 repo under this
|
||||||
|
# root in the same `<triple>/{include,lib,bin}` layout.
|
||||||
|
set(OPENRA3_SDL3_ROOT "/opt/sdl3-aarch64" CACHE PATH "SDL3 aarch64 MinGW development package root")
|
||||||
|
set(OPENRA3_SDL3_MINGW_TRIPLE "${OPENRA3_MINGW_TRIPLE}" CACHE STRING "SDL3 MinGW triple subdirectory")
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH "${OPENRA3_LLVM_MINGW_ROOT}/${OPENRA3_MINGW_TRIPLE}")
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Cross-compile for Windows x86_64 with llvm-mingw: clang + libc++ + the
|
||||||
|
# libc++ `std` module, targeting the MSVCRT MinGW runtime (which is what the
|
||||||
|
# official SDL3 MinGW package links against).
|
||||||
|
|
||||||
|
# The `import std` gate must be set before the compiler is probed; a toolchain
|
||||||
|
# file is processed earlier than the project's own top-level settings.
|
||||||
|
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
|
||||||
|
|
||||||
|
set(CMAKE_SYSTEM_NAME Windows)
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||||
|
|
||||||
|
set(OPENRA3_LLVM_MINGW_ROOT "/opt/llvm-mingw" CACHE PATH "llvm-mingw installation root")
|
||||||
|
if(NOT EXISTS "${OPENRA3_LLVM_MINGW_ROOT}/bin/clang++")
|
||||||
|
message(FATAL_ERROR "llvm-mingw not found at ${OPENRA3_LLVM_MINGW_ROOT} (expected bin/clang++)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER "${OPENRA3_LLVM_MINGW_ROOT}/bin/clang")
|
||||||
|
set(CMAKE_CXX_COMPILER "${OPENRA3_LLVM_MINGW_ROOT}/bin/clang++")
|
||||||
|
set(CMAKE_C_COMPILER_TARGET x86_64-w64-mingw32)
|
||||||
|
set(CMAKE_CXX_COMPILER_TARGET x86_64-w64-mingw32)
|
||||||
|
# CMake's standard-library detection does not pass COMPILER_TARGET, so without
|
||||||
|
# this it probes the host standard library. Putting the target in ARG1 makes
|
||||||
|
# the probe see libc++ (the MinGW target's library).
|
||||||
|
set(CMAKE_C_COMPILER_ARG1 "--target=x86_64-w64-mingw32")
|
||||||
|
set(CMAKE_CXX_COMPILER_ARG1 "--target=x86_64-w64-mingw32")
|
||||||
|
|
||||||
|
# libc++ standard-library module description for the MinGW target. Must be set
|
||||||
|
# before the compiler is probed so CMake can build `import std;`.
|
||||||
|
set(CMAKE_CXX_STDLIB_MODULES_JSON "${OPENRA3_LLVM_MINGW_ROOT}/x86_64-w64-mingw32/lib/libc++.modules.json")
|
||||||
|
|
||||||
|
# Select libc++ explicitly so CMake's `import std` detection recognises the
|
||||||
|
# standard library (llvm-mingw defaults to it, but CMake keys off -stdlib=).
|
||||||
|
set(CMAKE_CXX_FLAGS_INIT "-stdlib=libc++")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_INIT "-stdlib=libc++")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-stdlib=libc++")
|
||||||
|
|
||||||
|
# Official SDL3 MinGW development package (headers + import library + DLL).
|
||||||
|
set(OPENRA3_SDL3_ROOT "/opt/sdl3-mingw" CACHE PATH "SDL3 MinGW development package root")
|
||||||
|
# The triple-named subdirectory inside the SDL3 MinGW package.
|
||||||
|
set(OPENRA3_SDL3_MINGW_TRIPLE "x86_64-w64-mingw32" CACHE STRING "SDL3 MinGW triple subdirectory")
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH "${OPENRA3_LLVM_MINGW_ROOT}/x86_64-w64-mingw32")
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
+764
-70
@@ -1,93 +1,787 @@
|
|||||||
# Architecture
|
# Architecture & Master Plan
|
||||||
|
|
||||||
OpenRA3 is organised as a stack of C++ modules. Each layer may import the ones
|
OpenRA3 is a from-scratch, portable re-implementation of **Command & Conquer:
|
||||||
below it, never the ones above.
|
Red Alert 3** (SAGE 2.0) in **pure C++26** — C++ modules, `import std;`, no
|
||||||
|
scripting language, no managed runtime, no other programming language anywhere
|
||||||
|
in the tree. It is built with Clang + libc++ on Linux and cross-compiled to
|
||||||
|
Windows with llvm-mingw.
|
||||||
|
|
||||||
|
This document is two things at once:
|
||||||
|
|
||||||
|
1. the **layer architecture** (how the modules sit on top of one another), and
|
||||||
|
2. the **master plan** — the complete Red Alert 3 feature set decomposed three
|
||||||
|
levels deep into **Module → Function → Feature**, each tagged with its
|
||||||
|
implementation status and target milestone.
|
||||||
|
|
||||||
|
The decomposition is the contract: every feature RA3 has is either implemented,
|
||||||
|
being implemented, or explicitly planned here. Nothing is silently dropped.
|
||||||
|
|
||||||
|
> **Scope.** Offline only. Single-player campaign, skirmish, Commander's
|
||||||
|
> Challenge and LAN lockstep — never an online service, matchmaking, EA account
|
||||||
|
> or GameSpy/Steam integration. The simulation is deterministic and
|
||||||
|
> self-contained. No game assets or binaries are shipped; the engine reads the
|
||||||
|
> user's own install at runtime.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Principles
|
||||||
|
|
||||||
|
Same five rules as the rest of the tree, restated because the plan is written
|
||||||
|
against them:
|
||||||
|
|
||||||
|
1. **Pure C++.** One language. No Lua, no JS, no C#, no Python at runtime.
|
||||||
|
Python is allowed *only* in offline build tooling (`tools/`, cmake helper
|
||||||
|
scripts), never linked into `openra3`.
|
||||||
|
2. **No raw owning pointers.** Ownership is `std::unique_ptr`; cross-references
|
||||||
|
are non-owning views (`thing *`, handles, indices).
|
||||||
|
3. **Portable simulation.** No platform API in the foundation or simulation
|
||||||
|
layers. All platform concerns live behind `display` / `audio` / `video`.
|
||||||
|
4. **Determinism.** Anything that can diverge between runs (RNG, iteration
|
||||||
|
order, float accumulation, hash order) is explicit, seeded and testable.
|
||||||
|
5. **RE-traceable.** Every structure or constant taken from the retail
|
||||||
|
`ra3_1.12.game` (image base `0x400000`) or from the GPLv3 SAGE 1.0 reference
|
||||||
|
cites its origin. No asserted fact without a source.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Layer architecture
|
||||||
|
|
||||||
|
Each layer may import the ones below it, never the ones above. The `ra3`
|
||||||
|
umbrella module re-exports the SDK; applications import `ra3` only.
|
||||||
|
|
||||||
```
|
```
|
||||||
┌───────────────────────────────────────┐
|
L6 tooling / apps openra3 (CLI) tools/ (offline, Python allowed)
|
||||||
applications │ openra3 (apps/openra3) │
|
| import ra3
|
||||||
└───────────────────┬───────────────────┘
|
---------------------------------------------------------------------------
|
||||||
│ import ra3
|
v
|
||||||
┌───────────────────▼───────────────────┐
|
L5 meta ra3.i18n ra3.mod ra3.net (deferred)
|
||||||
umbrella │ ra3 (re-exports everything) │
|
|
|
||||||
└───────────────────┬───────────────────┘
|
L4 match services ra3.match ra3.replay ra3.save
|
||||||
┌──────────────┬───────────────┼───────────────┬──────────────┐
|
|
|
||||||
▼ ▼ ▼ ▼ ▼
|
L3 presentation ra3.client ── ra3.render ── ra3.audio ── ra3.video
|
||||||
ra3.ui ra3.render ra3.skirmish ra3.client ra3.game
|
| \ |
|
||||||
SDL3 window framebuffer match rules display/loop RA3 sides
|
| \ v
|
||||||
│ │ │ │ │
|
| ra3.display (backend pick)
|
||||||
└──────┬───────┘ └───────┬───────┘ │
|
| / | \
|
||||||
▼ ▼ │
|
v ra3.ui ra3.vulkan ra3.dx
|
||||||
ra3.core ra3.logic │
|
| (SDL3) (Vulkan) (D3D11/12)
|
||||||
simulation │
|
---------------------------------------------------------------------------
|
||||||
┌───────────────────────────────────────┬──────────────────────┘
|
v
|
||||||
▼ ▼
|
L2 simulation ra3.logic ra3.modules ra3.combat ra3.movement
|
||||||
ra3.map ra3.fs
|
ra3.economy ra3.ai ra3.script ra3.powers ra3.shroud
|
||||||
map catalog BIG4 + RefPack
|
|
|
||||||
|
L1 data & assets ra3.data ── ra3.assets ── ra3.map ── ra3.terrain
|
||||||
|
| |
|
||||||
|
v v
|
||||||
|
ra3.fs (BIG4 / RefPack / install)
|
||||||
|
|
|
||||||
|
L0 foundation ra3.core (types, math, containers, RNG, message bus)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Module responsibilities
|
The current concrete modules (`ra3.core`, `ra3.logic`, `ra3.data`,
|
||||||
|
`ra3.skirmish`, `ra3.fs`, `ra3.map`, `ra3.terrain`, `ra3.render`, `ra3.ui.*`,
|
||||||
|
`ra3.vulkan.*`, `ra3.dx.*`, `ra3.display`, `ra3.game`, `ra3.client`, and the
|
||||||
|
vendored `ender.log`) are the **seeds** of the
|
||||||
|
target modules below. `ra3.skirmish` and `ra3.game` will be absorbed into
|
||||||
|
`ra3.ai` / `ra3.match`; new modules are added as their subsystems are recovered.
|
||||||
|
|
||||||
### `ra3.core`
|
### Status legend
|
||||||
The vocabulary every other module shares, mirroring SAGE's `GameEngine/Common`:
|
|
||||||
`real`/`int32`/`uint32`, `coord3d`/`coord2d`/`rgb_color`, `ascii_string` +
|
|
||||||
`make_name_key`, the deterministic `random` stream, and the `message_stream`
|
|
||||||
command bus (node layout derived from retail `MessageStream::appendMessage`,
|
|
||||||
`0x0060c4a0`).
|
|
||||||
|
|
||||||
### `ra3.logic`
|
| Tag | Meaning |
|
||||||
The deterministic simulation, mirroring SAGE's `GameLogic`: `thing` → `object`
|
| --- | --- |
|
||||||
with pluggable `update_module`s, `player`/`player_list`, the `partition_manager`
|
| `[x]` | implemented and tested on `main` |
|
||||||
spatial grid, and the 30 Hz `game_logic` driver (`prepare_new_game` /
|
| `[~]` | partially implemented / works for the happy path |
|
||||||
`start_new_game` / `update`).
|
| `[ ]` | planned, not started |
|
||||||
|
| `(vX.Y)` | target milestone (see the roll-up in §5) |
|
||||||
|
| `!!` | needs reverse engineering before it can be built |
|
||||||
|
|
||||||
### `ra3.client`
|
| Function tag | Meaning |
|
||||||
The presentation boundary: an abstract `display` with a `headless_display`
|
| --- | --- |
|
||||||
implementation, and `game_client`, the seam a future W3D/D3D9 renderer plugs
|
| `D` | **done** — the function's features are largely present |
|
||||||
into.
|
| `P` | **partial** — some features present |
|
||||||
|
|
||||||
### `ra3.game`
|
---
|
||||||
Red Alert 3 data that SAGE keeps in `PlayerTemplate`: the three sides
|
|
||||||
(`faction` flags `Empire=2`, `Allied=4`, `Soviet=8`, `Random=7`, recovered from
|
|
||||||
the retail skirmish setup) and skirmish defaults.
|
|
||||||
|
|
||||||
### `ra3.fs`
|
## 3. Master plan — Module → Function → Feature
|
||||||
Reading the user's installation. Implements the `BIG4` archive container and
|
|
||||||
EA's RefPack codec, plus `find_game_dir` (`--game-dir` / `$RA3_GAME_DIR` /
|
|
||||||
`C:\Red Alert 3`). Only the archive index is held in memory; payloads are read
|
|
||||||
on demand.
|
|
||||||
|
|
||||||
### `ra3.map`
|
### M00 `ra3.core` — foundation vocabulary `[D]`
|
||||||
Map discovery and loading: scans `MapsMultiplayer.big` for main map entries,
|
|
||||||
unwraps the two compression layers (`BIG4` RefPack → `EAR\0` wrapper → RefPack →
|
|
||||||
`CkMp`), and recovers `Player_N_Start` waypoint coordinates. Degenerate
|
|
||||||
extractions are rejected so the caller can fall back.
|
|
||||||
|
|
||||||
### `ra3.skirmish`
|
Mirrors SAGE `GameEngine/Common`. Everything else speaks this.
|
||||||
The minimal match: two players, unit classes (harvester/infantry/tank/base),
|
|
||||||
passive + harvester income, a simple build AI, movement and combat on a fixed
|
|
||||||
30 Hz step, and a base-destruction win condition. Fully deterministic.
|
|
||||||
|
|
||||||
### `ra3.render`
|
- **F1 Types & math** `[D]`
|
||||||
A dependency-free software renderer: an ARGB8888 `image` framebuffer with
|
- `[x]` `real` (float), `int32`/`uint32`/`uint16`/`uint8`, `bool` aliases
|
||||||
blit/line/circle primitives, a TGA decoder for the game's map art, a 24-bit BMP
|
- `[x]` `coord2d` / `coord3d` vectors, dot/cross/length/normalize
|
||||||
encoder for headless output, and `compose` which overlays a world grid and
|
- `[x]` `rgb_color` / `argb_color`, packing and lerp
|
||||||
markers (start positions, live units) onto the map art.
|
- `[x]` geometry: `segment`, `triangle`, `plane`, ray/segment intersection
|
||||||
|
- `[ ]` fixed-point helpers for replay-stable accumulation `(v0.4)`
|
||||||
|
- `[ ]` matrix / quaternion (needed by W3D models) `(v0.6)`
|
||||||
|
- `[ ]` `KindOf` flag bitset (SAGE object taxonomy) `(v0.4)`
|
||||||
|
- **F2 Containers** `[D]`
|
||||||
|
- `[x]` `ascii_string` + `make_name_key` (case-folded hash for data lookup)
|
||||||
|
- `[x]` intrusive doubly linked list (message stream node layout)
|
||||||
|
- `[x]` deterministic iteration-order map (insertion-ordered)
|
||||||
|
- `[ ]` object pool / arena for per-frame allocations `(v0.4)`
|
||||||
|
- `[ ]` interned string table `(v0.5)`
|
||||||
|
- **F3 Deterministic RNG** `[D]`
|
||||||
|
- `[x]` seedable `random` stream (`game_logic` random)
|
||||||
|
- `[ ]` independent per-player / per-subsystem streams `(v0.4)`
|
||||||
|
- `[ ]` shuffle / weighted-pick primitives `(v0.4)`
|
||||||
|
- **F4 Message stream** `[D]`
|
||||||
|
- `[x]` command bus with retail node layout (`appendMessage` `0x0060c4a0`)
|
||||||
|
- `[x]` ordered per-frame command drain
|
||||||
|
- `[ ]` command argument blocks (build/target/waypoint payloads) `(v0.4)`
|
||||||
|
- `[ ]` network/replay source tagging `(v0.4)`
|
||||||
|
- **F5 Diagnostics** `[~]`
|
||||||
|
- `[x]` logging sink + assert macro
|
||||||
|
- `[ ]` scoped profiling timers, per-subsystem counters `(v0.4)`
|
||||||
|
- `[ ]` structured crash/report capture `(v0.8)`
|
||||||
|
- **F6 Serialization primitives** `[~]`
|
||||||
|
- `[x]` little-endian byte reader/writer
|
||||||
|
- `[ ]` chunked binary reader/writer with versioning `(v0.5)`
|
||||||
|
- `[ ]` stable content hashing (replay/desync checks) `(v0.4)`
|
||||||
|
- **F7 Localization primitives** `[~]` (see M26)
|
||||||
|
- `[x]` UTF-16 unit access, CSF low-byte `^0xFF` decode
|
||||||
|
- `[ ]` placeholder substitution, plural/gender rules `(v0.5)`
|
||||||
|
|
||||||
### `ra3.ui`
|
### M01 `ra3.fs` — containers & install `[D]`
|
||||||
The windowed viewer. The SDL3 backend streams the rendered image to a texture
|
|
||||||
with pan/zoom and Esc-to-quit; when the build has no SDL3, a null backend
|
|
||||||
returns `false` so the caller writes an offscreen image instead.
|
|
||||||
|
|
||||||
## Design rules
|
- **F1 BIG archive** `[D]`
|
||||||
|
- `[x]` `BIG4` header parse (`fileSize` LE, `fileCount`/offsets BE) + name index
|
||||||
|
- `[x]` payload read on demand (index-only resident memory)
|
||||||
|
- `[ ]` `BIGF` (RefPack whole-archive) variant `!!` `(v0.5)`
|
||||||
|
- `[ ]` write support (pack/repack, used by tooling) `(v0.8)`
|
||||||
|
- **F2 RefPack codec** `[D]`
|
||||||
|
- `[x]` decode: 2/3/4-byte commands, long-literal, stop opcode
|
||||||
|
- `[x]` `refpack_output_size` without full decompress
|
||||||
|
- `[ ]` encode (for tooling round-trips) `(v0.8)`
|
||||||
|
- **F3 Install locator** `[D]`
|
||||||
|
- `[x]` `--game-dir` / `$RA3_GAME_DIR` / `C:\Red Alert 3` defaults
|
||||||
|
- `[ ]` registry / Steam / EA-app discovery `(v0.5)`
|
||||||
|
- `[ ]` Uprising as an optional content source `(v0.5)`
|
||||||
|
- **F4 Virtual file system** `[~]`
|
||||||
|
- `[x]` layered archive mounts (loose files → `*.big`)
|
||||||
|
- `[ ]` patch/language precedence rules (newest `Lang-*.big` wins) `(v0.5)`
|
||||||
|
- `[ ]` case-insensitive lookup + path normalisation `(v0.5)`
|
||||||
|
- **F5 Extraction targets** `[D]`
|
||||||
|
- `[x]` `openra3 extract` dumps maps/terrain to `assets/`
|
||||||
|
- `[ ]` full asset dump via `ra3tools` integration as a build target `(v0.5)`
|
||||||
|
|
||||||
|
### M02 `ra3.data` — data schema & balance `[P]`
|
||||||
|
|
||||||
|
- **F1 Data schema** `[ ]` `!!`
|
||||||
|
- `[ ]` SAGE INI parser (`#include`, `#define`, inheritance) `(v0.5)`
|
||||||
|
- `[ ]` XML rule schema
|
||||||
|
- `[ ]` `.manifest` compiled-blob schema `!!` `(v0.5)`
|
||||||
|
- **F2 Compiled asset blobs** `[ ]` `!!`
|
||||||
|
- `[ ]` `global.bin` deserialisation `(v0.5)`
|
||||||
|
- `[ ]` `static.*.bin` deserialisation `(v0.5)`
|
||||||
|
- `[ ]` version/dependency validation `(v0.5)`
|
||||||
|
- **F3 Balance tables** `[~]`
|
||||||
|
- `[x]` damage types, `ArmorTemplate` percentages
|
||||||
|
- `[x]` weapon target masks, weapon definitions
|
||||||
|
- `[x]` unit/structure build costs, times, prerequisites
|
||||||
|
- `[x]` ore economy constants
|
||||||
|
- `[ ]` read these from the install instead of pinned constants `(v0.5)`
|
||||||
|
- **F4 Object definitions** `[ ]`
|
||||||
|
- `[ ]` `ThingTemplate`/`ObjectTemplate` inheritance graph `(v0.5)`
|
||||||
|
- `[ ]` module descriptor lists (per-object update/draw module sets)
|
||||||
|
- `[ ]` `WeaponTemplate`/`ArmorTemplate`/`LocomotorTemplate` stores
|
||||||
|
- **F5 Faction & player templates** `[~]`
|
||||||
|
- `[x]` Allied / Soviet / Empire side flags (`2/4/8`), match templates
|
||||||
|
- `[ ]` commander/sub-commander definitions `(v0.7)`
|
||||||
|
- `[ ]` build/upgrade unlock trees per faction `(v0.5)`
|
||||||
|
- **F6 Rules & settings** `[ ]`
|
||||||
|
- `[ ]` skirmish options (cash, crates, superweapons, speed, limits)
|
||||||
|
- `[ ]` bonus crate effect table
|
||||||
|
- `[ ]` AI personality tables
|
||||||
|
- `[ ]` map-specific rule overrides (`map.ini`)
|
||||||
|
- **F7 Validation** `[ ]`
|
||||||
|
- `[ ]` schema diagnostics with source location
|
||||||
|
- `[ ]` cross-reference integrity (missing templates/refs)
|
||||||
|
|
||||||
|
### M03 `ra3.assets` — runtime asset manager `[ ]`
|
||||||
|
|
||||||
|
- **F1 Textures** `[~]`
|
||||||
|
- `[x]` TGA decode (`ra3.render`); 256×256 terrain cells
|
||||||
|
- `[ ]` DDS / DXT compressed textures `(v0.5)`
|
||||||
|
- `[ ]` atlas + mip generation, gutter padding (GPU) `[~]`
|
||||||
|
- `[ ]` async upload / streaming `(v0.6)`
|
||||||
|
- **F2 Models** `[ ]` `!!`
|
||||||
|
- `[ ]` W3D container parse (chunks, hierarchy, meshes) `(v0.6)`
|
||||||
|
- `[ ]` materials, shaders, texture references
|
||||||
|
- `[ ]` LOD sets, collision meshes
|
||||||
|
- **F3 Animation** `[ ]` `!!`
|
||||||
|
- `[ ]` W3D animation chunks, bone poses `(v0.6)`
|
||||||
|
- `[ ]` blend trees / transition animations
|
||||||
|
- **F4 Audio** `[ ]` `!!`
|
||||||
|
- `[ ]` audio container + codec decode `(v0.7)`
|
||||||
|
- `[ ]` cue/event tables (unit responses, weapon foley)
|
||||||
|
- **F5 Fonts & glyphs** `[ ]`
|
||||||
|
- `[ ]` bitmap/vector font load; CJK coverage `(v0.5)`
|
||||||
|
- **F6 Asset registry** `[ ]`
|
||||||
|
- `[ ]` cache with ref counting, eviction `(v0.6)`
|
||||||
|
- `[ ]` name-key lookup into the data schema
|
||||||
|
- **F7 UI art (`.apt`)** `[ ]` `!!`
|
||||||
|
- `[ ]` RA3 interface art decode (HUD/command bar) `(v0.7)`
|
||||||
|
|
||||||
|
### M04 `ra3.map` — map catalog & loader `[D]`
|
||||||
|
|
||||||
|
- **F1 Catalog** `[D]`
|
||||||
|
- `[x]` scan `MapsMultiplayer.big` for main map entries
|
||||||
|
- `[ ]` campaign + challenge map catalog `(v0.7)`
|
||||||
|
- **F2 Compiled map (`CkMp`)** `[D]`
|
||||||
|
- `[x]` double unwrap (`BIG4` RefPack → `EAR\0` → RefPack → `CkMp`)
|
||||||
|
- `[x]` name table + chunk list (`{index,version,size,data}`)
|
||||||
|
- `[ ]` full typed chunk dispatch for all chunk kinds `(v0.5)`
|
||||||
|
- **F3 Start positions** `[~]`
|
||||||
|
- `[x]` `Player_N_Start` waypoint scan → `coord3d`
|
||||||
|
- `[ ]` `MPPositionList` layout for maps without waypoints `!!` `(v0.5)`
|
||||||
|
- **F4 Metadata** `[~]`
|
||||||
|
- `[x]` display names from `gamestrings.csf` (`MAP:<ID>`)
|
||||||
|
- `[ ]` player count, size, supported game modes `(v0.5)`
|
||||||
|
- **F5 Map rules** `[ ]`
|
||||||
|
- `[ ]` `map.ini` override application `(v0.5)`
|
||||||
|
- **F6 Preview art** `[~]`
|
||||||
|
- `[x]` `<map>_art.tga` overview decode (`--thumbnail`)
|
||||||
|
- **F7 Validation & fallback** `[D]`
|
||||||
|
- `[x]` reject degenerate extractions, fall back to built-in test map
|
||||||
|
- `[ ]` integrity/version checks with actionable errors `(v0.5)`
|
||||||
|
|
||||||
|
### M05 `ra3.terrain` — terrain `[P]`
|
||||||
|
|
||||||
|
- **F1 Heightmap** `[D]`
|
||||||
|
- `[x]` `HeightMapData` v6 (grid, border, `u16` elevations, scale)
|
||||||
|
- `[ ]` multi-resolution / LOD height sampling `(v0.6)`
|
||||||
|
- **F2 Blend tiles** `[D]`
|
||||||
|
- `[x]` `BlendTileData` v27: tile grid, blends/three-way/cliff tables
|
||||||
|
- `[x]` `BlendDescription` parse; `secondaryTile` decode
|
||||||
|
- `[x]` retail `Terrain.fx` blend ramp (`blend_factor`, axis flags)
|
||||||
|
- **F3 Terrain textures** `[D]`
|
||||||
|
- `[x]` `art\terrain\*.tga` from `Terrain.big` / `Core11.big`
|
||||||
|
- `[x]` cell atlas with replicated gutter (GPU bleeding fix)
|
||||||
|
- `[ ]` continuous 32 px / Morton layout (kill residual grid lines) `(v0.4)`
|
||||||
|
- **F4 Water** `[ ]` `!!`
|
||||||
|
- `[ ]` water height/type, sea level `(v0.6)`
|
||||||
|
- `[ ]` animated waves + shoreline blending `(v0.6)`
|
||||||
|
- `[ ]` shroud-aware water rendering `(v0.6)`
|
||||||
|
- **F5 Cliffs & roads** `[ ]` `!!`
|
||||||
|
- `[x]` `CliffTextures` table parsed
|
||||||
|
- `[ ]` cliff mesh + `CliffTextureMapping` UV remap `(v0.6)`
|
||||||
|
- `[ ]` roads and bridges (passability + render) `(v0.6)`
|
||||||
|
- **F6 Terrain lighting** `[ ]`
|
||||||
|
- `[ ]` per-vertex normals, cell lighting, global light `(v0.6)`
|
||||||
|
- **F7 Terrain queries** `[~]`
|
||||||
|
- `[x]` height lookup (render)
|
||||||
|
- `[ ]` passability grid (ground/naval/amphibious/air) `(v0.4)`
|
||||||
|
- `[ ]` buildability grid (flatness, slope, water) `(v0.4)`
|
||||||
|
|
||||||
|
### M06 `ra3.logic` — simulation core `[P]`
|
||||||
|
|
||||||
|
- **F1 Objects & things** `[~]`
|
||||||
|
- `[x]` `thing` → `object` with pluggable `update_module`s
|
||||||
|
- `[x]` global object registry with stable ids
|
||||||
|
- `[ ]` handle/reference system (survives deletion) `(v0.4)`
|
||||||
|
- `[ ]` object destruction lifecycle + death dispatch `(v0.4)`
|
||||||
|
- **F2 Players & teams** `[~]`
|
||||||
|
- `[x]` `player` / `player_list`
|
||||||
|
- `[x]` money (`std::vector<Money*>`) and power fields
|
||||||
|
- `[x]` team assignment and relations
|
||||||
|
- `[ ]` diplomacy matrix, ally vision sharing `(v0.5)`
|
||||||
|
- **F3 Partition manager** `[~]`
|
||||||
|
- `[x]` spatial grid, neighborhood queries
|
||||||
|
- `[ ]` cell-resolution + large-object multi-cell registration `(v0.4)`
|
||||||
|
- **F4 Game loop** `[~]`
|
||||||
|
- `[x]` 30 Hz deterministic step with frame counter
|
||||||
|
- `[x]` `prepare_new_game` / `start_new_game` two-phase start
|
||||||
|
- `[ ]` per-tick module scheduling with stable ordering `(v0.4)`
|
||||||
|
- **F5 Commands** `[~]`
|
||||||
|
- `[x]` `message_stream` bus
|
||||||
|
- `[ ]` typed commands (build, attack, move, ability, sell, repair) `(v0.4)`
|
||||||
|
- `[ ]` command validation + feedback (insufficient funds, etc.) `(v0.4)`
|
||||||
|
- **F6 Determinism** `[~]`
|
||||||
|
- `[x]` seeded logic random
|
||||||
|
- `[ ]` replay-hash of state per tick `(v0.4)`
|
||||||
|
- `[ ]` float determinism policy / fixed-point where required `(v0.4)`
|
||||||
|
- **F7 Victory / defeat** `[~]`
|
||||||
|
- `[x]` team-wipe / base-destruction win condition
|
||||||
|
- `[ ]` surrender, disconnection, timed, objective victories `(v0.5)`
|
||||||
|
- `[ ]` score / stats accumulation `(v0.8)`
|
||||||
|
|
||||||
|
### M07 `ra3.modules` — object update & draw modules `[P]`
|
||||||
|
|
||||||
|
- **F1 Module system** `[~]`
|
||||||
|
- `[x]` update modules attached to objects with a simple order
|
||||||
|
- `[ ]` module descriptor data-binding (from `ThingTemplate`) `(v0.5)`
|
||||||
|
- `[ ]` interface queries (get WeaponModule / ContainModule on demand) `(v0.4)`
|
||||||
|
- **F2 Locomotor** `[ ]`
|
||||||
|
- `[ ]` movement state machine (idle/moving/attacking) `(v0.4)`
|
||||||
|
- **F3 Weapon module** `[~]`
|
||||||
|
- `[x]` simple weapons on units, auto-target + fire
|
||||||
|
- `[ ]` multi-weapon slots, turret aiming/rotation `(v0.4)`
|
||||||
|
- `[ ]` reload/clip, deploy/undeploy states `(v0.6)`
|
||||||
|
- **F4 Contain** `[ ]`
|
||||||
|
- `[ ]` transport passenger slots, load/unload `(v0.4)`
|
||||||
|
- `[ ]` garrison of civilian structures `(v0.6)`
|
||||||
|
- `[ ]` paradrop / airdrop `(v0.6)`
|
||||||
|
- **F5 Production** `[~]`
|
||||||
|
- `[x]` pay-as-you-go build queue on a factory
|
||||||
|
- `[ ]` per-factory queues, rally points, queue reordering `(v0.4)`
|
||||||
|
- `[ ]` building placement → production handoff `(v0.4)`
|
||||||
|
- **F6 Power** `[~]`
|
||||||
|
- `[x]` power production/consumption fields
|
||||||
|
- `[ ]` brownout/blackout effects on radar + build speed `(v0.4)`
|
||||||
|
- **F7 Upgrades** `[ ]`
|
||||||
|
- `[ ]` upgrade research, unlock dependent modules/weapons `(v0.5)`
|
||||||
|
- **F8 Experience / veterancy** `[ ]`
|
||||||
|
- `[ ]` XP from kills, ranks (veteran/elite/heroic), bonuses `(v0.6)`
|
||||||
|
- `[ ]` chevron rendering `(v0.6)`
|
||||||
|
- **F9 Special abilities** `[~]`
|
||||||
|
- `[ ]` secondary ability slots with cooldown, target/area types `(v0.6)`
|
||||||
|
- `[ ]` toggle/instant/targeted ability kinds `(v0.6)`
|
||||||
|
- **F10 Stealth / disguise / detection** `[ ]`
|
||||||
|
- `[ ]` stealth states, detection radius, decloak on fire `(v0.6)`
|
||||||
|
- `[ ]` disguise (spy-like) and detection interaction `(v0.6)`
|
||||||
|
- **F11 Structure modules** `[~]`
|
||||||
|
- `[x]` base structures, destruction
|
||||||
|
- `[ ]` construction/assembly animation, sell, repair `(v0.4)`
|
||||||
|
- `[ ]` walls/gates if present, defensive structures `(v0.6)`
|
||||||
|
- **F12 Resource modules** `[~]`
|
||||||
|
- `[x]` harvester ↔ refinery ore cycle
|
||||||
|
- `[ ]` ore field spread/depletion, multiple miners, dock queue `(v0.4)`
|
||||||
|
- **F13 Shroud modules** `[ ]`
|
||||||
|
- `[ ]` per-object shroud reveal / clearance `(v0.4)`
|
||||||
|
- **F14 Draw modules** `[ ]`
|
||||||
|
- `[ ]` model draw, animation, particles, construction ghost, temp effects `(v0.6)`
|
||||||
|
|
||||||
|
### M08 `ra3.combat` — weapons, warheads, damage `[P]`
|
||||||
|
|
||||||
|
- **F1 Weapons** `[~]`
|
||||||
|
- `[x]` damage, range, rate of fire, target masks
|
||||||
|
- `[ ]` clip/burst, scatter, arc, continuous beam `(v0.4)`
|
||||||
|
- `[ ]` primary vs secondary weapon selection `(v0.4)`
|
||||||
|
- **F2 Warheads** `[~]`
|
||||||
|
- `[x]` damage type + armor multiplier resolution
|
||||||
|
- `[ ]` radius/falloff, affects mask, death type on kill `(v0.4)`
|
||||||
|
- **F3 Armor** `[~]`
|
||||||
|
- `[x]` `ArmorTemplate` percentage table
|
||||||
|
- `[ ]` armor upgrades and per-state armor `(v0.6)`
|
||||||
|
- **F4 Damage application** `[~]`
|
||||||
|
- `[x]` damage resolution against armor
|
||||||
|
- `[ ]` conditional modifiers (from above, in air, moving) `(v0.4)`
|
||||||
|
- `[ ]` friendly-fire policy, self-damage `(v0.4)`
|
||||||
|
- **F5 Projectiles** `[ ]`
|
||||||
|
- `[ ]` ballistic / laser / missile / beam / homing / arcing `(v0.4)`
|
||||||
|
- `[ ]` projectile draw + impact VFX hook `(v0.6)`
|
||||||
|
- **F6 Targeting** `[~]`
|
||||||
|
- `[x]` simple nearest/in-range acquisition
|
||||||
|
- `[ ]` priority scans (attack-move, guard, force-attack) `(v0.4)`
|
||||||
|
- `[ ]` re-targeting, leash, target ground `(v0.4)`
|
||||||
|
- **F7 Special effects** `[ ]`
|
||||||
|
- `[ ]` EMP/stun, flame/radiation DoT, mind-control, shrink/grow `(v0.7)`
|
||||||
|
- **F8 Death & corpses** `[ ]`
|
||||||
|
- `[ ]` death types, wrecks, gibs, salvage, rebuild `(v0.6)`
|
||||||
|
|
||||||
|
### M09 `ra3.movement` — locomotion & pathfinding `[ ]`
|
||||||
|
|
||||||
|
- **F1 Locomotors** `[ ]`
|
||||||
|
- `[ ]` ground / air / naval / amphibious / hover / teleport `!!` `(v0.4)`
|
||||||
|
- `[ ]` turn rates, acceleration, braking, banking `(v0.6)`
|
||||||
|
- **F2 Pathfinding** `[ ]`
|
||||||
|
- `[ ]` grid A* over the passability grid `(v0.4)`
|
||||||
|
- `[ ]` hierarchical / jump-point refinement `(v0.6)`
|
||||||
|
- `[ ]` dynamic obstacle integration (buildings, units) `(v0.6)`
|
||||||
|
- **F3 Steering & flocking** `[ ]`
|
||||||
|
- `[ ]` separation, avoidance, group cohesion `(v0.6)`
|
||||||
|
- `[ ]` formation slots (line/wedge/box) `(v0.6)`
|
||||||
|
- **F4 Orders & waypoints** `[~]`
|
||||||
|
- `[x]` straight-line move toward a target (skirmish)
|
||||||
|
- `[ ]` waypoint queues, queued orders with shift `(v0.4)`
|
||||||
|
- `[ ]` guard / patrol / attack-move / stop / scatter `(v0.4)`
|
||||||
|
- **F5 Collision & crush** `[ ]`
|
||||||
|
- `[ ]` unit-unit collision, pushing, crush damage `(v0.6)`
|
||||||
|
- **F6 Naval & amphibious** `[ ]`
|
||||||
|
- `[ ]` water-only movement, amphibious land↔water transition `!!` `(v0.6)`
|
||||||
|
- **F7 Transport & airdrop** `[ ]`
|
||||||
|
- `[ ]` boarding/unloading, airdrop descent `(v0.6)`
|
||||||
|
|
||||||
|
### M10 `ra3.economy` — resources, power, construction `[P]`
|
||||||
|
|
||||||
|
- **F1 Ore / resource** `[~]`
|
||||||
|
- `[x]` ore fields and harvester↔refinery cycle
|
||||||
|
- `[ ]` ore spread/regrowth, depletion, ore density `(v0.4)`
|
||||||
|
- **F2 Power** `[~]`
|
||||||
|
- `[x]` power balance fields
|
||||||
|
- `[ ]` brownout/blackout consequences `(v0.4)`
|
||||||
|
- **F3 Construction** `[~]`
|
||||||
|
- `[x]` pay-as-you-go queue, tech prerequisites, build times
|
||||||
|
- `[ ]` build-radius rules (structures must be in base vicinity) `(v0.4)`
|
||||||
|
- `[ ]` low-power build-speed penalty `(v0.4)`
|
||||||
|
- **F4 Placement** `[ ]`
|
||||||
|
- `[ ]` placement grid, footprint validation, green/red ghost `(v0.4)`
|
||||||
|
- `[ ]` adjacency bonuses / prerequisite-adjacent structures `(v0.6)`
|
||||||
|
- **F5 Repair & sell** `[ ]`
|
||||||
|
- `[ ]` structure repair over time, cost, sell refund `(v0.4)`
|
||||||
|
- `[ ]` unit repair pads if present `(v0.6)`
|
||||||
|
- **F6 Rally points** `[ ]`
|
||||||
|
- `[ ]` factory rally, rally preview, waypoint rally `(v0.6)`
|
||||||
|
- **F7 Income modifiers** `[ ]`
|
||||||
|
- `[ ]` bonus crates (cash/units/repair/heal, `random_bonus_crates`) `(v0.5)`
|
||||||
|
|
||||||
|
### M11 `ra3.ai` — computer opponents `[P]`
|
||||||
|
|
||||||
|
- **F1 Skirmish AI** `[~]`
|
||||||
|
- `[x]` simple build AI (base, ore, a couple of unit types)
|
||||||
|
- `[ ]` data-driven build orders per faction `(v0.5)`
|
||||||
|
- `[ ]` economy management (expand, defend harvesters) `(v0.5)`
|
||||||
|
- **F2 Attack management** `[~]`
|
||||||
|
- `[x]` send units at the enemy base
|
||||||
|
- `[ ]` attack force assembly, waves, retreat/regroup `(v0.5)`
|
||||||
|
- `[ ]` targeting priorities (harvesters, key structures) `(v0.6)`
|
||||||
|
- **F3 Team AI / diplomacy** `[ ]`
|
||||||
|
- `[ ]` allied coordination, shared attacks, base defense `(v0.6)`
|
||||||
|
- **F4 Difficulty & personalities** `[ ]`
|
||||||
|
- `[ ]` easy/normal/hard modifiers, AI cheating options `(v0.5)`
|
||||||
|
- `[ ]` commander personalities (aggressive/turtle/air/naval) `(v0.7)`
|
||||||
|
- **F5 Scouting** `[ ]`
|
||||||
|
- `[ ]` exploration, map awareness, threat response `(v0.6)`
|
||||||
|
- **F6 Superweapon usage** `[ ]`
|
||||||
|
- `[ ]` AI powers/power targeting `(v0.7)`
|
||||||
|
- **F7 Script hooks** `[ ]`
|
||||||
|
- `[ ]` AI cooperation with script triggers (campaign) `(v0.7)`
|
||||||
|
|
||||||
|
### M12 `ra3.script` — triggers & missions `[ ]`
|
||||||
|
|
||||||
|
- **F1 Trigger system** `[ ]` `!!`
|
||||||
|
- `[ ]` conditions (elapsed, object in region, destroyed, flag) `(v0.7)`
|
||||||
|
- `[ ]` actions (spawn, order, reveal, camera, dialog, win/lose) `(v0.7)`
|
||||||
|
- **F2 Script engine** `[ ]` `!!`
|
||||||
|
- `[ ]` SAGE script language / mission script parse `(v0.7)`
|
||||||
|
- `[ ]` timers, counters, flags, per-player state `(v0.7)`
|
||||||
|
- **F3 Campaign missions** `[ ]`
|
||||||
|
- `[ ]` mission objectives, sequential phases, briefing `(v0.8)`
|
||||||
|
- `[ ]` three faction campaigns (Allied/Soviet/Empire) `(v0.8)`
|
||||||
|
- **F4 Reinforcements & spawns** `[ ]`
|
||||||
|
- `[ ]` scripted spawns, cinematic units, capture `(v0.8)`
|
||||||
|
- **F5 Tutorials** `[ ]`
|
||||||
|
- `[ ]` tutorial message gates, camera lock/unlock actions `(v0.8)`
|
||||||
|
- **F6 Co-op** `[ ]`
|
||||||
|
- `[ ]` co-op commander missions `(v0.9)`
|
||||||
|
|
||||||
|
### M13 `ra3.powers` — superweapons & support powers `[ ]`
|
||||||
|
|
||||||
|
- **F1 Superweapons** `[ ]` `!!`
|
||||||
|
- `[ ]` Allied Chronosphere `(v0.7)`
|
||||||
|
- `[ ]` Soviet Vacuum Imploder `(v0.7)`
|
||||||
|
- `[ ]` Empire Psionic Decimator `(v0.7)`
|
||||||
|
- `[ ]` charge timer, targeting, ready state, HUD `(v0.7)`
|
||||||
|
- **F2 Support powers** `[ ]`
|
||||||
|
- `[ ]` per-faction powers (spy satellite, air support, etc.) `(v0.7)`
|
||||||
|
- `[ ]` cooldowns, targeting types, cost `(v0.7)`
|
||||||
|
- **F3 Commander's Challenge powers** `[ ]`
|
||||||
|
- `[ ]` challenge-mode power loadouts `(v0.8)`
|
||||||
|
|
||||||
|
### M14 `ra3.shroud` — fog of war & radar `[ ]`
|
||||||
|
|
||||||
|
- **F1 Shroud** `[ ]`
|
||||||
|
- `[ ]` per-player unexplored grid `(v0.4)`
|
||||||
|
- **F2 Fog of war** `[ ]`
|
||||||
|
- `[ ]` explored-but-unseen dimming `(v0.4)`
|
||||||
|
- **F3 Reveal sources** `[ ]`
|
||||||
|
- `[ ]` units/structures reveal radius, abilities, spy satellite `(v0.6)`
|
||||||
|
- **F4 Radar / minimap** `[ ]`
|
||||||
|
- `[ ]` radar texture, unit blips, radar-offline on low power `(v0.6)`
|
||||||
|
- **F5 Shroud ↔ logic** `[ ]`
|
||||||
|
- `[ ]` targetability gating, option for AI to ignore shroud `(v0.4)`
|
||||||
|
|
||||||
|
### M15 `ra3.client` — game client & shell `[P]`
|
||||||
|
|
||||||
|
- **F1 Game client** `[~]`
|
||||||
|
- `[x]` `game_client` facade owning the sim, driving the frame loop
|
||||||
|
- `[ ]` sim/present decoupling, interpolation, catch-up `(v0.6)`
|
||||||
|
- **F2 Tactical view / camera** `[~]`
|
||||||
|
- `[x]` controls: wheel zoom, edge scroll, clamped pan, open on player start
|
||||||
|
- `[x]` camera tuning table (`cameraMinHeight` …) + lock actions
|
||||||
|
- `[ ]` retail perspective camera, pitch/yaw, FOV (needs 3D terrain) `(v0.6)`
|
||||||
|
- **F3 Selection** `[ ]`
|
||||||
|
- `[ ]` click select, drag-box, double-click type select `(v0.6)`
|
||||||
|
- `[ ]` control groups (Ctrl+N), type filters, select-all-of-type `(v0.6)`
|
||||||
|
- **F4 Orders** `[ ]`
|
||||||
|
- `[ ]` contextual right-click orders, force-attack, force-move `(v0.6)`
|
||||||
|
- `[ ]` order queue with shift, formation move `(v0.6)`
|
||||||
|
- **F5 Command bar** `[ ]` `!!`
|
||||||
|
- `[ ]` build/production palettes, ability buttons, portraits `(v0.7)`
|
||||||
|
- `[ ]` tooltips, cost/time, cooldown sweep, disabled states `(v0.7)`
|
||||||
|
- **F6 HUD** `[ ]`
|
||||||
|
- `[x]` minimal match-state overlay (units, start markers)
|
||||||
|
- `[ ]` resource/power readouts, objectives, notifications `(v0.7)`
|
||||||
|
- `[ ]` EVA voice announcements hook `(v0.7)`
|
||||||
|
- **F7 Shell menus** `[~]`
|
||||||
|
- `[x]` map list by localized name + full render/skirmish options; console fallback
|
||||||
|
- `[ ]` main menu, skirmish setup, faction/team/color pickers `(v0.7)`
|
||||||
|
- `[ ]` options (video/audio/keybinds), pause, load/save, credits `(v0.8)`
|
||||||
|
- **F8 Feedback & cursors** `[ ]`
|
||||||
|
- `[ ]` action cursor, placement ghost, move/attack markers `(v0.6)`
|
||||||
|
|
||||||
|
### M16 `ra3.render` — renderer & RHI `[P]`
|
||||||
|
|
||||||
|
- **F1 RHI** `[ ]`
|
||||||
|
- `[ ]` device/queue/swapchain abstraction over Vulkan `(v0.6)`
|
||||||
|
- `[ ]` buffers, textures, samplers, descriptor sets, pipelines `(v0.6)`
|
||||||
|
- `[ ]` `present_terrain` is the seam where this lands today `[~]`
|
||||||
|
- **F2 Render graph** `[ ]`
|
||||||
|
- `[ ]` pass scheduling, barriers, transient/aliased resources `(v0.6)`
|
||||||
|
- **F3 Terrain render** `[~]`
|
||||||
|
- `[x]` top-down software + GPU heightfield with blend ramp + gutter atlas
|
||||||
|
- `[ ]` perspective terrain mesh, LOD, cliff, water `(v0.6)`
|
||||||
|
- **F4 Model render** `[ ]` `!!`
|
||||||
|
- `[ ]` W3D draw, skinning, materials, team colors `(v0.6)`
|
||||||
|
- `[ ]` shadows, decals, ground marks `(v0.6)`
|
||||||
|
- **F5 VFX** `[ ]`
|
||||||
|
- `[ ]` particle systems, beams, muzzle flashes, explosions `(v0.6)`
|
||||||
|
- `[ ]` shader effect graph (retail `.fxo` parity where feasible) `!!` `(v0.7)`
|
||||||
|
- **F6 Sky & atmosphere** `[ ]`
|
||||||
|
- `[ ]` skybox, fog, weather, time-of-day `(v0.7)`
|
||||||
|
- **F7 HUD render** `[ ]`
|
||||||
|
- `[ ]` 2D art layer, fonts, minimap/radar texture `(v0.7)`
|
||||||
|
- **F8 Software renderer** `[D]`
|
||||||
|
- `[x]` ARGB framebuffer, blit/line/circle/text, TGA decode, BMP encode
|
||||||
|
- `[x]` map compositing, grid, markers; headless output
|
||||||
|
- **F9 Post-processing** `[ ]`
|
||||||
|
- `[ ]` bloom, color grading, AA, resolution scaling `(v0.7)`
|
||||||
|
|
||||||
|
### M17 `ra3.ui` — platform layer & backends `[D]`
|
||||||
|
|
||||||
|
- **F1 Display abstraction** `[D]`
|
||||||
|
- `[x]` shared primitives + interactive loops + `ui_event` mapping in the base
|
||||||
|
- `[x]` backends implement primitives only (SDL/Vulkan cannot drift)
|
||||||
|
- **F2 SDL3 backend** `[D]`
|
||||||
|
- `[x]` window, streaming-texture blit, input polling
|
||||||
|
- `[ ]` gamepad support `(v0.8)`
|
||||||
|
- **F3 Backend selection (`ra3.display`)** `[D]`
|
||||||
|
- `[x]` preferred backend + ordered fallback (Vulkan / D3D11 / D3D12 / SDL)
|
||||||
|
- `[x]` in-game Renderer option; report failure for offscreen fallback
|
||||||
|
- **F4 Input mapping** `[~]`
|
||||||
|
- `[x]` keyboard/mouse state, modifier masks
|
||||||
|
- `[ ]` rebindable keybinds, mouse capture, scroll wheel events `(v0.6)`
|
||||||
|
- **F5 Window modes** `[ ]`
|
||||||
|
- `[ ]` windowed/fullscreen/borderless, resize, multi-monitor `(v0.6)`
|
||||||
|
- **F6 Null/headless backend** `[D]`
|
||||||
|
- `[x]` returns false so the tree builds and runs without SDL3/Vulkan
|
||||||
|
|
||||||
|
### M18 `ra3.vulkan` — Vulkan backend `[P]`
|
||||||
|
|
||||||
|
- **F1 Device & swapchain** `[~]`
|
||||||
|
- `[x]` embedded SPIR-V, SDL3 surface, present path
|
||||||
|
- `[ ]` formal RHI integration (see M16 F1) `(v0.6)`
|
||||||
|
- **F2 Terrain presentation** `[D]`
|
||||||
|
- `[x]` GPU heightfield raymarch, mipmapped atlas, gutter, retail blend ramp
|
||||||
|
- **F3 Materials & pipelines** `[ ]`
|
||||||
|
- `[ ]` model/particle/HUD pipelines `(v0.6)`
|
||||||
|
- **F4 Null fallback** `[D]`
|
||||||
|
- `[x]` report failure when no Vulkan loader is present
|
||||||
|
|
||||||
|
### M18b `ra3.dx` — Direct3D 11 / 12 backend `[P]`
|
||||||
|
|
||||||
|
- **F1 Device & swapchain** `[D]`
|
||||||
|
- `[x]` SDL3 window → HWND, DXGI flip-model swapchain, resize
|
||||||
|
- `[x]` runtime HLSL via `d3dcompiler_47` (no build-time shader compiler)
|
||||||
|
- **F2 2D image path** `[D]`
|
||||||
|
- `[x]` BGRA scene texture + fullscreen-triangle blit (D3D11/D3D12)
|
||||||
|
- **F3 Terrain presentation** `[D]`
|
||||||
|
- `[x]` GPU heightfield raymarch (HLSL port of `terrain.frag`), D3D11 and D3D12
|
||||||
|
- **F4 Root signatures / PSOs (D3D12)** `[~]`
|
||||||
|
- `[x]` root constants, descriptor tables, static samplers, barriers
|
||||||
|
- `[ ]` shared RHI with Vulkan (see M16 F1) `(v0.6)`
|
||||||
|
- **F5 Null fallback** `[D]`
|
||||||
|
- `[x]` non-Windows builds link a stub that fails `init`
|
||||||
|
|
||||||
|
### M19 `ra3.audio` — audio `[ ]`
|
||||||
|
|
||||||
|
- **F1 SFX** `[ ]`
|
||||||
|
- `[ ]` 3D positional sound from cues/events `(v0.7)`
|
||||||
|
- **F2 Music** `[ ]`
|
||||||
|
- `[ ]` streaming music, playlists, combat stingers `(v0.7)`
|
||||||
|
- **F3 Voice & EVA** `[ ]`
|
||||||
|
- `[ ]` unit response lines, announcer (EVA) events `(v0.7)`
|
||||||
|
- **F4 Mixer** `[ ]`
|
||||||
|
- `[ ]` buses (master/sfx/music/voice), volume, ducking, reverb `(v0.7)`
|
||||||
|
- **F5 Codecs** `[ ]` `!!`
|
||||||
|
- `[ ]` decode RA3 audio formats `(v0.7)`
|
||||||
|
|
||||||
|
### M20 `ra3.video` — movies & cutscenes `[ ]`
|
||||||
|
|
||||||
|
- **F1 Movie playback** `[ ]` `!!`
|
||||||
|
- `[ ]` intro/briefing/ending video decode + playback `(v0.8)`
|
||||||
|
- `[ ]` skip, subtitles, aspect handling `(v0.8)`
|
||||||
|
- **F2 In-engine cutscenes** `[ ]`
|
||||||
|
- `[ ]` scripted camera + unit animation sequences `(v0.8)`
|
||||||
|
|
||||||
|
### M21 `ra3.match` — game setup & match rules `[P]`
|
||||||
|
|
||||||
|
- **F1 Game setup** `[~]`
|
||||||
|
- `[x]` map + two players + seed defaults
|
||||||
|
- `[ ]` faction/color/team/start-slot selection, AI personalities `(v0.7)`
|
||||||
|
- **F2 Rules & options** `[ ]`
|
||||||
|
- `[ ]` starting cash, crates on/off, superweapons on/off, speed, limits `(v0.7)`
|
||||||
|
- **F3 Match flow** `[~]`
|
||||||
|
- `[x]` `prepare_new_game` / `start_new_game` split mirroring retail
|
||||||
|
- `[ ]` loading progress, in-game start countdown `(v0.7)`
|
||||||
|
- **F4 Factions** `[~]`
|
||||||
|
- `[x]` Allied / Soviet / Empire player templates
|
||||||
|
- `[ ]` full faction tech trees and rosters `(v0.5)`
|
||||||
|
- **F5 Victory & scoring** `[~]`
|
||||||
|
- `[x]` team-wipe victory
|
||||||
|
- `[ ]` full victory conditions, post-match score screen `(v0.8)`
|
||||||
|
|
||||||
|
### M22 `ra3.replay` — recording & playback `[ ]`
|
||||||
|
|
||||||
|
- **F1 Recorder** `[ ]`
|
||||||
|
- `[ ]` capture command stream + seed + map id per match `(v0.4)`
|
||||||
|
- **F2 Replay format** `[ ]`
|
||||||
|
- `[ ]` self-describing header, command log, checksums `(v0.4)`
|
||||||
|
- `[ ]` compatibility with retail `.RA3Replay` playback `!!` `(v0.9)`
|
||||||
|
- **F3 Playback** `[ ]`
|
||||||
|
- `[ ]` deterministic re-simulation, speed control, seek `(v0.4)`
|
||||||
|
- **F4 Golden replays** `[ ]`
|
||||||
|
- `[ ]` curated replay corpus as a regression test `(v0.4)`
|
||||||
|
- **F5 Observer / spectator** `[ ]`
|
||||||
|
- `[ ]` watch live or recorded matches, fog option `(v0.9)`
|
||||||
|
|
||||||
|
### M23 `ra3.save` — save/load & profiles `[ ]`
|
||||||
|
|
||||||
|
- **F1 Save / load** `[ ]`
|
||||||
|
- `[ ]` full simulation state serialization (objects, modules, queues) `(v0.8)`
|
||||||
|
- `[ ]` versioned saves with migration `(v0.8)`
|
||||||
|
- **F2 Profiles** `[ ]`
|
||||||
|
- `[ ]` player profile, stats, progress/unlocks `(v0.8)`
|
||||||
|
- **F3 Options persistence** `[ ]`
|
||||||
|
- `[ ]` settings, keybinds, last-used skirmish config `(v0.7)`
|
||||||
|
- **F4 Checkpoints** `[ ]`
|
||||||
|
- `[ ]` campaign checkpoint save/restore `(v0.9)`
|
||||||
|
|
||||||
|
### M24 `ra3.mod` — data packages `[ ]`
|
||||||
|
|
||||||
|
- **F1 Data packages** `[ ]`
|
||||||
|
- `[ ]` load order, override precedence, loose-file mounting `(v0.8)`
|
||||||
|
- **F2 Content discovery** `[ ]`
|
||||||
|
- `[ ]` scan user mod dirs and additional `.big` archives `(v0.8)`
|
||||||
|
- **F3 Mod validation** `[ ]`
|
||||||
|
- `[ ]` schema + reference checks, actionable errors `(v0.8)`
|
||||||
|
|
||||||
|
### M25 `ra3.net` — LAN lockstep `[ ]` *(deferred, offline-only)*
|
||||||
|
|
||||||
|
- **F1 Lockstep** `[ ]`
|
||||||
|
- `[ ]` deterministic lockstep over LAN, command-exchange only `(v1.0)`
|
||||||
|
- **F2 Lobby & sync** `[ ]`
|
||||||
|
- `[ ]` lobby, slot/team assignment, start sync `(v1.0)`
|
||||||
|
- **F3 Desync detection** `[ ]`
|
||||||
|
- `[ ]` state-hash comparison, desync report `(v1.0)`
|
||||||
|
|
||||||
|
> No online service, matchmaking, EA account or third-party network
|
||||||
|
> integration — ever. LAN only.
|
||||||
|
|
||||||
|
### M26 `ra3.i18n` — localization `[P]`
|
||||||
|
|
||||||
|
- **F1 CSF strings** `[~]`
|
||||||
|
- `[x]` parse `gamestrings.csf` (UTF-16 units, low byte `^0xFF`)
|
||||||
|
- `[x]` map display-name lookup (`MAP:<ID>`)
|
||||||
|
- `[ ]` full string-table load for all UI text `(v0.5)`
|
||||||
|
- **F2 Language selection** `[~]`
|
||||||
|
- `[x]` newest `Lang-English*.big` wins
|
||||||
|
- `[ ]` all supported languages, fallback chain `(v0.5)`
|
||||||
|
- **F3 Fonts & shaping** `[ ]`
|
||||||
|
- `[ ]` glyph coverage, CJK/RTL shaping where applicable `(v0.6)`
|
||||||
|
- **F4 Substitution** `[ ]`
|
||||||
|
- `[ ]` placeholders, numbers, plurals `(v0.5)`
|
||||||
|
|
||||||
|
### M27 `apps` — applications `[D]`
|
||||||
|
|
||||||
|
- **F1 CLI** `[D]`
|
||||||
|
- `[x]` `menu` / `menu-preview` / `maps` / `skirmish` / `render` / `extract`
|
||||||
|
- `[ ]` `replay`, `benchmark`, `validate-data` subcommands `(v0.5)`
|
||||||
|
- **F2 Extraction pipeline** `[~]`
|
||||||
|
- `[x]` `extract` → `assets/` (maps, terrain)
|
||||||
|
- `[ ]` full asset dump (models/textures/audio/movies) as a build target `(v0.5)`
|
||||||
|
- **F3 Diagnostics** `[ ]`
|
||||||
|
- `[ ]` headless render/benchmark modes for CI `(v0.4)`
|
||||||
|
|
||||||
|
### M28 `tools` — offline tooling `[~]`
|
||||||
|
|
||||||
|
*(Python is permitted here; it is never linked into `openra3`.)*
|
||||||
|
|
||||||
|
- **F1 Reference fetch** `[D]`
|
||||||
|
- `[x]` sparse-clone SAGE 1.0 reference into git-ignored `reference/`
|
||||||
|
- **F2 Ghidra workflow** `[D]`
|
||||||
|
- `[x]` MCP-driven recovery loop + recovered symbol map (see RE doc)
|
||||||
|
- `[ ]` automated structure/table extractors `(v0.5)`
|
||||||
|
- **F3 Format inspectors** `[ ]`
|
||||||
|
- `[ ]` BIG/RefPack/CkMp/W3D/APT dumpers `(v0.5)`
|
||||||
|
- **F4 CI & packaging** `[D]`
|
||||||
|
- `[x]` GitLab CI builds both targets → test → package
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Cross-cutting invariants
|
||||||
|
|
||||||
|
These hold across **every** module above and are enforced in review:
|
||||||
|
|
||||||
|
1. **Language** — runtime code is C++ only. Build tooling may use Python.
|
||||||
|
2. **Ownership** — no raw owning pointers; `unique_ptr` for ownership, handles or
|
||||||
|
`thing *` views for references.
|
||||||
|
3. **Layering** — a module imports only lower layers. `ra3.core` imports no
|
||||||
|
engine module and no platform API.
|
||||||
|
4. **Determinism** — RNG, iteration, hashing and float use are explicit and
|
||||||
|
seedable; the logic step must be bit-reproducible for a given input stream.
|
||||||
|
5. **RE-traceable** — retail structure/constant changes cite an address in
|
||||||
|
[`REVERSE_ENGINEERING.md`](REVERSE_ENGINEERING.md).
|
||||||
|
6. **Offline** — no online service; LAN lockstep (M25) is the only networking,
|
||||||
|
and only command exchange.
|
||||||
|
7. **No bundled assets** — game data is read from the user's install; nothing
|
||||||
|
from it is committed. The tree builds and runs in CI on a built-in test map.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Milestone mapping
|
||||||
|
|
||||||
|
The plan rolls up to these releases:
|
||||||
|
|
||||||
|
| Milestone | Modules advanced | Delivers |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `v0.3.x` (done) | M00, M01, M04, M05(F1–F3), M06, M07(partial), M08(partial), M10(partial), M16(F3,F8), M17, M18 | minimal deterministic skirmish, real terrain, Vulkan present, menu |
|
||||||
|
| `v0.4.0` | M05(F4–F7), M06, M07, M08, M09(F1,F2,F4), M14, M22(F1–F4) | real update modules: locomotor, projectiles/warheads, placement, shroud, pathfinding, replay |
|
||||||
|
| `v0.5.0` | M02, M03, M04(F3,F4), M08, M10(F3,F4), M11(F1,F2), M26 | data-driven content: deserialise `.bin`/`.manifest`, real rosters, maps, strings |
|
||||||
|
| `v0.6.0` | M03(F1–F3), M07(F4,F6,F8,F14), M09(F2–F7), M16(F1,F2,F4,F5), M18(F3), M15(F1–F4,F8) | full renderer: perspective terrain, W3D models, in-game client + input |
|
||||||
|
| `v0.7.0` | M13, M15(F5–F7), M19, M11(F4–F6), M20, M21 | HUD/command bar, audio, superweapons, Commander's Challenge |
|
||||||
|
| `v0.8.0` | M12, M20(F1), M23, M24, M21(F5) | campaigns, cutscenes, save/load, mods |
|
||||||
|
| `v0.9.0` | M22(F2,F5), M12(F6), M25 | retail replay playback, co-op, LAN lockstep |
|
||||||
|
| `v1.0.0` | all | feature-complete offline RA3 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Design rules
|
||||||
|
|
||||||
|
The five rules the codebase is held to (restated from §1, with the concrete
|
||||||
|
consequences that trip people up):
|
||||||
|
|
||||||
1. **No raw owning pointers.** Ownership is `std::unique_ptr`; the partition
|
1. **No raw owning pointers.** Ownership is `std::unique_ptr`; the partition
|
||||||
manager holds non-owning `thing *` views only.
|
manager and object registry hold non-owning `thing *` views only.
|
||||||
2. **Portable simulation.** No platform APIs in `ra3.core` / `ra3.logic`. All
|
2. **Portable simulation.** No platform APIs in `ra3.core` / simulation
|
||||||
platform concerns live behind `display`.
|
modules. All platform concerns live behind `display`.
|
||||||
3. **Determinism.** Anything that can diverge between runs (random, iteration
|
3. **Determinism.** Anything that can diverge between runs (random, iteration
|
||||||
order) is explicit and seedable.
|
order) is explicit and seedable.
|
||||||
4. **RE-traceable.** Where a structure or constant comes from the retail
|
4. **RE-traceable.** Where a structure or constant comes from the retail
|
||||||
binary, the address is cited in the comment.
|
binary, the address is cited in the comment.
|
||||||
5. **No bundled assets, no online.** Game data is read from the user's install
|
5. **No bundled assets, no online.** Game data is read from the user's install
|
||||||
and never committed; there is no networking or online service.
|
and never committed; there is no networking or online service.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Where to start
|
||||||
|
|
||||||
|
- New to the codebase: read this plan top-to-bottom (§3 is the feature set,
|
||||||
|
§5 the near-term order of work).
|
||||||
|
- Picking up a feature: find its **Function** above, take the lowest-numbered
|
||||||
|
unmet `[ ]` **Feature**, and cite its supporting retail address.
|
||||||
|
- Reverse engineering a subsystem: follow the loop in
|
||||||
|
[`REVERSE_ENGINEERING.md`](REVERSE_ENGINEERING.md); the `!!` tags above mark
|
||||||
|
the functions that still need a recovery pass before they can be built.
|
||||||
|
|||||||
@@ -78,6 +78,27 @@ Each holds an object pointer (0 when the subsystem is down).
|
|||||||
cash `+0x64`, player slots `+0xfc` (stride `0x5c`, 6 slots), faction at
|
cash `+0x64`, player slots `+0xfc` (stride `0x5c`, 6 slots), faction at
|
||||||
`slot + 0x18` (`Empire=2`, `Allied=4`, `Soviet=8`, `Random=7`).
|
`slot + 0x18` (`Empire=2`, `Allied=4`, `Soviet=8`, `Random=7`).
|
||||||
|
|
||||||
|
### Tactical view (camera)
|
||||||
|
|
||||||
|
The in-game camera is the `TheTacticalView` object, held in the global at
|
||||||
|
`0x00cdb7b4`. Its vtable accessors return zoom, pitch (current/target), yaw,
|
||||||
|
world position and FOV; the debug overlay `FUN_005ef0a0` prints them through the
|
||||||
|
format string at `0x00c0b900`. Mouse state is the singleton at `0x00ce9284`
|
||||||
|
(cursor position vtable slot `+0x3c`, button down `+0x48`); the keyboard manager
|
||||||
|
is `0x00ce927c` (modifier mask `+0x38`).
|
||||||
|
|
||||||
|
Per-map camera tuning is a named-field table in `.rdata` (around
|
||||||
|
`0x00c11a54`): `cameraMinHeight`, `cameraMaxHeight`, `cameraPitchAngle`,
|
||||||
|
`cameraYawAngle`, `cameraScrollSpeedScalar`, `cameraGroundMinHeight`,
|
||||||
|
`cameraGroundMaxHeight`. The map-load chunk `CHUNK_TacticalView`
|
||||||
|
(`0x00beea14`, consumed near `0x00548a00`) seeds the view from the map; the
|
||||||
|
tutorial actions `LOCK_CAMERA_SCROLL` / `LOCK_CAMERA_ZOOM` / `LOCK_CAMERA_ROTATION`
|
||||||
|
gate the controls.
|
||||||
|
|
||||||
|
OpenRA3 has no 3D terrain yet, so `ra3::render::view_camera` reproduces the
|
||||||
|
*controls* - wheel zoom, screen-edge scroll, clamped pan and opening on the
|
||||||
|
player's start - over the 2D map overview, not the retail perspective camera.
|
||||||
|
|
||||||
### Match start
|
### Match start
|
||||||
|
|
||||||
The BEGIN button calls `SkirmishGameOptionsMenu::start` (`0x00b28d60`), which
|
The BEGIN button calls `SkirmishGameOptionsMenu::start` (`0x00b28d60`), which
|
||||||
@@ -127,9 +148,84 @@ in `MPPositionList` instead yield no waypoints and fall back.
|
|||||||
Verified example (`map_mp_2_feasel4`): `Player_1_Start` = `(1338.9, 1940.5, 0)`,
|
Verified example (`map_mp_2_feasel4`): `Player_1_Start` = `(1338.9, 1940.5, 0)`,
|
||||||
`Player_2_Start` = `(1290.8, 1404.9, 0)`.
|
`Player_2_Start` = `(1290.8, 1404.9, 0)`.
|
||||||
|
|
||||||
|
### Terrain (`ra3.terrain`)
|
||||||
|
|
||||||
|
The `CkMp` tree is a flat chunk list: `"CkMp"`, `u32 assetCount`, the name
|
||||||
|
table (`{ u8 len, name, u32 index }`, index descending from `assetCount`), then
|
||||||
|
`{ u32 index, u16 version, u32 size, data }` per chunk. On
|
||||||
|
`map_mp_2_feasel4` the terrain is `HeightMapData` v6 (540 x 600, border 20,
|
||||||
|
`u16` elevations, scale `0.0390625`) and `BlendTileData` v27.
|
||||||
|
|
||||||
|
`BlendTileData` opens with `NumTiles`, the `u16` tile grid, then the
|
||||||
|
blend/three-way/cliff tables (`u16` for v27); the passability flag arrays that
|
||||||
|
follow are not needed for rendering, so the texture table is located by
|
||||||
|
scanning for its `{ cellStart, cellCount, cellSize, magic }` + `u16`-prefixed
|
||||||
|
name entries. A tile value is `(cellIndex << 2) | variant`, and `cellIndex`
|
||||||
|
indexes the global `TextureCellCount`-cell table (each texture owning
|
||||||
|
`cellSize^2` 64 px cells). The textures themselves are
|
||||||
|
`art\terrain\<stem>.tga` in `Terrain.big` / `Core11.big` (RefPack + 256x256
|
||||||
|
TGA). Rendered top-down, `map_mp_2_feasel4` correlates 0.94 with the official
|
||||||
|
`_art.tga` overview.
|
||||||
|
|
||||||
|
### Terrain blending (`BlendTileData` tail)
|
||||||
|
|
||||||
|
After the tile grid, `BlendTileData` stores three per-cell `u16` tables —
|
||||||
|
`Blends`, `ThreeWayBlends` and `CliffTextures` — then `TextureCellCount`,
|
||||||
|
`BlendsCount`, the texture table, two magic words and `BlendsCount - 1` blend
|
||||||
|
descriptions (18 bytes each: `u32 secondaryTile`, four direction bytes,
|
||||||
|
`u8 flags`, `u8 twoSided`, `u32 0xFFFFFFFF`, `u32 0x7ADA0000`). A non-zero
|
||||||
|
`Blends[cell]` is a 1-based index into the descriptions; `secondaryTile` is a
|
||||||
|
packed tile value (`secondaryTile >> 2` is its cell).
|
||||||
|
|
||||||
|
The retail `Terrain.fx` (compiled `terrain.fxo`, parameters `Terrain.BaseTexture`,
|
||||||
|
`Terrain.MacroTexture`, `MapCellSize`, `IsTerrainAtlasEnabled`; technique
|
||||||
|
`TerrainTile`) cross-fades a cell's base tile into `secondaryTile` with a linear
|
||||||
|
ramp selected by `BlendDirection`: `1` right, `2` top, `4` top-right, `8`
|
||||||
|
top-left, where `flags` bit 0 flips the axis and bit 1 marks a two-sided
|
||||||
|
diagonal. OpenSAGE's `Terrain.frag` reconstructs the exact
|
||||||
|
`CalculateBlendFactor`; `ra3::terrain::blend_factor` and `shaders/terrain.frag`
|
||||||
|
mirror it. The row axis is *not* inverted (73% of long-axis blends point at a
|
||||||
|
neighbour of the same texture, versus 25% inverted). On `map_mp_2_feasel4`,
|
||||||
|
33828 of 324000 cells carry a blend and there are 7094 descriptions.
|
||||||
|
|
||||||
|
The GPU atlas pads every 64 px tile with a 2-texel replicated gutter. Packed
|
||||||
|
edge-to-edge, bilinear/mipmap filtering averaged two unrelated tiles at every
|
||||||
|
cell border — that cross-tile bleed was the visible grid line the hardware path
|
||||||
|
drew. (`shaders/terrain.frag` samples `cell_stride = cell_texels + 2 * gutter`.)
|
||||||
|
|
||||||
|
### Open question: per-cell tile sampling
|
||||||
|
|
||||||
|
Measured on `map_mp_2_feasel4`, sampling each cell as its own 64 px block and
|
||||||
|
restarting the UV every cell leaves a 1.39x edge spike at cell boundaries
|
||||||
|
(43.4 vs 31.1 mean gradient at 8 px/cell). Two candidate mappings reduce it and
|
||||||
|
need a visual decision against the retail art:
|
||||||
|
|
||||||
|
| Mapping | Boundary/interior |
|
||||||
|
| --- | --- |
|
||||||
|
| per-cell 64 px block (current) | 1.39 |
|
||||||
|
| OpenSAGE `BlendTileTextureIndex` Morton layout, 32 px block | 1.18 |
|
||||||
|
| continuous `uv / (cellSize * 2)`, per OpenSAGE `Terrain.frag` | 1.11 |
|
||||||
|
|
||||||
|
The retail `Terrain.frag` (OpenSAGE) samples the tile texture *continuously*
|
||||||
|
(`uv / (CellSize * 2)`), so adjacent cells never restart the texture; our
|
||||||
|
per-cell restart is the remaining source of grid lines. Switching the atlas
|
||||||
|
from 64 px blocks to continuous 32 px regions (or the Morton 8x8 layout) is the
|
||||||
|
next step, pending an art correlation check.
|
||||||
|
|
||||||
|
### Map display names
|
||||||
|
|
||||||
|
The skirmish map list labels live in `Data\English.big`'s
|
||||||
|
`data\gamestrings.csf` (the newest `Lang-English*.big` wins) under
|
||||||
|
`MAP:<UPPERCASE_ID>`, e.g. `MAP:MAP_MP_2_FEASEL4` = "Battlebase Beta". CSF
|
||||||
|
values are UTF-16 code units whose low byte is XORed with `0xFF`
|
||||||
|
(`ra3::map::parse_map_names`). `openra3 extract` writes the decoded table to
|
||||||
|
`maps/map_names.tsv`; `openra3 menu` shows them instead of the raw map id.
|
||||||
|
|
||||||
|
|
||||||
### Still to recover
|
### Still to recover
|
||||||
|
|
||||||
- `MPPositionList` layout (per-player starts for maps without waypoints).
|
- `MPPositionList` layout (per-player starts for maps without waypoints).
|
||||||
- Map dimensions / `HeightMapData` / `BlendTileData`.
|
- Cliff textures and the `CliffTextureMapping` UV remap (`CliffTextures` is
|
||||||
- Compiled asset blobs (`map.bin`, `global.bin`, `static.*.bin`) and the
|
parsed but not yet drawn).
|
||||||
`.manifest` schema used to deserialise them.
|
- Compiled asset blobs (`global.bin`, `static.*.bin`) and the `.manifest`
|
||||||
|
schema used to deserialise them.
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
# Roadmap
|
|
||||||
|
|
||||||
OpenRA3 is a very large undertaking. This roadmap is deliberately honest about
|
|
||||||
scope: reconstructing a 2008 RTS engine from a decompiler plus a related open
|
|
||||||
engine is a multi-year, multi-person effort. The milestones below are ordered so
|
|
||||||
that each one produces something that builds and runs.
|
|
||||||
|
|
||||||
## Done
|
|
||||||
|
|
||||||
- [x] **v0.0.1 — skeleton + minimal skirmish.** C++26 modules, GCC 16,
|
|
||||||
CMake/Ninja, Docker `dev`/`deploy`, GitLab CI. Reads `BIG4`/RefPack data
|
|
||||||
from a local install, recovers map start waypoints, and runs a
|
|
||||||
deterministic headless two-player skirmish to a decision.
|
|
||||||
- [x] **v0.1.0 — map renderer + window.** Software ARGB framebuffer, TGA
|
|
||||||
decoder for the map art, BMP output, map/grid/unit compositing, and an
|
|
||||||
SDL3 window viewer (pan/zoom).
|
|
||||||
|
|
||||||
## Next
|
|
||||||
|
|
||||||
- [ ] **v0.1.1 — correct world calibration.** Parse the map heightmap
|
|
||||||
dimensions (and `MPPositionList` starts) so the unit overlay lines up with
|
|
||||||
the map art instead of using an assumed world size.
|
|
||||||
- [ ] **v0.2.0 — data & file formats.** Parse compiled gameplay assets
|
|
||||||
(`GameObject`, `WeaponTemplate`, `ArmorTemplate`, `LocomotorTemplate`) so
|
|
||||||
units use the real balance numbers instead of OpenRA3's stand-ins.
|
|
||||||
- [ ] **v0.3.0 — deterministic simulation.** Real update modules, locomotor
|
|
||||||
movement, weapons/damage/armour resolution, build queues and the tech
|
|
||||||
tree, pathfinding and shroud.
|
|
||||||
- [ ] **v0.4.0 — AI.** Skirmish AI: build states, team composition, attack
|
|
||||||
waves (the reference tree's `AI*` modules).
|
|
||||||
- [ ] **v0.5.0 — full renderer.** Terrain heightmap rendering, W3D models and a
|
|
||||||
D3D9/Vulkan/OpenGL client backend implementing `ra3::client::display`,
|
|
||||||
plus full input.
|
|
||||||
- [ ] **v0.6.0 — content.** Load real maps, units, powers and strings; play a
|
|
||||||
skirmish end-to-end with a UI.
|
|
||||||
|
|
||||||
## Cross-cutting tracks
|
|
||||||
|
|
||||||
- **RE depth** — keep recovering retail layouts (see
|
|
||||||
[`REVERSE_ENGINEERING.md`](REVERSE_ENGINEERING.md)); every structure gets an
|
|
||||||
address citation and a test.
|
|
||||||
- **Determinism & replay** — the logic random stream and frame ordering must be
|
|
||||||
reproducible; replay format and a golden-replay test suite.
|
|
||||||
- **Offline only** — no online mode. Multiplayer, if pursued, is LAN lockstep on
|
|
||||||
the message stream, never an online service.
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the Debian (latest stable) Linux/ARM64 target (.deb + binary).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
IMAGE="${OPENRA3_DEBIAN_ARM64_IMAGE:-openra3-debian-arm64:local}"
|
||||||
|
BUILD_DIR="${1:-build/debian-arm64}"
|
||||||
|
|
||||||
|
docker build --target dev -f "$ROOT/Dockerfile.debian-arm64" -t "$IMAGE" "$ROOT"
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake -S . -B "$BUILD_DIR" -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-aarch64-linux-gnu.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DOPENRA3_AARCH64_CC=clang-19 -DOPENRA3_AARCH64_CXX=clang++-19 \
|
||||||
|
-DOPENRA3_AARCH64_STDLIB_MODULES_JSON=/usr/lib/llvm-19/lib/libc++.modules.json \
|
||||||
|
-DOPENRA3_DISTRO_TAG=debian13
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake --build "$BUILD_DIR" -j
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" bash -c "cd $BUILD_DIR && cpack -G DEB"
|
||||||
|
|
||||||
|
echo "Debian arm64: $ROOT/$BUILD_DIR/bin/openra3 (+ .deb)"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the Debian (latest stable) target (.deb + binary) with the isolated image.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
IMAGE="${OPENRA3_DEBIAN_IMAGE:-openra3-debian:local}"
|
||||||
|
BUILD_DIR="${1:-build/debian}"
|
||||||
|
|
||||||
|
docker build --target dev -f "$ROOT/Dockerfile.debian" -t "$IMAGE" "$ROOT"
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake -S . -B "$BUILD_DIR" -G Ninja \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_CXX_STDLIB_MODULES_JSON=/usr/lib/llvm-19/lib/libc++.modules.json \
|
||||||
|
-DOPENRA3_DISTRO_TAG=debian13
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake --build "$BUILD_DIR" -j
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" bash -c "cd $BUILD_DIR && cpack -G DEB"
|
||||||
|
|
||||||
|
echo "Debian: $ROOT/$BUILD_DIR/bin/openra3 (+ .deb)"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the Linux/ARM64 target (.deb + binary) with the isolated cross image.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
IMAGE="${OPENRA3_LINUX_ARM64_IMAGE:-openra3-linux-arm64:local}"
|
||||||
|
BUILD_DIR="${1:-build/linux-arm64}"
|
||||||
|
|
||||||
|
docker build --target dev -f "$ROOT/Dockerfile.linux-arm64" -t "$IMAGE" "$ROOT"
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake -S . -B "$BUILD_DIR" -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-aarch64-linux-gnu.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release -DOPENRA3_DISTRO_TAG=ubuntu26.04
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake --build "$BUILD_DIR" -j
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" bash -c "cd $BUILD_DIR && cpack -G DEB"
|
||||||
|
|
||||||
|
echo "Linux arm64: $ROOT/$BUILD_DIR/bin/openra3 (+ .deb)"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the Linux target inside its own isolated image (Dockerfile).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
IMAGE="${OPENRA3_LINUX_IMAGE:-openra3-linux:local}"
|
||||||
|
BUILD_DIR="${1:-build/linux}"
|
||||||
|
|
||||||
|
docker build --target dev -t "$IMAGE" "$ROOT"
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake -S . -B "$BUILD_DIR" -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake --build "$BUILD_DIR" -j
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" ctest --test-dir "$BUILD_DIR" --output-on-failure
|
||||||
|
|
||||||
|
echo "Linux build: $ROOT/$BUILD_DIR/bin/openra3"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the Windows/ARM64 target (portable .zip) with the isolated cross image.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
IMAGE="${OPENRA3_WINDOWS_ARM64_IMAGE:-openra3-windows-arm64:local}"
|
||||||
|
BUILD_DIR="${1:-build/windows-arm64}"
|
||||||
|
|
||||||
|
docker build --target dev -f "$ROOT/Dockerfile.win-arm64" -t "$IMAGE" "$ROOT"
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake -S . -B "$BUILD_DIR" -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/llvm-mingw-aarch64.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake --build "$BUILD_DIR" -j
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" bash -c "cd $BUILD_DIR && cpack -G ZIP"
|
||||||
|
|
||||||
|
echo "Windows arm64: $ROOT/$BUILD_DIR/bin/openra3.exe (+ .zip)"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the Windows x86_64 target inside its own isolated image (Dockerfile.win).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
IMAGE="${OPENRA3_WINDOWS_IMAGE:-openra3-windows:local}"
|
||||||
|
BUILD_DIR="${1:-build/windows}"
|
||||||
|
|
||||||
|
docker build --target dev -f "$ROOT/Dockerfile.win" -t "$IMAGE" "$ROOT"
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" \
|
||||||
|
cmake -S . -B "$BUILD_DIR" -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/llvm-mingw-x86_64.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
docker run --rm -v "$ROOT:/work" -w /work "$IMAGE" cmake --build "$BUILD_DIR" -j
|
||||||
|
|
||||||
|
echo "Windows build: $ROOT/$BUILD_DIR/bin/openra3.exe (+ SDL3.dll)"
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# Regenerate the committed SPIR-V blobs under shaders/generated/.
|
||||||
|
#
|
||||||
|
# The build embeds the .spv files directly (see CMakeLists.txt), so no shader
|
||||||
|
# compiler is needed to build OpenRA3. Run this only after editing a .vert/.frag.
|
||||||
|
#
|
||||||
|
# Needs one of: glslc (shaderc) or glslangValidator (glslang-tools). On a host
|
||||||
|
# without either, use a throwaway container:
|
||||||
|
# docker run --rm -v "$PWD/shaders:/s" ubuntu:26.04 bash -c \
|
||||||
|
# "apt-get update -qq && apt-get install -y -qq glslang-tools && \
|
||||||
|
# cd /s && sh ./compile.sh"
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
OUT="$DIR/generated"
|
||||||
|
mkdir -p "$OUT"
|
||||||
|
|
||||||
|
compile() {
|
||||||
|
src="$1"
|
||||||
|
dst="$2"
|
||||||
|
if command -v glslc >/dev/null 2>&1; then
|
||||||
|
glslc "$src" -o "$dst"
|
||||||
|
else
|
||||||
|
glslangValidator -V "$src" -o "$dst"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for name in scene terrain; do
|
||||||
|
compile "$DIR/$name.vert" "$OUT/$name.vert.spv"
|
||||||
|
compile "$DIR/$name.frag" "$OUT/$name.frag.spv"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "wrote $OUT/{scene,terrain}.{vert,frag}.spv"
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// 2D image blit for the Direct3D backends (the D3D port of scene.vert/scene.frag).
|
||||||
|
//
|
||||||
|
// The scene image is drawn as a single fullscreen triangle sampling the
|
||||||
|
// software-rendered `ra3::render::image`. The constant buffer carries the
|
||||||
|
// destination rectangle in window-normalized coordinates (y down), so the map
|
||||||
|
// is letterboxed rather than stretched. D3D clip space has +Y up, so the vertex
|
||||||
|
// position flips Y relative to the Vulkan shader (which relies on Vulkan's
|
||||||
|
// +Y-down clip space); the sampled UVs and the image's top-left origin are
|
||||||
|
// unchanged.
|
||||||
|
|
||||||
|
cbuffer RectCB : register(b0) {
|
||||||
|
float4 rect; // xy = top-left (0..1), zw = size (0..1)
|
||||||
|
};
|
||||||
|
|
||||||
|
Texture2D scene_tex : register(t0);
|
||||||
|
SamplerState scene_smp : register(s0);
|
||||||
|
|
||||||
|
struct VSOut {
|
||||||
|
float4 pos : SV_Position;
|
||||||
|
float2 uv : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
VSOut VSMain(uint vertex_id : SV_VertexID) {
|
||||||
|
float2 p = float2((vertex_id << 1) & 2, vertex_id & 2);
|
||||||
|
VSOut o;
|
||||||
|
o.uv = (p - rect.xy) / rect.zw;
|
||||||
|
o.pos = float4(p.x * 2.0 - 1.0, 1.0 - p.y * 2.0, 0.0, 1.0);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 PSMain(VSOut input) : SV_Target {
|
||||||
|
if (input.uv.x < 0.0 || input.uv.x > 1.0 || input.uv.y < 0.0 || input.uv.y > 1.0) discard;
|
||||||
|
return scene_tex.Sample(scene_smp, input.uv);
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
// GPU heightfield raymarcher for the Direct3D backends (the D3D port of
|
||||||
|
// terrain.vert/terrain.frag).
|
||||||
|
//
|
||||||
|
// Textures: heightmap (R16), a per-cell blend record (R16G16B16A16_UNORM: base
|
||||||
|
// layer, blend layer, three-way layer, packed direction/flags; unpacked with
|
||||||
|
// *65535) and a texture array of the tile materials (RGBA8, REPEAT). The
|
||||||
|
// material is sampled continuously (`uv = cell / span`), as the retail
|
||||||
|
// `Terrain.fx` / OpenSAGE `Terrain.frag` do, so it never restarts at a cell
|
||||||
|
// edge; material boundaries cross-fade with the SAGE blend ramp.
|
||||||
|
//
|
||||||
|
// The Vulkan push constants (20 floats) become a constant buffer.
|
||||||
|
|
||||||
|
cbuffer TerrainCB : register(b0) {
|
||||||
|
float4 cam; // x=target_x, y=target_y, z=yaw, w=height
|
||||||
|
float4 params; // x=pitch, y=fov, z=water_z, w=has_water
|
||||||
|
float4 sun; // xyz=sun dir, w=ambient
|
||||||
|
float4 mapinfo; // x=W, y=H, z=unused, w=z_scale
|
||||||
|
float4 misc; // x=time, y=unused, z=cells per texture repeat, w=aspect
|
||||||
|
};
|
||||||
|
|
||||||
|
Texture2D<float> heightmap : register(t0);
|
||||||
|
Texture2D<float4> celldata : register(t1);
|
||||||
|
Texture2DArray<float4> atlas : register(t2);
|
||||||
|
SamplerState height_smp : register(s0);
|
||||||
|
SamplerState cell_smp : register(s1);
|
||||||
|
SamplerState atlas_smp : register(s2);
|
||||||
|
|
||||||
|
static const float CELL = 10.0; // must match ra3::terrain::cell_size
|
||||||
|
|
||||||
|
struct VSOut {
|
||||||
|
float4 pos : SV_Position;
|
||||||
|
float2 uv : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
VSOut VSMain(uint vertex_id : SV_VertexID) {
|
||||||
|
float2 p = float2((vertex_id << 1) & 2, vertex_id & 2);
|
||||||
|
VSOut o;
|
||||||
|
o.uv = p;
|
||||||
|
o.pos = float4(p.x * 2.0 - 1.0, 1.0 - p.y * 2.0, 0.0, 1.0);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
float height_at(int2 c) {
|
||||||
|
c = clamp(c, int2(0, 0), int2((int) mapinfo.x - 1, (int) mapinfo.y - 1));
|
||||||
|
return heightmap.Load(int3(c, 0)) * 65535.0 * mapinfo.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
float world_height(float wx, float wy) {
|
||||||
|
float world_w = mapinfo.x * CELL;
|
||||||
|
float world_h = mapinfo.y * CELL;
|
||||||
|
if (wx < 0.0 || wy < 0.0 || wx >= world_w || wy >= world_h) return -1.0e9;
|
||||||
|
int2 c = int2((int) (wx / CELL), (int) ((world_h - wy) / CELL));
|
||||||
|
return height_at(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 sky_color(float3 dir) {
|
||||||
|
float3 d = normalize(dir);
|
||||||
|
float3 sun_dir = normalize(sun.xyz);
|
||||||
|
float t = clamp(d.z, 0.0, 1.0);
|
||||||
|
float3 horizon = float3(0.70, 0.78, 0.85);
|
||||||
|
float3 zenith = float3(0.28, 0.48, 0.80);
|
||||||
|
float3 col = lerp(horizon, zenith, pow(t, 0.6));
|
||||||
|
float s = max(dot(d, sun_dir), 0.0);
|
||||||
|
col += float3(1.0, 0.95, 0.82) * pow(s, 300.0) * 1.6; // sun disk
|
||||||
|
col += float3(1.0, 0.90, 0.72) * pow(s, 8.0) * 0.18; // glow
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The retail SAGE blend ramp: 0 at one edge of the cell, 1 at the opposite.
|
||||||
|
// Direction: 1 right, 2 top, 4 top-right, 8 top-left; flag bit 0 flips,
|
||||||
|
// bit 1 marks a two-sided diagonal.
|
||||||
|
float blend_factor(uint direction, uint flags, float2 f) {
|
||||||
|
bool flipped = (flags & 1u) != 0u;
|
||||||
|
bool two_sided = (flags & 2u) != 0u;
|
||||||
|
if (flipped) {
|
||||||
|
if (direction == 1u) {
|
||||||
|
f.x = 1.0 - f.x;
|
||||||
|
} else if (direction == 2u || direction == 4u || direction == 8u) {
|
||||||
|
f.y = 1.0 - f.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direction == 1u) return f.x;
|
||||||
|
if (direction == 2u) return f.y;
|
||||||
|
if (direction == 4u) {
|
||||||
|
float s = (1.0 - f.x) + (1.0 - f.y);
|
||||||
|
return two_sided ? 1.0 - clamp(s - 1.0, 0.0, 1.0) : clamp(1.0 - s, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
if (direction == 8u) {
|
||||||
|
float s = f.x + (1.0 - f.y);
|
||||||
|
return two_sided ? 1.0 - clamp(s - 1.0, 0.0, 1.0) : clamp(1.0 - s, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample one tile material layer at global cell coordinates. The texture repeats
|
||||||
|
// every `span` cells with REPEAT addressing, so it never restarts at a cell edge.
|
||||||
|
float3 sample_layer(uint layer, float wx, float wy) {
|
||||||
|
float span = max(misc.z, 1.0);
|
||||||
|
uint lw = 0;
|
||||||
|
uint lh = 0;
|
||||||
|
uint layer_count = 0;
|
||||||
|
atlas.GetDimensions(lw, lh, layer_count);
|
||||||
|
float l = (float) min(layer, layer_count > 0u ? layer_count - 1u : 0u);
|
||||||
|
return atlas.Sample(atlas_smp, float3(float2(wx, wy) / span, l)).rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 PSMain(VSOut input) : SV_Target {
|
||||||
|
float4 p = cam;
|
||||||
|
float pitch = clamp(params.x, 0.15, 1.45);
|
||||||
|
float fov = clamp(params.y, 0.3, 1.4);
|
||||||
|
float world_w = mapinfo.x * CELL;
|
||||||
|
float world_h = mapinfo.y * CELL;
|
||||||
|
|
||||||
|
float cp = cos(pitch);
|
||||||
|
float3 fwd = float3(cp * sin(p.z), cp * cos(p.z), -sin(pitch));
|
||||||
|
float3 right = normalize(cross(fwd, float3(0, 0, 1)));
|
||||||
|
float3 up = cross(right, fwd);
|
||||||
|
|
||||||
|
float target_z = world_height(p.x, p.y);
|
||||||
|
if (target_z < -1.0e8) target_z = 0.0;
|
||||||
|
float dist = p.w / sin(pitch);
|
||||||
|
float3 cam_pos = float3(p.x, p.y, target_z + p.w) - fwd * dist;
|
||||||
|
|
||||||
|
float2 ndc = float2(input.uv.x * 2.0 - 1.0, 1.0 - input.uv.y * 2.0);
|
||||||
|
float aspect = misc.w;
|
||||||
|
float th = tan(fov * 0.5);
|
||||||
|
float3 dir = normalize(fwd + right * ndc.x * th * aspect + up * ndc.y * th);
|
||||||
|
|
||||||
|
if (dir.z >= -1e-4) {
|
||||||
|
return float4(sky_color(dir), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// March the heightfield (bounded work: the step grows toward the horizon).
|
||||||
|
float t = CELL * 0.5;
|
||||||
|
float dt = CELL * 0.5;
|
||||||
|
float prev = t;
|
||||||
|
bool hit = false;
|
||||||
|
float hit_t = 0.0;
|
||||||
|
for (int i = 0; i < 512 && t < 60000.0; ++i) {
|
||||||
|
float3 w = cam_pos + dir * t;
|
||||||
|
if (w.x < 0.0 || w.y < 0.0 || w.x >= world_w || w.y >= world_h) {
|
||||||
|
prev = t;
|
||||||
|
dt *= 1.06;
|
||||||
|
t += dt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (params.w > 0.5 && w.z <= params.z) {
|
||||||
|
hit = true;
|
||||||
|
hit_t = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (w.z <= world_height(w.x, w.y)) {
|
||||||
|
hit = true;
|
||||||
|
hit_t = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = t;
|
||||||
|
dt *= 1.06;
|
||||||
|
t += dt;
|
||||||
|
}
|
||||||
|
if (!hit) {
|
||||||
|
return float4(sky_color(dir), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float lo = prev;
|
||||||
|
float hi = hit_t;
|
||||||
|
for (int i = 0; i < 6; ++i) {
|
||||||
|
float mid = 0.5 * (lo + hi);
|
||||||
|
float3 w = cam_pos + dir * mid;
|
||||||
|
bool water = params.w > 0.5 && w.z <= params.z;
|
||||||
|
if (water || w.z <= world_height(w.x, w.y)) {
|
||||||
|
hi = mid;
|
||||||
|
} else {
|
||||||
|
lo = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float3 hitpos = cam_pos + dir * hi;
|
||||||
|
|
||||||
|
float3 sun_dir = normalize(sun.xyz);
|
||||||
|
float ambient = sun.w;
|
||||||
|
|
||||||
|
if (params.w > 0.5 && hitpos.z <= params.z + 0.01) {
|
||||||
|
// Water: animated normal from a procedural wave, sky reflection + fresnel.
|
||||||
|
float time = misc.x;
|
||||||
|
float2 q = hitpos.xy * 0.015;
|
||||||
|
float nx = sin(q.x * 1.3 + time * 1.7) + 0.5 * sin(q.x * 3.1 - time * 2.3);
|
||||||
|
float ny = sin(q.y * 1.1 - time * 1.3) + 0.5 * sin(q.y * 2.7 + time * 1.9);
|
||||||
|
float3 n = normalize(float3(nx * 0.06, ny * 0.06, 1.0));
|
||||||
|
float fres = pow(1.0 - clamp(-dir.z, 0.0, 1.0), 3.0);
|
||||||
|
float3 deep = float3(0.03, 0.16, 0.28);
|
||||||
|
float3 refl = sky_color(reflect(dir, n));
|
||||||
|
float lam = max(0.0, dot(n, sun_dir));
|
||||||
|
float3 water = lerp(deep, refl, clamp(0.25 + 0.55 * fres, 0.0, 0.9));
|
||||||
|
water += float3(1.0, 0.98, 0.9) * pow(lam, 64.0) * 0.6; // sun glint
|
||||||
|
float wfog = clamp(1.0 - exp(-hi * 0.00009), 0.0, 0.75);
|
||||||
|
water = lerp(water, sky_color(float3(dir.x, dir.y, 0.0)), wfog);
|
||||||
|
return float4(water, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terrain: read the per-cell blend record, sample the base/blend/three-way
|
||||||
|
// material layers continuously and ramp between them across the cell.
|
||||||
|
float wx = hitpos.x / CELL;
|
||||||
|
float wy = (world_h - hitpos.y) / CELL;
|
||||||
|
int cx = clamp((int) wx, 0, (int) mapinfo.x - 1);
|
||||||
|
int cy = clamp((int) wy, 0, (int) mapinfo.y - 1);
|
||||||
|
float fx = wx - floor(wx);
|
||||||
|
float fy = wy - floor(wy);
|
||||||
|
|
||||||
|
uint4 record = (uint4) (celldata.Load(int3(cx, cy, 0)) * 65535.0 + 0.5);
|
||||||
|
uint packed = record.w;
|
||||||
|
uint dir1 = packed & 0xFu;
|
||||||
|
uint flags1 = (packed >> 4u) & 0x3u;
|
||||||
|
uint dir2 = (packed >> 8u) & 0xFu;
|
||||||
|
uint flags2 = (packed >> 12u) & 0x3u;
|
||||||
|
|
||||||
|
float2 fracUV = float2(fx, fy);
|
||||||
|
float3 c0 = sample_layer(record.x, wx, wy);
|
||||||
|
float3 c1 = sample_layer(record.y, wx, wy);
|
||||||
|
float3 c2 = sample_layer(record.z, wx, wy);
|
||||||
|
float f1 = blend_factor(dir1, flags1, fracUV);
|
||||||
|
float f2 = blend_factor(dir2, flags2, fracUV);
|
||||||
|
float3 albedo = lerp(lerp(c0, c1, f1), c2, f2);
|
||||||
|
|
||||||
|
// Per-pixel normal from the heightfield.
|
||||||
|
float hl = world_height(hitpos.x - CELL, hitpos.y);
|
||||||
|
float hr = world_height(hitpos.x + CELL, hitpos.y);
|
||||||
|
float hd = world_height(hitpos.x, hitpos.y - CELL);
|
||||||
|
float hu = world_height(hitpos.x, hitpos.y + CELL);
|
||||||
|
float3 n = normalize(float3(hl - hr, hd - hu, 2.0 * CELL));
|
||||||
|
float lambert = max(0.0, dot(n, sun_dir));
|
||||||
|
float3 lit = albedo * (ambient + (1.0 - ambient) * lambert);
|
||||||
|
// Distance haze toward the horizon so the map edge blends into the sky.
|
||||||
|
float fog = clamp(1.0 - exp(-hi * 0.00009), 0.0, 0.75);
|
||||||
|
lit = lerp(lit, sky_color(float3(dir.x, dir.y, 0.0)), fog);
|
||||||
|
return float4(lit, 1.0);
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2D scene;
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 in_uv;
|
||||||
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
if (in_uv.x < 0.0 || in_uv.x > 1.0 || in_uv.y < 0.0 || in_uv.y > 1.0) discard;
|
||||||
|
out_color = texture(scene, in_uv);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// Fullscreen triangle. The scene image is drawn as a single textured quad; the
|
||||||
|
// push constant carries the destination rectangle (in window-normalized
|
||||||
|
// coordinates, y down), so the map is letterboxed rather than stretched.
|
||||||
|
layout(push_constant) uniform Push {
|
||||||
|
vec4 rect; // xy = top-left (0..1), zw = size (0..1)
|
||||||
|
} pc;
|
||||||
|
|
||||||
|
layout(location = 0) out vec2 out_uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Vulkan clip space has +Y pointing DOWN, so p already runs top->bottom and
|
||||||
|
// matches the image's top-left origin; do NOT flip it (OpenGL would).
|
||||||
|
vec2 p = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
|
||||||
|
out_uv = (p - pc.rect.xy) / pc.rect.zw;
|
||||||
|
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// GPU heightfield raymarcher for the real RA3 terrain.
|
||||||
|
//
|
||||||
|
// Textures: heightmap (R16), a per-cell blend record (R16G16B16A16_UNORM: base
|
||||||
|
// layer, blend layer, three-way layer, packed direction/flags; unpacked with
|
||||||
|
// *65535) and a texture array of the tile materials (RGBA8, mipmapped, REPEAT).
|
||||||
|
// The material is sampled **continuously** (`uv = cell / (2 * cellSize)`), as the
|
||||||
|
// retail `Terrain.fx` / OpenSAGE `Terrain.frag` do, so it never restarts at a
|
||||||
|
// cell edge; material boundaries cross-fade with the SAGE blend ramp.
|
||||||
|
layout(binding = 0) uniform sampler2D heightmap;
|
||||||
|
layout(binding = 1) uniform sampler2D celldata;
|
||||||
|
layout(binding = 2) uniform sampler2DArray atlas;
|
||||||
|
|
||||||
|
layout(push_constant) uniform Push {
|
||||||
|
vec4 cam; // x=target_x, y=target_y, z=yaw, w=height
|
||||||
|
vec4 params; // x=pitch, y=fov, z=water_z, w=has_water
|
||||||
|
vec4 sun; // xyz=sun dir, w=ambient
|
||||||
|
vec4 mapinfo; // x=W, y=H, z=unused, w=z_scale
|
||||||
|
vec4 misc; // x=time, y=unused, z=cells per texture repeat, w=aspect
|
||||||
|
} pc;
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 in_uv;
|
||||||
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
|
const float CELL = 10.0; // must match ra3::terrain::cell_size
|
||||||
|
|
||||||
|
float height_at(ivec2 c) {
|
||||||
|
c = clamp(c, ivec2(0), ivec2(pc.mapinfo.xy) - 1);
|
||||||
|
return texelFetch(heightmap, c, 0).r * 65535.0 * pc.mapinfo.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
float world_height(float wx, float wy) {
|
||||||
|
float world_w = pc.mapinfo.x * CELL;
|
||||||
|
float world_h = pc.mapinfo.y * CELL;
|
||||||
|
if (wx < 0.0 || wy < 0.0 || wx >= world_w || wy >= world_h) return -1.0e9;
|
||||||
|
ivec2 c = ivec2(int(wx / CELL), int((world_h - wy) / CELL));
|
||||||
|
return height_at(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 sky_color(vec3 dir) {
|
||||||
|
vec3 d = normalize(dir);
|
||||||
|
vec3 sun_dir = normalize(pc.sun.xyz);
|
||||||
|
float t = clamp(d.z, 0.0, 1.0);
|
||||||
|
vec3 horizon = vec3(0.70, 0.78, 0.85);
|
||||||
|
vec3 zenith = vec3(0.28, 0.48, 0.80);
|
||||||
|
vec3 col = mix(horizon, zenith, pow(t, 0.6));
|
||||||
|
float sun = max(dot(d, sun_dir), 0.0);
|
||||||
|
col += vec3(1.0, 0.95, 0.82) * pow(sun, 300.0) * 1.6; // sun disk
|
||||||
|
col += vec3(1.0, 0.90, 0.72) * pow(sun, 8.0) * 0.18; // glow
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The retail SAGE blend ramp: 0 at one edge of the cell, 1 at the opposite.
|
||||||
|
// Direction: 1 right, 2 top, 4 top-right, 8 top-left; flag bit 0 flips,
|
||||||
|
// bit 1 marks a two-sided diagonal.
|
||||||
|
float blend_factor(uint direction, uint flags, vec2 f) {
|
||||||
|
bool flipped = (flags & 1u) != 0u;
|
||||||
|
bool two_sided = (flags & 2u) != 0u;
|
||||||
|
if (flipped) {
|
||||||
|
if (direction == 1u) {
|
||||||
|
f.x = 1.0 - f.x;
|
||||||
|
} else if (direction == 2u || direction == 4u || direction == 8u) {
|
||||||
|
f.y = 1.0 - f.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direction == 1u) return f.x;
|
||||||
|
if (direction == 2u) return f.y;
|
||||||
|
if (direction == 4u) {
|
||||||
|
float s = (1.0 - f.x) + (1.0 - f.y);
|
||||||
|
return two_sided ? 1.0 - clamp(s - 1.0, 0.0, 1.0) : clamp(1.0 - s, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
if (direction == 8u) {
|
||||||
|
float s = f.x + (1.0 - f.y);
|
||||||
|
return two_sided ? 1.0 - clamp(s - 1.0, 0.0, 1.0) : clamp(1.0 - s, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample one tile material layer at global cell coordinates. The texture repeats
|
||||||
|
// every `span` cells (SAGE `uv / (cellSize * 2)`) with REPEAT addressing, so it
|
||||||
|
// never restarts at a cell edge.
|
||||||
|
vec3 sample_layer(uint layer, float wx, float wy) {
|
||||||
|
float span = max(pc.misc.z, 1.0);
|
||||||
|
int layer_count = textureSize(atlas, 0).z;
|
||||||
|
float l = float(min(layer, uint(layer_count - 1)));
|
||||||
|
return texture(atlas, vec3(vec2(wx, wy) / span, l)).rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 p = pc.cam;
|
||||||
|
float pitch = clamp(pc.params.x, 0.15, 1.45);
|
||||||
|
float fov = clamp(pc.params.y, 0.3, 1.4);
|
||||||
|
float world_w = pc.mapinfo.x * CELL;
|
||||||
|
float world_h = pc.mapinfo.y * CELL;
|
||||||
|
|
||||||
|
float cp = cos(pitch);
|
||||||
|
vec3 fwd = vec3(cp * sin(p.z), cp * cos(p.z), -sin(pitch));
|
||||||
|
vec3 right = normalize(cross(fwd, vec3(0, 0, 1)));
|
||||||
|
vec3 up = cross(right, fwd);
|
||||||
|
|
||||||
|
float target_z = world_height(p.x, p.y);
|
||||||
|
if (target_z < -1.0e8) target_z = 0.0;
|
||||||
|
float dist = p.w / sin(pitch);
|
||||||
|
vec3 cam = vec3(p.x, p.y, target_z + p.w) - fwd * dist;
|
||||||
|
|
||||||
|
float aspect = 1.0; // set by caller implicitly via square-ish UV; corrected below
|
||||||
|
vec2 ndc = vec2(in_uv.x * 2.0 - 1.0, 1.0 - in_uv.y * 2.0);
|
||||||
|
// aspect passed in misc.w
|
||||||
|
aspect = pc.misc.w;
|
||||||
|
float th = tan(fov * 0.5);
|
||||||
|
vec3 dir = normalize(fwd + right * ndc.x * th * aspect + up * ndc.y * th);
|
||||||
|
|
||||||
|
if (dir.z >= -1e-4) {
|
||||||
|
out_color = vec4(sky_color(dir), 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// March the heightfield (bounded work: the step grows toward the horizon).
|
||||||
|
float t = CELL * 0.5;
|
||||||
|
float dt = CELL * 0.5;
|
||||||
|
float prev = t;
|
||||||
|
bool hit = false;
|
||||||
|
float hit_t = 0.0;
|
||||||
|
for (int i = 0; i < 512 && t < 60000.0; ++i) {
|
||||||
|
vec3 w = cam + dir * t;
|
||||||
|
if (w.x < 0.0 || w.y < 0.0 || w.x >= world_w || w.y >= world_h) {
|
||||||
|
prev = t; dt *= 1.06; t += dt; continue;
|
||||||
|
}
|
||||||
|
if (pc.params.w > 0.5 && w.z <= pc.params.z) { hit = true; hit_t = t; break; }
|
||||||
|
if (w.z <= world_height(w.x, w.y)) { hit = true; hit_t = t; break; }
|
||||||
|
prev = t; dt *= 1.06; t += dt;
|
||||||
|
}
|
||||||
|
if (!hit) { out_color = vec4(sky_color(dir), 1.0); return; }
|
||||||
|
|
||||||
|
float lo = prev, hi = hit_t;
|
||||||
|
for (int i = 0; i < 6; ++i) {
|
||||||
|
float mid = 0.5 * (lo + hi);
|
||||||
|
vec3 w = cam + dir * mid;
|
||||||
|
bool water = pc.params.w > 0.5 && w.z <= pc.params.z;
|
||||||
|
if (water || w.z <= world_height(w.x, w.y)) hi = mid; else lo = mid;
|
||||||
|
}
|
||||||
|
vec3 hitpos = cam + dir * hi;
|
||||||
|
|
||||||
|
vec3 sun = normalize(pc.sun.xyz);
|
||||||
|
float ambient = pc.sun.w;
|
||||||
|
|
||||||
|
if (pc.params.w > 0.5 && hitpos.z <= pc.params.z + 0.01) {
|
||||||
|
// Water: animated normal from a procedural wave, sky reflection + fresnel.
|
||||||
|
float time = pc.misc.x;
|
||||||
|
vec2 q = hitpos.xy * 0.015;
|
||||||
|
float nx = sin(q.x * 1.3 + time * 1.7) + 0.5 * sin(q.x * 3.1 - time * 2.3);
|
||||||
|
float ny = sin(q.y * 1.1 - time * 1.3) + 0.5 * sin(q.y * 2.7 + time * 1.9);
|
||||||
|
vec3 n = normalize(vec3(nx * 0.06, ny * 0.06, 1.0));
|
||||||
|
float fres = pow(1.0 - clamp(-dir.z, 0.0, 1.0), 3.0);
|
||||||
|
vec3 deep = vec3(0.03, 0.16, 0.28);
|
||||||
|
vec3 refl = sky_color(reflect(dir, n));
|
||||||
|
float lam = max(0.0, dot(n, sun));
|
||||||
|
vec3 water = mix(deep, refl, clamp(0.25 + 0.55 * fres, 0.0, 0.9));
|
||||||
|
water += vec3(1.0, 0.98, 0.9) * pow(lam, 64.0) * 0.6; // sun glint
|
||||||
|
float wfog = clamp(1.0 - exp(-hi * 0.00009), 0.0, 0.75);
|
||||||
|
water = mix(water, sky_color(vec3(dir.x, dir.y, 0.0)), wfog);
|
||||||
|
out_color = vec4(water, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terrain: read the per-cell blend record, sample the base/blend/three-way
|
||||||
|
// material layers continuously and ramp between them across the cell.
|
||||||
|
float wx = hitpos.x / CELL;
|
||||||
|
float wy = (world_h - hitpos.y) / CELL;
|
||||||
|
int cx = clamp(int(wx), 0, int(pc.mapinfo.x) - 1);
|
||||||
|
int cy = clamp(int(wy), 0, int(pc.mapinfo.y) - 1);
|
||||||
|
float fx = wx - floor(wx);
|
||||||
|
float fy = wy - floor(wy);
|
||||||
|
|
||||||
|
uvec4 record = uvec4(texelFetch(celldata, ivec2(cx, cy), 0) * 65535.0 + 0.5);
|
||||||
|
uint packed = record.w;
|
||||||
|
uint dir1 = packed & 0xFu;
|
||||||
|
uint flags1 = (packed >> 4u) & 0x3u;
|
||||||
|
uint dir2 = (packed >> 8u) & 0xFu;
|
||||||
|
uint flags2 = (packed >> 12u) & 0x3u;
|
||||||
|
|
||||||
|
vec2 fracUV = vec2(fx, fy);
|
||||||
|
vec3 c0 = sample_layer(record.x, wx, wy);
|
||||||
|
vec3 c1 = sample_layer(record.y, wx, wy);
|
||||||
|
vec3 c2 = sample_layer(record.z, wx, wy);
|
||||||
|
float f1 = blend_factor(dir1, flags1, fracUV);
|
||||||
|
float f2 = blend_factor(dir2, flags2, fracUV);
|
||||||
|
vec3 albedo = mix(mix(c0, c1, f1), c2, f2);
|
||||||
|
|
||||||
|
// Per-pixel normal from the heightfield.
|
||||||
|
float hl = world_height(hitpos.x - CELL, hitpos.y);
|
||||||
|
float hr = world_height(hitpos.x + CELL, hitpos.y);
|
||||||
|
float hd = world_height(hitpos.x, hitpos.y - CELL);
|
||||||
|
float hu = world_height(hitpos.x, hitpos.y + CELL);
|
||||||
|
vec3 n = normalize(vec3(hl - hr, hd - hu, 2.0 * CELL));
|
||||||
|
float lambert = max(0.0, dot(n, sun));
|
||||||
|
vec3 lit = albedo * (ambient + (1.0 - ambient) * lambert);
|
||||||
|
// Distance haze toward the horizon so the map edge blends into the sky.
|
||||||
|
float fog = clamp(1.0 - exp(-hi * 0.00009), 0.0, 0.75);
|
||||||
|
lit = mix(lit, sky_color(vec3(dir.x, dir.y, 0.0)), fog);
|
||||||
|
out_color = vec4(lit, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// Fullscreen triangle; the terrain is ray-marched in the fragment shader
|
||||||
|
// (GPU), so the vertex stage only emits the screen UV.
|
||||||
|
layout(location = 0) out vec2 out_uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 p = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
|
||||||
|
out_uv = p;
|
||||||
|
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
+496
-11
@@ -1,34 +1,513 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
export module ra3.client;
|
export module ra3.client;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
import ra3.core;
|
import ra3.core;
|
||||||
import ra3.logic;
|
import ra3.logic;
|
||||||
|
import ra3.render;
|
||||||
|
import ra3.terrain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The presentation layer: an abstract output surface plus the client facade
|
* The presentation layer: an abstract output surface plus the client facade
|
||||||
* that owns the simulation and drives the frame loop.
|
* that owns the simulation and drives the frame loop.
|
||||||
*
|
*
|
||||||
* A future W3D/D3D9 backend implements the same `display` interface; the
|
* Backends implement only the low-level primitives (`init`, `present`,
|
||||||
* default headless backend keeps OpenRA3 runnable on a CPU-only box.
|
* `poll_event`, `window_size`, `key_down`, `shutdown`) and, optionally,
|
||||||
|
* `present_terrain`; the interactive loops (menu, image viewer, camera viewer,
|
||||||
|
* GPU terrain) and the loading screen live here once, so input mapping and the
|
||||||
|
* frame loop never diverge between the SDL and Vulkan backends. The loops
|
||||||
|
* assume the display is already initialized, which lets one window host a menu,
|
||||||
|
* a loading bar and a viewer in sequence.
|
||||||
*/
|
*/
|
||||||
export namespace ra3::client {
|
export namespace ra3::client {
|
||||||
using ra3::core::uint32;
|
using ra3::core::uint32;
|
||||||
|
using ra3::render::image;
|
||||||
|
using ra3::render::ui_event;
|
||||||
|
using ra3::render::ui_event_type;
|
||||||
|
using ra3::render::ui_key;
|
||||||
|
using ra3::render::view_rect;
|
||||||
|
|
||||||
|
/** Window parameters for a display. */
|
||||||
|
struct display_options {
|
||||||
|
std::string title = "OpenRA3";
|
||||||
|
int width = 1280;
|
||||||
|
int height = 720;
|
||||||
|
bool fullscreen = false;
|
||||||
|
int fps_limit = 0; ///< 0 = vertical sync (present throttled to the display).
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Small overlays drawn on top of the GPU terrain (top-left FPS, corner minimap). */
|
||||||
|
struct terrain_overlay {
|
||||||
|
image label; ///< top-left label (may be empty)
|
||||||
|
image minimap; ///< bottom-right overview (may be empty)
|
||||||
|
bool label_changed = true;
|
||||||
|
bool minimap_changed = true;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract output surface presented once per logic frame.
|
* Abstract presentation surface.
|
||||||
*/
|
*/
|
||||||
class display {
|
class display {
|
||||||
public:
|
public:
|
||||||
virtual ~display() = default;
|
virtual ~display() = default;
|
||||||
|
|
||||||
virtual auto begin_frame() -> void = 0;
|
virtual auto begin_frame() -> void {}
|
||||||
virtual auto end_frame() -> void = 0;
|
virtual auto end_frame() -> void {}
|
||||||
|
|
||||||
|
/** Create the window and rendering objects. */
|
||||||
|
[[nodiscard]] virtual auto init(const display_options &options) -> bool = 0;
|
||||||
|
/** Draw `frame` scaled into `dest` (window coordinates). `changed` is
|
||||||
|
* false when `frame` is the same image as the previous call, so the
|
||||||
|
* backend may skip re-uploading its texture. */
|
||||||
|
[[nodiscard]] virtual auto present(const image &frame, const view_rect &dest, bool changed) -> bool = 0;
|
||||||
|
/** Pop one OS event into `out`; returns false when the queue is empty. */
|
||||||
|
[[nodiscard]] virtual auto poll_event(ui_event &out) -> bool = 0;
|
||||||
|
/** Current drawable size in pixels. */
|
||||||
|
[[nodiscard]] virtual auto window_size() const -> std::pair<int, int> = 0;
|
||||||
|
/** Whether a key is currently held (for panning). */
|
||||||
|
[[nodiscard]] virtual auto key_down(ui_key key) const -> bool = 0;
|
||||||
|
virtual auto shutdown() -> void = 0;
|
||||||
[[nodiscard]] virtual auto name() const -> std::string_view = 0;
|
[[nodiscard]] virtual auto name() const -> std::string_view = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional GPU terrain path. The default reports `false`, so a low-end
|
||||||
|
* backend simply does not offer it; the Vulkan backend overrides it.
|
||||||
|
* `overlays` (FPS label, minimap) are drawn on top of the terrain.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual auto present_terrain(const ra3::terrain::gpu_terrain &, const ra3::render::camera3d &, float, const terrain_overlay &) -> bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether this backend can draw the GPU terrain path. */
|
||||||
|
[[nodiscard]] virtual auto supports_terrain() const -> bool { return false; }
|
||||||
|
|
||||||
|
int fps_limit_ = 0; ///< 0 = vsync, >0 = target FPS cap, <0 = uncapped.
|
||||||
|
|
||||||
|
/** Throttle an idle frame according to the configured limit. */
|
||||||
|
auto sleep_frame() const -> void {
|
||||||
|
if (fps_limit_ > 0) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(std::max(1, 1000 / fps_limit_)));
|
||||||
|
} else if (fps_limit_ < 0) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
// fps_limit_ == 0: vertical sync already blocks in present().
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- shared interactive loops (assume `init` succeeded) --------------
|
||||||
|
|
||||||
|
/** Letterbox `frame` into the current window. */
|
||||||
|
[[nodiscard]] auto present_fit(const image &frame, bool changed) -> bool {
|
||||||
|
const auto [w, h] = this->window_size();
|
||||||
|
return this->present(frame, ra3::render::fit_rect(static_cast<float>(frame.width()), static_cast<float>(frame.height()), static_cast<float>(w),
|
||||||
|
static_cast<float>(h)),
|
||||||
|
changed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Draw a warm loading screen with a progress bar (0..1). */
|
||||||
|
auto present_progress(float progress, std::string_view label) -> void {
|
||||||
|
const auto [w, h] = this->window_size();
|
||||||
|
const auto frame = ra3::render::compose_progress(static_cast<uint32>(std::max(1, w)), static_cast<uint32>(std::max(1, h)), progress, label);
|
||||||
|
this->present_fit(frame, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a staged loader on this display: `load` is called with a `report`
|
||||||
|
* callback that draws a progress bar and pumps events (returning false
|
||||||
|
* if the user closed the window).
|
||||||
|
*/
|
||||||
|
template<typename Loader>
|
||||||
|
[[nodiscard]] auto run_with_progress(Loader &&load) -> bool {
|
||||||
|
this->present_progress(0.0F, "Loading...");
|
||||||
|
const auto report = [&](float progress, std::string_view label) -> bool {
|
||||||
|
this->present_progress(progress, label);
|
||||||
|
ui_event event;
|
||||||
|
while (this->poll_event(event)) {
|
||||||
|
if (event.type == ui_event_type::quit) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return load(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interactive menu. `frame` returns the image to display for an input
|
||||||
|
* event, or `nullopt` when nothing changed (so the previous frame is
|
||||||
|
* kept and nothing is recomposed/re-uploaded). It sets `done` to end
|
||||||
|
* the loop. `frame` is called with the current drawable size so text
|
||||||
|
* stays crisp on HiDPI displays.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] auto menu_loop(const std::function<std::optional<image>(const ui_event &, uint32, uint32, bool &)> &frame) -> bool {
|
||||||
|
auto [sw, sh] = this->window_size();
|
||||||
|
const auto w = static_cast<uint32>(std::max(1, sw));
|
||||||
|
const auto h = static_cast<uint32>(std::max(1, sh));
|
||||||
|
bool done = false;
|
||||||
|
auto first = frame({}, w, h, done);
|
||||||
|
if (!first) return false;
|
||||||
|
image current = std::move(*first);
|
||||||
|
if (!this->present_fit(current, true)) return false;
|
||||||
|
while (!done) {
|
||||||
|
ui_event event;
|
||||||
|
bool idle = true;
|
||||||
|
bool changed = false;
|
||||||
|
while (this->poll_event(event)) {
|
||||||
|
idle = false;
|
||||||
|
if (event.type == ui_event_type::quit) {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto result = frame(event, w, h, done);
|
||||||
|
if (done) break;
|
||||||
|
if (result) {
|
||||||
|
current = std::move(*result);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (done) break;
|
||||||
|
if (changed) {
|
||||||
|
if (!this->present_fit(current, true)) break;
|
||||||
|
} else if (idle) {
|
||||||
|
if (!this->present_fit(current, false)) break;
|
||||||
|
this->sleep_frame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pan/zoom viewer over a raster image, reproducing the retail tactical
|
||||||
|
* view controls (wheel zoom, edge scroll, drag pan).
|
||||||
|
*/
|
||||||
|
[[nodiscard]] auto image_loop(const image &scene, ra3::render::view_camera camera) -> bool {
|
||||||
|
if (scene.empty()) return false;
|
||||||
|
camera.min_zoom = 1.0F;
|
||||||
|
camera.clamp_center();
|
||||||
|
|
||||||
|
float mouse_x = 0.0F;
|
||||||
|
float mouse_y = 0.0F;
|
||||||
|
bool running = true;
|
||||||
|
auto last = std::chrono::steady_clock::now();
|
||||||
|
while (running) {
|
||||||
|
ui_event event;
|
||||||
|
float drag_x = 0.0F;
|
||||||
|
float drag_y = 0.0F;
|
||||||
|
while (this->poll_event(event)) {
|
||||||
|
if (event.type == ui_event_type::quit) {
|
||||||
|
running = false;
|
||||||
|
} else if (event.type == ui_event_type::key) {
|
||||||
|
if (event.key == ui_key::cancel) {
|
||||||
|
running = false;
|
||||||
|
} else if (event.key == ui_key::page_up) {
|
||||||
|
camera.zoom_by(camera.zoom_step);
|
||||||
|
} else if (event.key == ui_key::page_down) {
|
||||||
|
camera.zoom_by(1.0F / camera.zoom_step);
|
||||||
|
}
|
||||||
|
} else if (event.type == ui_event_type::mouse_move) {
|
||||||
|
mouse_x = event.x;
|
||||||
|
mouse_y = event.y;
|
||||||
|
if (event.left) {
|
||||||
|
drag_x += event.dx;
|
||||||
|
drag_y += event.dy;
|
||||||
|
}
|
||||||
|
} else if (event.type == ui_event_type::wheel) {
|
||||||
|
camera.zoom_by(event.wheel > 0.0F ? camera.zoom_step : (1.0F / camera.zoom_step));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!running) break;
|
||||||
|
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
const auto dt = std::min(0.1F, std::chrono::duration<float>(now - last).count());
|
||||||
|
last = now;
|
||||||
|
|
||||||
|
const auto [w, h] = this->window_size();
|
||||||
|
auto view = camera.rect(static_cast<float>(scene.width()), static_cast<float>(scene.height()), static_cast<float>(w), static_cast<float>(h));
|
||||||
|
if (view.w > 0.0F && view.h > 0.0F && (drag_x != 0.0F || drag_y != 0.0F)) {
|
||||||
|
camera.scroll(-drag_x / view.w, -drag_y / view.h);
|
||||||
|
}
|
||||||
|
camera.edge_scroll(mouse_x, mouse_y, static_cast<float>(w), static_cast<float>(h), dt);
|
||||||
|
view = camera.rect(static_cast<float>(scene.width()), static_cast<float>(scene.height()), static_cast<float>(w), static_cast<float>(h));
|
||||||
|
if (!this->present(scene, view, false)) break;
|
||||||
|
this->sleep_frame();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interactive 3D camera over a software-rendered scene: the provider
|
||||||
|
* re-renders whenever the camera moves.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] auto camera_loop(const std::function<image(const ra3::render::camera3d &, uint32, uint32)> &provider, ra3::render::camera3d camera) -> bool {
|
||||||
|
auto [window_w, window_h] = this->window_size();
|
||||||
|
image current = provider(camera, static_cast<uint32>(std::max(1, window_w)), static_cast<uint32>(std::max(1, window_h)));
|
||||||
|
if (current.empty()) return false;
|
||||||
|
if (!this->present(current, {0.0F, 0.0F, static_cast<float>(window_w), static_cast<float>(window_h)}, true)) return false;
|
||||||
|
|
||||||
|
float mouse_x = 0.5F * static_cast<float>(window_w);
|
||||||
|
float mouse_y = 0.5F * static_cast<float>(window_h);
|
||||||
|
bool running = true;
|
||||||
|
auto last = std::chrono::steady_clock::now();
|
||||||
|
while (running) {
|
||||||
|
ui_event event;
|
||||||
|
float drag_x = 0.0F;
|
||||||
|
float drag_y = 0.0F;
|
||||||
|
bool dirty = false;
|
||||||
|
while (this->poll_event(event)) {
|
||||||
|
if (event.type == ui_event_type::quit) {
|
||||||
|
running = false;
|
||||||
|
} else if (event.type == ui_event_type::key) {
|
||||||
|
switch (event.key) {
|
||||||
|
case ui_key::cancel: running = false; break;
|
||||||
|
case ui_key::page_up: camera.height = std::clamp(camera.height / 1.15F, camera.min_height, camera.max_height); dirty = true; break;
|
||||||
|
case ui_key::page_down: camera.height = std::clamp(camera.height * 1.15F, camera.min_height, camera.max_height); dirty = true; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
} else if (event.type == ui_event_type::mouse_move) {
|
||||||
|
mouse_x = event.x;
|
||||||
|
mouse_y = event.y;
|
||||||
|
if (event.left) {
|
||||||
|
drag_x += event.dx;
|
||||||
|
drag_y += event.dy;
|
||||||
|
}
|
||||||
|
} else if (event.type == ui_event_type::wheel) {
|
||||||
|
camera.height = std::clamp(camera.height * (event.wheel > 0.0F ? (1.0F / 1.15F) : 1.15F), camera.min_height, camera.max_height);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!running) break;
|
||||||
|
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
const auto dt = std::min(0.1F, std::chrono::duration<float>(now - last).count());
|
||||||
|
last = now;
|
||||||
|
|
||||||
|
if (drag_x != 0.0F || drag_y != 0.0F) {
|
||||||
|
camera.yaw -= drag_x * 0.005F;
|
||||||
|
camera.pitch = std::clamp(camera.pitch + drag_y * 0.004F, 0.15F, 1.45F);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float move_x = 0.0F;
|
||||||
|
float move_y = 0.0F;
|
||||||
|
if (this->key_down(ui_key::up)) move_y += 1.0F;
|
||||||
|
if (this->key_down(ui_key::down)) move_y -= 1.0F;
|
||||||
|
if (this->key_down(ui_key::left)) move_x -= 1.0F;
|
||||||
|
if (this->key_down(ui_key::right)) move_x += 1.0F;
|
||||||
|
if (mouse_x <= 24.0F) {
|
||||||
|
move_x -= 1.0F;
|
||||||
|
} else if (mouse_x >= static_cast<float>(window_w) - 24.0F) {
|
||||||
|
move_x += 1.0F;
|
||||||
|
}
|
||||||
|
if (mouse_y <= 24.0F) {
|
||||||
|
move_y += 1.0F;
|
||||||
|
} else if (mouse_y >= static_cast<float>(window_h) - 24.0F) {
|
||||||
|
move_y -= 1.0F;
|
||||||
|
}
|
||||||
|
if (move_x != 0.0F || move_y != 0.0F) {
|
||||||
|
const auto fwd_x = std::sin(camera.yaw);
|
||||||
|
const auto fwd_y = std::cos(camera.yaw);
|
||||||
|
const auto right_x = std::cos(camera.yaw);
|
||||||
|
const auto right_y = -std::sin(camera.yaw);
|
||||||
|
const auto step = camera.height * 0.9F * dt;
|
||||||
|
camera.target_x += (fwd_x * move_y + right_x * move_x) * step;
|
||||||
|
camera.target_y += (fwd_y * move_y + right_y * move_x) * step;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto latest = this->window_size();
|
||||||
|
if (latest.first != window_w || latest.second != window_h) {
|
||||||
|
window_w = latest.first;
|
||||||
|
window_h = latest.second;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
if (dirty) {
|
||||||
|
current = provider(camera, static_cast<uint32>(std::max(1, window_w)), static_cast<uint32>(std::max(1, window_h)));
|
||||||
|
if (current.empty()) break;
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
if (!this->present(current, {0.0F, 0.0F, static_cast<float>(window_w), static_cast<float>(window_h)}, true)) break;
|
||||||
|
this->sleep_frame();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GPU terrain viewer. `present_terrain` draws the heightfield; the loop
|
||||||
|
* here owns the camera controls. The top-left shows the FPS (current /
|
||||||
|
* cap) and, when `minimap_overview` is not empty, a corner minimap with
|
||||||
|
* the camera location is drawn.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] auto terrain_loop(const ra3::terrain::gpu_terrain &terrain, ra3::render::camera3d camera, const image &minimap_overview) -> bool {
|
||||||
|
if (terrain.width == 0U || terrain.height == 0U || !this->supports_terrain()) return false;
|
||||||
|
|
||||||
|
const auto world_w = static_cast<float>(terrain.width) * 10.0F;
|
||||||
|
const auto world_h = static_cast<float>(terrain.height) * 10.0F;
|
||||||
|
camera.target_x = std::clamp(camera.target_x, 0.0F, world_w);
|
||||||
|
camera.target_y = std::clamp(camera.target_y, 0.0F, world_h);
|
||||||
|
|
||||||
|
auto [window_w, window_h] = this->window_size();
|
||||||
|
const auto start = std::chrono::steady_clock::now();
|
||||||
|
auto last = start;
|
||||||
|
float mouse_x = 0.5F * static_cast<float>(window_w);
|
||||||
|
float mouse_y = 0.5F * static_cast<float>(window_h);
|
||||||
|
|
||||||
|
terrain_overlay overlay;
|
||||||
|
uint32 fps = 0;
|
||||||
|
int fps_frames = 0;
|
||||||
|
auto fps_window = start;
|
||||||
|
auto minimap_time = start;
|
||||||
|
|
||||||
|
bool running = true;
|
||||||
|
bool presented = false;
|
||||||
|
while (running) {
|
||||||
|
ui_event event;
|
||||||
|
float drag_x = 0.0F;
|
||||||
|
float drag_y = 0.0F;
|
||||||
|
bool camera_moved = false;
|
||||||
|
while (this->poll_event(event)) {
|
||||||
|
if (event.type == ui_event_type::quit) {
|
||||||
|
running = false;
|
||||||
|
} else if (event.type == ui_event_type::key) {
|
||||||
|
switch (event.key) {
|
||||||
|
case ui_key::cancel: running = false; break;
|
||||||
|
case ui_key::page_up: camera.height = std::clamp(camera.height / 1.15F, camera.min_height, camera.max_height); camera_moved = true; break;
|
||||||
|
case ui_key::page_down: camera.height = std::clamp(camera.height * 1.15F, camera.min_height, camera.max_height); camera_moved = true; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
} else if (event.type == ui_event_type::mouse_move) {
|
||||||
|
mouse_x = event.x;
|
||||||
|
mouse_y = event.y;
|
||||||
|
if (event.left) {
|
||||||
|
drag_x += event.dx;
|
||||||
|
drag_y += event.dy;
|
||||||
|
}
|
||||||
|
} else if (event.type == ui_event_type::wheel) {
|
||||||
|
camera.height = std::clamp(camera.height * (event.wheel > 0.0F ? (1.0F / 1.15F) : 1.15F), camera.min_height, camera.max_height);
|
||||||
|
camera_moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!running) break;
|
||||||
|
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
const auto dt = std::min(0.1F, std::chrono::duration<float>(now - last).count());
|
||||||
|
last = now;
|
||||||
|
|
||||||
|
if (drag_x != 0.0F || drag_y != 0.0F) {
|
||||||
|
camera.yaw -= drag_x * 0.005F;
|
||||||
|
camera.pitch = std::clamp(camera.pitch + drag_y * 0.004F, 0.15F, 1.45F);
|
||||||
|
camera_moved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float move_x = 0.0F;
|
||||||
|
float move_y = 0.0F;
|
||||||
|
if (this->key_down(ui_key::up)) move_y += 1.0F;
|
||||||
|
if (this->key_down(ui_key::down)) move_y -= 1.0F;
|
||||||
|
if (this->key_down(ui_key::left)) move_x -= 1.0F;
|
||||||
|
if (this->key_down(ui_key::right)) move_x += 1.0F;
|
||||||
|
if (mouse_x <= 24.0F) {
|
||||||
|
move_x -= 1.0F;
|
||||||
|
} else if (mouse_x >= static_cast<float>(window_w) - 24.0F) {
|
||||||
|
move_x += 1.0F;
|
||||||
|
}
|
||||||
|
if (mouse_y <= 24.0F) {
|
||||||
|
move_y += 1.0F;
|
||||||
|
} else if (mouse_y >= static_cast<float>(window_h) - 24.0F) {
|
||||||
|
move_y -= 1.0F;
|
||||||
|
}
|
||||||
|
if (move_x != 0.0F || move_y != 0.0F) {
|
||||||
|
const auto fwd_x = std::sin(camera.yaw);
|
||||||
|
const auto fwd_y = std::cos(camera.yaw);
|
||||||
|
const auto right_x = std::cos(camera.yaw);
|
||||||
|
const auto right_y = -std::sin(camera.yaw);
|
||||||
|
const auto step = camera.height * 0.9F * dt;
|
||||||
|
camera.target_x += (fwd_x * move_y + right_x * move_x) * step;
|
||||||
|
camera.target_y += (fwd_y * move_y + right_y * move_x) * step;
|
||||||
|
camera_moved = true;
|
||||||
|
}
|
||||||
|
camera.target_x = std::clamp(camera.target_x, 0.0F, world_w);
|
||||||
|
camera.target_y = std::clamp(camera.target_y, 0.0F, world_h);
|
||||||
|
|
||||||
|
const auto latest = this->window_size();
|
||||||
|
if (latest.first != window_w || latest.second != window_h) {
|
||||||
|
window_w = latest.first;
|
||||||
|
window_h = latest.second;
|
||||||
|
camera_moved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (camera_moved) {
|
||||||
|
++fps_frames;
|
||||||
|
const auto window_s = std::chrono::duration<float>(now - fps_window).count();
|
||||||
|
if (window_s >= 0.4F) {
|
||||||
|
fps = static_cast<uint32>(std::lround(static_cast<float>(fps_frames) / window_s));
|
||||||
|
fps_frames = 0;
|
||||||
|
fps_window = now;
|
||||||
|
overlay.label = ra3::render::compose_fps_label(fps, fps_limit_);
|
||||||
|
overlay.label_changed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fps_frames = 0;
|
||||||
|
fps_window = now;
|
||||||
|
}
|
||||||
|
const auto since_minimap = std::chrono::duration<float>(now - minimap_time).count();
|
||||||
|
if (!minimap_overview.empty() && (overlay.minimap.empty() || (camera_moved && since_minimap >= 0.15F))) {
|
||||||
|
const auto u = camera.target_x / world_w;
|
||||||
|
const auto v = 1.0F - camera.target_y / world_h;
|
||||||
|
overlay.minimap = ra3::render::compose_minimap(minimap_overview, u, v);
|
||||||
|
overlay.minimap_changed = true;
|
||||||
|
minimap_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render only when the view changes: an idle terrain costs nothing.
|
||||||
|
if (camera_moved || !presented) {
|
||||||
|
const auto time_s = std::chrono::duration<float>(now - start).count();
|
||||||
|
if (!this->present_terrain(terrain, camera, time_s, overlay)) {
|
||||||
|
return presented;
|
||||||
|
}
|
||||||
|
overlay.label_changed = false;
|
||||||
|
overlay.minimap_changed = false;
|
||||||
|
presented = true;
|
||||||
|
}
|
||||||
|
this->sleep_frame();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- one-shot helpers (own the lifecycle) ----------------------------
|
||||||
|
|
||||||
|
[[nodiscard]] auto run_menu(const display_options &options, const std::function<std::optional<image>(const ui_event &, uint32, uint32, bool &)> &frame) -> bool {
|
||||||
|
if (!this->init(options)) return false;
|
||||||
|
(void)this->menu_loop(frame);
|
||||||
|
this->shutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto run_image(const display_options &options, const image &scene, ra3::render::view_camera camera) -> bool {
|
||||||
|
if (scene.empty() || !this->init(options)) return false;
|
||||||
|
(void)this->image_loop(scene, camera);
|
||||||
|
this->shutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto run_camera(const display_options &options, const std::function<image(const ra3::render::camera3d &, uint32, uint32)> &provider,
|
||||||
|
ra3::render::camera3d camera) -> bool {
|
||||||
|
if (!this->init(options)) return false;
|
||||||
|
(void)this->camera_loop(provider, camera);
|
||||||
|
this->shutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto run_terrain(const display_options &options, const ra3::terrain::gpu_terrain &terrain, ra3::render::camera3d camera) -> bool {
|
||||||
|
if (!this->init(options)) return false;
|
||||||
|
(void)this->terrain_loop(terrain, camera, {});
|
||||||
|
this->shutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GPU terrain viewer with a corner minimap overlay. */
|
||||||
|
[[nodiscard]] auto run_terrain(const display_options &options, const ra3::terrain::gpu_terrain &terrain, ra3::render::camera3d camera,
|
||||||
|
const image &minimap) -> bool {
|
||||||
|
if (!this->init(options)) return false;
|
||||||
|
(void)this->terrain_loop(terrain, camera, minimap);
|
||||||
|
this->shutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,6 +517,12 @@ export namespace ra3::client {
|
|||||||
public:
|
public:
|
||||||
auto begin_frame() -> void override {}
|
auto begin_frame() -> void override {}
|
||||||
auto end_frame() -> void override {}
|
auto end_frame() -> void override {}
|
||||||
|
[[nodiscard]] auto init(const display_options &) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto present(const image &, const view_rect &, bool) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto poll_event(ui_event &) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto window_size() const -> std::pair<int, int> override { return {0, 0}; }
|
||||||
|
[[nodiscard]] auto key_down(ui_key) const -> bool override { return false; }
|
||||||
|
auto shutdown() -> void override {}
|
||||||
[[nodiscard]] auto name() const -> std::string_view override { return "headless"; }
|
[[nodiscard]] auto name() const -> std::string_view override { return "headless"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+3
-16
@@ -1,20 +1,7 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <compare>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <limits>
|
|
||||||
#include <optional>
|
|
||||||
#include <ostream>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
export module ra3.core;
|
export module ra3.core;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fundamental engine types shared by every OpenRA3 module.
|
* Fundamental engine types shared by every OpenRA3 module.
|
||||||
*
|
*
|
||||||
@@ -24,7 +11,7 @@ export module ra3.core;
|
|||||||
*/
|
*/
|
||||||
export namespace ra3::core {
|
export namespace ra3::core {
|
||||||
inline constexpr int version_major = 0;
|
inline constexpr int version_major = 0;
|
||||||
inline constexpr int version_minor = 1;
|
inline constexpr int version_minor = 3;
|
||||||
inline constexpr int version_patch = 0;
|
inline constexpr int version_patch = 0;
|
||||||
|
|
||||||
using real = float;
|
using real = float;
|
||||||
|
|||||||
@@ -0,0 +1,615 @@
|
|||||||
|
export module ra3.data;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
|
export import ra3.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Faithful Red Alert 3 gameplay data.
|
||||||
|
*
|
||||||
|
* Every number in this module is taken from the retail game's own asset
|
||||||
|
* definitions. Two sources back it, both auditable:
|
||||||
|
*
|
||||||
|
* 1. EA's open-sourced RA3 asset XML (`CnC_Modding_Support/Red Alert 3/Xml`,
|
||||||
|
* GPLv3) - the `*.xml` that `BinaryAssetBuilder` compiles into `*.big`.
|
||||||
|
* The `ra3_id` on each record is the originating `GameObject` id, so a
|
||||||
|
* value can be looked up in `Allied/Structures/AlliedPowerPlant.xml`,
|
||||||
|
* `Allied/Infantry/AlliedScoutInfantry.xml`, etc.
|
||||||
|
* 2. The shipped `ArmorTemplate` percentages, cross-checked against the
|
||||||
|
* compiled assets by the sibling `ra3-headless` extractor.
|
||||||
|
*
|
||||||
|
* The *semantics* of the fields (armour adjusts damage per damage type, a
|
||||||
|
* weapon's `AntiMask` decides what it may target, `UNRESISTABLE` bypasses
|
||||||
|
* armour) follow SAGE and are corroborated by the OpenSAGE re-implementation
|
||||||
|
* (GPLv3). Where a value is not yet recovered the field is documented as such.
|
||||||
|
*
|
||||||
|
* Nothing here is loaded from disk yet: the compiled `global.bin` /
|
||||||
|
* `static.*.bin` deserialiser is a later milestone, so the base-game balance
|
||||||
|
* used by `ra3.skirmish` is pinned as constants.
|
||||||
|
*/
|
||||||
|
export namespace ra3::data {
|
||||||
|
using ra3::core::int32;
|
||||||
|
using ra3::core::uint8;
|
||||||
|
using ra3::core::uint32;
|
||||||
|
using ra3::core::usize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* World units per terrain cell. RA3's `TheTerrainLogic` grid stores
|
||||||
|
* `1 / cellSize` at `grid + 0x38` and the layer reports `cellSize == 10`.
|
||||||
|
*/
|
||||||
|
inline constexpr float cell_size = 10.0F;
|
||||||
|
inline constexpr float inv_cell_size = 1.0F / cell_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The simulation advances on a fixed 30 Hz logic tick, matching SAGE.
|
||||||
|
*
|
||||||
|
* Note: the sibling `ra3-sim` paces its own simulator at 15 ticks/s from
|
||||||
|
* `DAT_00cdbc1c = 1000 / 15`; `ra3-headless` (which drives the real binary)
|
||||||
|
* reports a fixed 30 Hz logic rate. OpenRA3 keeps the 30 Hz SAGE cadence
|
||||||
|
* used elsewhere in this codebase until the discrepancy is settled.
|
||||||
|
*/
|
||||||
|
inline constexpr int logic_frames_per_second = ra3::core::logic_frames_per_second;
|
||||||
|
|
||||||
|
/** Convert a duration in seconds to a whole number of logic frames. */
|
||||||
|
[[nodiscard]] constexpr auto seconds_to_frames(float seconds) -> uint32 {
|
||||||
|
return static_cast<uint32>(seconds * static_cast<float>(logic_frames_per_second) + 0.5F);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Damage types
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RA3's damage types, recovered from the keys of the shipped
|
||||||
|
* `ArmorTemplate` tables and the `DamageType` of every `WeaponTemplate`.
|
||||||
|
*
|
||||||
|
* This is *not* the Generals/ZH set (`EXPLOSION`/`SMALL_ARMS`/...): RA3
|
||||||
|
* replaced it with these. `unresistable` is special - it skips the armour
|
||||||
|
* multiplier entirely.
|
||||||
|
*/
|
||||||
|
enum class damage_type : uint8 {
|
||||||
|
gun,
|
||||||
|
melee,
|
||||||
|
concussive,
|
||||||
|
auto_cannon,
|
||||||
|
rocket,
|
||||||
|
flak,
|
||||||
|
cannon,
|
||||||
|
prism,
|
||||||
|
tesla,
|
||||||
|
explosive,
|
||||||
|
impact,
|
||||||
|
sniper,
|
||||||
|
grenade,
|
||||||
|
radiation,
|
||||||
|
magic,
|
||||||
|
crush,
|
||||||
|
healing,
|
||||||
|
unresistable,
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr usize damage_type_count = static_cast<usize>(damage_type::count);
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto to_string(damage_type value) -> std::string_view {
|
||||||
|
switch (value) {
|
||||||
|
case damage_type::gun: return "GUN";
|
||||||
|
case damage_type::melee: return "MELEE";
|
||||||
|
case damage_type::concussive: return "CONCUSSIVE";
|
||||||
|
case damage_type::auto_cannon: return "AUTO_CANNON";
|
||||||
|
case damage_type::rocket: return "ROCKET";
|
||||||
|
case damage_type::flak: return "FLAK";
|
||||||
|
case damage_type::cannon: return "CANNON";
|
||||||
|
case damage_type::prism: return "PRISM";
|
||||||
|
case damage_type::tesla: return "TESLA";
|
||||||
|
case damage_type::explosive: return "EXPLOSIVE";
|
||||||
|
case damage_type::impact: return "IMPACT";
|
||||||
|
case damage_type::sniper: return "SNIPER";
|
||||||
|
case damage_type::grenade: return "GRENADE";
|
||||||
|
case damage_type::radiation: return "RADIATION";
|
||||||
|
case damage_type::magic: return "MAGIC";
|
||||||
|
case damage_type::crush: return "CRUSH";
|
||||||
|
case damage_type::healing: return "HEALING";
|
||||||
|
case damage_type::unresistable: return "UNRESISTABLE";
|
||||||
|
case damage_type::count: break;
|
||||||
|
}
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse an asset damage-type name (e.g. `"AUTO_CANNON"`). */
|
||||||
|
[[nodiscard]] inline auto damage_type_from_name(std::string_view name) -> damage_type {
|
||||||
|
for (usize i = 0; i < damage_type_count; ++i) {
|
||||||
|
const auto value = static_cast<damage_type>(i);
|
||||||
|
if (to_string(value) == name) return value;
|
||||||
|
}
|
||||||
|
return damage_type::gun;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Armour
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `ArmorTemplate`: a per-damage-type damage multiplier.
|
||||||
|
*
|
||||||
|
* Values are fractions (1.0 == 100%). Unlisted types default to 100%,
|
||||||
|
* except when the template declares a `DEFAULT` (`default_fraction`), as
|
||||||
|
* `InvulnerableArmor` does with 0. Matches OpenSAGE's `ArmorTemplate`.
|
||||||
|
*/
|
||||||
|
struct armor_set {
|
||||||
|
std::string_view id;
|
||||||
|
std::array<float, damage_type_count> multipliers{};
|
||||||
|
float default_fraction = 1.0F;
|
||||||
|
|
||||||
|
/** Apply this armour. `unresistable` is never scaled. */
|
||||||
|
[[nodiscard]] constexpr auto adjust(damage_type type, float damage) const -> float {
|
||||||
|
if (type == damage_type::unresistable) return damage;
|
||||||
|
const auto scaled = damage * multipliers[static_cast<usize>(type)];
|
||||||
|
return scaled < 0.0F ? 0.0F : scaled;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an armour set. `vs` entries are the raw percentages from the
|
||||||
|
* shipped `ArmorTemplate` (`100.0` means normal damage).
|
||||||
|
*/
|
||||||
|
constexpr auto make_armor(std::string_view id, float default_percent, std::initializer_list<std::pair<damage_type, float>> vs) -> armor_set {
|
||||||
|
armor_set armor{};
|
||||||
|
armor.id = id;
|
||||||
|
armor.default_fraction = default_percent / 100.0F;
|
||||||
|
armor.multipliers.fill(armor.default_fraction);
|
||||||
|
for (const auto &[type, percent]: vs) armor.multipliers[static_cast<usize>(type)] = percent / 100.0F;
|
||||||
|
return armor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The armour templates used by the modelled units, copied verbatim from the
|
||||||
|
* shipped `ArmorTemplate` tables (`ra3-headless/out/armors.json`).
|
||||||
|
*/
|
||||||
|
inline constexpr armor_set allied_scout_infantry_armor = make_armor(
|
||||||
|
"AlliedScoutInfantryArmor", 100.0F,
|
||||||
|
{{damage_type::gun, 1.0F}, {damage_type::melee, 50.0F}, {damage_type::concussive, 50.0F}, {damage_type::auto_cannon, 50.0F},
|
||||||
|
{damage_type::rocket, 50.0F}, {damage_type::flak, 50.0F}, {damage_type::cannon, 50.0F}, {damage_type::prism, 50.0F},
|
||||||
|
{damage_type::tesla, 50.0F}, {damage_type::explosive, 50.0F}, {damage_type::impact, 50.0F}});
|
||||||
|
|
||||||
|
inline constexpr armor_set allied_anti_infantry_infantry_armor = make_armor(
|
||||||
|
"AlliedAntiInfantryInfantryArmor", 100.0F,
|
||||||
|
{{damage_type::sniper, 100.0F}, {damage_type::cannon, 20.0F}, {damage_type::rocket, 20.0F}, {damage_type::grenade, 100.0F},
|
||||||
|
{damage_type::gun, 100.0F}, {damage_type::melee, 100.0F}, {damage_type::auto_cannon, 150.0F}, {damage_type::impact, 100.0F},
|
||||||
|
{damage_type::explosive, 100.0F}, {damage_type::flak, 10.0F}, {damage_type::prism, 100.0F}, {damage_type::tesla, 1000.0F},
|
||||||
|
{damage_type::radiation, 100.0F}});
|
||||||
|
|
||||||
|
inline constexpr armor_set soviet_anti_infantry_infantry_armor = make_armor(
|
||||||
|
"SovietAntiInfantryInfantryArmor", 100.0F,
|
||||||
|
{{damage_type::sniper, 100.0F}, {damage_type::cannon, 20.0F}, {damage_type::rocket, 20.0F}, {damage_type::grenade, 100.0F},
|
||||||
|
{damage_type::gun, 100.0F}, {damage_type::melee, 100.0F}, {damage_type::auto_cannon, 150.0F}, {damage_type::impact, 100.0F},
|
||||||
|
{damage_type::explosive, 100.0F}, {damage_type::flak, 10.0F}, {damage_type::prism, 100.0F}, {damage_type::tesla, 1000.0F},
|
||||||
|
{damage_type::radiation, 100.0F}});
|
||||||
|
|
||||||
|
inline constexpr armor_set soviet_scout_infantry_armor = make_armor(
|
||||||
|
"SovietScoutInfantryArmor", 100.0F,
|
||||||
|
{{damage_type::gun, 1.0F}, {damage_type::melee, 50.0F}, {damage_type::concussive, 50.0F}, {damage_type::auto_cannon, 50.0F},
|
||||||
|
{damage_type::rocket, 50.0F}, {damage_type::flak, 50.0F}, {damage_type::cannon, 50.0F}, {damage_type::prism, 50.0F},
|
||||||
|
{damage_type::tesla, 50.0F}, {damage_type::explosive, 50.0F}, {damage_type::impact, 50.0F}});
|
||||||
|
|
||||||
|
inline constexpr armor_set allied_miner_armor =
|
||||||
|
make_armor("AlliedMinerArmor", 100.0F,
|
||||||
|
{{damage_type::melee, 0.0F}, {damage_type::sniper, 0.0F}, {damage_type::gun, 50.0F}, {damage_type::auto_cannon, 100.0F},
|
||||||
|
{damage_type::impact, 100.0F}, {damage_type::flak, 100.0F}, {damage_type::rocket, 100.0F}, {damage_type::cannon, 150.0F},
|
||||||
|
{damage_type::prism, 150.0F}, {damage_type::tesla, 150.0F}, {damage_type::explosive, 100.0F},
|
||||||
|
{damage_type::concussive, 100.0F}, {damage_type::radiation, 5.0F}});
|
||||||
|
|
||||||
|
inline constexpr armor_set allied_anti_vehicle_vehicle_tech1_armor = make_armor(
|
||||||
|
"AlliedAntiVehicleVehicleTech1Armor", 100.0F,
|
||||||
|
{{damage_type::melee, 0.0F}, {damage_type::sniper, 0.0F}, {damage_type::gun, 25.0F}, {damage_type::auto_cannon, 100.0F},
|
||||||
|
{damage_type::impact, 75.0F}, {damage_type::rocket, 75.0F}, {damage_type::flak, 100.0F}, {damage_type::cannon, 170.0F},
|
||||||
|
{damage_type::prism, 170.0F}, {damage_type::tesla, 170.0F}, {damage_type::explosive, 100.0F}, {damage_type::radiation, 5.0F}});
|
||||||
|
|
||||||
|
inline constexpr armor_set soviet_anti_vehicle_vehicle_tech1_armor = make_armor(
|
||||||
|
"SovietAntiVehicleVehicleTech1Armor", 100.0F,
|
||||||
|
{{damage_type::melee, 0.0F}, {damage_type::sniper, 0.0F}, {damage_type::gun, 50.0F}, {damage_type::auto_cannon, 25.0F},
|
||||||
|
{damage_type::impact, 75.0F}, {damage_type::flak, 100.0F}, {damage_type::rocket, 100.0F}, {damage_type::cannon, 100.0F},
|
||||||
|
{damage_type::prism, 100.0F}, {damage_type::tesla, 100.0F}, {damage_type::explosive, 100.0F}, {damage_type::concussive, 100.0F},
|
||||||
|
{damage_type::radiation, 5.0F}});
|
||||||
|
|
||||||
|
/** The faction structure armour shared by con yard, power plant, barracks, refinery and war factory. */
|
||||||
|
inline constexpr armor_set allied_structure_armor = make_armor(
|
||||||
|
"AlliedConYardArmor", 100.0F,
|
||||||
|
{{damage_type::sniper, 0.0F}, {damage_type::cannon, 100.0F}, {damage_type::rocket, 100.0F}, {damage_type::grenade, 100.0F},
|
||||||
|
{damage_type::gun, 100.0F}, {damage_type::melee, 0.0F}, {damage_type::auto_cannon, 50.0F}, {damage_type::impact, 100.0F},
|
||||||
|
{damage_type::explosive, 125.0F}, {damage_type::flak, 100.0F}, {damage_type::prism, 100.0F}, {damage_type::tesla, 100.0F},
|
||||||
|
{damage_type::radiation, 0.0F}});
|
||||||
|
|
||||||
|
inline constexpr armor_set soviet_structure_armor = make_armor(
|
||||||
|
"SovietConYardArmor", 100.0F,
|
||||||
|
{{damage_type::sniper, 0.0F}, {damage_type::cannon, 100.0F}, {damage_type::rocket, 100.0F}, {damage_type::grenade, 100.0F},
|
||||||
|
{damage_type::gun, 100.0F}, {damage_type::melee, 0.0F}, {damage_type::auto_cannon, 50.0F}, {damage_type::impact, 100.0F},
|
||||||
|
{damage_type::explosive, 125.0F}, {damage_type::flak, 100.0F}, {damage_type::prism, 100.0F}, {damage_type::tesla, 100.0F},
|
||||||
|
{damage_type::radiation, 0.0F}});
|
||||||
|
|
||||||
|
/** Neutral armour: everything does normal damage. */
|
||||||
|
inline constexpr armor_set neutral_armor = make_armor("NoArmor", 100.0F, {});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Target classification and weapon anti-mask
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Coarse target classes, derived from a `GameObject`'s `KindOf` flags. */
|
||||||
|
namespace target_class {
|
||||||
|
inline constexpr uint32 none = 0U;
|
||||||
|
inline constexpr uint32 infantry = 1U << 0U;
|
||||||
|
inline constexpr uint32 vehicle = 1U << 1U;
|
||||||
|
inline constexpr uint32 structure = 1U << 2U;
|
||||||
|
inline constexpr uint32 aircraft = 1U << 3U;
|
||||||
|
inline constexpr uint32 ground = infantry | vehicle | structure;
|
||||||
|
inline constexpr uint32 any = 0xFFFFFFFFU;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `WeaponTemplate`'s `AntiMask`. RA3's tokens are recovered from the
|
||||||
|
* shipped weapons (`ra3-headless/out/weapons.json`).
|
||||||
|
*/
|
||||||
|
namespace anti {
|
||||||
|
inline constexpr uint32 ground = 1U << 0U;
|
||||||
|
inline constexpr uint32 water = 1U << 1U;
|
||||||
|
inline constexpr uint32 structure = 1U << 2U;
|
||||||
|
inline constexpr uint32 infantry = 1U << 3U;
|
||||||
|
inline constexpr uint32 vehicle = 1U << 4U;
|
||||||
|
inline constexpr uint32 airborne_vehicle = 1U << 5U;
|
||||||
|
inline constexpr uint32 airborne_infantry = 1U << 6U;
|
||||||
|
inline constexpr uint32 submerged = 1U << 7U;
|
||||||
|
inline constexpr uint32 lifted_ground_unit = 1U << 8U;
|
||||||
|
inline constexpr uint32 mine = 1U << 9U;
|
||||||
|
inline constexpr uint32 projectile = 1U << 10U;
|
||||||
|
inline constexpr uint32 small_missile = 1U << 11U;
|
||||||
|
inline constexpr uint32 ballistic_missile = 1U << 12U;
|
||||||
|
inline constexpr uint32 parachute = 1U << 13U;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Weapons
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Primary weapon parameters. Timings are in seconds. */
|
||||||
|
struct weapon_data {
|
||||||
|
std::string_view ra3_id;
|
||||||
|
float attack_range = 0.0F;
|
||||||
|
float damage = 0.0F;
|
||||||
|
damage_type type = damage_type::gun;
|
||||||
|
float splash_radius = 0.0F;
|
||||||
|
float weapon_speed = 0.0F;
|
||||||
|
uint32 clip_size = 1;
|
||||||
|
float reload_seconds = 1.0F;
|
||||||
|
float firing_seconds = 0.5F;
|
||||||
|
uint32 anti_mask = 0U;
|
||||||
|
/** Classes this weapon may never target (the nugget's `SpecialObjectFilter`). */
|
||||||
|
uint32 excluded_classes = target_class::none;
|
||||||
|
/** One hit kills any valid target (the maul's `InstakillNugget`). */
|
||||||
|
bool instakill = false;
|
||||||
|
bool projectile = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can this weapon target an object of `target` class?
|
||||||
|
*
|
||||||
|
* `ANTI_GROUND` covers all ground classes; the airborne flags cover
|
||||||
|
* aircraft. A weapon with no anti-air flag cannot hit aircraft, which is
|
||||||
|
* why an attack dog can never maul a plane.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr auto can_target(uint32 target) const -> bool {
|
||||||
|
if ((target & excluded_classes) != 0U) return false;
|
||||||
|
if ((target & target_class::aircraft) != 0U) {
|
||||||
|
return (anti_mask & (anti::airborne_vehicle | anti::airborne_infantry)) != 0U;
|
||||||
|
}
|
||||||
|
if ((anti_mask & anti::ground) != 0U && (target & target_class::ground) != 0U) return true;
|
||||||
|
if ((anti_mask & anti::infantry) != 0U && (target & target_class::infantry) != 0U) return true;
|
||||||
|
if ((anti_mask & anti::vehicle) != 0U && (target & target_class::vehicle) != 0U) return true;
|
||||||
|
if ((anti_mask & anti::structure) != 0U && (target & target_class::structure) != 0U) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The primary weapons of the modelled units, from the shipped
|
||||||
|
* `WeaponTemplate`s.
|
||||||
|
*/
|
||||||
|
inline constexpr weapon_data maul_weapon{
|
||||||
|
"AlliedScoutInfantryMaul", 30.0F, 1.0F, damage_type::unresistable, 0.0F, 125.0F, 1U, 1.5F, 0.5F, anti::ground | anti::water,
|
||||||
|
target_class::vehicle | target_class::structure | target_class::aircraft, true, false};
|
||||||
|
|
||||||
|
inline constexpr weapon_data shotgun_weapon{"AlliedAntiInfantryInfantryShotgun",
|
||||||
|
150.0F,
|
||||||
|
40.0F,
|
||||||
|
damage_type::gun,
|
||||||
|
155.0F,
|
||||||
|
750.0F,
|
||||||
|
1U,
|
||||||
|
1.0F,
|
||||||
|
0.5F,
|
||||||
|
anti::ground | anti::structure | anti::water,
|
||||||
|
target_class::none,
|
||||||
|
false,
|
||||||
|
true};
|
||||||
|
|
||||||
|
inline constexpr weapon_data ak47_weapon{"SovietAntiInfantryInfantryAK47",
|
||||||
|
150.0F,
|
||||||
|
5.0F,
|
||||||
|
damage_type::gun,
|
||||||
|
0.0F,
|
||||||
|
750.0F,
|
||||||
|
1U,
|
||||||
|
0.5F,
|
||||||
|
0.25F,
|
||||||
|
anti::water | anti::ground | anti::structure,
|
||||||
|
target_class::none,
|
||||||
|
false,
|
||||||
|
true};
|
||||||
|
|
||||||
|
inline constexpr weapon_data guardian_cannon_weapon{"AlliedAntiVehicleVehicleTech1Cannon",
|
||||||
|
150.0F,
|
||||||
|
60.0F,
|
||||||
|
damage_type::cannon,
|
||||||
|
0.0F,
|
||||||
|
999999.0F,
|
||||||
|
1U,
|
||||||
|
1.8F,
|
||||||
|
0.2F,
|
||||||
|
anti::water | anti::ground | anti::structure,
|
||||||
|
target_class::none,
|
||||||
|
false,
|
||||||
|
true};
|
||||||
|
|
||||||
|
inline constexpr weapon_data rhino_cannon_weapon{"SovietAntiVehicleVehicleTech1CannonWeapon",
|
||||||
|
150.0F,
|
||||||
|
35.0F,
|
||||||
|
damage_type::cannon,
|
||||||
|
0.0F,
|
||||||
|
999999.0F,
|
||||||
|
1U,
|
||||||
|
1.0F,
|
||||||
|
0.2F,
|
||||||
|
anti::water | anti::ground | anti::structure,
|
||||||
|
target_class::none,
|
||||||
|
false,
|
||||||
|
true};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Entities
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Everything the skirmish simulation can place. */
|
||||||
|
enum class entity_kind : uint8 {
|
||||||
|
none = 0,
|
||||||
|
attack_dog, ///< AlliedScoutInfantry
|
||||||
|
peacekeeper, ///< AlliedAntiInfantryInfantry
|
||||||
|
conscript, ///< SovietAntiInfantryInfantry
|
||||||
|
guardian_tank, ///< AlliedAntiVehicleVehicleTech1
|
||||||
|
rhino_tank, ///< SovietAntiVehicleVehicleTech1
|
||||||
|
miner, ///< AlliedMiner
|
||||||
|
construction_yard,
|
||||||
|
power_plant,
|
||||||
|
barracks,
|
||||||
|
war_factory,
|
||||||
|
refinery,
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr usize entity_kind_count = static_cast<usize>(entity_kind::count);
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto to_string(entity_kind value) -> std::string_view {
|
||||||
|
switch (value) {
|
||||||
|
case entity_kind::attack_dog: return "attack_dog";
|
||||||
|
case entity_kind::peacekeeper: return "peacekeeper";
|
||||||
|
case entity_kind::conscript: return "conscript";
|
||||||
|
case entity_kind::guardian_tank: return "guardian_tank";
|
||||||
|
case entity_kind::rhino_tank: return "rhino_tank";
|
||||||
|
case entity_kind::miner: return "miner";
|
||||||
|
case entity_kind::construction_yard: return "construction_yard";
|
||||||
|
case entity_kind::power_plant: return "power_plant";
|
||||||
|
case entity_kind::barracks: return "barracks";
|
||||||
|
case entity_kind::war_factory: return "war_factory";
|
||||||
|
case entity_kind::refinery: return "refinery";
|
||||||
|
case entity_kind::none: break;
|
||||||
|
case entity_kind::count: break;
|
||||||
|
}
|
||||||
|
return "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto is_unit(entity_kind kind) -> bool {
|
||||||
|
switch (kind) {
|
||||||
|
case entity_kind::attack_dog:
|
||||||
|
case entity_kind::peacekeeper:
|
||||||
|
case entity_kind::conscript:
|
||||||
|
case entity_kind::guardian_tank:
|
||||||
|
case entity_kind::rhino_tank:
|
||||||
|
case entity_kind::miner: return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto is_structure(entity_kind kind) -> bool {
|
||||||
|
switch (kind) {
|
||||||
|
case entity_kind::construction_yard:
|
||||||
|
case entity_kind::power_plant:
|
||||||
|
case entity_kind::barracks:
|
||||||
|
case entity_kind::war_factory:
|
||||||
|
case entity_kind::refinery: return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Target class of an entity, from its `KindOf` (`INFANTRY`/`VEHICLE`/...). */
|
||||||
|
[[nodiscard]] constexpr auto class_of(entity_kind kind) -> uint32 {
|
||||||
|
switch (kind) {
|
||||||
|
case entity_kind::attack_dog:
|
||||||
|
case entity_kind::peacekeeper:
|
||||||
|
case entity_kind::conscript: return target_class::infantry;
|
||||||
|
case entity_kind::guardian_tank:
|
||||||
|
case entity_kind::rhino_tank:
|
||||||
|
case entity_kind::miner: return target_class::vehicle;
|
||||||
|
case entity_kind::construction_yard:
|
||||||
|
case entity_kind::power_plant:
|
||||||
|
case entity_kind::barracks:
|
||||||
|
case entity_kind::war_factory:
|
||||||
|
case entity_kind::refinery: return target_class::structure;
|
||||||
|
default: return target_class::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Mobile unit parameters. */
|
||||||
|
struct unit_data {
|
||||||
|
entity_kind kind = entity_kind::none;
|
||||||
|
std::string_view ra3_id;
|
||||||
|
int32 cost = 0;
|
||||||
|
float build_seconds = 0.0F;
|
||||||
|
float max_health = 0.0F;
|
||||||
|
const armor_set *armor = &neutral_armor;
|
||||||
|
float speed = 0.0F; ///< LocomotorSet Speed, world units per second.
|
||||||
|
float vision = 0.0F; ///< VisionInfo VisionRange.
|
||||||
|
float shroud_clear = 0.0F; ///< VisionInfo ShroudClearingRange.
|
||||||
|
float radius = 0.0F; ///< Geometry MajorRadius.
|
||||||
|
const weapon_data *weapon = nullptr;
|
||||||
|
entity_kind built_by = entity_kind::none; ///< Producer structure.
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Structure parameters. */
|
||||||
|
struct structure_data {
|
||||||
|
entity_kind kind = entity_kind::none;
|
||||||
|
std::string_view ra3_id;
|
||||||
|
int32 cost = 0;
|
||||||
|
float build_seconds = 0.0F;
|
||||||
|
float max_health = 0.0F;
|
||||||
|
const armor_set *armor = &neutral_armor;
|
||||||
|
int32 energy = 0; ///< `EnergyProduction`: positive supplies, negative draws.
|
||||||
|
float vision = 0.0F;
|
||||||
|
float shroud_clear = 0.0F;
|
||||||
|
float radius = 0.0F;
|
||||||
|
int size_cells = 1; ///< Footprint, cells per side.
|
||||||
|
entity_kind prerequisite = entity_kind::none; ///< Prerequisite structure.
|
||||||
|
bool is_producer = false; ///< Can build units.
|
||||||
|
bool is_refinery = false; ///< Runs an ore extraction cycle.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mobile units. Health, cost, build time, speed and armour are the retail
|
||||||
|
* `GameObject`/`LocomotorSet`/`ArmorTemplate` values.
|
||||||
|
*/
|
||||||
|
inline constexpr std::array<unit_data, entity_kind_count> units{{
|
||||||
|
{}, // none
|
||||||
|
{entity_kind::attack_dog, "AlliedScoutInfantry", 200, 2.0F, 30.0F, &allied_scout_infantry_armor, 100.0F, 200.0F, 750.0F, 7.0F, &maul_weapon,
|
||||||
|
entity_kind::barracks},
|
||||||
|
{entity_kind::peacekeeper, "AlliedAntiInfantryInfantry", 200, 5.0F, 150.0F, &allied_anti_infantry_infantry_armor, 50.0F, 200.0F, 500.0F, 7.0F,
|
||||||
|
&shotgun_weapon, entity_kind::barracks},
|
||||||
|
{entity_kind::conscript, "SovietAntiInfantryInfantry", 100, 4.0F, 100.0F, &soviet_anti_infantry_infantry_armor, 50.0F, 200.0F, 500.0F, 5.0F,
|
||||||
|
&ak47_weapon, entity_kind::barracks},
|
||||||
|
{entity_kind::guardian_tank, "AlliedAntiVehicleVehicleTech1", 950, 10.0F, 480.0F, &allied_anti_vehicle_vehicle_tech1_armor, 80.0F, 200.0F, 500.0F,
|
||||||
|
20.0F, &guardian_cannon_weapon, entity_kind::war_factory},
|
||||||
|
{entity_kind::rhino_tank, "SovietAntiVehicleVehicleTech1", 1000, 10.0F, 550.0F, &soviet_anti_vehicle_vehicle_tech1_armor, 75.0F, 200.0F, 500.0F,
|
||||||
|
20.0F, &rhino_cannon_weapon, entity_kind::war_factory},
|
||||||
|
{entity_kind::miner, "AlliedMiner", 1000, 20.0F, 500.0F, &allied_miner_armor, 50.0F, 200.0F, 500.0F, 20.0F, nullptr, entity_kind::refinery},
|
||||||
|
{}, {}, {}, {}, {},
|
||||||
|
}};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structures. `energy` is the `EnergyProduction` attribute; `build_seconds`
|
||||||
|
* is `BuildTime`. Soviet `BuildTime` is not set on the base structures (the
|
||||||
|
* extractor reads 0), so the Allied values are used for both sides and the
|
||||||
|
* discrepancy is documented rather than invented.
|
||||||
|
*/
|
||||||
|
inline constexpr std::array<structure_data, entity_kind_count> structures{{
|
||||||
|
{}, {}, {}, {}, {}, {}, {}, // none + the six units
|
||||||
|
{entity_kind::construction_yard, "AlliedConstructionYard", 5000, 25.0F, 4000.0F, &allied_structure_armor, 50, 150.0F, 1000.0F, 60.0F, 3,
|
||||||
|
entity_kind::none, false, false},
|
||||||
|
{entity_kind::power_plant, "AlliedPowerPlant", 800, 10.0F, 1000.0F, &allied_structure_armor, 100, 150.0F, 300.0F, 30.0F, 2,
|
||||||
|
entity_kind::none, false, false},
|
||||||
|
{entity_kind::barracks, "AlliedBarracks", 500, 10.0F, 1000.0F, &allied_structure_armor, -25, 150.0F, 300.0F, 45.0F, 2, entity_kind::none,
|
||||||
|
true, false},
|
||||||
|
{entity_kind::war_factory, "AlliedWarFactory", 2000, 20.0F, 2500.0F, &allied_structure_armor, -50, 150.0F, 500.0F, 60.0F, 3,
|
||||||
|
entity_kind::none, true, false},
|
||||||
|
{entity_kind::refinery, "AlliedRefinery", 2000, 20.0F, 2000.0F, &allied_structure_armor, -50, 150.0F, 500.0F, 60.0F, 3,
|
||||||
|
entity_kind::power_plant, false, true},
|
||||||
|
}};
|
||||||
|
|
||||||
|
/** Look up a mobile unit. Only valid when `is_unit(kind)`. */
|
||||||
|
[[nodiscard]] inline auto unit_of(entity_kind kind) -> const unit_data & { return units.at(static_cast<usize>(kind)); }
|
||||||
|
|
||||||
|
/** Look up a structure. Only valid when `is_structure(kind)`. */
|
||||||
|
[[nodiscard]] inline auto structure_of(entity_kind kind) -> const structure_data & { return structures.at(static_cast<usize>(kind)); }
|
||||||
|
|
||||||
|
/** Cost in credits. */
|
||||||
|
[[nodiscard]] inline auto cost_of(entity_kind kind) -> int32 {
|
||||||
|
if (is_unit(kind)) return unit_of(kind).cost;
|
||||||
|
if (is_structure(kind)) return structure_of(kind).cost;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Build time in logic frames. */
|
||||||
|
[[nodiscard]] inline auto build_frames_of(entity_kind kind) -> uint32 {
|
||||||
|
if (is_unit(kind)) return seconds_to_frames(unit_of(kind).build_seconds);
|
||||||
|
if (is_structure(kind)) return seconds_to_frames(structure_of(kind).build_seconds);
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Maximum health. */
|
||||||
|
[[nodiscard]] inline auto max_health_of(entity_kind kind) -> float {
|
||||||
|
if (is_unit(kind)) return unit_of(kind).max_health;
|
||||||
|
if (is_structure(kind)) return structure_of(kind).max_health;
|
||||||
|
return 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Armour template. */
|
||||||
|
[[nodiscard]] inline auto armor_of(entity_kind kind) -> const armor_set & {
|
||||||
|
if (is_unit(kind)) return *unit_of(kind).armor;
|
||||||
|
if (is_structure(kind)) return *structure_of(kind).armor;
|
||||||
|
return neutral_armor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Vision range in world units. */
|
||||||
|
[[nodiscard]] inline auto vision_of(entity_kind kind) -> float {
|
||||||
|
if (is_unit(kind)) return unit_of(kind).vision;
|
||||||
|
if (is_structure(kind)) return structure_of(kind).vision;
|
||||||
|
return 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The weapon a unit fires, or `nullptr` (structures, miners). */
|
||||||
|
[[nodiscard]] inline auto weapon_of(entity_kind kind) -> const weapon_data * {
|
||||||
|
return is_unit(kind) ? unit_of(kind).weapon : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Economy and world rules
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ore economy, from `OreNodeBehaviour` and `MpGameRules`.
|
||||||
|
*
|
||||||
|
* A refinery runs one extraction cycle: `MOVE_TO_EXTRACT + EXTRACT +
|
||||||
|
* MOVE_TO_DELIVER + DELIVERY` = 11 s for 250 credits while its node has
|
||||||
|
* ore. The engine actually spawns a separate miner; OpenRA3 folds the cycle
|
||||||
|
* into the refinery (a documented simplification, as in `ra3-sim`).
|
||||||
|
*/
|
||||||
|
struct economy_data {
|
||||||
|
int32 starting_credits = 10000; ///< MpGameRules SkirmishStartCash LoCash.
|
||||||
|
int32 credits_step = 5000; ///< SkirmishStartCash ChoiceStepAmount.
|
||||||
|
int32 credits_max = 40000; ///< SkirmishStartCash HiCash.
|
||||||
|
int32 ore_per_delivery = 250; ///< OreNodeBehaviour DeliveryAmount.
|
||||||
|
int32 ore_per_delivery_empty = 60; ///< OreNodeBehaviour DeliveryAmountWhenEmpty.
|
||||||
|
int32 ore_node_capacity = 30000; ///< OreNodeBehaviour MaximumGatheredValue.
|
||||||
|
float extraction_seconds = 11.0F; ///< 3.5 + 2 + 3.5 + 2.
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Miscellaneous world rules. */
|
||||||
|
struct world_data {
|
||||||
|
float standard_shroud_clear = 500.0F; ///< Define STANDARD_SHROUD_CLEAR.
|
||||||
|
float scout_shroud_clear = 750.0F; ///< Define SCOUT_SHROUD_CLEAR.
|
||||||
|
int low_power_production_divisor = 2; ///< Low power halves production speed.
|
||||||
|
int low_power_mining_divisor = 2; ///< Low power halves refinery income.
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr economy_data economy{};
|
||||||
|
inline constexpr world_data world_rules{};
|
||||||
|
|
||||||
|
/** Ore extracted per completed refinery cycle, given the remaining node ore. */
|
||||||
|
[[nodiscard]] constexpr auto ore_per_cycle(int32 node_remaining) -> int32 {
|
||||||
|
return node_remaining > 0 ? economy.ore_per_delivery : economy.ore_per_delivery_empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
export module ra3.display;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
|
export import ra3.core;
|
||||||
|
import ra3.render;
|
||||||
|
import ra3.terrain;
|
||||||
|
import ra3.client;
|
||||||
|
import ra3.ui;
|
||||||
|
import ra3.vulkan;
|
||||||
|
import ra3.dx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backend selection for the presentation layer.
|
||||||
|
*
|
||||||
|
* The app talks only to `ra3::client::display`; this module picks the concrete
|
||||||
|
* backend so no caller has to know which one is in use. The available backends
|
||||||
|
* are Vulkan, Direct3D 11 and Direct3D 12 (both GPU terrain), and the SDL
|
||||||
|
* software blit fallback. The caller may name a preferred backend (the in-game
|
||||||
|
* menu exposes this); if it does not start, the others are tried in turn. The
|
||||||
|
* GPU terrain path is Vulkan/D3D-only and reports failure so the caller can fall
|
||||||
|
* back to the software renderer.
|
||||||
|
*/
|
||||||
|
export namespace ra3::display {
|
||||||
|
using ra3::client::display_options;
|
||||||
|
using ra3::render::camera3d;
|
||||||
|
using ra3::render::image;
|
||||||
|
using ra3::render::ui_event;
|
||||||
|
using ra3::render::view_camera;
|
||||||
|
|
||||||
|
using menu_frame = std::function<std::optional<image>(const ui_event &, ra3::core::uint32, ra3::core::uint32, bool &)>;
|
||||||
|
using camera_frame = std::function<image(const camera3d &, ra3::core::uint32, ra3::core::uint32)>;
|
||||||
|
|
||||||
|
/** A concrete presentation backend, or `none`. */
|
||||||
|
enum class backend { none, vulkan, d3d11, d3d12, sdl };
|
||||||
|
|
||||||
|
[[nodiscard]] inline auto backend_name(backend which) -> std::string_view {
|
||||||
|
switch (which) {
|
||||||
|
case backend::vulkan: return "vulkan";
|
||||||
|
case backend::d3d11: return "d3d11";
|
||||||
|
case backend::d3d12: return "d3d12";
|
||||||
|
case backend::sdl: return "sdl";
|
||||||
|
default: return "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline auto make_backend(backend which) -> std::unique_ptr<ra3::client::display> {
|
||||||
|
switch (which) {
|
||||||
|
case backend::vulkan: return std::make_unique<ra3::vulkan::vulkan_display>();
|
||||||
|
case backend::d3d11: return std::make_unique<ra3::dx::d3d11_display>();
|
||||||
|
case backend::d3d12: return std::make_unique<ra3::dx::d3d12_display>();
|
||||||
|
case backend::sdl: return std::make_unique<ra3::ui::sdl_display>();
|
||||||
|
default: return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The order in which backends are attempted for a preferred one. */
|
||||||
|
[[nodiscard]] inline auto backend_order(backend preferred) -> std::array<backend, 4> {
|
||||||
|
switch (preferred) {
|
||||||
|
case backend::d3d11: return {backend::d3d11, backend::d3d12, backend::vulkan, backend::sdl};
|
||||||
|
case backend::d3d12: return {backend::d3d12, backend::d3d11, backend::vulkan, backend::sdl};
|
||||||
|
case backend::sdl: return {backend::sdl, backend::d3d11, backend::d3d12, backend::vulkan};
|
||||||
|
case backend::vulkan:
|
||||||
|
default: return {backend::vulkan, backend::d3d11, backend::d3d12, backend::sdl};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and initialize the preferred backend, falling back to the others,
|
||||||
|
* or return null when none starts. The caller owns the display and may reuse
|
||||||
|
* it for a menu, a loading bar and a viewer in sequence.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto create(const display_options &options, backend preferred) -> std::unique_ptr<ra3::client::display> {
|
||||||
|
for (const auto which: backend_order(preferred)) {
|
||||||
|
auto candidate = make_backend(which);
|
||||||
|
if (candidate && candidate->init(options)) return candidate;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create and initialize the preferred display (Vulkan, then SDL). */
|
||||||
|
[[nodiscard]] inline auto create(const display_options &options, bool prefer_vulkan) -> std::unique_ptr<ra3::client::display> {
|
||||||
|
return create(options, prefer_vulkan ? backend::vulkan : backend::sdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run `action` on the first backend that initializes.
|
||||||
|
*
|
||||||
|
* `action` must call one of the shared `display` loops; it returns false to
|
||||||
|
* mean "this backend did not start", so the next one is tried.
|
||||||
|
*/
|
||||||
|
template<typename Fn>
|
||||||
|
[[nodiscard]] auto with_display(backend preferred, Fn &&action) -> bool {
|
||||||
|
for (const auto which: backend_order(preferred)) {
|
||||||
|
auto candidate = make_backend(which);
|
||||||
|
if (candidate && action(*candidate)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Run `action` on the first display that initializes: Vulkan, then SDL. */
|
||||||
|
template<typename Fn>
|
||||||
|
[[nodiscard]] auto with_display(bool prefer_vulkan, Fn &&action) -> bool {
|
||||||
|
return with_display(prefer_vulkan ? backend::vulkan : backend::sdl, std::forward<Fn>(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Interactive menu on the preferred (or first available) backend. */
|
||||||
|
[[nodiscard]] inline auto run_menu(const display_options &options, const menu_frame &frame, backend preferred = backend::vulkan) -> bool {
|
||||||
|
return with_display(preferred, [&](ra3::client::display &d) { return d.run_menu(options, frame); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Pan/zoom image viewer on the preferred (or first available) backend. */
|
||||||
|
[[nodiscard]] inline auto run_image(const display_options &options, const image &scene, view_camera camera, backend preferred) -> bool {
|
||||||
|
return with_display(preferred, [&](ra3::client::display &d) { return d.run_image(options, scene, camera); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Pan/zoom image viewer on the preferred (or first available) backend. */
|
||||||
|
[[nodiscard]] inline auto run_image(const display_options &options, const image &scene, view_camera camera, bool prefer_vulkan) -> bool {
|
||||||
|
return run_image(options, scene, camera, prefer_vulkan ? backend::vulkan : backend::sdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Software 3D camera viewer on the preferred (or first available) backend. */
|
||||||
|
[[nodiscard]] inline auto run_camera(const display_options &options, const camera_frame &provider, camera3d camera, bool prefer_vulkan) -> bool {
|
||||||
|
return with_display(prefer_vulkan ? backend::vulkan : backend::sdl,
|
||||||
|
[&](ra3::client::display &d) { return d.run_camera(options, provider, camera); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GPU terrain viewer on the preferred (or first available) backend. Returns
|
||||||
|
* false when no GPU backend that offers terrain starts, so the caller can
|
||||||
|
* render offscreen instead.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto run_terrain(const display_options &options, const ra3::terrain::gpu_terrain &terrain, camera3d camera,
|
||||||
|
backend preferred) -> bool {
|
||||||
|
return with_display(preferred, [&](ra3::client::display &d) {
|
||||||
|
if (!d.supports_terrain()) return false;
|
||||||
|
return d.run_terrain(options, terrain, camera);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GPU terrain viewer with a corner minimap overlay. */
|
||||||
|
[[nodiscard]] inline auto run_terrain(const display_options &options, const ra3::terrain::gpu_terrain &terrain, camera3d camera,
|
||||||
|
const image &minimap, backend preferred) -> bool {
|
||||||
|
return with_display(preferred, [&](ra3::client::display &d) {
|
||||||
|
if (!d.supports_terrain()) return false;
|
||||||
|
return d.run_terrain(options, terrain, camera, minimap);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GPU terrain viewer. Vulkan/D3D only: returns false when the preferred GPU
|
||||||
|
* backend is unavailable so the caller can render offscreen instead.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto run_terrain(const display_options &options, const ra3::terrain::gpu_terrain &terrain, camera3d camera) -> bool {
|
||||||
|
return run_terrain(options, terrain, camera, backend::vulkan);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1392
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
|||||||
|
export module ra3.dx;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
|
export import ra3.core;
|
||||||
|
import ra3.render;
|
||||||
|
import ra3.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fallback Direct3D backends used when the build has no Windows/D3D (for
|
||||||
|
* example the Linux development build). `init` fails so the caller can select
|
||||||
|
* another display; the class names match the real `ra3.dx` module so the
|
||||||
|
* backend factory in `ra3.display` compiles unchanged.
|
||||||
|
*/
|
||||||
|
export namespace ra3::dx {
|
||||||
|
class d3d11_display final : public ra3::client::display {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] auto init(const ra3::client::display_options &) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto present(const ra3::render::image &, const ra3::render::view_rect &, bool) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto poll_event(ra3::render::ui_event &) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto window_size() const -> std::pair<int, int> override { return {0, 0}; }
|
||||||
|
[[nodiscard]] auto key_down(ra3::render::ui_key) const -> bool override { return false; }
|
||||||
|
auto shutdown() -> void override {}
|
||||||
|
[[nodiscard]] auto name() const -> std::string_view override { return "d3d11(null)"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class d3d12_display final : public ra3::client::display {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] auto init(const ra3::client::display_options &) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto present(const ra3::render::image &, const ra3::render::view_rect &, bool) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto poll_event(ra3::render::ui_event &) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto window_size() const -> std::pair<int, int> override { return {0, 0}; }
|
||||||
|
[[nodiscard]] auto key_down(ra3::render::ui_key) const -> bool override { return false; }
|
||||||
|
auto shutdown() -> void override {}
|
||||||
|
[[nodiscard]] auto name() const -> std::string_view override { return "d3d12(null)"; }
|
||||||
|
};
|
||||||
|
}
|
||||||
+2
-18
@@ -1,23 +1,7 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <optional>
|
|
||||||
#include <span>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
export module ra3.fs;
|
export module ra3.fs;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
export import ra3.core;
|
export import ra3.core;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
export module ra3.game;
|
export module ra3.game;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
import ra3.core;
|
import ra3.core;
|
||||||
import ra3.logic;
|
import ra3.logic;
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,7 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
export module ra3.logic;
|
export module ra3.logic;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
import ra3.core;
|
import ra3.core;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+219
-15
@@ -1,18 +1,7 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <optional>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
export module ra3.map;
|
export module ra3.map;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
export import ra3.core;
|
export import ra3.core;
|
||||||
export import ra3.fs;
|
export import ra3.fs;
|
||||||
|
|
||||||
@@ -51,11 +40,12 @@ export namespace ra3::map {
|
|||||||
uint32 unpacked_size = 0;
|
uint32 unpacked_size = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The result of loading a map: its metadata and recovered start positions. */
|
/** The result of loading a map: its metadata, recovered start positions and the raw `CkMp` tree. */
|
||||||
struct loaded_map {
|
struct loaded_map {
|
||||||
map_info info;
|
map_info info;
|
||||||
std::vector<start_position> starts;
|
std::vector<start_position> starts;
|
||||||
usize ckmp_size = 0;
|
usize ckmp_size = 0;
|
||||||
|
std::vector<uint8> bytes; ///< The uncompressed `CkMp` payload (terrain/objects parsed from here).
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
@@ -192,6 +182,219 @@ export namespace ra3::map {
|
|||||||
std::vector<map_info> maps_;
|
std::vector<map_info> maps_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalise a loose `.map` payload to bare `CkMp` bytes.
|
||||||
|
*
|
||||||
|
* Accepts either the raw `.big` payload (the `EAR\0` wrapper around a
|
||||||
|
* RefPack stream, as `ra3tools` extracts it), a bare RefPack stream, or an
|
||||||
|
* already-unwrapped `CkMp` tree (as our own `extract` writes).
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto to_ckmp(std::span<const uint8> raw) -> std::vector<uint8> {
|
||||||
|
std::span<const uint8> payload = raw;
|
||||||
|
if (raw.size() >= 8U && std::memcmp(raw.data(), "EAR\0", 4) == 0) payload = raw.subspan(8);
|
||||||
|
return fs::maybe_decompress(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Recover `Player_N_Start` waypoints from raw, uncompressed `CkMp` bytes. */
|
||||||
|
[[nodiscard]] inline auto starts_from_ckmp(std::span<const uint8> ckmp) -> std::vector<start_position> {
|
||||||
|
auto starts = detail::extract_start_positions(ckmp);
|
||||||
|
if (detail::distinct_positions(starts) < 2U) starts.clear();
|
||||||
|
return starts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Map id -> localized display name, keyed by lowercased id. */
|
||||||
|
struct map_name_table {
|
||||||
|
std::unordered_map<std::string, std::string> names;
|
||||||
|
|
||||||
|
[[nodiscard]] auto empty() const -> bool { return names.empty(); }
|
||||||
|
|
||||||
|
/** The display name for `id`, or `id` itself when the string table has none. */
|
||||||
|
[[nodiscard]] auto lookup(std::string_view id) const -> std::string {
|
||||||
|
auto key = std::string{id};
|
||||||
|
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
|
||||||
|
if (const auto it = names.find(key); it != names.end() && !it->second.empty()) return it->second;
|
||||||
|
return std::string{id};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
/**
|
||||||
|
* Decode one SAGE `.csf` string value.
|
||||||
|
*
|
||||||
|
* RA3's `gamestrings.csf` stores each UTF-16 code unit with the low byte
|
||||||
|
* XORed by `0xFF` (the high byte is the padding `0xFF`); undoing that
|
||||||
|
* yields the plain ASCII/UTF-8 text.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto decode_csf_string(std::span<const uint8> raw) -> std::string {
|
||||||
|
std::string value;
|
||||||
|
for (usize i = 0; i + 1U < raw.size(); i += 2U) {
|
||||||
|
const auto ch = static_cast<char>(static_cast<uint8>(raw[i] ^ 0xFFU));
|
||||||
|
if (ch == '\0') break;
|
||||||
|
value.push_back(ch);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse `MAP:<id>` display names out of a SAGE `.csf` string table.
|
||||||
|
*
|
||||||
|
* The map list UI reads its labels from the install's `data\gamestrings.csf`
|
||||||
|
* under the key `MAP:<UPPERCASE_ID>` (e.g. `MAP:MAP_MP_2_FEASEL4` is
|
||||||
|
* "Battlebase Beta"). Values are byte-XORed with `0xFF`.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto parse_map_names(std::span<const uint8> csf) -> map_name_table {
|
||||||
|
map_name_table table;
|
||||||
|
if (csf.size() < 24U || std::memcmp(csf.data(), " FSC", 4) != 0) return table;
|
||||||
|
|
||||||
|
const auto read = [&](usize p) -> uint32 {
|
||||||
|
return static_cast<uint32>(csf[p]) | (static_cast<uint32>(csf[p + 1U]) << 8U) | (static_cast<uint32>(csf[p + 2U]) << 16U) |
|
||||||
|
(static_cast<uint32>(csf[p + 3U]) << 24U);
|
||||||
|
};
|
||||||
|
|
||||||
|
usize pos = 24; // header: magic + version + label/string counts + 8 reserved bytes
|
||||||
|
while (pos + 4U <= csf.size()) {
|
||||||
|
if (std::memcmp(csf.data() + pos, " LBL", 4) != 0) {
|
||||||
|
++pos;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pos += 4;
|
||||||
|
if (pos + 4U > csf.size()) break;
|
||||||
|
const auto count = read(pos);
|
||||||
|
pos += 4;
|
||||||
|
std::vector<std::string> labels;
|
||||||
|
labels.reserve(count);
|
||||||
|
bool ok = true;
|
||||||
|
for (uint32 i = 0; i < count; ++i) {
|
||||||
|
if (pos + 4U > csf.size()) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto len = read(pos);
|
||||||
|
pos += 4;
|
||||||
|
if (pos + len > csf.size()) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
labels.emplace_back(reinterpret_cast<const char *>(csf.data() + pos), len);
|
||||||
|
pos += len;
|
||||||
|
}
|
||||||
|
if (!ok) break;
|
||||||
|
for (const auto &label: labels) {
|
||||||
|
if (pos + 4U > csf.size() || std::memcmp(csf.data() + pos, " RTS", 4) != 0) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += 4;
|
||||||
|
if (pos + 4U > csf.size()) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto chars = read(pos);
|
||||||
|
pos += 4;
|
||||||
|
const auto bytes = static_cast<usize>(chars) * 2U;
|
||||||
|
if (pos + bytes > csf.size()) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto value = detail::decode_csf_string(csf.subspan(pos, bytes));
|
||||||
|
pos += bytes;
|
||||||
|
constexpr std::string_view prefix = "MAP:";
|
||||||
|
if (label.size() > prefix.size() && label.compare(0, prefix.size(), prefix) == 0) {
|
||||||
|
auto id = label.substr(prefix.size());
|
||||||
|
std::transform(id.begin(), id.end(), id.begin(), [](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
|
||||||
|
if (!value.empty()) table.names.try_emplace(std::move(id), std::move(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ok) break;
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load display names from the install's newest English string table. */
|
||||||
|
[[nodiscard]] inline auto load_map_names_from_data_dir(const std::filesystem::path &data_dir) -> map_name_table {
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::path chosen;
|
||||||
|
int best_version = -1;
|
||||||
|
for (const auto &entry: std::filesystem::directory_iterator(data_dir, ec)) {
|
||||||
|
if (!entry.is_regular_file()) continue;
|
||||||
|
const auto name = entry.path().filename().string();
|
||||||
|
if (name.rfind("Lang-English", 0) != 0 || !name.ends_with(".big")) continue;
|
||||||
|
const auto mid = name.substr(12, name.size() - 16U);
|
||||||
|
int version = 0;
|
||||||
|
try {
|
||||||
|
version = std::stoi(mid);
|
||||||
|
} catch (const std::exception &) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (version > best_version) {
|
||||||
|
best_version = version;
|
||||||
|
chosen = entry.path();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chosen.empty()) chosen = data_dir / "English.big";
|
||||||
|
if (!std::filesystem::exists(chosen, ec)) return {};
|
||||||
|
try {
|
||||||
|
const auto archive = fs::big_archive::open(chosen);
|
||||||
|
const auto matches = archive.find("gamestrings.csf");
|
||||||
|
if (matches.empty()) return {};
|
||||||
|
return parse_map_names(archive.read(matches.front()->name, true));
|
||||||
|
} catch (const std::exception &) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read `<assets>/maps/map_names.tsv`, or any `gamestrings.csf` under `assets`. */
|
||||||
|
[[nodiscard]] inline auto load_map_names(const std::filesystem::path &assets) -> map_name_table {
|
||||||
|
const auto read_file = [](const std::filesystem::path &path) {
|
||||||
|
std::ifstream in(path, std::ios::binary);
|
||||||
|
return std::vector<uint8>{std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
const auto tsv = assets / "maps" / "map_names.tsv";
|
||||||
|
if (std::filesystem::exists(tsv, ec)) {
|
||||||
|
const auto bytes = read_file(tsv);
|
||||||
|
map_name_table table;
|
||||||
|
std::string text{bytes.begin(), bytes.end()};
|
||||||
|
for (std::size_t start = 0; start < text.size();) {
|
||||||
|
const auto end = text.find('\n', start);
|
||||||
|
const auto line = text.substr(start, end == std::string::npos ? std::string::npos : end - start);
|
||||||
|
start = end == std::string::npos ? text.size() : end + 1U;
|
||||||
|
const auto tab = line.find('\t');
|
||||||
|
if (tab == std::string::npos || tab == 0U) continue;
|
||||||
|
auto id = line.substr(0, tab);
|
||||||
|
std::transform(id.begin(), id.end(), id.begin(), [](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
|
||||||
|
table.names.try_emplace(std::move(id), line.substr(tab + 1U));
|
||||||
|
}
|
||||||
|
if (!table.empty()) return table;
|
||||||
|
}
|
||||||
|
for (const auto &entry: std::filesystem::recursive_directory_iterator(assets, ec)) {
|
||||||
|
if (!entry.is_regular_file() || entry.path().filename() != "gamestrings.csf") continue;
|
||||||
|
return parse_map_names(read_file(entry.path()));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Serialize a name table as `id<TAB>name` lines (tabs/newlines stripped). */
|
||||||
|
[[nodiscard]] inline auto serialize_map_names(const map_name_table &table) -> std::string {
|
||||||
|
std::vector<std::string> ids;
|
||||||
|
ids.reserve(table.names.size());
|
||||||
|
for (const auto &[id, _]: table.names) ids.push_back(id);
|
||||||
|
std::sort(ids.begin(), ids.end());
|
||||||
|
std::string out;
|
||||||
|
for (const auto &id: ids) {
|
||||||
|
auto name = table.names.at(id);
|
||||||
|
std::replace(name.begin(), name.end(), '\t', ' ');
|
||||||
|
std::replace(name.begin(), name.end(), '\n', ' ');
|
||||||
|
out += id;
|
||||||
|
out += '\t';
|
||||||
|
out += name;
|
||||||
|
out += '\n';
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a map: unwrap the `EAR\0` + RefPack container and recover the
|
* Load a map: unwrap the `EAR\0` + RefPack container and recover the
|
||||||
* player start waypoints.
|
* player start waypoints.
|
||||||
@@ -212,7 +415,8 @@ export namespace ra3::map {
|
|||||||
// Layer 2: the wrapper body is RefPack again, yielding the `CkMp` tree.
|
// Layer 2: the wrapper body is RefPack again, yielding the `CkMp` tree.
|
||||||
auto ckmp = fs::maybe_decompress(payload);
|
auto ckmp = fs::maybe_decompress(payload);
|
||||||
result.ckmp_size = ckmp.size();
|
result.ckmp_size = ckmp.size();
|
||||||
result.starts = detail::extract_start_positions(ckmp);
|
result.starts = starts_from_ckmp(ckmp);
|
||||||
|
result.bytes = std::move(ckmp);
|
||||||
// Maps that keep starts in `MPPositionList` instead of `Player_N_Start`
|
// Maps that keep starts in `MPPositionList` instead of `Player_N_Start`
|
||||||
// waypoints yield fewer than two distinct points; signal "unknown" so
|
// waypoints yield fewer than two distinct points; signal "unknown" so
|
||||||
// the caller can fall back rather than spawn everyone at the origin.
|
// the caller can fall back rather than spawn everyone at the origin.
|
||||||
|
|||||||
@@ -9,9 +9,14 @@ export module ra3;
|
|||||||
export import ra3.core;
|
export import ra3.core;
|
||||||
export import ra3.logic;
|
export import ra3.logic;
|
||||||
export import ra3.client;
|
export import ra3.client;
|
||||||
|
export import ra3.data;
|
||||||
export import ra3.game;
|
export import ra3.game;
|
||||||
export import ra3.fs;
|
export import ra3.fs;
|
||||||
export import ra3.map;
|
export import ra3.map;
|
||||||
export import ra3.skirmish;
|
export import ra3.skirmish;
|
||||||
export import ra3.render;
|
export import ra3.render;
|
||||||
|
export import ra3.terrain;
|
||||||
export import ra3.ui;
|
export import ra3.ui;
|
||||||
|
export import ra3.vulkan;
|
||||||
|
export import ra3.dx;
|
||||||
|
export import ra3.display;
|
||||||
|
|||||||
+384
-14
@@ -1,18 +1,7 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <span>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
export module ra3.render;
|
export module ra3.render;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
export import ra3.core;
|
export import ra3.core;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,6 +123,264 @@ export namespace ra3::render {
|
|||||||
std::vector<uint32> pixels_;
|
std::vector<uint32> pixels_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
// 8x12 bitmap font for ASCII 32..126 (baseline at row 9 so descenders fit);
|
||||||
|
// each row byte has bit 7 as the leftmost column, top row first.
|
||||||
|
inline constexpr std::array<std::array<uint8, 12>, 95> font8x8 = {{
|
||||||
|
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ' '
|
||||||
|
{0x00,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x40,0x00,0x00,0x00}, // '!'
|
||||||
|
{0x00,0x6C,0x48,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // '"'
|
||||||
|
{0x00,0x14,0x14,0x28,0x7C,0x28,0x7C,0x28,0x50,0x50,0x00,0x00}, // '#'
|
||||||
|
{0x00,0x10,0x38,0x40,0x40,0x38,0x48,0x70,0x10,0x10,0x00,0x00}, // '$'
|
||||||
|
{0x00,0x20,0x50,0x20,0x0C,0x70,0x08,0x14,0x08,0x00,0x00,0x00}, // '%'
|
||||||
|
{0x00,0x00,0x00,0x18,0x20,0x20,0x54,0x48,0x34,0x00,0x00,0x00}, // '&'
|
||||||
|
{0x00,0x40,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // "'"
|
||||||
|
{0x00,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x00}, // '('
|
||||||
|
{0x00,0x40,0x40,0x20,0x20,0x20,0x20,0x20,0x20,0x40,0x40,0x00}, // ')'
|
||||||
|
{0x00,0x10,0x7C,0x10,0x28,0x08,0x00,0x00,0x00,0x00,0x00,0x00}, // '*'
|
||||||
|
{0x00,0x00,0x08,0x08,0x08,0x7F,0x08,0x08,0x08,0x00,0x00,0x00}, // '+'
|
||||||
|
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x20,0x60,0x40,0x00}, // ','
|
||||||
|
{0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00}, // '-'
|
||||||
|
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00}, // '.'
|
||||||
|
{0x00,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x00,0x00}, // '/'
|
||||||
|
{0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00}, // '0'
|
||||||
|
{0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,0x00}, // '1'
|
||||||
|
{0x00,0x38,0x44,0x04,0x08,0x10,0x20,0x44,0x7C,0x00,0x00,0x00}, // '2'
|
||||||
|
{0x00,0x38,0x44,0x04,0x18,0x04,0x04,0x44,0x38,0x00,0x00,0x00}, // '3'
|
||||||
|
{0x00,0x0C,0x14,0x14,0x24,0x44,0x7E,0x04,0x0E,0x00,0x00,0x00}, // '4'
|
||||||
|
{0x00,0x3C,0x20,0x20,0x38,0x04,0x04,0x44,0x38,0x00,0x00,0x00}, // '5'
|
||||||
|
{0x00,0x1C,0x20,0x40,0x78,0x44,0x44,0x44,0x38,0x00,0x00,0x00}, // '6'
|
||||||
|
{0x00,0x7C,0x44,0x04,0x08,0x08,0x08,0x10,0x10,0x00,0x00,0x00}, // '7'
|
||||||
|
{0x00,0x38,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00}, // '8'
|
||||||
|
{0x00,0x38,0x44,0x44,0x44,0x3C,0x04,0x08,0x70,0x00,0x00,0x00}, // '9'
|
||||||
|
{0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x00}, // ':'
|
||||||
|
{0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x60,0x40,0x00,0x00}, // ';'
|
||||||
|
{0x00,0x06,0x08,0x30,0x40,0x30,0x08,0x06,0x00,0x00,0x00,0x00}, // '<'
|
||||||
|
{0x00,0x00,0x00,0x00,0x7C,0x00,0x7C,0x00,0x00,0x00,0x00,0x00}, // '='
|
||||||
|
{0x00,0x60,0x10,0x0C,0x02,0x0C,0x10,0x60,0x00,0x00,0x00,0x00}, // '>'
|
||||||
|
{0x00,0x00,0x38,0x44,0x04,0x08,0x10,0x00,0x30,0x00,0x00,0x00}, // '?'
|
||||||
|
{0x38,0x44,0x44,0x4C,0x54,0x54,0x4C,0x40,0x44,0x38,0x00,0x00}, // '@'
|
||||||
|
{0x00,0x18,0x08,0x14,0x14,0x14,0x3E,0x22,0x77,0x00,0x00,0x00}, // 'A'
|
||||||
|
{0x00,0x7C,0x22,0x22,0x3C,0x22,0x22,0x22,0x7C,0x00,0x00,0x00}, // 'B'
|
||||||
|
{0x00,0x3C,0x44,0x40,0x40,0x40,0x40,0x44,0x38,0x00,0x00,0x00}, // 'C'
|
||||||
|
{0x00,0x78,0x24,0x22,0x22,0x22,0x22,0x24,0x78,0x00,0x00,0x00}, // 'D'
|
||||||
|
{0x00,0x7E,0x22,0x28,0x38,0x28,0x20,0x22,0x7E,0x00,0x00,0x00}, // 'E'
|
||||||
|
{0x00,0x7E,0x22,0x28,0x38,0x28,0x20,0x20,0x70,0x00,0x00,0x00}, // 'F'
|
||||||
|
{0x00,0x3C,0x44,0x40,0x40,0x4E,0x44,0x44,0x38,0x00,0x00,0x00}, // 'G'
|
||||||
|
{0x00,0x77,0x22,0x22,0x3E,0x22,0x22,0x22,0x77,0x00,0x00,0x00}, // 'H'
|
||||||
|
{0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,0x00}, // 'I'
|
||||||
|
{0x00,0x3C,0x08,0x08,0x08,0x48,0x48,0x48,0x30,0x00,0x00,0x00}, // 'J'
|
||||||
|
{0x00,0x77,0x22,0x24,0x28,0x38,0x24,0x22,0x73,0x00,0x00,0x00}, // 'K'
|
||||||
|
{0x00,0x70,0x20,0x20,0x20,0x20,0x24,0x24,0x7C,0x00,0x00,0x00}, // 'L'
|
||||||
|
{0x00,0x77,0x36,0x36,0x2A,0x2A,0x22,0x22,0x77,0x00,0x00,0x00}, // 'M'
|
||||||
|
{0x00,0x77,0x32,0x32,0x2A,0x2A,0x2A,0x26,0x76,0x00,0x00,0x00}, // 'N'
|
||||||
|
{0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00}, // 'O'
|
||||||
|
{0x00,0x78,0x24,0x24,0x24,0x38,0x20,0x20,0x70,0x00,0x00,0x00}, // 'P'
|
||||||
|
{0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x1C,0x00,0x00}, // 'Q'
|
||||||
|
{0x00,0x7C,0x22,0x22,0x22,0x3C,0x24,0x22,0x71,0x00,0x00,0x00}, // 'R'
|
||||||
|
{0x00,0x34,0x4C,0x40,0x38,0x04,0x04,0x64,0x58,0x00,0x00,0x00}, // 'S'
|
||||||
|
{0x00,0x7F,0x49,0x08,0x08,0x08,0x08,0x08,0x1C,0x00,0x00,0x00}, // 'T'
|
||||||
|
{0x00,0x77,0x22,0x22,0x22,0x22,0x22,0x22,0x1C,0x00,0x00,0x00}, // 'U'
|
||||||
|
{0x00,0x77,0x22,0x22,0x14,0x14,0x14,0x08,0x08,0x00,0x00,0x00}, // 'V'
|
||||||
|
{0x00,0x77,0x22,0x22,0x2A,0x2A,0x2A,0x2A,0x14,0x00,0x00,0x00}, // 'W'
|
||||||
|
{0x00,0x63,0x22,0x14,0x08,0x08,0x14,0x22,0x63,0x00,0x00,0x00}, // 'X'
|
||||||
|
{0x00,0x77,0x22,0x14,0x14,0x08,0x08,0x08,0x1C,0x00,0x00,0x00}, // 'Y'
|
||||||
|
{0x00,0x7C,0x44,0x08,0x10,0x10,0x20,0x44,0x7C,0x00,0x00,0x00}, // 'Z'
|
||||||
|
{0x00,0x70,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x70,0x00}, // '['
|
||||||
|
{0x00,0x00,0x40,0x40,0x40,0x20,0x20,0x10,0x10,0x00,0x00,0x00}, // '\\'
|
||||||
|
{0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70,0x00}, // ']'
|
||||||
|
{0x00,0x10,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // '^'
|
||||||
|
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F}, // '_'
|
||||||
|
{0x00,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // '`'
|
||||||
|
{0x00,0x00,0x00,0x38,0x04,0x3C,0x44,0x44,0x3E,0x00,0x00,0x00}, // 'a'
|
||||||
|
{0x00,0x60,0x20,0x2C,0x32,0x22,0x22,0x22,0x7C,0x00,0x00,0x00}, // 'b'
|
||||||
|
{0x00,0x00,0x00,0x3C,0x44,0x40,0x40,0x44,0x38,0x00,0x00,0x00}, // 'c'
|
||||||
|
{0x00,0x0C,0x04,0x34,0x4C,0x44,0x44,0x44,0x3E,0x00,0x00,0x00}, // 'd'
|
||||||
|
{0x00,0x00,0x00,0x38,0x44,0x7C,0x40,0x40,0x3C,0x00,0x00,0x00}, // 'e'
|
||||||
|
{0x00,0x1C,0x20,0x7C,0x20,0x20,0x20,0x20,0x7C,0x00,0x00,0x00}, // 'f'
|
||||||
|
{0x00,0x00,0x00,0x36,0x4C,0x44,0x44,0x44,0x3C,0x04,0x38,0x00}, // 'g'
|
||||||
|
{0x00,0x60,0x20,0x2C,0x32,0x22,0x22,0x22,0x77,0x00,0x00,0x00}, // 'h'
|
||||||
|
{0x00,0x10,0x00,0x70,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,0x00}, // 'i'
|
||||||
|
{0x00,0x10,0x00,0x78,0x08,0x08,0x08,0x08,0x08,0x08,0x70,0x00}, // 'j'
|
||||||
|
{0x00,0x60,0x20,0x2E,0x24,0x38,0x28,0x24,0x6E,0x00,0x00,0x00}, // 'k'
|
||||||
|
{0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,0x00}, // 'l'
|
||||||
|
{0x00,0x00,0x00,0x74,0x2A,0x2A,0x2A,0x2A,0x7F,0x00,0x00,0x00}, // 'm'
|
||||||
|
{0x00,0x00,0x00,0x6C,0x32,0x22,0x22,0x22,0x77,0x00,0x00,0x00}, // 'n'
|
||||||
|
{0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00}, // 'o'
|
||||||
|
{0x00,0x00,0x00,0x6C,0x32,0x22,0x22,0x22,0x3C,0x20,0x70,0x00}, // 'p'
|
||||||
|
{0x00,0x00,0x00,0x36,0x4C,0x44,0x44,0x44,0x3C,0x04,0x0E,0x00}, // 'q'
|
||||||
|
{0x00,0x00,0x00,0x6C,0x30,0x20,0x20,0x20,0x7C,0x00,0x00,0x00}, // 'r'
|
||||||
|
{0x00,0x00,0x00,0x3C,0x44,0x38,0x04,0x44,0x78,0x00,0x00,0x00}, // 's'
|
||||||
|
{0x00,0x00,0x20,0x7C,0x20,0x20,0x20,0x22,0x1C,0x00,0x00,0x00}, // 't'
|
||||||
|
{0x00,0x00,0x00,0x66,0x22,0x22,0x22,0x26,0x1B,0x00,0x00,0x00}, // 'u'
|
||||||
|
{0x00,0x00,0x00,0x77,0x22,0x22,0x14,0x14,0x08,0x00,0x00,0x00}, // 'v'
|
||||||
|
{0x00,0x00,0x00,0x77,0x22,0x2A,0x2A,0x2A,0x14,0x00,0x00,0x00}, // 'w'
|
||||||
|
{0x00,0x00,0x00,0x66,0x24,0x18,0x18,0x24,0x66,0x00,0x00,0x00}, // 'x'
|
||||||
|
{0x00,0x00,0x00,0x77,0x22,0x12,0x14,0x0C,0x08,0x08,0x3C,0x00}, // 'y'
|
||||||
|
{0x00,0x00,0x00,0x7C,0x48,0x10,0x20,0x44,0x7C,0x00,0x00,0x00}, // 'z'
|
||||||
|
{0x00,0x10,0x20,0x20,0x20,0x20,0x40,0x20,0x20,0x20,0x10,0x00}, // '{'
|
||||||
|
{0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00}, // '|'
|
||||||
|
{0x00,0x40,0x20,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x40,0x00}, // '}'
|
||||||
|
{0x00,0x00,0x00,0x00,0x00,0x24,0x58,0x00,0x00,0x00,0x00,0x00}, // '~'
|
||||||
|
}};
|
||||||
|
|
||||||
|
inline constexpr uint32 glyph_width = 8;
|
||||||
|
inline constexpr uint32 glyph_height = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Pixel width of `text` drawn at `scale`. */
|
||||||
|
[[nodiscard]] inline auto text_width(std::string_view text, uint32 scale = 1U) -> uint32 {
|
||||||
|
return static_cast<uint32>(text.size()) * detail::glyph_width * std::max(1U, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Draw `text` with its top-left at `(x, y)`, glyphs scaled by `scale`. */
|
||||||
|
inline auto draw_text(image &target, int x, int y, std::string_view text, uint32 color, uint32 scale = 1U) -> void {
|
||||||
|
scale = std::max(1U, scale);
|
||||||
|
int cursor = x;
|
||||||
|
for (const auto ch: text) {
|
||||||
|
const auto code = static_cast<unsigned char>(ch);
|
||||||
|
if (code >= 32U && code < 127U) {
|
||||||
|
const auto &glyph = detail::font8x8[code - 32U];
|
||||||
|
for (uint32 gy = 0; gy < detail::glyph_height; ++gy) {
|
||||||
|
const auto bits = glyph[gy];
|
||||||
|
if (bits == 0U) continue;
|
||||||
|
for (uint32 gx = 0; gx < detail::glyph_width; ++gx) {
|
||||||
|
if ((bits & (1U << (7U - gx))) == 0U) continue;
|
||||||
|
for (uint32 sy = 0; sy < scale; ++sy) {
|
||||||
|
for (uint32 sx = 0; sx < scale; ++sx) {
|
||||||
|
target.set(cursor + static_cast<int>(gx * scale + sx), y + static_cast<int>(gy * scale + sy), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor += static_cast<int>(detail::glyph_width * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backend-agnostic input for the interactive loops. The windowing backends
|
||||||
|
* (`ra3.vulkan` / `ra3.ui`) translate their native events into this and hand
|
||||||
|
* it to the shared `ra3::client::display` loops.
|
||||||
|
*/
|
||||||
|
enum class ui_key : uint8 { none, up, down, left, right, page_up, page_down, confirm, cancel, tab, backspace };
|
||||||
|
|
||||||
|
enum class ui_event_type : uint8 { none, quit, key, text, mouse_move, mouse_button, wheel };
|
||||||
|
|
||||||
|
struct ui_event {
|
||||||
|
ui_event_type type = ui_event_type::none;
|
||||||
|
ui_key key = ui_key::none;
|
||||||
|
char character = '\0';
|
||||||
|
float x = 0.0F;
|
||||||
|
float y = 0.0F;
|
||||||
|
float dx = 0.0F;
|
||||||
|
float dy = 0.0F;
|
||||||
|
float wheel = 0.0F;
|
||||||
|
bool left = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A warm red/gold loading screen with a progress bar (0..1). */
|
||||||
|
[[nodiscard]] inline auto compose_progress(uint32 width, uint32 height, float progress, std::string_view label) -> image {
|
||||||
|
const auto background = argb(34, 9, 8);
|
||||||
|
const auto panel = argb(58, 14, 12);
|
||||||
|
const auto edge = argb(120, 30, 22);
|
||||||
|
const auto gold = argb(236, 190, 80);
|
||||||
|
const auto cream = argb(246, 228, 192);
|
||||||
|
image img(width, height, background);
|
||||||
|
const auto k = static_cast<double>(height) / 720.0;
|
||||||
|
const auto S = [k](double v) { return static_cast<int>(std::lround(v * k)); };
|
||||||
|
const auto scale = static_cast<uint32>(std::max(1, static_cast<int>(std::lround(2.0 * k))));
|
||||||
|
const auto t = std::clamp(progress, 0.0F, 1.0F);
|
||||||
|
|
||||||
|
const auto title = std::string_view{"OpenRA3"};
|
||||||
|
draw_text(img, (static_cast<int>(width) - static_cast<int>(text_width(title, scale + 1U))) / 2, S(250), title, gold, scale + 1U);
|
||||||
|
draw_text(img, (static_cast<int>(width) - static_cast<int>(text_width(label, scale))) / 2, S(300), label, cream, scale);
|
||||||
|
|
||||||
|
const int bar_x = S(240);
|
||||||
|
const int bar_y = S(360);
|
||||||
|
const int bar_w = static_cast<int>(width) - 2 * bar_x;
|
||||||
|
const int bar_h = S(28);
|
||||||
|
img.draw_rect(bar_x - S(3), bar_y - S(3), bar_w + S(6), bar_h + S(6), edge);
|
||||||
|
img.draw_rect(bar_x, bar_y, bar_w, bar_h, panel);
|
||||||
|
img.draw_rect(bar_x, bar_y, static_cast<int>(static_cast<float>(bar_w) * t), bar_h, gold);
|
||||||
|
|
||||||
|
char percent[32];
|
||||||
|
std::snprintf(percent, sizeof(percent), "%d%%", static_cast<int>(t * 100.0F + 0.5F));
|
||||||
|
draw_text(img, (static_cast<int>(width) - static_cast<int>(text_width(percent, scale))) / 2, bar_y + bar_h + S(18), percent, cream, scale);
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A small translucent label for the top-left corner, e.g. `FPS: 155/160`.
|
||||||
|
* `cap == 0` means vertical sync, `cap < 0` means uncapped.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto compose_fps_label(uint32 fps, int cap) -> image {
|
||||||
|
char text[64];
|
||||||
|
if (cap == 0) {
|
||||||
|
std::snprintf(text, sizeof(text), "FPS: %u/vsync", fps);
|
||||||
|
} else if (cap < 0) {
|
||||||
|
std::snprintf(text, sizeof(text), "FPS: %u", fps);
|
||||||
|
} else {
|
||||||
|
std::snprintf(text, sizeof(text), "FPS: %u/%d", fps, cap);
|
||||||
|
}
|
||||||
|
const auto w = text_width(text, 1U) + 8U;
|
||||||
|
const auto h = detail::glyph_height + 6U;
|
||||||
|
image img(w, h, argb(30, 6, 6, 200)); // warm translucent backing
|
||||||
|
draw_text(img, 4, 3, text, argb(240, 200, 90), 1U);
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Box-downsample `source` so its width is at most `max_width` (aspect
|
||||||
|
* preserved). Returns a copy when it is already small enough.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto downscale(const image &source, uint32 max_width) -> image {
|
||||||
|
if (source.empty() || source.width() <= max_width) return source;
|
||||||
|
const auto scale = static_cast<uint32>(std::ceil(static_cast<double>(source.width()) / static_cast<double>(max_width)));
|
||||||
|
const auto w = std::max(1U, source.width() / scale);
|
||||||
|
const auto h = std::max(1U, source.height() / scale);
|
||||||
|
image out(w, h);
|
||||||
|
for (uint32 y = 0; y < h; ++y) {
|
||||||
|
for (uint32 x = 0; x < w; ++x) {
|
||||||
|
uint32 r = 0;
|
||||||
|
uint32 g = 0;
|
||||||
|
uint32 b = 0;
|
||||||
|
uint32 n = 0;
|
||||||
|
for (uint32 dy = 0; dy < scale; ++dy) {
|
||||||
|
for (uint32 dx = 0; dx < scale; ++dx) {
|
||||||
|
const auto sx = std::min(source.width() - 1U, x * scale + dx);
|
||||||
|
const auto sy = std::min(source.height() - 1U, y * scale + dy);
|
||||||
|
const auto px = source.data()[static_cast<usize>(sy) * source.width() + sx];
|
||||||
|
r += (px >> 16U) & 0xFFU;
|
||||||
|
g += (px >> 8U) & 0xFFU;
|
||||||
|
b += px & 0xFFU;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.set(static_cast<int>(x), static_cast<int>(y), argb(static_cast<uint8>(r / n), static_cast<uint8>(g / n), static_cast<uint8>(b / n)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy `full` (a whole-map overview) and mark the camera's location with a
|
||||||
|
* gold rectangle. `u`/`v` are the normalized map position (v is y-down).
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto compose_minimap(const image &full, float u, float v) -> image {
|
||||||
|
image out = full;
|
||||||
|
if (full.empty()) return out;
|
||||||
|
const int r = std::max(4, static_cast<int>(full.width()) / 48);
|
||||||
|
const int x = std::clamp(static_cast<int>(u * static_cast<float>(full.width())), r, static_cast<int>(full.width()) - r - 1);
|
||||||
|
const int y = std::clamp(static_cast<int>(v * static_cast<float>(full.height())), r, static_cast<int>(full.height()) - r - 1);
|
||||||
|
const auto gold = argb(240, 200, 90);
|
||||||
|
out.draw_rect(x - r, y - r, 2 * r, 2, gold);
|
||||||
|
out.draw_rect(x - r, y + r - 2, 2 * r, 2, gold);
|
||||||
|
out.draw_rect(x - r, y - r, 2, 2 * r, gold);
|
||||||
|
out.draw_rect(x + r - 2, y - r, 2, 2 * r, gold);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline auto read_u16(std::span<const uint8> data, usize at) -> uint32 {
|
[[nodiscard]] inline auto read_u16(std::span<const uint8> data, usize at) -> uint32 {
|
||||||
return static_cast<uint32>(data[at]) | (static_cast<uint32>(data[at + 1U]) << 8U);
|
return static_cast<uint32>(data[at]) | (static_cast<uint32>(data[at + 1U]) << 8U);
|
||||||
}
|
}
|
||||||
@@ -223,7 +470,7 @@ export namespace ra3::render {
|
|||||||
out[at + 2U] = static_cast<uint8>(value >> 16U);
|
out[at + 2U] = static_cast<uint8>(value >> 16U);
|
||||||
out[at + 3U] = static_cast<uint8>(value >> 24U);
|
out[at + 3U] = static_cast<uint8>(value >> 24U);
|
||||||
};
|
};
|
||||||
auto put16 = [&out](usize at, uint16_t value) {
|
auto put16 = [&out](usize at, std::uint16_t value) {
|
||||||
out[at] = static_cast<uint8>(value);
|
out[at] = static_cast<uint8>(value);
|
||||||
out[at + 1U] = static_cast<uint8>(value >> 8U);
|
out[at + 1U] = static_cast<uint8>(value >> 8U);
|
||||||
};
|
};
|
||||||
@@ -308,4 +555,127 @@ export namespace ra3::render {
|
|||||||
}
|
}
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The destination of the map image on screen, in window coordinates.
|
||||||
|
*
|
||||||
|
* `x`/`y` is the top-left corner and `w`/`h` the size, in the same units as
|
||||||
|
* the window. The viewer draws `image * w` into this rectangle; whatever is
|
||||||
|
* left of the window stays background, so the map is letterboxed instead of
|
||||||
|
* stretched.
|
||||||
|
*/
|
||||||
|
struct view_rect {
|
||||||
|
float x = 0.0F;
|
||||||
|
float y = 0.0F;
|
||||||
|
float w = 0.0F;
|
||||||
|
float h = 0.0F;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Letterbox an `iw`x`ih` image into a `ww`x`wh` window, preserving aspect. */
|
||||||
|
[[nodiscard]] inline auto fit_rect(float iw, float ih, float ww, float wh) -> view_rect {
|
||||||
|
if (iw <= 0.0F || ih <= 0.0F || ww <= 0.0F || wh <= 0.0F) return {};
|
||||||
|
const auto scale = std::min(ww / iw, wh / ih);
|
||||||
|
const auto w = iw * scale;
|
||||||
|
const auto h = ih * scale;
|
||||||
|
return {(ww - w) * 0.5F, (wh - h) * 0.5F, w, h};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 2D pan/zoom camera over a raster map, reproducing the Red Alert 3
|
||||||
|
* tactical view controls:
|
||||||
|
*
|
||||||
|
* - the wheel zooms (zoom 1 === the whole map fits the window);
|
||||||
|
* - pushing the cursor against a screen edge scrolls the view;
|
||||||
|
* - the view is clamped so it never leaves the map.
|
||||||
|
*
|
||||||
|
* `center_x`/`center_y` are the normalized image position (0..1, y down)
|
||||||
|
* held at the centre of the viewport. The camera mirrors the retail view
|
||||||
|
* object (`TheTacticalView`, retail `ra3_1.12.game` `0x00cdb7b4`), whose
|
||||||
|
* zoom is the scalar the debug overlay prints at `0x00c0b900`; the
|
||||||
|
* per-map scroll scaling is `cameraScrollSpeedScalar` (map data table at
|
||||||
|
* `0x00c11a54`).
|
||||||
|
*/
|
||||||
|
struct view_camera {
|
||||||
|
float zoom = 1.0F;
|
||||||
|
float center_x = 0.5F;
|
||||||
|
float center_y = 0.5F;
|
||||||
|
float min_zoom = 1.0F;
|
||||||
|
float max_zoom = 24.0F;
|
||||||
|
float zoom_step = 1.15F;
|
||||||
|
float edge_scroll_viewports_per_second = 0.55F; ///< speed of a full edge push
|
||||||
|
float edge_margin = 24.0F; ///< pixels from the border
|
||||||
|
|
||||||
|
/** Keep the visible window inside the image. */
|
||||||
|
auto clamp_center() -> void {
|
||||||
|
const float half = 0.5F / zoom;
|
||||||
|
center_x = std::clamp(center_x, half, 1.0F - half);
|
||||||
|
center_y = std::clamp(center_y, half, 1.0F - half);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Zoom by `factor` (wheel up > 1) about the viewport centre. */
|
||||||
|
auto zoom_by(float factor) -> void {
|
||||||
|
zoom = std::clamp(zoom * factor, min_zoom, max_zoom);
|
||||||
|
this->clamp_center();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Scroll directly by a normalized image delta. */
|
||||||
|
auto scroll(float dx, float dy) -> void {
|
||||||
|
center_x += dx;
|
||||||
|
center_y += dy;
|
||||||
|
this->clamp_center();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push the camera when the cursor `(mouse_x, mouse_y)` is within
|
||||||
|
* `edge_margin` of a window edge. `dt` is the frame time in seconds.
|
||||||
|
*/
|
||||||
|
auto edge_scroll(float mouse_x, float mouse_y, float window_w, float window_h, float dt) -> void {
|
||||||
|
if (zoom <= min_zoom + 1.0e-4F || window_w <= 0.0F || window_h <= 0.0F) return;
|
||||||
|
float dir_x = 0.0F;
|
||||||
|
float dir_y = 0.0F;
|
||||||
|
if (mouse_x <= edge_margin) {
|
||||||
|
dir_x = -1.0F;
|
||||||
|
} else if (mouse_x >= window_w - edge_margin) {
|
||||||
|
dir_x = 1.0F;
|
||||||
|
}
|
||||||
|
if (mouse_y <= edge_margin) {
|
||||||
|
dir_y = -1.0F;
|
||||||
|
} else if (mouse_y >= window_h - edge_margin) {
|
||||||
|
dir_y = 1.0F;
|
||||||
|
}
|
||||||
|
if (dir_x == 0.0F && dir_y == 0.0F) return;
|
||||||
|
const auto step = (1.0F / zoom) * edge_scroll_viewports_per_second * dt;
|
||||||
|
this->scroll(dir_x * step, dir_y * step);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resolve the image destination rectangle for a window of `window_w` x `window_h`. */
|
||||||
|
[[nodiscard]] auto rect(float image_w, float image_h, float window_w, float window_h) const -> view_rect {
|
||||||
|
if (image_w <= 0.0F || image_h <= 0.0F) return {};
|
||||||
|
const auto fit = std::min(window_w / image_w, window_h / image_h);
|
||||||
|
const auto scale = fit * zoom;
|
||||||
|
const auto w = image_w * scale;
|
||||||
|
const auto h = image_h * scale;
|
||||||
|
auto x = window_w * 0.5F - w * center_x;
|
||||||
|
auto y = window_h * 0.5F - h * center_y;
|
||||||
|
x = w <= window_w ? (window_w - w) * 0.5F : std::clamp(x, window_w - w, 0.0F);
|
||||||
|
y = h <= window_h ? (window_h - h) * 0.5F : std::clamp(y, window_h - h, 0.0F);
|
||||||
|
return {x, y, w, h};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A perspective camera aimed at a ground target, like the retail tactical
|
||||||
|
* view. The wheel changes `height` (moving the camera closer/farther), not
|
||||||
|
* an image scale; `yaw`/`pitch` orbit it.
|
||||||
|
*/
|
||||||
|
struct camera3d {
|
||||||
|
float target_x = 0.0F; ///< World position the camera looks at.
|
||||||
|
float target_y = 0.0F;
|
||||||
|
float yaw = 0.0F; ///< Radians; 0 looks toward +Y.
|
||||||
|
float pitch = 1.02F; ///< Radians above the horizon (~58 degrees down).
|
||||||
|
float height = 420.0F; ///< Camera height above the target's ground.
|
||||||
|
float fov = 0.85F; ///< Vertical field of view, radians.
|
||||||
|
float min_height = 120.0F;
|
||||||
|
float max_height = 1600.0F;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+256
-144
@@ -1,29 +1,33 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
export module ra3.skirmish;
|
export module ra3.skirmish;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
export import ra3.core;
|
export import ra3.core;
|
||||||
export import ra3.map;
|
export import ra3.map;
|
||||||
export import ra3.game;
|
export import ra3.game;
|
||||||
|
export import ra3.data;
|
||||||
import ra3.logic;
|
import ra3.logic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A minimal, headless skirmish simulation.
|
* A headless skirmish simulation driven by the real Red Alert 3 balance.
|
||||||
*
|
*
|
||||||
* Two sides start on a real map's waypoint positions, earn credits, train
|
* Two sides start on a real map's waypoints, build a base, extract ore and
|
||||||
* units and fight until one side's base is destroyed. The simulation is fully
|
* fight. The mechanics mirror SAGE where it matters and use the retail numbers
|
||||||
* deterministic: fixed 30 Hz steps, no wall-clock, and a seeded logic random
|
* from `ra3.data`:
|
||||||
* stream. There is no networking, no online service and no EA account - this is
|
*
|
||||||
* offline skirmish only.
|
* - damage is resolved through the target's `ArmorTemplate` for the weapon's
|
||||||
|
* damage type (`UNRESISTABLE` bypasses it);
|
||||||
|
* - a weapon may only target what its `AntiMask` allows (an attack dog's maul
|
||||||
|
* can never touch a structure or vehicle);
|
||||||
|
* - structures and units are built through a pay-as-you-go queue gated on
|
||||||
|
* power, cost and the tech prerequisite;
|
||||||
|
* - refineries run the ore extraction cycle against a finite node;
|
||||||
|
* - a side is defeated when it has no structures left, and the last side
|
||||||
|
* standing wins (the engine's team-wipe victory).
|
||||||
|
*
|
||||||
|
* The simulation is fully deterministic: fixed 30 Hz steps, no wall-clock and a
|
||||||
|
* seeded logic random stream. There is no networking, no online service and no
|
||||||
|
* EA account - this is offline skirmish only.
|
||||||
*/
|
*/
|
||||||
export namespace ra3::skirmish {
|
export namespace ra3::skirmish {
|
||||||
using ra3::core::coord3d;
|
using ra3::core::coord3d;
|
||||||
@@ -32,75 +36,29 @@ export namespace ra3::skirmish {
|
|||||||
using ra3::core::uint32;
|
using ra3::core::uint32;
|
||||||
using ra3::core::uint8;
|
using ra3::core::uint8;
|
||||||
using ra3::core::usize;
|
using ra3::core::usize;
|
||||||
|
using ra3::data::entity_kind;
|
||||||
|
|
||||||
/** Broad unit role. */
|
/** A live entity instance. */
|
||||||
enum class unit_class : uint8 { harvester, infantry, tank, base };
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto to_string(unit_class value) -> std::string_view {
|
|
||||||
switch (value) {
|
|
||||||
case unit_class::harvester:
|
|
||||||
return "Harvester";
|
|
||||||
case unit_class::infantry:
|
|
||||||
return "Infantry";
|
|
||||||
case unit_class::tank:
|
|
||||||
return "Tank";
|
|
||||||
case unit_class::base:
|
|
||||||
return "Base";
|
|
||||||
}
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Static combat/economy stats for a unit class.
|
|
||||||
*
|
|
||||||
* These are OpenRA3's own balance values (the retail numbers live in
|
|
||||||
* compiled assets that are not parsed yet), chosen so a skirmish resolves.
|
|
||||||
*/
|
|
||||||
struct unit_type {
|
|
||||||
unit_class cls = unit_class::infantry;
|
|
||||||
std::string_view name;
|
|
||||||
real max_health = 1.0F;
|
|
||||||
real speed = 0.0F;
|
|
||||||
real weapon_damage = 0.0F;
|
|
||||||
real weapon_range = 0.0F;
|
|
||||||
real weapon_cooldown = 1.0F;
|
|
||||||
int32 cost = 0;
|
|
||||||
real build_time = 1.0F;
|
|
||||||
bool mobile = false;
|
|
||||||
bool produces = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr unit_type harvester_type{unit_class::harvester, "Harvester", 300.0F, 10.0F, 0.0F, 0.0F, 0.0F, 1400, 8.0F, true, false};
|
|
||||||
inline constexpr unit_type infantry_type{unit_class::infantry, "Infantry", 120.0F, 12.0F, 8.0F, 55.0F, 1.0F, 150, 3.0F, true, false};
|
|
||||||
inline constexpr unit_type tank_type{unit_class::tank, "Tank", 480.0F, 9.0F, 30.0F, 70.0F, 1.6F, 900, 8.0F, true, false};
|
|
||||||
inline constexpr unit_type base_type{unit_class::base, "Base", 2500.0F, 0.0F, 20.0F, 90.0F, 2.0F, 0, 0.0F, false, true};
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto unit_stats(unit_class value) -> const unit_type & {
|
|
||||||
switch (value) {
|
|
||||||
case unit_class::harvester:
|
|
||||||
return harvester_type;
|
|
||||||
case unit_class::infantry:
|
|
||||||
return infantry_type;
|
|
||||||
case unit_class::tank:
|
|
||||||
return tank_type;
|
|
||||||
case unit_class::base:
|
|
||||||
return base_type;
|
|
||||||
}
|
|
||||||
return infantry_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A live unit instance. */
|
|
||||||
struct unit {
|
struct unit {
|
||||||
uint32 id = 0;
|
uint32 id = 0;
|
||||||
uint32 owner = 0;
|
uint32 owner = 0;
|
||||||
unit_class cls = unit_class::infantry;
|
entity_kind kind = entity_kind::none;
|
||||||
coord3d position{};
|
coord3d position{};
|
||||||
real health = 0.0F;
|
real health = 0.0F;
|
||||||
real max_health = 0.0F;
|
real max_health = 0.0F;
|
||||||
real cooldown = 0.0F;
|
real cooldown = 0.0F; ///< Seconds until the weapon may fire again.
|
||||||
|
real cycle_timer = 0.0F; ///< Refinery ore-extraction progress, seconds.
|
||||||
bool alive = true;
|
bool alive = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** One entry in a player's production queue. */
|
||||||
|
struct build_job {
|
||||||
|
entity_kind kind = entity_kind::none;
|
||||||
|
uint32 progress = 0; ///< Frames completed.
|
||||||
|
uint32 total = 0; ///< Frames required.
|
||||||
|
int32 paid = 0; ///< Credits paid so far (pay-as-you-go).
|
||||||
|
};
|
||||||
|
|
||||||
/** Per-player state for the match. */
|
/** Per-player state for the match. */
|
||||||
struct player_state {
|
struct player_state {
|
||||||
uint32 index = 0;
|
uint32 index = 0;
|
||||||
@@ -111,19 +69,22 @@ export namespace ra3::skirmish {
|
|||||||
real money_accumulator = 0.0F;
|
real money_accumulator = 0.0F;
|
||||||
coord3d start{};
|
coord3d start{};
|
||||||
uint32 base_unit = 0;
|
uint32 base_unit = 0;
|
||||||
real build_timer = 0.0F;
|
real think_timer = 0.0F;
|
||||||
int32 kills = 0;
|
int32 kills = 0;
|
||||||
int32 losses = 0;
|
int32 losses = 0;
|
||||||
|
int32 power_produced = 0;
|
||||||
|
int32 power_consumed = 0;
|
||||||
|
int32 ore_remaining = 0; ///< Credits left in this side's ore node(s).
|
||||||
|
std::vector<build_job> queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Skirmish parameters. */
|
/** Skirmish parameters. */
|
||||||
struct match_config {
|
struct match_config {
|
||||||
std::string map_id = "builtin";
|
std::string map_id = "builtin";
|
||||||
int32 starting_money = 10000;
|
int32 starting_money = ra3::data::economy.starting_credits;
|
||||||
uint32 seed = 1;
|
uint32 seed = 1;
|
||||||
uint32 max_frames = 30U * 60U * 15U;
|
uint32 max_frames = 30U * 60U * 15U;
|
||||||
real income_per_second = 25.0F;
|
uint32 max_units_per_player = 30U;
|
||||||
real harvester_income_per_second = 15.0F;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Outcome of a completed (or capped) match. */
|
/** Outcome of a completed (or capped) match. */
|
||||||
@@ -165,16 +126,19 @@ export namespace ra3::skirmish {
|
|||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Run until a base falls or the frame cap is reached. */
|
/** Run until a side is wiped out or the frame cap is reached. */
|
||||||
auto run() -> match_result {
|
auto run() -> match_result {
|
||||||
while (!this->decided() && frame_ < config_.max_frames) this->step();
|
while (!this->decided() && frame_ < config_.max_frames) this->step();
|
||||||
|
if (!decided_) this->decide_on_timeout();
|
||||||
return this->result();
|
return this->result();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Advance exactly one 30 Hz frame. */
|
/** Advance exactly one 30 Hz frame. */
|
||||||
auto step() -> void {
|
auto step() -> void {
|
||||||
constexpr real dt = 1.0F / 30.0F;
|
constexpr real dt = 1.0F / 30.0F;
|
||||||
this->apply_income(dt);
|
this->update_power();
|
||||||
|
this->update_economy(dt);
|
||||||
|
this->update_production();
|
||||||
this->run_ai(dt);
|
this->run_ai(dt);
|
||||||
this->update_units(dt);
|
this->update_units(dt);
|
||||||
this->check_victory();
|
this->check_victory();
|
||||||
@@ -211,7 +175,7 @@ export namespace ra3::skirmish {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr real build_interval_seconds = 4.0F;
|
static constexpr real ai_think_seconds = 2.0F;
|
||||||
|
|
||||||
auto spawn_player(uint32 index, std::string name, game::faction side, bool human, const coord3d &start) -> void {
|
auto spawn_player(uint32 index, std::string name, game::faction side, bool human, const coord3d &start) -> void {
|
||||||
player_state player;
|
player_state player;
|
||||||
@@ -221,66 +185,168 @@ export namespace ra3::skirmish {
|
|||||||
player.human = human;
|
player.human = human;
|
||||||
player.money = config_.starting_money;
|
player.money = config_.starting_money;
|
||||||
player.start = start;
|
player.start = start;
|
||||||
player.base_unit = this->spawn_unit(index, unit_class::base, start);
|
player.ore_remaining = ra3::data::economy.ore_node_capacity;
|
||||||
this->spawn_unit(index, unit_class::harvester, {start.x + 60.0F, start.y + 20.0F, 0.0F});
|
player.base_unit = this->spawn_unit(index, entity_kind::construction_yard, start);
|
||||||
this->spawn_unit(index, unit_class::tank, {start.x + 40.0F, start.y + 50.0F, 0.0F});
|
|
||||||
this->spawn_unit(index, unit_class::tank, {start.x + 70.0F, start.y - 30.0F, 0.0F});
|
|
||||||
this->spawn_unit(index, unit_class::infantry, {start.x + 30.0F, start.y - 60.0F, 0.0F});
|
|
||||||
this->spawn_unit(index, unit_class::infantry, {start.x + 90.0F, start.y - 60.0F, 0.0F});
|
|
||||||
players_.push_back(std::move(player));
|
players_.push_back(std::move(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto spawn_unit(uint32 owner, unit_class cls, const coord3d &position) -> uint32 {
|
auto spawn_unit(uint32 owner, entity_kind kind, const coord3d &position) -> uint32 {
|
||||||
const auto &stats = unit_stats(cls);
|
|
||||||
unit created;
|
unit created;
|
||||||
created.id = next_unit_id_++;
|
created.id = next_unit_id_++;
|
||||||
created.owner = owner;
|
created.owner = owner;
|
||||||
created.cls = cls;
|
created.kind = kind;
|
||||||
created.position = position;
|
created.position = position;
|
||||||
created.health = stats.max_health;
|
created.health = ra3::data::max_health_of(kind);
|
||||||
created.max_health = stats.max_health;
|
created.max_health = created.health;
|
||||||
units_.push_back(created);
|
units_.push_back(created);
|
||||||
return created.id;
|
return created.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto apply_income(real dt) -> void {
|
/** Sum structure energy; a side is low on power when it draws more than it makes. */
|
||||||
|
auto update_power() -> void {
|
||||||
for (auto &player: players_) {
|
for (auto &player: players_) {
|
||||||
real rate = config_.income_per_second;
|
player.power_produced = 0;
|
||||||
|
player.power_consumed = 0;
|
||||||
for (const auto &entry: units_) {
|
for (const auto &entry: units_) {
|
||||||
if (entry.alive && entry.owner == player.index && entry.cls == unit_class::harvester) rate += config_.harvester_income_per_second;
|
if (!entry.alive || entry.owner != player.index || !ra3::data::is_structure(entry.kind)) continue;
|
||||||
}
|
const auto energy = ra3::data::structure_of(entry.kind).energy;
|
||||||
player.money_accumulator += rate * dt;
|
if (energy >= 0) {
|
||||||
const auto whole = static_cast<int32>(player.money_accumulator);
|
player.power_produced += energy;
|
||||||
if (whole > 0) {
|
} else {
|
||||||
player.money += whole;
|
player.power_consumed += -energy;
|
||||||
player.money_accumulator -= static_cast<real>(whole);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto low_power(uint32 owner) const -> bool {
|
||||||
|
const auto &player = players_.at(owner);
|
||||||
|
return player.power_consumed > player.power_produced;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ore extraction: every refinery runs the RA3 cycle (11 s for 250
|
||||||
|
* credits) against the side's finite node. Under low power the cycle is
|
||||||
|
* half speed.
|
||||||
|
*/
|
||||||
|
auto update_economy(real dt) -> void {
|
||||||
|
for (auto &player: players_) {
|
||||||
|
const auto rate = low_power(player.index) ? (1.0F / static_cast<real>(ra3::data::world_rules.low_power_mining_divisor)) : 1.0F;
|
||||||
|
for (auto &entry: units_) {
|
||||||
|
if (!entry.alive || entry.owner != player.index || !ra3::data::is_structure(entry.kind)) continue;
|
||||||
|
if (!ra3::data::structure_of(entry.kind).is_refinery) continue;
|
||||||
|
|
||||||
|
entry.cycle_timer += dt * rate;
|
||||||
|
if (entry.cycle_timer < ra3::data::economy.extraction_seconds) continue;
|
||||||
|
entry.cycle_timer -= ra3::data::economy.extraction_seconds;
|
||||||
|
|
||||||
|
const auto yield = ra3::data::ore_per_cycle(player.ore_remaining);
|
||||||
|
player.ore_remaining = player.ore_remaining > yield ? player.ore_remaining - yield : 0;
|
||||||
|
player.money += yield;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Work each player's production queue. Cost is paid as the job
|
||||||
|
* progresses, a job stalls while unaffordable, and low power halves the
|
||||||
|
* production rate.
|
||||||
|
*/
|
||||||
|
auto update_production() -> void {
|
||||||
|
for (auto &player: players_) {
|
||||||
|
if (player.queue.empty()) continue;
|
||||||
|
auto &job = player.queue.front();
|
||||||
|
|
||||||
|
const auto cost = ra3::data::cost_of(job.kind);
|
||||||
|
const auto advance = low_power(player.index) ? (frame_ % static_cast<uint32>(ra3::data::world_rules.low_power_production_divisor) == 0U) : true;
|
||||||
|
if (advance && job.total > 0U) {
|
||||||
|
const auto next_progress = job.progress + 1U;
|
||||||
|
const auto target_paid = static_cast<int32>((static_cast<std::int64_t>(cost) * next_progress) / job.total);
|
||||||
|
const auto delta = target_paid - job.paid;
|
||||||
|
if (delta <= player.money) {
|
||||||
|
player.money -= delta;
|
||||||
|
job.paid = target_paid;
|
||||||
|
job.progress = next_progress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job.progress < job.total) continue;
|
||||||
|
|
||||||
|
if (ra3::data::is_structure(job.kind)) {
|
||||||
|
const auto &base = this->find_unit(player.base_unit);
|
||||||
|
const coord3d at = base != nullptr && base->alive ? base->position : player.start;
|
||||||
|
const auto offset = static_cast<real>(ra3::data::structure_of(job.kind).size_cells) * ra3::data::cell_size + 40.0F;
|
||||||
|
this->spawn_unit(player.index, job.kind, {at.x + offset, at.y + offset * static_cast<real>(player.queue.size()), 0.0F});
|
||||||
|
} else {
|
||||||
|
const auto *producer = this->find_producer(player.index, ra3::data::unit_of(job.kind).built_by);
|
||||||
|
if (producer == nullptr) continue; // wait for the producer
|
||||||
|
this->spawn_unit(player.index, job.kind, {producer->position.x + 40.0F, producer->position.y + 40.0F, 0.0F});
|
||||||
|
}
|
||||||
|
player.queue.erase(player.queue.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A simple scripted opponent: build a base, then train an army. */
|
||||||
auto run_ai(real dt) -> void {
|
auto run_ai(real dt) -> void {
|
||||||
for (auto &player: players_) {
|
for (auto &player: players_) {
|
||||||
player.build_timer += dt;
|
player.think_timer += dt;
|
||||||
if (player.build_timer < build_interval_seconds) continue;
|
if (player.think_timer < ai_think_seconds) continue;
|
||||||
player.build_timer = 0.0F;
|
player.think_timer = 0.0F;
|
||||||
|
if (!player.queue.empty()) continue;
|
||||||
|
if (this->unit_count(player.index) >= config_.max_units_per_player) continue;
|
||||||
|
|
||||||
const auto *base = this->find_unit(player.base_unit);
|
if (this->enqueue_if_missing(player, entity_kind::power_plant)) continue;
|
||||||
if (base == nullptr || !base->alive) continue;
|
if (this->enqueue_if_missing(player, entity_kind::refinery)) continue;
|
||||||
if (player.money < tank_type.cost) continue;
|
if (this->enqueue_if_missing(player, entity_kind::barracks)) continue;
|
||||||
|
if (this->enqueue_if_missing(player, entity_kind::war_factory)) continue;
|
||||||
|
|
||||||
player.money -= tank_type.cost;
|
// Army composition: roughly half infantry, half tanks.
|
||||||
this->spawn_unit(player.index, unit_class::tank, {base->position.x + 50.0F, base->position.y - 50.0F, 0.0F});
|
const bool want_infantry = (frame_ / 30U) % 2U == 0U;
|
||||||
|
const auto choice = want_infantry ? entity_kind::peacekeeper : entity_kind::guardian_tank;
|
||||||
|
if (player.money >= ra3::data::cost_of(choice) * 2) this->enqueue(player, choice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Queue `kind` when the side has none and can afford it. */
|
||||||
|
auto enqueue_if_missing(player_state &player, entity_kind kind) -> bool {
|
||||||
|
for (const auto &entry: units_) {
|
||||||
|
if (entry.alive && entry.owner == player.index && entry.kind == kind) return false;
|
||||||
|
}
|
||||||
|
const auto &data = ra3::data::structure_of(kind);
|
||||||
|
if (data.prerequisite != entity_kind::none) {
|
||||||
|
bool have_prereq = false;
|
||||||
|
for (const auto &entry: units_) {
|
||||||
|
if (entry.alive && entry.owner == player.index && entry.kind == data.prerequisite) have_prereq = true;
|
||||||
|
}
|
||||||
|
if (!have_prereq) return false;
|
||||||
|
}
|
||||||
|
if (player.money < ra3::data::cost_of(kind)) return false;
|
||||||
|
return this->enqueue(player, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto enqueue(player_state &player, entity_kind kind) -> bool {
|
||||||
|
if (player.queue.size() >= 9U) return false;
|
||||||
|
build_job job;
|
||||||
|
job.kind = kind;
|
||||||
|
job.total = ra3::data::build_frames_of(kind);
|
||||||
|
player.queue.push_back(job);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move and fight. A unit seeks the nearest enemy its weapon can legally
|
||||||
|
* target; with no such target it advances on the enemy base so a match
|
||||||
|
* always converges.
|
||||||
|
*/
|
||||||
auto update_units(real dt) -> void {
|
auto update_units(real dt) -> void {
|
||||||
for (auto &entry: units_) {
|
for (auto &entry: units_) {
|
||||||
if (!entry.alive) continue;
|
if (!entry.alive) continue;
|
||||||
if (entry.cooldown > 0.0F) entry.cooldown -= dt;
|
if (entry.cooldown > 0.0F) entry.cooldown -= dt;
|
||||||
if (entry.cls == unit_class::harvester) continue; // economy only, no combat
|
|
||||||
|
|
||||||
const auto &stats = unit_stats(entry.cls);
|
const auto *weapon = ra3::data::weapon_of(entry.kind);
|
||||||
auto *target = this->nearest_enemy(entry);
|
if (weapon == nullptr) continue;
|
||||||
|
|
||||||
|
auto *target = this->nearest_target(entry, *weapon);
|
||||||
if (target == nullptr) continue;
|
if (target == nullptr) continue;
|
||||||
|
|
||||||
const auto dx = target->position.x - entry.position.x;
|
const auto dx = target->position.x - entry.position.x;
|
||||||
@@ -288,50 +354,52 @@ export namespace ra3::skirmish {
|
|||||||
const auto dz = target->position.z - entry.position.z;
|
const auto dz = target->position.z - entry.position.z;
|
||||||
const auto distance = std::sqrt(dx * dx + dy * dy + dz * dz);
|
const auto distance = std::sqrt(dx * dx + dy * dy + dz * dz);
|
||||||
|
|
||||||
if (distance > stats.weapon_range && stats.mobile && stats.speed > 0.0F) {
|
if (distance > weapon->attack_range) {
|
||||||
if (distance > 1.0e-3F) {
|
const auto speed = ra3::data::unit_of(entry.kind).speed;
|
||||||
const auto travel = stats.speed * dt;
|
if (speed > 0.0F && distance > 1.0e-3F) {
|
||||||
|
const auto travel = speed * dt;
|
||||||
entry.position.x += dx / distance * travel;
|
entry.position.x += dx / distance * travel;
|
||||||
entry.position.y += dy / distance * travel;
|
entry.position.y += dy / distance * travel;
|
||||||
}
|
}
|
||||||
} else if (stats.weapon_damage > 0.0F && distance <= stats.weapon_range && entry.cooldown <= 0.0F) {
|
continue;
|
||||||
entry.cooldown = stats.weapon_cooldown;
|
}
|
||||||
target->health -= stats.weapon_damage;
|
|
||||||
if (target->health <= 0.0F) {
|
if (entry.cooldown > 0.0F) continue;
|
||||||
target->health = 0.0F;
|
entry.cooldown = weapon->reload_seconds + weapon->firing_seconds;
|
||||||
target->alive = false;
|
this->apply_damage(entry, *target, *weapon);
|
||||||
this->player_ref(entry.owner).kills += 1;
|
|
||||||
this->player_ref(target->owner).losses += 1;
|
if (weapon->splash_radius > 0.0F) {
|
||||||
|
for (auto &other: units_) {
|
||||||
|
if (!other.alive || other.owner == entry.owner || &other == target) continue;
|
||||||
|
if (!weapon->can_target(ra3::data::class_of(other.kind))) continue;
|
||||||
|
const auto ox = other.position.x - target->position.x;
|
||||||
|
const auto oy = other.position.y - target->position.y;
|
||||||
|
if (ox * ox + oy * oy <= weapon->splash_radius * weapon->splash_radius) this->apply_damage(entry, other, *weapon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto check_victory() -> void {
|
/** Resolve one hit through the target's armour. */
|
||||||
const bool first_alive = this->base_alive(0U);
|
auto apply_damage(const unit &attacker, unit &target, const ra3::data::weapon_data &weapon) -> void {
|
||||||
const bool second_alive = this->base_alive(1U);
|
if (!target.alive) return;
|
||||||
if (first_alive && second_alive) return;
|
const auto damage = weapon.instakill ? target.max_health : ra3::data::armor_of(target.kind).adjust(weapon.type, weapon.damage);
|
||||||
decided_ = true;
|
target.health -= damage;
|
||||||
if (first_alive) {
|
if (target.health > 0.0F) return;
|
||||||
winner_ = 0;
|
|
||||||
} else if (second_alive) {
|
target.health = 0.0F;
|
||||||
winner_ = 1;
|
target.alive = false;
|
||||||
} else {
|
this->player_ref(attacker.owner).kills += 1;
|
||||||
winner_ = -1;
|
this->player_ref(target.owner).losses += 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto base_alive(uint32 owner) const -> bool {
|
/** Nearest enemy this weapon may target, or `nullptr`. */
|
||||||
const auto &player = players_.at(owner);
|
[[nodiscard]] auto nearest_target(const unit &self, const ra3::data::weapon_data &weapon) -> unit * {
|
||||||
const auto *base = this->find_unit(player.base_unit);
|
|
||||||
return base != nullptr && base->alive;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto nearest_enemy(const unit &self) -> unit * {
|
|
||||||
unit *best = nullptr;
|
unit *best = nullptr;
|
||||||
real best_distance = 1.0e30F;
|
real best_distance = 1.0e30F;
|
||||||
for (auto &candidate: units_) {
|
for (auto &candidate: units_) {
|
||||||
if (!candidate.alive || candidate.owner == self.owner) continue;
|
if (!candidate.alive || candidate.owner == self.owner) continue;
|
||||||
|
if (!weapon.can_target(ra3::data::class_of(candidate.kind))) continue;
|
||||||
const auto dx = candidate.position.x - self.position.x;
|
const auto dx = candidate.position.x - self.position.x;
|
||||||
const auto dy = candidate.position.y - self.position.y;
|
const auto dy = candidate.position.y - self.position.y;
|
||||||
const auto squared = dx * dx + dy * dy;
|
const auto squared = dx * dx + dy * dy;
|
||||||
@@ -343,6 +411,43 @@ export namespace ra3::skirmish {
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Defeat is losing every structure; the last side standing wins. */
|
||||||
|
auto check_victory() -> void {
|
||||||
|
const bool first_alive = this->has_structures(0U);
|
||||||
|
const bool second_alive = this->has_structures(1U);
|
||||||
|
if (first_alive && second_alive) return;
|
||||||
|
decided_ = true;
|
||||||
|
winner_ = first_alive ? 0 : (second_alive ? 1 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Frame cap: the higher-scoring side wins, with a deterministic tie-break. */
|
||||||
|
auto decide_on_timeout() -> void {
|
||||||
|
decided_ = true;
|
||||||
|
const auto first = this->score_tuple(0U);
|
||||||
|
const auto second = this->score_tuple(1U);
|
||||||
|
winner_ = first == second ? -1 : (first > second ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto score_tuple(uint32 owner) const -> std::tuple<int32, int32, int32> {
|
||||||
|
const auto &player = players_.at(owner);
|
||||||
|
return {this->score(owner), player.kills, static_cast<int32>(this->unit_count(owner))};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto score(uint32 owner) const -> int32 {
|
||||||
|
int32 total = players_.at(owner).money;
|
||||||
|
for (const auto &entry: units_) {
|
||||||
|
if (entry.alive && entry.owner == owner) total += ra3::data::cost_of(entry.kind);
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto has_structures(uint32 owner) const -> bool {
|
||||||
|
for (const auto &entry: units_) {
|
||||||
|
if (entry.alive && entry.owner == owner && ra3::data::is_structure(entry.kind)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto find_unit(uint32 id) -> unit * {
|
[[nodiscard]] auto find_unit(uint32 id) -> unit * {
|
||||||
for (auto &entry: units_) {
|
for (auto &entry: units_) {
|
||||||
if (entry.id == id) return &entry;
|
if (entry.id == id) return &entry;
|
||||||
@@ -357,6 +462,13 @@ export namespace ra3::skirmish {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto find_producer(uint32 owner, entity_kind kind) -> unit * {
|
||||||
|
for (auto &entry: units_) {
|
||||||
|
if (entry.alive && entry.owner == owner && entry.kind == kind) return &entry;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto player_ref(uint32 index) -> player_state & { return players_.at(index); }
|
[[nodiscard]] auto player_ref(uint32 index) -> player_state & { return players_.at(index); }
|
||||||
|
|
||||||
match_config config_;
|
match_config config_;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+13
-14
@@ -1,24 +1,23 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
export module ra3.ui;
|
export module ra3.ui;
|
||||||
|
|
||||||
|
import std;
|
||||||
export import ra3.core;
|
export import ra3.core;
|
||||||
import ra3.render;
|
import ra3.render;
|
||||||
|
import ra3.client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fallback viewer used when the build has no SDL3. It never opens a window;
|
* Fallback SDL backend used when the build has no SDL3. It never opens a window;
|
||||||
* the caller should write an offscreen image instead.
|
* `init` fails so the caller can fall back to an offscreen image.
|
||||||
*/
|
*/
|
||||||
export namespace ra3::ui {
|
export namespace ra3::ui {
|
||||||
/** Window parameters (mirrors the SDL backend). */
|
class sdl_display final : public ra3::client::display {
|
||||||
struct viewer_options {
|
public:
|
||||||
std::string title = "OpenRA3";
|
[[nodiscard]] auto init(const ra3::client::display_options &) -> bool override { return false; }
|
||||||
int width = 1024;
|
[[nodiscard]] auto present(const ra3::render::image &, const ra3::render::view_rect &, bool) -> bool override { return false; }
|
||||||
int height = 768;
|
[[nodiscard]] auto poll_event(ra3::render::ui_event &) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto window_size() const -> std::pair<int, int> override { return {0, 0}; }
|
||||||
|
[[nodiscard]] auto key_down(ra3::render::ui_key) const -> bool override { return false; }
|
||||||
|
auto shutdown() -> void override {}
|
||||||
|
[[nodiscard]] auto name() const -> std::string_view override { return "sdl(null)"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Always returns false: this build has no windowing backend. */
|
|
||||||
[[nodiscard]] inline auto run_viewer(const ra3::render::image &, const viewer_options &) -> bool { return false; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
+159
-69
@@ -2,92 +2,182 @@ module;
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
export module ra3.ui;
|
export module ra3.ui;
|
||||||
|
|
||||||
|
import std;
|
||||||
export import ra3.core;
|
export import ra3.core;
|
||||||
import ra3.render;
|
import ra3.render;
|
||||||
|
import ra3.client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A minimal SDL3 window for viewing a rendered scene.
|
* The SDL3 presentation backend.
|
||||||
*
|
*
|
||||||
* Controls: drag with the left mouse button to pan, wheel to zoom, Esc/close
|
* SDL3 is the platform layer: it owns the window, the input, and (here) a
|
||||||
* to quit. When no display is available `run_viewer` returns false so the
|
* low-end `SDL_Renderer` blit path. Only the low-level display primitives live
|
||||||
* caller can fall back to an offscreen image.
|
* in this module; the interactive loops are shared in `ra3::client::display`.
|
||||||
|
* The Vulkan backend implements the same interface.
|
||||||
*/
|
*/
|
||||||
export namespace ra3::ui {
|
export namespace ra3::ui {
|
||||||
/** Window parameters. */
|
|
||||||
struct viewer_options {
|
|
||||||
std::string title = "OpenRA3";
|
|
||||||
int width = 1024;
|
|
||||||
int height = 768;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show `scene` in a window until the user quits.
|
* An SDL_Renderer-backed display: cheap, dependency-light, and used as the
|
||||||
*
|
* fallback when Vulkan is unavailable.
|
||||||
* @return true if the window ran; false if video init or window creation
|
|
||||||
* failed (typically no display).
|
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] inline auto run_viewer(const ra3::render::image &scene, const viewer_options &options) -> bool {
|
class sdl_display final : public ra3::client::display {
|
||||||
if (scene.empty()) return false;
|
public:
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO)) return false;
|
sdl_display() = default;
|
||||||
|
sdl_display(const sdl_display &) = delete;
|
||||||
|
auto operator=(const sdl_display &) -> sdl_display & = delete;
|
||||||
|
~sdl_display() override { this->shutdown(); }
|
||||||
|
|
||||||
SDL_Window *window = SDL_CreateWindow(options.title.c_str(), options.width, options.height, SDL_WINDOW_RESIZABLE);
|
[[nodiscard]] auto init(const ra3::client::display_options &options) -> bool override {
|
||||||
if (window == nullptr) {
|
if (!SDL_Init(SDL_INIT_VIDEO)) return false;
|
||||||
SDL_Quit();
|
this->fps_limit_ = options.fps_limit;
|
||||||
return false;
|
window_ = SDL_CreateWindow(options.title.c_str(), options.width, options.height,
|
||||||
|
SDL_WINDOW_RESIZABLE | (options.fullscreen ? SDL_WINDOW_FULLSCREEN : 0));
|
||||||
|
if (window_ == nullptr) {
|
||||||
|
SDL_Quit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SDL_RaiseWindow(window_);
|
||||||
|
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
||||||
|
if (renderer_ == nullptr) {
|
||||||
|
this->shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SDL_SetRenderVSync(renderer_, options.fps_limit == 0 ? 1 : 0);
|
||||||
|
SDL_StartTextInput(window_);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
SDL_Renderer *renderer = SDL_CreateRenderer(window, nullptr);
|
|
||||||
if (renderer == nullptr) {
|
|
||||||
SDL_DestroyWindow(window);
|
|
||||||
SDL_Quit();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, static_cast<int>(scene.width()),
|
|
||||||
static_cast<int>(scene.height()));
|
|
||||||
if (texture == nullptr) {
|
|
||||||
SDL_DestroyRenderer(renderer);
|
|
||||||
SDL_DestroyWindow(window);
|
|
||||||
SDL_Quit();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SDL_UpdateTexture(texture, nullptr, scene.data(), static_cast<int>(scene.width()) * 4);
|
|
||||||
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
|
|
||||||
|
|
||||||
float zoom = 1.0F;
|
[[nodiscard]] auto present(const ra3::render::image &frame, const ra3::render::view_rect &dest, bool changed) -> bool override {
|
||||||
float pan_x = 0.0F;
|
if (frame.empty() || renderer_ == nullptr) return false;
|
||||||
float pan_y = 0.0F;
|
if (changed || texture_ == nullptr || frame.width() != texture_w_ || frame.height() != texture_h_) {
|
||||||
bool running = true;
|
if (texture_ != nullptr) SDL_DestroyTexture(texture_);
|
||||||
SDL_Event event;
|
texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, static_cast<int>(frame.width()),
|
||||||
while (running) {
|
static_cast<int>(frame.height()));
|
||||||
|
if (texture_ == nullptr) return false;
|
||||||
|
texture_w_ = frame.width();
|
||||||
|
texture_h_ = frame.height();
|
||||||
|
SDL_SetTextureScaleMode(texture_, SDL_SCALEMODE_LINEAR);
|
||||||
|
SDL_UpdateTexture(texture_, nullptr, frame.data(), static_cast<int>(frame.width()) * 4);
|
||||||
|
}
|
||||||
|
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(renderer_);
|
||||||
|
const SDL_FRect dst{dest.x, dest.y, dest.w, dest.h};
|
||||||
|
SDL_RenderTexture(renderer_, texture_, nullptr, &dst);
|
||||||
|
SDL_RenderPresent(renderer_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto poll_event(ra3::render::ui_event &out) -> bool override {
|
||||||
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
if (event.type == SDL_EVENT_QUIT) {
|
switch (event.type) {
|
||||||
running = false;
|
case SDL_EVENT_QUIT:
|
||||||
} else if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE) {
|
out = {};
|
||||||
running = false;
|
out.type = ra3::render::ui_event_type::quit;
|
||||||
} else if (event.type == SDL_EVENT_MOUSE_WHEEL) {
|
return true;
|
||||||
zoom *= event.wheel.y > 0.0F ? 1.1F : (1.0F / 1.1F);
|
case SDL_EVENT_KEY_DOWN:
|
||||||
zoom = std::clamp(zoom, 0.1F, 20.0F);
|
if (const auto key = map_key(event.key.key); key != ra3::render::ui_key::none) {
|
||||||
} else if (event.type == SDL_EVENT_MOUSE_MOTION && (event.motion.state & SDL_BUTTON_LMASK) != 0U) {
|
out = {};
|
||||||
pan_x += event.motion.xrel;
|
out.type = ra3::render::ui_event_type::key;
|
||||||
pan_y += event.motion.yrel;
|
out.key = key;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDL_EVENT_TEXT_INPUT:
|
||||||
|
if (event.text.text[0] != '\0') {
|
||||||
|
out = {};
|
||||||
|
out.type = ra3::render::ui_event_type::text;
|
||||||
|
out.character = event.text.text[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDL_EVENT_MOUSE_MOTION:
|
||||||
|
out = {};
|
||||||
|
out.type = ra3::render::ui_event_type::mouse_move;
|
||||||
|
out.x = event.motion.x;
|
||||||
|
out.y = event.motion.y;
|
||||||
|
out.dx = event.motion.xrel;
|
||||||
|
out.dy = event.motion.yrel;
|
||||||
|
out.left = (event.motion.state & SDL_BUTTON_LMASK) != 0U;
|
||||||
|
return true;
|
||||||
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
|
out = {};
|
||||||
|
out.type = ra3::render::ui_event_type::mouse_button;
|
||||||
|
out.x = event.button.x;
|
||||||
|
out.y = event.button.y;
|
||||||
|
out.left = event.button.button == SDL_BUTTON_LEFT;
|
||||||
|
return true;
|
||||||
|
case SDL_EVENT_MOUSE_WHEEL:
|
||||||
|
out = {};
|
||||||
|
out.type = ra3::render::ui_event_type::wheel;
|
||||||
|
out.wheel = event.wheel.y;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
|
||||||
SDL_RenderClear(renderer);
|
|
||||||
const SDL_FRect dst{pan_x, pan_y, static_cast<float>(scene.width()) * zoom, static_cast<float>(scene.height()) * zoom};
|
|
||||||
SDL_RenderTexture(renderer, texture, nullptr, &dst);
|
|
||||||
SDL_RenderPresent(renderer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_DestroyTexture(texture);
|
[[nodiscard]] auto window_size() const -> std::pair<int, int> override {
|
||||||
SDL_DestroyRenderer(renderer);
|
int w = 0;
|
||||||
SDL_DestroyWindow(window);
|
int h = 0;
|
||||||
SDL_Quit();
|
SDL_GetWindowSizeInPixels(window_, &w, &h);
|
||||||
return true;
|
return {w, h};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto key_down(ra3::render::ui_key key) const -> bool override {
|
||||||
|
const bool *keys = SDL_GetKeyboardState(nullptr);
|
||||||
|
if (keys == nullptr) return false;
|
||||||
|
switch (key) {
|
||||||
|
case ra3::render::ui_key::up: return keys[SDL_SCANCODE_W] || keys[SDL_SCANCODE_UP];
|
||||||
|
case ra3::render::ui_key::down: return keys[SDL_SCANCODE_S] || keys[SDL_SCANCODE_DOWN];
|
||||||
|
case ra3::render::ui_key::left: return keys[SDL_SCANCODE_A] || keys[SDL_SCANCODE_LEFT];
|
||||||
|
case ra3::render::ui_key::right: return keys[SDL_SCANCODE_D] || keys[SDL_SCANCODE_RIGHT];
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto shutdown() -> void override {
|
||||||
|
if (window_ != nullptr) SDL_StopTextInput(window_);
|
||||||
|
if (texture_ != nullptr) SDL_DestroyTexture(texture_);
|
||||||
|
if (renderer_ != nullptr) SDL_DestroyRenderer(renderer_);
|
||||||
|
if (window_ != nullptr) SDL_DestroyWindow(window_);
|
||||||
|
SDL_Quit();
|
||||||
|
texture_ = nullptr;
|
||||||
|
renderer_ = nullptr;
|
||||||
|
window_ = nullptr;
|
||||||
|
texture_w_ = 0;
|
||||||
|
texture_h_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto name() const -> std::string_view override { return "sdl"; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] static auto map_key(SDL_Keycode key) -> ra3::render::ui_key {
|
||||||
|
using ra3::render::ui_key;
|
||||||
|
switch (key) {
|
||||||
|
case SDLK_UP: return ui_key::up;
|
||||||
|
case SDLK_DOWN: return ui_key::down;
|
||||||
|
case SDLK_LEFT: return ui_key::left;
|
||||||
|
case SDLK_RIGHT: return ui_key::right;
|
||||||
|
case SDLK_PAGEUP: return ui_key::page_up;
|
||||||
|
case SDLK_PAGEDOWN: return ui_key::page_down;
|
||||||
|
case SDLK_RETURN:
|
||||||
|
case SDLK_KP_ENTER: return ui_key::confirm;
|
||||||
|
case SDLK_ESCAPE: return ui_key::cancel;
|
||||||
|
case SDLK_TAB: return ui_key::tab;
|
||||||
|
case SDLK_BACKSPACE: return ui_key::backspace;
|
||||||
|
default: return ui_key::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Window *window_ = nullptr;
|
||||||
|
SDL_Renderer *renderer_ = nullptr;
|
||||||
|
SDL_Texture *texture_ = nullptr;
|
||||||
|
ra3::core::uint32 texture_w_ = 0;
|
||||||
|
ra3::core::uint32 texture_h_ = 0;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export module ra3.vulkan;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
|
export import ra3.core;
|
||||||
|
import ra3.render;
|
||||||
|
import ra3.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fallback Vulkan backend used when the build has no Vulkan loader (for example
|
||||||
|
* the Windows cross-build without a Vulkan SDK). `init` fails so the caller can
|
||||||
|
* select another display.
|
||||||
|
*/
|
||||||
|
export namespace ra3::vulkan {
|
||||||
|
class vulkan_display final : public ra3::client::display {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] auto init(const ra3::client::display_options &) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto present(const ra3::render::image &, const ra3::render::view_rect &, bool) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto poll_event(ra3::render::ui_event &) -> bool override { return false; }
|
||||||
|
[[nodiscard]] auto window_size() const -> std::pair<int, int> override { return {0, 0}; }
|
||||||
|
[[nodiscard]] auto key_down(ra3::render::ui_key) const -> bool override { return false; }
|
||||||
|
auto shutdown() -> void override {}
|
||||||
|
[[nodiscard]] auto name() const -> std::string_view override { return "vulkan(null)"; }
|
||||||
|
};
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+91
-9
@@ -1,12 +1,4 @@
|
|||||||
#include <cstdint>
|
import std;
|
||||||
#include <cstdio>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
import ra3;
|
import ra3;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -89,6 +81,25 @@ auto main() -> int {
|
|||||||
|
|
||||||
check(game::to_string(game::faction::soviet) == "Soviet", "faction names round-trip");
|
check(game::to_string(game::faction::soviet) == "Soviet", "faction names round-trip");
|
||||||
|
|
||||||
|
// RA3 data: damage types, armour resolution and weapon target masks.
|
||||||
|
check(data::to_string(data::damage_type::auto_cannon) == "AUTO_CANNON", "damage type names round-trip");
|
||||||
|
check(data::damage_type_from_name("PRISM") == data::damage_type::prism, "damage type parses from asset name");
|
||||||
|
check(data::allied_scout_infantry_armor.adjust(data::damage_type::gun, 100.0F) == 1.0F, "scout armour takes 1% from GUN");
|
||||||
|
check(data::allied_scout_infantry_armor.adjust(data::damage_type::tesla, 100.0F) == 50.0F, "scout armour takes 50% from TESLA");
|
||||||
|
check(data::allied_anti_infantry_infantry_armor.adjust(data::damage_type::tesla, 100.0F) == 1000.0F, "peacekeeper armour takes 10x from TESLA");
|
||||||
|
check(data::allied_anti_infantry_infantry_armor.adjust(data::damage_type::unresistable, 100.0F) == 100.0F, "UNRESISTABLE bypasses armour");
|
||||||
|
check(data::maul_weapon.can_target(data::target_class::infantry), "the maul can target infantry");
|
||||||
|
check(!data::maul_weapon.can_target(data::target_class::vehicle), "the maul cannot target vehicles");
|
||||||
|
check(!data::maul_weapon.can_target(data::target_class::structure), "the maul cannot target structures");
|
||||||
|
check(!data::maul_weapon.can_target(data::target_class::aircraft), "the maul cannot target aircraft");
|
||||||
|
check(data::shotgun_weapon.can_target(data::target_class::infantry) && data::shotgun_weapon.can_target(data::target_class::structure),
|
||||||
|
"the shotgun hits infantry and structures");
|
||||||
|
check(!data::shotgun_weapon.can_target(data::target_class::aircraft), "the shotgun cannot hit aircraft");
|
||||||
|
check(data::cost_of(data::entity_kind::guardian_tank) == 950, "guardian tank costs 950");
|
||||||
|
check(data::max_health_of(data::entity_kind::power_plant) == 1000.0F, "power plant has 1000 health");
|
||||||
|
check(data::structure_of(data::entity_kind::power_plant).energy == 100, "power plant supplies 100 energy");
|
||||||
|
check(data::seconds_to_frames(1.0F) == 30U, "one second is 30 logic frames");
|
||||||
|
|
||||||
// RefPack: a literal-only stream decodes to its payload.
|
// RefPack: a literal-only stream decodes to its payload.
|
||||||
const std::vector<core::uint8> refpack{0x10, 0xFB, 0x00, 0x00, 0x03, 0xFF, 'A', 'B', 'C'};
|
const std::vector<core::uint8> refpack{0x10, 0xFB, 0x00, 0x00, 0x03, 0xFF, 'A', 'B', 'C'};
|
||||||
check(fs::is_refpack(refpack), "literal RefPack stream is recognised");
|
check(fs::is_refpack(refpack), "literal RefPack stream is recognised");
|
||||||
@@ -128,11 +139,36 @@ auto main() -> int {
|
|||||||
const auto composed = render::compose(decoded_tga, markers, scene);
|
const auto composed = render::compose(decoded_tga, markers, scene);
|
||||||
check(composed.width() == 2U && composed.height() == 2U, "compose preserves size");
|
check(composed.width() == 2U && composed.height() == 2U, "compose preserves size");
|
||||||
|
|
||||||
|
// Camera: wheel zoom, clamped pan and RA3-style edge scrolling.
|
||||||
|
render::view_camera camera;
|
||||||
|
camera.zoom = 2.0F;
|
||||||
|
camera.center_x = 0.5F;
|
||||||
|
camera.center_y = 0.5F;
|
||||||
|
camera.scroll(10.0F, 10.0F);
|
||||||
|
check(camera.center_x == 0.75F && camera.center_y == 0.75F, "camera clamps the centre to the map");
|
||||||
|
camera.zoom_by(100.0F);
|
||||||
|
check(camera.zoom == camera.max_zoom, "camera clamps the maximum zoom");
|
||||||
|
camera.zoom_by(0.001F);
|
||||||
|
check(camera.zoom == camera.min_zoom, "camera clamps the minimum zoom");
|
||||||
|
const auto fit_rect = camera.rect(512.0F, 512.0F, 1024.0F, 768.0F);
|
||||||
|
check(fit_rect.w == 768.0F && fit_rect.h == 768.0F && fit_rect.x == 128.0F && fit_rect.y == 0.0F, "camera letterboxes a square map into a wide window");
|
||||||
|
|
||||||
|
render::view_camera edge;
|
||||||
|
edge.zoom = 2.0F;
|
||||||
|
edge.center_x = 0.5F;
|
||||||
|
edge.center_y = 0.5F;
|
||||||
|
edge.edge_scroll(0.0F, 0.0F, 1024.0F, 768.0F, 1.0F);
|
||||||
|
check(edge.center_x < 0.5F && edge.center_y < 0.5F, "the cursor at the top-left edge scrolls up-left");
|
||||||
|
render::view_camera whole;
|
||||||
|
whole.edge_scroll(0.0F, 0.0F, 1024.0F, 768.0F, 1.0F);
|
||||||
|
check(whole.center_x == 0.5F && whole.center_y == 0.5F, "no edge scroll while the whole map fits");
|
||||||
|
|
||||||
// Skirmish: deterministic, resolves within the frame cap.
|
// Skirmish: deterministic, resolves within the frame cap.
|
||||||
skirmish::match_config config;
|
skirmish::match_config config;
|
||||||
config.seed = 42U;
|
config.seed = 42U;
|
||||||
const auto starts = skirmish::builtin_start_positions();
|
const auto starts = skirmish::builtin_start_positions();
|
||||||
auto first = skirmish::skirmish_match::create(config, starts);
|
auto first = skirmish::skirmish_match::create(config, starts);
|
||||||
|
check(first.unit_count(0U) == 1U && first.unit_count(1U) == 1U, "each side starts with a construction yard");
|
||||||
const auto first_result = first.run();
|
const auto first_result = first.run();
|
||||||
auto second = skirmish::skirmish_match::create(config, starts);
|
auto second = skirmish::skirmish_match::create(config, starts);
|
||||||
const auto second_result = second.run();
|
const auto second_result = second.run();
|
||||||
@@ -140,6 +176,52 @@ auto main() -> int {
|
|||||||
check(first_result.winner == 0 || first_result.winner == 1, "skirmish has a winner");
|
check(first_result.winner == 0 || first_result.winner == 1, "skirmish has a winner");
|
||||||
check(first_result.frames == second_result.frames && first_result.winner == second_result.winner, "skirmish is deterministic");
|
check(first_result.frames == second_result.frames && first_result.winner == second_result.winner, "skirmish is deterministic");
|
||||||
|
|
||||||
|
// Text/font: glyphs paint, spaces are blank and widths scale.
|
||||||
|
check(render::text_width("ABC", 2U) == 48U, "text width scales with glyph size");
|
||||||
|
render::image text_image(64U, 16U, render::black);
|
||||||
|
render::draw_text(text_image, 0, 0, "A", render::white, 2U);
|
||||||
|
int lit = 0;
|
||||||
|
for (std::size_t i = 0; i < 64U * 16U; ++i) {
|
||||||
|
if (text_image.data()[i] != render::black) ++lit;
|
||||||
|
}
|
||||||
|
check(lit > 0, "draw_text paints glyph pixels");
|
||||||
|
render::image space_image(64U, 16U, render::black);
|
||||||
|
render::draw_text(space_image, 0, 0, " ", render::white, 2U);
|
||||||
|
bool space_lit = false;
|
||||||
|
for (std::size_t i = 0; i < 64U * 16U; ++i) {
|
||||||
|
if (space_image.data()[i] != render::black) space_lit = true;
|
||||||
|
}
|
||||||
|
check(!space_lit, "a space paints nothing");
|
||||||
|
|
||||||
|
const auto box = render::fit_rect(1280.0F, 720.0F, 1920.0F, 1080.0F);
|
||||||
|
check(box.x == 0.0F && box.y == 0.0F && box.w == 1920.0F && box.h == 1080.0F, "fit_rect fills a same-aspect window");
|
||||||
|
|
||||||
|
// Map display names: decode a synthetic `gamestrings.csf` `MAP:` label.
|
||||||
|
std::vector<core::uint8> csf;
|
||||||
|
const auto put_le32 = [&csf](std::uint32_t v) {
|
||||||
|
for (int i = 0; i < 4; ++i) csf.push_back(static_cast<core::uint8>(v >> (8 * i)));
|
||||||
|
};
|
||||||
|
csf.insert(csf.end(), {' ', 'F', 'S', 'C'});
|
||||||
|
put_le32(3U);
|
||||||
|
put_le32(1U);
|
||||||
|
put_le32(1U);
|
||||||
|
for (int i = 0; i < 8; ++i) csf.push_back(0U);
|
||||||
|
csf.insert(csf.end(), {' ', 'L', 'B', 'L'});
|
||||||
|
put_le32(1U);
|
||||||
|
const std::string csf_label = "MAP:MAP_MP_2_FEASEL4";
|
||||||
|
put_le32(static_cast<std::uint32_t>(csf_label.size()));
|
||||||
|
csf.insert(csf.end(), csf_label.begin(), csf_label.end());
|
||||||
|
csf.insert(csf.end(), {' ', 'R', 'T', 'S'});
|
||||||
|
const std::string csf_value = "Battlebase Beta";
|
||||||
|
put_le32(static_cast<std::uint32_t>(csf_value.size()));
|
||||||
|
for (const auto ch: csf_value) {
|
||||||
|
csf.push_back(static_cast<core::uint8>(ch ^ 0xFF));
|
||||||
|
csf.push_back(0xFFU);
|
||||||
|
}
|
||||||
|
const auto names = map::parse_map_names(csf);
|
||||||
|
check(names.lookup("map_mp_2_feasel4") == "Battlebase Beta", "CSF map names decode (byte-XOR 0xFF)");
|
||||||
|
check(names.lookup("unknown_map") == "unknown_map", "an unknown id falls back to itself");
|
||||||
|
|
||||||
if (failures == 0) {
|
if (failures == 0) {
|
||||||
std::puts("ra3_tests: OK");
|
std::puts("ra3_tests: OK");
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 EnderTheCoder
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
Vendored
+170
@@ -0,0 +1,170 @@
|
|||||||
|
# libenderlog
|
||||||
|
|
||||||
|
A standalone C++26 logging library, built as a C++20/26 **module** (`import ender.log;`)
|
||||||
|
with **`std::stacktrace`** call-stack capture on severe records.
|
||||||
|
|
||||||
|
It was extracted from the `ender-physics` engine, where it started life as
|
||||||
|
`ender.log`, and turned into a library that has no other dependency — not on the
|
||||||
|
engine, not on a logging framework.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Leveled records.** `trace`, `debug`, `info`, `warn`, `error`, `critical`,
|
||||||
|
filtered by an atomic `minimum` check that is cheap enough to guard expensive
|
||||||
|
message construction: `if (log::enabled(log::level::debug)) { ... }`.
|
||||||
|
- **Source location.** Every record carries the file, line and function of the
|
||||||
|
caller, taken from `std::source_location` at the call site — exact even in a
|
||||||
|
stripped release binary, because it is a compile-time constant.
|
||||||
|
- **Call stacks.** Records at or above `options::stacktrace_from` carry a
|
||||||
|
formatted `std::stacktrace`. The frames belonging to the library itself are
|
||||||
|
stripped by symbol, so the first reported frame is the caller regardless of
|
||||||
|
the optimisation level (the level wrappers get inlined away under `-O`).
|
||||||
|
- **Pluggable sinks.** A `console_sink` (stderr by default) and a `memory_sink`
|
||||||
|
(for tests and in-game consoles) ship; `sink` is a small interface.
|
||||||
|
- **File output with archiving.** `file_sink` writes to a file and, on open,
|
||||||
|
moves an existing log aside to a timestamped archive, so a run never appends
|
||||||
|
onto a previous run's log. It can also rotate by size and bound how many
|
||||||
|
archives are kept.
|
||||||
|
- **No stacktrace? No problem.** Where `<stacktrace>` is missing (libc++, and
|
||||||
|
therefore every cross target), the module still compiles and records still
|
||||||
|
carry their call site — they simply have no stack.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
C++26 modules and `import std;` need a recent toolchain:
|
||||||
|
|
||||||
|
| Requirement | Version |
|
||||||
|
|---|---|
|
||||||
|
| Compiler | **GCC 15+** (or Clang with a standard library that provides the `std` module) |
|
||||||
|
| CMake | **3.30+** (for `CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`) |
|
||||||
|
| Standard library | libstdc++ for `std::stacktrace` |
|
||||||
|
|
||||||
|
Ubuntu 26.04's default `g++` (GCC 15) and CMake 4 satisfy this, and that is the
|
||||||
|
release the CI targets and the `.deb` is built for. Ubuntu 24.04 ships GCC 13 and
|
||||||
|
CMake 3.28 and cannot build `import std;` without extra toolchains, so it is not
|
||||||
|
supported.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||||
|
cmake --build build -j
|
||||||
|
ctest --test-dir build --output-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
| Option | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `ENDERLOG_WERROR` | `OFF` | Treat warnings as errors |
|
||||||
|
| `ENDERLOG_BACKTRACE_SYMBOLS` | `ON` | Link executables with `-rdynamic` so traces can name frames |
|
||||||
|
| `ENDERLOG_BUILD_TESTS` | `ON` | Build the test suite |
|
||||||
|
| `ENDERLOG_BUILD_EXAMPLES` | `ON` | Build the example program |
|
||||||
|
|
||||||
|
Build Debug or RelWithDebInfo when you need to read a trace: frame names come
|
||||||
|
from debug information, and a release build often reports application frames as
|
||||||
|
`<unknown>`.
|
||||||
|
|
||||||
|
## Using the library
|
||||||
|
|
||||||
|
### `add_subdirectory`
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
add_subdirectory(libenderlog)
|
||||||
|
target_link_libraries(my_app PRIVATE enderlog::enderlog)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installed package
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
find_package(enderlog REQUIRED)
|
||||||
|
target_link_libraries(my_app PRIVATE enderlog::enderlog)
|
||||||
|
```
|
||||||
|
|
||||||
|
The static archive is installed together with the module interface source
|
||||||
|
(`ender.log.cppm`) because a module's BMI is compiler-version-specific — the
|
||||||
|
consumer rebuilds it from the source.
|
||||||
|
|
||||||
|
### In code
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
import std;
|
||||||
|
import ender.log;
|
||||||
|
|
||||||
|
namespace log = ender::log;
|
||||||
|
|
||||||
|
auto main() -> int {
|
||||||
|
log::configure({.minimum = log::level::debug, .stacktrace_from = log::level::warn});
|
||||||
|
|
||||||
|
log::info(std::format("body {} moved to {:.2f}", 7, 12.35)); // formatted by the caller
|
||||||
|
log::error("a body left the world"); // carries a stack trace
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
|
||||||
|
```
|
||||||
|
[11:32:18] ERROR example: a body left the world (examples/main.cpp:8)
|
||||||
|
#0 simulate_one_step (examples/main.cpp:8)
|
||||||
|
#1 main (examples/main.cpp:20)
|
||||||
|
#2 <unknown>
|
||||||
|
#3 __libc_start_main
|
||||||
|
#4 _start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
log::configure({
|
||||||
|
.minimum = log::level::debug, // drop everything below this
|
||||||
|
.stacktrace_from = log::level::error, // capture a stack at/above this
|
||||||
|
.stacktrace_depth = 16, // max frames kept
|
||||||
|
.stacktrace_skip = 2, // frames dropped before the caller is found
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
`log::current_options()` reads it back, `log::add_sink(...)` adds a destination,
|
||||||
|
and `log::set_sinks({...})` replaces them.
|
||||||
|
|
||||||
|
### Writing to a file
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace log = ender::log;
|
||||||
|
|
||||||
|
// Archive any existing enderlog.log to enderlog.log.<timestamp>, then start a
|
||||||
|
// fresh file for this run. Rotate at 64 KiB and keep the last 5 archives.
|
||||||
|
auto sink = log::add_file_sink("enderlog.log", {.max_file_size = 64 * 1024, .max_archives = 5});
|
||||||
|
```
|
||||||
|
|
||||||
|
- **No appending onto a previous run.** On open, an existing non-empty
|
||||||
|
`enderlog.log` is renamed to `enderlog.log.<YYYYmmdd-HHMMSS>` before the new
|
||||||
|
file is created, so every run gets its own file and the previous run's log is
|
||||||
|
preserved. A leftover empty file is simply replaced.
|
||||||
|
- `file_options::max_file_size` (0 disables) rotates the active file mid-run the
|
||||||
|
same way, and never archives an empty file. `file_options::max_archives`
|
||||||
|
(0 keeps all) deletes the oldest archives beyond the limit.
|
||||||
|
- `file_options::flush_each_record` (on by default) flushes after every record so
|
||||||
|
a crash keeps the tail.
|
||||||
|
- `add_file_sink` adds the sink to the global logger and returns it; `path()` and
|
||||||
|
`archives()` expose what it wrote. The `file_sink` class can also be used
|
||||||
|
directly and installed with `set_sinks`.
|
||||||
|
|
||||||
|
## Packaging
|
||||||
|
|
||||||
|
`cpack` produces a Debian package:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -S . -B build -DENDERLOG_DISTRO=ubuntu26.04
|
||||||
|
cmake --build build -j
|
||||||
|
cd build
|
||||||
|
cpack
|
||||||
|
# -> libenderlog-dev_0.0.1_amd64_ubuntu26.04.deb
|
||||||
|
```
|
||||||
|
|
||||||
|
The package installs the static archive, the module interface source and the
|
||||||
|
CMake package config. CI builds it for Ubuntu 26.04 and publishes it as a job
|
||||||
|
artifact.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT — see [LICENSE](LICENSE).
|
||||||
+542
@@ -0,0 +1,542 @@
|
|||||||
|
/**
|
||||||
|
* Logging with call-stack capture.
|
||||||
|
*
|
||||||
|
* Records carry a level, message, source location and — for severe enough
|
||||||
|
* levels — a formatted `std::stacktrace`. Capturing a trace walks the stack and
|
||||||
|
* reads debug information, so it is only done when the record's level is at or
|
||||||
|
* above `options::stacktrace_from`, and the whole call is skipped when the
|
||||||
|
* level is disabled.
|
||||||
|
*
|
||||||
|
* `std::stacktrace` is implemented by libstdc++ only. With GCC the module has
|
||||||
|
* to link `stdc++exp` (the static library that implements it); the CMake target
|
||||||
|
* takes care of that. File and line numbers in the trace come from debug
|
||||||
|
* information, so build with `-g` (Debug or RelWithDebInfo) to see them; symbol
|
||||||
|
* names work in any build.
|
||||||
|
*/
|
||||||
|
module;
|
||||||
|
|
||||||
|
// libc++ (the OpenRA3 toolchain) has no <stacktrace>, so this vendored copy adds
|
||||||
|
// a native fallback for the frames: Windows CaptureStackBackTrace and POSIX
|
||||||
|
// execinfo. These live in the global module fragment because they are C headers.
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define NOMINMAX
|
||||||
|
#include <windows.h>
|
||||||
|
#elif defined(__unix__) || defined(__APPLE__)
|
||||||
|
#include <execinfo.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
export module ender.log;
|
||||||
|
|
||||||
|
import std;
|
||||||
|
|
||||||
|
export namespace ender::log {
|
||||||
|
/** Severity of a record, ordered from most to least verbose. */
|
||||||
|
enum class level: std::uint8_t {
|
||||||
|
trace = 0,
|
||||||
|
debug,
|
||||||
|
info,
|
||||||
|
warn,
|
||||||
|
error,
|
||||||
|
critical,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Short upper-case name of a level, for output. */
|
||||||
|
[[nodiscard]] inline auto to_string(const level severity) -> std::string_view {
|
||||||
|
switch (severity) {
|
||||||
|
case level::trace: return "TRACE";
|
||||||
|
case level::debug: return "DEBUG";
|
||||||
|
case level::info: return "INFO";
|
||||||
|
case level::warn: return "WARN";
|
||||||
|
case level::error: return "ERROR";
|
||||||
|
case level::critical: return "CRITICAL";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Logger configuration. */
|
||||||
|
struct options {
|
||||||
|
/** Records below this level are dropped before anything is built. */
|
||||||
|
level minimum{level::info};
|
||||||
|
/** Capture a stack trace for records at this level and above. */
|
||||||
|
level stacktrace_from{level::error};
|
||||||
|
/** Maximum number of frames kept in a captured trace. */
|
||||||
|
std::size_t stacktrace_depth{16};
|
||||||
|
/**
|
||||||
|
* Frames to drop from the top of a captured trace.
|
||||||
|
*
|
||||||
|
* The default drops `capture_stacktrace` and `emit`, which always exist
|
||||||
|
* as frames. The level wrappers are inlined away in optimised builds, so
|
||||||
|
* a fixed count cannot cover them; any leading frame that belongs to
|
||||||
|
* this module is therefore stripped by name instead, which keeps the
|
||||||
|
* caller visible whether or not the wrappers were inlined.
|
||||||
|
*/
|
||||||
|
std::size_t stacktrace_skip{2};
|
||||||
|
};
|
||||||
|
|
||||||
|
/** One log record. */
|
||||||
|
struct record {
|
||||||
|
level severity{level::info};
|
||||||
|
std::string message{};
|
||||||
|
std::string file{};
|
||||||
|
std::uint32_t line{0};
|
||||||
|
std::string function{};
|
||||||
|
/** Formatted call stack; empty when it was not captured. */
|
||||||
|
std::string stacktrace{};
|
||||||
|
std::chrono::system_clock::time_point time{};
|
||||||
|
std::thread::id thread{};
|
||||||
|
|
||||||
|
[[nodiscard]] auto has_stacktrace() const -> bool { return !stacktrace.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
/**
|
||||||
|
* Render one record as a human-readable block: a header line and, when
|
||||||
|
* present, the indented stack frames. Shared by the stream sinks.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto format_record(const record &entry) -> std::string {
|
||||||
|
auto text = std::format("[{:%H:%M:%S}] {:<8} {}",
|
||||||
|
std::chrono::floor<std::chrono::seconds>(entry.time),
|
||||||
|
to_string(entry.severity),
|
||||||
|
entry.message);
|
||||||
|
if (!entry.file.empty()) {
|
||||||
|
text += std::format(" ({}:{})", entry.file, entry.line);
|
||||||
|
}
|
||||||
|
text += '\n';
|
||||||
|
if (entry.has_stacktrace()) {
|
||||||
|
text += entry.stacktrace;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Where records go. */
|
||||||
|
class sink {
|
||||||
|
public:
|
||||||
|
virtual ~sink() = default;
|
||||||
|
|
||||||
|
/** Receive one record; called with the logger's mutex held. */
|
||||||
|
virtual auto write(const record &entry) -> void = 0;
|
||||||
|
|
||||||
|
/** Flush any buffering. */
|
||||||
|
virtual auto flush() -> void {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Writes a human-readable line per record to a stream (stderr by default). */
|
||||||
|
class console_sink final: public sink {
|
||||||
|
public:
|
||||||
|
explicit console_sink(std::ostream &stream = std::cerr): stream_(&stream) {}
|
||||||
|
|
||||||
|
auto write(const record &entry) -> void override {
|
||||||
|
*stream_ << detail::format_record(entry);
|
||||||
|
stream_->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostream *stream_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Keeps every record in memory; useful for tests and in-game consoles. */
|
||||||
|
class memory_sink final: public sink {
|
||||||
|
public:
|
||||||
|
auto write(const record &entry) -> void override {
|
||||||
|
const auto lock = std::scoped_lock{mutex_};
|
||||||
|
records_.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto records() const -> std::vector<record> {
|
||||||
|
const auto lock = std::scoped_lock{mutex_};
|
||||||
|
return records_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto size() const -> std::size_t {
|
||||||
|
const auto lock = std::scoped_lock{mutex_};
|
||||||
|
return records_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto clear() -> void {
|
||||||
|
const auto lock = std::scoped_lock{mutex_};
|
||||||
|
records_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
std::vector<record> records_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Configuration for `file_sink`. */
|
||||||
|
struct file_options {
|
||||||
|
/** Move an existing log file aside to an archive when the sink opens it. */
|
||||||
|
bool archive_on_open{true};
|
||||||
|
/** Flush after every record, so the tail survives a crash. */
|
||||||
|
bool flush_each_record{true};
|
||||||
|
/** Rotate once the active file would grow past this many bytes; 0 disables. */
|
||||||
|
std::size_t max_file_size{0};
|
||||||
|
/** Keep at most this many archives, dropping the oldest first; 0 keeps them all. */
|
||||||
|
std::size_t max_archives{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes records to a file, archiving the previous one on open.
|
||||||
|
*
|
||||||
|
* `path` is the active file. When the sink opens it and the file already
|
||||||
|
* holds data, that file is renamed to a timestamped archive first, so a run
|
||||||
|
* never appends onto a previous run's log: every start begins a fresh file
|
||||||
|
* and the old one is preserved as `<path>.<YYYYmmdd-HHMMSS>`. The same
|
||||||
|
* happens mid-run once the active file passes `file_options::max_file_size`.
|
||||||
|
* `file_options::max_archives` bounds how many archives are kept.
|
||||||
|
*
|
||||||
|
* As with every sink, `write` is called with the logger's mutex held, so one
|
||||||
|
* sink is safe to share; it is not safe for two processes to point at the
|
||||||
|
* same file.
|
||||||
|
*/
|
||||||
|
class file_sink final: public sink {
|
||||||
|
public:
|
||||||
|
explicit file_sink(std::filesystem::path path, const file_options options = {})
|
||||||
|
: path_(std::move(path)), options_(options) {
|
||||||
|
if (options_.archive_on_open && std::filesystem::exists(path_)) {
|
||||||
|
if (std::filesystem::file_size(path_) > 0) {
|
||||||
|
archive_current();
|
||||||
|
} else {
|
||||||
|
std::filesystem::remove(path_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto write(const record &entry) -> void override {
|
||||||
|
const auto block = detail::format_record(entry);
|
||||||
|
// Rotate before writing, but never rotate an empty file: that would
|
||||||
|
// archive nothing and lose the record that is about to be written.
|
||||||
|
if (options_.max_file_size > 0 && size_ > 0 && size_ + block.size() > options_.max_file_size) {
|
||||||
|
archive_current();
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
stream_ << block;
|
||||||
|
size_ += block.size();
|
||||||
|
if (options_.flush_each_record) stream_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto flush() -> void override {
|
||||||
|
if (stream_.is_open()) stream_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The active log file. */
|
||||||
|
[[nodiscard]] auto path() const -> const std::filesystem::path & { return path_; }
|
||||||
|
|
||||||
|
/** Archives this sink created, oldest first. */
|
||||||
|
[[nodiscard]] auto archives() const -> const std::vector<std::filesystem::path> & { return archives_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto open() -> void {
|
||||||
|
stream_.clear();
|
||||||
|
stream_.open(path_, std::ios::out | std::ios::trunc | std::ios::binary);
|
||||||
|
size_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto archive_current() -> void {
|
||||||
|
if (stream_.is_open()) stream_.close();
|
||||||
|
const auto stamp = std::format("{:%Y%m%d-%H%M%S}",
|
||||||
|
std::chrono::floor<std::chrono::seconds>(std::chrono::system_clock::now()));
|
||||||
|
auto archive = path_;
|
||||||
|
archive += "." + stamp;
|
||||||
|
// Two rotations can land in the same second; disambiguate with a
|
||||||
|
// counter rather than overwrite the earlier archive.
|
||||||
|
for (auto counter = 1; std::filesystem::exists(archive); ++counter) {
|
||||||
|
archive = path_;
|
||||||
|
archive += std::format(".{}.{}", stamp, counter);
|
||||||
|
}
|
||||||
|
std::filesystem::rename(path_, archive);
|
||||||
|
archives_.push_back(archive);
|
||||||
|
prune_archives();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto prune_archives() -> void {
|
||||||
|
if (options_.max_archives == 0) return;
|
||||||
|
while (archives_.size() > options_.max_archives) {
|
||||||
|
auto ignored = std::error_code{};
|
||||||
|
std::filesystem::remove(archives_.front(), ignored);
|
||||||
|
archives_.erase(archives_.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path path_;
|
||||||
|
file_options options_;
|
||||||
|
std::ofstream stream_;
|
||||||
|
std::size_t size_{0};
|
||||||
|
std::vector<std::filesystem::path> archives_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <stacktrace> is not portable: libc++ has never implemented it, and only
|
||||||
|
* libstdc++ provides it here. Where it is missing, records still carry their
|
||||||
|
* call site through std::source_location - they simply carry no stack, and
|
||||||
|
* everything below degrades to an empty string rather than the module
|
||||||
|
* refusing to compile.
|
||||||
|
*
|
||||||
|
* CMake decides this and passes it in, rather than the module testing
|
||||||
|
* `__cpp_lib_stacktrace` itself: feature-test macros come from the standard
|
||||||
|
* library's headers, and `import std;` does not export them, so probing for
|
||||||
|
* one here silently reports "absent" even on libstdc++, which has it.
|
||||||
|
*/
|
||||||
|
#ifndef ENDERLOG_HAS_STACKTRACE
|
||||||
|
#define ENDERLOG_HAS_STACKTRACE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ENDERLOG_HAS_STACKTRACE
|
||||||
|
/** True when a frame belongs to the logging module itself. */
|
||||||
|
[[nodiscard]] inline auto is_logger_frame(const std::stacktrace_entry &entry) -> bool {
|
||||||
|
if (entry.description().find("ender::log") != std::string::npos) return true;
|
||||||
|
return entry.source_file().find("ender.log.cppm") != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a trace as one indented line per frame.
|
||||||
|
*
|
||||||
|
* @param skip_logger_frames Drop leading frames belonging to this module, so
|
||||||
|
* the first reported frame is the caller. This is what makes the
|
||||||
|
* output stable across optimisation levels: in a release build the
|
||||||
|
* level wrappers are inlined into the caller, so counting frames
|
||||||
|
* alone would either over- or under-skip.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline auto format_stacktrace(const std::stacktrace &trace,
|
||||||
|
const bool skip_logger_frames = true) -> std::string {
|
||||||
|
if (trace.empty()) return " <empty stacktrace>\n";
|
||||||
|
|
||||||
|
auto first = std::size_t{0};
|
||||||
|
if (skip_logger_frames) {
|
||||||
|
while (first < trace.size() && is_logger_frame(trace.at(first))) ++first;
|
||||||
|
if (first >= trace.size()) first = 0; // never hide the whole trace
|
||||||
|
}
|
||||||
|
|
||||||
|
auto text = std::string{};
|
||||||
|
for (auto index = first; index < trace.size(); ++index) {
|
||||||
|
const auto &entry = trace.at(index);
|
||||||
|
auto description = entry.description();
|
||||||
|
if (description.empty()) description = "<unknown>";
|
||||||
|
|
||||||
|
auto location = std::string{};
|
||||||
|
if (!entry.source_file().empty()) {
|
||||||
|
location = std::format(" ({}:{})", entry.source_file(), entry.source_line());
|
||||||
|
}
|
||||||
|
text += std::format(" #{:<3}{}{}\n", index - first, description, location);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Capture and render the current call stack, innermost frame first. */
|
||||||
|
[[nodiscard]] inline auto capture_stacktrace(const std::size_t skip = 2, const std::size_t depth = 16)
|
||||||
|
-> std::string {
|
||||||
|
return format_stacktrace(std::stacktrace::current(skip, depth));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/**
|
||||||
|
* libc++ fallback: capture the current call stack with the platform's own
|
||||||
|
* backtrace API and render one indented line per frame.
|
||||||
|
*
|
||||||
|
* On Windows a frame is reported as `module.dll+0xRVA` (a MinGW release build
|
||||||
|
* has DWARF, not the PDB symbols dbghelp resolves, so a module+offset is the
|
||||||
|
* practical answer). On POSIX `backtrace_symbols` is used, which names a
|
||||||
|
* frame when the executable was linked with `-rdynamic`.
|
||||||
|
*/
|
||||||
|
#if defined(_WIN32)
|
||||||
|
[[nodiscard]] inline auto symbolicate_frame(void *address) -> std::string {
|
||||||
|
const auto value = reinterpret_cast<std::uintptr_t>(address);
|
||||||
|
HMODULE module = nullptr;
|
||||||
|
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||||
|
reinterpret_cast<LPCWSTR>(address), &module)) {
|
||||||
|
wchar_t wide[260] = L"?";
|
||||||
|
GetModuleFileNameW(module, wide, 260U);
|
||||||
|
char narrow[260] = "?";
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, wide, -1, narrow, sizeof(narrow), nullptr, nullptr);
|
||||||
|
const char *base = std::strrchr(narrow, '\\');
|
||||||
|
return std::format("{}+0x{:X}", base != nullptr ? base + 1 : narrow, value - reinterpret_cast<std::uintptr_t>(module));
|
||||||
|
}
|
||||||
|
return std::format("0x{:X}", value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[[nodiscard]] inline auto capture_stacktrace(const std::size_t skip = 2, const std::size_t depth = 16) -> std::string {
|
||||||
|
constexpr std::size_t max_frames = 64;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
void *frames[max_frames] = {};
|
||||||
|
const auto want = static_cast<DWORD>(std::min(max_frames, skip + std::max<std::size_t>(depth, 1U)));
|
||||||
|
const USHORT count = CaptureStackBackTrace(static_cast<DWORD>(skip), want, frames, nullptr);
|
||||||
|
auto text = std::string{};
|
||||||
|
for (USHORT index = 0; index < count; ++index) {
|
||||||
|
text += std::format(" #{:<3}{}\n", index, symbolicate_frame(frames[index]));
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
#elif defined(__unix__) || defined(__APPLE__)
|
||||||
|
void *frames[max_frames] = {};
|
||||||
|
const int count = ::backtrace(frames, static_cast<int>(std::min(max_frames, skip + std::max<std::size_t>(depth, 1U))));
|
||||||
|
char **symbols = ::backtrace_symbols(frames, count);
|
||||||
|
auto text = std::string{};
|
||||||
|
for (int index = static_cast<int>(std::min<std::size_t>(skip, static_cast<std::size_t>(count))); index < count; ++index) {
|
||||||
|
text += std::format(" #{:<3}{}\n", index - static_cast<int>(skip),
|
||||||
|
symbols != nullptr ? symbols[index] : std::format("0x{:X}", reinterpret_cast<std::uintptr_t>(frames[index])));
|
||||||
|
}
|
||||||
|
if (symbols != nullptr) std::free(symbols);
|
||||||
|
return text;
|
||||||
|
#else
|
||||||
|
(void) skip;
|
||||||
|
(void) depth;
|
||||||
|
return {};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The process-wide logger.
|
||||||
|
*
|
||||||
|
* `enabled` is an atomic read so hot paths can guard expensive message
|
||||||
|
* construction; everything else takes the mutex.
|
||||||
|
*/
|
||||||
|
class logger {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] static auto instance() -> logger & {
|
||||||
|
static logger shared;
|
||||||
|
return shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto configure(const options &config) -> void {
|
||||||
|
const auto lock = std::scoped_lock{mutex_};
|
||||||
|
options_ = config;
|
||||||
|
minimum_.store(static_cast<std::uint8_t>(config.minimum), std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto configuration() const -> options {
|
||||||
|
const auto lock = std::scoped_lock{mutex_};
|
||||||
|
return options_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto enabled(const level severity) const -> bool {
|
||||||
|
return static_cast<std::uint8_t>(severity) >= minimum_.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add_sink(std::shared_ptr<sink> destination) -> void {
|
||||||
|
const auto lock = std::scoped_lock{mutex_};
|
||||||
|
sinks_.push_back(std::move(destination));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto set_sinks(std::vector<std::shared_ptr<sink>> destinations) -> void {
|
||||||
|
const auto lock = std::scoped_lock{mutex_};
|
||||||
|
sinks_ = std::move(destinations);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dispatch(const record &entry) -> void {
|
||||||
|
const auto lock = std::scoped_lock{mutex_};
|
||||||
|
for (const auto &destination: sinks_) {
|
||||||
|
destination->write(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
logger() { sinks_.push_back(std::make_shared<console_sink>()); }
|
||||||
|
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
options options_{};
|
||||||
|
std::atomic<std::uint8_t> minimum_{static_cast<std::uint8_t>(options{}.minimum)};
|
||||||
|
std::vector<std::shared_ptr<sink>> sinks_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Apply a configuration to the process-wide logger. */
|
||||||
|
inline auto configure(const options &config) -> void { logger::instance().configure(config); }
|
||||||
|
|
||||||
|
/** Current configuration of the process-wide logger. */
|
||||||
|
[[nodiscard]] inline auto current_options() -> options { return logger::instance().configuration(); }
|
||||||
|
|
||||||
|
/** Route records to an additional sink. */
|
||||||
|
inline auto add_sink(std::shared_ptr<sink> destination) -> void {
|
||||||
|
logger::instance().add_sink(std::move(destination));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a file sink, route records to it, and hand it back.
|
||||||
|
*
|
||||||
|
* The previous log at `path` is archived on open, so this never appends onto
|
||||||
|
* an earlier run.
|
||||||
|
*
|
||||||
|
* @return The sink, so the caller can inspect the archives it creates.
|
||||||
|
*/
|
||||||
|
inline auto add_file_sink(std::filesystem::path path, const file_options &options = {})
|
||||||
|
-> std::shared_ptr<file_sink> {
|
||||||
|
auto destination = std::make_shared<file_sink>(std::move(path), options);
|
||||||
|
logger::instance().add_sink(destination);
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replace every sink. */
|
||||||
|
inline auto set_sinks(std::vector<std::shared_ptr<sink>> destinations) -> void {
|
||||||
|
logger::instance().set_sinks(std::move(destinations));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True when a record at this level would be emitted. */
|
||||||
|
[[nodiscard]] inline auto enabled(const level severity) -> bool { return logger::instance().enabled(severity); }
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
/** Build and dispatch one record. Not for direct use. */
|
||||||
|
inline auto emit(const level severity, std::string message, const std::source_location location) -> void {
|
||||||
|
auto &target = logger::instance();
|
||||||
|
if (!target.enabled(severity)) return;
|
||||||
|
|
||||||
|
const auto config = target.configuration();
|
||||||
|
auto entry = record{
|
||||||
|
.severity = severity,
|
||||||
|
.message = std::move(message),
|
||||||
|
.file = location.file_name(),
|
||||||
|
.line = static_cast<std::uint32_t>(location.line()),
|
||||||
|
.function = location.function_name(),
|
||||||
|
.time = std::chrono::system_clock::now(),
|
||||||
|
.thread = std::this_thread::get_id(),
|
||||||
|
};
|
||||||
|
if (severity >= config.stacktrace_from) {
|
||||||
|
entry.stacktrace = capture_stacktrace(config.stacktrace_skip, config.stacktrace_depth);
|
||||||
|
}
|
||||||
|
target.dispatch(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a record.
|
||||||
|
*
|
||||||
|
* The source location defaults to the call site, so this reports exactly
|
||||||
|
* where it was called from.
|
||||||
|
*/
|
||||||
|
inline auto log(const level severity,
|
||||||
|
std::string message,
|
||||||
|
const std::source_location location = std::source_location::current()) -> void {
|
||||||
|
detail::emit(severity, std::move(message), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto trace(std::string message, const std::source_location location = std::source_location::current())
|
||||||
|
-> void {
|
||||||
|
detail::emit(level::trace, std::move(message), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto debug(std::string message, const std::source_location location = std::source_location::current())
|
||||||
|
-> void {
|
||||||
|
detail::emit(level::debug, std::move(message), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto info(std::string message, const std::source_location location = std::source_location::current())
|
||||||
|
-> void {
|
||||||
|
detail::emit(level::info, std::move(message), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto warn(std::string message, const std::source_location location = std::source_location::current())
|
||||||
|
-> void {
|
||||||
|
detail::emit(level::warn, std::move(message), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Emits at `error`, which captures a call stack by default. */
|
||||||
|
inline auto error(std::string message, const std::source_location location = std::source_location::current())
|
||||||
|
-> void {
|
||||||
|
detail::emit(level::error, std::move(message), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto critical(std::string message, const std::source_location location = std::source_location::current())
|
||||||
|
-> void {
|
||||||
|
detail::emit(level::critical, std::move(message), location);
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2018-2024 Arseny Kapoulkine
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
Vendored
+3365
File diff suppressed because it is too large
Load Diff
Vendored
+2176
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,394 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODEC_AV1STD_H_
|
||||||
|
#define VULKAN_VIDEO_CODEC_AV1STD_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codec_av1std is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codec_av1std 1
|
||||||
|
#include "vulkan_video_codecs_common.h"
|
||||||
|
#define STD_VIDEO_AV1_NUM_REF_FRAMES 8
|
||||||
|
#define STD_VIDEO_AV1_REFS_PER_FRAME 7
|
||||||
|
#define STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME 8
|
||||||
|
#define STD_VIDEO_AV1_MAX_TILE_COLS 64
|
||||||
|
#define STD_VIDEO_AV1_MAX_TILE_ROWS 64
|
||||||
|
#define STD_VIDEO_AV1_MAX_SEGMENTS 8
|
||||||
|
#define STD_VIDEO_AV1_SEG_LVL_MAX 8
|
||||||
|
#define STD_VIDEO_AV1_PRIMARY_REF_NONE 7
|
||||||
|
#define STD_VIDEO_AV1_SELECT_INTEGER_MV 2
|
||||||
|
#define STD_VIDEO_AV1_SELECT_SCREEN_CONTENT_TOOLS 2
|
||||||
|
#define STD_VIDEO_AV1_SKIP_MODE_FRAMES 2
|
||||||
|
#define STD_VIDEO_AV1_MAX_LOOP_FILTER_STRENGTHS 4
|
||||||
|
#define STD_VIDEO_AV1_LOOP_FILTER_ADJUSTMENTS 2
|
||||||
|
#define STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS 8
|
||||||
|
#define STD_VIDEO_AV1_MAX_NUM_PLANES 3
|
||||||
|
#define STD_VIDEO_AV1_GLOBAL_MOTION_PARAMS 6
|
||||||
|
#define STD_VIDEO_AV1_MAX_NUM_Y_POINTS 14
|
||||||
|
#define STD_VIDEO_AV1_MAX_NUM_CB_POINTS 10
|
||||||
|
#define STD_VIDEO_AV1_MAX_NUM_CR_POINTS 10
|
||||||
|
#define STD_VIDEO_AV1_MAX_NUM_POS_LUMA 24
|
||||||
|
#define STD_VIDEO_AV1_MAX_NUM_POS_CHROMA 25
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1Profile {
|
||||||
|
STD_VIDEO_AV1_PROFILE_MAIN = 0,
|
||||||
|
STD_VIDEO_AV1_PROFILE_HIGH = 1,
|
||||||
|
STD_VIDEO_AV1_PROFILE_PROFESSIONAL = 2,
|
||||||
|
STD_VIDEO_AV1_PROFILE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_PROFILE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1Profile;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1Level {
|
||||||
|
STD_VIDEO_AV1_LEVEL_2_0 = 0,
|
||||||
|
STD_VIDEO_AV1_LEVEL_2_1 = 1,
|
||||||
|
STD_VIDEO_AV1_LEVEL_2_2 = 2,
|
||||||
|
STD_VIDEO_AV1_LEVEL_2_3 = 3,
|
||||||
|
STD_VIDEO_AV1_LEVEL_3_0 = 4,
|
||||||
|
STD_VIDEO_AV1_LEVEL_3_1 = 5,
|
||||||
|
STD_VIDEO_AV1_LEVEL_3_2 = 6,
|
||||||
|
STD_VIDEO_AV1_LEVEL_3_3 = 7,
|
||||||
|
STD_VIDEO_AV1_LEVEL_4_0 = 8,
|
||||||
|
STD_VIDEO_AV1_LEVEL_4_1 = 9,
|
||||||
|
STD_VIDEO_AV1_LEVEL_4_2 = 10,
|
||||||
|
STD_VIDEO_AV1_LEVEL_4_3 = 11,
|
||||||
|
STD_VIDEO_AV1_LEVEL_5_0 = 12,
|
||||||
|
STD_VIDEO_AV1_LEVEL_5_1 = 13,
|
||||||
|
STD_VIDEO_AV1_LEVEL_5_2 = 14,
|
||||||
|
STD_VIDEO_AV1_LEVEL_5_3 = 15,
|
||||||
|
STD_VIDEO_AV1_LEVEL_6_0 = 16,
|
||||||
|
STD_VIDEO_AV1_LEVEL_6_1 = 17,
|
||||||
|
STD_VIDEO_AV1_LEVEL_6_2 = 18,
|
||||||
|
STD_VIDEO_AV1_LEVEL_6_3 = 19,
|
||||||
|
STD_VIDEO_AV1_LEVEL_7_0 = 20,
|
||||||
|
STD_VIDEO_AV1_LEVEL_7_1 = 21,
|
||||||
|
STD_VIDEO_AV1_LEVEL_7_2 = 22,
|
||||||
|
STD_VIDEO_AV1_LEVEL_7_3 = 23,
|
||||||
|
STD_VIDEO_AV1_LEVEL_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_LEVEL_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1Level;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1FrameType {
|
||||||
|
STD_VIDEO_AV1_FRAME_TYPE_KEY = 0,
|
||||||
|
STD_VIDEO_AV1_FRAME_TYPE_INTER = 1,
|
||||||
|
STD_VIDEO_AV1_FRAME_TYPE_INTRA_ONLY = 2,
|
||||||
|
STD_VIDEO_AV1_FRAME_TYPE_SWITCH = 3,
|
||||||
|
STD_VIDEO_AV1_FRAME_TYPE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_FRAME_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1FrameType;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1ReferenceName {
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_INTRA_FRAME = 0,
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME = 1,
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_LAST2_FRAME = 2,
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_LAST3_FRAME = 3,
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_GOLDEN_FRAME = 4,
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_BWDREF_FRAME = 5,
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_ALTREF2_FRAME = 6,
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_ALTREF_FRAME = 7,
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_REFERENCE_NAME_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1ReferenceName;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1InterpolationFilter {
|
||||||
|
STD_VIDEO_AV1_INTERPOLATION_FILTER_EIGHTTAP = 0,
|
||||||
|
STD_VIDEO_AV1_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH = 1,
|
||||||
|
STD_VIDEO_AV1_INTERPOLATION_FILTER_EIGHTTAP_SHARP = 2,
|
||||||
|
STD_VIDEO_AV1_INTERPOLATION_FILTER_BILINEAR = 3,
|
||||||
|
STD_VIDEO_AV1_INTERPOLATION_FILTER_SWITCHABLE = 4,
|
||||||
|
STD_VIDEO_AV1_INTERPOLATION_FILTER_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_INTERPOLATION_FILTER_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1InterpolationFilter;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1TxMode {
|
||||||
|
STD_VIDEO_AV1_TX_MODE_ONLY_4X4 = 0,
|
||||||
|
STD_VIDEO_AV1_TX_MODE_LARGEST = 1,
|
||||||
|
STD_VIDEO_AV1_TX_MODE_SELECT = 2,
|
||||||
|
STD_VIDEO_AV1_TX_MODE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_TX_MODE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1TxMode;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1FrameRestorationType {
|
||||||
|
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_NONE = 0,
|
||||||
|
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_WIENER = 1,
|
||||||
|
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_SGRPROJ = 2,
|
||||||
|
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_SWITCHABLE = 3,
|
||||||
|
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1FrameRestorationType;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1ColorPrimaries {
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_709 = 1,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_UNSPECIFIED = 2,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_470_M = 4,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_470_B_G = 5,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_601 = 6,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_SMPTE_240 = 7,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_GENERIC_FILM = 8,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_2020 = 9,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_XYZ = 10,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_SMPTE_431 = 11,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_SMPTE_432 = 12,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_EBU_3213 = 22,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_INVALID = 0x7FFFFFFF,
|
||||||
|
// STD_VIDEO_AV1_COLOR_PRIMARIES_BT_UNSPECIFIED is a deprecated alias
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_UNSPECIFIED = STD_VIDEO_AV1_COLOR_PRIMARIES_UNSPECIFIED,
|
||||||
|
STD_VIDEO_AV1_COLOR_PRIMARIES_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1ColorPrimaries;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1TransferCharacteristics {
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_RESERVED_0 = 0,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_709 = 1,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_UNSPECIFIED = 2,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_RESERVED_3 = 3,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_470_M = 4,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_470_B_G = 5,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_601 = 6,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_SMPTE_240 = 7,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_LINEAR = 8,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_LOG_100 = 9,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_LOG_100_SQRT10 = 10,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_IEC_61966 = 11,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_1361 = 12,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_SRGB = 13,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_2020_10_BIT = 14,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_2020_12_BIT = 15,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_SMPTE_2084 = 16,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_SMPTE_428 = 17,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_HLG = 18,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1TransferCharacteristics;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1MatrixCoefficients {
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_IDENTITY = 0,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_709 = 1,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_UNSPECIFIED = 2,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_RESERVED_3 = 3,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_FCC = 4,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_470_B_G = 5,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_601 = 6,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_SMPTE_240 = 7,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_SMPTE_YCGCO = 8,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_2020_NCL = 9,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_2020_CL = 10,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_SMPTE_2085 = 11,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_CHROMAT_NCL = 12,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_CHROMAT_CL = 13,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_ICTCP = 14,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1MatrixCoefficients;
|
||||||
|
|
||||||
|
typedef enum StdVideoAV1ChromaSamplePosition {
|
||||||
|
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_UNKNOWN = 0,
|
||||||
|
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_VERTICAL = 1,
|
||||||
|
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_COLOCATED = 2,
|
||||||
|
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_RESERVED = 3,
|
||||||
|
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoAV1ChromaSamplePosition;
|
||||||
|
typedef struct StdVideoAV1ColorConfigFlags {
|
||||||
|
uint32_t mono_chrome : 1;
|
||||||
|
uint32_t color_range : 1;
|
||||||
|
uint32_t separate_uv_delta_q : 1;
|
||||||
|
uint32_t color_description_present_flag : 1;
|
||||||
|
uint32_t reserved : 28;
|
||||||
|
} StdVideoAV1ColorConfigFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1ColorConfig {
|
||||||
|
StdVideoAV1ColorConfigFlags flags;
|
||||||
|
uint8_t BitDepth;
|
||||||
|
uint8_t subsampling_x;
|
||||||
|
uint8_t subsampling_y;
|
||||||
|
uint8_t reserved1;
|
||||||
|
StdVideoAV1ColorPrimaries color_primaries;
|
||||||
|
StdVideoAV1TransferCharacteristics transfer_characteristics;
|
||||||
|
StdVideoAV1MatrixCoefficients matrix_coefficients;
|
||||||
|
StdVideoAV1ChromaSamplePosition chroma_sample_position;
|
||||||
|
} StdVideoAV1ColorConfig;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1TimingInfoFlags {
|
||||||
|
uint32_t equal_picture_interval : 1;
|
||||||
|
uint32_t reserved : 31;
|
||||||
|
} StdVideoAV1TimingInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1TimingInfo {
|
||||||
|
StdVideoAV1TimingInfoFlags flags;
|
||||||
|
uint32_t num_units_in_display_tick;
|
||||||
|
uint32_t time_scale;
|
||||||
|
uint32_t num_ticks_per_picture_minus_1;
|
||||||
|
} StdVideoAV1TimingInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1LoopFilterFlags {
|
||||||
|
uint32_t loop_filter_delta_enabled : 1;
|
||||||
|
uint32_t loop_filter_delta_update : 1;
|
||||||
|
uint32_t reserved : 30;
|
||||||
|
} StdVideoAV1LoopFilterFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1LoopFilter {
|
||||||
|
StdVideoAV1LoopFilterFlags flags;
|
||||||
|
uint8_t loop_filter_level[STD_VIDEO_AV1_MAX_LOOP_FILTER_STRENGTHS];
|
||||||
|
uint8_t loop_filter_sharpness;
|
||||||
|
uint8_t update_ref_delta;
|
||||||
|
int8_t loop_filter_ref_deltas[STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME];
|
||||||
|
uint8_t update_mode_delta;
|
||||||
|
int8_t loop_filter_mode_deltas[STD_VIDEO_AV1_LOOP_FILTER_ADJUSTMENTS];
|
||||||
|
} StdVideoAV1LoopFilter;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1QuantizationFlags {
|
||||||
|
uint32_t using_qmatrix : 1;
|
||||||
|
uint32_t diff_uv_delta : 1;
|
||||||
|
uint32_t reserved : 30;
|
||||||
|
} StdVideoAV1QuantizationFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1Quantization {
|
||||||
|
StdVideoAV1QuantizationFlags flags;
|
||||||
|
uint8_t base_q_idx;
|
||||||
|
int8_t DeltaQYDc;
|
||||||
|
int8_t DeltaQUDc;
|
||||||
|
int8_t DeltaQUAc;
|
||||||
|
int8_t DeltaQVDc;
|
||||||
|
int8_t DeltaQVAc;
|
||||||
|
uint8_t qm_y;
|
||||||
|
uint8_t qm_u;
|
||||||
|
uint8_t qm_v;
|
||||||
|
} StdVideoAV1Quantization;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1Segmentation {
|
||||||
|
uint8_t FeatureEnabled[STD_VIDEO_AV1_MAX_SEGMENTS];
|
||||||
|
int16_t FeatureData[STD_VIDEO_AV1_MAX_SEGMENTS][STD_VIDEO_AV1_SEG_LVL_MAX];
|
||||||
|
} StdVideoAV1Segmentation;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1TileInfoFlags {
|
||||||
|
uint32_t uniform_tile_spacing_flag : 1;
|
||||||
|
uint32_t reserved : 31;
|
||||||
|
} StdVideoAV1TileInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1TileInfo {
|
||||||
|
StdVideoAV1TileInfoFlags flags;
|
||||||
|
uint8_t TileCols;
|
||||||
|
uint8_t TileRows;
|
||||||
|
uint16_t context_update_tile_id;
|
||||||
|
uint8_t tile_size_bytes_minus_1;
|
||||||
|
uint8_t reserved1[7];
|
||||||
|
const uint16_t* pMiColStarts;
|
||||||
|
const uint16_t* pMiRowStarts;
|
||||||
|
const uint16_t* pWidthInSbsMinus1;
|
||||||
|
const uint16_t* pHeightInSbsMinus1;
|
||||||
|
} StdVideoAV1TileInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1CDEF {
|
||||||
|
uint8_t cdef_damping_minus_3;
|
||||||
|
uint8_t cdef_bits;
|
||||||
|
uint8_t cdef_y_pri_strength[STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS];
|
||||||
|
uint8_t cdef_y_sec_strength[STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS];
|
||||||
|
uint8_t cdef_uv_pri_strength[STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS];
|
||||||
|
uint8_t cdef_uv_sec_strength[STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS];
|
||||||
|
} StdVideoAV1CDEF;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1LoopRestoration {
|
||||||
|
StdVideoAV1FrameRestorationType FrameRestorationType[STD_VIDEO_AV1_MAX_NUM_PLANES];
|
||||||
|
uint16_t LoopRestorationSize[STD_VIDEO_AV1_MAX_NUM_PLANES];
|
||||||
|
} StdVideoAV1LoopRestoration;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1GlobalMotion {
|
||||||
|
uint8_t GmType[STD_VIDEO_AV1_NUM_REF_FRAMES];
|
||||||
|
int32_t gm_params[STD_VIDEO_AV1_NUM_REF_FRAMES][STD_VIDEO_AV1_GLOBAL_MOTION_PARAMS];
|
||||||
|
} StdVideoAV1GlobalMotion;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1FilmGrainFlags {
|
||||||
|
uint32_t chroma_scaling_from_luma : 1;
|
||||||
|
uint32_t overlap_flag : 1;
|
||||||
|
uint32_t clip_to_restricted_range : 1;
|
||||||
|
uint32_t update_grain : 1;
|
||||||
|
uint32_t reserved : 28;
|
||||||
|
} StdVideoAV1FilmGrainFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1FilmGrain {
|
||||||
|
StdVideoAV1FilmGrainFlags flags;
|
||||||
|
uint8_t grain_scaling_minus_8;
|
||||||
|
uint8_t ar_coeff_lag;
|
||||||
|
uint8_t ar_coeff_shift_minus_6;
|
||||||
|
uint8_t grain_scale_shift;
|
||||||
|
uint16_t grain_seed;
|
||||||
|
uint8_t film_grain_params_ref_idx;
|
||||||
|
uint8_t num_y_points;
|
||||||
|
uint8_t point_y_value[STD_VIDEO_AV1_MAX_NUM_Y_POINTS];
|
||||||
|
uint8_t point_y_scaling[STD_VIDEO_AV1_MAX_NUM_Y_POINTS];
|
||||||
|
uint8_t num_cb_points;
|
||||||
|
uint8_t point_cb_value[STD_VIDEO_AV1_MAX_NUM_CB_POINTS];
|
||||||
|
uint8_t point_cb_scaling[STD_VIDEO_AV1_MAX_NUM_CB_POINTS];
|
||||||
|
uint8_t num_cr_points;
|
||||||
|
uint8_t point_cr_value[STD_VIDEO_AV1_MAX_NUM_CR_POINTS];
|
||||||
|
uint8_t point_cr_scaling[STD_VIDEO_AV1_MAX_NUM_CR_POINTS];
|
||||||
|
int8_t ar_coeffs_y_plus_128[STD_VIDEO_AV1_MAX_NUM_POS_LUMA];
|
||||||
|
int8_t ar_coeffs_cb_plus_128[STD_VIDEO_AV1_MAX_NUM_POS_CHROMA];
|
||||||
|
int8_t ar_coeffs_cr_plus_128[STD_VIDEO_AV1_MAX_NUM_POS_CHROMA];
|
||||||
|
uint8_t cb_mult;
|
||||||
|
uint8_t cb_luma_mult;
|
||||||
|
uint16_t cb_offset;
|
||||||
|
uint8_t cr_mult;
|
||||||
|
uint8_t cr_luma_mult;
|
||||||
|
uint16_t cr_offset;
|
||||||
|
} StdVideoAV1FilmGrain;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1SequenceHeaderFlags {
|
||||||
|
uint32_t still_picture : 1;
|
||||||
|
uint32_t reduced_still_picture_header : 1;
|
||||||
|
uint32_t use_128x128_superblock : 1;
|
||||||
|
uint32_t enable_filter_intra : 1;
|
||||||
|
uint32_t enable_intra_edge_filter : 1;
|
||||||
|
uint32_t enable_interintra_compound : 1;
|
||||||
|
uint32_t enable_masked_compound : 1;
|
||||||
|
uint32_t enable_warped_motion : 1;
|
||||||
|
uint32_t enable_dual_filter : 1;
|
||||||
|
uint32_t enable_order_hint : 1;
|
||||||
|
uint32_t enable_jnt_comp : 1;
|
||||||
|
uint32_t enable_ref_frame_mvs : 1;
|
||||||
|
uint32_t frame_id_numbers_present_flag : 1;
|
||||||
|
uint32_t enable_superres : 1;
|
||||||
|
uint32_t enable_cdef : 1;
|
||||||
|
uint32_t enable_restoration : 1;
|
||||||
|
uint32_t film_grain_params_present : 1;
|
||||||
|
uint32_t timing_info_present_flag : 1;
|
||||||
|
uint32_t initial_display_delay_present_flag : 1;
|
||||||
|
uint32_t reserved : 13;
|
||||||
|
} StdVideoAV1SequenceHeaderFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoAV1SequenceHeader {
|
||||||
|
StdVideoAV1SequenceHeaderFlags flags;
|
||||||
|
StdVideoAV1Profile seq_profile;
|
||||||
|
uint8_t frame_width_bits_minus_1;
|
||||||
|
uint8_t frame_height_bits_minus_1;
|
||||||
|
uint16_t max_frame_width_minus_1;
|
||||||
|
uint16_t max_frame_height_minus_1;
|
||||||
|
uint8_t delta_frame_id_length_minus_2;
|
||||||
|
uint8_t additional_frame_id_length_minus_1;
|
||||||
|
uint8_t order_hint_bits_minus_1;
|
||||||
|
uint8_t seq_force_integer_mv;
|
||||||
|
uint8_t seq_force_screen_content_tools;
|
||||||
|
uint8_t reserved1[5];
|
||||||
|
const StdVideoAV1ColorConfig* pColorConfig;
|
||||||
|
const StdVideoAV1TimingInfo* pTimingInfo;
|
||||||
|
} StdVideoAV1SequenceHeader;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODEC_AV1STD_DECODE_H_
|
||||||
|
#define VULKAN_VIDEO_CODEC_AV1STD_DECODE_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codec_av1std_decode is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codec_av1std_decode 1
|
||||||
|
#include "vulkan_video_codec_av1std.h"
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_API_VERSION_1_0_0
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_av1_decode"
|
||||||
|
typedef struct StdVideoDecodeAV1PictureInfoFlags {
|
||||||
|
uint32_t error_resilient_mode : 1;
|
||||||
|
uint32_t disable_cdf_update : 1;
|
||||||
|
uint32_t use_superres : 1;
|
||||||
|
uint32_t render_and_frame_size_different : 1;
|
||||||
|
uint32_t allow_screen_content_tools : 1;
|
||||||
|
uint32_t is_filter_switchable : 1;
|
||||||
|
uint32_t force_integer_mv : 1;
|
||||||
|
uint32_t frame_size_override_flag : 1;
|
||||||
|
uint32_t buffer_removal_time_present_flag : 1;
|
||||||
|
uint32_t allow_intrabc : 1;
|
||||||
|
uint32_t frame_refs_short_signaling : 1;
|
||||||
|
uint32_t allow_high_precision_mv : 1;
|
||||||
|
uint32_t is_motion_mode_switchable : 1;
|
||||||
|
uint32_t use_ref_frame_mvs : 1;
|
||||||
|
uint32_t disable_frame_end_update_cdf : 1;
|
||||||
|
uint32_t allow_warped_motion : 1;
|
||||||
|
uint32_t reduced_tx_set : 1;
|
||||||
|
uint32_t reference_select : 1;
|
||||||
|
uint32_t skip_mode_present : 1;
|
||||||
|
uint32_t delta_q_present : 1;
|
||||||
|
uint32_t delta_lf_present : 1;
|
||||||
|
uint32_t delta_lf_multi : 1;
|
||||||
|
uint32_t segmentation_enabled : 1;
|
||||||
|
uint32_t segmentation_update_map : 1;
|
||||||
|
uint32_t segmentation_temporal_update : 1;
|
||||||
|
uint32_t segmentation_update_data : 1;
|
||||||
|
uint32_t UsesLr : 1;
|
||||||
|
uint32_t usesChromaLr : 1;
|
||||||
|
uint32_t apply_grain : 1;
|
||||||
|
uint32_t reserved : 3;
|
||||||
|
} StdVideoDecodeAV1PictureInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoDecodeAV1PictureInfo {
|
||||||
|
StdVideoDecodeAV1PictureInfoFlags flags;
|
||||||
|
StdVideoAV1FrameType frame_type;
|
||||||
|
uint32_t current_frame_id;
|
||||||
|
uint8_t OrderHint;
|
||||||
|
uint8_t primary_ref_frame;
|
||||||
|
uint8_t refresh_frame_flags;
|
||||||
|
uint8_t reserved1;
|
||||||
|
StdVideoAV1InterpolationFilter interpolation_filter;
|
||||||
|
StdVideoAV1TxMode TxMode;
|
||||||
|
uint8_t delta_q_res;
|
||||||
|
uint8_t delta_lf_res;
|
||||||
|
uint8_t SkipModeFrame[STD_VIDEO_AV1_SKIP_MODE_FRAMES];
|
||||||
|
uint8_t coded_denom;
|
||||||
|
uint8_t reserved2[3];
|
||||||
|
uint8_t OrderHints[STD_VIDEO_AV1_NUM_REF_FRAMES];
|
||||||
|
uint32_t expectedFrameId[STD_VIDEO_AV1_NUM_REF_FRAMES];
|
||||||
|
const StdVideoAV1TileInfo* pTileInfo;
|
||||||
|
const StdVideoAV1Quantization* pQuantization;
|
||||||
|
const StdVideoAV1Segmentation* pSegmentation;
|
||||||
|
const StdVideoAV1LoopFilter* pLoopFilter;
|
||||||
|
const StdVideoAV1CDEF* pCDEF;
|
||||||
|
const StdVideoAV1LoopRestoration* pLoopRestoration;
|
||||||
|
const StdVideoAV1GlobalMotion* pGlobalMotion;
|
||||||
|
const StdVideoAV1FilmGrain* pFilmGrain;
|
||||||
|
} StdVideoDecodeAV1PictureInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoDecodeAV1ReferenceInfoFlags {
|
||||||
|
uint32_t disable_frame_end_update_cdf : 1;
|
||||||
|
uint32_t segmentation_enabled : 1;
|
||||||
|
uint32_t reserved : 30;
|
||||||
|
} StdVideoDecodeAV1ReferenceInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoDecodeAV1ReferenceInfo {
|
||||||
|
StdVideoDecodeAV1ReferenceInfoFlags flags;
|
||||||
|
uint8_t frame_type;
|
||||||
|
uint8_t RefFrameSignBias;
|
||||||
|
uint8_t OrderHint;
|
||||||
|
uint8_t SavedOrderHints[STD_VIDEO_AV1_NUM_REF_FRAMES];
|
||||||
|
} StdVideoDecodeAV1ReferenceInfo;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODEC_AV1STD_ENCODE_H_
|
||||||
|
#define VULKAN_VIDEO_CODEC_AV1STD_ENCODE_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codec_av1std_encode is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codec_av1std_encode 1
|
||||||
|
#include "vulkan_video_codec_av1std.h"
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_ENCODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_ENCODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_AV1_ENCODE_API_VERSION_1_0_0
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_ENCODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_av1_encode"
|
||||||
|
typedef struct StdVideoEncodeAV1DecoderModelInfo {
|
||||||
|
uint8_t buffer_delay_length_minus_1;
|
||||||
|
uint8_t buffer_removal_time_length_minus_1;
|
||||||
|
uint8_t frame_presentation_time_length_minus_1;
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint32_t num_units_in_decoding_tick;
|
||||||
|
} StdVideoEncodeAV1DecoderModelInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeAV1ExtensionHeader {
|
||||||
|
uint8_t temporal_id;
|
||||||
|
uint8_t spatial_id;
|
||||||
|
} StdVideoEncodeAV1ExtensionHeader;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeAV1OperatingPointInfoFlags {
|
||||||
|
uint32_t decoder_model_present_for_this_op : 1;
|
||||||
|
uint32_t low_delay_mode_flag : 1;
|
||||||
|
uint32_t initial_display_delay_present_for_this_op : 1;
|
||||||
|
uint32_t reserved : 29;
|
||||||
|
} StdVideoEncodeAV1OperatingPointInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeAV1OperatingPointInfo {
|
||||||
|
StdVideoEncodeAV1OperatingPointInfoFlags flags;
|
||||||
|
uint16_t operating_point_idc;
|
||||||
|
uint8_t seq_level_idx;
|
||||||
|
uint8_t seq_tier;
|
||||||
|
uint32_t decoder_buffer_delay;
|
||||||
|
uint32_t encoder_buffer_delay;
|
||||||
|
uint8_t initial_display_delay_minus_1;
|
||||||
|
} StdVideoEncodeAV1OperatingPointInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeAV1PictureInfoFlags {
|
||||||
|
uint32_t error_resilient_mode : 1;
|
||||||
|
uint32_t disable_cdf_update : 1;
|
||||||
|
uint32_t use_superres : 1;
|
||||||
|
uint32_t render_and_frame_size_different : 1;
|
||||||
|
uint32_t allow_screen_content_tools : 1;
|
||||||
|
uint32_t is_filter_switchable : 1;
|
||||||
|
uint32_t force_integer_mv : 1;
|
||||||
|
uint32_t frame_size_override_flag : 1;
|
||||||
|
uint32_t buffer_removal_time_present_flag : 1;
|
||||||
|
uint32_t allow_intrabc : 1;
|
||||||
|
uint32_t frame_refs_short_signaling : 1;
|
||||||
|
uint32_t allow_high_precision_mv : 1;
|
||||||
|
uint32_t is_motion_mode_switchable : 1;
|
||||||
|
uint32_t use_ref_frame_mvs : 1;
|
||||||
|
uint32_t disable_frame_end_update_cdf : 1;
|
||||||
|
uint32_t allow_warped_motion : 1;
|
||||||
|
uint32_t reduced_tx_set : 1;
|
||||||
|
uint32_t skip_mode_present : 1;
|
||||||
|
uint32_t delta_q_present : 1;
|
||||||
|
uint32_t delta_lf_present : 1;
|
||||||
|
uint32_t delta_lf_multi : 1;
|
||||||
|
uint32_t segmentation_enabled : 1;
|
||||||
|
uint32_t segmentation_update_map : 1;
|
||||||
|
uint32_t segmentation_temporal_update : 1;
|
||||||
|
uint32_t segmentation_update_data : 1;
|
||||||
|
uint32_t UsesLr : 1;
|
||||||
|
uint32_t usesChromaLr : 1;
|
||||||
|
uint32_t show_frame : 1;
|
||||||
|
uint32_t showable_frame : 1;
|
||||||
|
uint32_t reserved : 3;
|
||||||
|
} StdVideoEncodeAV1PictureInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeAV1PictureInfo {
|
||||||
|
StdVideoEncodeAV1PictureInfoFlags flags;
|
||||||
|
StdVideoAV1FrameType frame_type;
|
||||||
|
uint32_t frame_presentation_time;
|
||||||
|
uint32_t current_frame_id;
|
||||||
|
uint8_t order_hint;
|
||||||
|
uint8_t primary_ref_frame;
|
||||||
|
uint8_t refresh_frame_flags;
|
||||||
|
uint8_t coded_denom;
|
||||||
|
uint16_t render_width_minus_1;
|
||||||
|
uint16_t render_height_minus_1;
|
||||||
|
StdVideoAV1InterpolationFilter interpolation_filter;
|
||||||
|
StdVideoAV1TxMode TxMode;
|
||||||
|
uint8_t delta_q_res;
|
||||||
|
uint8_t delta_lf_res;
|
||||||
|
uint8_t ref_order_hint[STD_VIDEO_AV1_NUM_REF_FRAMES];
|
||||||
|
int8_t ref_frame_idx[STD_VIDEO_AV1_REFS_PER_FRAME];
|
||||||
|
uint8_t reserved1[3];
|
||||||
|
uint32_t delta_frame_id_minus_1[STD_VIDEO_AV1_REFS_PER_FRAME];
|
||||||
|
const StdVideoAV1TileInfo* pTileInfo;
|
||||||
|
const StdVideoAV1Quantization* pQuantization;
|
||||||
|
const StdVideoAV1Segmentation* pSegmentation;
|
||||||
|
const StdVideoAV1LoopFilter* pLoopFilter;
|
||||||
|
const StdVideoAV1CDEF* pCDEF;
|
||||||
|
const StdVideoAV1LoopRestoration* pLoopRestoration;
|
||||||
|
const StdVideoAV1GlobalMotion* pGlobalMotion;
|
||||||
|
const StdVideoEncodeAV1ExtensionHeader* pExtensionHeader;
|
||||||
|
const uint32_t* pBufferRemovalTimes;
|
||||||
|
} StdVideoEncodeAV1PictureInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeAV1ReferenceInfoFlags {
|
||||||
|
uint32_t disable_frame_end_update_cdf : 1;
|
||||||
|
uint32_t segmentation_enabled : 1;
|
||||||
|
uint32_t reserved : 30;
|
||||||
|
} StdVideoEncodeAV1ReferenceInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeAV1ReferenceInfo {
|
||||||
|
StdVideoEncodeAV1ReferenceInfoFlags flags;
|
||||||
|
uint32_t RefFrameId;
|
||||||
|
StdVideoAV1FrameType frame_type;
|
||||||
|
uint8_t OrderHint;
|
||||||
|
uint8_t reserved1[3];
|
||||||
|
const StdVideoEncodeAV1ExtensionHeader* pExtensionHeader;
|
||||||
|
} StdVideoEncodeAV1ReferenceInfo;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODEC_H264STD_H_
|
||||||
|
#define VULKAN_VIDEO_CODEC_H264STD_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codec_h264std is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codec_h264std 1
|
||||||
|
#include "vulkan_video_codecs_common.h"
|
||||||
|
#define STD_VIDEO_H264_CPB_CNT_LIST_SIZE 32
|
||||||
|
#define STD_VIDEO_H264_SCALING_LIST_4X4_NUM_LISTS 6
|
||||||
|
#define STD_VIDEO_H264_SCALING_LIST_4X4_NUM_ELEMENTS 16
|
||||||
|
#define STD_VIDEO_H264_SCALING_LIST_8X8_NUM_LISTS 6
|
||||||
|
#define STD_VIDEO_H264_SCALING_LIST_8X8_NUM_ELEMENTS 64
|
||||||
|
#define STD_VIDEO_H264_MAX_NUM_LIST_REF 32
|
||||||
|
#define STD_VIDEO_H264_MAX_CHROMA_PLANES 2
|
||||||
|
#define STD_VIDEO_H264_NO_REFERENCE_PICTURE 0xFF
|
||||||
|
|
||||||
|
typedef enum StdVideoH264ChromaFormatIdc {
|
||||||
|
STD_VIDEO_H264_CHROMA_FORMAT_IDC_MONOCHROME = 0,
|
||||||
|
STD_VIDEO_H264_CHROMA_FORMAT_IDC_420 = 1,
|
||||||
|
STD_VIDEO_H264_CHROMA_FORMAT_IDC_422 = 2,
|
||||||
|
STD_VIDEO_H264_CHROMA_FORMAT_IDC_444 = 3,
|
||||||
|
STD_VIDEO_H264_CHROMA_FORMAT_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_CHROMA_FORMAT_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264ChromaFormatIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264ProfileIdc {
|
||||||
|
STD_VIDEO_H264_PROFILE_IDC_BASELINE = 66,
|
||||||
|
STD_VIDEO_H264_PROFILE_IDC_MAIN = 77,
|
||||||
|
STD_VIDEO_H264_PROFILE_IDC_HIGH = 100,
|
||||||
|
STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE = 244,
|
||||||
|
STD_VIDEO_H264_PROFILE_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_PROFILE_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264ProfileIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264LevelIdc {
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_1_0 = 0,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_1_1 = 1,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_1_2 = 2,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_1_3 = 3,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_2_0 = 4,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_2_1 = 5,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_2_2 = 6,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_3_0 = 7,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_3_1 = 8,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_3_2 = 9,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_4_0 = 10,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_4_1 = 11,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_4_2 = 12,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_5_0 = 13,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_5_1 = 14,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_5_2 = 15,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_6_0 = 16,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_6_1 = 17,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_6_2 = 18,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_LEVEL_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264LevelIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264PocType {
|
||||||
|
STD_VIDEO_H264_POC_TYPE_0 = 0,
|
||||||
|
STD_VIDEO_H264_POC_TYPE_1 = 1,
|
||||||
|
STD_VIDEO_H264_POC_TYPE_2 = 2,
|
||||||
|
STD_VIDEO_H264_POC_TYPE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_POC_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264PocType;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264AspectRatioIdc {
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_UNSPECIFIED = 0,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_SQUARE = 1,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_12_11 = 2,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_10_11 = 3,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_16_11 = 4,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_40_33 = 5,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_24_11 = 6,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_20_11 = 7,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_32_11 = 8,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_80_33 = 9,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_18_11 = 10,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_15_11 = 11,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_64_33 = 12,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_160_99 = 13,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_4_3 = 14,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_3_2 = 15,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_2_1 = 16,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_EXTENDED_SAR = 255,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_ASPECT_RATIO_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264AspectRatioIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264WeightedBipredIdc {
|
||||||
|
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_DEFAULT = 0,
|
||||||
|
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_EXPLICIT = 1,
|
||||||
|
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_IMPLICIT = 2,
|
||||||
|
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264WeightedBipredIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264ModificationOfPicNumsIdc {
|
||||||
|
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_SHORT_TERM_SUBTRACT = 0,
|
||||||
|
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_SHORT_TERM_ADD = 1,
|
||||||
|
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_LONG_TERM = 2,
|
||||||
|
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_END = 3,
|
||||||
|
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264ModificationOfPicNumsIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264MemMgmtControlOp {
|
||||||
|
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_END = 0,
|
||||||
|
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_UNMARK_SHORT_TERM = 1,
|
||||||
|
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_UNMARK_LONG_TERM = 2,
|
||||||
|
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_MARK_LONG_TERM = 3,
|
||||||
|
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_SET_MAX_LONG_TERM_INDEX = 4,
|
||||||
|
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_UNMARK_ALL = 5,
|
||||||
|
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_MARK_CURRENT_AS_LONG_TERM = 6,
|
||||||
|
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264MemMgmtControlOp;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264CabacInitIdc {
|
||||||
|
STD_VIDEO_H264_CABAC_INIT_IDC_0 = 0,
|
||||||
|
STD_VIDEO_H264_CABAC_INIT_IDC_1 = 1,
|
||||||
|
STD_VIDEO_H264_CABAC_INIT_IDC_2 = 2,
|
||||||
|
STD_VIDEO_H264_CABAC_INIT_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_CABAC_INIT_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264CabacInitIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264DisableDeblockingFilterIdc {
|
||||||
|
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_DISABLED = 0,
|
||||||
|
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_ENABLED = 1,
|
||||||
|
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_PARTIAL = 2,
|
||||||
|
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264DisableDeblockingFilterIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264SliceType {
|
||||||
|
STD_VIDEO_H264_SLICE_TYPE_P = 0,
|
||||||
|
STD_VIDEO_H264_SLICE_TYPE_B = 1,
|
||||||
|
STD_VIDEO_H264_SLICE_TYPE_I = 2,
|
||||||
|
STD_VIDEO_H264_SLICE_TYPE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_SLICE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264SliceType;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264PictureType {
|
||||||
|
STD_VIDEO_H264_PICTURE_TYPE_P = 0,
|
||||||
|
STD_VIDEO_H264_PICTURE_TYPE_B = 1,
|
||||||
|
STD_VIDEO_H264_PICTURE_TYPE_I = 2,
|
||||||
|
STD_VIDEO_H264_PICTURE_TYPE_IDR = 5,
|
||||||
|
STD_VIDEO_H264_PICTURE_TYPE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_PICTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264PictureType;
|
||||||
|
|
||||||
|
typedef enum StdVideoH264NonVclNaluType {
|
||||||
|
STD_VIDEO_H264_NON_VCL_NALU_TYPE_SPS = 0,
|
||||||
|
STD_VIDEO_H264_NON_VCL_NALU_TYPE_PPS = 1,
|
||||||
|
STD_VIDEO_H264_NON_VCL_NALU_TYPE_AUD = 2,
|
||||||
|
STD_VIDEO_H264_NON_VCL_NALU_TYPE_PREFIX = 3,
|
||||||
|
STD_VIDEO_H264_NON_VCL_NALU_TYPE_END_OF_SEQUENCE = 4,
|
||||||
|
STD_VIDEO_H264_NON_VCL_NALU_TYPE_END_OF_STREAM = 5,
|
||||||
|
STD_VIDEO_H264_NON_VCL_NALU_TYPE_PRECODED = 6,
|
||||||
|
STD_VIDEO_H264_NON_VCL_NALU_TYPE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H264_NON_VCL_NALU_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH264NonVclNaluType;
|
||||||
|
typedef struct StdVideoH264SpsVuiFlags {
|
||||||
|
uint32_t aspect_ratio_info_present_flag : 1;
|
||||||
|
uint32_t overscan_info_present_flag : 1;
|
||||||
|
uint32_t overscan_appropriate_flag : 1;
|
||||||
|
uint32_t video_signal_type_present_flag : 1;
|
||||||
|
uint32_t video_full_range_flag : 1;
|
||||||
|
uint32_t color_description_present_flag : 1;
|
||||||
|
uint32_t chroma_loc_info_present_flag : 1;
|
||||||
|
uint32_t timing_info_present_flag : 1;
|
||||||
|
uint32_t fixed_frame_rate_flag : 1;
|
||||||
|
uint32_t bitstream_restriction_flag : 1;
|
||||||
|
uint32_t nal_hrd_parameters_present_flag : 1;
|
||||||
|
uint32_t vcl_hrd_parameters_present_flag : 1;
|
||||||
|
} StdVideoH264SpsVuiFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH264HrdParameters {
|
||||||
|
uint8_t cpb_cnt_minus1;
|
||||||
|
uint8_t bit_rate_scale;
|
||||||
|
uint8_t cpb_size_scale;
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint32_t bit_rate_value_minus1[STD_VIDEO_H264_CPB_CNT_LIST_SIZE];
|
||||||
|
uint32_t cpb_size_value_minus1[STD_VIDEO_H264_CPB_CNT_LIST_SIZE];
|
||||||
|
uint8_t cbr_flag[STD_VIDEO_H264_CPB_CNT_LIST_SIZE];
|
||||||
|
uint32_t initial_cpb_removal_delay_length_minus1;
|
||||||
|
uint32_t cpb_removal_delay_length_minus1;
|
||||||
|
uint32_t dpb_output_delay_length_minus1;
|
||||||
|
uint32_t time_offset_length;
|
||||||
|
} StdVideoH264HrdParameters;
|
||||||
|
|
||||||
|
typedef struct StdVideoH264SequenceParameterSetVui {
|
||||||
|
StdVideoH264SpsVuiFlags flags;
|
||||||
|
StdVideoH264AspectRatioIdc aspect_ratio_idc;
|
||||||
|
uint16_t sar_width;
|
||||||
|
uint16_t sar_height;
|
||||||
|
uint8_t video_format;
|
||||||
|
uint8_t colour_primaries;
|
||||||
|
uint8_t transfer_characteristics;
|
||||||
|
uint8_t matrix_coefficients;
|
||||||
|
uint32_t num_units_in_tick;
|
||||||
|
uint32_t time_scale;
|
||||||
|
uint8_t max_num_reorder_frames;
|
||||||
|
uint8_t max_dec_frame_buffering;
|
||||||
|
uint8_t chroma_sample_loc_type_top_field;
|
||||||
|
uint8_t chroma_sample_loc_type_bottom_field;
|
||||||
|
uint32_t reserved1;
|
||||||
|
const StdVideoH264HrdParameters* pHrdParameters;
|
||||||
|
} StdVideoH264SequenceParameterSetVui;
|
||||||
|
|
||||||
|
typedef struct StdVideoH264SpsFlags {
|
||||||
|
uint32_t constraint_set0_flag : 1;
|
||||||
|
uint32_t constraint_set1_flag : 1;
|
||||||
|
uint32_t constraint_set2_flag : 1;
|
||||||
|
uint32_t constraint_set3_flag : 1;
|
||||||
|
uint32_t constraint_set4_flag : 1;
|
||||||
|
uint32_t constraint_set5_flag : 1;
|
||||||
|
uint32_t direct_8x8_inference_flag : 1;
|
||||||
|
uint32_t mb_adaptive_frame_field_flag : 1;
|
||||||
|
uint32_t frame_mbs_only_flag : 1;
|
||||||
|
uint32_t delta_pic_order_always_zero_flag : 1;
|
||||||
|
uint32_t separate_colour_plane_flag : 1;
|
||||||
|
uint32_t gaps_in_frame_num_value_allowed_flag : 1;
|
||||||
|
uint32_t qpprime_y_zero_transform_bypass_flag : 1;
|
||||||
|
uint32_t frame_cropping_flag : 1;
|
||||||
|
uint32_t seq_scaling_matrix_present_flag : 1;
|
||||||
|
uint32_t vui_parameters_present_flag : 1;
|
||||||
|
} StdVideoH264SpsFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH264ScalingLists {
|
||||||
|
uint16_t scaling_list_present_mask;
|
||||||
|
uint16_t use_default_scaling_matrix_mask;
|
||||||
|
uint8_t ScalingList4x4[STD_VIDEO_H264_SCALING_LIST_4X4_NUM_LISTS][STD_VIDEO_H264_SCALING_LIST_4X4_NUM_ELEMENTS];
|
||||||
|
uint8_t ScalingList8x8[STD_VIDEO_H264_SCALING_LIST_8X8_NUM_LISTS][STD_VIDEO_H264_SCALING_LIST_8X8_NUM_ELEMENTS];
|
||||||
|
} StdVideoH264ScalingLists;
|
||||||
|
|
||||||
|
typedef struct StdVideoH264SequenceParameterSet {
|
||||||
|
StdVideoH264SpsFlags flags;
|
||||||
|
StdVideoH264ProfileIdc profile_idc;
|
||||||
|
StdVideoH264LevelIdc level_idc;
|
||||||
|
StdVideoH264ChromaFormatIdc chroma_format_idc;
|
||||||
|
uint8_t seq_parameter_set_id;
|
||||||
|
uint8_t bit_depth_luma_minus8;
|
||||||
|
uint8_t bit_depth_chroma_minus8;
|
||||||
|
uint8_t log2_max_frame_num_minus4;
|
||||||
|
StdVideoH264PocType pic_order_cnt_type;
|
||||||
|
int32_t offset_for_non_ref_pic;
|
||||||
|
int32_t offset_for_top_to_bottom_field;
|
||||||
|
uint8_t log2_max_pic_order_cnt_lsb_minus4;
|
||||||
|
uint8_t num_ref_frames_in_pic_order_cnt_cycle;
|
||||||
|
uint8_t max_num_ref_frames;
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint32_t pic_width_in_mbs_minus1;
|
||||||
|
uint32_t pic_height_in_map_units_minus1;
|
||||||
|
uint32_t frame_crop_left_offset;
|
||||||
|
uint32_t frame_crop_right_offset;
|
||||||
|
uint32_t frame_crop_top_offset;
|
||||||
|
uint32_t frame_crop_bottom_offset;
|
||||||
|
uint32_t reserved2;
|
||||||
|
const int32_t* pOffsetForRefFrame;
|
||||||
|
const StdVideoH264ScalingLists* pScalingLists;
|
||||||
|
const StdVideoH264SequenceParameterSetVui* pSequenceParameterSetVui;
|
||||||
|
} StdVideoH264SequenceParameterSet;
|
||||||
|
|
||||||
|
typedef struct StdVideoH264PpsFlags {
|
||||||
|
uint32_t transform_8x8_mode_flag : 1;
|
||||||
|
uint32_t redundant_pic_cnt_present_flag : 1;
|
||||||
|
uint32_t constrained_intra_pred_flag : 1;
|
||||||
|
uint32_t deblocking_filter_control_present_flag : 1;
|
||||||
|
uint32_t weighted_pred_flag : 1;
|
||||||
|
uint32_t bottom_field_pic_order_in_frame_present_flag : 1;
|
||||||
|
uint32_t entropy_coding_mode_flag : 1;
|
||||||
|
uint32_t pic_scaling_matrix_present_flag : 1;
|
||||||
|
} StdVideoH264PpsFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH264PictureParameterSet {
|
||||||
|
StdVideoH264PpsFlags flags;
|
||||||
|
uint8_t seq_parameter_set_id;
|
||||||
|
uint8_t pic_parameter_set_id;
|
||||||
|
uint8_t num_ref_idx_l0_default_active_minus1;
|
||||||
|
uint8_t num_ref_idx_l1_default_active_minus1;
|
||||||
|
StdVideoH264WeightedBipredIdc weighted_bipred_idc;
|
||||||
|
int8_t pic_init_qp_minus26;
|
||||||
|
int8_t pic_init_qs_minus26;
|
||||||
|
int8_t chroma_qp_index_offset;
|
||||||
|
int8_t second_chroma_qp_index_offset;
|
||||||
|
const StdVideoH264ScalingLists* pScalingLists;
|
||||||
|
} StdVideoH264PictureParameterSet;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODEC_H264STD_DECODE_H_
|
||||||
|
#define VULKAN_VIDEO_CODEC_H264STD_DECODE_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codec_h264std_decode is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codec_h264std_decode 1
|
||||||
|
#include "vulkan_video_codec_h264std.h"
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_API_VERSION_1_0_0
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_h264_decode"
|
||||||
|
#define STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_LIST_SIZE 2
|
||||||
|
|
||||||
|
typedef enum StdVideoDecodeH264FieldOrderCount {
|
||||||
|
STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_TOP = 0,
|
||||||
|
STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_BOTTOM = 1,
|
||||||
|
STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoDecodeH264FieldOrderCount;
|
||||||
|
typedef struct StdVideoDecodeH264PictureInfoFlags {
|
||||||
|
uint32_t field_pic_flag : 1;
|
||||||
|
uint32_t is_intra : 1;
|
||||||
|
uint32_t IdrPicFlag : 1;
|
||||||
|
uint32_t bottom_field_flag : 1;
|
||||||
|
uint32_t is_reference : 1;
|
||||||
|
uint32_t complementary_field_pair : 1;
|
||||||
|
} StdVideoDecodeH264PictureInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoDecodeH264PictureInfo {
|
||||||
|
StdVideoDecodeH264PictureInfoFlags flags;
|
||||||
|
uint8_t seq_parameter_set_id;
|
||||||
|
uint8_t pic_parameter_set_id;
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint16_t frame_num;
|
||||||
|
uint16_t idr_pic_id;
|
||||||
|
int32_t PicOrderCnt[STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_LIST_SIZE];
|
||||||
|
} StdVideoDecodeH264PictureInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoDecodeH264ReferenceInfoFlags {
|
||||||
|
uint32_t top_field_flag : 1;
|
||||||
|
uint32_t bottom_field_flag : 1;
|
||||||
|
uint32_t used_for_long_term_reference : 1;
|
||||||
|
uint32_t is_non_existing : 1;
|
||||||
|
} StdVideoDecodeH264ReferenceInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoDecodeH264ReferenceInfo {
|
||||||
|
StdVideoDecodeH264ReferenceInfoFlags flags;
|
||||||
|
uint16_t FrameNum;
|
||||||
|
uint16_t reserved;
|
||||||
|
int32_t PicOrderCnt[STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_LIST_SIZE];
|
||||||
|
} StdVideoDecodeH264ReferenceInfo;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODEC_H264STD_ENCODE_H_
|
||||||
|
#define VULKAN_VIDEO_CODEC_H264STD_ENCODE_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codec_h264std_encode is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codec_h264std_encode 1
|
||||||
|
#include "vulkan_video_codec_h264std.h"
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_API_VERSION_1_0_0
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_h264_encode"
|
||||||
|
typedef struct StdVideoEncodeH264WeightTableFlags {
|
||||||
|
uint32_t luma_weight_l0_flag;
|
||||||
|
uint32_t chroma_weight_l0_flag;
|
||||||
|
uint32_t luma_weight_l1_flag;
|
||||||
|
uint32_t chroma_weight_l1_flag;
|
||||||
|
} StdVideoEncodeH264WeightTableFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264WeightTable {
|
||||||
|
StdVideoEncodeH264WeightTableFlags flags;
|
||||||
|
uint8_t luma_log2_weight_denom;
|
||||||
|
uint8_t chroma_log2_weight_denom;
|
||||||
|
int8_t luma_weight_l0[STD_VIDEO_H264_MAX_NUM_LIST_REF];
|
||||||
|
int8_t luma_offset_l0[STD_VIDEO_H264_MAX_NUM_LIST_REF];
|
||||||
|
int8_t chroma_weight_l0[STD_VIDEO_H264_MAX_NUM_LIST_REF][STD_VIDEO_H264_MAX_CHROMA_PLANES];
|
||||||
|
int8_t chroma_offset_l0[STD_VIDEO_H264_MAX_NUM_LIST_REF][STD_VIDEO_H264_MAX_CHROMA_PLANES];
|
||||||
|
int8_t luma_weight_l1[STD_VIDEO_H264_MAX_NUM_LIST_REF];
|
||||||
|
int8_t luma_offset_l1[STD_VIDEO_H264_MAX_NUM_LIST_REF];
|
||||||
|
int8_t chroma_weight_l1[STD_VIDEO_H264_MAX_NUM_LIST_REF][STD_VIDEO_H264_MAX_CHROMA_PLANES];
|
||||||
|
int8_t chroma_offset_l1[STD_VIDEO_H264_MAX_NUM_LIST_REF][STD_VIDEO_H264_MAX_CHROMA_PLANES];
|
||||||
|
} StdVideoEncodeH264WeightTable;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264SliceHeaderFlags {
|
||||||
|
uint32_t direct_spatial_mv_pred_flag : 1;
|
||||||
|
uint32_t num_ref_idx_active_override_flag : 1;
|
||||||
|
uint32_t reserved : 30;
|
||||||
|
} StdVideoEncodeH264SliceHeaderFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264PictureInfoFlags {
|
||||||
|
uint32_t IdrPicFlag : 1;
|
||||||
|
uint32_t is_reference : 1;
|
||||||
|
uint32_t no_output_of_prior_pics_flag : 1;
|
||||||
|
uint32_t long_term_reference_flag : 1;
|
||||||
|
uint32_t adaptive_ref_pic_marking_mode_flag : 1;
|
||||||
|
uint32_t reserved : 27;
|
||||||
|
} StdVideoEncodeH264PictureInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264ReferenceInfoFlags {
|
||||||
|
uint32_t used_for_long_term_reference : 1;
|
||||||
|
uint32_t reserved : 31;
|
||||||
|
} StdVideoEncodeH264ReferenceInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264ReferenceListsInfoFlags {
|
||||||
|
uint32_t ref_pic_list_modification_flag_l0 : 1;
|
||||||
|
uint32_t ref_pic_list_modification_flag_l1 : 1;
|
||||||
|
uint32_t reserved : 30;
|
||||||
|
} StdVideoEncodeH264ReferenceListsInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264RefListModEntry {
|
||||||
|
StdVideoH264ModificationOfPicNumsIdc modification_of_pic_nums_idc;
|
||||||
|
uint16_t abs_diff_pic_num_minus1;
|
||||||
|
uint16_t long_term_pic_num;
|
||||||
|
} StdVideoEncodeH264RefListModEntry;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264RefPicMarkingEntry {
|
||||||
|
StdVideoH264MemMgmtControlOp memory_management_control_operation;
|
||||||
|
uint16_t difference_of_pic_nums_minus1;
|
||||||
|
uint16_t long_term_pic_num;
|
||||||
|
uint16_t long_term_frame_idx;
|
||||||
|
uint16_t max_long_term_frame_idx_plus1;
|
||||||
|
} StdVideoEncodeH264RefPicMarkingEntry;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264ReferenceListsInfo {
|
||||||
|
StdVideoEncodeH264ReferenceListsInfoFlags flags;
|
||||||
|
uint8_t num_ref_idx_l0_active_minus1;
|
||||||
|
uint8_t num_ref_idx_l1_active_minus1;
|
||||||
|
uint8_t RefPicList0[STD_VIDEO_H264_MAX_NUM_LIST_REF];
|
||||||
|
uint8_t RefPicList1[STD_VIDEO_H264_MAX_NUM_LIST_REF];
|
||||||
|
uint8_t refList0ModOpCount;
|
||||||
|
uint8_t refList1ModOpCount;
|
||||||
|
uint8_t refPicMarkingOpCount;
|
||||||
|
uint8_t reserved1[7];
|
||||||
|
const StdVideoEncodeH264RefListModEntry* pRefList0ModOperations;
|
||||||
|
const StdVideoEncodeH264RefListModEntry* pRefList1ModOperations;
|
||||||
|
const StdVideoEncodeH264RefPicMarkingEntry* pRefPicMarkingOperations;
|
||||||
|
} StdVideoEncodeH264ReferenceListsInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264PictureInfo {
|
||||||
|
StdVideoEncodeH264PictureInfoFlags flags;
|
||||||
|
uint8_t seq_parameter_set_id;
|
||||||
|
uint8_t pic_parameter_set_id;
|
||||||
|
uint16_t idr_pic_id;
|
||||||
|
StdVideoH264PictureType primary_pic_type;
|
||||||
|
uint32_t frame_num;
|
||||||
|
int32_t PicOrderCnt;
|
||||||
|
uint8_t temporal_id;
|
||||||
|
uint8_t reserved1[3];
|
||||||
|
const StdVideoEncodeH264ReferenceListsInfo* pRefLists;
|
||||||
|
} StdVideoEncodeH264PictureInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264ReferenceInfo {
|
||||||
|
StdVideoEncodeH264ReferenceInfoFlags flags;
|
||||||
|
StdVideoH264PictureType primary_pic_type;
|
||||||
|
uint32_t FrameNum;
|
||||||
|
int32_t PicOrderCnt;
|
||||||
|
uint16_t long_term_pic_num;
|
||||||
|
uint16_t long_term_frame_idx;
|
||||||
|
uint8_t temporal_id;
|
||||||
|
} StdVideoEncodeH264ReferenceInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH264SliceHeader {
|
||||||
|
StdVideoEncodeH264SliceHeaderFlags flags;
|
||||||
|
uint32_t first_mb_in_slice;
|
||||||
|
StdVideoH264SliceType slice_type;
|
||||||
|
int8_t slice_alpha_c0_offset_div2;
|
||||||
|
int8_t slice_beta_offset_div2;
|
||||||
|
int8_t slice_qp_delta;
|
||||||
|
uint8_t reserved1;
|
||||||
|
StdVideoH264CabacInitIdc cabac_init_idc;
|
||||||
|
StdVideoH264DisableDeblockingFilterIdc disable_deblocking_filter_idc;
|
||||||
|
const StdVideoEncodeH264WeightTable* pWeightTable;
|
||||||
|
} StdVideoEncodeH264SliceHeader;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODEC_H265STD_H_
|
||||||
|
#define VULKAN_VIDEO_CODEC_H265STD_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codec_h265std is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codec_h265std 1
|
||||||
|
#include "vulkan_video_codecs_common.h"
|
||||||
|
#define STD_VIDEO_H265_CPB_CNT_LIST_SIZE 32
|
||||||
|
#define STD_VIDEO_H265_SUBLAYERS_LIST_SIZE 7
|
||||||
|
#define STD_VIDEO_H265_SCALING_LIST_4X4_NUM_LISTS 6
|
||||||
|
#define STD_VIDEO_H265_SCALING_LIST_4X4_NUM_ELEMENTS 16
|
||||||
|
#define STD_VIDEO_H265_SCALING_LIST_8X8_NUM_LISTS 6
|
||||||
|
#define STD_VIDEO_H265_SCALING_LIST_8X8_NUM_ELEMENTS 64
|
||||||
|
#define STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS 6
|
||||||
|
#define STD_VIDEO_H265_SCALING_LIST_16X16_NUM_ELEMENTS 64
|
||||||
|
#define STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS 2
|
||||||
|
#define STD_VIDEO_H265_SCALING_LIST_32X32_NUM_ELEMENTS 64
|
||||||
|
#define STD_VIDEO_H265_CHROMA_QP_OFFSET_LIST_SIZE 6
|
||||||
|
#define STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_COLS_LIST_SIZE 19
|
||||||
|
#define STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_ROWS_LIST_SIZE 21
|
||||||
|
#define STD_VIDEO_H265_PREDICTOR_PALETTE_COMPONENTS_LIST_SIZE 3
|
||||||
|
#define STD_VIDEO_H265_PREDICTOR_PALETTE_COMP_ENTRIES_LIST_SIZE 128
|
||||||
|
#define STD_VIDEO_H265_MAX_NUM_LIST_REF 15
|
||||||
|
#define STD_VIDEO_H265_MAX_CHROMA_PLANES 2
|
||||||
|
#define STD_VIDEO_H265_MAX_SHORT_TERM_REF_PIC_SETS 64
|
||||||
|
#define STD_VIDEO_H265_MAX_DPB_SIZE 16
|
||||||
|
#define STD_VIDEO_H265_MAX_LONG_TERM_REF_PICS_SPS 32
|
||||||
|
#define STD_VIDEO_H265_MAX_LONG_TERM_PICS 16
|
||||||
|
#define STD_VIDEO_H265_MAX_DELTA_POC 48
|
||||||
|
#define STD_VIDEO_H265_NO_REFERENCE_PICTURE 0xFF
|
||||||
|
|
||||||
|
typedef enum StdVideoH265ChromaFormatIdc {
|
||||||
|
STD_VIDEO_H265_CHROMA_FORMAT_IDC_MONOCHROME = 0,
|
||||||
|
STD_VIDEO_H265_CHROMA_FORMAT_IDC_420 = 1,
|
||||||
|
STD_VIDEO_H265_CHROMA_FORMAT_IDC_422 = 2,
|
||||||
|
STD_VIDEO_H265_CHROMA_FORMAT_IDC_444 = 3,
|
||||||
|
STD_VIDEO_H265_CHROMA_FORMAT_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H265_CHROMA_FORMAT_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH265ChromaFormatIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH265ProfileIdc {
|
||||||
|
STD_VIDEO_H265_PROFILE_IDC_MAIN = 1,
|
||||||
|
STD_VIDEO_H265_PROFILE_IDC_MAIN_10 = 2,
|
||||||
|
STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE = 3,
|
||||||
|
STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS = 4,
|
||||||
|
STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS = 9,
|
||||||
|
STD_VIDEO_H265_PROFILE_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H265_PROFILE_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH265ProfileIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH265LevelIdc {
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_1_0 = 0,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_2_0 = 1,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_2_1 = 2,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_3_0 = 3,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_3_1 = 4,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_4_0 = 5,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_4_1 = 6,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_5_0 = 7,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_5_1 = 8,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_5_2 = 9,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_6_0 = 10,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_6_1 = 11,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_6_2 = 12,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H265_LEVEL_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH265LevelIdc;
|
||||||
|
|
||||||
|
typedef enum StdVideoH265SliceType {
|
||||||
|
STD_VIDEO_H265_SLICE_TYPE_B = 0,
|
||||||
|
STD_VIDEO_H265_SLICE_TYPE_P = 1,
|
||||||
|
STD_VIDEO_H265_SLICE_TYPE_I = 2,
|
||||||
|
STD_VIDEO_H265_SLICE_TYPE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H265_SLICE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH265SliceType;
|
||||||
|
|
||||||
|
typedef enum StdVideoH265PictureType {
|
||||||
|
STD_VIDEO_H265_PICTURE_TYPE_P = 0,
|
||||||
|
STD_VIDEO_H265_PICTURE_TYPE_B = 1,
|
||||||
|
STD_VIDEO_H265_PICTURE_TYPE_I = 2,
|
||||||
|
STD_VIDEO_H265_PICTURE_TYPE_IDR = 3,
|
||||||
|
STD_VIDEO_H265_PICTURE_TYPE_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H265_PICTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH265PictureType;
|
||||||
|
|
||||||
|
typedef enum StdVideoH265AspectRatioIdc {
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_UNSPECIFIED = 0,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_SQUARE = 1,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_12_11 = 2,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_10_11 = 3,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_16_11 = 4,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_40_33 = 5,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_24_11 = 6,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_20_11 = 7,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_32_11 = 8,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_80_33 = 9,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_18_11 = 10,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_15_11 = 11,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_64_33 = 12,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_160_99 = 13,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_4_3 = 14,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_3_2 = 15,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_2_1 = 16,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_EXTENDED_SAR = 255,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_INVALID = 0x7FFFFFFF,
|
||||||
|
STD_VIDEO_H265_ASPECT_RATIO_IDC_MAX_ENUM = 0x7FFFFFFF
|
||||||
|
} StdVideoH265AspectRatioIdc;
|
||||||
|
typedef struct StdVideoH265DecPicBufMgr {
|
||||||
|
uint32_t max_latency_increase_plus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
|
||||||
|
uint8_t max_dec_pic_buffering_minus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
|
||||||
|
uint8_t max_num_reorder_pics[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
|
||||||
|
} StdVideoH265DecPicBufMgr;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265SubLayerHrdParameters {
|
||||||
|
uint32_t bit_rate_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
|
||||||
|
uint32_t cpb_size_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
|
||||||
|
uint32_t cpb_size_du_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
|
||||||
|
uint32_t bit_rate_du_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
|
||||||
|
uint32_t cbr_flag;
|
||||||
|
} StdVideoH265SubLayerHrdParameters;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265HrdFlags {
|
||||||
|
uint32_t nal_hrd_parameters_present_flag : 1;
|
||||||
|
uint32_t vcl_hrd_parameters_present_flag : 1;
|
||||||
|
uint32_t sub_pic_hrd_params_present_flag : 1;
|
||||||
|
uint32_t sub_pic_cpb_params_in_pic_timing_sei_flag : 1;
|
||||||
|
uint32_t fixed_pic_rate_general_flag : 8;
|
||||||
|
uint32_t fixed_pic_rate_within_cvs_flag : 8;
|
||||||
|
uint32_t low_delay_hrd_flag : 8;
|
||||||
|
} StdVideoH265HrdFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265HrdParameters {
|
||||||
|
StdVideoH265HrdFlags flags;
|
||||||
|
uint8_t tick_divisor_minus2;
|
||||||
|
uint8_t du_cpb_removal_delay_increment_length_minus1;
|
||||||
|
uint8_t dpb_output_delay_du_length_minus1;
|
||||||
|
uint8_t bit_rate_scale;
|
||||||
|
uint8_t cpb_size_scale;
|
||||||
|
uint8_t cpb_size_du_scale;
|
||||||
|
uint8_t initial_cpb_removal_delay_length_minus1;
|
||||||
|
uint8_t au_cpb_removal_delay_length_minus1;
|
||||||
|
uint8_t dpb_output_delay_length_minus1;
|
||||||
|
uint8_t cpb_cnt_minus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
|
||||||
|
uint16_t elemental_duration_in_tc_minus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
|
||||||
|
uint16_t reserved[3];
|
||||||
|
const StdVideoH265SubLayerHrdParameters* pSubLayerHrdParametersNal;
|
||||||
|
const StdVideoH265SubLayerHrdParameters* pSubLayerHrdParametersVcl;
|
||||||
|
} StdVideoH265HrdParameters;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265VpsFlags {
|
||||||
|
uint32_t vps_temporal_id_nesting_flag : 1;
|
||||||
|
uint32_t vps_sub_layer_ordering_info_present_flag : 1;
|
||||||
|
uint32_t vps_timing_info_present_flag : 1;
|
||||||
|
uint32_t vps_poc_proportional_to_timing_flag : 1;
|
||||||
|
} StdVideoH265VpsFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265ProfileTierLevelFlags {
|
||||||
|
uint32_t general_tier_flag : 1;
|
||||||
|
uint32_t general_progressive_source_flag : 1;
|
||||||
|
uint32_t general_interlaced_source_flag : 1;
|
||||||
|
uint32_t general_non_packed_constraint_flag : 1;
|
||||||
|
uint32_t general_frame_only_constraint_flag : 1;
|
||||||
|
} StdVideoH265ProfileTierLevelFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265ProfileTierLevel {
|
||||||
|
StdVideoH265ProfileTierLevelFlags flags;
|
||||||
|
StdVideoH265ProfileIdc general_profile_idc;
|
||||||
|
StdVideoH265LevelIdc general_level_idc;
|
||||||
|
} StdVideoH265ProfileTierLevel;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265VideoParameterSet {
|
||||||
|
StdVideoH265VpsFlags flags;
|
||||||
|
uint8_t vps_video_parameter_set_id;
|
||||||
|
uint8_t vps_max_sub_layers_minus1;
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint32_t vps_num_units_in_tick;
|
||||||
|
uint32_t vps_time_scale;
|
||||||
|
uint32_t vps_num_ticks_poc_diff_one_minus1;
|
||||||
|
uint32_t reserved3;
|
||||||
|
const StdVideoH265DecPicBufMgr* pDecPicBufMgr;
|
||||||
|
const StdVideoH265HrdParameters* pHrdParameters;
|
||||||
|
const StdVideoH265ProfileTierLevel* pProfileTierLevel;
|
||||||
|
} StdVideoH265VideoParameterSet;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265ScalingLists {
|
||||||
|
uint8_t ScalingList4x4[STD_VIDEO_H265_SCALING_LIST_4X4_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_4X4_NUM_ELEMENTS];
|
||||||
|
uint8_t ScalingList8x8[STD_VIDEO_H265_SCALING_LIST_8X8_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_8X8_NUM_ELEMENTS];
|
||||||
|
uint8_t ScalingList16x16[STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_16X16_NUM_ELEMENTS];
|
||||||
|
uint8_t ScalingList32x32[STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_32X32_NUM_ELEMENTS];
|
||||||
|
uint8_t ScalingListDCCoef16x16[STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS];
|
||||||
|
uint8_t ScalingListDCCoef32x32[STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS];
|
||||||
|
} StdVideoH265ScalingLists;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265SpsVuiFlags {
|
||||||
|
uint32_t aspect_ratio_info_present_flag : 1;
|
||||||
|
uint32_t overscan_info_present_flag : 1;
|
||||||
|
uint32_t overscan_appropriate_flag : 1;
|
||||||
|
uint32_t video_signal_type_present_flag : 1;
|
||||||
|
uint32_t video_full_range_flag : 1;
|
||||||
|
uint32_t colour_description_present_flag : 1;
|
||||||
|
uint32_t chroma_loc_info_present_flag : 1;
|
||||||
|
uint32_t neutral_chroma_indication_flag : 1;
|
||||||
|
uint32_t field_seq_flag : 1;
|
||||||
|
uint32_t frame_field_info_present_flag : 1;
|
||||||
|
uint32_t default_display_window_flag : 1;
|
||||||
|
uint32_t vui_timing_info_present_flag : 1;
|
||||||
|
uint32_t vui_poc_proportional_to_timing_flag : 1;
|
||||||
|
uint32_t vui_hrd_parameters_present_flag : 1;
|
||||||
|
uint32_t bitstream_restriction_flag : 1;
|
||||||
|
uint32_t tiles_fixed_structure_flag : 1;
|
||||||
|
uint32_t motion_vectors_over_pic_boundaries_flag : 1;
|
||||||
|
uint32_t restricted_ref_pic_lists_flag : 1;
|
||||||
|
} StdVideoH265SpsVuiFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265SequenceParameterSetVui {
|
||||||
|
StdVideoH265SpsVuiFlags flags;
|
||||||
|
StdVideoH265AspectRatioIdc aspect_ratio_idc;
|
||||||
|
uint16_t sar_width;
|
||||||
|
uint16_t sar_height;
|
||||||
|
uint8_t video_format;
|
||||||
|
uint8_t colour_primaries;
|
||||||
|
uint8_t transfer_characteristics;
|
||||||
|
uint8_t matrix_coeffs;
|
||||||
|
uint8_t chroma_sample_loc_type_top_field;
|
||||||
|
uint8_t chroma_sample_loc_type_bottom_field;
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint16_t def_disp_win_left_offset;
|
||||||
|
uint16_t def_disp_win_right_offset;
|
||||||
|
uint16_t def_disp_win_top_offset;
|
||||||
|
uint16_t def_disp_win_bottom_offset;
|
||||||
|
uint32_t vui_num_units_in_tick;
|
||||||
|
uint32_t vui_time_scale;
|
||||||
|
uint32_t vui_num_ticks_poc_diff_one_minus1;
|
||||||
|
uint16_t min_spatial_segmentation_idc;
|
||||||
|
uint16_t reserved3;
|
||||||
|
uint8_t max_bytes_per_pic_denom;
|
||||||
|
uint8_t max_bits_per_min_cu_denom;
|
||||||
|
uint8_t log2_max_mv_length_horizontal;
|
||||||
|
uint8_t log2_max_mv_length_vertical;
|
||||||
|
const StdVideoH265HrdParameters* pHrdParameters;
|
||||||
|
} StdVideoH265SequenceParameterSetVui;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265PredictorPaletteEntries {
|
||||||
|
uint16_t PredictorPaletteEntries[STD_VIDEO_H265_PREDICTOR_PALETTE_COMPONENTS_LIST_SIZE][STD_VIDEO_H265_PREDICTOR_PALETTE_COMP_ENTRIES_LIST_SIZE];
|
||||||
|
} StdVideoH265PredictorPaletteEntries;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265SpsFlags {
|
||||||
|
uint32_t sps_temporal_id_nesting_flag : 1;
|
||||||
|
uint32_t separate_colour_plane_flag : 1;
|
||||||
|
uint32_t conformance_window_flag : 1;
|
||||||
|
uint32_t sps_sub_layer_ordering_info_present_flag : 1;
|
||||||
|
uint32_t scaling_list_enabled_flag : 1;
|
||||||
|
uint32_t sps_scaling_list_data_present_flag : 1;
|
||||||
|
uint32_t amp_enabled_flag : 1;
|
||||||
|
uint32_t sample_adaptive_offset_enabled_flag : 1;
|
||||||
|
uint32_t pcm_enabled_flag : 1;
|
||||||
|
uint32_t pcm_loop_filter_disabled_flag : 1;
|
||||||
|
uint32_t long_term_ref_pics_present_flag : 1;
|
||||||
|
uint32_t sps_temporal_mvp_enabled_flag : 1;
|
||||||
|
uint32_t strong_intra_smoothing_enabled_flag : 1;
|
||||||
|
uint32_t vui_parameters_present_flag : 1;
|
||||||
|
uint32_t sps_extension_present_flag : 1;
|
||||||
|
uint32_t sps_range_extension_flag : 1;
|
||||||
|
uint32_t transform_skip_rotation_enabled_flag : 1;
|
||||||
|
uint32_t transform_skip_context_enabled_flag : 1;
|
||||||
|
uint32_t implicit_rdpcm_enabled_flag : 1;
|
||||||
|
uint32_t explicit_rdpcm_enabled_flag : 1;
|
||||||
|
uint32_t extended_precision_processing_flag : 1;
|
||||||
|
uint32_t intra_smoothing_disabled_flag : 1;
|
||||||
|
uint32_t high_precision_offsets_enabled_flag : 1;
|
||||||
|
uint32_t persistent_rice_adaptation_enabled_flag : 1;
|
||||||
|
uint32_t cabac_bypass_alignment_enabled_flag : 1;
|
||||||
|
uint32_t sps_scc_extension_flag : 1;
|
||||||
|
uint32_t sps_curr_pic_ref_enabled_flag : 1;
|
||||||
|
uint32_t palette_mode_enabled_flag : 1;
|
||||||
|
uint32_t sps_palette_predictor_initializers_present_flag : 1;
|
||||||
|
uint32_t intra_boundary_filtering_disabled_flag : 1;
|
||||||
|
} StdVideoH265SpsFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265ShortTermRefPicSetFlags {
|
||||||
|
uint32_t inter_ref_pic_set_prediction_flag : 1;
|
||||||
|
uint32_t delta_rps_sign : 1;
|
||||||
|
} StdVideoH265ShortTermRefPicSetFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265ShortTermRefPicSet {
|
||||||
|
StdVideoH265ShortTermRefPicSetFlags flags;
|
||||||
|
uint32_t delta_idx_minus1;
|
||||||
|
uint16_t use_delta_flag;
|
||||||
|
uint16_t abs_delta_rps_minus1;
|
||||||
|
uint16_t used_by_curr_pic_flag;
|
||||||
|
uint16_t used_by_curr_pic_s0_flag;
|
||||||
|
uint16_t used_by_curr_pic_s1_flag;
|
||||||
|
uint16_t reserved1;
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint8_t reserved3;
|
||||||
|
uint8_t num_negative_pics;
|
||||||
|
uint8_t num_positive_pics;
|
||||||
|
uint16_t delta_poc_s0_minus1[STD_VIDEO_H265_MAX_DPB_SIZE];
|
||||||
|
uint16_t delta_poc_s1_minus1[STD_VIDEO_H265_MAX_DPB_SIZE];
|
||||||
|
} StdVideoH265ShortTermRefPicSet;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265LongTermRefPicsSps {
|
||||||
|
uint32_t used_by_curr_pic_lt_sps_flag;
|
||||||
|
uint32_t lt_ref_pic_poc_lsb_sps[STD_VIDEO_H265_MAX_LONG_TERM_REF_PICS_SPS];
|
||||||
|
} StdVideoH265LongTermRefPicsSps;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265SequenceParameterSet {
|
||||||
|
StdVideoH265SpsFlags flags;
|
||||||
|
StdVideoH265ChromaFormatIdc chroma_format_idc;
|
||||||
|
uint32_t pic_width_in_luma_samples;
|
||||||
|
uint32_t pic_height_in_luma_samples;
|
||||||
|
uint8_t sps_video_parameter_set_id;
|
||||||
|
uint8_t sps_max_sub_layers_minus1;
|
||||||
|
uint8_t sps_seq_parameter_set_id;
|
||||||
|
uint8_t bit_depth_luma_minus8;
|
||||||
|
uint8_t bit_depth_chroma_minus8;
|
||||||
|
uint8_t log2_max_pic_order_cnt_lsb_minus4;
|
||||||
|
uint8_t log2_min_luma_coding_block_size_minus3;
|
||||||
|
uint8_t log2_diff_max_min_luma_coding_block_size;
|
||||||
|
uint8_t log2_min_luma_transform_block_size_minus2;
|
||||||
|
uint8_t log2_diff_max_min_luma_transform_block_size;
|
||||||
|
uint8_t max_transform_hierarchy_depth_inter;
|
||||||
|
uint8_t max_transform_hierarchy_depth_intra;
|
||||||
|
uint8_t num_short_term_ref_pic_sets;
|
||||||
|
uint8_t num_long_term_ref_pics_sps;
|
||||||
|
uint8_t pcm_sample_bit_depth_luma_minus1;
|
||||||
|
uint8_t pcm_sample_bit_depth_chroma_minus1;
|
||||||
|
uint8_t log2_min_pcm_luma_coding_block_size_minus3;
|
||||||
|
uint8_t log2_diff_max_min_pcm_luma_coding_block_size;
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint8_t palette_max_size;
|
||||||
|
uint8_t delta_palette_max_predictor_size;
|
||||||
|
uint8_t motion_vector_resolution_control_idc;
|
||||||
|
uint8_t sps_num_palette_predictor_initializers_minus1;
|
||||||
|
uint32_t conf_win_left_offset;
|
||||||
|
uint32_t conf_win_right_offset;
|
||||||
|
uint32_t conf_win_top_offset;
|
||||||
|
uint32_t conf_win_bottom_offset;
|
||||||
|
const StdVideoH265ProfileTierLevel* pProfileTierLevel;
|
||||||
|
const StdVideoH265DecPicBufMgr* pDecPicBufMgr;
|
||||||
|
const StdVideoH265ScalingLists* pScalingLists;
|
||||||
|
const StdVideoH265ShortTermRefPicSet* pShortTermRefPicSet;
|
||||||
|
const StdVideoH265LongTermRefPicsSps* pLongTermRefPicsSps;
|
||||||
|
const StdVideoH265SequenceParameterSetVui* pSequenceParameterSetVui;
|
||||||
|
const StdVideoH265PredictorPaletteEntries* pPredictorPaletteEntries;
|
||||||
|
} StdVideoH265SequenceParameterSet;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265PpsFlags {
|
||||||
|
uint32_t dependent_slice_segments_enabled_flag : 1;
|
||||||
|
uint32_t output_flag_present_flag : 1;
|
||||||
|
uint32_t sign_data_hiding_enabled_flag : 1;
|
||||||
|
uint32_t cabac_init_present_flag : 1;
|
||||||
|
uint32_t constrained_intra_pred_flag : 1;
|
||||||
|
uint32_t transform_skip_enabled_flag : 1;
|
||||||
|
uint32_t cu_qp_delta_enabled_flag : 1;
|
||||||
|
uint32_t pps_slice_chroma_qp_offsets_present_flag : 1;
|
||||||
|
uint32_t weighted_pred_flag : 1;
|
||||||
|
uint32_t weighted_bipred_flag : 1;
|
||||||
|
uint32_t transquant_bypass_enabled_flag : 1;
|
||||||
|
uint32_t tiles_enabled_flag : 1;
|
||||||
|
uint32_t entropy_coding_sync_enabled_flag : 1;
|
||||||
|
uint32_t uniform_spacing_flag : 1;
|
||||||
|
uint32_t loop_filter_across_tiles_enabled_flag : 1;
|
||||||
|
uint32_t pps_loop_filter_across_slices_enabled_flag : 1;
|
||||||
|
uint32_t deblocking_filter_control_present_flag : 1;
|
||||||
|
uint32_t deblocking_filter_override_enabled_flag : 1;
|
||||||
|
uint32_t pps_deblocking_filter_disabled_flag : 1;
|
||||||
|
uint32_t pps_scaling_list_data_present_flag : 1;
|
||||||
|
uint32_t lists_modification_present_flag : 1;
|
||||||
|
uint32_t slice_segment_header_extension_present_flag : 1;
|
||||||
|
uint32_t pps_extension_present_flag : 1;
|
||||||
|
uint32_t cross_component_prediction_enabled_flag : 1;
|
||||||
|
uint32_t chroma_qp_offset_list_enabled_flag : 1;
|
||||||
|
uint32_t pps_curr_pic_ref_enabled_flag : 1;
|
||||||
|
uint32_t residual_adaptive_colour_transform_enabled_flag : 1;
|
||||||
|
uint32_t pps_slice_act_qp_offsets_present_flag : 1;
|
||||||
|
uint32_t pps_palette_predictor_initializers_present_flag : 1;
|
||||||
|
uint32_t monochrome_palette_flag : 1;
|
||||||
|
uint32_t pps_range_extension_flag : 1;
|
||||||
|
} StdVideoH265PpsFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoH265PictureParameterSet {
|
||||||
|
StdVideoH265PpsFlags flags;
|
||||||
|
uint8_t pps_pic_parameter_set_id;
|
||||||
|
uint8_t pps_seq_parameter_set_id;
|
||||||
|
uint8_t sps_video_parameter_set_id;
|
||||||
|
uint8_t num_extra_slice_header_bits;
|
||||||
|
uint8_t num_ref_idx_l0_default_active_minus1;
|
||||||
|
uint8_t num_ref_idx_l1_default_active_minus1;
|
||||||
|
int8_t init_qp_minus26;
|
||||||
|
uint8_t diff_cu_qp_delta_depth;
|
||||||
|
int8_t pps_cb_qp_offset;
|
||||||
|
int8_t pps_cr_qp_offset;
|
||||||
|
int8_t pps_beta_offset_div2;
|
||||||
|
int8_t pps_tc_offset_div2;
|
||||||
|
uint8_t log2_parallel_merge_level_minus2;
|
||||||
|
uint8_t log2_max_transform_skip_block_size_minus2;
|
||||||
|
uint8_t diff_cu_chroma_qp_offset_depth;
|
||||||
|
uint8_t chroma_qp_offset_list_len_minus1;
|
||||||
|
int8_t cb_qp_offset_list[STD_VIDEO_H265_CHROMA_QP_OFFSET_LIST_SIZE];
|
||||||
|
int8_t cr_qp_offset_list[STD_VIDEO_H265_CHROMA_QP_OFFSET_LIST_SIZE];
|
||||||
|
uint8_t log2_sao_offset_scale_luma;
|
||||||
|
uint8_t log2_sao_offset_scale_chroma;
|
||||||
|
int8_t pps_act_y_qp_offset_plus5;
|
||||||
|
int8_t pps_act_cb_qp_offset_plus5;
|
||||||
|
int8_t pps_act_cr_qp_offset_plus3;
|
||||||
|
uint8_t pps_num_palette_predictor_initializers;
|
||||||
|
uint8_t luma_bit_depth_entry_minus8;
|
||||||
|
uint8_t chroma_bit_depth_entry_minus8;
|
||||||
|
uint8_t num_tile_columns_minus1;
|
||||||
|
uint8_t num_tile_rows_minus1;
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint16_t column_width_minus1[STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_COLS_LIST_SIZE];
|
||||||
|
uint16_t row_height_minus1[STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_ROWS_LIST_SIZE];
|
||||||
|
uint32_t reserved3;
|
||||||
|
const StdVideoH265ScalingLists* pScalingLists;
|
||||||
|
const StdVideoH265PredictorPaletteEntries* pPredictorPaletteEntries;
|
||||||
|
} StdVideoH265PictureParameterSet;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODEC_H265STD_DECODE_H_
|
||||||
|
#define VULKAN_VIDEO_CODEC_H265STD_DECODE_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codec_h265std_decode is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codec_h265std_decode 1
|
||||||
|
#include "vulkan_video_codec_h265std.h"
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_API_VERSION_1_0_0
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_h265_decode"
|
||||||
|
#define STD_VIDEO_DECODE_H265_REF_PIC_SET_LIST_SIZE 8
|
||||||
|
typedef struct StdVideoDecodeH265PictureInfoFlags {
|
||||||
|
uint32_t IrapPicFlag : 1;
|
||||||
|
uint32_t IdrPicFlag : 1;
|
||||||
|
uint32_t IsReference : 1;
|
||||||
|
uint32_t short_term_ref_pic_set_sps_flag : 1;
|
||||||
|
} StdVideoDecodeH265PictureInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoDecodeH265PictureInfo {
|
||||||
|
StdVideoDecodeH265PictureInfoFlags flags;
|
||||||
|
uint8_t sps_video_parameter_set_id;
|
||||||
|
uint8_t pps_seq_parameter_set_id;
|
||||||
|
uint8_t pps_pic_parameter_set_id;
|
||||||
|
uint8_t NumDeltaPocsOfRefRpsIdx;
|
||||||
|
int32_t PicOrderCntVal;
|
||||||
|
uint16_t NumBitsForSTRefPicSetInSlice;
|
||||||
|
uint16_t reserved;
|
||||||
|
uint8_t RefPicSetStCurrBefore[STD_VIDEO_DECODE_H265_REF_PIC_SET_LIST_SIZE];
|
||||||
|
uint8_t RefPicSetStCurrAfter[STD_VIDEO_DECODE_H265_REF_PIC_SET_LIST_SIZE];
|
||||||
|
uint8_t RefPicSetLtCurr[STD_VIDEO_DECODE_H265_REF_PIC_SET_LIST_SIZE];
|
||||||
|
} StdVideoDecodeH265PictureInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoDecodeH265ReferenceInfoFlags {
|
||||||
|
uint32_t used_for_long_term_reference : 1;
|
||||||
|
uint32_t unused_for_reference : 1;
|
||||||
|
} StdVideoDecodeH265ReferenceInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoDecodeH265ReferenceInfo {
|
||||||
|
StdVideoDecodeH265ReferenceInfoFlags flags;
|
||||||
|
int32_t PicOrderCntVal;
|
||||||
|
} StdVideoDecodeH265ReferenceInfo;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODEC_H265STD_ENCODE_H_
|
||||||
|
#define VULKAN_VIDEO_CODEC_H265STD_ENCODE_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codec_h265std_encode is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codec_h265std_encode 1
|
||||||
|
#include "vulkan_video_codec_h265std.h"
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
|
||||||
|
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_API_VERSION_1_0_0
|
||||||
|
#define VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_h265_encode"
|
||||||
|
typedef struct StdVideoEncodeH265WeightTableFlags {
|
||||||
|
uint16_t luma_weight_l0_flag;
|
||||||
|
uint16_t chroma_weight_l0_flag;
|
||||||
|
uint16_t luma_weight_l1_flag;
|
||||||
|
uint16_t chroma_weight_l1_flag;
|
||||||
|
} StdVideoEncodeH265WeightTableFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265WeightTable {
|
||||||
|
StdVideoEncodeH265WeightTableFlags flags;
|
||||||
|
uint8_t luma_log2_weight_denom;
|
||||||
|
int8_t delta_chroma_log2_weight_denom;
|
||||||
|
int8_t delta_luma_weight_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF];
|
||||||
|
int8_t luma_offset_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF];
|
||||||
|
int8_t delta_chroma_weight_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF][STD_VIDEO_H265_MAX_CHROMA_PLANES];
|
||||||
|
int8_t delta_chroma_offset_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF][STD_VIDEO_H265_MAX_CHROMA_PLANES];
|
||||||
|
int8_t delta_luma_weight_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF];
|
||||||
|
int8_t luma_offset_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF];
|
||||||
|
int8_t delta_chroma_weight_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF][STD_VIDEO_H265_MAX_CHROMA_PLANES];
|
||||||
|
int8_t delta_chroma_offset_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF][STD_VIDEO_H265_MAX_CHROMA_PLANES];
|
||||||
|
} StdVideoEncodeH265WeightTable;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265SliceSegmentHeaderFlags {
|
||||||
|
uint32_t first_slice_segment_in_pic_flag : 1;
|
||||||
|
uint32_t dependent_slice_segment_flag : 1;
|
||||||
|
uint32_t slice_sao_luma_flag : 1;
|
||||||
|
uint32_t slice_sao_chroma_flag : 1;
|
||||||
|
uint32_t num_ref_idx_active_override_flag : 1;
|
||||||
|
uint32_t mvd_l1_zero_flag : 1;
|
||||||
|
uint32_t cabac_init_flag : 1;
|
||||||
|
uint32_t cu_chroma_qp_offset_enabled_flag : 1;
|
||||||
|
uint32_t deblocking_filter_override_flag : 1;
|
||||||
|
uint32_t slice_deblocking_filter_disabled_flag : 1;
|
||||||
|
uint32_t collocated_from_l0_flag : 1;
|
||||||
|
uint32_t slice_loop_filter_across_slices_enabled_flag : 1;
|
||||||
|
uint32_t reserved : 20;
|
||||||
|
} StdVideoEncodeH265SliceSegmentHeaderFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265SliceSegmentHeader {
|
||||||
|
StdVideoEncodeH265SliceSegmentHeaderFlags flags;
|
||||||
|
StdVideoH265SliceType slice_type;
|
||||||
|
uint32_t slice_segment_address;
|
||||||
|
uint8_t collocated_ref_idx;
|
||||||
|
uint8_t MaxNumMergeCand;
|
||||||
|
int8_t slice_cb_qp_offset;
|
||||||
|
int8_t slice_cr_qp_offset;
|
||||||
|
int8_t slice_beta_offset_div2;
|
||||||
|
int8_t slice_tc_offset_div2;
|
||||||
|
int8_t slice_act_y_qp_offset;
|
||||||
|
int8_t slice_act_cb_qp_offset;
|
||||||
|
int8_t slice_act_cr_qp_offset;
|
||||||
|
int8_t slice_qp_delta;
|
||||||
|
uint16_t reserved1;
|
||||||
|
const StdVideoEncodeH265WeightTable* pWeightTable;
|
||||||
|
} StdVideoEncodeH265SliceSegmentHeader;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265ReferenceListsInfoFlags {
|
||||||
|
uint32_t ref_pic_list_modification_flag_l0 : 1;
|
||||||
|
uint32_t ref_pic_list_modification_flag_l1 : 1;
|
||||||
|
uint32_t reserved : 30;
|
||||||
|
} StdVideoEncodeH265ReferenceListsInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265ReferenceListsInfo {
|
||||||
|
StdVideoEncodeH265ReferenceListsInfoFlags flags;
|
||||||
|
uint8_t num_ref_idx_l0_active_minus1;
|
||||||
|
uint8_t num_ref_idx_l1_active_minus1;
|
||||||
|
uint8_t RefPicList0[STD_VIDEO_H265_MAX_NUM_LIST_REF];
|
||||||
|
uint8_t RefPicList1[STD_VIDEO_H265_MAX_NUM_LIST_REF];
|
||||||
|
uint8_t list_entry_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF];
|
||||||
|
uint8_t list_entry_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF];
|
||||||
|
} StdVideoEncodeH265ReferenceListsInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265PictureInfoFlags {
|
||||||
|
uint32_t is_reference : 1;
|
||||||
|
uint32_t IrapPicFlag : 1;
|
||||||
|
uint32_t used_for_long_term_reference : 1;
|
||||||
|
uint32_t discardable_flag : 1;
|
||||||
|
uint32_t cross_layer_bla_flag : 1;
|
||||||
|
uint32_t pic_output_flag : 1;
|
||||||
|
uint32_t no_output_of_prior_pics_flag : 1;
|
||||||
|
uint32_t short_term_ref_pic_set_sps_flag : 1;
|
||||||
|
uint32_t slice_temporal_mvp_enabled_flag : 1;
|
||||||
|
uint32_t reserved : 23;
|
||||||
|
} StdVideoEncodeH265PictureInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265LongTermRefPics {
|
||||||
|
uint8_t num_long_term_sps;
|
||||||
|
uint8_t num_long_term_pics;
|
||||||
|
uint8_t lt_idx_sps[STD_VIDEO_H265_MAX_LONG_TERM_REF_PICS_SPS];
|
||||||
|
uint8_t poc_lsb_lt[STD_VIDEO_H265_MAX_LONG_TERM_PICS];
|
||||||
|
uint16_t used_by_curr_pic_lt_flag;
|
||||||
|
uint8_t delta_poc_msb_present_flag[STD_VIDEO_H265_MAX_DELTA_POC];
|
||||||
|
uint8_t delta_poc_msb_cycle_lt[STD_VIDEO_H265_MAX_DELTA_POC];
|
||||||
|
} StdVideoEncodeH265LongTermRefPics;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265PictureInfo {
|
||||||
|
StdVideoEncodeH265PictureInfoFlags flags;
|
||||||
|
StdVideoH265PictureType pic_type;
|
||||||
|
uint8_t sps_video_parameter_set_id;
|
||||||
|
uint8_t pps_seq_parameter_set_id;
|
||||||
|
uint8_t pps_pic_parameter_set_id;
|
||||||
|
uint8_t short_term_ref_pic_set_idx;
|
||||||
|
int32_t PicOrderCntVal;
|
||||||
|
uint8_t TemporalId;
|
||||||
|
uint8_t reserved1[7];
|
||||||
|
const StdVideoEncodeH265ReferenceListsInfo* pRefLists;
|
||||||
|
const StdVideoH265ShortTermRefPicSet* pShortTermRefPicSet;
|
||||||
|
const StdVideoEncodeH265LongTermRefPics* pLongTermRefPics;
|
||||||
|
} StdVideoEncodeH265PictureInfo;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265ReferenceInfoFlags {
|
||||||
|
uint32_t used_for_long_term_reference : 1;
|
||||||
|
uint32_t unused_for_reference : 1;
|
||||||
|
uint32_t reserved : 30;
|
||||||
|
} StdVideoEncodeH265ReferenceInfoFlags;
|
||||||
|
|
||||||
|
typedef struct StdVideoEncodeH265ReferenceInfo {
|
||||||
|
StdVideoEncodeH265ReferenceInfoFlags flags;
|
||||||
|
StdVideoH265PictureType pic_type;
|
||||||
|
int32_t PicOrderCntVal;
|
||||||
|
uint8_t TemporalId;
|
||||||
|
} StdVideoEncodeH265ReferenceInfo;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef VULKAN_VIDEO_CODECS_COMMON_H_
|
||||||
|
#define VULKAN_VIDEO_CODECS_COMMON_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// vulkan_video_codecs_common is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define vulkan_video_codecs_common 1
|
||||||
|
#if !defined(VK_NO_STDINT_H)
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define VK_MAKE_VIDEO_STD_VERSION(major, minor, patch) \
|
||||||
|
((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch)))
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+244
@@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015-2023 The Khronos Group Inc.
|
||||||
|
* Copyright 2015-2023 Valve Corporation
|
||||||
|
* Copyright 2015-2023 LunarG, Inc.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "vulkan.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// Loader-ICD version negotiation API. Versions add the following features:
|
||||||
|
// Version 0 - Initial. Doesn't support vk_icdGetInstanceProcAddr
|
||||||
|
// or vk_icdNegotiateLoaderICDInterfaceVersion.
|
||||||
|
// Version 1 - Add support for vk_icdGetInstanceProcAddr.
|
||||||
|
// Version 2 - Add Loader/ICD Interface version negotiation
|
||||||
|
// via vk_icdNegotiateLoaderICDInterfaceVersion.
|
||||||
|
// Version 3 - Add ICD creation/destruction of KHR_surface objects.
|
||||||
|
// Version 4 - Add unknown physical device extension querying via
|
||||||
|
// vk_icdGetPhysicalDeviceProcAddr.
|
||||||
|
// Version 5 - Tells ICDs that the loader is now paying attention to the
|
||||||
|
// application version of Vulkan passed into the ApplicationInfo
|
||||||
|
// structure during vkCreateInstance. This will tell the ICD
|
||||||
|
// that if the loader is older, it should automatically fail a
|
||||||
|
// call for any API version > 1.0. Otherwise, the loader will
|
||||||
|
// manually determine if it can support the expected version.
|
||||||
|
// Version 6 - Add support for vk_icdEnumerateAdapterPhysicalDevices.
|
||||||
|
// Version 7 - If an ICD supports any of the following functions, they must be
|
||||||
|
// queryable with vk_icdGetInstanceProcAddr:
|
||||||
|
// vk_icdNegotiateLoaderICDInterfaceVersion
|
||||||
|
// vk_icdGetPhysicalDeviceProcAddr
|
||||||
|
// vk_icdEnumerateAdapterPhysicalDevices (Windows only)
|
||||||
|
// In addition, these functions no longer need to be exported directly.
|
||||||
|
// This version allows drivers provided through the extension
|
||||||
|
// VK_LUNARG_direct_driver_loading be able to support the entire
|
||||||
|
// Driver-Loader interface.
|
||||||
|
|
||||||
|
#define CURRENT_LOADER_ICD_INTERFACE_VERSION 7
|
||||||
|
#define MIN_SUPPORTED_LOADER_ICD_INTERFACE_VERSION 0
|
||||||
|
#define MIN_PHYS_DEV_EXTENSION_ICD_INTERFACE_VERSION 4
|
||||||
|
|
||||||
|
// Old typedefs that don't follow a proper naming convention but are preserved for compatibility
|
||||||
|
typedef VkResult(VKAPI_PTR *PFN_vkNegotiateLoaderICDInterfaceVersion)(uint32_t *pVersion);
|
||||||
|
// This is defined in vk_layer.h which will be found by the loader, but if an ICD is building against this
|
||||||
|
// file directly, it won't be found.
|
||||||
|
#ifndef PFN_GetPhysicalDeviceProcAddr
|
||||||
|
typedef PFN_vkVoidFunction(VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char *pName);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Typedefs for loader/ICD interface
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vk_icdNegotiateLoaderICDInterfaceVersion)(uint32_t* pVersion);
|
||||||
|
typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vk_icdGetInstanceProcAddr)(VkInstance instance, const char* pName);
|
||||||
|
typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vk_icdGetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName);
|
||||||
|
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vk_icdEnumerateAdapterPhysicalDevices)(VkInstance instance, LUID adapterLUID,
|
||||||
|
uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Prototypes for loader/ICD interface
|
||||||
|
#if !defined(VK_NO_PROTOTYPES)
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pVersion);
|
||||||
|
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetInstanceProcAddr(VkInstance instance, const char* pName);
|
||||||
|
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char* pName);
|
||||||
|
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vk_icdEnumerateAdapterPhysicalDevices(VkInstance instance, LUID adapterLUID,
|
||||||
|
uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
|
||||||
|
#endif
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The ICD must reserve space for a pointer for the loader's dispatch
|
||||||
|
* table, at the start of <each object>.
|
||||||
|
* The ICD must initialize this variable using the SET_LOADER_MAGIC_VALUE macro.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ICD_LOADER_MAGIC 0x01CDC0DE
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
uintptr_t loaderMagic;
|
||||||
|
void *loaderData;
|
||||||
|
} VK_LOADER_DATA;
|
||||||
|
|
||||||
|
static inline void set_loader_magic_value(void *pNewObject) {
|
||||||
|
VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject;
|
||||||
|
loader_info->loaderMagic = ICD_LOADER_MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool valid_loader_magic_value(void *pNewObject) {
|
||||||
|
const VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject;
|
||||||
|
return (loader_info->loaderMagic & 0xffffffff) == ICD_LOADER_MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows and Linux ICDs will treat VkSurfaceKHR as a pointer to a struct that
|
||||||
|
* contains the platform-specific connection and surface information.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
VK_ICD_WSI_PLATFORM_MIR,
|
||||||
|
VK_ICD_WSI_PLATFORM_WAYLAND,
|
||||||
|
VK_ICD_WSI_PLATFORM_WIN32,
|
||||||
|
VK_ICD_WSI_PLATFORM_XCB,
|
||||||
|
VK_ICD_WSI_PLATFORM_XLIB,
|
||||||
|
VK_ICD_WSI_PLATFORM_ANDROID,
|
||||||
|
VK_ICD_WSI_PLATFORM_MACOS,
|
||||||
|
VK_ICD_WSI_PLATFORM_IOS,
|
||||||
|
VK_ICD_WSI_PLATFORM_DISPLAY,
|
||||||
|
VK_ICD_WSI_PLATFORM_HEADLESS,
|
||||||
|
VK_ICD_WSI_PLATFORM_METAL,
|
||||||
|
VK_ICD_WSI_PLATFORM_DIRECTFB,
|
||||||
|
VK_ICD_WSI_PLATFORM_VI,
|
||||||
|
VK_ICD_WSI_PLATFORM_GGP,
|
||||||
|
VK_ICD_WSI_PLATFORM_SCREEN,
|
||||||
|
VK_ICD_WSI_PLATFORM_FUCHSIA,
|
||||||
|
} VkIcdWsiPlatform;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
VkIcdWsiPlatform platform;
|
||||||
|
} VkIcdSurfaceBase;
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_MIR_KHR
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
MirConnection *connection;
|
||||||
|
MirSurface *mirSurface;
|
||||||
|
} VkIcdSurfaceMir;
|
||||||
|
#endif // VK_USE_PLATFORM_MIR_KHR
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
struct wl_display *display;
|
||||||
|
struct wl_surface *surface;
|
||||||
|
} VkIcdSurfaceWayland;
|
||||||
|
#endif // VK_USE_PLATFORM_WAYLAND_KHR
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
HINSTANCE hinstance;
|
||||||
|
HWND hwnd;
|
||||||
|
} VkIcdSurfaceWin32;
|
||||||
|
#endif // VK_USE_PLATFORM_WIN32_KHR
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
xcb_connection_t *connection;
|
||||||
|
xcb_window_t window;
|
||||||
|
} VkIcdSurfaceXcb;
|
||||||
|
#endif // VK_USE_PLATFORM_XCB_KHR
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
Display *dpy;
|
||||||
|
Window window;
|
||||||
|
} VkIcdSurfaceXlib;
|
||||||
|
#endif // VK_USE_PLATFORM_XLIB_KHR
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_DIRECTFB_EXT
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
IDirectFB *dfb;
|
||||||
|
IDirectFBSurface *surface;
|
||||||
|
} VkIcdSurfaceDirectFB;
|
||||||
|
#endif // VK_USE_PLATFORM_DIRECTFB_EXT
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
struct ANativeWindow *window;
|
||||||
|
} VkIcdSurfaceAndroid;
|
||||||
|
#endif // VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_MACOS_MVK
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
const void *pView;
|
||||||
|
} VkIcdSurfaceMacOS;
|
||||||
|
#endif // VK_USE_PLATFORM_MACOS_MVK
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_IOS_MVK
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
const void *pView;
|
||||||
|
} VkIcdSurfaceIOS;
|
||||||
|
#endif // VK_USE_PLATFORM_IOS_MVK
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_GGP
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
GgpStreamDescriptor streamDescriptor;
|
||||||
|
} VkIcdSurfaceGgp;
|
||||||
|
#endif // VK_USE_PLATFORM_GGP
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
VkDisplayModeKHR displayMode;
|
||||||
|
uint32_t planeIndex;
|
||||||
|
uint32_t planeStackIndex;
|
||||||
|
VkSurfaceTransformFlagBitsKHR transform;
|
||||||
|
float globalAlpha;
|
||||||
|
VkDisplayPlaneAlphaFlagBitsKHR alphaMode;
|
||||||
|
VkExtent2D imageExtent;
|
||||||
|
} VkIcdSurfaceDisplay;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
} VkIcdSurfaceHeadless;
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_METAL_EXT
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
const CAMetalLayer *pLayer;
|
||||||
|
} VkIcdSurfaceMetal;
|
||||||
|
#endif // VK_USE_PLATFORM_METAL_EXT
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_VI_NN
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
void *window;
|
||||||
|
} VkIcdSurfaceVi;
|
||||||
|
#endif // VK_USE_PLATFORM_VI_NN
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_SCREEN_QNX
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
struct _screen_context *context;
|
||||||
|
struct _screen_window *window;
|
||||||
|
} VkIcdSurfaceScreen;
|
||||||
|
#endif // VK_USE_PLATFORM_SCREEN_QNX
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_FUCHSIA
|
||||||
|
typedef struct {
|
||||||
|
VkIcdSurfaceBase base;
|
||||||
|
} VkIcdSurfaceImagePipe;
|
||||||
|
#endif // VK_USE_PLATFORM_FUCHSIA
|
||||||
+189
@@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015-2023 The Khronos Group Inc.
|
||||||
|
* Copyright 2015-2023 Valve Corporation
|
||||||
|
* Copyright 2015-2023 LunarG, Inc.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* Need to define dispatch table
|
||||||
|
* Core struct can then have ptr to dispatch table at the top
|
||||||
|
* Along with object ptrs for current and next OBJ
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vulkan_core.h"
|
||||||
|
|
||||||
|
#define MAX_NUM_UNKNOWN_EXTS 250
|
||||||
|
|
||||||
|
// Loader-Layer version negotiation API. Versions add the following features:
|
||||||
|
// Versions 0/1 - Initial. Doesn't support vk_layerGetPhysicalDeviceProcAddr
|
||||||
|
// or vk_icdNegotiateLoaderLayerInterfaceVersion.
|
||||||
|
// Version 2 - Add support for vk_layerGetPhysicalDeviceProcAddr and
|
||||||
|
// vk_icdNegotiateLoaderLayerInterfaceVersion.
|
||||||
|
#define CURRENT_LOADER_LAYER_INTERFACE_VERSION 2
|
||||||
|
#define MIN_SUPPORTED_LOADER_LAYER_INTERFACE_VERSION 1
|
||||||
|
|
||||||
|
#define VK_CURRENT_CHAIN_VERSION 1
|
||||||
|
|
||||||
|
// Typedef for use in the interfaces below
|
||||||
|
typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName);
|
||||||
|
|
||||||
|
// Version negotiation values
|
||||||
|
typedef enum VkNegotiateLayerStructType {
|
||||||
|
LAYER_NEGOTIATE_UNINTIALIZED = 0,
|
||||||
|
LAYER_NEGOTIATE_INTERFACE_STRUCT = 1,
|
||||||
|
} VkNegotiateLayerStructType;
|
||||||
|
|
||||||
|
// Version negotiation structures
|
||||||
|
typedef struct VkNegotiateLayerInterface {
|
||||||
|
VkNegotiateLayerStructType sType;
|
||||||
|
void *pNext;
|
||||||
|
uint32_t loaderLayerInterfaceVersion;
|
||||||
|
PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr;
|
||||||
|
PFN_vkGetDeviceProcAddr pfnGetDeviceProcAddr;
|
||||||
|
PFN_GetPhysicalDeviceProcAddr pfnGetPhysicalDeviceProcAddr;
|
||||||
|
} VkNegotiateLayerInterface;
|
||||||
|
|
||||||
|
// Version negotiation functions
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkNegotiateLoaderLayerInterfaceVersion)(VkNegotiateLayerInterface *pVersionStruct);
|
||||||
|
|
||||||
|
// Function prototype for unknown physical device extension command
|
||||||
|
typedef VkResult(VKAPI_PTR *PFN_PhysDevExt)(VkPhysicalDevice phys_device);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// CreateInstance and CreateDevice support structures
|
||||||
|
|
||||||
|
/* Sub type of structure for instance and device loader ext of CreateInfo.
|
||||||
|
* When sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
|
||||||
|
* or sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO
|
||||||
|
* then VkLayerFunction indicates struct type pointed to by pNext
|
||||||
|
*/
|
||||||
|
typedef enum VkLayerFunction_ {
|
||||||
|
VK_LAYER_LINK_INFO = 0,
|
||||||
|
VK_LOADER_DATA_CALLBACK = 1,
|
||||||
|
VK_LOADER_LAYER_CREATE_DEVICE_CALLBACK = 2,
|
||||||
|
VK_LOADER_FEATURES = 3,
|
||||||
|
} VkLayerFunction;
|
||||||
|
|
||||||
|
typedef struct VkLayerInstanceLink_ {
|
||||||
|
struct VkLayerInstanceLink_ *pNext;
|
||||||
|
PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
|
||||||
|
PFN_GetPhysicalDeviceProcAddr pfnNextGetPhysicalDeviceProcAddr;
|
||||||
|
} VkLayerInstanceLink;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When creating the device chain the loader needs to pass
|
||||||
|
* down information about it's device structure needed at
|
||||||
|
* the end of the chain. Passing the data via the
|
||||||
|
* VkLayerDeviceInfo avoids issues with finding the
|
||||||
|
* exact instance being used.
|
||||||
|
*/
|
||||||
|
typedef struct VkLayerDeviceInfo_ {
|
||||||
|
void *device_info;
|
||||||
|
PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
|
||||||
|
} VkLayerDeviceInfo;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkSetInstanceLoaderData)(VkInstance instance,
|
||||||
|
void *object);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkSetDeviceLoaderData)(VkDevice device,
|
||||||
|
void *object);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkLayerCreateDevice)(VkInstance instance, VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo,
|
||||||
|
const VkAllocationCallbacks *pAllocator, VkDevice *pDevice, PFN_vkGetInstanceProcAddr layerGIPA, PFN_vkGetDeviceProcAddr *nextGDPA);
|
||||||
|
typedef void (VKAPI_PTR *PFN_vkLayerDestroyDevice)(VkDevice physicalDevice, const VkAllocationCallbacks *pAllocator, PFN_vkDestroyDevice destroyFunction);
|
||||||
|
|
||||||
|
typedef enum VkLoaderFeastureFlagBits {
|
||||||
|
VK_LOADER_FEATURE_PHYSICAL_DEVICE_SORTING = 0x00000001,
|
||||||
|
} VkLoaderFlagBits;
|
||||||
|
typedef VkFlags VkLoaderFeatureFlags;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
|
||||||
|
const void *pNext;
|
||||||
|
VkLayerFunction function;
|
||||||
|
union {
|
||||||
|
VkLayerInstanceLink *pLayerInfo;
|
||||||
|
PFN_vkSetInstanceLoaderData pfnSetInstanceLoaderData;
|
||||||
|
struct {
|
||||||
|
PFN_vkLayerCreateDevice pfnLayerCreateDevice;
|
||||||
|
PFN_vkLayerDestroyDevice pfnLayerDestroyDevice;
|
||||||
|
} layerDevice;
|
||||||
|
VkLoaderFeatureFlags loaderFeatures;
|
||||||
|
} u;
|
||||||
|
} VkLayerInstanceCreateInfo;
|
||||||
|
|
||||||
|
typedef struct VkLayerDeviceLink_ {
|
||||||
|
struct VkLayerDeviceLink_ *pNext;
|
||||||
|
PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
|
||||||
|
PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr;
|
||||||
|
} VkLayerDeviceLink;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO
|
||||||
|
const void *pNext;
|
||||||
|
VkLayerFunction function;
|
||||||
|
union {
|
||||||
|
VkLayerDeviceLink *pLayerInfo;
|
||||||
|
PFN_vkSetDeviceLoaderData pfnSetDeviceLoaderData;
|
||||||
|
} u;
|
||||||
|
} VkLayerDeviceCreateInfo;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct);
|
||||||
|
|
||||||
|
typedef enum VkChainType {
|
||||||
|
VK_CHAIN_TYPE_UNKNOWN = 0,
|
||||||
|
VK_CHAIN_TYPE_ENUMERATE_INSTANCE_EXTENSION_PROPERTIES = 1,
|
||||||
|
VK_CHAIN_TYPE_ENUMERATE_INSTANCE_LAYER_PROPERTIES = 2,
|
||||||
|
VK_CHAIN_TYPE_ENUMERATE_INSTANCE_VERSION = 3,
|
||||||
|
} VkChainType;
|
||||||
|
|
||||||
|
typedef struct VkChainHeader {
|
||||||
|
VkChainType type;
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t size;
|
||||||
|
} VkChainHeader;
|
||||||
|
|
||||||
|
typedef struct VkEnumerateInstanceExtensionPropertiesChain {
|
||||||
|
VkChainHeader header;
|
||||||
|
VkResult(VKAPI_PTR *pfnNextLayer)(const struct VkEnumerateInstanceExtensionPropertiesChain *, const char *, uint32_t *,
|
||||||
|
VkExtensionProperties *);
|
||||||
|
const struct VkEnumerateInstanceExtensionPropertiesChain *pNextLink;
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
inline VkResult CallDown(const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) const {
|
||||||
|
return pfnNextLayer(pNextLink, pLayerName, pPropertyCount, pProperties);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} VkEnumerateInstanceExtensionPropertiesChain;
|
||||||
|
|
||||||
|
typedef struct VkEnumerateInstanceLayerPropertiesChain {
|
||||||
|
VkChainHeader header;
|
||||||
|
VkResult(VKAPI_PTR *pfnNextLayer)(const struct VkEnumerateInstanceLayerPropertiesChain *, uint32_t *, VkLayerProperties *);
|
||||||
|
const struct VkEnumerateInstanceLayerPropertiesChain *pNextLink;
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
inline VkResult CallDown(uint32_t *pPropertyCount, VkLayerProperties *pProperties) const {
|
||||||
|
return pfnNextLayer(pNextLink, pPropertyCount, pProperties);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} VkEnumerateInstanceLayerPropertiesChain;
|
||||||
|
|
||||||
|
typedef struct VkEnumerateInstanceVersionChain {
|
||||||
|
VkChainHeader header;
|
||||||
|
VkResult(VKAPI_PTR *pfnNextLayer)(const struct VkEnumerateInstanceVersionChain *, uint32_t *);
|
||||||
|
const struct VkEnumerateInstanceVersionChain *pNextLink;
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
inline VkResult CallDown(uint32_t *pApiVersion) const {
|
||||||
|
return pfnNextLayer(pNextLink, pApiVersion);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} VkEnumerateInstanceVersionChain;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
//
|
||||||
|
// File: vk_platform.h
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
** Copyright 2014-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef VK_PLATFORM_H_
|
||||||
|
#define VK_PLATFORM_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
/*
|
||||||
|
***************************************************************************************************
|
||||||
|
* Platform-specific directives and type declarations
|
||||||
|
***************************************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Platform-specific calling convention macros.
|
||||||
|
*
|
||||||
|
* Platforms should define these so that Vulkan clients call Vulkan commands
|
||||||
|
* with the same calling conventions that the Vulkan implementation expects.
|
||||||
|
*
|
||||||
|
* VKAPI_ATTR - Placed before the return type in function declarations.
|
||||||
|
* Useful for C++11 and GCC/Clang-style function attribute syntax.
|
||||||
|
* VKAPI_CALL - Placed after the return type in function declarations.
|
||||||
|
* Useful for MSVC-style calling convention syntax.
|
||||||
|
* VKAPI_PTR - Placed between the '(' and '*' in function pointer types.
|
||||||
|
*
|
||||||
|
* Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void);
|
||||||
|
* Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void);
|
||||||
|
*/
|
||||||
|
#if defined(_WIN32)
|
||||||
|
// On Windows, Vulkan commands use the stdcall convention
|
||||||
|
#define VKAPI_ATTR
|
||||||
|
#define VKAPI_CALL __stdcall
|
||||||
|
#define VKAPI_PTR VKAPI_CALL
|
||||||
|
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
|
||||||
|
#error "Vulkan is not supported for the 'armeabi' NDK ABI"
|
||||||
|
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
|
||||||
|
// On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
|
||||||
|
// calling convention, i.e. float parameters are passed in registers. This
|
||||||
|
// is true even if the rest of the application passes floats on the stack,
|
||||||
|
// as it does by default when compiling for the armeabi-v7a NDK ABI.
|
||||||
|
#define VKAPI_ATTR __attribute__((pcs("aapcs-vfp")))
|
||||||
|
#define VKAPI_CALL
|
||||||
|
#define VKAPI_PTR VKAPI_ATTR
|
||||||
|
#else
|
||||||
|
// On other platforms, use the default calling convention
|
||||||
|
#define VKAPI_ATTR
|
||||||
|
#define VKAPI_CALL
|
||||||
|
#define VKAPI_PTR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(VK_NO_STDDEF_H)
|
||||||
|
#include <stddef.h>
|
||||||
|
#endif // !defined(VK_NO_STDDEF_H)
|
||||||
|
|
||||||
|
#if !defined(VK_NO_STDINT_H)
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||||
|
typedef signed __int8 int8_t;
|
||||||
|
typedef unsigned __int8 uint8_t;
|
||||||
|
typedef signed __int16 int16_t;
|
||||||
|
typedef unsigned __int16 uint16_t;
|
||||||
|
typedef signed __int32 int32_t;
|
||||||
|
typedef unsigned __int32 uint32_t;
|
||||||
|
typedef signed __int64 int64_t;
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#endif // !defined(VK_NO_STDINT_H)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif
|
||||||
+99
@@ -0,0 +1,99 @@
|
|||||||
|
#ifndef VULKAN_H_
|
||||||
|
#define VULKAN_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vk_platform.h"
|
||||||
|
#include "vulkan_core.h"
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
#include "vulkan_android.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_FUCHSIA
|
||||||
|
#include <zircon/types.h>
|
||||||
|
#include "vulkan_fuchsia.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_IOS_MVK
|
||||||
|
#include "vulkan_ios.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_MACOS_MVK
|
||||||
|
#include "vulkan_macos.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_METAL_EXT
|
||||||
|
#include "vulkan_metal.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_VI_NN
|
||||||
|
#include "vulkan_vi.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||||
|
#include "vulkan_wayland.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||||
|
#include <windows.h>
|
||||||
|
#include "vulkan_win32.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
#include "vulkan_xcb.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include "vulkan_xlib.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_DIRECTFB_EXT
|
||||||
|
#include <directfb.h>
|
||||||
|
#include "vulkan_directfb.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/extensions/Xrandr.h>
|
||||||
|
#include "vulkan_xlib_xrandr.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_GGP
|
||||||
|
#include <ggp_c/vulkan_types.h>
|
||||||
|
#include "vulkan_ggp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_SCREEN_QNX
|
||||||
|
#include <screen/screen.h>
|
||||||
|
#include "vulkan_screen.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_SCI
|
||||||
|
#include <nvscisync.h>
|
||||||
|
#include <nvscibuf.h>
|
||||||
|
#include "vulkan_sci.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VK_ENABLE_BETA_EXTENSIONS
|
||||||
|
#include "vulkan_beta.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // VULKAN_H_
|
||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
#ifndef VULKAN_ANDROID_H_
|
||||||
|
#define VULKAN_ANDROID_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_android_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_android_surface 1
|
||||||
|
struct ANativeWindow;
|
||||||
|
#define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6
|
||||||
|
#define VK_KHR_ANDROID_SURFACE_EXTENSION_NAME "VK_KHR_android_surface"
|
||||||
|
typedef VkFlags VkAndroidSurfaceCreateFlagsKHR;
|
||||||
|
typedef struct VkAndroidSurfaceCreateInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkAndroidSurfaceCreateFlagsKHR flags;
|
||||||
|
struct ANativeWindow* window;
|
||||||
|
} VkAndroidSurfaceCreateInfoKHR;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateAndroidSurfaceKHR)(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateAndroidSurfaceKHR(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkAndroidSurfaceCreateInfoKHR* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_ANDROID_external_memory_android_hardware_buffer is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_ANDROID_external_memory_android_hardware_buffer 1
|
||||||
|
struct AHardwareBuffer;
|
||||||
|
#define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION 5
|
||||||
|
#define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME "VK_ANDROID_external_memory_android_hardware_buffer"
|
||||||
|
typedef struct VkAndroidHardwareBufferUsageANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
uint64_t androidHardwareBufferUsage;
|
||||||
|
} VkAndroidHardwareBufferUsageANDROID;
|
||||||
|
|
||||||
|
typedef struct VkAndroidHardwareBufferPropertiesANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkDeviceSize allocationSize;
|
||||||
|
uint32_t memoryTypeBits;
|
||||||
|
} VkAndroidHardwareBufferPropertiesANDROID;
|
||||||
|
|
||||||
|
typedef struct VkAndroidHardwareBufferFormatPropertiesANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkFormat format;
|
||||||
|
uint64_t externalFormat;
|
||||||
|
VkFormatFeatureFlags formatFeatures;
|
||||||
|
VkComponentMapping samplerYcbcrConversionComponents;
|
||||||
|
VkSamplerYcbcrModelConversion suggestedYcbcrModel;
|
||||||
|
VkSamplerYcbcrRange suggestedYcbcrRange;
|
||||||
|
VkChromaLocation suggestedXChromaOffset;
|
||||||
|
VkChromaLocation suggestedYChromaOffset;
|
||||||
|
} VkAndroidHardwareBufferFormatPropertiesANDROID;
|
||||||
|
|
||||||
|
typedef struct VkImportAndroidHardwareBufferInfoANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
struct AHardwareBuffer* buffer;
|
||||||
|
} VkImportAndroidHardwareBufferInfoANDROID;
|
||||||
|
|
||||||
|
typedef struct VkMemoryGetAndroidHardwareBufferInfoANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
} VkMemoryGetAndroidHardwareBufferInfoANDROID;
|
||||||
|
|
||||||
|
typedef struct VkExternalFormatANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
uint64_t externalFormat;
|
||||||
|
} VkExternalFormatANDROID;
|
||||||
|
|
||||||
|
typedef struct VkAndroidHardwareBufferFormatProperties2ANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkFormat format;
|
||||||
|
uint64_t externalFormat;
|
||||||
|
VkFormatFeatureFlags2 formatFeatures;
|
||||||
|
VkComponentMapping samplerYcbcrConversionComponents;
|
||||||
|
VkSamplerYcbcrModelConversion suggestedYcbcrModel;
|
||||||
|
VkSamplerYcbcrRange suggestedYcbcrRange;
|
||||||
|
VkChromaLocation suggestedXChromaOffset;
|
||||||
|
VkChromaLocation suggestedYChromaOffset;
|
||||||
|
} VkAndroidHardwareBufferFormatProperties2ANDROID;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetAndroidHardwareBufferPropertiesANDROID)(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryAndroidHardwareBufferANDROID)(VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, struct AHardwareBuffer** pBuffer);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetAndroidHardwareBufferPropertiesANDROID(
|
||||||
|
VkDevice device,
|
||||||
|
const struct AHardwareBuffer* buffer,
|
||||||
|
VkAndroidHardwareBufferPropertiesANDROID* pProperties);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryAndroidHardwareBufferANDROID(
|
||||||
|
VkDevice device,
|
||||||
|
const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo,
|
||||||
|
struct AHardwareBuffer** pBuffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_ANDROID_external_format_resolve is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_ANDROID_external_format_resolve 1
|
||||||
|
#define VK_ANDROID_EXTERNAL_FORMAT_RESOLVE_SPEC_VERSION 1
|
||||||
|
#define VK_ANDROID_EXTERNAL_FORMAT_RESOLVE_EXTENSION_NAME "VK_ANDROID_external_format_resolve"
|
||||||
|
typedef struct VkPhysicalDeviceExternalFormatResolveFeaturesANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkBool32 externalFormatResolve;
|
||||||
|
} VkPhysicalDeviceExternalFormatResolveFeaturesANDROID;
|
||||||
|
|
||||||
|
typedef struct VkPhysicalDeviceExternalFormatResolvePropertiesANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkBool32 nullColorAttachmentWithExternalFormatResolve;
|
||||||
|
VkChromaLocation externalFormatResolveChromaOffsetX;
|
||||||
|
VkChromaLocation externalFormatResolveChromaOffsetY;
|
||||||
|
} VkPhysicalDeviceExternalFormatResolvePropertiesANDROID;
|
||||||
|
|
||||||
|
typedef struct VkAndroidHardwareBufferFormatResolvePropertiesANDROID {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkFormat colorAttachmentFormat;
|
||||||
|
} VkAndroidHardwareBufferFormatResolvePropertiesANDROID;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+226
@@ -0,0 +1,226 @@
|
|||||||
|
#ifndef VULKAN_BETA_H_
|
||||||
|
#define VULKAN_BETA_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_portability_subset is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_portability_subset 1
|
||||||
|
#define VK_KHR_PORTABILITY_SUBSET_SPEC_VERSION 1
|
||||||
|
#define VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME "VK_KHR_portability_subset"
|
||||||
|
typedef struct VkPhysicalDevicePortabilitySubsetFeaturesKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkBool32 constantAlphaColorBlendFactors;
|
||||||
|
VkBool32 events;
|
||||||
|
VkBool32 imageViewFormatReinterpretation;
|
||||||
|
VkBool32 imageViewFormatSwizzle;
|
||||||
|
VkBool32 imageView2DOn3DImage;
|
||||||
|
VkBool32 multisampleArrayImage;
|
||||||
|
VkBool32 mutableComparisonSamplers;
|
||||||
|
VkBool32 pointPolygons;
|
||||||
|
VkBool32 samplerMipLodBias;
|
||||||
|
VkBool32 separateStencilMaskRef;
|
||||||
|
VkBool32 shaderSampleRateInterpolationFunctions;
|
||||||
|
VkBool32 tessellationIsolines;
|
||||||
|
VkBool32 tessellationPointMode;
|
||||||
|
VkBool32 triangleFans;
|
||||||
|
VkBool32 vertexAttributeAccessBeyondStride;
|
||||||
|
} VkPhysicalDevicePortabilitySubsetFeaturesKHR;
|
||||||
|
|
||||||
|
typedef struct VkPhysicalDevicePortabilitySubsetPropertiesKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
uint32_t minVertexInputBindingStrideAlignment;
|
||||||
|
} VkPhysicalDevicePortabilitySubsetPropertiesKHR;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_AMDX_shader_enqueue is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_AMDX_shader_enqueue 1
|
||||||
|
#define VK_AMDX_SHADER_ENQUEUE_SPEC_VERSION 2
|
||||||
|
#define VK_AMDX_SHADER_ENQUEUE_EXTENSION_NAME "VK_AMDX_shader_enqueue"
|
||||||
|
#define VK_SHADER_INDEX_UNUSED_AMDX (~0U)
|
||||||
|
typedef struct VkPhysicalDeviceShaderEnqueueFeaturesAMDX {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkBool32 shaderEnqueue;
|
||||||
|
VkBool32 shaderMeshEnqueue;
|
||||||
|
} VkPhysicalDeviceShaderEnqueueFeaturesAMDX;
|
||||||
|
|
||||||
|
typedef struct VkPhysicalDeviceShaderEnqueuePropertiesAMDX {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
uint32_t maxExecutionGraphDepth;
|
||||||
|
uint32_t maxExecutionGraphShaderOutputNodes;
|
||||||
|
uint32_t maxExecutionGraphShaderPayloadSize;
|
||||||
|
uint32_t maxExecutionGraphShaderPayloadCount;
|
||||||
|
uint32_t executionGraphDispatchAddressAlignment;
|
||||||
|
uint32_t maxExecutionGraphWorkgroupCount[3];
|
||||||
|
uint32_t maxExecutionGraphWorkgroups;
|
||||||
|
} VkPhysicalDeviceShaderEnqueuePropertiesAMDX;
|
||||||
|
|
||||||
|
typedef struct VkExecutionGraphPipelineScratchSizeAMDX {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkDeviceSize minSize;
|
||||||
|
VkDeviceSize maxSize;
|
||||||
|
VkDeviceSize sizeGranularity;
|
||||||
|
} VkExecutionGraphPipelineScratchSizeAMDX;
|
||||||
|
|
||||||
|
typedef struct VkExecutionGraphPipelineCreateInfoAMDX {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkPipelineCreateFlags flags;
|
||||||
|
uint32_t stageCount;
|
||||||
|
const VkPipelineShaderStageCreateInfo* pStages;
|
||||||
|
const VkPipelineLibraryCreateInfoKHR* pLibraryInfo;
|
||||||
|
VkPipelineLayout layout;
|
||||||
|
VkPipeline basePipelineHandle;
|
||||||
|
int32_t basePipelineIndex;
|
||||||
|
} VkExecutionGraphPipelineCreateInfoAMDX;
|
||||||
|
|
||||||
|
typedef union VkDeviceOrHostAddressConstAMDX {
|
||||||
|
VkDeviceAddress deviceAddress;
|
||||||
|
const void* hostAddress;
|
||||||
|
} VkDeviceOrHostAddressConstAMDX;
|
||||||
|
|
||||||
|
typedef struct VkDispatchGraphInfoAMDX {
|
||||||
|
uint32_t nodeIndex;
|
||||||
|
uint32_t payloadCount;
|
||||||
|
VkDeviceOrHostAddressConstAMDX payloads;
|
||||||
|
uint64_t payloadStride;
|
||||||
|
} VkDispatchGraphInfoAMDX;
|
||||||
|
|
||||||
|
typedef struct VkDispatchGraphCountInfoAMDX {
|
||||||
|
uint32_t count;
|
||||||
|
VkDeviceOrHostAddressConstAMDX infos;
|
||||||
|
uint64_t stride;
|
||||||
|
} VkDispatchGraphCountInfoAMDX;
|
||||||
|
|
||||||
|
typedef struct VkPipelineShaderStageNodeCreateInfoAMDX {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
const char* pName;
|
||||||
|
uint32_t index;
|
||||||
|
} VkPipelineShaderStageNodeCreateInfoAMDX;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateExecutionGraphPipelinesAMDX)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkExecutionGraphPipelineCreateInfoAMDX* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetExecutionGraphPipelineScratchSizeAMDX)(VkDevice device, VkPipeline executionGraph, VkExecutionGraphPipelineScratchSizeAMDX* pSizeInfo);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetExecutionGraphPipelineNodeIndexAMDX)(VkDevice device, VkPipeline executionGraph, const VkPipelineShaderStageNodeCreateInfoAMDX* pNodeInfo, uint32_t* pNodeIndex);
|
||||||
|
typedef void (VKAPI_PTR *PFN_vkCmdInitializeGraphScratchMemoryAMDX)(VkCommandBuffer commandBuffer, VkPipeline executionGraph, VkDeviceAddress scratch, VkDeviceSize scratchSize);
|
||||||
|
typedef void (VKAPI_PTR *PFN_vkCmdDispatchGraphAMDX)(VkCommandBuffer commandBuffer, VkDeviceAddress scratch, VkDeviceSize scratchSize, const VkDispatchGraphCountInfoAMDX* pCountInfo);
|
||||||
|
typedef void (VKAPI_PTR *PFN_vkCmdDispatchGraphIndirectAMDX)(VkCommandBuffer commandBuffer, VkDeviceAddress scratch, VkDeviceSize scratchSize, const VkDispatchGraphCountInfoAMDX* pCountInfo);
|
||||||
|
typedef void (VKAPI_PTR *PFN_vkCmdDispatchGraphIndirectCountAMDX)(VkCommandBuffer commandBuffer, VkDeviceAddress scratch, VkDeviceSize scratchSize, VkDeviceAddress countInfo);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateExecutionGraphPipelinesAMDX(
|
||||||
|
VkDevice device,
|
||||||
|
VkPipelineCache pipelineCache,
|
||||||
|
uint32_t createInfoCount,
|
||||||
|
const VkExecutionGraphPipelineCreateInfoAMDX* pCreateInfos,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkPipeline* pPipelines);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetExecutionGraphPipelineScratchSizeAMDX(
|
||||||
|
VkDevice device,
|
||||||
|
VkPipeline executionGraph,
|
||||||
|
VkExecutionGraphPipelineScratchSizeAMDX* pSizeInfo);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetExecutionGraphPipelineNodeIndexAMDX(
|
||||||
|
VkDevice device,
|
||||||
|
VkPipeline executionGraph,
|
||||||
|
const VkPipelineShaderStageNodeCreateInfoAMDX* pNodeInfo,
|
||||||
|
uint32_t* pNodeIndex);
|
||||||
|
|
||||||
|
VKAPI_ATTR void VKAPI_CALL vkCmdInitializeGraphScratchMemoryAMDX(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
VkPipeline executionGraph,
|
||||||
|
VkDeviceAddress scratch,
|
||||||
|
VkDeviceSize scratchSize);
|
||||||
|
|
||||||
|
VKAPI_ATTR void VKAPI_CALL vkCmdDispatchGraphAMDX(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
VkDeviceAddress scratch,
|
||||||
|
VkDeviceSize scratchSize,
|
||||||
|
const VkDispatchGraphCountInfoAMDX* pCountInfo);
|
||||||
|
|
||||||
|
VKAPI_ATTR void VKAPI_CALL vkCmdDispatchGraphIndirectAMDX(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
VkDeviceAddress scratch,
|
||||||
|
VkDeviceSize scratchSize,
|
||||||
|
const VkDispatchGraphCountInfoAMDX* pCountInfo);
|
||||||
|
|
||||||
|
VKAPI_ATTR void VKAPI_CALL vkCmdDispatchGraphIndirectCountAMDX(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
VkDeviceAddress scratch,
|
||||||
|
VkDeviceSize scratchSize,
|
||||||
|
VkDeviceAddress countInfo);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_NV_displacement_micromap is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_NV_displacement_micromap 1
|
||||||
|
#define VK_NV_DISPLACEMENT_MICROMAP_SPEC_VERSION 2
|
||||||
|
#define VK_NV_DISPLACEMENT_MICROMAP_EXTENSION_NAME "VK_NV_displacement_micromap"
|
||||||
|
|
||||||
|
typedef enum VkDisplacementMicromapFormatNV {
|
||||||
|
VK_DISPLACEMENT_MICROMAP_FORMAT_64_TRIANGLES_64_BYTES_NV = 1,
|
||||||
|
VK_DISPLACEMENT_MICROMAP_FORMAT_256_TRIANGLES_128_BYTES_NV = 2,
|
||||||
|
VK_DISPLACEMENT_MICROMAP_FORMAT_1024_TRIANGLES_128_BYTES_NV = 3,
|
||||||
|
VK_DISPLACEMENT_MICROMAP_FORMAT_MAX_ENUM_NV = 0x7FFFFFFF
|
||||||
|
} VkDisplacementMicromapFormatNV;
|
||||||
|
typedef struct VkPhysicalDeviceDisplacementMicromapFeaturesNV {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkBool32 displacementMicromap;
|
||||||
|
} VkPhysicalDeviceDisplacementMicromapFeaturesNV;
|
||||||
|
|
||||||
|
typedef struct VkPhysicalDeviceDisplacementMicromapPropertiesNV {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
uint32_t maxDisplacementMicromapSubdivisionLevel;
|
||||||
|
} VkPhysicalDeviceDisplacementMicromapPropertiesNV;
|
||||||
|
|
||||||
|
typedef struct VkAccelerationStructureTrianglesDisplacementMicromapNV {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkFormat displacementBiasAndScaleFormat;
|
||||||
|
VkFormat displacementVectorFormat;
|
||||||
|
VkDeviceOrHostAddressConstKHR displacementBiasAndScaleBuffer;
|
||||||
|
VkDeviceSize displacementBiasAndScaleStride;
|
||||||
|
VkDeviceOrHostAddressConstKHR displacementVectorBuffer;
|
||||||
|
VkDeviceSize displacementVectorStride;
|
||||||
|
VkDeviceOrHostAddressConstKHR displacedMicromapPrimitiveFlags;
|
||||||
|
VkDeviceSize displacedMicromapPrimitiveFlagsStride;
|
||||||
|
VkIndexType indexType;
|
||||||
|
VkDeviceOrHostAddressConstKHR indexBuffer;
|
||||||
|
VkDeviceSize indexStride;
|
||||||
|
uint32_t baseTriangle;
|
||||||
|
uint32_t usageCountsCount;
|
||||||
|
const VkMicromapUsageEXT* pUsageCounts;
|
||||||
|
const VkMicromapUsageEXT* const* ppUsageCounts;
|
||||||
|
VkMicromapEXT micromap;
|
||||||
|
} VkAccelerationStructureTrianglesDisplacementMicromapNV;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+21246
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef VULKAN_DIRECTFB_H_
|
||||||
|
#define VULKAN_DIRECTFB_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_EXT_directfb_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_EXT_directfb_surface 1
|
||||||
|
#define VK_EXT_DIRECTFB_SURFACE_SPEC_VERSION 1
|
||||||
|
#define VK_EXT_DIRECTFB_SURFACE_EXTENSION_NAME "VK_EXT_directfb_surface"
|
||||||
|
typedef VkFlags VkDirectFBSurfaceCreateFlagsEXT;
|
||||||
|
typedef struct VkDirectFBSurfaceCreateInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkDirectFBSurfaceCreateFlagsEXT flags;
|
||||||
|
IDirectFB* dfb;
|
||||||
|
IDirectFBSurface* surface;
|
||||||
|
} VkDirectFBSurfaceCreateInfoEXT;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateDirectFBSurfaceEXT)(VkInstance instance, const VkDirectFBSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceDirectFBPresentationSupportEXT)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, IDirectFB* dfb);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDirectFBSurfaceEXT(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkDirectFBSurfaceCreateInfoEXT* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceDirectFBPresentationSupportEXT(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
uint32_t queueFamilyIndex,
|
||||||
|
IDirectFB* dfb);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+262
@@ -0,0 +1,262 @@
|
|||||||
|
#ifndef VULKAN_FUCHSIA_H_
|
||||||
|
#define VULKAN_FUCHSIA_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_FUCHSIA_imagepipe_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_FUCHSIA_imagepipe_surface 1
|
||||||
|
#define VK_FUCHSIA_IMAGEPIPE_SURFACE_SPEC_VERSION 1
|
||||||
|
#define VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME "VK_FUCHSIA_imagepipe_surface"
|
||||||
|
typedef VkFlags VkImagePipeSurfaceCreateFlagsFUCHSIA;
|
||||||
|
typedef struct VkImagePipeSurfaceCreateInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkImagePipeSurfaceCreateFlagsFUCHSIA flags;
|
||||||
|
zx_handle_t imagePipeHandle;
|
||||||
|
} VkImagePipeSurfaceCreateInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateImagePipeSurfaceFUCHSIA)(VkInstance instance, const VkImagePipeSurfaceCreateInfoFUCHSIA* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateImagePipeSurfaceFUCHSIA(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkImagePipeSurfaceCreateInfoFUCHSIA* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_FUCHSIA_external_memory is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_FUCHSIA_external_memory 1
|
||||||
|
#define VK_FUCHSIA_EXTERNAL_MEMORY_SPEC_VERSION 1
|
||||||
|
#define VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME "VK_FUCHSIA_external_memory"
|
||||||
|
typedef struct VkImportMemoryZirconHandleInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkExternalMemoryHandleTypeFlagBits handleType;
|
||||||
|
zx_handle_t handle;
|
||||||
|
} VkImportMemoryZirconHandleInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkMemoryZirconHandlePropertiesFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
uint32_t memoryTypeBits;
|
||||||
|
} VkMemoryZirconHandlePropertiesFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkMemoryGetZirconHandleInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
VkExternalMemoryHandleTypeFlagBits handleType;
|
||||||
|
} VkMemoryGetZirconHandleInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryZirconHandleFUCHSIA)(VkDevice device, const VkMemoryGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, zx_handle_t* pZirconHandle);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryZirconHandlePropertiesFUCHSIA)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, zx_handle_t zirconHandle, VkMemoryZirconHandlePropertiesFUCHSIA* pMemoryZirconHandleProperties);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryZirconHandleFUCHSIA(
|
||||||
|
VkDevice device,
|
||||||
|
const VkMemoryGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo,
|
||||||
|
zx_handle_t* pZirconHandle);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryZirconHandlePropertiesFUCHSIA(
|
||||||
|
VkDevice device,
|
||||||
|
VkExternalMemoryHandleTypeFlagBits handleType,
|
||||||
|
zx_handle_t zirconHandle,
|
||||||
|
VkMemoryZirconHandlePropertiesFUCHSIA* pMemoryZirconHandleProperties);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_FUCHSIA_external_semaphore is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_FUCHSIA_external_semaphore 1
|
||||||
|
#define VK_FUCHSIA_EXTERNAL_SEMAPHORE_SPEC_VERSION 1
|
||||||
|
#define VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_FUCHSIA_external_semaphore"
|
||||||
|
typedef struct VkImportSemaphoreZirconHandleInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkSemaphore semaphore;
|
||||||
|
VkSemaphoreImportFlags flags;
|
||||||
|
VkExternalSemaphoreHandleTypeFlagBits handleType;
|
||||||
|
zx_handle_t zirconHandle;
|
||||||
|
} VkImportSemaphoreZirconHandleInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkSemaphoreGetZirconHandleInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkSemaphore semaphore;
|
||||||
|
VkExternalSemaphoreHandleTypeFlagBits handleType;
|
||||||
|
} VkSemaphoreGetZirconHandleInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreZirconHandleFUCHSIA)(VkDevice device, const VkImportSemaphoreZirconHandleInfoFUCHSIA* pImportSemaphoreZirconHandleInfo);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreZirconHandleFUCHSIA)(VkDevice device, const VkSemaphoreGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, zx_handle_t* pZirconHandle);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreZirconHandleFUCHSIA(
|
||||||
|
VkDevice device,
|
||||||
|
const VkImportSemaphoreZirconHandleInfoFUCHSIA* pImportSemaphoreZirconHandleInfo);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreZirconHandleFUCHSIA(
|
||||||
|
VkDevice device,
|
||||||
|
const VkSemaphoreGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo,
|
||||||
|
zx_handle_t* pZirconHandle);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_FUCHSIA_buffer_collection is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_FUCHSIA_buffer_collection 1
|
||||||
|
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferCollectionFUCHSIA)
|
||||||
|
#define VK_FUCHSIA_BUFFER_COLLECTION_SPEC_VERSION 2
|
||||||
|
#define VK_FUCHSIA_BUFFER_COLLECTION_EXTENSION_NAME "VK_FUCHSIA_buffer_collection"
|
||||||
|
typedef VkFlags VkImageFormatConstraintsFlagsFUCHSIA;
|
||||||
|
|
||||||
|
typedef enum VkImageConstraintsInfoFlagBitsFUCHSIA {
|
||||||
|
VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_RARELY_FUCHSIA = 0x00000001,
|
||||||
|
VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_OFTEN_FUCHSIA = 0x00000002,
|
||||||
|
VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_RARELY_FUCHSIA = 0x00000004,
|
||||||
|
VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_OFTEN_FUCHSIA = 0x00000008,
|
||||||
|
VK_IMAGE_CONSTRAINTS_INFO_PROTECTED_OPTIONAL_FUCHSIA = 0x00000010,
|
||||||
|
VK_IMAGE_CONSTRAINTS_INFO_FLAG_BITS_MAX_ENUM_FUCHSIA = 0x7FFFFFFF
|
||||||
|
} VkImageConstraintsInfoFlagBitsFUCHSIA;
|
||||||
|
typedef VkFlags VkImageConstraintsInfoFlagsFUCHSIA;
|
||||||
|
typedef struct VkBufferCollectionCreateInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
zx_handle_t collectionToken;
|
||||||
|
} VkBufferCollectionCreateInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkImportMemoryBufferCollectionFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkBufferCollectionFUCHSIA collection;
|
||||||
|
uint32_t index;
|
||||||
|
} VkImportMemoryBufferCollectionFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkBufferCollectionImageCreateInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkBufferCollectionFUCHSIA collection;
|
||||||
|
uint32_t index;
|
||||||
|
} VkBufferCollectionImageCreateInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkBufferCollectionConstraintsInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
uint32_t minBufferCount;
|
||||||
|
uint32_t maxBufferCount;
|
||||||
|
uint32_t minBufferCountForCamping;
|
||||||
|
uint32_t minBufferCountForDedicatedSlack;
|
||||||
|
uint32_t minBufferCountForSharedSlack;
|
||||||
|
} VkBufferCollectionConstraintsInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkBufferConstraintsInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkBufferCreateInfo createInfo;
|
||||||
|
VkFormatFeatureFlags requiredFormatFeatures;
|
||||||
|
VkBufferCollectionConstraintsInfoFUCHSIA bufferCollectionConstraints;
|
||||||
|
} VkBufferConstraintsInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkBufferCollectionBufferCreateInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkBufferCollectionFUCHSIA collection;
|
||||||
|
uint32_t index;
|
||||||
|
} VkBufferCollectionBufferCreateInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkSysmemColorSpaceFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
uint32_t colorSpace;
|
||||||
|
} VkSysmemColorSpaceFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkBufferCollectionPropertiesFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
uint32_t memoryTypeBits;
|
||||||
|
uint32_t bufferCount;
|
||||||
|
uint32_t createInfoIndex;
|
||||||
|
uint64_t sysmemPixelFormat;
|
||||||
|
VkFormatFeatureFlags formatFeatures;
|
||||||
|
VkSysmemColorSpaceFUCHSIA sysmemColorSpaceIndex;
|
||||||
|
VkComponentMapping samplerYcbcrConversionComponents;
|
||||||
|
VkSamplerYcbcrModelConversion suggestedYcbcrModel;
|
||||||
|
VkSamplerYcbcrRange suggestedYcbcrRange;
|
||||||
|
VkChromaLocation suggestedXChromaOffset;
|
||||||
|
VkChromaLocation suggestedYChromaOffset;
|
||||||
|
} VkBufferCollectionPropertiesFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkImageFormatConstraintsInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkImageCreateInfo imageCreateInfo;
|
||||||
|
VkFormatFeatureFlags requiredFormatFeatures;
|
||||||
|
VkImageFormatConstraintsFlagsFUCHSIA flags;
|
||||||
|
uint64_t sysmemPixelFormat;
|
||||||
|
uint32_t colorSpaceCount;
|
||||||
|
const VkSysmemColorSpaceFUCHSIA* pColorSpaces;
|
||||||
|
} VkImageFormatConstraintsInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef struct VkImageConstraintsInfoFUCHSIA {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
uint32_t formatConstraintsCount;
|
||||||
|
const VkImageFormatConstraintsInfoFUCHSIA* pFormatConstraints;
|
||||||
|
VkBufferCollectionConstraintsInfoFUCHSIA bufferCollectionConstraints;
|
||||||
|
VkImageConstraintsInfoFlagsFUCHSIA flags;
|
||||||
|
} VkImageConstraintsInfoFUCHSIA;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateBufferCollectionFUCHSIA)(VkDevice device, const VkBufferCollectionCreateInfoFUCHSIA* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferCollectionFUCHSIA* pCollection);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkSetBufferCollectionImageConstraintsFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkSetBufferCollectionBufferConstraintsFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo);
|
||||||
|
typedef void (VKAPI_PTR *PFN_vkDestroyBufferCollectionFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkAllocationCallbacks* pAllocator);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetBufferCollectionPropertiesFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, VkBufferCollectionPropertiesFUCHSIA* pProperties);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateBufferCollectionFUCHSIA(
|
||||||
|
VkDevice device,
|
||||||
|
const VkBufferCollectionCreateInfoFUCHSIA* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkBufferCollectionFUCHSIA* pCollection);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkSetBufferCollectionImageConstraintsFUCHSIA(
|
||||||
|
VkDevice device,
|
||||||
|
VkBufferCollectionFUCHSIA collection,
|
||||||
|
const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkSetBufferCollectionBufferConstraintsFUCHSIA(
|
||||||
|
VkDevice device,
|
||||||
|
VkBufferCollectionFUCHSIA collection,
|
||||||
|
const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo);
|
||||||
|
|
||||||
|
VKAPI_ATTR void VKAPI_CALL vkDestroyBufferCollectionFUCHSIA(
|
||||||
|
VkDevice device,
|
||||||
|
VkBufferCollectionFUCHSIA collection,
|
||||||
|
const VkAllocationCallbacks* pAllocator);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetBufferCollectionPropertiesFUCHSIA(
|
||||||
|
VkDevice device,
|
||||||
|
VkBufferCollectionFUCHSIA collection,
|
||||||
|
VkBufferCollectionPropertiesFUCHSIA* pProperties);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
#ifndef VULKAN_GGP_H_
|
||||||
|
#define VULKAN_GGP_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_GGP_stream_descriptor_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_GGP_stream_descriptor_surface 1
|
||||||
|
#define VK_GGP_STREAM_DESCRIPTOR_SURFACE_SPEC_VERSION 1
|
||||||
|
#define VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME "VK_GGP_stream_descriptor_surface"
|
||||||
|
typedef VkFlags VkStreamDescriptorSurfaceCreateFlagsGGP;
|
||||||
|
typedef struct VkStreamDescriptorSurfaceCreateInfoGGP {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkStreamDescriptorSurfaceCreateFlagsGGP flags;
|
||||||
|
GgpStreamDescriptor streamDescriptor;
|
||||||
|
} VkStreamDescriptorSurfaceCreateInfoGGP;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateStreamDescriptorSurfaceGGP)(VkInstance instance, const VkStreamDescriptorSurfaceCreateInfoGGP* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateStreamDescriptorSurfaceGGP(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkStreamDescriptorSurfaceCreateInfoGGP* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_GGP_frame_token is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_GGP_frame_token 1
|
||||||
|
#define VK_GGP_FRAME_TOKEN_SPEC_VERSION 1
|
||||||
|
#define VK_GGP_FRAME_TOKEN_EXTENSION_NAME "VK_GGP_frame_token"
|
||||||
|
typedef struct VkPresentFrameTokenGGP {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
GgpFrameToken frameToken;
|
||||||
|
} VkPresentFrameTokenGGP;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef VULKAN_IOS_H_
|
||||||
|
#define VULKAN_IOS_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_MVK_ios_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_MVK_ios_surface 1
|
||||||
|
#define VK_MVK_IOS_SURFACE_SPEC_VERSION 3
|
||||||
|
#define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface"
|
||||||
|
typedef VkFlags VkIOSSurfaceCreateFlagsMVK;
|
||||||
|
typedef struct VkIOSSurfaceCreateInfoMVK {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkIOSSurfaceCreateFlagsMVK flags;
|
||||||
|
const void* pView;
|
||||||
|
} VkIOSSurfaceCreateInfoMVK;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateIOSSurfaceMVK)(VkInstance instance, const VkIOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateIOSSurfaceMVK(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkIOSSurfaceCreateInfoMVK* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef VULKAN_MACOS_H_
|
||||||
|
#define VULKAN_MACOS_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_MVK_macos_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_MVK_macos_surface 1
|
||||||
|
#define VK_MVK_MACOS_SURFACE_SPEC_VERSION 3
|
||||||
|
#define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
|
||||||
|
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
|
||||||
|
typedef struct VkMacOSSurfaceCreateInfoMVK {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkMacOSSurfaceCreateFlagsMVK flags;
|
||||||
|
const void* pView;
|
||||||
|
} VkMacOSSurfaceCreateInfoMVK;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateMacOSSurfaceMVK)(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateMacOSSurfaceMVK(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkMacOSSurfaceCreateInfoMVK* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
#ifndef VULKAN_METAL_H_
|
||||||
|
#define VULKAN_METAL_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_EXT_metal_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_EXT_metal_surface 1
|
||||||
|
#ifdef __OBJC__
|
||||||
|
@class CAMetalLayer;
|
||||||
|
#else
|
||||||
|
typedef void CAMetalLayer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define VK_EXT_METAL_SURFACE_SPEC_VERSION 1
|
||||||
|
#define VK_EXT_METAL_SURFACE_EXTENSION_NAME "VK_EXT_metal_surface"
|
||||||
|
typedef VkFlags VkMetalSurfaceCreateFlagsEXT;
|
||||||
|
typedef struct VkMetalSurfaceCreateInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkMetalSurfaceCreateFlagsEXT flags;
|
||||||
|
const CAMetalLayer* pLayer;
|
||||||
|
} VkMetalSurfaceCreateInfoEXT;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateMetalSurfaceEXT)(VkInstance instance, const VkMetalSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateMetalSurfaceEXT(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_EXT_metal_objects is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_EXT_metal_objects 1
|
||||||
|
#ifdef __OBJC__
|
||||||
|
@protocol MTLDevice;
|
||||||
|
typedef __unsafe_unretained id<MTLDevice> MTLDevice_id;
|
||||||
|
#else
|
||||||
|
typedef void* MTLDevice_id;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
@protocol MTLCommandQueue;
|
||||||
|
typedef __unsafe_unretained id<MTLCommandQueue> MTLCommandQueue_id;
|
||||||
|
#else
|
||||||
|
typedef void* MTLCommandQueue_id;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
@protocol MTLBuffer;
|
||||||
|
typedef __unsafe_unretained id<MTLBuffer> MTLBuffer_id;
|
||||||
|
#else
|
||||||
|
typedef void* MTLBuffer_id;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
@protocol MTLTexture;
|
||||||
|
typedef __unsafe_unretained id<MTLTexture> MTLTexture_id;
|
||||||
|
#else
|
||||||
|
typedef void* MTLTexture_id;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct __IOSurface* IOSurfaceRef;
|
||||||
|
#ifdef __OBJC__
|
||||||
|
@protocol MTLSharedEvent;
|
||||||
|
typedef __unsafe_unretained id<MTLSharedEvent> MTLSharedEvent_id;
|
||||||
|
#else
|
||||||
|
typedef void* MTLSharedEvent_id;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define VK_EXT_METAL_OBJECTS_SPEC_VERSION 2
|
||||||
|
#define VK_EXT_METAL_OBJECTS_EXTENSION_NAME "VK_EXT_metal_objects"
|
||||||
|
|
||||||
|
typedef enum VkExportMetalObjectTypeFlagBitsEXT {
|
||||||
|
VK_EXPORT_METAL_OBJECT_TYPE_METAL_DEVICE_BIT_EXT = 0x00000001,
|
||||||
|
VK_EXPORT_METAL_OBJECT_TYPE_METAL_COMMAND_QUEUE_BIT_EXT = 0x00000002,
|
||||||
|
VK_EXPORT_METAL_OBJECT_TYPE_METAL_BUFFER_BIT_EXT = 0x00000004,
|
||||||
|
VK_EXPORT_METAL_OBJECT_TYPE_METAL_TEXTURE_BIT_EXT = 0x00000008,
|
||||||
|
VK_EXPORT_METAL_OBJECT_TYPE_METAL_IOSURFACE_BIT_EXT = 0x00000010,
|
||||||
|
VK_EXPORT_METAL_OBJECT_TYPE_METAL_SHARED_EVENT_BIT_EXT = 0x00000020,
|
||||||
|
VK_EXPORT_METAL_OBJECT_TYPE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF
|
||||||
|
} VkExportMetalObjectTypeFlagBitsEXT;
|
||||||
|
typedef VkFlags VkExportMetalObjectTypeFlagsEXT;
|
||||||
|
typedef struct VkExportMetalObjectCreateInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkExportMetalObjectTypeFlagBitsEXT exportObjectType;
|
||||||
|
} VkExportMetalObjectCreateInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkExportMetalObjectsInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
} VkExportMetalObjectsInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkExportMetalDeviceInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
MTLDevice_id mtlDevice;
|
||||||
|
} VkExportMetalDeviceInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkExportMetalCommandQueueInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkQueue queue;
|
||||||
|
MTLCommandQueue_id mtlCommandQueue;
|
||||||
|
} VkExportMetalCommandQueueInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkExportMetalBufferInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
MTLBuffer_id mtlBuffer;
|
||||||
|
} VkExportMetalBufferInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkImportMetalBufferInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
MTLBuffer_id mtlBuffer;
|
||||||
|
} VkImportMetalBufferInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkExportMetalTextureInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkImage image;
|
||||||
|
VkImageView imageView;
|
||||||
|
VkBufferView bufferView;
|
||||||
|
VkImageAspectFlagBits plane;
|
||||||
|
MTLTexture_id mtlTexture;
|
||||||
|
} VkExportMetalTextureInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkImportMetalTextureInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkImageAspectFlagBits plane;
|
||||||
|
MTLTexture_id mtlTexture;
|
||||||
|
} VkImportMetalTextureInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkExportMetalIOSurfaceInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkImage image;
|
||||||
|
IOSurfaceRef ioSurface;
|
||||||
|
} VkExportMetalIOSurfaceInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkImportMetalIOSurfaceInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
IOSurfaceRef ioSurface;
|
||||||
|
} VkImportMetalIOSurfaceInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkExportMetalSharedEventInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkSemaphore semaphore;
|
||||||
|
VkEvent event;
|
||||||
|
MTLSharedEvent_id mtlSharedEvent;
|
||||||
|
} VkExportMetalSharedEventInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkImportMetalSharedEventInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
MTLSharedEvent_id mtlSharedEvent;
|
||||||
|
} VkImportMetalSharedEventInfoEXT;
|
||||||
|
|
||||||
|
typedef void (VKAPI_PTR *PFN_vkExportMetalObjectsEXT)(VkDevice device, VkExportMetalObjectsInfoEXT* pMetalObjectsInfo);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR void VKAPI_CALL vkExportMetalObjectsEXT(
|
||||||
|
VkDevice device,
|
||||||
|
VkExportMetalObjectsInfoEXT* pMetalObjectsInfo);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
#ifndef VULKAN_SCREEN_H_
|
||||||
|
#define VULKAN_SCREEN_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_QNX_screen_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_QNX_screen_surface 1
|
||||||
|
#define VK_QNX_SCREEN_SURFACE_SPEC_VERSION 1
|
||||||
|
#define VK_QNX_SCREEN_SURFACE_EXTENSION_NAME "VK_QNX_screen_surface"
|
||||||
|
typedef VkFlags VkScreenSurfaceCreateFlagsQNX;
|
||||||
|
typedef struct VkScreenSurfaceCreateInfoQNX {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkScreenSurfaceCreateFlagsQNX flags;
|
||||||
|
struct _screen_context* context;
|
||||||
|
struct _screen_window* window;
|
||||||
|
} VkScreenSurfaceCreateInfoQNX;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateScreenSurfaceQNX)(VkInstance instance, const VkScreenSurfaceCreateInfoQNX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceScreenPresentationSupportQNX)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, struct _screen_window* window);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateScreenSurfaceQNX(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkScreenSurfaceCreateInfoQNX* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceScreenPresentationSupportQNX(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
uint32_t queueFamilyIndex,
|
||||||
|
struct _screen_window* window);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_QNX_external_memory_screen_buffer is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_QNX_external_memory_screen_buffer 1
|
||||||
|
#define VK_QNX_EXTERNAL_MEMORY_SCREEN_BUFFER_SPEC_VERSION 1
|
||||||
|
#define VK_QNX_EXTERNAL_MEMORY_SCREEN_BUFFER_EXTENSION_NAME "VK_QNX_external_memory_screen_buffer"
|
||||||
|
typedef struct VkScreenBufferPropertiesQNX {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkDeviceSize allocationSize;
|
||||||
|
uint32_t memoryTypeBits;
|
||||||
|
} VkScreenBufferPropertiesQNX;
|
||||||
|
|
||||||
|
typedef struct VkScreenBufferFormatPropertiesQNX {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkFormat format;
|
||||||
|
uint64_t externalFormat;
|
||||||
|
uint64_t screenUsage;
|
||||||
|
VkFormatFeatureFlags formatFeatures;
|
||||||
|
VkComponentMapping samplerYcbcrConversionComponents;
|
||||||
|
VkSamplerYcbcrModelConversion suggestedYcbcrModel;
|
||||||
|
VkSamplerYcbcrRange suggestedYcbcrRange;
|
||||||
|
VkChromaLocation suggestedXChromaOffset;
|
||||||
|
VkChromaLocation suggestedYChromaOffset;
|
||||||
|
} VkScreenBufferFormatPropertiesQNX;
|
||||||
|
|
||||||
|
typedef struct VkImportScreenBufferInfoQNX {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
struct _screen_buffer* buffer;
|
||||||
|
} VkImportScreenBufferInfoQNX;
|
||||||
|
|
||||||
|
typedef struct VkExternalFormatQNX {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
uint64_t externalFormat;
|
||||||
|
} VkExternalFormatQNX;
|
||||||
|
|
||||||
|
typedef struct VkPhysicalDeviceExternalMemoryScreenBufferFeaturesQNX {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkBool32 screenBufferImport;
|
||||||
|
} VkPhysicalDeviceExternalMemoryScreenBufferFeaturesQNX;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetScreenBufferPropertiesQNX)(VkDevice device, const struct _screen_buffer* buffer, VkScreenBufferPropertiesQNX* pProperties);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetScreenBufferPropertiesQNX(
|
||||||
|
VkDevice device,
|
||||||
|
const struct _screen_buffer* buffer,
|
||||||
|
VkScreenBufferPropertiesQNX* pProperties);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef VULKAN_VI_H_
|
||||||
|
#define VULKAN_VI_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_NN_vi_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_NN_vi_surface 1
|
||||||
|
#define VK_NN_VI_SURFACE_SPEC_VERSION 1
|
||||||
|
#define VK_NN_VI_SURFACE_EXTENSION_NAME "VK_NN_vi_surface"
|
||||||
|
typedef VkFlags VkViSurfaceCreateFlagsNN;
|
||||||
|
typedef struct VkViSurfaceCreateInfoNN {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkViSurfaceCreateFlagsNN flags;
|
||||||
|
void* window;
|
||||||
|
} VkViSurfaceCreateInfoNN;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateViSurfaceNN)(VkInstance instance, const VkViSurfaceCreateInfoNN* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateViSurfaceNN(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkViSurfaceCreateInfoNN* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef VULKAN_WAYLAND_H_
|
||||||
|
#define VULKAN_WAYLAND_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_wayland_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_wayland_surface 1
|
||||||
|
#define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6
|
||||||
|
#define VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME "VK_KHR_wayland_surface"
|
||||||
|
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
|
||||||
|
typedef struct VkWaylandSurfaceCreateInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkWaylandSurfaceCreateFlagsKHR flags;
|
||||||
|
struct wl_display* display;
|
||||||
|
struct wl_surface* surface;
|
||||||
|
} VkWaylandSurfaceCreateInfoKHR;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateWaylandSurfaceKHR)(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, struct wl_display* display);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateWaylandSurfaceKHR(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkWaylandSurfaceCreateInfoKHR* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWaylandPresentationSupportKHR(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
uint32_t queueFamilyIndex,
|
||||||
|
struct wl_display* display);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+342
@@ -0,0 +1,342 @@
|
|||||||
|
#ifndef VULKAN_WIN32_H_
|
||||||
|
#define VULKAN_WIN32_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_win32_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_win32_surface 1
|
||||||
|
#define VK_KHR_WIN32_SURFACE_SPEC_VERSION 6
|
||||||
|
#define VK_KHR_WIN32_SURFACE_EXTENSION_NAME "VK_KHR_win32_surface"
|
||||||
|
typedef VkFlags VkWin32SurfaceCreateFlagsKHR;
|
||||||
|
typedef struct VkWin32SurfaceCreateInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkWin32SurfaceCreateFlagsKHR flags;
|
||||||
|
HINSTANCE hinstance;
|
||||||
|
HWND hwnd;
|
||||||
|
} VkWin32SurfaceCreateInfoKHR;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateWin32SurfaceKHR)(VkInstance instance, const VkWin32SurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateWin32SurfaceKHR(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkWin32SurfaceCreateInfoKHR* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWin32PresentationSupportKHR(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
uint32_t queueFamilyIndex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_external_memory_win32 is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_external_memory_win32 1
|
||||||
|
#define VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
|
||||||
|
#define VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHR_external_memory_win32"
|
||||||
|
typedef struct VkImportMemoryWin32HandleInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkExternalMemoryHandleTypeFlagBits handleType;
|
||||||
|
HANDLE handle;
|
||||||
|
LPCWSTR name;
|
||||||
|
} VkImportMemoryWin32HandleInfoKHR;
|
||||||
|
|
||||||
|
typedef struct VkExportMemoryWin32HandleInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
const SECURITY_ATTRIBUTES* pAttributes;
|
||||||
|
DWORD dwAccess;
|
||||||
|
LPCWSTR name;
|
||||||
|
} VkExportMemoryWin32HandleInfoKHR;
|
||||||
|
|
||||||
|
typedef struct VkMemoryWin32HandlePropertiesKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
uint32_t memoryTypeBits;
|
||||||
|
} VkMemoryWin32HandlePropertiesKHR;
|
||||||
|
|
||||||
|
typedef struct VkMemoryGetWin32HandleInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
VkExternalMemoryHandleTypeFlagBits handleType;
|
||||||
|
} VkMemoryGetWin32HandleInfoKHR;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleKHR)(VkDevice device, const VkMemoryGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandlePropertiesKHR)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkMemoryGetWin32HandleInfoKHR* pGetWin32HandleInfo,
|
||||||
|
HANDLE* pHandle);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandlePropertiesKHR(
|
||||||
|
VkDevice device,
|
||||||
|
VkExternalMemoryHandleTypeFlagBits handleType,
|
||||||
|
HANDLE handle,
|
||||||
|
VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_win32_keyed_mutex is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_win32_keyed_mutex 1
|
||||||
|
#define VK_KHR_WIN32_KEYED_MUTEX_SPEC_VERSION 1
|
||||||
|
#define VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHR_win32_keyed_mutex"
|
||||||
|
typedef struct VkWin32KeyedMutexAcquireReleaseInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
uint32_t acquireCount;
|
||||||
|
const VkDeviceMemory* pAcquireSyncs;
|
||||||
|
const uint64_t* pAcquireKeys;
|
||||||
|
const uint32_t* pAcquireTimeouts;
|
||||||
|
uint32_t releaseCount;
|
||||||
|
const VkDeviceMemory* pReleaseSyncs;
|
||||||
|
const uint64_t* pReleaseKeys;
|
||||||
|
} VkWin32KeyedMutexAcquireReleaseInfoKHR;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_external_semaphore_win32 is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_external_semaphore_win32 1
|
||||||
|
#define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1
|
||||||
|
#define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHR_external_semaphore_win32"
|
||||||
|
typedef struct VkImportSemaphoreWin32HandleInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkSemaphore semaphore;
|
||||||
|
VkSemaphoreImportFlags flags;
|
||||||
|
VkExternalSemaphoreHandleTypeFlagBits handleType;
|
||||||
|
HANDLE handle;
|
||||||
|
LPCWSTR name;
|
||||||
|
} VkImportSemaphoreWin32HandleInfoKHR;
|
||||||
|
|
||||||
|
typedef struct VkExportSemaphoreWin32HandleInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
const SECURITY_ATTRIBUTES* pAttributes;
|
||||||
|
DWORD dwAccess;
|
||||||
|
LPCWSTR name;
|
||||||
|
} VkExportSemaphoreWin32HandleInfoKHR;
|
||||||
|
|
||||||
|
typedef struct VkD3D12FenceSubmitInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
uint32_t waitSemaphoreValuesCount;
|
||||||
|
const uint64_t* pWaitSemaphoreValues;
|
||||||
|
uint32_t signalSemaphoreValuesCount;
|
||||||
|
const uint64_t* pSignalSemaphoreValues;
|
||||||
|
} VkD3D12FenceSubmitInfoKHR;
|
||||||
|
|
||||||
|
typedef struct VkSemaphoreGetWin32HandleInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkSemaphore semaphore;
|
||||||
|
VkExternalSemaphoreHandleTypeFlagBits handleType;
|
||||||
|
} VkSemaphoreGetWin32HandleInfoKHR;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreWin32HandleKHR)(VkDevice device, const VkImportSemaphoreWin32HandleInfoKHR* pImportSemaphoreWin32HandleInfo);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreWin32HandleKHR)(VkDevice device, const VkSemaphoreGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreWin32HandleKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkImportSemaphoreWin32HandleInfoKHR* pImportSemaphoreWin32HandleInfo);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreWin32HandleKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkSemaphoreGetWin32HandleInfoKHR* pGetWin32HandleInfo,
|
||||||
|
HANDLE* pHandle);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_external_fence_win32 is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_external_fence_win32 1
|
||||||
|
#define VK_KHR_EXTERNAL_FENCE_WIN32_SPEC_VERSION 1
|
||||||
|
#define VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME "VK_KHR_external_fence_win32"
|
||||||
|
typedef struct VkImportFenceWin32HandleInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkFence fence;
|
||||||
|
VkFenceImportFlags flags;
|
||||||
|
VkExternalFenceHandleTypeFlagBits handleType;
|
||||||
|
HANDLE handle;
|
||||||
|
LPCWSTR name;
|
||||||
|
} VkImportFenceWin32HandleInfoKHR;
|
||||||
|
|
||||||
|
typedef struct VkExportFenceWin32HandleInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
const SECURITY_ATTRIBUTES* pAttributes;
|
||||||
|
DWORD dwAccess;
|
||||||
|
LPCWSTR name;
|
||||||
|
} VkExportFenceWin32HandleInfoKHR;
|
||||||
|
|
||||||
|
typedef struct VkFenceGetWin32HandleInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkFence fence;
|
||||||
|
VkExternalFenceHandleTypeFlagBits handleType;
|
||||||
|
} VkFenceGetWin32HandleInfoKHR;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkImportFenceWin32HandleKHR)(VkDevice device, const VkImportFenceWin32HandleInfoKHR* pImportFenceWin32HandleInfo);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetFenceWin32HandleKHR)(VkDevice device, const VkFenceGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkImportFenceWin32HandleKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkImportFenceWin32HandleInfoKHR* pImportFenceWin32HandleInfo);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceWin32HandleKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkFenceGetWin32HandleInfoKHR* pGetWin32HandleInfo,
|
||||||
|
HANDLE* pHandle);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_NV_external_memory_win32 is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_NV_external_memory_win32 1
|
||||||
|
#define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
|
||||||
|
#define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32"
|
||||||
|
typedef struct VkImportMemoryWin32HandleInfoNV {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkExternalMemoryHandleTypeFlagsNV handleType;
|
||||||
|
HANDLE handle;
|
||||||
|
} VkImportMemoryWin32HandleInfoNV;
|
||||||
|
|
||||||
|
typedef struct VkExportMemoryWin32HandleInfoNV {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
const SECURITY_ATTRIBUTES* pAttributes;
|
||||||
|
DWORD dwAccess;
|
||||||
|
} VkExportMemoryWin32HandleInfoNV;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV(
|
||||||
|
VkDevice device,
|
||||||
|
VkDeviceMemory memory,
|
||||||
|
VkExternalMemoryHandleTypeFlagsNV handleType,
|
||||||
|
HANDLE* pHandle);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_NV_win32_keyed_mutex is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_NV_win32_keyed_mutex 1
|
||||||
|
#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 2
|
||||||
|
#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
|
||||||
|
typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
uint32_t acquireCount;
|
||||||
|
const VkDeviceMemory* pAcquireSyncs;
|
||||||
|
const uint64_t* pAcquireKeys;
|
||||||
|
const uint32_t* pAcquireTimeoutMilliseconds;
|
||||||
|
uint32_t releaseCount;
|
||||||
|
const VkDeviceMemory* pReleaseSyncs;
|
||||||
|
const uint64_t* pReleaseKeys;
|
||||||
|
} VkWin32KeyedMutexAcquireReleaseInfoNV;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_EXT_full_screen_exclusive is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_EXT_full_screen_exclusive 1
|
||||||
|
#define VK_EXT_FULL_SCREEN_EXCLUSIVE_SPEC_VERSION 4
|
||||||
|
#define VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME "VK_EXT_full_screen_exclusive"
|
||||||
|
|
||||||
|
typedef enum VkFullScreenExclusiveEXT {
|
||||||
|
VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT = 0,
|
||||||
|
VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT = 1,
|
||||||
|
VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT = 2,
|
||||||
|
VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT = 3,
|
||||||
|
VK_FULL_SCREEN_EXCLUSIVE_MAX_ENUM_EXT = 0x7FFFFFFF
|
||||||
|
} VkFullScreenExclusiveEXT;
|
||||||
|
typedef struct VkSurfaceFullScreenExclusiveInfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkFullScreenExclusiveEXT fullScreenExclusive;
|
||||||
|
} VkSurfaceFullScreenExclusiveInfoEXT;
|
||||||
|
|
||||||
|
typedef struct VkSurfaceCapabilitiesFullScreenExclusiveEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
void* pNext;
|
||||||
|
VkBool32 fullScreenExclusiveSupported;
|
||||||
|
} VkSurfaceCapabilitiesFullScreenExclusiveEXT;
|
||||||
|
|
||||||
|
typedef struct VkSurfaceFullScreenExclusiveWin32InfoEXT {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
HMONITOR hmonitor;
|
||||||
|
} VkSurfaceFullScreenExclusiveWin32InfoEXT;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModes2EXT)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkAcquireFullScreenExclusiveModeEXT)(VkDevice device, VkSwapchainKHR swapchain);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkReleaseFullScreenExclusiveModeEXT)(VkDevice device, VkSwapchainKHR swapchain);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModes2EXT)(VkDevice device, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkDeviceGroupPresentModeFlagsKHR* pModes);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModes2EXT(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
|
||||||
|
uint32_t* pPresentModeCount,
|
||||||
|
VkPresentModeKHR* pPresentModes);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireFullScreenExclusiveModeEXT(
|
||||||
|
VkDevice device,
|
||||||
|
VkSwapchainKHR swapchain);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkReleaseFullScreenExclusiveModeEXT(
|
||||||
|
VkDevice device,
|
||||||
|
VkSwapchainKHR swapchain);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModes2EXT(
|
||||||
|
VkDevice device,
|
||||||
|
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
|
||||||
|
VkDeviceGroupPresentModeFlagsKHR* pModes);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// VK_NV_acquire_winrt_display is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_NV_acquire_winrt_display 1
|
||||||
|
#define VK_NV_ACQUIRE_WINRT_DISPLAY_SPEC_VERSION 1
|
||||||
|
#define VK_NV_ACQUIRE_WINRT_DISPLAY_EXTENSION_NAME "VK_NV_acquire_winrt_display"
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkAcquireWinrtDisplayNV)(VkPhysicalDevice physicalDevice, VkDisplayKHR display);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetWinrtDisplayNV)(VkPhysicalDevice physicalDevice, uint32_t deviceRelativeId, VkDisplayKHR* pDisplay);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireWinrtDisplayNV(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
VkDisplayKHR display);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetWinrtDisplayNV(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
uint32_t deviceRelativeId,
|
||||||
|
VkDisplayKHR* pDisplay);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
#ifndef VULKAN_XCB_H_
|
||||||
|
#define VULKAN_XCB_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_xcb_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_xcb_surface 1
|
||||||
|
#define VK_KHR_XCB_SURFACE_SPEC_VERSION 6
|
||||||
|
#define VK_KHR_XCB_SURFACE_EXTENSION_NAME "VK_KHR_xcb_surface"
|
||||||
|
typedef VkFlags VkXcbSurfaceCreateFlagsKHR;
|
||||||
|
typedef struct VkXcbSurfaceCreateInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkXcbSurfaceCreateFlagsKHR flags;
|
||||||
|
xcb_connection_t* connection;
|
||||||
|
xcb_window_t window;
|
||||||
|
} VkXcbSurfaceCreateInfoKHR;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateXcbSurfaceKHR)(VkInstance instance, const VkXcbSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateXcbSurfaceKHR(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkXcbSurfaceCreateInfoKHR* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXcbPresentationSupportKHR(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
uint32_t queueFamilyIndex,
|
||||||
|
xcb_connection_t* connection,
|
||||||
|
xcb_visualid_t visual_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
#ifndef VULKAN_XLIB_H_
|
||||||
|
#define VULKAN_XLIB_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_KHR_xlib_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_KHR_xlib_surface 1
|
||||||
|
#define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6
|
||||||
|
#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface"
|
||||||
|
typedef VkFlags VkXlibSurfaceCreateFlagsKHR;
|
||||||
|
typedef struct VkXlibSurfaceCreateInfoKHR {
|
||||||
|
VkStructureType sType;
|
||||||
|
const void* pNext;
|
||||||
|
VkXlibSurfaceCreateFlagsKHR flags;
|
||||||
|
Display* dpy;
|
||||||
|
Window window;
|
||||||
|
} VkXlibSurfaceCreateInfoKHR;
|
||||||
|
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||||
|
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display* dpy, VisualID visualID);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateXlibSurfaceKHR(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkXlibSurfaceCreateInfoKHR* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSurfaceKHR* pSurface);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXlibPresentationSupportKHR(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
uint32_t queueFamilyIndex,
|
||||||
|
Display* dpy,
|
||||||
|
VisualID visualID);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef VULKAN_XLIB_XRANDR_H_
|
||||||
|
#define VULKAN_XLIB_XRANDR_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2015-2024 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos Vulkan XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VK_EXT_acquire_xlib_display is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define VK_EXT_acquire_xlib_display 1
|
||||||
|
#define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1
|
||||||
|
#define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display"
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkAcquireXlibDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, VkDisplayKHR display);
|
||||||
|
typedef VkResult (VKAPI_PTR *PFN_vkGetRandROutputDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, RROutput rrOutput, VkDisplayKHR* pDisplay);
|
||||||
|
|
||||||
|
#ifndef VK_NO_PROTOTYPES
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireXlibDisplayEXT(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
Display* dpy,
|
||||||
|
VkDisplayKHR display);
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkGetRandROutputDisplayEXT(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
Display* dpy,
|
||||||
|
RROutput rrOutput,
|
||||||
|
VkDisplayKHR* pDisplay);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user