Zurück zum Blog

Der Steam FPS Predictor kommt: Hardware Telemetry richtig architecten

Veröffentlicht am 6. April 2026
Der Steam FPS Predictor kommt: Hardware Telemetry richtig architecten

Jeder Indie-Entwickler kennt dieses flaue Gefühl im Magen, wenn eine Steam-Review eintrudelt – nicht weil der core gameplay loop versagt hat, sondern weil der Spieler versucht hat, eine 2026 rendering pipeline auf einer integrierten GPU von 2014 laufen zu lassen. Der Erstattungsantrag zitiert unweigerlich „poor optimization, unplayable.“

Die wahren Kosten schlechter Performance sind nicht nur der verlorene 19,99-Dollar-Verkauf. Es ist der algorithmische Schaden, der Ihrer Shopseite zugefügt wird. Steams visibility algorithm bestraft gnadenlos Spiele mit hohen Rückerstattungsraten und „Mixed“ oder „Mostly Negative“ Review-Aggregaten. Eine Welle von Spielern, die versuchen, Ihr Spiel auf nicht unterstützter Hardware auszuführen, kann Ihren Titel dauerhaft in der Discovery Queue begraben.

Bald wird Valve diese Dynamik komplett ändern. Jüngstes Datamining des Steam-Clients zeigt, dass sich ein Feature zur prädiktiven Performance derzeit in der Entwicklung befindet. Dieses Tool wird Spielern voraussichtlich sagen, wie viele Frames per second (FPS) sie in Ihrem Spiel erwarten können, bevor sie überhaupt auf den Kaufen-Button klicken.

Dies ist ein seismischer Shift für die PC-Spieledistribution. Es beseitigt die Zweideutigkeit von „Mindestsystemanforderungen“ und ersetzt sie durch kalte, harte Daten. Wenn Ihr Spiel schlecht optimiert ist oder auf den gängigsten Hardwarekonfigurationen schrecklich läuft, wird Steam diese Tatsache direkt auf Ihrer Shopseite verbreiten. Die Last des Hardware-Bewusstseins verlagert sich, und Entwickler, die nicht proaktiv Performance Telemetry sammeln und darauf reagieren, werden zusehen müssen, wie ihre Conversion-Raten einbrechen.

Dissecting the Steam FPS Predictor Leak

Die zugrunde liegenden Mechanismen dieses kommenden Features, wie sie von SteamDB und Lambda Generation aufgedeckt wurden, deuten auf eine massive Aggregation von Spielerdaten hin. Valve führt seit über zwei Jahrzehnten seine Hardware & Software Survey durch. Sie wissen genau, welche CPUs, GPUs und Speicherkonfigurationen weltweit aktiv genutzt werden.

Statische Hardware-Umfragen erzählen jedoch nur die halbe Geschichte. Das Predictor-Tool erfordert aktives Performance Profiling. Wenn ein Benutzer Ihr Spiel spielt, ist das Overlay von Steam bereits in der Lage, die Frameraten zu überwachen. Durch die Korrelation dieser Live-Telemetry mit dem spezifischen Hardware-Profil des Benutzers kann Valve eine prädiktive Matrix für jeden Titel auf der Plattform erstellen.

Der geleakte Code deutet auf ein manuelles Konfigurations-Interface hin, in das Benutzer verschiedene Hardware-Spezifikationen eingeben können, um die erwartete Performance zu berechnen. Noch wichtiger ist, dass es Benutzern ermöglicht, die Konfiguration ihrer Maschine zu „speichern“, um sofort die erwarteten Frameraten im gesamten Store zu sehen.

Für Entwickler bedeutet dies, dass die Black Box der Spieler-Performance aufgerissen wird. Sie können sich nicht mehr auf vorgerenderte Trailer oder hochoptimierte vertikale Slices verlassen, um Verkäufe anzukurbeln, wenn das eigentliche Executable auf einer RTX 3060 mit 24 FPS dahinruckelt. Der Algorithmus wird Sie enttarnen.

The Analytics Challenge: Why Performance Prediction is Hard

Die Vorhersage der Spiele-Performance ist bekanntermaßen schwierig, da Hardware nicht linear skaliert und Bottlenecks vollständig kontextabhängig sind. Eine GPU mag in einer geschlossenen Innenraumumgebung locker 120 FPS schaffen, aber in dem Moment, in dem der Spieler eine weitläufige Open World mit intensiver AI-Simulation betritt, wird die CPU zum Bottleneck für den render thread, und die Frameraten brechen ein.

Darüber hinaus spiegeln synthetische Benchmarks selten die Realität eines fragmentierten PC-Ökosystems wider, das von thermal throttling, veralteten Treibern und Hintergrundprozessen geplagt wird, die den System-RAM auffressen. Deshalb ist das Tracking einfacher „Average FPS“ eine gefährliche Falle. Ein Durchschnitt von 60 FPS klingt perfekt spielbar, aber wenn dieser Durchschnitt aus 120-FPS-Spitzen und häufigen Einbrüchen auf 15 FPS während Kämpfen besteht, ist die Player Experience grundlegend gestört.

Diese Mikro-Ruckler – oft als 1% und 0.1% lows bezeichnet – sind die wahren Killer des Spielgefühls. Wenn sich das Prognose-Tool von Steam auf aggregierte Durchschnittswerte verlässt, könnte es die Stabilität Ihres Spiels tatsächlich falsch darstellen. Dies macht es absolut kritisch für Sie als Entwickler, Ihre eigene Source of Truth zu haben.

Sie müssen Ihre eigene Hardware Telemetry sammeln, um diese Mikro-Ruckler zu identifizieren und zu beheben, bevor der Algorithmus von Steam Ihr Spiel als Titel mit schlechter Performance kennzeichnet. Sich bei der Performance-Analyse auf Berichte aus dem Community-Discord zu verlassen, ist ein Rezept für eine Katastrophe.

Architecting Your Own Hardware Telemetry Pipeline in Godot 4

Um dem Performance-Tracking auf Plattformebene einen Schritt voraus zu sein, müssen Sie automatisiertes Hardware Profiling direkt in Ihren Game Client einbetten. Man kann nicht optimieren, was man nicht misst.

Das Ziel ist es, passiv Performance-Metriken während des tatsächlichen Gameplays zu sammeln und diese Daten zusammen mit den Hardware-Spezifikationen des Spielers an Ihre Server zurückzusenden. Dies ermöglicht es Ihnen, Ihre eigene Matrix der erwarteten Performance aufzubauen und genau zu identifizieren, welche CPU/GPU-Kombinationen Schwierigkeiten haben.

Hier erfahren Sie, wie Sie einen umfassenden Hardware Profiler in Godot 4 erstellen können. Dieses Skript zeichnet die Frame-Zeiten über eine festgelegte Dauer auf und berechnet die entscheidenden 1% lows, die das wahrgenommene Ruckeln definieren.

# Godot 4.x - Comprehensive Hardware Telemetry Profiler
extends Node

var _frame_times: PackedFloat64Array = []
var _is_profiling: bool = false
var _profile_timer: float = 0.0
const PROFILE_DURATION: float = 120.0 # Profile a 2-minute slice of gameplay

func start_profiling() -> void:
    _frame_times.clear()
    _is_profiling = true
    _profile_timer = 0.0

func _process(delta: float) -> void:
    if not _is_profiling:
        return
        
    # Record delta time in milliseconds
    _frame_times.append(delta * 1000.0)
    _profile_timer += delta
    
    if _profile_timer >= PROFILE_DURATION:
        _finish_profiling()

