Skip to content

D7vk support#338

Closed
phobos665 wants to merge 23 commits intoutkarshdalal:masterfrom
phobos665:d7vk-support
Closed

D7vk support#338
phobos665 wants to merge 23 commits intoutkarshdalal:masterfrom
phobos665:d7vk-support

Conversation

@phobos665
Copy link
Copy Markdown
Contributor

@phobos665 phobos665 commented Dec 13, 2025

Added D7VK Support

This PR adds support for D7VK as a graphics wrapper option alongside DXVK and VKD3D.

What is D7VK?

D7VK is a Vulkan-based translation layer for DirectX 7/8/9, designed to improve compatibility and performance for older Windows games on ARM devices. Unlike DXVK (which focuses on DirectX 10/11), D7VK specifically targets legacy DirectX APIs commonly used by games from the late 1990s to mid-2000s.

Benefits (According to D7VK Repository):

  • Better performance for older DirectX 7/8/9 games compared to native Wine translation
  • Reduced CPU overhead by leveraging modern Vulkan drivers
  • Improved compatibility with legacy rendering techniques
  • Lower memory usage for games using older graphics API

Changes:

  • Added D7VK 1.1 asset (d7vk-1.0.tzst) to assets/dxwrapper/
  • Added D7VK as a selectable wrapper in container configuration UI
  • Implemented version normalization (d7vk → d7vk-1.0) before extraction
  • Added DefaultVersion.D7VK constant for version validation

Note: D7VK is still in a decently early stage of development, so games will still be a while before in a good state.

Summary by CodeRabbit

  • New Features

    • Added D7VK as a selectable graphics wrapper with its own version dropdown; D7VK versions load, persist, and apply alongside existing wrappers.
    • FPS HUD can now be enabled for D7VK via the HUD setting.
  • Bug Fixes

    • Improved robustness when deriving and syncing per-wrapper version selections, including D7VK variants.
  • Resources

    • Added UI labels, version entries, and environment variable support for D7VK.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 13, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds first-class D7VK support: UI state and dropdown, resource entries and default version, content-type enum, dxwrapper normalization/extraction updates, and container eligibility for d7vk-* values; surfaces per-wrapper D7VK version list in ContainerConfigState.

Changes

Cohort / File(s) Summary
UI: Container config & tabs
app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt, app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigState.kt, app/src/main/java/app/gamenative/ui/component/dialog/GraphicsTab.kt
Add D7VK per-wrapper state (d7vkVersionsBase, d7vkVersionsAll, d7vkVersionIndex), expose d7vkVersionsAll on ContainerConfigState, and add a D7VK Version dropdown wired to dxwrapperConfig.
Setup & extraction flow
app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
Persist/derive D7VK version (default DefaultVersion.D7VK), set D7VK_HUD env var when HUD enabled, and treat d7vk-* like other dxwrapper variants during extraction (logging and asset selection updated).
Container core eligibility
app/src/main/java/com/winlator/container/Container.java
Extend dxwrapper eligibility checks to accept d7vk- prefixed dxwrapper names alongside existing dxvk- and d8vk-.
Content types & defaults
app/src/main/java/com/winlator/contents/ContentProfile.java, app/src/main/java/com/winlator/core/DefaultVersion.java
Add CONTENT_TYPE_D7VK enum value and DefaultVersion.D7VK constant.
Resources & strings
app/src/main/res/values/arrays.xml, app/src/main/res/values/strings.xml
Add d7vk_version_entries array, insert D7VK into dxwrapper_entries, and add d7vk_version string resource.
Env vars
app/src/main/java/com/winlator/core/envvars/EnvVarInfo.kt
Add D7VK_HUD to known env vars (multi-select, same options as DXVK_HUD).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as ContainerConfigDialog (UI)
  participant State as ContainerConfigState
  participant Setup as XServerScreen
  participant Container as Container core
  participant FS as Filesystem

  Note over UI,State: User selects wrapper and per-wrapper version
  UI->>State: update dxwrapperConfig (e.g., "d7vk-1.1") and d7vkVersionIndex
  State-->>UI: state updated

  alt dxwrapper starts with "d7vk-"
    UI->>Setup: setupWineSystemFiles(dxwrapper)
    Setup->>State: read/validate d7vk version (fallback DefaultVersion.D7VK)
    Setup->>Container: request extraction for dxwrapper (d7vk-*)
    Container->>FS: extract DXVK/D7VK/D8VK DLLs / apply content profile
    FS-->>Container: extraction result
    Container-->>Setup: extraction applied
  else other dxwrapper variants
    UI->>Setup: setupWineSystemFiles(dxwrapper=...)
    Setup->>Container: request extraction for dxwrapper
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped into code with a curious click,
D7VK now dances, nimble and quick.
Dropdowns aligned, defaults set true,
Extraction hums, versions cozy and new. 🎮✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'D7vk support' directly summarizes the main change: adding D7VK as a new graphics wrapper option alongside DXVK and VKD3D.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@phobos665
Copy link
Copy Markdown
Contributor Author

