Seite 95 von 252

Re: Jammer-Thread

Verfasst: 14.11.2012, 23:23
von CodingCat
Hm, ich bringe es mit ordentlicher Schleifenbedingung tatsächlich nicht zum Laufen. Habe auch schon etliche Male die Branches umgestellt, bringt nix. Eventuell wegen massiver Konkurrenz ganze Warps endlos im Lockzustand? Aber wie käme ein solcher Warp dort hinein? Mögliches Szenario: Warp lockt teilweise, wird suspendet, anderer Warp läuft endlos ohne Suspend?

Re: Jammer-Thread

Verfasst: 15.11.2012, 17:43
von kaiserludi
Wieso verlangt C++ keinen expliziten cast, wenn ich einem void* den Wert eines void** zu weise? Das hätte mir den Bughunt eben erspart.

Re: Jammer-Thread

Verfasst: 15.11.2012, 17:51
von dot
kaiserludi hat geschrieben:Wieso verlangt C++ keinen expliziten cast, wenn ich einem void* den Wert eines void** zu weise?
C++ verlangt da sogar einen reinterpret_cast...

Re: Jammer-Thread

Verfasst: 15.11.2012, 17:52
von CodingCat
Weil void* nunmal keinen expliziten Cast verlangt. void steht für irgendetwas, auch einen Zeiger auf irgendetwas. Man kann sich darüber streiten, ob nicht jeder Cast von und zu void* grundsätzlich immer einen (reinterpretierenden) Cast erfordern sollte. Gewisse Abhilfe schafft es, statt void* konsequent char* einzusetzen, dann hast du nicht nur wohldefinierte Zeigerarithmetik, sondern auch wesentlich striktere Typisierung inklusive eines stets expliziten Casts.

Re: Jammer-Thread

Verfasst: 15.11.2012, 18:10
von kaiserludi
dot hat geschrieben:
kaiserludi hat geschrieben:Wieso verlangt C++ keinen expliziten cast, wenn ich einem void* den Wert eines void** zu weise?
C++ verlangt da sogar einen reinterpret_cast...
Schön wärs, das ist genau, was ich mir wünsche, tut es aber leider nicht. Probiere es aus.

Code: Alles auswählen

void** a;
void* b = a; // compiles just fines
@CodingCat:
Ich finde, bei myVoidPointer = &myVoidPointer oder auch myVoidPointer = &myIntPointer einen expliziten cast nochmal eine ganze Stufe sinnvoller als bei myVoidPointer = myIntPointer, weil man eben nicht nur den Typ mit dem die Daten interpretiert werden,, auf die der Pointer zeigt, ändert, sondern auch die Anzahl der Referenzierungen. Ich mache da ja aus einem ** einen * und nicht einfach nur einen anderen einfachen **. Bei myVoidPointer = myInt und selbst bei myVoidPointerPointer = myVoidPointer wird schließlich auch ein expliziter reinterpret_cast verlangt.
Stimmt, alle void* durch char* zu ersetzen wäre eine Idee, gerade auch in Bezug auf Zeigerarithmetik sehr praktisch. Allerdings muss man dann bei der Kommunikation mit bestehender API, welche void* nutzt, immer noch casten.

Re: Jammer-Thread

Verfasst: 15.11.2012, 18:14
von CodingCat
kaiserludi hat geschrieben:Allerdings muss man dann bei der Kommunikation mit bestehender API, welche void* nutzt, immer noch casten.
Siehst du? Genau deshalb geht das bei void* implizit. ;) Deine Wünsche wären was für eine Warnung. Grundsätzlich will man Typsysteme möglichst konsistent halten.

Re: Jammer-Thread

Verfasst: 15.11.2012, 18:18
von eXile
CodingCat hat geschrieben:Hm, ich bringe es mit ordentlicher Schleifenbedingung tatsächlich nicht zum Laufen. Habe auch schon etliche Male die Branches umgestellt, bringt nix. Eventuell wegen massiver Konkurrenz ganze Warps endlos im Lockzustand? Aber wie käme ein solcher Warp dort hinein? Mögliches Szenario: Warp lockt teilweise, wird suspendet, anderer Warp läuft endlos ohne Suspend?
Ja, dito hier. Ich habe gestern während des Live-Streams daran rumgebastelt. Fun fact: Auf Fermi werden mindestens 75 Iterationen, auf Kepler nur 16 Iterationen benötigt, um das Bild halbwegs flickerfrei zu bekommen. Selbst wenn man also das Spinlock sauber implementiert bekommt, wird das immer noch ziemlich ineffizient sein (aber ich glaube allein der Name „Spinlock in Pixel-Shader“ ließ dies schon erahnen).

