Variablen in HLSL



  • Hallo zusammen
    Gibt es in HLSL die Möglichkeit, Werte zwischen einzelnen Vertex Shader ausführungen zu speichern? Also ich meine jtzt nicht mit Konstantenregistern, sondern die Werte werden in einem Aufruf des Vertex Shaders berechnet und für den nächsten Aufruf gespeichert. Man kann sich fragen, wofür das gut sein soll, aber ich arbeite viel mit Instanzen und habe bspw. folgende Situation:

    Für die Fauna benutze ich 6 Planes (12 Vertizen, 18 Indices). Die Höhe einer Planze wird mit Displacement Mapping im Vertex Shader mit Hilfe eines Vertex Texture Fetchings ermittelt. Nun muss dieses langsame Texture Lookup 18 Mal pro Pflanze durchgeführt werden, einmal würde jedoch genügen, da die Höhe für jeden Vertex derselben Planze Instanz völlig identisch ist. Hierfür bräuchte ich aber einen Weg, diese Höhe für den nächsten Aufruf vom Vertex Shader zwischenzuspeichern...

    Gibt es da eine Möglichkeit?

    Mfg Samuel Lörtscher



  • Hält eine uniform-Variable nicht ihren Wert, wenn du sie nicht variierst? also über mehrere Aufrufe hinweg. Wenn ich dich richtig verstehe, bräuchtest du doch genau so etwas? Oder liege ich komplett daneben?

    Gruß Kimmi



  • zwischen den Aufrufen? Das wäre absolut genial! 🙂



  • Ishildur schrieb:

    Für die Fauna benutze ich 6 Planes (12 Vertizen, 18 Indices). Die Höhe einer Planze wird mit Displacement Mapping im Vertex Shader mit Hilfe eines Vertex Texture Fetchings ermittelt. Nun muss dieses langsame Texture Lookup 18 Mal pro Pflanze durchgeführt werden, einmal würde jedoch genügen, da die Höhe für jeden Vertex derselben Planze Instanz völlig identisch ist. Hierfür bräuchte ich aber einen Weg, diese Höhe für den nächsten Aufruf vom Vertex Shader zwischenzuspeichern...

    Gibt es da eine Möglichkeit?

    Leider nein. Der Vertex Shader wird ja auch nicht hintereinander aufgerufen sondern möglichst viele Vertices parallel abgearbeitet. Wenn du schon Instancing verwendest könntest du ja aber die Pflanzenhöhe vielleicht irgendwie in deiner per Instance Data ablegen und dir den Texture Lookup so komplett sparen!? Falls du D3D10+ verwendest gäbe es die Möglichkeit z.B. die Höhe erst im Geometry Shader zu lesen.



  • Ich hab folgendes:

    // ******************************************** struct "Input" *******************************************
    struct Input{
     // ------------------------------------------- dynamic members ------------------------------------------
     float4 PosGeo: POSITION0; // the position in model space per vertex  (stream 0)
     float2 TexGeo: TEXCOORD0; // the texture coordinates per vertex      (stream 0)
     float2 PosIns: TEXCOORD1; // the position in worl space per instance (stream 1)
     float2 TexIns: TEXCOORD2; // the texture coordinates per instance    (stream 1)
     float3 NrmIns: NORMAL0;   // the normal per instance                 (stream 1)
     // ------------------------------------------------------------------------------------------------------
    };
    // *******************************************************************************************************
    
    // ******************************************* struct "Output" *******************************************
    struct Output{
     // ------------------------------------------- dynamic members ------------------------------------------
     float4 Position: POSITION0; // the position in screen space
     float2 Texture:  TEXCOORD0; // the texture coordinates
     float3 Normal:   TEXCOORD1; // the normal vector
     float3 Light:    TEXCOORD2; // the light vector
     float  Fog:      TEXCOORD3; // the fog amount
     float  Blend:    TEXCOORD4; // the blend amount
     // ------------------------------------------------------------------------------------------------------
    };
    // *******************************************************************************************************
    
    uniform float  Height;
    uniform float2 Pos;
    
    // ##################################### define all needed functions #####################################
    // ******************************************* function "main" *******************************************
    void main(in Input In,out Output Out){
     // scale the local coordinate system of the plants (do not affect w!!)
     In.PosGeo.xyz *= 40.0f;
    
     // transform from model into world space (the y position must be looked up within the heightmap
     In.PosGeo.xz += In.PosIns;
    
     if(Pos != In.PosGeo.xz){
      // compute the position of the current instance on the heightmap
      float2 PosTex = (MapSize.yy+float2(In.PosIns.x,-In.PosIns.y))/MapSize.x;
      Height = In.PosGeo.y  += tex2Dlod(HeightmapSampler,float4(PosTex,0.0f,0.0f)).r;
     }
    
     // finally assign the position in view space, the texture coordinates and the fog
     Out.Position = mul(ViewProj,In.PosGeo);
     Out.Texture  = In.TexGeo+In.TexIns;
     Out.Normal   = normalize(In.NrmIns);
     Out.Light    = normalize(Lgt);
     Out.Fog      = (distance(Eye,In.PosGeo.xyz)-Fog.x)/(Fog.y-Fog.x);
     Out.Blend    = (distance(Eye.xz,In.PosGeo.xz)-900.0)/(940.0f-900.0f);
    }
    // *******************************************************************************************************
    // #######################################################################################################
    

    Der Teil innerhalb des if's müsste eigentlich nur bei jedem 18. Vertex neu berechnet werden...

    Ich kriege folgende Fehlermeldung:

    error X3025: global variables are implicitly constant, enable compatibility mode to allow modification



  • Ja, wie schon gesagt geht das aufgrund der Art und Weise wie die Grafikkarte arbeitet nicht. Möglichkeiten wären z.B. richtiges Instancing und dabei die Höhe nicht per Texture Lookup sondern aus der per Instance Data lesen. In D3D10+ könnte man auch was mit der Stream Output Stage anstellen falls es unbedingt über eine Displacement Map laufen muss oder z.B. den Geometry Shader verwenden.



  • @dot
    Ja das habe ich natürlich auch versucht, erstaunlicherweise ist es dann aber noch langsamer 😞
    Ich habe generell ein Problem mit den Pflanzen, diese bremsen das System derart übel aus, ich weiss auch nicht, was ich falsch mache.

    Ohne Fauna habe ich ca. 400 fps, mit Fauna gerade noch 80 fps. (Ich zeichne gerade mal 9216 Planzen). Witzig ist allerdings auch, dass wenn ich die Planzen zwar zeichne, jedoch in eine Richtung schaue, wo man sie nicht sieht, dann habe ich immer hin ca. 250 fps. Ich muss das ganze zweimal Rendern, einmal mit Alphatesting und eingeschaltetem Z-Buffer (fürs z-sorting), und dann das Ganze noch einmal mit eingeschaltetem Alphablending für die transparenten Ränder. Erstaunlicherweise macht es allerdings überhaupt keinen Unterschied, ob ich die Pflanzen nun einmal oder zweimal rendere. Auch wenn ich den zweiten Pass mit eingeschaltetem Alphablending komplett weglasse, bemerke ich keinen Unterschied in der Framerate.

    Ich bin bereits seit Tagen damit beschäftigt, den Flaschenhals auszuspüren, finde ihn jedoch nicht 😞



  • @dot
    Was meinst du mit "richtiges Instancing"?



  • Mit richtigem Instancing meine ich sowas wie das hier: http://msdn.microsoft.com/en-us/library/bb173349(VS.85).aspx

    Allerdings halte ich den Texture Lookup im Vertexshader eher nicht für das Problem, deinen Symptomen nach bist vermutlich nicht vertexbound sondern pixelbound (Alphatesting kann da ziemlich reinhaun). Wie verhält sichs mit der Performance wenn du das ganze mal nur einmal renderst und das ohne Alphatesting sondern nur mit Alphablending?



  • @dot

    Mit richtigem Instancing meine ich sowas wie das hier

    Genauso mache ich es doch??

    Du must mir schnell helfen: VertexBound? Fillrate?

    Dein Verdacht bestätigt sich:

    Beide Renderpasses: ca. 80 fps
    Nur Alphatesting: ca. 85 fps
    Nur Alphablendin: ca. 120 fps

    Ohne Alphatesting wird es aber nicht mehr korrekt gerendert (weil die Planzen dann nicht mehr nach z sortiert sind.)



  • Ishildur schrieb:

    Genauso mache ich es doch??

    Stimmt, sry hab ich übersehen. Wäre es dann aber nicht einfach möglich die Höhe jedes Grasbüschels in der Instance Data zu übergeben statt aus der Textur zu samplen? Oder hast du eine animierte Displacementmap oder dergleichen weswegen du unbedingt einen Lookup machen musst?

    Wenn der Flaschenhals beim Vertexprocessing liegt sagt man die Anwendung ist Vertexbound, analog dazu spricht man von Pixelbound wenn der Flaschenhals beim Pixelprocessing liegt. Die Fillrate ist einfach die Anzahl von Pixeln/Fragments die die Graka pro Sekunde schreiben kann. Normalerweise ist man heutzutage viel eher Pixel- als Vertexbound (aufwändige Pixelshader etc). Auch wenn deine Vegetation nicht sehr viele sichtbare Pixel am Bildschirm ausmacht hast du vermutlich einen extrem großen Overdraw, d.h. die Anzahl der Pixel die in den Backbuffer wandern und wieder überschrieben werden ist sehr hoch. Deine 9216*18 = 165888 Vertices machen im Vergleich dazu nicht soviel aus, vor allem da dank Indices und Vertexcache wohl effektiv sowieso weniger als 18 Vertices pro Instance durch den Shader müssen (ich gehe davon aus dass deine Pflanzen aus Quads bestehen).
    Ich könnte mir vorstellen dass die Performance merkbar besser ist wenn du die Szene von einem Blickwinkel betrachtest wo die zuerst gemalten Quads weiter hinten liegende verdecken als wenn du sie aus einem Blickpunkt betrachtest wo die Reihenfolge annähernd "richtig" ist. (Early Z-Culling sorgt in ersterem Fall nämlich dafür dass die "verdeckten" Pixel sofort eliminiert werden)

    Was du versuchen kannst wäre erstmal z.B. die Pflanzen so zu sortieren dass sie Front-To-Back gerendert werden und du ohne Alphatesting auskommst. Desweiteren könntest du z.B. (was heute durchaus üblich ist) einen Z-Prepass rendern. D.h. deine ganze Szene (ohne Gras) erstmal nur in den Z-Buffer rendern (also keine Pixel Shader und D3DRS_COLORWRITEENABLE auf 0, einige Grakas haben einen eigenen Z-Only Mode und rendern dir das bis zu doppelt so schnell als normal). Das sorgt dafür dass das Early-Z-Culling beim darauffolgenden Rendern der Vegetation alles eliminiert was im endgültigen Frame sowieso von anderen Dingen verdeckt würde (Hilft natürlich nur was wenn du nach der Vegetation noch irgendwas rendern würdest was selbige verdecken würde). Wieviel und ob das alles im Endeffekt was bringt hängt letztendlich aber von deiner Szene ab...



  • Ishildur schrieb:

    Ohne Fauna habe ich ca. 400 fps, mit Fauna gerade noch 80 fps. (Ich zeichne gerade mal 9216 Planzen).

    Wie kommst du darauf, das dein Rechner bei 9216 anderen Objekten, die gezeichnet werden, schneller waere? f'`8k

    Gruß, TGGC (der kostenlose DMC Download)



  • @TGGC
    Weil die Planzen jeweils nur aus 3 Planes bestehen und allesamt in einem einzigen Batch mit Geometry Instancing gerendert werden was IMHO so ungefähr dem Zeichnen von EINEM komplexen Objekt entsprechen würde.

    @dot
    Ich kann doch Semitransparente Objekte nicht Front-To-Back rendern?



  • Ishildur schrieb:

    Ich kann doch Semitransparente Objekte nicht Front-To-Back rendern?

    Sry, ich meinte natürlich Back-To-Front...



  • Ishildur schrieb:

    @TGGC
    Weil die Planzen jeweils nur aus 3 Planes bestehen und allesamt in einem einzigen Batch mit Geometry Instancing gerendert werden was IMHO so ungefähr dem Zeichnen von EINEM komplexen Objekt entsprechen würde.

    Dann ist deine Meinung wohl falsch. Hast ja selbst festgestellt, das es langsamer rendert. f'`8k

    Gruß, TGGC (der kostenlose DMC Download)



  • TGGC schrieb:

    Wie kommst du darauf, das dein Rechner bei 9216 anderen Objekten, die gezeichnet werden, schneller waere? f'`8k

    Die Probleme die hier auftreten sind relativ spezifisch für die Vegetation. Falls du darauf hinauswillst dass weniger Pflanzen schneller wären hast du natürlich recht da eine geringere Pflanzendichte auch für weniger Overdraw sorgen würde.
    Allerdings kann man ein oder mehrere "normale" Modelle (also z.B. geschlossene Körper) mit einer äquivalenten Anzahl an Dreiecken mit Sicherheit sehr viel schneller rendern (55296 Dreiecke sind auf aktuellen Karten kein wirklich großes Problem). Sein Problem hängt also nicht so sehr mit der generellen Anzahl der Objekte zusammen sondern vor allem mit der Art derselben.

    Wenn man davon ausgeht dass er ein Feld mit 96x96 = 9216 Pflanzen hat und seine Kamera in typischer Ego-Perspektive relativ flach draufschaut kann man ganz grob über den Daumen sagen dass, wenns blöd läuft, viele Pixel im Bereich von 96 mal überschrieben werden was sehr Bandbreitenintensiv is und hier der Flaschenhals sein dürfte. Bei "normalen" Objekten hast du bei weitem nicht so hohe Werte und damit auch nicht dieses Problem. Ich wette die Performance wäre drastisch besser wenn er die Kamera senkrecht von oben draufrichten würde 😉



  • @dot
    Ganz deine Meinung! 😉

    Hier übrigens ein paar aktuelle Screenshots von unserer Bachelorarbeit mit den Pflanzen 🙂

    http://s1.directupload.net/file/d/2187/2sqo9rmn_jpg.htm
    http://s3.directupload.net/file/d/2187/arf3dt3q_jpg.htm



  • Hm, also handelt es sich um diese Bäume!? Die Screenshots sind recht dunkel aber ich würde mal meinen dass hier kaum alle 9216 Pflanzen ständig sichtbar sind!? Da könnte man sicher was rausholen wenn man möglichst nur die sichbaren rendert. Zumindest einfaches View Frustrum Culling könnte schon was bringen.

    Nichts desto trotz: Schaut cool aus 🙂



  • Und wenn du 10000 Kisten in der Groesse machst und die einfach so zeichnest, dann hast du auch Perspektiven in denen genauso viele Pixel gezeichnet/ ueberschreiben werden. Ergo wird die Framerate dann genauso sein. Was hat das also mit Pflanzen zu tun? Die Frage hoert sich halt momentan so an, sobald ich auf meinem C64 10000 Multiplikationen durchfuehre, bricht die Framrate ein - irgendwie multipliziere ich wohl die falschen Zahlen. f'`8k

    Gruß, TGGC (der kostenlose DMC Download)



  • TGGC schrieb:

    Und wenn du 10000 Kisten in der Groesse machst und die einfach so zeichnest, dann hast du auch Perspektiven in denen genauso viele Pixel gezeichnet/ ueberschreiben werden.

    Da die Kisten aber nicht durchsichtig sind kann man die Front-to-Back und/oder mit Z-Prepass rendern und hat das Problem entschärft da man dann nurmehr Z-Buffer reads aber keine BackBuffer writes mehr hat...


Log in to reply