Jak může vypadat Open Rails CZ/SK?

Než začnu zveřejňovat jednotlivé technické úpravy Open Rails CZ/SK, chci hned na začátku vyjasnit jednu věc týkající se zdrojového kódu a jeho licence.

Pokud by chtěl někdo namítat, že nemám právo zdrojový kód OR CZ/SK studovat nebo upravovat bez souhlasu jeho autora, není tomu tak.

Nekontroloval jsem pouze licenci současného Open Rails někde na internetu. Kontrolována byla licence přímo přiložená ke konkrétní záloze zdrojového kódu OR CZ/SK, se kterou pracuji.

V kořeni zdrojových kódů je přiložena GNU General Public License Version 3 (GPLv3). Stejná licence je součástí projektu také v Source\RunActivity\Copying.txt a jednotlivé zdrojové soubory obsahují licenční hlavičky, podle kterých je možné Open Rails redistribuovat a/nebo upravovat za podmínek GNU GPL.

GPL přitom výslovně umožňuje zdrojový kód studovat, upravovat a vytvářet vlastní modifikované verze. Pro vytvoření vlastní soukromé upravené verze tedy není potřeba dodatečný souhlas autora jednotlivých úprav.

To je ostatně jeden ze základních principů licence, pod kterou je distribuován samotný Open Rails.

Důležitá je také druhá strana této licence.

Pokud je program založený na tomto GPL kódu distribuován uživatelům v binární podobě (.exe a další soubory), GPL stanovuje podmínky, za kterých musí mít příjemci možnost získat také Corresponding Source – odpovídající zdrojový kód distribuované verze.

Neznamená to nutně, že zdrojové kódy musí za všech okolností ležet veřejně na GitHubu pro celý internet. GPLv3 v oddílu 6 připouští několik způsobů distribuce nebo nabídnutí zdrojového kódu. Distributor však nemůže vzít GPL program, veřejně poskytovat jeho upravenou binární verzi a současně příjemcům této verze odejmout práva, která jim GPL poskytuje.

To zahrnuje právo odpovídající zdrojový kód za podmínek GPL získat a následně jej studovat, upravovat a dále redistribuovat.

Stejnými pravidly se samozřejmě budu řídit i já.

Dokud si své úpravy sestavuji pouze pro vlastní potřebu, nemusím svou pracovní verzi zveřejňovat. Pokud později zveřejním vlastní upravenou binární verzi OR CZ/SK, zveřejním nebo odpovídajícím způsobem zpřístupním také její zdrojový kód, zachovám původní licenční a copyrightová oznámení a své zásahy jasně označím.

Smyslem tohoto vlákna navíc není někomu „ukrást“ práci nebo bezhlavě přepisovat celý Open Rails. Chci jednotlivé části kódu technicky studovat, hledat konkrétní problémy a postupně je řešit.

Proto zde budu u jednotlivých úprav zveřejňovat také technické informace – co bylo v původním kódu, co jsem změnil a proč jsem to změnil.

Ať je tedy od začátku jasné, že studium a úpravy tohoto zdrojového kódu nejsou obcházením autora. Jsou využitím práv, která licence GPL uživatelům a dalším vývojářům výslovně poskytuje.

Pozn.: Jde o technické shrnutí podmínek licence přiložené přímo ke konkrétní záloze OR CZ/SK, nikoliv o individuální právní stanovisko.
 
1. úprava – MSAA podle skutečných možností GPU

První změna je hotová, sestavená a otestovaná.

CO BYL PROBLÉM

Původní menu Open Rails CZ/SK nabízelo pevně hodnoty až do 32× MSAA, protože TrackBar měl v Designeru napevno:

Kód:
Maximum = 6;

Hodnoty 1–6 jsou přímo mapovány na AntiAliasingMethod:

Kód:
1 = vypnuto
2 = 2× MSAA
3 = 4× MSAA
4 = 8× MSAA
5 = 16× MSAA
6 = 32× MSAA

Takové nastavení ale samo o sobě vůbec neověřovalo, zda konkrétní grafický adaptér danou úroveň MSAA skutečně podporuje.

RunActivity potom pouze převádělo vybranou hodnotu na:

Kód:
PresentationParameters.MultiSampleCount

Takže pokud menu nabídlo například 32×, nebylo to výsledkem detekce možností GPU.


NOVÉ ŘEŠENÍ

Před nastavením rozsahu TrackBaru se nyní vytvoří skutečné hardwarové Direct3D 11 zařízení přes SharpDX.

