Proton, the Steam runtime, and compatibility policy

Distinguish Proton components, runtime dependencies and legitimate compatibility limits.

13 min readChapter 167 of 168Intermediate → advanced
In this chapter

Before you start#

Review Wine prefixes, Wine waits, Windows APIs and Unix calls, and DXVK and vkd3d-proton. This chapter pins Proton tag proton-9.0-4 to commit b7cb2fc548d20bdb3bfe603d30f6b6f191d509bd. It does not inspect or launch Steam, games or existing compatibility data.

Learning goals#

Follow the launcher's prefix and environment setup, distinguish a runtime container from a VM, identify pinned component boundaries, and construct a falsifiable per-application compatibility investigation without changing a user's installation.

Source walkthrough#

A per-application launch boundary
Application
Requests Windows-facing interfaces.
Proton components
Versioned Wine fork and graphics translators.
Runtime environment
Userspace libraries and environment setup.
Host kernel and drivers
Enforce host authority and device support.
Conceptual model. Conceptual environment and ownership; no Steam session is observed.

Proton is Valve's compatibility distribution built around a Wine fork and separately versioned components and integration. Its Wine changes, graphics translators, launcher, runtime libraries and application-specific behavior are not simply upstream Wine 10.0 with universal settings. A particular build is identified by its own revision and component revisions. It is not a guarantee that every Windows program, hardware configuration or protected game works. This historical source baseline is not a current-release claim and has not been tested with the earlier Wine or graphics baselines in this book.

The following exact excerpt is from Proton's proton launcher, CompatData.setup_prefix, at proton-9.0-4. The top-level LICENSE.proton grants the BSD three-clause license. Surrounding migration and configuration code is omitted:

python
        with self.prefix_lock:
            if file_exists(self.version_file, follow_symlinks=True):
                with open(self.version_file, "r") as f:
                    old_ver = f.readline().strip()
            else:
                old_ver = None

The lock serializes this setup region for the compatibility data object. The launcher tests for a version file, reads its first line when present, and otherwise records no old version. The following code calls upgrade_pfx(old_ver), creates a missing prefix directory and can copy initial registry data. A version marker is input to migration logic; its absence is not proof that a preexisting directory is safe to overwrite or that a migration cannot fail. Locks, filesystem operations and exception paths all matter. The sample here is source reading only and does not run this mutating method.

A second exact excerpt from the same pinned launcher, Session.run_proc, has the same BSD three-clause license:

python
        if local_env is None:
            local_env = self.env
        return subprocess.call(args, env=local_env, stderr=self.log_file, stdout=self.log_file)

If a caller does not provide an environment, the launcher uses its session environment. It starts the selected subprocess and directs its standard output and error to the configured log file. The returned code is a subprocess result, not proof of a particular graphical or audio outcome. The surrounding run method selects an optional runtime launcher interface if found and otherwise uses an empty prefix list; it can start and later stop a remote debugger. If process creation fails, Python can raise an exception rather than produce a successful application result. A launcher log may contain sensitive paths or arguments and should be handled as private data.

Component versions and launch boundaries#

The pinned Proton tree records submodule commits, including Wine 117daf8d508860a8552f59ebdd6ee16361431136, DXVK d96fa54080320e9b63227e76124ffa50a927ced2 and vkd3d-proton 4fd7d3ab3d351de4ad50576b1a9df0a45a4a7c74. The pinned .gitmodules records the corresponding repository locations. These are the Proton tree's gitlinks, not the separately selected upstream Wine 10.0, DXVK 2.5.3 or vkd3d-proton 2.14 commits used in earlier lessons. The tree listing establishes source relationships; it does not prove which binary artifacts were installed in a particular user's runtime.

An application's launch has several independently failing steps. A launcher constructs environment variables and paths; compatibility data identifies a prefix and versioned state; the selected Wine executable loads Windows-facing modules; those modules interact with a host runtime and device drivers. A DLL search path or override can select a different implementation than expected. The path to a file is not evidence that it exists or has the right architecture. A versioned source checkout is not the same as the binary that was built from it, and a binary's dependencies are not identified solely by the launcher's version string.

In the inspected init_wine, the session adjusts locale handling, removes WINEARCH from its environment, preserves an original library path where applicable, prepends Proton library paths and sets WINEDLLPATH. It also configures GStreamer-related paths. These operations affect how later processes find libraries and plugins. They do not make arbitrary libraries ABI compatible or grant access to host devices. A missing environment variable, unavailable executable, permission error, incompatible shared library or failed prefix migration can stop progress before a Windows program starts. A diagnostic needs the failing stage and returned result rather than a conclusion based only on a blank window.

The program checks for STEAM_COMPAT_DATA_PATH at its entry point and exits with an error when it is absent. A different failure after that check can occur while opening version files, creating directories, migrating state or starting a subprocess. The code uses a prefix lock around setup, but locking does not make all filesystem operations infallible. An exception while copying or migrating can leave a partially prepared directory; recovery must be guided by the actual error and authorized backup strategy. Do not test those paths against a real Steam installation or personal prefix.

In the __main__ block of Proton 9.0-4 proton, BSD three-clause license per LICENSE.proton (the lines after the check are omitted):