func _finish_profiling() -> void:
    _is_profiling = false
    
    if _frame_times.is_empty():
        return
        
    # Sort the array to calculate percentiles (1% lows)
    _frame_times.sort()
    
    var total_time: float = 0.0
    for time in _frame_times:
        total_time += time
        
    var avg_time: float = total_time / _frame_times.size()
    
    # Calculate the 99th percentile of frame times (the longest frames)
    # This represents the 1% lows
    var one_percent_idx: int = int(_frame_times.size() * 0.99)
    one_percent_idx = clampi(one_percent_idx, 0, _frame_times.size() - 1)
    var one_percent_time: float = _frame_times[one_percent_idx]
    
    # Convert timings back to FPS for the final payload
    var telemetry_payload = {
        "event_type": "performance_profile",
        "client_version": ProjectSettings.get_setting("application/config/version"),
        "hardware": _get_hardware_specs(),
        "performance": {
            "avg_fps": 1000.0 / avg_time,
            "one_percent_low_fps": 1000.0 / one_percent_time,
            "total_frames_analyzed": _frame_times.size()
        }
    }
    
    _transmit_telemetry(telemetry_payload)

func _get_hardware_specs() -> Dictionary:
    return {
        "os": OS.get_name(),
        "cpu": OS.get_processor_name(),
        "gpu": RenderingServer.get_video_adapter_name(),
        "ram_mb": OS.get_memory_info().get("physical", 0) / (1024 * 1024)
    }

func _transmit_telemetry(payload: Dictionary) -> void:
    # Serialize and transmit to your analytics backend
    var json_string = JSON.stringify(payload)
    print("Telemetry Ready: ", json_string)
    # HTTP Request implementation omitted

Dieses Godot-Skript erreicht zwei kritische Dinge. Erstens vermeidet es das Blockieren des Main Threads während der Datenerfassung vollständig. Zweitens sortiert es das Array lokal, um die Perzentile vor der Übertragung zu extrahieren, anstatt ein massives Array von rohen Floats über das Netzwerk zu senden.

Building a Thread-Safe Profiler in Unreal Engine C++

Für Entwickler, die Unreal Engine verwenden, bleiben die Prinzipien gleich, aber die Implementierung erfordert ein sorgfältiges Speichermanagement, um genau die Ruckler zu vermeiden, die man zu messen versucht. Die Verwendung eines GameInstanceSubsystem stellt sicher, dass Ihr Profiler über Level-Loads hinweg bestehen bleibt.

Es ist entscheidend, im Voraus Speicher für Ihr Array zu reservieren. Das tausendfache Realloziieren eines Arrays pro Sekunde während des Gameplays wird Ihre CPU-Frametime zerstören.

// Unreal Engine C++ - Hardware Telemetry Subsystem
// PerformanceTrackerSubsystem.h
#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "PerformanceTrackerSubsystem.generated.h"

UCLASS()
class YOURGAME_API UPerformanceTrackerSubsystem : public UGameInstanceSubsystem, public FTickableGameObject
{
    GENERATED_BODY()

public:
    virtual void Initialize(FSubsystemCollectionBase& Collection) override;
    virtual void Deinitialize() override;
    
    // FTickableGameObject interface
    virtual void Tick(float DeltaTime) override;
    virtual TStatId GetStatId() const override;
    virtual bool IsTickable() const override { return bIsTracking; }

    UFUNCTION(BlueprintCallable, Category = "Analytics")
    void StartPerformanceTracking(float DurationInSeconds);

private:
    void ConcludeTrackingSession();
    FString GetHardwareProfileJSON() const;
    void TransmitPayload(const FString& Payload);

    bool bIsTracking = false;
    float TrackingDuration = 0.0f;
    float TimeElapsed = 0.0f;
    
    TArray<float> FrameTimeHistory;
};
// PerformanceTrackerSubsystem.cpp
#include "PerformanceTrackerSubsystem.h"
#include "GenericPlatform/GenericPlatformDriver.h"
#include "GenericPlatform/GenericPlatformMemory.h"
#include "Kismet/GameplayStatics.h"
#include "HttpModule.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"

void UPerformanceTrackerSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
    Super::Initialize(Collection);
    FrameTimeHistory.Reserve(10000); // Prevent array reallocation during tracking
}