Pro úrovně:

Kód:
2×
4×
8×
16×
32×

se následně ověřuje skutečná podpora pomocí:

Kód:
ID3D11Device::CheckMultisampleQualityLevels

Kontrolují se oba formáty používané RunActivity:

Kód:
Back buffer:
R8G8B8A8_UNORM
(MonoGame SurfaceFormat.Color)

Depth/stencil:
D24_UNORM_S8_UINT
(MonoGame Depth24Stencil8)

Do menu se potom zpřístupní pouze nejvyšší souvislá řada skutečně podporovaných hodnot.

Pokud některá úroveň podporována není, vyšší hodnoty se už nenabídnou.

Pokud vytvoření D3D11 zařízení nebo samotný dotaz selže, zůstane dostupné pouze vypnutí MSAA. Program tedy raději nenabídne hodnotu, jejíž podporu nedokázal ověřit.

Na testovaném GPU nyní nastavení správně končí na:

8× MSAA

namísto původních 32×.


ZMĚNĚNÝ SOUBOR 1

Kód:
Source\Menu\Options.cs

1. Přidání namespace pro přímý D3D11 dotaz

PŮVODNÍ KÓD:


Kód:
using ORTS.Settings;
using ORTS.Updater;
using System;

NOVÝ KÓD:

Kód:
using ORTS.Settings;
using ORTS.Updater;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System;

PROČ:

Menu potřebuje vytvořit skutečné Direct3D 11 hardware zařízení a přímo se D3D11 zeptat na podporu multisamplingu.

Nejde tedy o odhad podle předem daných hodnot v menu.


2. Pevné nastavení TrackBaru nahrazeno detekcí maxima

PŮVODNÍ KÓD:


Kód:
trackAntiAliasing.Value = Settings.AntiAliasing == 0 ? 1 : Settings.AntiAliasing;
trackAntiAliasing_ValueChanged(null, null);

NOVÝ KÓD:

Kód:
// Detect the real Direct3D 11 MSAA capability before assigning the saved value.
// The TrackBar values map directly to UserSettings.AntiAliasingMethod (1=None, 2=2x, ...).
trackAntiAliasing.Maximum = GetMaximumSupportedAntiAliasingSetting();

var antiAliasingSetting =
    Settings.AntiAliasing == 0
        ? (int)UserSettings.AntiAliasingMethod.None
        : Settings.AntiAliasing;

trackAntiAliasing.Value =
    Math.Min(
        trackAntiAliasing.Maximum,
        Math.Max(trackAntiAliasing.Minimum, antiAliasingSetting));

trackAntiAliasing_ValueChanged(null, null);

PROČ:

Maximum už neurčuje pevná hodnota 6 z Designeru.

Při otevření nastavení se nejprve zjistí skutečně podporované maximum.

Pokud měl uživatel například ve staré konfiguraci uložené 32× MSAA, ale současný grafický adaptér dovolí maximálně 8×, uložená hodnota se bezpečně omezí na 8×.

TrackBar tak zároveň nikdy nedostane hodnotu mimo svůj aktuálně povolený rozsah.


3. Nová metoda pro detekci podporovaného MSAA

PŮVODNÍ KÓD:


Metoda neexistovala.

NOVÝ KÓD:

Kód:
private static int GetMaximumSupportedAntiAliasingSetting()
{
    var maximumSetting =
        (int)UserSettings.AntiAliasingMethod.None;

    try
    {
        using (var device =
            new SharpDX.Direct3D11.Device(
                DriverType.Hardware,
                DeviceCreationFlags.None))
        {
            var sampleCounts = new[] { 2, 4, 8, 16, 32 };

            foreach (var sampleCount in sampleCounts)
            {
                var colorQualityLevels =
                    device.CheckMultisampleQualityLevels(
                        Format.R8G8B8A8_UNorm,
                        sampleCount);

                var depthQualityLevels =
                    device.CheckMultisampleQualityLevels(
                        Format.D24_UNorm_S8_UInt,
                        sampleCount);

                if (colorQualityLevels <= 0 ||
                    depthQualityLevels <= 0)
                    break;

                maximumSetting++;
            }
        }
    }
    catch (SharpDX.SharpDXException)
    {
    }

    return maximumSetting;
}

PROČ:

CheckMultisampleQualityLevels vrací počet dostupných quality levels pro konkrétní DXGI formát a konkrétní počet vzorků.

Pokud vrátí 0, daný počet vzorků pro daný formát podporovaný není.


