Seite 2 von 2

Re: [Projekt] Mein Stalker-Build

Verfasst: 21.05.2018, 11:55
von Krishty
MasterQ32 hat geschrieben:
Mir geht’s vor allem darum, es spielbar zu halten in den nächsten 10, 20 Jahren. Das ist ja erfahrungsgemäß schwer ohne Quelltext.
Löblich! Ich muss die Spiele unbedingt auch mal noch spielen... Und da hat man ja gleich noch nen kleine Modernisierung dabei!

Du hast ja schon die Shadow Map hochgedreht, wie viel Aufwand wäre es, ne Option für SSAA oder MSAA einzubauen? Müsste wesentlich mehr Aufwand sein, oder?
Siehe die unteren beiden Screenshots hier: https://zfx.info/viewtopic.php?f=10&t=4 ... 1df#p55028

Das ist ein Deferred Renderer, also muss ich die Auflösung aller Buffer (Color, Light, Ambience, Material, …) parallel hochdrehen. Ich muss aber außerdem zwischen interner Auflösung und angezeigter Auflösung unterscheiden. Und hier wird’s haarig, denn die Shader haben hunderte Parameter und sobald man einen vergisst, sieht es wie oben aus.

Re: [Projekt] Mein Stalker-Build

Verfasst: 21.05.2018, 12:44
von xq
Schade, der Screenshot mit 2x2-SSAA sah schon besser aus

Re: [Projekt] Mein Stalker-Build

Verfasst: 21.05.2018, 15:08
von Krishty
Ja; es steht noch immer auf meiner To-Do-Liste – dass es mir wichtig ist, sieht man ja daran, dass ich es recht früh ausprobiert habe. Aber es wird halt ein großes Unterfangen :(

Re: [Projekt] Mein Stalker-Build

Verfasst: 20.09.2018, 23:05
von Krishty
Ich versuche immer nebenher, das unübersichtliche Gewirr aus willkürlichen DLLs zu lichten, indem ich Module zusammenfasse. Am Ende soll maximal eine EXE mit drei DLLs übrig bleiben. (S.T.A.L.K.E.R. exportiert tausende(!) Funktionen über DLL-Grenzen, und das behindert den Optimizer gewaltig. Auf exportierte Funktionen lässt sich z. B. weniger Aliasing-Analyse anwenden und kein Inlining. Ganz übel.)

Das lief bisher relativ gut. Es gab oft Konflikte in der Initialisierungsreihenfolge globaler Variablen, aber dann packe ich die Initialisierung halt in eine Funktion und schiebe den Aufruf an eine Stelle, an der er keine Probleme mehr macht.

Ein besonderes Problem ist aber das Lua-Modul (xrLua.dll). Zur Erklärung: Sehr viel KI und Gameplay in S.T.A.L.K.E.R. wurde mit Lua gescriptet. Lua hat einen Just-in-Time-Compiler, der Lua zu x86-Befehlen kompiliert, damit es mit quasi-nativer Geschwindigkeit ausführbar ist. Und das … kracht.

Nicht erst seit der Vereinigung mit der Haupt-EXE – ich habe gemerkt, dass es immer kracht, sobald es mit Optimierung kompiliert wird! Ein Zeiger in einem Callback ist dann verschoben. Der Code, der den Zeiger bereitstellt, ist automatisch erzeugt – und deshalb kaum zu debuggen.

Hm. Ich habe das Problem mit Binärsuche auf eine Quelldateie lfunc.c eingegrenzt, die ohne Optimierung kompiliert werden muss. Nun, kann ich das Problem hoffentlich via #pragma optimize("", off) auf eine einzelne Funktion bestimmen. Ich bin gespannt, ob der Auslöser lückenhafter Code ist (undefiniertes Verhalten?) oder ein weiterer Bug in Visual C++.

Nachtrag: Der Übeltäter ist die selbe Funktion, die auch abstürzte:

Code: Alles auswählen

#pragma optimize("g", off)
UpVal *luaF_findupval (lua_State *L, StkId level) {
  global_State *g = G(L);
  GCObject **pp = &L->openupval;
  UpVal *p;
  UpVal *uv;
  while ((p = ngcotouv(*pp)) != NULL && p->v >= level) {
    lua_assert(p->v != &p->u.value);
    if(p->v == level) {  /* found a corresponding upvalue? */
      if(isdead(g, obj2gco(p)))  /* is it dead? */
        changewhite(obj2gco(p));  /* ressurect it */
      return p;
    }
    pp = &p->next;
  }
  uv = luaM_new(L, UpVal);  /* not found: create a new one */
  uv->tt = LUA_TUPVAL;
  uv->marked = luaC_white(g);
  uv->v = level;  /* current value lives in the stack */
  uv->next = *pp;  /* chain it in the proper position */
  *pp = obj2gco(uv);
  uv->u.l.prev = &g->uvhead;  /* double link it in `uvhead' list */
  uv->u.l.next = g->uvhead.u.l.next;
  uv->u.l.next->u.l.prev = uv;
  g->uvhead.u.l.next = uv;
  lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
  return uv;
}
#pragma optimize("g", on)
Nehme ich die #pragma optimize raus, dann kommt L mit falschem Wert an (120 B verschoben, was exakt der Größe von lua_State entspricht).

Nachtrag 2: Wer das mit den tausenden exportierten Funktionen für eine Übertreibung hielt: Meine xr_3DA.exe exportiert hier gerade 2398 Funktionen; xrCore.dll 606. Ich brenne darauf, den Müll endlich rauszuschmeißen.

Re: [Projekt] Mein Stalker-Build

Verfasst: 17.02.2019, 02:19
von Krishty
Ich habe die DLL für den Renderer und die EXE des Spiels verschmolzen. Weil ich sechs(!) verschiedene vorkompilierte Header innerhalb des selben Projekts nicht mehr pflegen wollte, habe ich vorkompilierte Header komplett abgeschaltet.

Das Ergebnis ist, dass der Linker nun mehr als 4 GiB RAM verbraucht, und dann erstmal mangels Adressraum abschmiert.

Wieder was gelernt: Wenn man in der .vcxproj-Datei unter <PropertyGroup Label="Globals"> den zusätzlichen Wert <PreferredToolArchitecture>x64</PreferredToolArchitecture> einträgt, wird auch für 32-Bit-Programme der 64-Bit-Compiler/-Linker eingesetzt. Das ist langsamer und verbraucht mehr Speicher, aber damit kriegt man dann auch 4+ GiB gelinkt … (Es hat sich nun bei 5.7 GiB eingependelt.)

Re: [Projekt] Mein Stalker-Build

Verfasst: 08.03.2019, 13:30
von Krishty
Krishty hat geschrieben: 14.04.2018, 23:24Ich präsentiere hiermit die Abhängigkeiten der S.T.A.L.K.E.R.-Teilprojekte untereinander:
Das ist nun alles zu einer großen EXE verschmolzen. Keine DLL-Hölle mehr; keine zyklischen Abhängigkeiten mehr; nur noch ein riiiiesiges Projekt, das 20 Minuten kompiliert und 6 GiB zum Linken braucht.

Das Verschmelzen hat rund 100 KiB Code rausgeschmissen, weil der Optimizer nun über Funktionsgrenzen optimieren kann und tausende überflüssige Import- und Exporttabelleneinträge entfernen konnte.

Außerdem bin ich auf Visual Studio 2019 umgestiegen, was wiederum an die 100 KiB durch verbesserte Code Generation gebracht hat. Insgesamt fast vier Prozent Größe runter. Geschwindigkeits-Benchmarks muss ich mal nachreichen.

Download: http://krishty.com/stalker_en

Re: [Projekt] Mein Stalker-Build

Verfasst: 08.03.2019, 14:03
von Schrompf
Sehr cool. Entspricht auch meinen seit Jahren gepflegten Vorurteilen vom statischen Linken :-)

