[D3D 11] Crash in CreateBuffer()

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

[D3D 11] Crash in CreateBuffer()

Beitrag von Krishty »

Ich habe gerade einen Crash Dump von einem Anwender bekommen. Demnach stürzt mein Programm bei ihm ständig direkt beim Start ab. Andere Anwender haben keine Probleme.

Der Dump ist 32-Bit, aber die 64-Bit stürzt genau so ab. Der Absturz findet in ID3D11Device::CreateBuffer() statt. Das leitet weiter an
  • d3d11.dll!CDevice::CreateBuffer
  • danach d3d11.dll!CDevice::CreateAndRecreateLayeredChild<struct SD3D11LayeredBufferCreationArgs>
  • dann d3d11.dll!CDevice::CreateBuffer_Worker
  • … und dann sind wir auch schon im Windows Error Reporting, das scheinbar den Stack zurückgespult hat. Stehengeblieben ist er bei call d3d11.dll!NOutermost::CDevice::CreateLayeredChild.
Als erstes habe ich mir die Parameter angesehen, mit denen ich CreateBuffer() aufrufe. An dieser Stelle möchte ich einen dynamischen Constant Buffer erzeugen, ohne seinen Inhalt zu initialisieren. Daher:

  pDesc D3D11_BUFFER_DESC * 0x036efc40
    ByteWidth            16
    Usage                D3D11_USAGE_DYNAMIC (2)
    BindFlags            D3D11_BIND_CONSTANT_BUFFER (4)
    CPUAccessFlags       D3D11_CPU_ACCESS_WRITE (65536)
    MiscFlags            0
    StructureByteStride  0

  pInitialData D3D11_SUBRESOURCE_DATA * nullptr

  ppBuffer ID3D11Buffer * * 0x040b0818
    nullptr


Das ist der dritte Constant Buffer, den ich erzeuge. Die beiden davor funktionieren problemlos.

Die Größe ist gültig (glatt durch 16 teilbar und kleiner als 4096). Ich habe überprüft, ob das Alignment der Parameter stimmt – tut es. Dann, ob die Virtual Function Tables des Devices in Ordnung sind – sind sie (wie wir auch am Call Stack sehen).

Ich bin nun ratlos, was das Problem sein könnte – überall sonst funktioniert es; das spricht für Probleme mit dem PC des Anwenders. Andererseits finde ich nirgends sonst Berichte von Crashes in der speziellen Funktion, also muss es doch irgendwie an mir liegen.

Ich werde schnell noch versuchen, an genaue Exception-Informationen zu kommen. Bis dahin: Fällt euch irgendwas ein?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [D3D 11] Crash in CreateBuffer()

Beitrag von Schrompf »

Ich hatte mal einen unerklärlichen Crash in Splatter, der am Ende an einem bestimmten Screen Recorder lag. Ich hatte was in nem OpenGL-Call getan, was eigentlich böse war, aber alle Treiber haben das anhand des Null-Pointers erkannt und stillschweigend gar nichts getan. Der Screen Recorder hatte sich in die Funktionen reingehängt, aber diesen Test nicht gemacht. Ergebnis war ein Illegaler Speicherzugriff auf 0x000000000000.

Was ist denn der Anlass des Dumps?
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [D3D 11] Crash in CreateBuffer()

Beitrag von Krishty »

Den Anlass habe ich eben gefunden – Access Violation (0xc0000005) in d3d11.dll + 4c193. Also hier:

  NOutermost::CDevice::CreateLayeredChild:
  push 2Ch
  mov eax,5AC85E23h
  call __EH_prolog3_catch_GS (5AC8306Bh)
  mov edi,dword ptr [ebp+8]
  mov eax,dword ptr [edi+20h]
  mov ebx,dword ptr [ebp+0Ch]
⇒ mov ecx,dword ptr [eax]


Passt perfekt zum Rest des Dumps. Mal gucken, ob ich rekonstruiert kriege, was in eax sein sollte und warum stattdessen Null drin landet.

Der Hinweis mit dem Screen Recorder ist nicht schlecht; sowas hatte ein Kunde auch mal. Aber in diesem Fall ist der Call Stack nicht tief genug. Es spielt sich alles in d3d11.dll ab (er kommt nicht einmal zum Grafiktreiber) und die d3d11.dll ist auch noch im Auslieferungszustand (sonst hätte ich keine Debug-Symbole dafür bekommen).
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [D3D 11] Crash in CreateBuffer()

Beitrag von Krishty »

Okay, also
  • eax wird in der 6. Zeile durch edi+20h befüllt
  • edi wiederum wird in der fünften Zeile durch ebp+8 befüllt
  • ebp ist der Base Pointer, der auf die aktuellen Funktionsparameter und lokalen Variablen zeigt
  • wenn wir die Adresse von NOutermost::CDevice::CreateLayeredChild in Visual Studios Watch Window eingeben, bekommen wir die Signatur

      NOutermost::CDevice::CreateLayeredChild(unsigned int,void const *,unsigned long,struct ID3D11LayeredUseCounted *,struct _GUID const &,void * *)

    sie hat also sechs Parameter plus den unsichtbaren this-Zeiger
  • die Funktion ist entweder __stdcall (weil wir es mit COM zu tun haben) oder __thiscall (weil sie in C++ geschrieben ist) – der Unterschied ist, dass bei __stdcall der this-Zeiger als erster Parameter auf dem Stack landet; bei __thiscall ist er im ecx-Register
  • Die Funktion überschreibt ecx (das ist die letzte Zeile; die, die abstürzt) sofort, also ist es nicht __thiscall sondern __stdcall – deckt sich damit, dass sieben Parameter vom Stack gelesen werden:
      ebp+8h
      ebp+Ch
      ebp+10h
      ebp+14h
      ebp+18h
      ebp+1Ch
      ebp+20h
  • über das Layout von Parametern im Speicher bei stdcall-Aufrufen hat Raymond Chen einen schönen Beitrag: https://blogs.msdn.microsoft.com/oldnew ... 0/?p=41023
    Da sehen wir, dass das mit den sieben Parametern passt (wobei ebp+8h der erste ist) und dass negative Offsets (ebp-*) lokale Variablen referenzieren
  • mov edi,dword ptr [ebp+8] schiebt also den this-Pointer (der als erster unsichtbarer Parameter übergeben wurde) nach edi
  • mov eax,dword ptr [edi+20h] lädt das Member, das 32 B vom Anfang von NOutermost::CDevice entfernt liegt, in eax
  • … und das ist nullptr, und darum crasht der erste Zugriff darauf.
Liege ich damit halbwegs richtg? Einsprüche?

Dann gehe ich mal in der Hierarchie nach oben und prüfe, welcher Zeiger 32 B in NOutermost::CDevice hinein eigentlich liegen sollte.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [D3D 11] Crash in CreateBuffer()

Beitrag von Krishty »

Schrompf, ich tendiere jetzt auch zu einem Screen Recorder.

Wenn ich eine Device-Methode aufrufe, wird der this-Zeiger von einer Position acht Bytes *vor* meinem Device geholt. Der Debugger spuckt für diese Adressen aus:

  -8 d3d11.dll!const CLayeredObject<class CDevice>::CContainedObject::`vftable'{for `ID3D11VideoDevice'}
  -4 d3d11.dll!const CLayeredObject<class CDevice>::CContainedObject::`vftable'{for `ID3D11VideoCapture'}
  ±0 d3d11.dll!const CLayeredObject<class CDevice>::CContainedObject::`vftable'{for `CID3D11Forwarder'}


Mein D3D 11-Device ist also vom Typ CID3D11Forwarder, und leitet auf Methoden von ID3D11VideoDevice weiter.

Die ersten beiden Constant Buffers werden erzeugt, bevor ich einen Back Buffer habe (uninteressant für Screen Recorders). Der dritte Constant Buffer, dessen Erzeugung abstürzt, wird direkt nach dem Back Buffer angelegt.

Das große Rätsel ist jetzt aber: Der Anwender hat Windows 7. ID3D11VideoDevice steht aber erst seit Windows 8 zur Verfügung. Wtf?!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [D3D 11] Crash in CreateBuffer()

Beitrag von Krishty »

Nein, der User hat keinen Screen Recorder. Dafür Intel-Treiber. Verdammt. Wie kriege ich das jetzt wieder getestet …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [D3D 11] Crash in CreateBuffer()

Beitrag von Schrompf »

Sorry, aber Du bist da bereits sehr viel weiter vorgedrungen, als mein Wissen über die Interna reicht. Viel Glück.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: [D3D 11] Crash in CreateBuffer()

Beitrag von starcow »

Rein aus Neugier: Ist es Dir gelungen das Problem zu lösen? Oder konntest Du einen Fortschritt erzielen?
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [D3D 11] Crash in CreateBuffer()

Beitrag von Krishty »

Die nächstbeste Anlaufstelle ist https://groups.google.com/forum/#!topic ... hPOjNM53no (die inoffizielle Mailing-Liste der DirectX-Entwickler bei Microsoft).

Auch dort kein wirklicher Fortschritt, außer:
The presence of "layered child" to me implies that you are creating one of the 'device layers'. The most common one is the debug layer […]
Ich bin natürlich nicht so doof, die Debug-Version an die Kunden auszuliefern, daher muss dort ein anderer Device Layer aktiv sein und GPU-Anweisungen abfangen. Auch das spricht wieder für Screen Recorder, obwohl der Nutzer das vehement abstreitet.

Der Nutzer kann den Debug-Layer auch nicht versehentlich eingeschaltet haben, weil er dafür das DirectX-SDK braucht, meine Anwendung explizit eintippen muss, und sie bei mir trotzdem nicht abstürzt. Daher schließe ich das aus.

Da mir seit einer Woche niemand mehr geantwortet hat, denke ich, dass die genau so ratlos sind wie ich. Ich werde nachher nochmal nachhaken, ob Feature Level 10.0 etwas damit zu tun haben könnte.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten