Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
102 changes: 91 additions & 11 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,38 @@ powershell.exe -Command "./BuildRelease.bat [PRESET_NAME]"

**Available Presets** (from CMakePresets.json):

- `ALL` (default) - Builds for SE/AE/VR in single binary
- `SE` - Skyrim Special Edition only
- `AE` - Anniversary Edition only
- `VR` - Skyrim VR only
- `ALL-TRACY` - Includes Tracy profiler support
- `ALL-WITH-AUTO-DEPLOYMENT` - Auto-deploys to configured Skyrim directories when template used.
- `ALL` (default) - Builds universal binary supporting SE/AE/VR runtime detection
- `SE` - Skyrim Special Edition only (compile-time targeting)
- `AE` - Anniversary Edition only (compile-time targeting)
- `VR` - Skyrim VR only (compile-time targeting)
- `PRE-AE` - SE + VR (excludes AE)
- `FLATRIM` - SE + AE (excludes VR)
- `ALL-TRACY` - Universal binary with Tracy profiler support enabled

**User Preset Template**:

- `ALL-WITH-AUTO-DEPLOYMENT` - Extends `ALL` with `AUTO_PLUGIN_DEPLOYMENT=ON` (copy template to use)

### Development Setup

1. Copy `CMakeUserPresets.json.template` → `CMakeUserPresets.json`
2. Configure `CommunityShadersOutputDir` for auto-deployment to Skyrim installations
3. Set build options in user preset:
- `AUTO_PLUGIN_DEPLOYMENT`: Auto-copy to Skyrim dirs
- `AIO_ZIP_TO_DIST`: Creates all-in-one distribution package
- `ZIP_TO_DIST`: Creates individual feature packages
- `TRACY_SUPPORT`: Enables performance profiling
3. Set build options in user preset or CMake cache:

**Build Options** (CMake cache variables):

- `AUTO_PLUGIN_DEPLOYMENT` (default: OFF) - Auto-copy build output to `CommunityShadersOutputDir`
- `ZIP_TO_DIST` (default: ON) - Creates individual feature packages as 7z files in `/dist`
- `AIO_ZIP_TO_DIST` (default: ON) - Creates all-in-one distribution package as 7z in `/dist`
- `TRACY_SUPPORT` (default: OFF) - Enables Tracy profiler integration for performance analysis

**Auto-Deployment Configuration**:

Set `CommunityShadersOutputDir` environment variable to semicolon-separated Skyrim Data directories:

```
CommunityShadersOutputDir=F:/MySkyrimModpack/mods/CommunityShaders;F:/SteamLibrary/steamapps/common/SkyrimVR/Data;F:/SteamLibrary/steamapps/common/Skyrim Special Edition/Data
```

### Shader Development and Testing

Expand Down Expand Up @@ -73,8 +89,72 @@ hlslkit-generate-defines --log CommunityShaders.log
hlslkit-buffer-scan --features-dir features/
```

### Custom CMake Targets

**Package and Deployment Targets**:

```bash
# Prepare AIO package structure (automatic with AIO_ZIP_TO_DIST or AUTO_PLUGIN_DEPLOYMENT)
cmake --build ./build/ALL --target PREPARE_AIO

# Prepare shaders only (useful for CI shader validation)
cmake --build ./build/ALL --target prepare_shaders

# Copy shaders to deployment directories (when AUTO_PLUGIN_DEPLOYMENT=ON)
cmake --build ./build/ALL --target COPY_SHADERS

# Create AIO zip package (when AIO_ZIP_TO_DIST=ON)
cmake --build ./build/ALL --target AIO_ZIP_PACKAGE
```

**Development Targets**:

```bash
# Format all C++ and HLSL code (requires clang-format)
cmake --build ./build/ALL --target FORMAT_CODE

# Generate shader validation configs from game logs (requires PowerShell)
cmake --build ./build/ALL --target generate_shader_configs
```

## Architecture Overview

### Manual packaging targets (detailed)

The project also provides a set of manual packaging targets that create distributable 7z packages or install the project into the AIO folder. These targets are useful when you want precise control over packaging (CI artifacts, local QA, or manual deployment).

Quick commands:

```bash
# Create the Core package (includes CORE features + plugin DLL)
cmake --build ./build/ALL --target Package-Core

# Create a manual AIO package (.7z) via install + tar
cmake --build ./build/ALL --target Package-AIO-Manual