Ich werde mal weiter über das Thema nachdenken; ich jedoch für mich habe noch nie einen flicker-freien, sowohl auf Fermi wie auch Kepler funktionierenden und nicht-deadlockenden Spinlock in einem Pixel-Shader gesehen.

Re: Jammer-Thread

Verfasst: 15.11.2012, 18:22
von CodingCat
eXile hat geschrieben:Ich werde mal weiter über das Thema nachdenken; ich jedoch für mich habe noch nie einen flicker-freien, sowohl auf Fermi wie auch Kepler funktionierenden und nicht-deadlockenden Spinlock in einem Pixel-Shader gesehen.
Interessanterweise hatte ich damit in meiner eigenen Arbeit ja recht wenig Probleme. Dort habe ich vermutlich einfach durch intensive Ressourcennutzung genügend "Context Switches" provoziert (z.B. wenn die GPU Latenzen versteckt). Bei ArtificialMind auf der Fermi habe ich es allerdings nicht zum Laufen bringen können, jedoch kam es dort wenn ich mich recht erinnere zu keinem eindeutigen Timeout, sondern zu einem etwas dubiosen Crash, insofern ist das etwas spekulativ.

Ohne eine Möglichkeit, die GPU zu solchen Switches zu bewegen, fällt mir im Moment auch nichts zuverlässiges ein.

Re: Jammer-Thread

Verfasst: 15.11.2012, 18:24
von dot
kaiserludi hat geschrieben:Schön wärs, das ist genau, was ich mir wünsche, tut es aber leider nicht. Probiere aus.
Ah so rum, sry, hab gedacht umgekehrt...

Re: Jammer-Thread

Verfasst: 15.11.2012, 18:43
von CodingCat
ACHTUNG! ACHTUNG! Der HLSL-Compiler konstruiert falschen Code aus UAV-Counter-Increments im ternären Operator mit konstanter Bedingung:

Code: Alles auswählen

uint taskID = bLite ? nonAtomicTaskID : TracingVoxelOut.IncrementCounter();
Ruft auch dann imm_atomic_alloc auf, wenn bLite bereits zur Compile-Zeit zu true ausgewertet wird. if stattdessen generiert korrekten Code (d.h. Increment verschwindet komplett). Möglicherweise kann der HLSL-Compiler auch sonst nicht mit Nebenwirkungen im ternären Operator umgehen und interpretiert diesen als reines movc.

Re: Jammer-Thread

Verfasst: 15.11.2012, 18:51
von dot
MSDN hat geschrieben:Unlike short-circuit evaluation of &&, ||, and ?: in C, HLSL expressions never short-circuit an evaluation because they are vector operations. All sides of the expression are always evaluated.
Aber ich wär bzw. bin auch drauf reingefallen, bzw. hatte bisher wohl einfach nur Glück...

Re: Jammer-Thread

Verfasst: 15.11.2012, 19:00
von kaiserludi
CodingCat hat geschrieben:
dot hat geschrieben:Ich muss leider nochmal: Sollte wohl >= 0 heißen... ;)
Ich habe die STL bei diesem Projekt bewusst rausgehalten. Aber es gibt ja noch andere Alternativen zu new[], z.B. ordentlich benannte Funktions-Templates zur Allokation und Konstruktion nach Wunsch, welche ich schlussendlich auch gewählt habe.
Magst du deine Funktionstemplatelösung hier mit uns teilen?
Ich stehe gerade vor einem ähnlichen Problem.

Derzeit habe ich folgende beiden Funktionstemplates, die anstelle von new und new[] bzw. von delete und delete[] eingesetzt werden. GlobalMemoryPoolManager.alloc() und GlobalMemoryPoolManager.dealloc() kann man auch direkt aufrufen und hat dann ein verhalten wie bei malloc() und free(), bloß das die Implementation den angefragten Speicher aus vorallokierten Pools holt, statt jedes Mal direkt eine Speicheranfrage an den Heap zu stellen.

