Skip to content

feat(scan): budget rules for triangles, bones, and textures (#365) - #712

Merged
fernandotonon merged 3 commits into
masterfrom
feat/scan-budget-rules-365
Jun 5, 2026
Merged

feat(scan): budget rules for triangles, bones, and textures (#365)#712
fernandotonon merged 3 commits into
masterfrom
feat/scan-budget-rules-365

Conversation

@fernandotonon

@fernandotonon fernandotonon commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds actionable scan budget rules backed by Scan: extend AssetInfo / inspectAsset for triangles and texture metadata #364 AssetInfo fields: max_triangle_count, max_triangles_per_mesh, max_bones, max_submesh_count, max_draw_calls, texture POT/format checks, and max_texture_dimension alias for texture size limits.
  • Wires rules through ScanConfig / evaluateRules, SARIF descriptions, CLI overrides (--max-triangles, --max-bones, etc.), and built-in example-budget profile.
  • Texture POT/format rules require probed textureStats (enable via profile metadata inspect_textures: true or example-texture-inspect / example-budget).

Test plan

  • ScanEngineTest.EvaluateRules_* budget rule coverage (synthetic AssetInfo)
  • ScanEngineTest.FormatSarif_IncludesBudgetRuleDescriptions
  • ScanConfigApplyOverridesTest.ApplyRuleOverridesAffectsAllFields
  • PlatformProfileLoaderTest.BuiltinExampleBudgetProfileLoadsRules
  • CI Linux unit tests

Closes #365

Made with Cursor

Summary by CodeRabbit

Release Notes

  • New Features
    • Added budget rules to constrain scans by triangle count, bone count, submesh count, and draw calls.
    • Added texture constraints: power-of-two requirement and format allow/disallow lists.
    • Introduced new CLI parameters (--max-triangles, --max-bones, --max-submeshes, --max-draw-calls) for budget overrides.
    • Added example budget profile demonstrating configuration of scan constraints.

Introduce platform-profile and YAML budget rules (max triangles, bones,
submeshes, draw calls, texture POT/format checks) with evaluateRules,
SARIF metadata, CLI overrides, and example-budget profile.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@fernandotonon, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 12 minutes and 38 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e44017c8-633f-4914-8660-5ac02717c11c

📥 Commits

Reviewing files that changed from the base of the PR and between d82c3c8 and 6a9240a.

📒 Files selected for processing (4)
  • src/CLIPipeline.cpp
  • src/PlatformProfile_test.cpp
  • src/ScanConfig.cpp
  • src/ScanConfig_test.cpp
📝 Walkthrough

Walkthrough

This PR implements budget rule validation for mesh scans, adding configuration fields and rule-evaluation logic to enforce limits on triangle counts, bone counts, texture properties, and draw-call estimates. Changes include new ScanConfig fields, platform profile support, CLI budget override flags, and comprehensive rule evaluation in ScanEngine with tests.

Changes

Budget Rules Implementation

Layer / File(s) Summary
Budget configuration fields and rule override parsing
src/ScanConfig.h, src/ScanConfig.cpp
ScanConfig adds new fields for triangle/bone/submesh/draw-call caps and texture constraints. applyRuleOverrides parses these from variant maps and handles max_texture_dimension as an alias. applyProjectConfig refactored to delegate rule application.
ScanConfig rule override verification
src/ScanConfig_test.cpp
Tests verify that applyRuleOverrides correctly populates all new budget and texture fields, including alias override behavior.
Platform profile rule key registration and example budget profile
src/PlatformProfile.cpp, profiles/example-budget.json
PlatformProfile.knownRuleKeys() extended to recognize new budget rule identifiers. New example-budget.json profile defines reusable constraints extending example-texture-inspect.
Example budget profile loading and application
src/PlatformProfile_test.cpp
Test verifies the example-budget profile loads and applies correctly to ScanConfig with expected field values.
CLI budget override flags and argument parsing
src/CLIPipeline.cpp
Help text documents new --max-* CLI flags. cmdScan parses triangle/bone/submesh/draw-call overrides as validated non-negative integers and applies them to ScanConfig and cliRuleOverrides.
Budget rule evaluation logic and helper functions
src/ScanEngine.cpp
Helper functions for power-of-two checking and texture dimension computation. evaluateRules extended with geometry, skeleton, and texture constraint rule checks. SARIF descriptions updated with new rule IDs.
Budget rule evaluation test suite
src/ScanEngine_test.cpp
Comprehensive tests for geometry budgets, texture budgets with probed dimensions, power-of-two validation, format allow/disallow logic, disabled-by-default behavior, and SARIF output formatting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • fernandotonon/QtMeshEditor#369: This PR directly enables preset profile configuration support, which is the foundation requested in issue #369 for providing pre-built platform validation profiles.

Possibly related PRs

  • fernandotonon/QtMeshEditor#682: Extends the existing PlatformProfile rule-key allowlist to include new budget rule identifiers and adds the example-budget profile on top of platform profile loading infrastructure.
  • fernandotonon/QtMeshEditor#711: Adds texture probing to AssetInfo (probed texture dimensions and format stats), which this PR's rule evaluation logic in ScanEngine directly consumes for texture budget checks.

Poem

🐰 Budget limits bloom, profiles guide the way,
Triangle counts checked, bone constraints at play,
Textures inspected—POT and formats true,
Rules evaluated, validation comes through!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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
Title check ✅ Passed The PR title accurately describes the main feature being added: budget rules for triangles, bones, and textures, with reference to the related issue #365.
Description check ✅ Passed The description comprehensively covers the summary, technical details, test plan with clear status indicators, and references the closed issue. It follows the intent of the template and provides sufficient implementation context.
Linked Issues check ✅ Passed The PR successfully implements all primary objectives from issue #365: new budget rule IDs, ScanConfig fields, rule evaluation in evaluateRules, SARIF descriptions, CLI overrides, and comprehensive unit test coverage.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #365 requirements: budget rules implementation, config field extensions, CLI argument handling, tests, and profile definitions. No extraneous modifications detected.

✏️ 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 feat/scan-budget-rules-365

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

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d82c3c85f2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/ScanConfig.cpp Outdated
Comment on lines +310 to +311
if (r.contains("max_texture_dimension"))
maxTextureResolution = r["max_texture_dimension"].toInt();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve child max_texture_resolution over parent alias

When profile inheritance or another layered config leaves both keys in the merged rules map, this unconditional alias assignment makes max_texture_dimension win even if it came from a parent profile and the child overrode the canonical max_texture_resolution. In that mixed-name scenario, the child profile's texture budget is silently ignored; the alias should be normalized per layer or only applied when the canonical key is absent in the same override set.

Useful? React with 👍 / 👎.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/CLIPipeline.cpp (1)

4288-4315: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

--max-acmr loses CLI precedence when scope rules are present

maxAcmrOverride is applied to config.maxAcmr (Line 4258), but cliRuleOverrides never includes "max_acmr". That means scoped rules can still override the CLI value, which violates the precedence contract in this block.

Suggested fix
     if (maxVerticesOverride >= 0)              cliRuleOverrides["max_vertex_count"] = config.maxVertexCount;
     if (minVerticesOverride >= 0)              cliRuleOverrides["min_vertex_count"] = config.minVertexCount;
+    if (maxAcmrOverride >= 0.0)                cliRuleOverrides["max_acmr"] = config.maxAcmr;
     if (maxTrianglesOverride >= 0)             cliRuleOverrides["max_triangle_count"] = config.maxTriangleCount;
     if (maxTrianglesPerMeshOverride >= 0)      cliRuleOverrides["max_triangles_per_mesh"] = config.maxTrianglesPerMesh;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/CLIPipeline.cpp` around lines 4288 - 4315, The CLI flag maxAcmrOverride
is applied to config.maxAcmr but never added to cliRuleOverrides, so scoped
rules can override the CLI value; update the block that builds cliRuleOverrides
(the same section using hasAllowedFormatsOverride and others) to add
cliRuleOverrides["max_acmr"] = config.maxAcmr when maxAcmrOverride indicates the
CLI override is present (mirror the existing pattern used for
maxFileSizeMbOverride/maxTrianglesOverride), ensuring maxAcmrOverride,
config.maxAcmr, and cliRuleOverrides are referenced consistently so the CLI
value takes precedence.
🧹 Nitpick comments (1)
src/PlatformProfile_test.cpp (1)

208-217: ⚡ Quick win

Expand assertions to cover all budget keys declared by the profile.

This test validates only part of example-budget (max_triangle_count, max_bones, POT/probe). Please also assert max_triangles_per_mesh, max_submesh_count, max_draw_calls, max_texture_dimension mapping, and allowed_texture_formats so future key drift is caught in one place.

Suggested assertion additions
 TEST(PlatformProfileLoaderTest, BuiltinExampleBudgetProfileLoadsRules)
 {
     const PlatformProfileLoadResult loaded =
         PlatformProfileLoader::load(QStringLiteral("example-budget"));
     ASSERT_TRUE(loaded.ok) << loaded.error.toStdString();
     EXPECT_EQ(loaded.profile.rules.value(QStringLiteral("max_triangle_count")).toInt(), 50000);
+    EXPECT_EQ(loaded.profile.rules.value(QStringLiteral("max_triangles_per_mesh")).toInt(), 20000);
     EXPECT_EQ(loaded.profile.rules.value(QStringLiteral("max_bones")).toInt(), 64);
+    EXPECT_EQ(loaded.profile.rules.value(QStringLiteral("max_submesh_count")).toInt(), 8);
+    EXPECT_EQ(loaded.profile.rules.value(QStringLiteral("max_draw_calls")).toInt(), 16);
+    EXPECT_EQ(loaded.profile.rules.value(QStringLiteral("max_texture_dimension")).toInt(), 512);
+    EXPECT_EQ(loaded.profile.rules.value(QStringLiteral("allowed_texture_formats")).toStringList(),
+              (QStringList{QStringLiteral("png"), QStringLiteral("jpg")}));
     EXPECT_TRUE(loaded.profile.metadata.value(QStringLiteral("inspect_textures")).toBool());

     ScanConfig config = ScanConfig::defaults();
     applyPlatformProfile(config, loaded.profile);
     EXPECT_EQ(config.maxTriangleCount, 50000);
+    EXPECT_EQ(config.maxTrianglesPerMesh, 20000);
     EXPECT_EQ(config.maxBoneCount, 64);
+    EXPECT_EQ(config.maxSubmeshCount, 8);
+    EXPECT_EQ(config.maxDrawCalls, 16);
+    EXPECT_EQ(config.maxTextureResolution, 512);
+    EXPECT_EQ(config.allowedTextureFormats, (QStringList{QStringLiteral("png"), QStringLiteral("jpg")}));
     EXPECT_TRUE(config.probeTextureFiles);
     EXPECT_TRUE(config.requireTexturePowerOfTwo);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/PlatformProfile_test.cpp` around lines 208 - 217, Extend the test
assertions after applyPlatformProfile to verify all budget keys from the
profile: add EXPECT_EQ checks that loaded.profile.rules contains and maps
max_triangles_per_mesh -> config.maxTrianglesPerMesh, max_submesh_count ->
config.maxSubmeshCount, max_draw_calls -> config.maxDrawCalls,
max_texture_dimension -> config.maxTextureDimension, and that
allowed_texture_formats maps to config.allowedTextureFormats (or the
corresponding ScanConfig member) and contains the expected formats; keep
references to ScanConfig, applyPlatformProfile, and loaded.profile to locate the
changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/CLIPipeline.cpp`:
- Around line 4288-4315: The CLI flag maxAcmrOverride is applied to
config.maxAcmr but never added to cliRuleOverrides, so scoped rules can override
the CLI value; update the block that builds cliRuleOverrides (the same section
using hasAllowedFormatsOverride and others) to add cliRuleOverrides["max_acmr"]
= config.maxAcmr when maxAcmrOverride indicates the CLI override is present
(mirror the existing pattern used for
maxFileSizeMbOverride/maxTrianglesOverride), ensuring maxAcmrOverride,
config.maxAcmr, and cliRuleOverrides are referenced consistently so the CLI
value takes precedence.

---

Nitpick comments:
In `@src/PlatformProfile_test.cpp`:
- Around line 208-217: Extend the test assertions after applyPlatformProfile to
verify all budget keys from the profile: add EXPECT_EQ checks that
loaded.profile.rules contains and maps max_triangles_per_mesh ->
config.maxTrianglesPerMesh, max_submesh_count -> config.maxSubmeshCount,
max_draw_calls -> config.maxDrawCalls, max_texture_dimension ->
config.maxTextureDimension, and that allowed_texture_formats maps to
config.allowedTextureFormats (or the corresponding ScanConfig member) and
contains the expected formats; keep references to ScanConfig,
applyPlatformProfile, and loaded.profile to locate the changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 04d983f2-4ea8-4b83-957e-6f565d4d5ec9

📥 Commits

Reviewing files that changed from the base of the PR and between 1739351 and d82c3c8.

📒 Files selected for processing (9)
  • profiles/example-budget.json
  • src/CLIPipeline.cpp
  • src/PlatformProfile.cpp
  • src/PlatformProfile_test.cpp
  • src/ScanConfig.cpp
  • src/ScanConfig.h
  • src/ScanConfig_test.cpp
  • src/ScanEngine.cpp
  • src/ScanEngine_test.cpp

fernandotonon and others added 2 commits June 5, 2026 09:09
Prefer max_texture_resolution over the dimension alias when both keys appear in the same override set, and ensure --max-acmr is included in CLI scope overrides so scoped rules cannot override it.

Co-authored-by: Cursor <cursoragent@cursor.com>
Assert all budget rule keys from the profile map through applyPlatformProfile into ScanConfig.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sonarqubecloud

sonarqubecloud Bot commented Jun 5, 2026

Copy link
Copy Markdown

@fernandotonon
fernandotonon merged commit 54f83ce into master Jun 5, 2026
20 checks passed
@fernandotonon
fernandotonon deleted the feat/scan-budget-rules-365 branch June 5, 2026 13:59
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.

Scan: budget rules (triangles, bones, textures, POT, format allow-list)

1 participant