Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
896f100
fix: order vulkan forward and taa pass writes before their readers
CanReader Oct 8, 2026
1c9d12a
fix: clear the vulkan shadow map when it is created
CanReader Oct 8, 2026
d4833fc
fix: seed the first vulkan taa reprojection with the current matrix
CanReader Oct 8, 2026
6bac533
feat: add vulkan gpu pass timing and a deterministic frame capture
CanReader Oct 8, 2026
318b746
perf: load the vulkan validation layer only in debug or with --validate
CanReader Oct 8, 2026
acd2769
perf: skip repeated vulkan shadow push constants
CanReader Oct 8, 2026
816542d
perf: remove vulkan upload stalls and slow constant buffer reads
CanReader Oct 8, 2026
746702b
perf: persist the vulkan pipeline cache between runs
CanReader Oct 8, 2026
6d3abef
perf: build vulkan custom-format pipelines before their first draw
CanReader Oct 8, 2026
0b3c401
perf: enable only the vulkan device features the engine uses
CanReader Oct 8, 2026
4c90687
fix: declare only the vertex attributes vulkan forward shaders read
CanReader Oct 8, 2026
6926cac
fix: stop leaking vulkan shaders, pools and texture descriptor sets
CanReader Oct 8, 2026
36d6f3b
fix: grow vulkan per-image semaphores when the swapchain is recreated
CanReader Oct 8, 2026
85ac56f
perf: recreate the vulkan swapchain on suboptimal only when it resized
CanReader Oct 8, 2026
67db5f6
perf: read the vulkan taa resolve directly instead of copying it back
CanReader Oct 8, 2026
aa03f78
perf: skip dead ssr and bloom fetches in the vulkan composite
CanReader Oct 8, 2026
a9cbcb2
perf: gather the taa depth neighborhood and fetch its color taps
CanReader Oct 8, 2026
4e2cbf8
perf: fetch the gbuffer and skip back-face pcf in the vulkan lighting…
CanReader Oct 8, 2026
140430d
fix: apply ssao in the vulkan lighting pass when ibl is off
CanReader Oct 8, 2026
156d108
fix: warn once when ibl is requested on vulkan
CanReader Oct 8, 2026
0964165
fix: keep the imgui frame open on vulkan frames that fail to acquire
CanReader Oct 8, 2026
67a3969
perf: use a precomputed vogel disk for the vulkan shadow pcf
CanReader Oct 8, 2026
e356f20
fix: sample vulkan shadows with the matrix the map was rendered with
CanReader Oct 8, 2026
3f9928b
feat: expose the light matrix the next shadow pass renders with
CanReader Oct 8, 2026
c9aea73
fix: give every vulkan bone palette bind its own uniform slot
CanReader Oct 8, 2026
e92204c
fix: sample opengl shadows with the matrix the map was rendered with
CanReader Oct 9, 2026
1919fd8
refactor: replace bare literals with named constants
CanReader Oct 18, 2026
114447b
feat: let transparent vertex formats read the scene behind them
CanReader Oct 18, 2026
5ee11a9
feat: snapshot the scene for scene-reading transparent draws on vulkan
CanReader Oct 18, 2026
f0c7d24
feat: snapshot the scene for scene-reading transparent draws on opengl
CanReader Oct 18, 2026
5d3dd88
feat(Render): add deferred rendering enabled
CanReader Oct 24, 2026
2088c94
feat: mark transparent draws as taa-reactive in the scene alpha
CanReader Oct 25, 2026
095c4bc
fix: reproject vulkan taa in double precision and tighten its history…
CanReader Oct 25, 2026
1f643ac
feat: add rcas sharpening after taa on vulkan
CanReader Oct 25, 2026
f60d171
ci: route changes through staging
CanReader Oct 25, 2026
f6188e2
Merge pull request #216 from CanReader/ci/route-through-staging
CanReader Oct 25, 2026
807cc94
Merge branch 'staging' into vulkan-optimization
CanReader Oct 25, 2026
92180f8
style: apply clang-format to changed lines
CanReader Oct 25, 2026
fe77bd3
fix: avoid the windows far macro in the vulkan scene-read clear
CanReader Oct 25, 2026
690f50e
Merge pull request #217 from CanReader/vulkan-optimization
CanReader Oct 25, 2026
a30430f
feat: hot reload shaders in debug builds
CanReader Oct 31, 2026
9948e4f
feat: rebuild vulkan pipelines on shader reload
CanReader Oct 31, 2026
dd02552
feat: relink opengl programs on shader reload
CanReader Oct 31, 2026
0387f94
style: format changed lines, poll hot reload right before BeginRender
CanReader Oct 31, 2026
eb42961
feat: recompile directx 11 shaders on reload
CanReader Oct 31, 2026
a4a58d0
feat: rebuild directx 12 pipelines on shader reload
CanReader Oct 31, 2026
59c07c5
Merge pull request #224 from CanReader/Shader-HotReload
CanReader Nov 1, 2026
b191466
fix: make vector subscript constexpr for msvc
CanReader Nov 1, 2026
dd6dd6a
fix: fall back when the d3d11 debug layer is missing
CanReader Nov 1, 2026
2f1b2d1
style: format changed lines
CanReader Nov 1, 2026
f59f179
ci: build the downstream game against this engine checkout
CanReader Nov 1, 2026
f922aaf
Merge pull request #225 from CanReader/fix/windows-debug-build
CanReader Nov 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ version: 2
updates:
- package-ecosystem: github-actions
directory: /
# Updates are tested in staging like every other change; main only takes
# pull requests from staging.
target-branch: staging
schedule:
interval: monthly
commit-message:
Expand Down
2 changes: 2 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- Open this against staging. main only accepts pull requests from staging; see CONTRIBUTING.md. -->