Code: Alles auswählen

template<typename Ftype>
Ftype* allocate(size_t count=1u)
{
	size_t* pRaw = reinterpret_cast<size_t*>(GlobalMemoryPoolManager.alloc(sizeof(Ftype)*count+sizeof(size_t)));
	*pRaw = count;
	Ftype* p = reinterpret_cast<Ftype*>(pRaw+1);
	for(size_t i=0; i<count; ++i)
		new(p+i) Ftype();
	return p;
}

template<typename Ftype>
void deallocate(const Ftype* p)
{
	if(!p)
		return;
	size_t* pRaw = (reinterpret_cast<size_t*>(const_cast<Ftype*>(p))-1);
	for(size_t i=*pRaw; i-->0;)
		p[i].~Ftype();
	GlobalMemoryPoolManager.dealloc(pRaw);
}
Das Problem ist nun:
Das funktioniert so natürlich nur für den Default Konstruktor.
Wie teile ich dem Funktionstemplate jetzt am besten mit, welchen Konstruktor von Ftype er aufrufen soll, ohne dass es zu seiner Kompilierzeit wissen muss, über welche Konstruktoren Ftype verfügt, und ohne unnötige Komplexität für den Aufrufer?

Re: Jammer-Thread

Verfasst: 15.11.2012, 19:12
von CodingCat
kaiserludi hat geschrieben:Das funktioniert so natürlich nur für den Default Konstruktor.
Tut es bei new[] auch. Unglaublich, aber leider in C++11 noch immer wahr.
kaiserludi hat geschrieben:Wie teile ich dem Funktionstemplate jetzt am besten mit, welchen Konstruktor von Ftype er aufrufen soll [...]
In C++11 über variadische Templates, davor über viele viele Overloads. Wie du solche Overloads mit Hilfe von Makros automatisch generieren kannst, siehst du z.B. hier (Achtung, extrem hässlich), Beispielanwendung hier.
kaiserludi hat geschrieben:[...], ohne dass es zu seiner Kompilierzeit wissen muss, über welche Konstruktoren Ftype verfügt, und ohne unnötige Komplexität für den Aufrufer?
Gar nicht, die aufgerufenen Konstruktoren müssen genau wie bei new oder new[] im Aufrufer-/Instantiierungskontext deklariert worden sein. Wenn sie das sind, erhälst du mit variadischen Templates / Template Overloads über die mitgegebenen Parameter automatisch den richtigen Konstruktoraufruf.

Achtung, ausnahmesichere Konstruktion erfordert etwas mehr Arbeit:

Code: Alles auswählen

template <class Element>
inline void destruct(Element *destr, Element *destrEnd)
{
        while (destr < destrEnd)
                (--destrEnd)->~Element();
}

template <class Element>
inline void construct(Element *dest, Element *destEnd, ...)
{
        Element *destr = dest;

        try
        {
                for (; dest < destEnd; ++dest)
                        new((void*)dest) Element(...);
        }
        catch (...)
        {
                destruct(destr, dest);
                throw;
        }
}

Re: Jammer-Thread

Verfasst: 15.11.2012, 19:55
von Krishty
Ich werde noch bekloppt. Ich schaffe es einfach nicht, meine Render-Zeit zu messen. Über Flush() rauscht die GPU zwar nicht drüber; rendert aber auch nicht alles. Ich habe nun ein QueryPerformanceCounter() am Anfang meines Programms und nach dem Anzeigen des 2. Frames, und als Differenz werden mir 0,7 s angezeigt, obwohl die GPU mindestens fünf Sekunden am Rendern ist. Irgendwas mache ich falsch.

Setze ich Haltepunkte vor und nach dem Messen von Start- und Endzeitpunkt, kommen tatsächlich rund sieben Sekunden raus; je nachdem, wie schnell ich F5 drücke. Ohne Haltepunkte sind es immer 0,7 s. WTF

Re: Jammer-Thread

Verfasst: 15.11.2012, 19:59
von dot
Verwend Queries (1x D3D11_QUERY_TIMESTAMP_DISJOINT + nx D3D11_QUERY_TIMESTAMP)...

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:01
von Krishty
Er will ich wissen, warum mein Performance Counter Schrott ist.

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:03
von CodingCat
Das geht über GPU-Query-Objekte. Gerade wenig Zeit, deshalb nur Link. ID3D11DeviceContext::End(query) löst Queries aus. Mit Timing-Query alles umschließen (Begin und End()), mit Timestamp-Query und End() Timestamp aufnehmen. Am Ende Zeiten zurücklesen. Achtung, immer dieselbe Timestamp-Query innerhalb einer Messung wiederzuverwenden ist keine gute Idee.
Zu Flush hat geschrieben:Because Flush operates asynchronously, it can return either before or after the GPU finishes executing the queued graphics commands.

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:12
von Krishty
CodingCat hat geschrieben:
Zu Flush hat geschrieben:Because Flush operates asynchronously, it can return either before or after the GPU finishes executing the queued graphics commands.
Ja; danke. Mein Problem ist, dass auch Present() vor dem Rendern zurückkehrt – und zwar zwei Mal, obwohl ich nur einen Back Buffer habe. Wenn ich auf den dritten Frame warte, klappt das.

Wo wird da überall reingerendert? Ich hätte jetzt gedacht:
  • Das erste Present() schubst, wie die vorangegangenen Flush()s, das Rendern an und kehrt vor Vollendung zurück.
  • Das zweite Present muss auf den Back Buffer (ich habe in meiner Swap Chain nur einen definiert) warten, und damit auch auf Vollendung des allerersten Renderns.
Aber warum kehrt Present #2 auch schon vor Vollendung zurück?!

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:15
von eXile
dot hat geschrieben:Verwend Queries (1x D3D11_QUERY_TIMESTAMP_DISJOINT + nx D3D11_QUERY_TIMESTAMP)...
Korrekt. Ich kann übrigens euch nur empfehlen, auch beim Starten des Timers bereits alte Queries darauf zu überprüfen, ob sie vielleicht nun bereit sind. Das reduziert Hickups doch erheblich bei mir (und als positiver Nebeneffekt sind weniger Queries parallel ausstehend, so dass man auch noch etwas Speicher sparen kann).

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:16
von dot
Krishty hat geschrieben:Aber warum kehrt Present #2 auch schon vor Vollendung zurück?!
Weil dein Driver dir eben zwei Frames Vorsprung gibt. Die genannten Queries bieten die Möglichkeit, bestimmte Ereignisse im Command Stream der GPU zu timen...

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:18
von Krishty
Ja; stimmt … verdammt, bin ich eingerostet. Scheißding. Wie kriege ich diese Pain in the ass weg?

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:23
von CodingCat

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:26
von eXile
Da gibt es übrigens zwei Varianten: Man kann auch nicht nur via D3D11_QUERY_TIMESTAMP_DISJOINT und D3D11_QUERY_TIMESTAMP Zeitmessungen anstellen, sondern auch via zwei D3D11_QUERY_EVENTs: Beim Start ein Event in den Command-Buffer einfügen, GetData in einer Endlosschleife auf Erfolg testen, dann QueryPerformanceCounter starten. Beim Stop ebenso. Wenn man das so machen will, misst man auch noch die Latenz des Graphik-Stacks mit. Ich glaube, das AMD-SDK hat das auch noch drin.

Und wie bei allen Timern gilt: Man vergleicht keine Zeiten, die durch unterschiedliche Timer-Arten zustande kamen. Man kann nur Zeiten, generiert durch eine bestimmte Timer-Art, miteinander vergleichen.

Aber wen es interessiert: Für ein paar Lichter sagt erster Timer auf der GPU 340 µs, letzterer Timer auf der CPU mit Latenz des Graphikstacks ca. 2000 µs.
CodingCat hat geschrieben:IDXGIDevice1::SetMaximumFrameLatency
Ich glaube, das kann meiner Meinung nach vom Benutzer in den Einstellungen des Treibers auf einen anderen Wert forciert werden. Was jetzt hier zum Zeitmessen nicht schlimm ist.

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:32
von Krishty
CodingCat hat geschrieben:IDXGIDevice1::SetMaximumFrameLatency
Tausend Dank! :-)

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:52
von kaiserludi
CodingCat hat geschrieben:
kaiserludi hat geschrieben:[...], ohne dass es zu seiner Kompilierzeit wissen muss, über welche Konstruktoren Ftype verfügt, und ohne unnötige Komplexität für den Aufrufer?
Gar nicht, die aufgerufenen Konstruktoren müssen genau wie bei new oder new[] im Aufrufer-/Instantiierungskontext deklariert worden sein. Wenn sie das sind, erhälst du mit variadischen Templates / Template Overloads über die mitgegebenen Parameter automatisch den richtigen Konstruktoraufruf.
Aber sowas