ZMĚNĚNÝ SOUBOR 2

Kód:
Source\Menu\Menu.csproj

Projekt Menu měl z grafických knihoven přímo referencovaný pouze MonoGame framework.

Nově byly přidány reference:

Kód:
<Reference Include="SharpDX">
  <HintPath>..\3rdPartyLibs\Monogame\SharpDX.dll</HintPath>
</Reference>

<Reference Include="SharpDX.Direct3D11">
  <HintPath>..\3rdPartyLibs\Monogame\SharpDX.Direct3D11.dll</HintPath>
</Reference>

<Reference Include="SharpDX.DXGI">
  <HintPath>..\3rdPartyLibs\Monogame\SharpDX.DXGI.dll</HintPath>
</Reference>

PROČ:

Tyto DLL jsou už součástí zdrojového stromu OR CZ/SK v:

Kód:
Source\3rdPartyLibs\Monogame

Nepřidává se tedy žádný nový externí balíček.

Projekt Menu pouze začne přímo používat D3D11/SharpDX knihovny, které už tato verze Open Rails obsahuje.


PROVĚŘENÉ ZÁVISLOSTI

Při této změně nebyl upraven pouze jeden náhodný řádek. Byly dohledány také související části kódu:


Source\Menu\Options.cs

Čte a ukládá:

Kód:
Settings.AntiAliasing

a zobrazuje text aktuálně zvolené úrovně.


Source\Menu\Options.Designer.cs

Obsahuje původní:

Kód:
trackAntiAliasing.Maximum = 6;

Soubor nebylo nutné měnit, protože runtime hodnota Maximum se po InitializeComponent() přepíše podle výsledku D3D11 detekce.


Source\ORTS.Settings\UserSettings.cs

Enum AntiAliasingMethod mapuje hodnoty 1–6 na:

Kód:
vypnuto
2×
4×
8×
16×
32×

Enum zůstává beze změny, mimo jiné kvůli kompatibilitě se staršími konfiguracemi.


Source\RunActivity\Viewer3D\Processes\RenderProcess.cs

RunActivity používá:

Kód:
GraphicsProfile.HiDef
SurfaceFormat.Color
DepthFormat.Depth24Stencil8

a podle Settings.AntiAliasing nastavuje:

Kód:
PresentationParameters.MultiSampleCount

Tento soubor nebylo nutné měnit.

Jeho úkolem je zvolenou hodnotu aplikovat. Výběr hodnot podporovaných konkrétním GPU se nyní řeší už v menu.


Source\3rdPartyLibs\Monogame\SharpDX*.dll

Zdrojová záloha už obsahuje:

Kód:
SharpDX.dll
SharpDX.Direct3D11.dll
SharpDX.DXGI.dll


VÝSLEDEK

Před změnou:

Menu nabízelo natvrdo až 32× MSAA.

Po změně:
Menu se přímo přes Direct3D 11 dotáže na možnosti grafického adaptéru a nabídne pouze skutečně podporovaný rozsah.

Testovaný systém:
Maximum bylo automaticky určeno na:

8× MSAA

Úprava je sestavena a prakticky ověřena.

Snimek-obrazovky-2026-08-13-201331.png
 
Zatím jen krátce, obsáhlejší technické popisy jednotlivých změn přidám později, protože vše ještě testuji. :-)

Do Open Rails CZ/SK jsem nyní přidal možnost spustit simulátor v režimu Borderless / Bez rámečku. Režim je řešen přímo v Open Rails, takže není potřeba okno po spuštění dodatečně upravovat externím programem.

Následně jsem přidal skutečný FPS limiter přímo do RunActivity.exe. Limit je možné nastavit na požadovanou hodnotu a omezení probíhá přímo uvnitř herní/renderovací smyčky. Po dalším doladění časování používá limiter high-resolution časování a výsledný frame pacing je při testování velmi plynulý.

Je důležité odlišit tuto funkci od současného Performance Tuneru v Open Rails.

Ten není klasickým FPS limiterem. Snaží se dosáhnout nastaveného cílového FPS tím, že podle výkonu dynamicky upravuje grafickou náročnost – například dohlednost a LOD. Proto mohou při jeho použití mizet vzdálenější objekty.

Nová funkce nic takového nedělá.

Nastavím například 30 FPS → RunActivity skutečně omezuje frekvenci vykreslování na požadovanou hodnotu. Dohlednost, LOD ani množství objektů kvůli dosažení limitu nesnižuje.

Limiter je navíc nezávislý na programech třetích stran typu RTSS nebo na FPS limiteru v ovladači grafické karty.

Zatím stále testuji, takže podrobný rozpis změněných částí kódu přidám, až budu funkci považovat za definitivně odladěnou. :-)

Snimek-obrazovky-2026-08-13-215056.png Snimek-obrazovky-2026-08-13-222245.png
 
Takhle by podle mě mělo fungovat ověřování výkonu v Open Rails. :-)

Udělat si pořádnou diagnostiku a podle naměřených dat hledat, kde skutečně vzniká problém, místo pouhého odhadování podle FPS.

V tomto případě jsem například zjistil, že problém není v samotném udržení nastavených FPS. Limiter má dostatečnou výkonovou rezervu a časování snímků se drží velmi blízko požadované hodnotě.

Problém se ukázal až při porovnání renderovaných snímků se simulačními kroky a skutečným pohybem vlaku. Simulace sice postupuje správnou rychlostí, ale nový stav světa se k rendereru nedostává v dokonale pravidelných intervalech. Při jízdě se proto vzdálenost, o kterou se svět mezi jednotlivými snímky posune, nepatrně mění.

Výsledkem nejsou klasické propady FPS, ale mikroškubání nebo takový zvláštní pocit pohybu „na gumě“.

A přesně proto podle mě nestačí při optimalizaci sledovat jen číslo FPS. Mnohem důležitější je vědět, co během každého snímku dělá simulace, updater, renderer, limiter a Present a jak jsou tyto části mezi sebou časově synchronizované.
 
Když jsem dříve psal, že původní engine Open Rails podle mě není stavěný na takovou palbu dalších funkcí a grafických vychytávek, začíná se ukazovat, že jsem nebyl úplně vedle. :-)

Nestačí totiž pouze napsat novou funkci, spustit hru a říct: „Funguje to, hotovo.“ Pokud se něco přidává do enginu, je potřeba také ověřit, jak se nový stav dostává přes jednotlivé části hry až do výsledného vykresleného snímku a jestli je všechno správně časově synchronizované.

Při diagnostice mikroškubání jsem například postupně prošel simulaci → Updater → RenderFrame → renderer → limiter → Present. FPS přitom mohou vypadat naprosto ukázkově a výkonová rezerva může být velká, ale přesto obraz nemusí působit dokonale plynule.

Teď jsme se dostali až k samotným WorldPosition a transformacím vozidel. A tady je například zajímavé zjištění, že aktuální TrainCar.WorldPosition se během simulace průběžně přepisuje. RenderFrame pak dostane konkrétní transformaci pro svůj snímek, ale engine nemá automaticky připravený bezpečný pár předchozí + aktuální WorldPosition, který by šel jednoduše použít pro renderovací interpolaci.

A právě to je podle mě rozdíl mezi tím přidat funkci a integrovat ji správně do enginu.

Neříkám tím, že všechno, co bylo do OR CZ/SK přidáno, je špatně. Spíš že u takhle starého a postupně rozšiřovaného enginu už podle mě nestačí ověřit pouze to, že novinka na obrazovce funguje. Je potřeba sledovat i její návaznosti, časování, Updater, render a chování jednotlivých framů.

Běžný uživatel nebo laik tohle většinou ani nemá šanci odhalit. Vidí novou funkci, funguje mu, hra nespadne, takže je přirozené říct „super, hotovo“. Mikroproblémy v časování nebo předávání stavů se ale mohou projevit až při cíleném měření a porovnání jednotlivých framů.

A pokud tohle Icik vidí jinak nebo se tím zabývat nechce, tak s tím samozřejmě nic neudělám. Je to jeho práce a jeho rozhodnutí. Já si můžu maximálně udělat vlastní diagnostiku, ukázat konkrétní naměřené výsledky a zkusit si ty věci ve své verzi vyřešit jinak. Data jsou pro mě v tomhle směru podstatnější než dojem, že když nová funkce funguje, musí být automaticky správně začleněná do celého enginu.
 
no ted jedu aktivitu jk_ec172 a kdybych v nekterych usecich nekladal casteji tak bycch asi aktivitu nedohral, psalo to chybu 3D.render cabview nebo tak neco :-), trat v poradku a modely trainsset pouzit z aktivity

chyba.jpg
 