## What this changes

<!-- One or two sentences. What behavior is different after this merges? -->
Expand Down
27 changes: 27 additions & 0 deletions .github/rulesets/approval.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "Maintainer approval",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["refs/heads/main", "refs/heads/staging"],
"exclude": []
}
},
"bypass_actors": [
{ "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "pull_request" }
],
"rules": [
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 1,
"dismiss_stale_reviews_on_push": true,
"require_code_owner_review": false,
"require_last_push_approval": false,
"required_review_thread_resolution": false,
"allowed_merge_methods": ["merge", "squash", "rebase"]
}
}
]
}
70 changes: 70 additions & 0 deletions .github/rulesets/main.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"name": "Protect main",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": [
"refs/heads/main"
],
"exclude": []
}
},
"bypass_actors": [],
"rules": [
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 0,
"dismiss_stale_reviews_on_push": false,
"require_code_owner_review": false,
"require_last_push_approval": false,
"required_review_thread_resolution": true,
"allowed_merge_methods": [
"merge"
]
}
},
{
"type": "required_status_checks",
"parameters": {
"strict_required_status_checks_policy": false,
"required_status_checks": [
{
"context": "Only staging merges into main"
},
{
"context": "Format (changed lines)"
},
{
"context": "Linux Debug"
},
{
"context": "Linux Release"
},
{
"context": "Linux Clang Release"
},
{
"context": "Windows Debug"
},
{
"context": "Windows Release"
},
{
"context": "Shaders compile and are committed"
},
{
"context": "Downstream game still builds"
}
]
}
}
]
}
69 changes: 69 additions & 0 deletions .github/rulesets/staging.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"name": "Protect staging",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": [
"refs/heads/staging"
],
"exclude": []
}
},
"bypass_actors": [],
"rules": [
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 0,
"dismiss_stale_reviews_on_push": false,
"require_code_owner_review": false,
"require_last_push_approval": false,
"required_review_thread_resolution": true,
"allowed_merge_methods": [
"merge",
"squash",
"rebase"
]
}
},
{
"type": "required_status_checks",
"parameters": {
"strict_required_status_checks_policy": false,
"required_status_checks": [
{
"context": "Format (changed lines)"
},
{
"context": "Linux Debug"
},
{
"context": "Linux Release"
},
{
"context": "Linux Clang Release"
},
{
"context": "Windows Debug"
},
{
"context": "Windows Release"
},
{
"context": "Shaders compile and are committed"
},
{
"context": "Downstream game still builds"
}
]
}
}
]
}
43 changes: 43 additions & 0 deletions .github/workflows/branch-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Branch policy

# staging is the testing layer: every change lands there first, and main only
# takes pull requests from staging. This check fails any other pull request
# into main. It also runs on pull requests into staging, where it always
# passes, so retargeting a pull request from main to staging clears the
# failure it picked up there.
#
# pull_request_target runs this file as it exists on the base branch, so a
# pull request cannot edit the check into passing. Nothing from the pull
# request is checked out or run, and the job holds no token permissions.
on:
pull_request_target:
branches: [main, staging]
types: [opened, reopened, synchronize, edited]

permissions: {}

jobs:
# The job name is a required status check in .github/rulesets/main.json.
# Keep the verdict inside the step: a job skipped by an `if:` reports
# success, which would satisfy the required check.
staging-to-main:
name: Only staging merges into main
runs-on: ubuntu-latest
steps:
- name: Check where the pull request comes from
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
run: |
if [ "$BASE_REF" != "main" ]; then
echo "Targets $BASE_REF, which accepts pull requests from any branch."
exit 0
fi
if [ "$HEAD_REPO" = "$GITHUB_REPOSITORY" ] && [ "$HEAD_REF" = "staging" ]; then
echo "Release pull request from staging into main."
exit 0
fi
echo "This pull request comes from ${HEAD_REPO:-a deleted fork}:${HEAD_REF}."
echo "::error::main only accepts pull requests from staging. Edit this pull request and change its base branch to staging."
exit 1
11 changes: 9 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [main]
branches: [main, staging]
pull_request:
branches: [main]
branches: [main, staging]
workflow_dispatch:

# A newer push to the same branch makes the older run irrelevant.
Expand Down Expand Up @@ -245,12 +245,19 @@ jobs:
submodules: recursive
path: SleakEngine

# Engine staging pairs with game staging, engine main with game main
- name: Check out the game
uses: actions/checkout@v7
with:
repository: CanReader/SleakCraft
ref: ${{ github.base_ref }}
path: SleakCraft

- name: Point the game at this engine revision
run: |
rm -rf SleakCraft/Engine
ln -s ../SleakEngine SleakCraft/Engine