Re: [Projekt] Mein Stalker-Build

Verfasst: 28.05.2020, 20:36
von Krishty
Bloom und sRGB

S.T.A.L.K.E.R. ist aus den frühen 00ern, und deshalb kommt es mit einer dicken Portion Bloom.

Bloom wird gern als Gaußfilter implementiert – horizontal blurren, vertikal blurren, zum Bild addieren. Wenn man das auch so umsetzt, erhält man diesen Matsch:

Bild

Um das zu vermeiden, und wirklich nur die stark leuchtenden Bildteile strahlen zu lassen, führt man üblicherweise einen Grenzwert ein. Alles, was dunkler als dieser Grenzwert ist, wird im Bloom ignoriert. (Steuerbar hier via r2_ls_bloom_threshold.)

Das führt dann zum perfekten 00er-Look, bei dem alle hellen Bildteile total bloomen und alle mittelhellen und dunklen Bildteile absolut gar keinen Bloom haben (hier bewusst übertrieben):

Bild

Ich bin großer Feind des Thresholds und nutze es grundsätzlich nicht.

Die grundsätzliche Idee – Gauß zum Bild addieren – ist nicht verkehrt. Sie ist sogar relativ nah an dem, was im Auge passiert. Ich bin sogar ein kleines Bisschen Bloom-Freund.

Aber warum sieht das erste Bild oben dann derart vermatscht und verkehrt aus?

Das Problem ist, dass hier HDR versucht wird, ohne die Gammakurve zu beachten. Die Pixel des Back Buffers sind im sRGB-Farbraum mit Gamma ~2.2. Wenn man zwei Pixel addiert, die 10% Helligkeit haben, kommen naiv 20% Helligkeit heraus. Tatsächlich sollten es aber (0.1 ^ 2.2 + 0.1 ^ 2.2) ^ 1/2.2 sein; also eher 14% Helligkeit.

Da der Bloom sehr viele Pixel aufaddiert, ist der Fehler entsprechend groß – und der Bloom viel zu grell.

Also lasst uns S.T.A.L.K.E.R. so umbauen, dass es sRGB-korrekt rendert!

Das klingt schwieriger, als es ist. Das Spiel baut auf Direct3D 9. Dessen Mittel für sRGB sind relativ beschränkt, und lassen sich wie folgt zusammenfassen:
  1. Für jede Textur muss SetSamplerState(D3DSAMP_SRGBTEXTURE, TRUE) aufgerufen werden, so dass die Texel im linearen Farbraum geladen werden.
  2. Vor dem Rendern muss SetRenderState(D3DRS_SRGBWRITEENABLE, TRUE) aufgerufen werden. So werden die gerenderten Pixel beim Schreiben in den Back Buffer in den sRGB-Farbraum konvertiert.
Naja, ganz so einfach ist es nicht. Wer blind die beiden Aufrufe einbaut, wird merken, dass das Menü grün wird und das Spiel nur noch einen weißen Bildschirm anzeigt. S.T.A.L.K.E.R. nutzt auch Normal Maps und Displacement Maps (bspw. für Hitzeflimmern im Deferred Rendering), und wenn die fälschlicherweise als sRGB interpretiert werden, kracht es gewaltig.

Wir machen’s vorsichtiger. Wir setzen in der Funktion, die den Bloom-Filter implementiert, sRGB und setzen es danach wieder zurück:

    // r2_rendertarget_phase_bloom.cpp

    void CRenderTarget::phase_bloom()
    {
        for(int i = 0; i < 256; ++i)
            RCache.dbg_SetSS(i, D3DSAMP_SRGBTEXTURE, TRUE);
        RCache.dbg_SetRS(D3DRS_SRGBWRITEENABLE, TRUE);


        …

        for(int i = 0; i < 256; ++i)
            RCache.dbg_SetSS(i, D3DSAMP_SRGBTEXTURE, FALSE);
        RCache.dbg_SetRS(D3DRS_SRGBWRITEENABLE, FALSE);

    }


Das selbe für das Rendering der Geometrie:

    // r2_R_render.cpp

    void CRender::Render()
    {
        …
        LP_normal.sort();
        LP_pending.sort();

        for(int i = 0; i < 256; ++i)
            RCache.dbg_SetSS(i, D3DSAMP_SRGBTEXTURE, TRUE);
        RCache.dbg_SetRS(D3DRS_SRGBWRITEENABLE, TRUE);


        …

        // Lighting, dependant on OCCQ
        render_lights(LP_pending);

        for(int i = 0; i < 256; ++i)
            RCache.dbg_SetSS(i, D3DSAMP_SRGBTEXTURE, FALSE);
        RCache.dbg_SetRS(D3DRS_SRGBWRITEENABLE, FALSE);


        // Postprocess
        Target->phase_combine();
    }


TADAAAA

Bild

Ich musste natürlich ein paar Faktoren anpassen, da sich durch sRGB die Gesamthelligkeit des Bildes geändert hat.

Aber der Bloom ist nun an hellen Stellen genau so stark wie vorher, und an mittleren und dunklen Stellen viiiiel schwächer. Außerdem ist die Beleuchtung nun halbwegs sRGB-korrekt. Und das wirkt sich insbesondere auf die indirekte Beleuchtung aus:

Bild

Ich zementiere diese Einstellungen noch als Voreinstellung beim ersten Start der Engine, so dass sie jeder User meiner EXE automatisch bekommt. Und dann ist mal wieder ein Update fällig.

Jedenfalls kriegt man einen Hauch von sRGB recht einfach sogar in eine bestehende D3D9-Engine gezimmert. In diesem Fall waren vier Code-Ergänzungen ausreichend, wenngleich mit der Holzhammer-Methode. Wenn man es richtig machen wollte, würde man nun den Code der Lichter, Vertex-Farben, Materialien, usw. an sRGB anpassen.

Nun, wo der Bloom nicht mehr so stark weichzeichnet, fällt das fehlende Antialiasing stärker auf. Das könnte ich als nächstes angehen.

Re: [Projekt] Mein Stalker-Build

Verfasst: 04.06.2020, 12:40
von Thoran
Krishty hat geschrieben: 28.05.2020, 20:36 Nun, wo der Bloom nicht mehr so stark weichzeichnet, fällt das fehlende Antialiasing stärker auf. Das könnte ich als nächstes angehen.
Vielleicht gabs ja einen Grund für das übertriebene Weichzeichnen? ;-)

Re: [Projekt] Mein Stalker-Build

Verfasst: 04.06.2020, 14:20
von scheichs
Also... Ich muss wieder mal sagen: Mega! Danke für die geilen, verständlichen Erklärungen (gamma-korrigiertes Aufaddieren der Helligkeit).
Und das Ergebnis kann sich auch sehen lassen!

Re: [Projekt] Mein Stalker-Build

Verfasst: 05.06.2020, 02:04
von Krishty
Gern!
Thoran hat geschrieben: 04.06.2020, 12:40Vielleicht gabs ja einen Grund für das übertriebene Weichzeichnen? ;-)
Ja – nämlich, dass die in den 00er aufgewachsenen die Generation der Bloomers sind.