no ted jedu aktivitu jk_ec172 a kdybych v nekterych usecich nekladal casteji tak bycch asi aktivitu nedohral, psalo to chybu 3D.render cabview nebo tak neco :-), trat v poradku a modely trainsset pouzit z aktivity
Ano, a právě ta chyba 3D.Render/CabView klidně může být problém někde v kódu nebo rendereru, který se na výkonnější sestavě vůbec nemusí projevit. Icik má „super stroj“ a podobně na tom mohou být i někteří další, takže velká výkonová rezerva může určité problémy jednoduše zamaskovat. :-)

To, že stejná aktivita, kabina nebo model u Icika funguje bez pádu, ještě automaticky neznamená, že je všechno v pořádku a chyba musí být na straně uživatele. Na jiné nebo slabší sestavě se může projevit něco, na co Icik při svém HW vůbec nenarazí. Právě proto bych chybu 3D.Render/CabView automaticky nesváděl na trať nebo modely.
 
no ted jedu aktivitu jk_ec172 a kdybych v nekterych usecich nekladal casteji tak bycch asi aktivitu nedohral, psalo to chybu 3D.render cabview nebo tak neco :-), trat v poradku a modely trainsset pouzit z aktivity
Teď když jsi poslal přímo chybové okno, tak je to ještě zajímavější. Tohle už nevypadá jako klasický problém typu „nestíhá PC“. :-)

Pád jde přímo přes MSTSLocomotive.GetDataOf(CabViewControl) a následně CabViewDialRenderer.PrepareFrame. Konkrétně tam vznikne System.OverflowException při volání Math.Abs nad celočíselnou hodnotou, kterou už .NET nedokáže bezpečně převést na kladnou.

Takže tady bych opravdu hledal problém v obsluze hodnot CabView v kódu, případně kombinaci konkrétního CVF/modelu a toho, jak s jeho hodnotou současný kód pracuje.

Výkon PC může ovlivnit, kdy a jak často se některé problémy projeví, ale podle tohoto stack trace bych samotný pád na slabší počítač nesváděl. Tady máme konkrétní výjimku v kódu.

Jestli používáš stejné vozidlo a kabinu jako ostatní a jim to nespadne, je právě zajímavé zjistit, jaká konkrétní hodnota se do GetDataOf(CabViewControl) v okamžiku pádu dostala.
 
ja to nesvadim na trat a ani model :-) jak pises spis takovou vec bych osetril v kodu nebo opravil nevim nejssem programator.
aktivitu jssem sstahl z mojevlacky.cz
 
no nacetl jsem possledni bod a v tom useku uz ta chyba neobjevila, kazdopadne by to chtelo osetrit kdyz jedes dlouhou aktivitu tak je to pekne na hovno i kdyz sse doporucuje cassteji ukladat :-)
 
no nacetl jsem possledni bod a v tom useku uz ta chyba neobjevila, kazdopadne by to chtelo osetrit kdyz jedes dlouhou aktivitu tak je to pekne na hovno i kdyz sse doporucuje cassteji ukladat :-)
Jo, a právě tohle je na tom nepříjemné. Když se stejná chyba po načtení posledního uloženého bodu už neobjeví, vůbec to nemusí znamenat, že je problém pryč. Může jít o stav, který vznikne až po delší době běhu aktivity nebo určité kombinaci hodnot v kabině/renderu.

U dlouhé aktivity je pak samozřejmě dost na nic, když člověk jede třeba hodinu a hra spadne kvůli jedné neošetřené výjimce. Častější ukládání je dobrá pojistka, ale nemělo by nahrazovat ošetření chyby v kódu.

A podle toho chybového okna, které jsi poslal, tam opravdu byla konkrétní OverflowException v MSTSLocomotive.GetDataOf(CabViewControl). Takže minimálně tenhle případ by podle mě šel v kódu dohledat a ošetřit, aby kvůli jedné špatné hodnotě nespadla celá aktivita.
 
Generování snímků je hotové.

Omezovač běží přímo uvnitř Open Rails a funguje bez zahazování původních snímků. Při nastavení 30 FPS se mezi každý skutečný snímek vloží jeden generovaný, takže výsledkem je 30 reálných + 30 generovaných snímků.

Krátká ukázka z diagnostiky:
Kód:
[FGPIPE] ... True ... INTERNAL_MG_PRESENT_MIDPOINT ... 793;793;0;True
793 skutečných snímků → 793 vygenerovaných → 0 chyb při prezentaci.

Takže žádné zahazování snímků – generovaný snímek se skutečně vkládá mezi dva původní. :-)

Snimek-obrazovky-2026-08-14-230129.png
 
Back
Nahoře