Skip to content

Add caching for source modules - #968

Draft
skallweitNV wants to merge 3 commits into
mainfrom
dev/skallweit/source-module-cache
Draft

Add caching for source modules#968
skallweitNV wants to merge 3 commits into
mainfrom
dev/skallweit/source-module-cache

Conversation

@skallweitNV

@skallweitNV skallweitNV commented May 1, 2026

Copy link
Copy Markdown
Contributor

This doesn't currently work due to limitations of loading precompiled modules in Slang.
See shader-slang/slang#11036

Summary by CodeRabbit

  • New Features

    • Source-loaded shader modules are now cached (digest-named) under a session cache, persisting both source and compiled artifacts for reuse and faster reloads.
  • Tests

    • Added tests that verify compiled module artifacts are written to disk and an end-to-end scenario that loads a source module, checks runtime output, then reloads from cache to confirm identical behavior.

@skallweitNV
skallweitNV requested a review from a team as a code owner May 1, 2026 21:18
@skallweitNV
skallweitNV requested review from bmillsNV and removed request for a team May 1, 2026 21:18
@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@skallweitNV has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 51 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd7284f1-ddfd-43bc-856a-9a3e151dcf66

📥 Commits

Reviewing files that changed from the base of the PR and between 3c686c9 and 292ddd8.

📒 Files selected for processing (3)
  • slangpy/tests/device/test_module_cache.py
  • src/sgl/device/shader.cpp
  • src/sgl/device/shader.h
📝 Walkthrough

Walkthrough

Source-module caching adds digest-based cache identity for modules loaded from source, performs cache-first load using both cached source and compiled .slang-module files, and writes atomic source + binary cache artifacts under a per-session source_modules directory when compilation occurs.

Changes

Source-based Module Cache

