Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4625f5b
chore: rebrand as Open Shaders (#22)
alandtse May 20, 2026
5117702
ci: reconcile dev after hotfix-staging releases (#21)
alandtse May 20, 2026
20a38a1
feat(tracy): bump to upstream master snapshot (#26)
alandtse May 20, 2026
4f23e36
ci: address CodeQL security findings (cache poisoning + permissions) …
alandtse May 21, 2026
2ce7b61
ci: debounce auto-rebase by 30 minutes (#29)
alandtse May 21, 2026
fe7195f
ci: add merge-based upstream sync workflow (#30)
alandtse May 21, 2026
ec2db80
fix(screenshot): bounds-check staging texture mapped region (#25)
alandtse May 22, 2026
62ff479
feat(remote-control): add MCP server core feature (#27)
alandtse May 22, 2026
23c04e3
refactor(menu): regroup Advanced tabs by purpose (#28)
alandtse May 22, 2026
97556f0
refactor(utils): add stereo support to Util::Subrect::Controller (#23)
alandtse May 22, 2026
dc96878
test: fix cpp_tests build and add CI gate (#32)
alandtse May 22, 2026
a3e68ea
build: drop CORE marker from per-feature AIO copy to avoid race (#33)
alandtse May 22, 2026
8b162bd
chore(sync): merge upstream/dev as of bb6460dbf
alandtse May 23, 2026
37dfdee
fix(vr): full-resolution underwater mask (#34)
alandtse May 24, 2026
b84222b
feat(llf): restore contact shadows with VR-aware noise (#36)
alandtse May 24, 2026
77ae534
feat(VR): run post-process at DLSS internal resolution (DLSSperf) (#24)
alandtse May 24, 2026
5c00a38
build: drop /XO from robocopy auto-deploy (#37)
alandtse May 25, 2026
045e72e
refactor: unify restart-required infrastructure (#39)
Codex May 25, 2026
0f307d2
refactor(BootSnapshot): allow non-trivially-copyable Settings (#40)
alandtse May 25, 2026
c97626c
feat(upscaling): generalize VR perf-mode rendering (#42)
alandtse May 26, 2026
e34c113
feat(llf): expose contact-shadow settings (#43)
alandtse May 26, 2026
4e246fb
feat(slf): shadow limit fix (#35)
alandtse May 27, 2026
59fa87d
fix(slf): VR shadow-mask OOB CTD (#46)
alandtse May 27, 2026
694504c
feat(VR): add foveated rendering (#44)
alandtse May 28, 2026
25ead9c
fix(slf): VR accumulator OOB heap write
alandtse May 28, 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
49 changes: 46 additions & 3 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Fork identity

This repository is **Open Shaders** ([alandtse/open-shaders](https://github.com/alandtse/open-shaders)), a fork of [Community Shaders](https://github.com/community-shaders/skyrim-community-shaders) ([Nexus mod 180419](https://www.nexusmods.com/skyrimspecialedition/mods/180419)). The public/display name is "Open Shaders"; the runtime identity is intentionally kept as upstream Community Shaders so user installs are drop-in compatible:

- **Keep as `CommunityShaders`** (do NOT rename): the CMake `PROJECT_NAME`, the DLL filename, the `SKSE/Plugins/CommunityShaders/` runtime directory, the `CommunityShaders.log` log file, the ImGui window ID after `###`, asset paths under `package/Interface/CommunityShaders/`, and any HLSL include paths.
- **Use "Open Shaders"**: in-game menu titles, README/AI-INSTRUCTIONS public-facing copy, the AIO Nexus mod filename, the GitHub release name, the in-game Welcome / FAQ / About text.
- **Link "Community Shaders" explicitly to upstream**: when the text or comment refers to the upstream project (its Nexus page is 180419, its repo is `community-shaders/skyrim-community-shaders`, its wiki lives on that repo). Never link `doodlum/skyrim-community-shaders` — that path is dead.

The AIO bundle ships only features whose `Shaders/Features/*.ini` has `autoupload = true` (CORE features always included). The `AIO_INCLUDE_NON_AUTOUPLOAD=ON` CMake option overrides for local dev builds. The Nexus upload workflow ships only the AIO archive — there is no per-feature matrix distribution. See `.github/workflows/nexus-upload.yaml`.

**Logo absence is intentional.** The upstream Community Shaders logo is non-GPL, not trademark-licensed, and may not be redistributed by forks — so `cs-logo.png` is not present in this repo. The icon loader's load path is null-safe at every consumer (`IconLoader.cpp` retries the colored fallback; `Menu.cpp` derives `showLogo` from `texture != nullptr`; `MenuHeaderRenderer` and `HomePageRenderer` gate all logo draws on the null check). Missing logo → one `logger::warn`, menu renders headers without the logo image, layout adjusts via the `showLogo` flag. Do not "fix" the missing file or restore the upstream asset.

## Build Commands

### WSL/Linux Environment Note
Expand Down Expand Up @@ -361,6 +373,7 @@ Feature versions are automatically extracted from `.ini` files and compiled into
- JSON-based settings with nlohmann_json
- Hot-reload capability through ImGui interface
- Versioned feature configurations for compatibility
- Restart-gated fields use `Util::Settings::BootSnapshot` + `kRestartFields` metadata to diff boot-latched vs selected values (drives `Util::Text::RestartNeeded` banners and MCP introspection; see Upscaling for a canary)

### Error Handling

Expand Down Expand Up @@ -435,6 +448,9 @@ Feature versions are automatically extracted from `.ini` files and compiled into
- **Complete Solutions**: Provide fully functional code with proper error handling and resource management
- **Performance Conscious**: Always consider GPU workload and user experience impact
- **Documentation**: Include Doxygen comments for public methods, especially graphics-related functions
- **Concise Comments**: Comments explain _why_, not _what_. Skip restating code in prose. A 1-line "why this hack" beats a 4-line block paraphrasing the next 4 lines. Block comments at the top of a function/section are fine when they capture non-obvious context (invariants, gotchas, history); avoid mid-function tutorial paragraphs.
- **Minimal Churn**: PRs touch only what the change requires. Don't reformat unrelated lines, rename adjacent variables, or "clean up" code outside the PR's scope. If you spot something worth fixing nearby, open a follow-up PR or surface it in the description rather than expanding the diff. Auto-format/lint touching unrelated lines is acceptable only when it's the linter's own commit; mixing with logic changes obscures review.
- **Comments describe present code, not absent code**: Don't add comments that describe code that used to be in the file but isn't now ("the Discord banner was removed", "this constant was renamed from X"). The reader sees only the present file; the deletion isn't visible. Past-tense framing of present behavior is fine ("if someone landed a commit during the release, abort"); the rule is specifically about describing code that no longer exists. Exception: a regression-risk warning that names the removed code so a future maintainer doesn't restore it ("do not re-add the Discord banner — the upstream invite isn't a fork channel") is load-bearing and stays. Commit messages, PR descriptions, and CHANGELOG entries are the right place for "what changed" — code comments are not.

## Development Best Practices (Learned from Codebase)

Expand All @@ -445,13 +461,40 @@ Follow conventional commit format for consistency:
- **Format**: `type(scope): description`
- **Title Limit**: 50 characters maximum
- **Body Wrap**: 72 characters per line
- **Types**: `feat`, `fix`, `refactor`, `docs`, `style`, `test`, `chore`
- **Examples**:
- `feat(menu): extract DrawMenuVisitor helper methods`
- `fix(imgui): resolve orphaned TableNextColumn calls`
- `refactor(constants): centralize UI constants in ThemeManager`
- `ci: gate cpp_tests build on changed files`
- `build: drop CORE marker from per-feature AIO copy`
- `test: fix cpp_tests build under MSVC C++23 modules`

**Squash-merge note.** PRs are squash-merged, so the **PR title** becomes the commit message that semantic-release reads. Getting the title's type right matters more than per-commit messages on the PR branch — those get discarded. Use `gh pr edit <num> --title "..."` to fix a stale title before merge.

**Type → release impact** (the full set accepted by `amannn/action-semantic-pull-request@v5` + `@semantic-release/commit-analyzer` defaults):

| Type | Use for | Release impact |
| ---------- | --------------------------------------------------------- | ----------------------- |
| `feat` | New user-facing feature or capability | **minor** (1.X.0) |
| `fix` | Bug fix to user-facing behavior | **patch** (1.5.X) |
| `perf` | Performance improvement to user-facing behavior | **patch** (1.5.X) |
| `revert` | Revert of a prior commit | follows reverted commit |
| `build` | Build system, packaging, dependencies (CMake, vcpkg, AIO) | none |
| `chore` | Maintenance, misc tooling, repo hygiene | none |
| `ci` | CI workflows, GitHub Actions, lint configs | none |
| `docs` | Documentation, comments, READMEs, CLAUDE.md | none |
| `refactor` | Code restructuring with no behavior change | none |
| `style` | Formatting, whitespace, missing semicolons | none |
| `test` | Tests, test fixtures, test infrastructure | none |

Append `!` to the type (or add a `BREAKING CHANGE:` footer) for **major** (X.0.0).

**Pick the type with version impact in mind.** Common traps:

Conventional commits drive semantic-release. `feat:` triggers a minor bump, `fix:` triggers a patch bump, `feat!:` or `BREAKING CHANGE:` triggers a major bump. `chore:`, `docs:`, `style:`, `test:`, `refactor:` produce no release on their own. Pick the type with the version impact in mind — a refactor mislabeled `feat:` will force a minor bump on the next release.
- A pure build/CI/test change mislabeled `fix:` will burn a patch release on a non-user-visible change. Use `build:`, `ci:`, or `test:` instead.
- A refactor mislabeled `feat:` will force a minor bump.
- A perf win on internal code (not exposed to users) is `refactor:`, not `perf:`.
- `chore:` is a catch-all; prefer the specific type when one fits.

### Release Branch Model

Expand Down Expand Up @@ -493,7 +536,7 @@ After a hotfix release, open PRs targeting `dev` are auto-rebased by the `Auto-r
- PR a feature branch directly into `main`.
- Run `Release: Semantic Version` on `hotfix/X.Y.x` for the current line — it will fail with `cannot be published as it is out of range` because the maintenance contract requires the hotfix line to be strictly older than `main`. Use `ff_target` into `main` instead.

Full details: [Developers wiki — Patch Release Process](https://github.com/community-shaders/skyrim-community-shaders/wiki/Developers#patch-release-process-any-line).
Full details: [Open Shaders developer wiki — Patch Release Process](https://github.com/alandtse/open-shaders/wiki/Developers#patch-release-process-any-line). The fork now maintains its own wiki (transferred from upstream) at `alandtse/open-shaders/wiki`; the `maint-update-wiki.yaml` workflow auto-publishes buffer documentation there on every push to `dev`. Upstream Community Shaders maintains its own copy at `community-shaders/skyrim-community-shaders/wiki` — link to whichever is appropriate for the audience.

### Code Organization and Refactoring Patterns

Expand Down
54 changes: 54 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,57 @@

# Ensure patch files always use LF line endings
*.patch text eol=lf

# -----------------------------------------------------------------------------
# Fork-owned paths — `merge=ours` during 3-way merges
# -----------------------------------------------------------------------------
# These files are policy- or branding-owned by the fork. When the scheduled
# upstream-merge-sync workflow merges upstream/dev into dev, the `ours` driver
# preserves the fork's version of each path verbatim, ignoring whatever
# upstream did to the same file. This is what prevents the silent-revert
# class of bug (upstream cherry-picks our commit, then later deletes the
# file in a follow-up: a vanilla merge would honor the delete; `merge=ours`
# discards it).
#
# Prerequisites for the driver to fire:
# 1. Each clone (including CI runners) must define the `ours` driver:
# git config merge.ours.driver true
# The scheduled sync workflow does this in a setup step. Local
# contributors who run `git merge upstream/dev` by hand should run
# the same command once. See docs/development/upstream-sync.md.
# 2. The merge must be a true 3-way merge (i.e., `git merge`, not
# `git rebase` — rebase replays patches via `git apply` and never
# invokes merge drivers).
#
# Important: `merge=ours` fires on ANY 3-way merge that touches these
# paths, not just the scheduled upstream sync. If a PR is landed via
# GitHub's "Create a merge commit" button (or via a local `git merge`
# between fork branches), and the incoming branch changed one of these
# files, those changes are silently discarded. **Edits to fork-owned
# paths must land via squash-merge or rebase-merge** so the diff lands
# as a plain commit on dev rather than through a merge-driver-affected
# merge commit. The default PR merge methods on `dev` are configured
# to squash/rebase only to enforce this.
#
# When adding a path here, also add a one-line note in
# docs/development/upstream-sync.md explaining why the fork owns it. If a
# path stops being fork-owned (e.g., we converge with upstream's behavior),
# remove the entry so upstream's improvements flow back in.
# -----------------------------------------------------------------------------

# CI workflows the fork owns end-to-end (auto-rebase machinery, the
# release pipeline calibrated to RELEASE_PAT rather than upstream's
# GitHub App setup, hotfix flow, nexus upload, etc.).
.github/workflows/auto-rebase-prs.yaml merge=ours
.github/workflows/release-semantic.yaml merge=ours
.github/workflows/release-hotfix.yaml merge=ours
.github/workflows/nexus-upload.yaml merge=ours
.github/workflows/maint-cleanup-releases.yaml merge=ours
.github/workflows/maint-upstream-sync.yaml merge=ours

# Semantic-release config — fork has independent versioning and may
# diverge from upstream's release rules.
.releaserc merge=ours

# Branding-owned: rebrand commit established these as fork-specific.
README.md merge=ours
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ SKSE64 plugin providing modular DirectX 11 graphics enhancements for Skyrim SE/A
### Essential Repository Setup

```bash
git clone https://github.com/doodlum/skyrim-community-shaders.git --recursive
cd skyrim-community-shaders
git clone https://github.com/alandtse/open-shaders.git --recursive
cd open-shaders
git submodule update --init --recursive # If not cloned with --recursive
```

Expand Down
72 changes: 72 additions & 0 deletions .github/workflows/_shared-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ on:
description: "Run shader unit tests"
type: boolean
default: true
run-cpp-tests:
description: "Run C++ unit tests (tests/cpp)"
type: boolean
default: true
hlsl-should-build:
description: "Passed to check-hlsl-changes; 'true' forces shader steps to run"
type: string
Expand All @@ -31,6 +35,10 @@ on:
description: "Passed to check-hlsl-changes for unit tests"
type: string
default: "true"
cpp-tests-should-build:
description: "Forces cpp_tests build+run when 'true'; lets PR-checks skip it otherwise"
type: string
default: "true"
cache-key-suffix:
description: "Optional suffix to invalidate the build cache"
type: string
Expand Down Expand Up @@ -259,3 +267,67 @@ jobs:
build/ALL/Testing/**
retention-days: 7
if-no-files-found: ignore

cpp-unit-tests:
name: Run C++ Unit Tests
if: inputs.run-cpp-tests
runs-on: windows-2025
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}
repository: ${{ inputs.repository }}
submodules: recursive

# Inline gate (no composite action — the logic is one branch).
# PR events skip when no relevant files changed; push / dispatch /
# release always run.
- name: Check if cpp_tests should run
id: check-cpp
shell: bash
run: |
if [ "${{ github.event_name }}" != "pull_request_target" ]; then
echo "Non-PR event, proceeding."
echo "skip=false" >> $GITHUB_OUTPUT
elif [ "${{ inputs.cpp-tests-should-build }}" != "true" ]; then
echo "No cpp_tests-related changes detected, skipping."
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "cpp_tests-related changes detected, proceeding."
echo "skip=false" >> $GITHUB_OUTPUT
fi

- name: Setup Build Environment
id: setup
if: steps.check-cpp.outputs.skip != 'true'
uses: ./.github/actions/setup-build-environment
with:
cache-key-suffix: "cpp-tests${{ inputs.cache-key-suffix }}"
cmake-preset: "ALL"
build-dir: "build/ALL"

- name: Build cpp_tests
if: steps.check-cpp.outputs.skip != 'true'
uses: lukka/run-cmake@v10
with:
configurePreset: ALL
buildPreset: ALL
buildPresetAdditionalArgs: "['--target cpp_tests']"

- name: Run C++ unit tests
if: steps.check-cpp.outputs.skip != 'true'
run: |
ctest --test-dir build/ALL -C Release --output-on-failure -R CppUtilTests --timeout 60

- name: Upload test results on failure
if: failure() && steps.check-cpp.outputs.skip != 'true'
uses: actions/upload-artifact@v7
with:
name: cpp-test-results
path: |
build/ALL/Testing/**
retention-days: 7
if-no-files-found: ignore
Loading
Loading