Code: Alles auswählen

class MyClass
{
public:
MyClass(int a, int b, int c);
};

MyClass* myClass = new MyClass(1, 2, 3);
geht doch, ohne dass die Standardlib weiß, welche Parametertypen die Konstruktoren von MyClass haben, sprich, es kompiliert auch, wenn MyClass nur dem Code bekannt ist, der new MyClass(1, 2, 3) aufruft. Dem Code in der Standardlib hingegen, der new definiert, muss MyClass dafür nicht bekannt sein.
Ich vermute mal, wir haben uns hier einfach missverstanden?

Re: Jammer-Thread

Verfasst: 15.11.2012, 20:59
von CodingCat
kaiserludi hat geschrieben:Aber sowas

Code: Alles auswählen

class MyClass
{
public:
MyClass(int a, int b, int c);
};

MyClass* myClass = new MyClass(1, 2, 3);
geht doch, ohne dass die Standardlib weiß, welche Parametertypen die Konstruktoren von MyClass haben, sprich, es kompiliert auch, wenn MyClass nur dem Code bekannt ist, der new MyClass(1, 2, 3) aufruft. Dem Code in der Standardlib hingegen, der new definiert, muss MyClass dafür nicht bekannt sein.
Ich vermute mal, wir haben uns hier einfach missverstanden?
Die Standardbibliothek definiert nur die Templates, instantiiert werden diese von dir in deiner Übersetzungseinheit (i.d.R. implizit beim ersten Aufruf), wo MyClass mit all seinen Konstruktoren bekannt ist / sein muss. Genau so sieht es auch im Fall der Allokationsfunktionen aus, ohne dass du irgendetwas dafür tun musst.

Re: Jammer-Thread

Verfasst: 17.11.2012, 18:22
von eXile
eXile hat geschrieben:Bild
Direct3D 11.1 kommt doch. Aber warum dann im Jammer-Thread?

Weil das eher Direct3D 11.05 ist. Es unterstützt nichts, was einen WDDM-1.2-Treiber braucht.

D3D_FEATURE_LEVEL_11_1 braucht WDDM 1.2.

Meiner Einschätzung nach bringt das also exakt Null.

Re: Jammer-Thread

Verfasst: 17.11.2012, 19:27
von Andre
eXile hat geschrieben: Direct3D 11.1 kommt doch. Aber warum dann im Jammer-Thread?

Weil das eher Direct3D 11.05 ist. Es unterstützt nichts, was einen WDDM-1.2-Treiber braucht.

D3D_FEATURE_LEVEL_11_1 braucht WDDM 1.2.

Meiner Einschätzung nach bringt das also exakt Null.
Dann hören die Leute wenigstens auf danach zu fragen :D

Re: Jammer-Thread

Verfasst: 20.11.2012, 14:28
von CodingCat
Weil mich C++ typedefs nur auf umständlichen Umwegen fowärtsdeklarieren lässt, bin ich in der öffentlichen API dazu übergegangen, typedefs durch Vererbung zu ersetzen. Das ist zwar bei der Implementierung unschön, erleichtert aber überall sonst die Nutzung enorm:

Code: Alles auswählen

template <class Type>
class TypeDependentInterface
{
   INTERFACE_BEHAVIOR(TypeDependentInterface)
public: ...
};

// Vorher
typedef TypeDependentInterface<Foo> FooSpecificInterface;

// Nachher
class FooSpecificInterface : public TypeDependentInterface<Foo>
{
   INTERFACE_BEHAVIOR(FooSpecificInterface)
};
Im Gebrauch:

Code: Alles auswählen

// Aus
template <class Type> class TypeDependentInterface;
typedef TypeDependentInterface<Foo> FooSpecificInterface;

// Wird
class FooSpecificInterface;

Re: Jammer-Thread

Verfasst: 21.11.2012, 12:29
von CodingCat
Nicht zu fassen, die deutsche MSDN übersetzt sogar Schlüsselwörter: "ordnen Sie zu", "Beschränken Sie ein", "nackt", ...