# Create an individual feature package (name is sanitized from the feature folder)
cmake --build ./build/ALL --target Package-<Feature>

# Install into the AIO folder (installs to build/<preset>/aio)
cmake --build ./build/ALL --target AIO

# Alternatively use cmake --install to install to a custom prefix
cmake --install ./build/ALL --prefix <TARGET_DIR> # installs files according to CMake install() rules
```

Notes and behaviour:

- `Package-Core` collects everything marked as CORE and the built plugin into a temporary folder, then tars it to `dist/${PROJECT_NAME}-${UTC_NOW}.7z`.
- `Package-<Feature>` targets are generated per feature directory (non-CORE features). They create `${FEATURE}-${UTC_NOW}.7z` in `dist/`.
- `Package-AIO-Manual` performs an install to the AIO folder and then creates a single AIO archive. This is similar to the automated `AIO_ZIP_PACKAGE`, but wired as an explicit file-producing custom target (useful for CI reproducibility).
- `AIO` target runs `cmake --install` with the `aio` prefix so you can locally inspect the AIO folder layout without creating an archive.
- The install-based packaging uses the CMake `install()` rules defined near the top of `CMakeLists.txt` (the project installs `SKSE/Plugins`, copies `package/` and feature folders, and removes the Core placeholder). This makes manual installs and CI artifacts consistent with the runtime AIO layout.

Where to look in `CMakeLists.txt`:

- Manual packaging targets are defined in the "Manual packaging targets (Package-XXX)" section and create files under `${CMAKE_SOURCE_DIR}/dist`.
- The `install()` rules near the top of the file show what gets placed into the AIO layout when running `cmake --install`.

### Plugin Architecture

**Core Pattern**: Feature-driven modular system where each graphics enhancement is an independent `Feature` class that can be enabled/disabled at runtime.
Expand Down
20 changes: 20 additions & 0 deletions AI-INSTRUCTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ SKSE plugin providing advanced DirectX 11 graphics modifications for Skyrim SE/A
- **Shader Test**: `hlslkit-compile --shader-dir [target]` (install via pip first)
- **Feature Access**: `globals::features::*` namespace

### Build Options

**Runtime Presets**: `ALL` (universal), `SE`, `AE`, `VR`, `PRE-AE`, `FLATRIM`, `ALL-TRACY`

**CMake Options** (set in user preset):

- `AUTO_PLUGIN_DEPLOYMENT=ON` - Auto-copy to `CommunityShadersOutputDir`
- `ZIP_TO_DIST=ON` (default) - Create individual feature 7z packages
- `AIO_ZIP_TO_DIST=ON` (default) - Create all-in-one 7z package
- `TRACY_SUPPORT=ON` - Enable Tracy profiler integration

### Custom CMake Targets

**Quick targets** (common):

- `PREPARE_AIO`, `prepare_shaders`, `COPY_SHADERS`, `AIO_ZIP_PACKAGE`
- `FORMAT_CODE`, `generate_shader_configs`

For full details about manual packaging targets (Package-Core, Package-AIO-Manual, Package-<Feature>, AIO) and example workflows, see the "Manual packaging targets (detailed)" section in `.claude/CLAUDE.md` to avoid duplication.

### AI Assistant Role

**Act as an experienced graphics programming and Skyrim modding expert.**
Expand Down
186 changes: 178 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.21)
cmake_policy(SET CMP0116 NEW)
set(CMAKE_POLICY_WARNING_CMP0116 OFF)

if(CMAKE_VERSION VERSION_GREATER_EQUAL "4.0.0")
message(
ERROR
"EASTL will fail to install with vcpkg using cmake 4.0+, remove this line if the port get fixed."
)
endif()

project(
# gersemi: ignore
Expand All @@ -7,6 +16,14 @@ project(
LANGUAGES CXX
)

# default install path
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set_property(
CACHE CMAKE_INSTALL_PREFIX
PROPERTY VALUE "${CMAKE_CURRENT_BINARY_DIR}/aio"
)
endif()

list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")

# ########################################################################################################################
Expand Down Expand Up @@ -244,8 +261,38 @@ endif()
file(GLOB FEATURE_PATHS LIST_DIRECTORIES true ${CMAKE_SOURCE_DIR}/features/*)
string(TIMESTAMP UTC_NOW "%Y-%m-%dT%H-%MZ" UTC)

# Set AIO directory path used by multiple targets below
set(AIO_DIR "${CMAKE_CURRENT_BINARY_DIR}/aio")

# #######################################################################################################################
# # CMake install() infrastructure for manual packaging
# #######################################################################################################################

# Append a '/' to the end of each feature path for installation all its contents but not itself
set(FEATURE_PATHS_SLASH ${FEATURE_PATHS})
list(TRANSFORM FEATURE_PATHS_SLASH APPEND /)

# Install logic for AIO package
# To copy AIO package at a folder do `${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR} --prefix ${AIO_DIR}`
install(CODE "file(REMOVE_RECURSE \${CMAKE_INSTALL_PREFIX})")
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION SKSE/Plugins COMPONENT SKSE)
install(
FILES $<TARGET_PDB_FILE:${PROJECT_NAME}>
DESTINATION SKSE/Plugins
COMPONENT SKSE
)
install(
DIRECTORY ${CMAKE_SOURCE_DIR}/package/ ${FEATURE_PATHS_SLASH}
DESTINATION .
COMPONENT Shaders
)
install(CODE "file(REMOVE \${CMAKE_INSTALL_PREFIX}/Core)" COMPONENT Shaders)

Comment thread
ArcEarth marked this conversation as resolved.
# #######################################################################################################################
# # Automatic AIO preparation (incremental copy system)
# #######################################################################################################################

if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST)
set(AIO_DIR "${CMAKE_CURRENT_BINARY_DIR}/aio")
message("Preparing AIO package in ${AIO_DIR}")

# Prepare AIO only when sources change. Gather package + feature files as
Expand Down Expand Up @@ -660,15 +707,138 @@ if(AIO_ZIP_TO_DIST)
)
endif()

# Create a stamp-producing custom command for the AIO archive so CMake
# only rebuilds the archive when its inputs change. The archive filename
# keeps the UTC timestamp as before, but the command writes a stable
# stamp file that CMake can track as OUTPUT.
set(TARGET_AIO_ZIP "${PROJECT_NAME}_AIO-${UTC_NOW}.7z")
message("Zipping ${AIO_DIR} to ${CMAKE_SOURCE_DIR}/dist/${TARGET_AIO_ZIP}")
add_custom_target(
AIO_ZIP_PACKAGE
ALL
COMMAND
${CMAKE_COMMAND} -E tar cf
${CMAKE_SOURCE_DIR}/dist/${TARGET_AIO_ZIP} --format=7zip -- .
set(AIO_ARCHIVE "${CMAKE_SOURCE_DIR}/dist/${TARGET_AIO_ZIP}")
set(AIO_ZIP_STAMP "${CMAKE_CURRENT_BINARY_DIR}/aio_package.stamp")

message("Zipping ${AIO_DIR} to ${AIO_ARCHIVE}")

add_custom_command(
OUTPUT ${AIO_ZIP_STAMP}
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_SOURCE_DIR}/dist"
COMMAND ${CMAKE_COMMAND} -E tar cf ${AIO_ARCHIVE} --format=7zip -- .
COMMAND ${CMAKE_COMMAND} -E touch ${AIO_ZIP_STAMP}
WORKING_DIRECTORY ${AIO_DIR}
DEPENDS PREPARE_AIO
COMMENT "Creating AIO archive ${AIO_ARCHIVE}"
)

add_custom_target(AIO_ZIP_PACKAGE ALL DEPENDS ${AIO_ZIP_STAMP})
endif()

if(NOT DEFINED ENV{CommunityShadersOutputDir})
message(
"When using AUTO_PLUGIN_DEPLOYMENT option, you need to set environment variable 'CommunityShadersOutputDir'"
)
endif()

# #######################################################################################################################
# # Manual packaging targets (Package-XXX)
# #######################################################################################################################

set(DIST_PATH "${CMAKE_SOURCE_DIR}/dist")
file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/dist")

set(CORE_PACKAGE "${DIST_PATH}/${PROJECT_NAME}-${UTC_NOW}.7z")

# CORE_SOURCES = all content copied to the AIO directory + the SKSE plugin dll
file(
GLOB_RECURSE CORE_SOURCES
CONFIGURE_DEPENDS
"${CMAKE_SOURCE_DIR}/package/*"
)
# Add SKSE plugin dll as dependency (use target-file generator expression so CMake
# knows the actual output path of the target at build time)
list(APPEND CORE_SOURCES "$<TARGET_FILE:${PROJECT_NAME}>")

set(CORE_FEATURE_PATHS "${CMAKE_SOURCE_DIR}/package/")

foreach(FEATURE_PATH ${FEATURE_PATHS})
if(EXISTS "${FEATURE_PATH}/CORE")
list(APPEND CORE_FEATURE_PATHS "${FEATURE_PATH}/")
file(GLOB_RECURSE FEATURE_SOURCES CONFIGURE_DEPENDS "${FEATURE_PATH}/*")
list(APPEND CORE_SOURCES ${FEATURE_SOURCES})
endif()
endforeach()

# Core package
set(FEATURE_PATH "${CMAKE_BINARY_DIR}/Core")
file(MAKE_DIRECTORY ${FEATURE_PATH})
add_custom_command(
OUTPUT ${CORE_PACKAGE}
DEPENDS ${CORE_SOURCES}
COMMAND
${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR} --prefix ${FEATURE_PATH}
--component SKSE
COMMAND
${CMAKE_COMMAND} -E copy_directory ${CORE_FEATURE_PATHS} ${FEATURE_PATH}
COMMAND ${CMAKE_COMMAND} -E rm -f -- ${FEATURE_PATH}/Core
COMMAND ${CMAKE_COMMAND} -E tar cfv ${CORE_PACKAGE} --format=7zip -- .
WORKING_DIRECTORY ${FEATURE_PATH}
COMMENT "Creating Core zip package"
)
add_custom_target("Package-Core" DEPENDS ${CORE_PACKAGE})

# Feature packages
foreach(FEATURE_PATH ${FEATURE_PATHS})
if(EXISTS "${FEATURE_PATH}/CORE")
continue()
endif()

list(APPEND CORE_FEATURE_PATHS "${FEATURE_PATH}/")
file(GLOB_RECURSE FEATURE_SOURCES CONFIGURE_DEPENDS "${FEATURE_PATH}/*")
list(APPEND CORE_SOURCES ${FEATURE_SOURCES})

get_filename_component(FEATURE ${FEATURE_PATH} NAME)
set(FEATURE_PACKAGE "${DIST_PATH}/${FEATURE}-${UTC_NOW}.7z")

add_custom_command(
OUTPUT ${FEATURE_PACKAGE}
COMMAND
${CMAKE_COMMAND} -E tar cfv ${FEATURE_PACKAGE} --format=7zip -- .
WORKING_DIRECTORY "${FEATURE_PATH}"
DEPENDS ${FEATURE_SOURCES}
COMMENT "Creating ${FEATURE} zip package"
)

string(REPLACE " " "" FEATURE ${FEATURE})
string(REPLACE "-" "" FEATURE ${FEATURE})
add_custom_target("Package-${FEATURE}" DEPENDS ${FEATURE_PACKAGE})
endforeach()

# AIO Folder target
add_custom_command(
OUTPUT ${AIO_DIR}/SKSE/Plugins/${PROJECT_NAME}.dll
DEPENDS ${CORE_SOURCES}
COMMAND ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR} --prefix ${AIO_DIR}
COMMENT "Installing to AIO folder"
)
add_custom_target("AIO" DEPENDS ${AIO_DIR}/SKSE/Plugins/${PROJECT_NAME}.dll)

# Manual AIO package target
set(AIO_PACKAGE "${DIST_PATH}/${PROJECT_NAME}_AIO-${UTC_NOW}.7z")
add_custom_command(
OUTPUT ${AIO_PACKAGE}
DEPENDS ${CORE_SOURCES}
COMMAND ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR} --prefix ${AIO_DIR}
COMMAND ${CMAKE_COMMAND} -E tar cfv ${AIO_PACKAGE} --format=7zip -- .
WORKING_DIRECTORY ${AIO_DIR}
COMMENT "Creating AIO zip package (manual)"
)
add_custom_target("Package-AIO-Manual" DEPENDS ${AIO_PACKAGE})

message("*************************************************************")
message("Community Shaders configuration complete")
message("To prepare a ZIP package of AIO, Core, or Features")
message(" Build cmake targets:")
message(" - Package-Core: Core package")
message(" - Package-AIO-Manual: AIO package (manual)")
message(" - Package-<Feature>: Individual feature packages")
message(" Or use cmake --install for custom deployment:")
message(" cmake --install ./build/ALL --prefix <TARGET_DIR>")
message("Try switching to build preset 'Dev' for faster iteration time")
message("*************************************************************")
Loading