void UPerformanceTrackerSubsystem::Deinitialize()
{
    Super::Deinitialize();
}

void UPerformanceTrackerSubsystem::StartPerformanceTracking(float DurationInSeconds)
{
    FrameTimeHistory.Reset();
    TrackingDuration = DurationInSeconds;
    TimeElapsed = 0.0f;
    bIsTracking = true;
}

void UPerformanceTrackerSubsystem::Tick(float DeltaTime)
{
    if (!bIsTracking) return;

    // Store frame time in milliseconds
    FrameTimeHistory.Add(DeltaTime * 1000.0f);
    TimeElapsed += DeltaTime;

    if (TimeElapsed >= TrackingDuration)
    {        ConcludeTrackingSession();
    }
}

void UPerformanceTrackerSubsystem::ConcludeTrackingSession()
{
    bIsTracking = false;

    if (FrameTimeHistory.Num() == 0) return;

    // Sort to calculate 1% and 0.1% lows
    FrameTimeHistory.Sort();

    double TotalTime = 0.0;
    for (float FrameTime : FrameTimeHistory)
    {        TotalTime += FrameTime;
    }

    float AverageFrameTime = TotalTime / FrameTimeHistory.Num();
    
    // Calculate Percentiles
    int32 OnePercentIndex = FMath::Clamp(FMath::FloorToInt(FrameTimeHistory.Num() * 0.99f), 0, FrameTimeHistory.Num() - 1);
    int32 PointOnePercentIndex = FMath::Clamp(FMath::FloorToInt(FrameTimeHistory.Num() * 0.999f), 0, FrameTimeHistory.Num() - 1);

    float OnePercentLow = FrameTimeHistory[OnePercentIndex];
    float PointOnePercentLow = FrameTimeHistory[PointOnePercentIndex];

    // Construct JSON Payload
    FString Payload = FString::Printf(TEXT(
        "{\"average_fps\": %.2f, \"1_percent_low_fps\": %.2f, \"0_1_percent_low_fps\": %.2f, \"hardware\": %s}"),
        1000.0f / AverageFrameTime,
        1000.0f / OnePercentLow,
        1000.0f / PointOnePercentLow,
        *GetHardwareProfileJSON()
    );

    TransmitPayload(Payload);
}

FString UPerformanceTrackerSubsystem::GetHardwareProfileJSON() const
{
    FString OSVersion = FPlatformMisc::GetOSVersion();
    FString CPUBrand = FPlatformMisc::GetCPUBrand();
    FString GPUBrand = FPlatformMisc::GetPrimaryGPUBrand();
    
    const FPlatformMemoryConstants& MemoryConstants = FPlatformMemory::GetConstants();
    uint32 TotalPhysicalRAM_GB = MemoryConstants.TotalPhysical / (1024 * 1024 * 1024);

    return FString::Printf(TEXT("{\"os\": \"%s\", \"cpu\": \"%s\", \"gpu\": \"%s\", \"ram_gb\": %d}"),
        *OSVersion, *CPUBrand, *GPUBrand, TotalPhysicalRAM_GB);
}

void UPerformanceTrackerSubsystem::TransmitPayload(const FString& Payload)
{
    // Ensure async HTTP transmission to avoid hitches
    FHttpModule* Http = &FHttpModule::Get();
    TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = Http->CreateRequest();
    
    Request->SetURL("https://api.yourbackend.com/v1/telemetry/performance");
    Request->SetVerb("POST");
    Request->SetHeader("Content-Type", "application/json");
    Request->SetContentAsString(Payload);
    
    Request->ProcessRequest();
}

TStatId UPerformanceTrackerSubsystem::GetStatId() const
{
    RETURN_QUICK_DECLARE_CYCLE_STAT(UPerformanceTrackerSubsystem, STATGROUP_Tickables);
}

Deep Dive: Structuring Telemetry for Scale

Das Schreiben des client-seitigen Codes ist nur der erste Schritt. Die wahre technische Herausforderung liegt im sicheren Ingesten und Abfragen dieser Daten.