python
    if "STEAM_COMPAT_DATA_PATH" not in os.environ:
        log("No compat data path?")
        sys.exit(1)

The check runs before Proton(...) and CompatData are constructed, so a missing variable stops the launcher there. It tests only that the variable exists; it does not validate the path or check that it is writable, and later setup can still fail.

Runtime containers and the host#

A runtime such as the Steam Linux Runtime and pressure-vessel can provide a controlled userspace library environment for a launched program. A container shares the host kernel; it is not a virtual machine with an independent guest kernel. Its filesystem view, environment and library versions can differ from the host's ordinary process environment, while host kernel interfaces and device drivers still constrain execution. A Flatpak deployment adds its own sandbox and portal boundaries; neither name alone proves a particular launch path or grants access to a GPU, audio server or user file.

The launcher checks whether steam-runtime-launcher-interface-0 is available and conditionally prepends its invocation in the run path. Finding that executable does not establish that a particular container image, filesystem mount, ABI or driver configuration is correct. A missing runtime library can prevent a process from starting even if the application files are intact. Conversely, a valid runtime cannot make an unsupported GPU extension available. Pressure-vessel and runtime versions are separate projects and are not pinned or asserted to have run in this lesson.

Where a launch can fail
Launch environment
The caller supplies STEAM_COMPAT_DATA_PATH and the options the launcher reads.
Proton launcher
Session.init_session prepares the environment; CompatData.setup_prefix handles version state.
Optional runtime container
steam-runtime-launcher-interface-0 is prepended only when found; it shares the host kernel.
Wine and translation DLLs
Windows-facing modules come from the selected build, WINEDLLPATH and any overrides.
Host kernel and devices
GPU, audio and file access still follow the host kernel, drivers and policy.
Conceptual model. Conceptual launch stack for the pinned Proton 9.0-4 launcher; the layers show where failures can start, not a captured process tree.

A prefix usually stores Windows-facing registry and file state for an application. The pinned code's setup path uses a per-compatibility-data location, version marker, lock and migration logic; it also creates DOS drive links under specified conditions. A drive mapping can expose host paths. The prefix is not a security sandbox and a container's actual permissions must be evaluated separately. A migration between versions can alter persistent state; before any authorized real experiment, a user would need to understand backup, ownership and recovery. This lesson uses no actual prefix and does not propose copying a personal one.

Synchronization and downstream differences#

Wine's ordinary server waits were studied earlier. Proton's pinned launcher also has options and environment handling for esync and fsync. These are downstream integration choices, not evidence that all upstream Wine 10.0 builds use those backends. The presence of an environment setting in a launcher does not prove that the corresponding implementation initialized successfully or that a specific wait used it. The actual Wine fork revision, kernel interfaces, configuration and fallback behavior must be inspected for the particular build.

Linux's futex_waitv is a kernel interface that can support waiting on multiple futex words under its own rules; it is not itself a complete Windows synchronization object model. A backend must still preserve event, semaphore, mutex, alertable-wait and lifetime semantics. Kernel version, availability, policy and downstream implementation determine whether a specific path can be used. The Linux v6.12 pedagogical baseline cannot be assumed to include a later NTSYNC driver. No kernel support claim follows merely from a Proton option name.

A wait investigation should record the exact Proton and Wine fork builds, application call, selected backend evidence, timeout and object states. If the application wakes early, a competing consumer, APC, timeout or incorrect predicate can be a different hypothesis from a backend bug. A synthetic model cannot establish which one occurred in a real process. Disabling synchronization features, security controls or host settings without an authorized isolated investigation is not part of this chapter.

Graphics, caching, and a worked failure#

DXVK and vkd3d-proton translate different Direct3D families to Vulkan, as discussed in the previous chapter. A successful launcher setup does not prove that the installed Vulkan driver supports the required extension, queue, format or shader capability. Shader and pipeline caches can reduce repeated compilation work; they have project, driver and device version dependencies. Fossilize is a separate project used in some graphics cache workflows, with its own versions and artifacts. This chapter does not pin or run it and does not claim that a particular cache was generated or reused.

Consider a synthetic per-application case. The launcher receives a compatibility-data path, finds a version marker and completes its source-modeled setup. The child process starts and requests a D3D11 device. Its translation layer then fails to create a suitable Vulkan device because a hypothetical required feature is unavailable. The evidence supports a failure after launch and before a usable graphics device; it does not establish prefix corruption. A competing hypothesis is that the translation DLL failed to load before any Vulkan query. These hypotheses predict different observations: module-load status and a device-feature result respectively. The example is not a measured game log or a claim about a specific GPU.

Four explanations, four different evidence trails

Synthetic teaching data, not a device capture.

DLL load failure
Predicts a module-load error or loader diagnostic before any Vulkan device query.
Unsupported Vulkan feature
Predicts a loaded translation DLL followed by a failed device-feature request.
Launcher setup error
Predicts failure before the child starts; the synthetic case did reach the child.
Prefix damage
Predicts file or registry anomalies; the synthetic case gives no evidence for it.
Synthetic teaching data. Synthetic hypotheses for the hypothetical device-creation failure above; each predicts different evidence, and none is a measured result.

