From fad5eb4e55e13e00ba0f4d2740ea2ff2fce444c9 Mon Sep 17 00:00:00 2001 From: Emil <65846814+emil28092005@users.noreply.github.com> Date: Fri, 18 Sep 2026 03:43:22 +0300 Subject: [PATCH] Add Windows native editor and software Vulkan export acceptance --- .github/workflows/windows-graphics.yml | 57 +++++++++++++ tools/ci/prepare_windows_vulkan.py | 111 +++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 .github/workflows/windows-graphics.yml create mode 100644 tools/ci/prepare_windows_vulkan.py diff --git a/.github/workflows/windows-graphics.yml b/.github/workflows/windows-graphics.yml new file mode 100644 index 0000000..3b318a2 --- /dev/null +++ b/.github/workflows/windows-graphics.yml @@ -0,0 +1,57 @@ +name: Windows editor and software Vulkan +on: + push: + pull_request: + workflow_dispatch: +permissions: + contents: read +concurrency: + group: windows-graphics-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + windows-graphics: + runs-on: windows-2025 + timeout-minutes: 90 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 + with: + python-version: '3.12' + - name: Visual Studio x64 environment + uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 + with: + arch: x64 + - name: Cache pinned Vulkan test tools + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 + with: + key: windows-2025-vulkan-${{ hashFiles('tools/ci/prepare_windows_vulkan.py') }} + path: | + .cache/windows-graphics/sdk + .cache/windows-graphics/driver + - name: Build official Vulkan loader and SwiftShader from pinned sources + run: python tools/ci/prepare_windows_vulkan.py + - name: Fetch checksum-verified Slang compiler + run: python tools/fetch_slang.py + - name: Configure full editor and Player + run: cmake --preset windows-debug -DFASET_BUILD_RENDERER=ON -DFASET_BUILD_EDITOR=ON + - name: Build full editor and tests + run: cmake --build --preset windows-debug --parallel 2 + - name: CPU contracts and software GPU pixel tests + run: ctest --preset windows-debug --timeout 180 + - name: Real Release exports, 2D and 3D execution, incremental Debug rebuild + run: build/windows-debug/faset_build_service_tests.exe --integration .cache/windows-export-e2e + - name: Preserve graphics and export evidence + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: windows-graphics-evidence + if-no-files-found: warn + retention-days: 14 + include-hidden-files: true + path: | + .cache/windows-graphics/toolchain.json + build/windows-debug/Testing/Temporary/LastTest.log + build/windows-debug/*test.ppm + .cache/windows-export-e2e/result-*.json + .cache/windows-export-e2e/export-*/generations/*/manifest.json + .cache/windows-export-e2e/export-*/generations/*/verification.ppm diff --git a/tools/ci/prepare_windows_vulkan.py b/tools/ci/prepare_windows_vulkan.py new file mode 100644 index 0000000..9bbd047 --- /dev/null +++ b/tools/ci/prepare_windows_vulkan.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Build a pinned Vulkan loader and CPU driver for Windows graphics CI. + +These are test tools, not engine or exported-game dependencies. This intentionally +uses upstream source archives rather than an unofficial prebuilt driver. It does +not install the Khronos validation layer; GPU pixel tests and packaging tests run +against SwiftShader; Khronos validation-layer runs are a separate check. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import tarfile +import urllib.request + +SOURCES = { + "headers": ("KhronosGroup/Vulkan-Headers", "0d3f509e57041fbd073a1ee84cd9ccd36d148446", "e82005a6bd3289ce213232ef41e0f8722d1365ed72bf5cc4be56911c56bc30e0"), + "loader": ("KhronosGroup/Vulkan-Loader", "32fcb949e253cbeb40cda7ea76122b492db579ae", "610fa9017226c49bc4f19398d94fce9e3c9ff94c14f6420f9fe24822707feacf"), + "swiftshader": ("google/swiftshader", "1e80438d2b93ef36a7c05f8d2b81233bac0e3d16", "1c3a1afc397c7aa4d3275790bc9b451e80b144a1db665135d5519838bacf44a8"), +} + + +def run(*arguments: str | Path) -> None: + command = [str(argument) for argument in arguments] + print("+ " + subprocess.list2cmdline(command), flush=True) + subprocess.run(command, check=True) + + +def source(cache: Path, name: str) -> Path: + repository, commit, expected = SOURCES[name] + directory = cache / "sources" / name + if (directory / "CMakeLists.txt").is_file(): + return directory + archives = cache / "downloads" + archives.mkdir(parents=True, exist_ok=True) + archive = archives / f"{name}-{commit}.tar.gz" + if not archive.is_file(): + temporary = archive.with_suffix(".download") + urllib.request.urlretrieve(f"https://codeload.github.com/{repository}/tar.gz/{commit}", temporary) + temporary.replace(archive) + with archive.open("rb") as stream: + digest = hashlib.file_digest(stream, "sha256").hexdigest() + if digest != expected: + raise RuntimeError(f"{name} source archive checksum mismatch") + extraction = cache / "sources" / f".{name}-extract" + extraction.mkdir(parents=True, exist_ok=True) + with tarfile.open(archive) as contents: + contents.extractall(extraction, filter="data") + roots = list(extraction.iterdir()) + if len(roots) != 1 or not roots[0].is_dir(): + raise RuntimeError(f"Unexpected source archive structure: {name}") + roots[0].rename(directory) + extraction.rmdir() + return directory + + +def configure(source_path: Path, build: Path, *arguments: str) -> None: + run("cmake", "-S", source_path, "-B", build, "-G", "Ninja", "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_C_COMPILER=cl", "-DCMAKE_CXX_COMPILER=cl", *arguments) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--cache", type=Path, default=Path(".cache/windows-graphics")) + args = parser.parse_args() + if sys.platform != "win32": + parser.error("Run this helper on Windows in a Visual Studio x64 developer environment") + for program in ("cmake", "ninja", "cl"): + if not shutil.which(program): + raise RuntimeError(f"Required tool missing: {program}") + cache = args.cache.resolve() + sdk, driver = cache / "sdk", cache / "driver" + if not all((sdk / path).is_file() for path in ("bin/vulkan-1.dll", "lib/vulkan-1.lib", "include/vulkan/vulkan.h")): + headers = source(cache, "headers") + configure(headers, cache / "headers-build", f"-DCMAKE_INSTALL_PREFIX={sdk}", "-DVULKAN_HEADERS_ENABLE_TESTS=OFF", "-DVULKAN_HEADERS_ENABLE_MODULE=OFF") + run("cmake", "--install", cache / "headers-build") + loader = source(cache, "loader") + configure(loader, cache / "loader-build", f"-DCMAKE_INSTALL_PREFIX={sdk}", f"-DCMAKE_PREFIX_PATH={sdk}", "-DBUILD_TESTS=OFF", "-DBUILD_WERROR=OFF") + run("cmake", "--build", cache / "loader-build", "--parallel", "2") + run("cmake", "--install", cache / "loader-build") + if not (driver / "vk_swiftshader_icd.json").is_file(): + swift = source(cache, "swiftshader") + build = cache / "swiftshader-build" + configure(swift, build, "-DSWIFTSHADER_BUILD_TESTS=OFF", "-DSWIFTSHADER_BUILD_BENCHMARKS=OFF", "-DSWIFTSHADER_BUILD_PVR=OFF", "-DSWIFTSHADER_WARNINGS_AS_ERRORS=OFF") + run("cmake", "--build", build, "--target", "vk_swiftshader", "--parallel", "2") + manifest_directory = build / "Windows" + manifest = json.loads((manifest_directory / "vk_swiftshader_icd.json").read_text(encoding="utf-8")) + library = (manifest_directory / manifest["ICD"]["library_path"]).resolve() + driver.mkdir(parents=True, exist_ok=True) + shutil.copy2(library, driver / library.name) + manifest["ICD"]["library_path"] = "./" + library.name + (driver / "vk_swiftshader_icd.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8") + report = {"sources": {name: {"repository": repository, "commit": commit, "sha256": digest} for name, (repository, commit, digest) in SOURCES.items()}, "validation_layer": False, "vulkan_sdk": str(sdk), "driver": str(driver / "vk_swiftshader_icd.json")} + cache.mkdir(parents=True, exist_ok=True) + (cache / "toolchain.json").write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8") + if "GITHUB_ENV" in os.environ: + with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as output: + output.write(f"VULKAN_SDK={sdk}\nVK_DRIVER_FILES={report['driver']}\nVK_ICD_FILENAMES={report['driver']}\n") + with open(os.environ["GITHUB_PATH"], "a", encoding="utf-8") as output: + output.write(str(sdk / "bin") + "\n") + print(json.dumps(report, indent=2)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())