Wenn Ihr Spiel kommerziellen Erfolg hat, werden Zehntausende von Clients gleichzeitig versuchen, diese JSON-Payloads zu senden. Wenn Ihre Clients alle paar Minuten Daten senden, wird eine Standard-REST API mit einer einzelnen relationalen Datenbank unter den Verbindungslimits und Write-Locks einknicken.

Bei der Architektur des Ingestion-Endpunkts müssen Sie eine Time-Series Database verwenden, die für hohen Write-Durchsatz optimiert ist, gekoppelt mit einer In-Memory-Queue (wie Redis), um die eingehenden HTTP-Anfragen zu puffern. Wenn Sie hochfrequente Performance-Daten sammeln, kann das Verlassen auf Standard-HTTP-Polling Ihr Backend überfordern. Der Wechsel zu persistenten Verbindungen kann den Overhead drastisch reduzieren, eine Strategie, die wir in unserem Unreal Engine WebSockets tutorial for real-time backends beschrieben haben.

The Backend Ingestion Bottleneck

Der Aufbau der Infrastruktur zum Ingesten, Validieren und Speichern von Millionen dieser Telemetry-Payloads erfordert erhebliche Engineering-Bandbreite. Sie müssen Load Balancer einrichten, Datenbank-Sharding für Ihre Time-Series-Daten konfigurieren und die kontinuierliche Erneuerung von SSL-Zertifikaten verwalten.

Für ein kleines Indie-Team sind das locker 4-6 Wochen dedizierte Backend-Arbeit – Zeit, die besser in die Optimierung des eigentlichen Spiels investiert werden sollte. Das Handling von Millionen von Analytics-Events erfordert eine ernsthafte Infrastrukturplanung, die wir kürzlich in unserem Breakdown des größten Indie-Game Backend-Updates von horizOn besprochen haben.

Mit horizOn sind diese Backend-Services bereits vorkonfiguriert. Sie können Ihre Telemetry direkt in eine skalierbare, sichere Ingestion Pipeline leiten, die Ihre JSON-Payloads automatisch parst und sofort abfragbar macht.

Best Practices for Hardware Profiling & Performance Tuning

Wenn Steam Ihre erwarteten FPS öffentlich verbreiten wird, brauchen Sie eine proaktive Strategie, um sicherzustellen, dass diese Zahlen ein poliertes Produkt widerspiegeln. Befolgen Sie diese Architektur-Richtlinien:

  1. Implementieren Sie ein automatisiertes Hardware Auto-Detect beim ersten Start.
  2. Tracken Sie 1% und 0.1% lows, nicht nur Durchschnitte.
  3. Pre-allozieren Sie Ihren Profiling-Speicher.
  4. Segmentieren Sie Telemetry nach Graphics Presets.
  5. Entkoppeln Sie Telemetry vom Main Game Loop.

The Era of Radical Transparency

Valves Schritt, prädiktive FPS-Daten offenzulegen, ist ein zweischneidiges Schwert. Für Entwickler, die Optimierung priorisieren, dient es als mächtiges Marketing-Tool. Eine hohe erwartete Framerate wirkt wie ein Qualitätssiegel.

Der einzige Weg, diesen Wandel zu überstehen, besteht darin, Performance Telemetry als Kernfeature zu behandeln, nicht als Nebensache. Sie müssen genau wissen, wie Ihr Spiel „in der Wildnis“ läuft, bevor die Storefront es der Welt offenbart.

Fangen Sie jetzt an, Ihre Telemetry Pipelines aufzubauen. Analysieren Sie Ihre Frame-Zeiten und stellen Sie sicher, dass der Algorithmus genau das bestätigt, was Sie versprochen haben: eine flüssige, stabile Erfahrung. Bereit, Ihr Analytics Backend ohne DevOps-Kopfschmerzen zu skalieren? Probieren Sie horizOn kostenlos aus und tracken Sie noch heute Ihre 1% lows.


Quelle: Steam could soon start telling you how many FPS you can expect in games before buying them