A source-only investigation records the pinned launcher and component revisions, the stage reached, the requested interface, the reported error and the next discriminating observation. A safe isolated runtime test, if later authorized and separately prepared, would use a self-authored program and fresh disposable data, record all versions, and compare a known supported request with the failing one. No such runtime, GPU test, game, Steam client or compatibility prefix was used here. Hardware drivers, proprietary integrations and vendor policy can impose additional limits; compatibility and anti-cheat support cannot be generalized from source presence.

Cache invalidation is a separate hypothesis from feature absence. If an old pipeline cache cannot be used after a driver change, an implementation might rebuild it or report an error depending on its own path. A cache directory's existence does not establish that the correct cache was read, that shader compilation succeeded or that a rendered frame was correct. Raw shader data and logs can reveal application details, so an evidence collection plan should use only owned, authorized artifacts and redact sensitive information before sharing. There is no need to inspect a user's library or cache for the source exercise.

Reading configuration as a conditional, not an outcome#

In the pinned launcher's session initialization, the PROTON_NO_ESYNC and PROTON_NO_FSYNC environment inputs are checked against compatibility configuration. The subsequent branches remove WINEESYNC or WINEFSYNC when the corresponding disable option is present, and otherwise set the respective variable to 1. This is a concrete statement about preparing the child environment in this launcher. It does not show a successful kernel call, a selected runtime backend or a completed wait. A report that captures only the environment can establish what was requested, not which execution path actually handled an object.

In the same proton launcher, Session.init_session, BSD three-clause license per LICENSE.proton (lines omitted, marked with a comment: an unrelated option between the two blocks):

python
        if "noesync" in self.compat_config:
            self.env.pop("WINEESYNC", "")
        else:
            self.env["WINEESYNC"] = "1"
        # ...
        if "nofsync" in self.compat_config:
            self.env.pop("WINEFSYNC", "")
        else:
            self.env["WINEFSYNC"] = "1"

Each sync option has the same shape: the disable flag removes the Wine variable, and otherwise the variable is set to 1. The excerpt shows what the launcher requests for the child environment, not which synchronization path a Wine process actually took.

The pinned tree's Wine fork synchronization source, DXVK device source, and vkd3d-proton command source are separately pinned entry points. Their existence verifies the gitlink targets can be read; this lesson does not assert a complete review of those implementations or a tested binary build. A change to any one component can alter behavior while the application and prefix remain unchanged, which is why an investigation records every relevant version rather than only a product name.

Practice#

This private arithmetic/state model illustrates a version-marker branch without reading a real compatibility directory:

sh
python3 - <<'PY'
for marker in (None, 'old-version'):
    action = 'initialize candidate' if marker is None else 'consider migration'
    print('synthetic marker:', marker, 'next investigation:', action)
PY

The two rows distinguish absence from a candidate migration. They do not call Proton, create a prefix or implement its migration logic. The counterexample is a missing marker in a directory that nevertheless contains important files: absence alone does not authorize deleting or replacing them. If Python is unavailable, make the same two rows on paper.

For the source-only alternative, use the supplied device-creation failure case. Construct a table for launcher setup, child start, DLL selection and Vulkan device request. Mark each as assumed, observed or unknown in the synthetic case. Explain how an unavailable translation DLL would predict different evidence from an unsupported Vulkan feature and which component version would be needed for either claim. No Steam account, prefix, game or runtime is needed.

Knowledge check#

  1. Why do the Proton tree's Wine and graphics gitlinks not identify the earlier Wine 10.0 and DXVK baselines?
  2. What does the prefix lock protect in the quoted source, and what does it not guarantee?
  3. Why is a runtime container different from a VM?
  4. Why does a launcher option for fsync not prove that an upstream Wine 10.0 wait used it?
  5. What evidence distinguishes a failed DLL load from a later unsupported Vulkan feature in the synthetic case?

Answers#

  1. The Proton release records its own commits for its fork and submodules. Those hashes differ from the independently chosen pedagogical tags and do not demonstrate binary compatibility between them.
  2. It serializes the shown setup region. It does not ensure filesystem operations succeed, make a migration reversible or prove that the directory has no valuable data.
  3. A container provides a userspace and namespace arrangement on the host kernel, while a VM supplies a separate guest-kernel environment. Actual isolation depends on the deployment and configuration.
  4. The option is downstream launcher configuration. The actual fork, kernel interface, enabled implementation and runtime path need independent evidence; upstream Wine's inspected server-wait code is a separate baseline.
  5. Module-load outcome and loader diagnostics identify the first boundary; a device feature query and its result identify a later graphics boundary. A blank window alone distinguishes neither.

Checkpoint#

Submit the version and component table, prefix setup state, and two competing failure hypotheses with distinguishing evidence. Pass when gitlinks are not confused with previously pinned versions and a shared-kernel runtime is not called a VM. Revisit the launcher and retry if a version marker is treated as a complete backup. This is not certification.

Sources#

A little further beneath the surface.

Check your understanding with the checkpoint. Marking a chapter as read saves a bookmark, not a proficiency assessment.