- name: Install dependencies
run: |
sudo apt-get update
Expand Down
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ set(CORE_SOURCES
src/Core/Application.cpp
src/Core/CommandLine.cpp
src/Core/File.cpp
src/Core/FileWatcher.cpp
src/Core/Logger.cpp
src/Core/ScopedTimer.cpp
src/Core/Serializable.cpp
Expand All @@ -128,6 +129,7 @@ set(DEBUG_SOURCES
src/Debug/Benchmark.cpp
src/Debug/DebugLineRenderer.cpp
src/Debug/DebugOverlay.cpp
src/Debug/ShaderHotReload.cpp
src/Debug/SystemMetrics.cpp
)

Expand All @@ -148,20 +150,24 @@ set(GRAPHICS_OPENGL_SOURCES
src/Graphics/OpenGL/OpenGLCubemapTexture.cpp
src/Graphics/OpenGL/OpenGLIBL.cpp
src/Graphics/OpenGL/OpenGLRenderer.cpp
src/Graphics/OpenGL/OpenGLSceneRead.cpp
src/Graphics/OpenGL/OpenGLShader.cpp
src/Graphics/OpenGL/OpenGLTexture.cpp
)

set(GRAPHICS_VULKAN_SOURCES
src/Graphics/Vulkan/VulkanBloom.cpp
src/Graphics/Vulkan/VulkanBuffer.cpp
src/Graphics/Vulkan/VulkanCapture.cpp
src/Graphics/Vulkan/VulkanCubemapTexture.cpp
src/Graphics/Vulkan/VulkanDeferred.cpp
src/Graphics/Vulkan/VulkanDescriptors.cpp
src/Graphics/Vulkan/VulkanDevice.cpp
src/Graphics/Vulkan/VulkanIBL.cpp
src/Graphics/Vulkan/VulkanPipelines.cpp
src/Graphics/Vulkan/VulkanProfiler.cpp
src/Graphics/Vulkan/VulkanRenderer.cpp
src/Graphics/Vulkan/VulkanSceneRead.cpp
src/Graphics/Vulkan/VulkanShader.cpp
src/Graphics/Vulkan/VulkanShadow.cpp
src/Graphics/Vulkan/VulkanSSAO.cpp
Expand Down Expand Up @@ -369,6 +375,7 @@ endforeach()
if(GLSLC)
set(ENGINE_SPIRV_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders")
file(MAKE_DIRECTORY "${ENGINE_SPIRV_DIR}")
target_compile_definitions(Engine PRIVATE SLEAK_GLSLC_PATH="${GLSLC}")
foreach(_src ${ENGINE_VULKAN_SHADERS})
get_filename_component(_name "${_src}" NAME)
set(_spv "${ENGINE_SPIRV_DIR}/${_name}.spv")
Expand Down Expand Up @@ -425,6 +432,10 @@ add_dependencies(Engine CompileShaders)
set_property(GLOBAL PROPERTY ENGINE_SPIRV_FILES ${ENGINE_SPIRV_FILES})
set_property(GLOBAL PROPERTY ENGINE_COMMITTED_SPIRV ${ENGINE_COMMITTED_SPIRV})

target_compile_definitions(Engine
PUBLIC $<$<CONFIG:Debug>:SLEAK_HOT_RELOAD>
PRIVATE SLEAK_ENGINE_SHADER_DIR="${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders")

function(_engine_collect_targets dir out)
get_property(_targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
get_property(_subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES)
Expand Down
57 changes: 57 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Contributing

## Branches

`staging` is the testing layer between a change and `main`. Everything lands in `staging` first, gets tested there, and reaches `main` only when `staging` is merged into it. Every game built on the engine follows `main`, so nothing reaches them untested.

| Branch | Holds | Accepts |
|--------|-------|---------|
| `staging` | Changes being tested | Pull requests from any branch, once CI passes and a maintainer approves |
| `main` | Tested, released code | Pull requests from `staging` only, once CI passes and a maintainer approves |

Open every pull request against `staging`. A pull request into `main` from any other branch fails the **Only staging merges into main** check and cannot be merged. To fix it, edit the pull request and change its base branch to `staging`.

---

## Testing in staging

Every pull request into `staging` runs the full CI: the format check, the Linux and Windows build matrix, the shader check, and the downstream game build.

The downstream game build uses the SleakCraft branch of the same tier: pull requests into `staging` build SleakCraft `staging`, and pull requests into `main` build SleakCraft `main`. SleakCraft's own CI pairs the same way, so a change that spans both repos goes into both `staging` branches and is tested together.

---

## Releasing staging to main

1. Open a pull request from `staging` into `main`.
2. Wait for CI to pass.
3. Merge it with **Create a merge commit**.
4. When SleakCraft has matching changes, release its `staging` right after, so the game's `main` builds against the engine's `main` again.

`main` only allows merge commits. Squash and rebase copy staging's commits into new ones, so the two branches would stop sharing history, and every later release would replay old commits and hit conflicts that aren't real.

After a release, GitHub shows `staging` as behind `main`. The commits it is missing are the release merge commits, which change nothing on their own and need no action.

---

## Branch protection

The rules are the JSON files in `.github/rulesets/`, which GitHub imports as they are: **Settings → Rules → Rulesets → New ruleset → Import a ruleset**.

| Rule | `main` | `staging` |
|------|--------|-----------|
| Changes arrive by pull request only | ✓ | ✓ |
| No force pushes, no deletion | ✓ | ✓ |
| CI must pass | ✓ | ✓ |
| Review threads resolved | ✓ | ✓ |
| A maintainer approves | ✓ | ✓ |
| Source must be `staging` | ✓ | |
| Merge method | Merge commit | Any |

> **Setting it up?** Import `main.json` only after `.github/workflows/branch-policy.yml` is on `main`. The ruleset requires the check that workflow reports, so until the workflow exists nothing can merge into `main`. **Allow merge commits** must stay on under Settings → General → Pull Requests.

The approval rule lives in its own ruleset, `approval.json`, because GitHub does not let authors approve their own pull requests. Repository admins can bypass that one ruleset, and only through a pull request, so a maintainer can merge their own work once CI passes. Everyone else needs a maintainer's approval, and a new push dismisses an earlier approval.

Nobody can bypass the other two rulesets, admins included. To get out of a jam, set the ruleset's enforcement to **Disabled**, fix the problem, then set it back to **Active**.

Required checks are matched by name, so renaming a job in `ci.yml` or `branch-policy.yml` means updating the rulesets to match.
Loading
Loading