Going to update this PR tomorrow to add the 1.1 instead as that has large improvements over 1.0

@phobos665 phobos665 marked this pull request as ready for review January 2, 2026 22:21
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt (1)

41-47: Duplicate import of TextButton.

TextButton is imported twice (lines 41 and 47). Remove one of the duplicates.

🔎 Proposed fix
 import androidx.compose.material3.TextButton
 import androidx.compose.material3.OutlinedTextField
 import androidx.compose.material3.Scaffold
 import androidx.compose.material3.Slider
 import androidx.compose.material3.CircularProgressIndicator
 import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
🧹 Nitpick comments (1)
app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt (1)

482-516: Consider using DefaultVersion constants instead of hardcoded index 0.

When resetting to a default version, using DefaultVersion.D7VK and DefaultVersion.DXVK would be more robust and consistent with how defaults are handled elsewhere in the codebase (e.g., line 709 uses DefaultVersion.DXVK).

🔎 Proposed fix
             when (config.dxwrapper) {
                 "d7vk" -> {
                     // Check if version is valid for D7VK
                     val isValidD7VKVersion = d7vkVersionsAll.any {
                         StringUtils.parseIdentifier(it) == savedVersion
                     }
                     if (!isValidD7VKVersion && d7vkVersionsAll.isNotEmpty()) {
-                        // Reset to first D7VK version
-                        val defaultVersion = StringUtils.parseIdentifier(d7vkVersionsAll[0])
+                        // Reset to default D7VK version
+                        val defaultIdx = d7vkVersionsAll.indexOfFirst { 
+                            StringUtils.parseIdentifier(it) == DefaultVersion.D7VK 
+                        }.coerceAtLeast(0)
+                        val defaultVersion = StringUtils.parseIdentifier(d7vkVersionsAll[defaultIdx])
                         currentConfig.put("version", defaultVersion)
                         config = config.copy(dxwrapperConfig = currentConfig.toString())
-                        d7vkVersionIndex = 0
+                        d7vkVersionIndex = defaultIdx
                     }
                 }
                 "dxvk" -> {
                     // Check if version is valid for DXVK
                     val isValidDXVKVersion = dxvkVersionsAll.any {
                         StringUtils.parseIdentifier(it) == savedVersion
                     }
                     if (!isValidDXVKVersion && dxvkVersionsAll.isNotEmpty()) {
-                        // Reset to first DXVK version
-                        val defaultVersion = StringUtils.parseIdentifier(dxvkVersionsAll[0])
+                        // Reset to default DXVK version
+                        val defaultIdx = dxvkVersionsAll.indexOfFirst { 
+                            StringUtils.parseIdentifier(it) == DefaultVersion.DXVK 
+                        }.coerceAtLeast(0)
+                        val defaultVersion = StringUtils.parseIdentifier(dxvkVersionsAll[defaultIdx])
                         currentConfig.put("version", version)
                         config = config.copy(dxwrapperConfig = currentConfig.toString())
-                        dxvkVersionIndex = 0
+                        dxvkVersionIndex = defaultIdx
                     }
                 }
             }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1774b56 and 907fb3a.

📒 Files selected for processing (7)
  • app/src/main/assets/dxwrapper/d7vk-1.1.tzst
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
  • app/src/main/java/com/winlator/container/Container.java
  • app/src/main/java/com/winlator/contents/ContentProfile.java
  • app/src/main/java/com/winlator/core/DefaultVersion.java
  • app/src/main/res/values/arrays.xml
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt (3)
app/src/main/java/com/winlator/core/KeyValueSet.java (1)
  • KeyValueSet (7-169)
app/src/main/java/app/gamenative/ui/component/settings/SettingsListDropdown.kt (1)
  • SettingsListDropdown (40-125)
app/src/main/java/app/gamenative/ui/theme/Color.kt (1)
  • settingsTileColors (27-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (8)
app/src/main/java/com/winlator/core/DefaultVersion.java (1)

18-18: LGTM!

The D7VK version constant follows the established pattern and is consistent with other wrapper version constants in the class.

app/src/main/java/com/winlator/contents/ContentProfile.java (1)

28-28: LGTM!

The D7VK content type enum addition is properly placed alongside other DirectX wrapper types and follows the established naming convention.

app/src/main/java/com/winlator/container/Container.java (1)

850-850: LGTM!

The extension of the dxwrapper eligibility check to include d7vk-* prefixes is consistent with the existing handling of d8vk-* and dxvk-* variants.

app/src/main/res/values/arrays.xml (1)

79-79: LGTM!

The D7VK resource entries are properly integrated:

  • "D7VK" added to wrapper selection list
  • Version array initialized with "1.1" (matching DefaultVersion.D7VK)
  • Logical placement alongside other wrapper options

Also applies to: 83-85

app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt (2)

2145-2155: Verify the intentional version enforcement for D7VK.

The D7VK version normalization logic differs from the DXVK implementation:

  • D7VK (lines 2145-2155): Forces DefaultVersion.D7VK ("1.1") when the version is null or doesn't match the default
  • DXVK (lines 2140-2144): Simply appends the version from config without validation

This inconsistency means:

  1. Users cannot select custom D7VK versions (e.g., future 1.2 or custom builds)
  2. Any existing D7VK 1.0 configurations will be automatically upgraded to 1.1

If this forced versioning is intentional (e.g., to ensure users benefit from 1.1 improvements mentioned in the PR), consider:

  • Adding a comment explaining why D7VK differs from DXVK
  • Evaluating whether DXVK should have similar validation

If unintentional, align with DXVK behavior by removing the version != DefaultVersion.D7VK condition.

🔎 Suggested refactor to align with DXVK behavior
 if (xServerState.value.dxwrapper == "d7vk") {
-    // Set version for d7vk or use default if not found.
-    var version = xServerState.value.dxwrapperConfig?.get("version")
-    if (version == null || version != DefaultVersion.D7VK) {
-        version = DefaultVersion.D7VK
-        xServerState.value.dxwrapperConfig?.put("version", version)
-    }
     xServerState.value = xServerState.value.copy(
-        dxwrapper = "d7vk-" + version,
+        dxwrapper = "d7vk-" + xServerState.value.dxwrapperConfig?.get("version"),
     )
 }

2335-2341: LGTM!

The updated comments and log messages appropriately reflect the expanded support for D7VK alongside DXVK and D8VK wrappers.

app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt (2)

149-149: LGTM!

The D7VK version list initialization follows the established pattern used for DXVK and VKD3D, correctly initializing base versions from resources, creating mutable state, and augmenting with installed content profiles.

Also applies to: 191-191, 231-231


714-742: LGTM!

Good addition of the guard clause to ensure this DXVK-specific async version handling only runs when the DXVK wrapper is selected, preventing unintended side effects when D7VK or other wrappers are active.

Comment thread app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt Outdated
Comment thread app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt Outdated
@phobos665
Copy link
Copy Markdown
Contributor Author

Will do some final tests to ensure that everything is working correctly.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt:
- Around line 591-597: Remove the composition-time mutation of config: delete
the else-if block that checks isVKD3D and writes version into
config.dxwrapperConfig (the block that constructs
KeyValueSet(config.dxwrapperConfig), puts "version", and assigns config =
config.copy(...)). This logic is already handled in
LaunchedEffect(graphicsDriverIndex, dxWrapperIndex), so stop performing
side-effectful updates to config during composition (remove references to
isVKD3D/isVortekLike in that block).
- Around line 535-554: The code sets version fields by directly indexing
dxvkVersionsAll[0] and d7vkVersionsAll[0] which can throw
IndexOutOfBoundsException; update the dxwrapper switch in
ContainerConfigDialog.kt (symbols: dxvkVersionIndex, d7vkVersionIndex,
dxvkVersionsAll, d7vkVersionsAll, currentConfig.put) to first check that the
respective list is not empty (e.g., if (dxvkVersionsAll.isNotEmpty()) {
dxvkVersionIndex = 0; val newVersion =
StringUtils.parseIdentifier(dxvkVersionsAll[0]); currentConfig.put("version",
newVersion) } else { /* set a safe default or skip putting version */ }) and do
the equivalent for d7vkVersionsAll so you only parse and store index 0 when
present and avoid updating the index when the list is empty.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 907fb3a and 66299d6.

📒 Files selected for processing (2)
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt
  • app/src/main/res/values/strings.xml
🔇 Additional comments (7)
app/src/main/res/values/strings.xml (1)

117-117: LGTM! String resource properly added.

The D7VK version string resource follows the same pattern as the existing DXVK version string (line 116) and is correctly placed in the Win Components section for proper internationalization.

app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt (6)

149-149: LGTM! D7VK version lists properly initialized.

The D7VK version base and runtime lists follow the same pattern as DXVK versions, ensuring consistency across wrapper implementations.

Also applies to: 191-191


231-231: LGTM! D7VK content profiles properly loaded.

The D7VK versions are correctly augmented with installed content profiles, following the same pattern as DXVK and VKD3D.


479-479: LGTM! D7VK version index state properly initialized.

The D7VK version index state variable follows the same pattern as the DXVK version index.


482-516: Good addition, but bounds checking still needed.

The validation logic properly fixes version mismatches when loading containers, which prevents crashes from stale or invalid version references. However, the bounds checking issue flagged in past reviews (lines 494-500 and 507-513) still applies: accessing d7vkVersionsAll[0] or dxvkVersionsAll[0] without checking for empty lists can throw IndexOutOfBoundsException.

Add defensive checks before accessing index 0:

"d7vk" -> {
    val isValidD7VKVersion = d7vkVersionsAll.any {
        StringUtils.parseIdentifier(it) == savedVersion
    }
    if (!isValidD7VKVersion && d7vkVersionsAll.isNotEmpty()) {
        val defaultVersion = StringUtils.parseIdentifier(d7vkVersionsAll[0])
        currentConfig.put("version", defaultVersion)
        config = config.copy(dxwrapperConfig = currentConfig.toString())
        d7vkVersionIndex = 0
    } else if (d7vkVersionsAll.isEmpty()) {
        // Handle empty list case - perhaps log warning or use a hardcoded default
        timber.log.Timber.w("D7VK versions list is empty, cannot reset version")
    }
}

Similar pattern for the "dxvk" case.


599-618: LGTM! D7VK version dropdown properly implemented.

The D7VK version dropdown is correctly implemented with:

  • Proper use of string resource for the title (line 606)
  • Bounds checking via coerceIn (line 607)
  • Correct state management and config updates
  • Conditional visibility based on wrapper selection

715-743: Good improvement to scope DXVK version handling.

The addition of the wrapper type check (lines 715-718) ensures DXVK version logic only runs when the DXVK wrapper is actually selected, preventing unintended version updates for D7VK or VKD3D wrappers. This makes the version management more robust and prevents conflicts between different wrapper types.

Comment thread app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt Outdated
Comment thread app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt Outdated
@phobos665 phobos665 marked this pull request as draft January 7, 2026 12:01
@phobos665 phobos665 marked this pull request as ready for review January 7, 2026 12:28
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt (1)

706-715: Missing D7VK version index synchronization.

DXVK has this LaunchedEffect to sync the version index from config.dxwrapperConfig when the dialog loads, ensuring the UI displays the correct saved version. D7VK lacks equivalent logic. When the dialog loads with a saved D7VK container, d7vkVersionIndex remains at its initial value of 0 (line 479) instead of reflecting the actual saved version, causing the dropdown at line 610 to display the first version in the list rather than the configured version.

🔄 Proposed fix: Add D7VK version index sync

Add a similar effect for D7VK after line 715:

        LaunchedEffect(versionsLoaded, d7vkVersionsAll, config.dxwrapper, config.dxwrapperConfig) {
            if (!versionsLoaded) return@LaunchedEffect
            if (config.dxwrapper != "d7vk") return@LaunchedEffect
            val kvs = KeyValueSet(config.dxwrapperConfig)
            val configuredVersion = kvs.get("version")
            val foundIndex = d7vkVersionsAll.indexOfFirst { StringUtils.parseIdentifier(it) == configuredVersion }
            val defaultIndex = d7vkVersionsAll.indexOfFirst { StringUtils.parseIdentifier(it) == DefaultVersion.D7VK }.coerceAtLeast(0)
            val newIdx = if (foundIndex >= 0) foundIndex else defaultIndex
            if (d7vkVersionIndex != newIdx) d7vkVersionIndex = newIdx
        }
🤖 Fix all issues with AI agents
In
@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt:
- Around line 595-600: The code block that builds and mutates `config` using
`val version = if (isVortekLike) "1.10.3" else "2.4.1"`,
`KeyValueSet(config.dxwrapperConfig)` and `config = config.copy(dxwrapperConfig
= currentConfig.toString())` must be removed because it performs a
composition-side effect that is already handled in
`LaunchedEffect(graphicsDriverIndex, dxWrapperIndex)` and incorrectly runs for
D7VK; delete that entire else-branch so DXVK/VKD3D versioning is only set by the
existing `LaunchedEffect` and D7VK retains its own handling in the D7VK-specific
code paths.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66299d6 and 9ed15fc.

📒 Files selected for processing (3)
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
  • app/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/res/values/strings.xml
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt (3)
app/src/main/java/com/winlator/core/KeyValueSet.java (1)
  • KeyValueSet (7-169)
app/src/main/java/app/gamenative/ui/component/settings/SettingsListDropdown.kt (1)
  • SettingsListDropdown (40-125)
app/src/main/java/app/gamenative/ui/theme/Color.kt (1)
  • settingsTileColors (27-32)
🔇 Additional comments (4)
app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt (4)

149-149: LGTM: D7VK state initialization follows established patterns.

The D7VK version lists and state variables are initialized consistently with the existing DXVK implementation.

Also applies to: 191-191, 231-231, 479-479


482-516: LGTM: Validation logic properly handles empty version lists.

The bounds checks (isNotEmpty()) correctly prevent IndexOutOfBoundsException when version lists are empty, addressing concerns from previous reviews.


535-558: LGTM: Wrapper switching safely resets versions with bounds checking.

The addition of isNotEmpty() guards before accessing [0] correctly addresses the IndexOutOfBoundsException concerns raised in previous reviews.


602-621: LGTM: D7VK version dropdown properly implemented.

The dropdown uses proper string resources (addressing the previous review comment) and safely handles bounds with coerceIn. The version selection logic correctly updates dxwrapperConfig.

Comment thread app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt Outdated
@phobos665 phobos665 marked this pull request as draft January 8, 2026 11:26
@phobos665 phobos665 marked this pull request as ready for review February 23, 2026 12:24
Also hide the dxvk version when d7vk is selected.
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 10 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt">

<violation number="1" location="app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt:2769">
P2: D7VK normalization overwrites any non-default version, so user-selected D7VK versions are reset to the default and ignored during extraction.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/ui/component/dialog/GraphicsTab.kt">

<violation number="1" location="app/src/main/java/app/gamenative/ui/component/dialog/GraphicsTab.kt:348">
P2: D7VK now hides the DXVK dropdown, but the hidden-branch still overwrites `dxwrapperConfig.version` with a DXVK default. This runs when D7VK is selected, so the D7VK version can’t persist and the UI reads the wrong default.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread app/src/main/java/app/gamenative/ui/component/dialog/GraphicsTab.kt
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

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

Inline comments:
In
`@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt`:
- Around line 677-679: Change the incorrect delegated declaration for
d7vkVersionIndexRef to an assigned MutableState so it matches the dxvk pattern:
replace "var d7vkVersionIndexRef by rememberSaveable { mutableIntStateOf(0) }"
with "val d7vkVersionIndexRef = rememberSaveable { mutableIntStateOf(0) }" so
that the subsequent "var d7vkVersionIndex by d7vkVersionIndexRef" can validly
use the state delegate; keep the rememberSaveable and mutableIntStateOf calls
unchanged.
- Around line 677-679: d7vkVersionIndexRef and d7vkVersionIndex are declared but
never used; either remove them or wire them into ContainerConfigState like
dxvkVersionIndex. Add a LaunchedEffect that consumes d7vkVersionIndexRef
(mirroring the dxvk LaunchedEffect pattern) and set d7vkVersionIndex =
d7vkVersionIndexRef where ContainerConfigState is constructed (similar to
dxvkVersionIndex = dxvkVersionIndexRef), or simply delete both
d7vkVersionIndexRef/d7vkVersionIndex declarations if D7VK version tracking isn’t
needed.

In `@app/src/main/java/app/gamenative/ui/component/dialog/GraphicsTab.kt`:
- Around line 455-473: The build fails because DefaultVersion is unresolved in
GraphicsTab.kt; fix it by adding the appropriate import for DefaultVersion (the
enum/object used for the default "D7VK" version) at the top of the file where
other imports are declared—locate DefaultVersion in the codebase (or via your
IDE) and import its declaring package so the reference in the D7VK Version UI
(used in the SettingsListDropdown logic) compiles.

In `@app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt`:
- Around line 2766-2775: The dxwrapper version logic currently forces the
default for any non-matching version; change it to only apply the default when
the stored value is missing or blank: check
xServerState.value.dxwrapperConfig?.get("version") with isNullOrBlank() (or
equivalent) and only then set version = DefaultVersion.D7VK and put it into
dxwrapperConfig; otherwise preserve the existing non-empty version and continue
to set xServerState.value = xServerState.value.copy(dxwrapper = "d7vk-" +
version).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ed15fc and 75173db.

📒 Files selected for processing (7)
  • app/src/main/assets/dxwrapper/d7vk-1.3.tzst
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigState.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/GraphicsTab.kt
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
  • app/src/main/java/com/winlator/container/Container.java
  • app/src/main/java/com/winlator/core/DefaultVersion.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/winlator/core/DefaultVersion.java

Comment on lines +677 to +679
var d7vkVersionIndexRef by rememberSaveable { mutableIntStateOf(0) }
var d7vkVersionIndex by d7vkVersionIndexRef

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Build failure: incorrect delegation syntax for d7vkVersionIndexRef.

var d7vkVersionIndexRef by rememberSaveable { mutableIntStateOf(0) } uses Kotlin property delegation — by causes d7vkVersionIndexRef to resolve to Int (the unwrapped value), not MutableState<Int>. The subsequent var d7vkVersionIndex by d7vkVersionIndexRef then tries to use a plain Int as a property delegate, which has no getValue method, producing the exact pipeline error:

Property delegate must have a 'getValue(Nothing?, KMutableProperty0<*>)' method.

The correct pattern, already used for the analogous dxvkVersionIndexRef at lines 674–675, is val … = (assignment), not var … by (delegation):

🐛 Proposed fix
-        var d7vkVersionIndexRef by rememberSaveable { mutableIntStateOf(0) }
+        val d7vkVersionIndexRef = rememberSaveable { mutableIntStateOf(0) }
         var d7vkVersionIndex by d7vkVersionIndexRef
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var d7vkVersionIndexRef by rememberSaveable { mutableIntStateOf(0) }
var d7vkVersionIndex by d7vkVersionIndexRef
val d7vkVersionIndexRef = rememberSaveable { mutableIntStateOf(0) }
var d7vkVersionIndex by d7vkVersionIndexRef
🧰 Tools
🪛 GitHub Actions: Pull Request build check

[error] 678-678: Command './gradlew :app:testDebugUnitTest' - Property delegate must have a 'getValue(Nothing?, KMutableProperty0<*>)' method.

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

In
`@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt`
around lines 677 - 679, Change the incorrect delegated declaration for
d7vkVersionIndexRef to an assigned MutableState so it matches the dxvk pattern:
replace "var d7vkVersionIndexRef by rememberSaveable { mutableIntStateOf(0) }"
with "val d7vkVersionIndexRef = rememberSaveable { mutableIntStateOf(0) }" so
that the subsequent "var d7vkVersionIndex by d7vkVersionIndexRef" can validly
use the state delegate; keep the rememberSaveable and mutableIntStateOf calls
unchanged.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n "d7vkVersionIndex" --type=kt -C3

Repository: utkarshdalal/GameNative

Length of output: 93


🏁 Script executed:

rg "d7vkVersionIndex" --type-add 'kt:*.kt' --type kt -C3

Repository: utkarshdalal/GameNative

Length of output: 1053


🏁 Script executed:

rg "d7vkVersionIndex" --type-add 'kt:*.kt' --type kt

Repository: utkarshdalal/GameNative

Length of output: 350


🏁 Script executed:

# Find ContainerConfigState definition
rg -n "class ContainerConfigState|data class ContainerConfigState" --type-add 'kt:*.kt' --type kt -A 20

Repository: utkarshdalal/GameNative

Length of output: 2817


🏁 Script executed:

# Get full ContainerConfigState class definition
rg -n "class ContainerConfigState" --type-add 'kt:*.kt' --type kt -A 50 | head -80

Repository: utkarshdalal/GameNative

Length of output: 6622


🏁 Script executed:

rg "d7vkVersionIndex" app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigState.kt

Repository: utkarshdalal/GameNative

Length of output: 49


🏁 Script executed:

# Check lines 722-776 for LaunchedEffect pattern
sed -n '722,776p' app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt

Repository: utkarshdalal/GameNative

Length of output: 3282


🏁 Script executed:

# Check lines 960-975 for state construction
sed -n '960,975p' app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt

Repository: utkarshdalal/GameNative

Length of output: 916


🏁 Script executed:

# Search for d7vkVersionIndex in state construction context
rg -n "d7vkVersionIndex" app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt -B2 -A2

Repository: utkarshdalal/GameNative

Length of output: 334


🏁 Script executed:

# Look for state construction section with d7vkVersionIndex
rg -n "d7vkVersionIndex.*=" app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt

Repository: utkarshdalal/GameNative

Length of output: 121


🏁 Script executed:

# Search for LaunchedEffect with d7vk
rg -n "LaunchedEffect.*d7vk|d7vk.*LaunchedEffect" app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt -B2 -A15

Repository: utkarshdalal/GameNative

Length of output: 49


🏁 Script executed:

# Search for any d7vk references
rg -n "d7vk" app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt -B2 -A2

Repository: utkarshdalal/GameNative

Length of output: 1536


🏁 Script executed:

# Search for where ContainerConfigState is instantiated
rg -n "ContainerConfigState(" app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt -A 70 | grep -E "^\s*[a-zA-Z]+.*=\s*(.*Ref|.*Index)"

Repository: utkarshdalal/GameNative

Length of output: 159


🏁 Script executed:

# Find ContainerConfigState instantiation
rg -n "ContainerConfigState\(" app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt -A 70

Repository: utkarshdalal/GameNative

Length of output: 4153


🏁 Script executed:

# Get more lines to see if d7vkVersionIndex appears later in state construction
sed -n '932,1010p' app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt | grep -i "d7vk\|dxvk"

Repository: utkarshdalal/GameNative

Length of output: 267


🏁 Script executed:

# Search for d7vk references in GraphicsTab
rg -n "d7vk" app/src/main/java/app/gamenative/ui/component/graphics/GraphicsTab.kt -B2 -A2

Repository: utkarshdalal/GameNative

Length of output: 176


d7vkVersionIndex is unused and not wired into ContainerConfigState.

The variable is declared at lines 677-678 but never referenced elsewhere in the file. Unlike dxvkVersionIndex, it has no LaunchedEffect consuming it (compare to lines 722-776) and is not passed to the ContainerConfigState constructor (compare dxvkVersionIndex = dxvkVersionIndexRef at line 967). Either remove the unused variable or wire it into the state following the same pattern as dxvkVersionIndex.

🧰 Tools
🪛 GitHub Actions: Pull Request build check

[error] 678-678: Command './gradlew :app:testDebugUnitTest' - Property delegate must have a 'getValue(Nothing?, KMutableProperty0<*>)' method.

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

In
`@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt`
around lines 677 - 679, d7vkVersionIndexRef and d7vkVersionIndex are declared
but never used; either remove them or wire them into ContainerConfigState like
dxvkVersionIndex. Add a LaunchedEffect that consumes d7vkVersionIndexRef
(mirroring the dxvk LaunchedEffect pattern) and set d7vkVersionIndex =
d7vkVersionIndexRef where ContainerConfigState is constructed (similar to
dxvkVersionIndex = dxvkVersionIndexRef), or simply delete both
d7vkVersionIndexRef/d7vkVersionIndex declarations if D7VK version tracking isn’t
needed.

Comment thread app/src/main/java/app/gamenative/ui/component/dialog/GraphicsTab.kt
Comment thread app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt (2)

677-678: ⚠️ Potential issue | 🔴 Critical

d7vkVersionIndexRef still uses by (delegation) instead of = (assignment), leaving it an unwrapped Int.

by rememberSaveable { mutableIntStateOf(0) } calls getValue() on the MutableIntState, so d7vkVersionIndexRef is of type Int, not MutableState<Int>. Consequently:

  • d7vkVersionIndex at line 678 is a plain Int copy — mutations to it don't trigger recomposition.
  • Neither can be passed as a MutableState<Int> ref to ContainerConfigState, preventing D7VK version tracking from working at all.

The correct pattern mirrors dxvkVersionIndexRef at lines 674–675:

🐛 Proposed fix
-        var d7vkVersionIndexRef by rememberSaveable { mutableIntStateOf(0) }
-        var d7vkVersionIndex = d7vkVersionIndexRef
+        val d7vkVersionIndexRef = rememberSaveable { mutableIntStateOf(0) }
+        var d7vkVersionIndex by d7vkVersionIndexRef
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt`
around lines 677 - 678, d7vkVersionIndexRef is currently declared with
delegation (by rememberSaveable { mutableIntStateOf(0) }) which yields an Int
not a MutableState, so d7vkVersionIndex becomes a plain Int and won't recompose
or be passable to ContainerConfigState; change the declaration to mirror
dxvkVersionIndexRef (i.e., remove the "by" and assign the result of
rememberSaveable { mutableIntStateOf(0) } to get a MutableIntState), then use
that MutableIntState (and not the copied Int) when passing the D7VK version ref
into ContainerConfigState so version changes trigger recomposition and state is
mutable.

939-939: ⚠️ Potential issue | 🔴 Critical

d7vkVersionIndexRef is not wired into ContainerConfigState.

Line 939 correctly passes d7vkVersionsAllRef, but after fixing the by= issue at line 677, the resulting MutableIntState also needs to be forwarded — matching the dxvkVersionIndex = dxvkVersionIndexRef parameter at line 967. Without it, GraphicsTab.kt (and any other downstream consumer of ContainerConfigState) cannot read or persist the selected D7VK version index.

// In ContainerConfigState constructor call, alongside line 967:
+ d7vkVersionIndex = d7vkVersionIndexRef,

Also applies to: 967-967

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

In
`@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt`
at line 939, ContainerConfigState is missing the d7vkVersionIndex binding so the
MutableIntState d7vkVersionIndexRef isn't forwarded; in the ContainerConfigState
constructor call (where d7vkVersionsAll = d7vkVersionsAllRef and
dxvkVersionIndex = dxvkVersionIndexRef are passed) add d7vkVersionIndex =
d7vkVersionIndexRef so GraphicsTab.kt and other consumers can read and persist
the selected D7VK index.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/com/winlator/core/envvars/EnvVarInfo.kt`:
- Around line 212-219: Remove the incorrect "D7VK_HUD" EnvVarInfo entry from
KNOWN_ENV_VARS (the map entry with identifier "D7VK_HUD" and selectionType
EnvVarSelectionType.MULTI_SELECT) so the HUD is controlled solely by the
existing "DXVK_HUD" entry; also remove any code that sets the D7VK_HUD
environment variable at runtime (where XServerScreen sets env vars) to avoid
silently ignored configuration and rely on DXVK_HUD instead.

---

Duplicate comments:
In
`@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt`:
- Around line 677-678: d7vkVersionIndexRef is currently declared with delegation
(by rememberSaveable { mutableIntStateOf(0) }) which yields an Int not a
MutableState, so d7vkVersionIndex becomes a plain Int and won't recompose or be
passable to ContainerConfigState; change the declaration to mirror
dxvkVersionIndexRef (i.e., remove the "by" and assign the result of
rememberSaveable { mutableIntStateOf(0) } to get a MutableIntState), then use
that MutableIntState (and not the copied Int) when passing the D7VK version ref
into ContainerConfigState so version changes trigger recomposition and state is
mutable.
- Line 939: ContainerConfigState is missing the d7vkVersionIndex binding so the
MutableIntState d7vkVersionIndexRef isn't forwarded; in the ContainerConfigState
constructor call (where d7vkVersionsAll = d7vkVersionsAllRef and
dxvkVersionIndex = dxvkVersionIndexRef are passed) add d7vkVersionIndex =
d7vkVersionIndexRef so GraphicsTab.kt and other consumers can read and persist
the selected D7VK index.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75173db and e97e56c.

📒 Files selected for processing (4)
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/GraphicsTab.kt
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
  • app/src/main/java/com/winlator/core/envvars/EnvVarInfo.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/app/gamenative/ui/component/dialog/GraphicsTab.kt
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt

Comment thread app/src/main/java/com/winlator/core/envvars/EnvVarInfo.kt
@phobos665 phobos665 marked this pull request as draft February 24, 2026 00:02
@phobos665
Copy link
Copy Markdown
Contributor Author

Had a chat with folk who've used d7vk.

Turns out I misread the readme. Instead of extracting to syswow64, we need to extract it to the game folder.

We should be careful in that we remove it on container termination to avoid breaking games.

@phobos665 phobos665 closed this Mar 10, 2026
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.

1 participant