Layer / File(s) Summary
Data Shape
src/sgl/device/shader.h
Adds SlangSessionData::source_cache_path and SlangModuleDesc::source_digest (optional) to carry per-session source cache root and SHA1 digest identity.
Core Implementation
src/sgl/device/shader.cpp, src/sgl/device/shader.h
Adds _get_source_module_cache_path(module_name, digest) and _write_source_module_to_cache(...) helpers; load_module_from_source computes SHA1 digest and stores it in SlangModuleDesc.
Control Flow / Cache Logic
src/sgl/device/shader.cpp
SlangModule::load for source descriptors tries cache-first when source_digest is present and both .slang and .slang-module files exist; falls back to compile-from-source and, on success, writes both cached source and compiled binary atomically.
Tests
slangpy/tests/device/test_module_cache.py
Adds test_source_module_cache to assert runtime output, verify .slang-module files appear under source_modules/**, recreate device with same cache dir, and confirm correct output on a subsequent load.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant SlangSession
    participant FileSystem
    participant SlangCompiler

    Client->>SlangSession: load_module_from_source(source_code)
    activate SlangSession
    SlangSession->>SlangSession: compute SHA1 digest
    SlangSession->>FileSystem: check cache path for `.slang` + `.slang-module`
    alt cache hit
        FileSystem-->>SlangSession: return cached files
        SlangSession->>SlangCompiler: loadModule(from cached .slang)
        SlangCompiler-->>SlangSession: IModule
    else cache miss
        SlangSession->>SlangCompiler: loadModuleFromSourceString(source_code)
        SlangCompiler-->>SlangSession: IModule
        SlangSession->>FileSystem: write temp `.slang-*` and rename -> `.slang`
        SlangSession->>FileSystem: write temp `.slang-module-*` and rename -> `.slang-module`
    end
    SlangSession-->>Client: return IModule
    deactivate SlangSession
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • bmillsNV
  • ccummingsNV

Poem

🐰 In a burrow of bits I write and tuck,
SHA1 crumbs guide each compiled pluck,
Save the source, save the build,
Hop once, then hop again — cache fulfilled! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add caching for source modules' accurately summarizes the main change: implementing caching functionality for source-loaded modules across multiple files (shader.h, shader.cpp, and tests).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev/skallweit/source-module-cache

Tip

💬 Introducing [Slack Agent](https://www.coderabbit.ai/agent): Turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 8 minutes and 51 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/sgl/device/shader.cpp`:
- Around line 1000-1042: The cache-load path currently uses
SGL_CATCH_INTERNAL_SLANG_ERROR around session_data->slang_session->loadModule
which rethrows SlangCompileError and prevents falling back to source; change the
logic so exceptions from loadModule are caught and treated the same as a nullptr
(i.e., do not let the exception escape): wrap the loadModule call (the one
inside the if (std::filesystem::exists(cache_file)) block where
SGL_CATCH_INTERNAL_SLANG_ERROR is used) so that any thrown error is
logged/debugged and loaded_from_cache remains false, allowing the subsequent
loadModuleFromSourceString code (and the later
m_session->_write_source_module_to_cache call) to run as the fallback; keep
existing behavior for a successful slang_module (set loaded_from_cache = true
and log).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 227dc70c-b3b9-41aa-9168-2aa78c233c84

📥 Commits

Reviewing files that changed from the base of the PR and between 921c53c and f307c62.

📒 Files selected for processing (3)
  • slangpy/tests/device/test_module_cache.py
  • src/sgl/device/shader.cpp
  • src/sgl/device/shader.h

Comment thread src/sgl/device/shader.cpp
Comment on lines +1000 to 1042
bool loaded_from_cache = false;
if (session_data->cache_enabled && desc.source_digest.has_value()) {
std::filesystem::path cache_file
= m_session->_get_source_module_cache_path(desc.module_name, *desc.source_digest);
if (std::filesystem::exists(cache_file)) {
SGL_CATCH_INTERNAL_SLANG_ERROR(
slang_module
= session_data->slang_session->loadModule(cache_file.string().c_str(), diagnostics.writeRef());
);
if (slang_module) {
loaded_from_cache = true;
log_debug("Loaded source module \"{}\" from cache", desc.module_name);
}
}
}

SGL_CATCH_INTERNAL_SLANG_ERROR(
slang_module = session_data->slang_session->loadModuleFromSourceString(
std::string{desc.module_name}.c_str(),
desc.path ? desc.path->string().c_str() : nullptr,
source_str.c_str(),
diagnostics.writeRef()
)
);
if (!slang_module) {
std::string msg = append_diagnostics(
fmt::format("Failed to load slang module \"{}\" from source", desc.module_name),
diagnostics
if (!loaded_from_cache) {
// TODO: This is a workaround until we use a Slang release with this fix:
// https://github.com/shader-slang/slang/pull/10996
// Once this is fixed on the Slang side, we can remove this.
std::string source_str = fmt::format("// {}\n{}", desc.module_name, desc.source.value());

SGL_CATCH_INTERNAL_SLANG_ERROR(
slang_module = session_data->slang_session->loadModuleFromSourceString(
std::string{desc.module_name}.c_str(),
desc.path ? desc.path->string().c_str() : nullptr,
source_str.c_str(),
diagnostics.writeRef()
)
);
throw SlangCompileError(msg);
if (!slang_module) {
std::string msg = append_diagnostics(
fmt::format("Failed to load slang module \"{}\" from source", desc.module_name),
diagnostics
);
throw SlangCompileError(msg);
}

// Write to source module cache.
if (session_data->cache_enabled && desc.source_digest.has_value()) {
m_session->_write_source_module_to_cache(slang_module, desc.module_name, *desc.source_digest);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing fallback to source when cache-file loadModule throws — hard failure on stale/corrupt cache

SGL_CATCH_INTERNAL_SLANG_ERROR re-throws any internal Slang exception as SlangCompileError. If the cached .slang-module file is corrupted (e.g., partial write from a previous crash, a Slang version upgrade that changes the binary format), loadModule will throw rather than return nullptr. That exception propagates past the if (!loaded_from_cache) block entirely, so the source fallback is never reached. The user gets an opaque "Internal slang error: ..." message and the only recovery is manually deleting the cache directory.

A null return from loadModule is handled gracefully (loaded_from_cache stays false → source is used). The throw case must be handled the same way.

🛡️ Proposed fix
             if (std::filesystem::exists(cache_file)) {
-                SGL_CATCH_INTERNAL_SLANG_ERROR(
-                    slang_module
-                    = session_data->slang_session->loadModule(cache_file.string().c_str(), diagnostics.writeRef());
-                );
-                if (slang_module) {
+                try {
+                    SGL_CATCH_INTERNAL_SLANG_ERROR(
+                        slang_module = session_data->slang_session->loadModule(
+                            cache_file.string().c_str(), diagnostics.writeRef()
+                        );
+                    );
+                } catch (const SlangCompileError& e) {
+                    log_warn(
+                        "Failed to load source module \"{}\" from cache ({}), falling back to source.",
+                        desc.module_name,
+                        e.what()
+                    );
+                    slang_module = nullptr;
+                }
+                if (slang_module) {
                     loaded_from_cache = true;
                     log_debug("Loaded source module \"{}\" from cache", desc.module_name);
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/sgl/device/shader.cpp` around lines 1000 - 1042, The cache-load path
currently uses SGL_CATCH_INTERNAL_SLANG_ERROR around
session_data->slang_session->loadModule which rethrows SlangCompileError and
prevents falling back to source; change the logic so exceptions from loadModule
are caught and treated the same as a nullptr (i.e., do not let the exception
escape): wrap the loadModule call (the one inside the if
(std::filesystem::exists(cache_file)) block where SGL_CATCH_INTERNAL_SLANG_ERROR
is used) so that any thrown error is logged/debugged and loaded_from_cache
remains false, allowing the subsequent loadModuleFromSourceString code (and the
later m_session->_write_source_module_to_cache call) to run as the fallback;
keep existing behavior for a successful slang_module (set loaded_from_cache =
true and log).

Co-authored-by: Copilot <copilot@github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/sgl/device/shader.cpp (1)

1038-1041: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Catch cache-load exceptions so source fallback still runs.

At Line 1038, SGL_CATCH_INTERNAL_SLANG_ERROR may throw on stale/corrupt cache artifacts; that exits before Line 1049, so source fallback is skipped. This should be treated as a cache miss (loaded_from_cache = false) rather than a hard failure.

Suggested fix
                 if (std::filesystem::exists(cache_source_file) && std::filesystem::exists(cache_binary_file)) {
-                    SGL_CATCH_INTERNAL_SLANG_ERROR(
-                        slang_module = session_data->slang_session
-                                           ->loadModule(cache_source_file.string().c_str(), diagnostics.writeRef());
-                    );
+                    try {
+                        SGL_CATCH_INTERNAL_SLANG_ERROR(
+                            slang_module = session_data->slang_session
+                                               ->loadModule(cache_source_file.string().c_str(), diagnostics.writeRef());
+                        );
+                    } catch (const SlangCompileError& e) {
+                        log_warn(
+                            "Failed to load source module \"{}\" from cache ({}). Falling back to source.",
+                            desc.module_name,
+                            e.what()
+                        );
+                        slang_module = nullptr;
+                    }
                     if (slang_module) {
                         loaded_from_cache = true;
                         log_debug("Loaded source module \"{}\" from cache", desc.module_name);
                     }
                 }

You can verify this path and macro behavior with:

#!/bin/bash
set -euo pipefail

# 1) Inspect macro definition/usage to confirm it can throw.
rg -n -C3 'SGL_CATCH_INTERNAL_SLANG_ERROR' --type=cpp --type=h

# 2) Inspect the source-cache load + fallback block in shader.cpp.
rg -n -C6 'cache_source_file|string\(\)\.c_str\(\)|loaded_from_cache|loadModuleFromSourceString' --type=cpp

Also applies to: 1049-1079

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/sgl/device/shader.cpp` around lines 1038 - 1041, The cache-load attempt
using SGL_CATCH_INTERNAL_SLANG_ERROR around the call that assigns slang_module =
session_data->slang_session->loadModule(...) can throw and currently aborts
before the source-fallback runs; change the logic so any exception from that
macro is caught and treated as a cache miss: wrap the cached load/assignment in
a try/catch that on exception logs the error, sets loaded_from_cache = false,
clears/sets slang_module appropriately, and allows execution to continue to the
existing source fallback (e.g., the loadModuleFromSourceString path) so
stale/corrupt cache artifacts don’t prevent fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/sgl/device/shader.cpp`:
- Around line 1038-1041: The cache-load attempt using
SGL_CATCH_INTERNAL_SLANG_ERROR around the call that assigns slang_module =
session_data->slang_session->loadModule(...) can throw and currently aborts
before the source-fallback runs; change the logic so any exception from that
macro is caught and treated as a cache miss: wrap the cached load/assignment in
a try/catch that on exception logs the error, sets loaded_from_cache = false,
clears/sets slang_module appropriately, and allows execution to continue to the
existing source fallback (e.g., the loadModuleFromSourceString path) so
stale/corrupt cache artifacts don’t prevent fallback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8f1a54b5-3462-40eb-b447-9c0088678703

📥 Commits

Reviewing files that changed from the base of the PR and between f307c62 and 3c686c9.

📒 Files selected for processing (2)
  • src/sgl/device/shader.cpp
  • src/sgl/device/shader.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/sgl/device/shader.h

Co-authored-by: Copilot <copilot@github.com>
@skallweitNV
skallweitNV marked this pull request as draft May 11, 2026 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants