Direct3D Problem



  • @RenéG
    Nachtrag:
    Wenn ich tatsächlich beim Initialisieren bzw. Freigeben von D3D etwas falsch machen würde, dann verrate mir dochmal warum in bei D3D Debug (Windowed und Fullscreen) und bei D3D Release (Windowed) alles ok ist und nur bei D3D Release (Fullscreen) diese Fehler auftreten.



  • Du scheinst nicht zu verstehen. Ich benutze kein Load Time Dynamic Linking, was d3d9.dll bereits beim Linken binden würde, wo du natürlich Recht hättest. Mal abgesehen davon das dies imo keine direkte Beziehung mit COM hat.

    MOMENT: Erst erfindest Du das Rad neu, und dann schimpfst Du noch auf die Entwickler des Rades, dass Dein Rad eckig geworden ist? Und Hinweise auf Deine Fehler willst Du gar nicht erst annehmen?

    Ich frage mich gerade, ob es überhaupt Sinn hat, dich nochmals darauf hinzuweisen, aber ich tue es trotzdem.

    Mal abgesehen davon das dies imo keine direkte Beziehung mit COM hat.

    Aber nur in YOUR humble opinion. Denn sobald Du Direct3DCreate9 aufrufst, wird die DLL automatisch geladen, was Dir ein LoadLibrary erspart. Weiterhin übernimmt ab dann COM die Kontrolle über die DLL. Das bedeutet, dass intern ein Referenzzähler hochgesetzt wird, der jedesmal erhöht wird, sobald eine Schnittstelle aus der DLL angefordert wird und verringert, sobald diese Schnittstelle mit Release freigegeben wird. Sobald dieser Referenzzähler auf 0 dekrementiert wird, gibt sich die DLL automatisch frei. Wenn dies durch FreeLibrary geschieht (FreeLibrary überwacht den Referenzzähler NICHT), versucht COM natürlich vergeblich, auf Code zuzugreifen, der innerhalb der DLL liegt.

    1. Zum Debug-Modus:
    Wir alle wissen, dass der Debug-Modus in der Lage ist, viele 'krasse' Programmierfehler einfach 'abzufangen', und höchstwahrscheinlich wird in diesem Modus die DLL erst entladen, wenn sich das Programm beendet.

    2. Zum Release-Modus:
    Tut mir leid, ich kenne die Quellen der DirectX-DLLs nicht. Ich kann dir nicht sagen, was beim Entladen alles notwendig ist. Ich weiss nur, dass Deine Entlade-Funktion zu früh aufgerufen wird.



  • MOMENT: Erst erfindest Du das Rad neu, und dann schimpfst Du noch auf die Entwickler des Rades, dass Dein Rad eckig geworden ist? Und Hinweise auf Deine Fehler willst Du gar nicht erst annehmen?

    Das verstehe ich nicht. Wieso hat das Benutzen von Run Time Dynamic Linking was damit zu tun, das Rad neu zu erfinden ? Das macht doch MS teilweise selbst.

    Aber nur in YOUR humble opinion

    War vielleicht etwas unglücklich von mir formuliert. Ich meinte, dass das Laden von DLL's, egal ob Load Time oder Run Time Dynamic Linking, nicht COM abhängig ist. Oder willst du mir etwa agen, dass JEDE DLL auf COM basiert ?

    Denn sobald Du Direct3DCreate9 aufrufst, wird die DLL automatisch geladen

    Du scheinst das tatsächlich nicht zu verstehen. Erstens wird mit dem Aufruf von Direct3DCreate9 die d3d9.dll nicht geladen. Was diese Funktion macht, sagt doch die DX Doku:

    Creates an instance of an IDirect3D9 object.

    Und zweitens verrate mir doch mal wie du Direct3DCreate9 aufrufst, ohne die DLL bereits beim Linken zu binden. Dann MUSST du LoadLibrary und FreeLibrary benutzen. Übrigens wird dort ein Referenzzähler verwendet und sobald dieser
    0 erreicht, wird die DLL aus dem Prozess-Speicher entfernt.

    Weiterhin übernimmt ab dann COM die Kontrolle über die DLL. Das bedeutet, dass intern ein Referenzzähler hochgesetzt wird, der jedesmal erhöht wird, sobald eine Schnittstelle aus der DLL angefordert wird und verringert, sobald diese Schnittstelle mit Release freigegeben wird. Sobald dieser Referenzzähler auf 0 dekrementiert wird, gibt sich die DLL automatisch frei.

    Wenn dies tatsächlich der Fall ist, dann
    1. Kannst du mir 'ne Quelle nennen wo ich das nachlesen kann und
    2. müsste folgender Code das Programm zum Absturz bringen

    IDirect3D9 *d3d = Direct3DCreate9(D3D_SDK_VERSION);
        d3d->Release();
        d3d = Direct3DCreate9(D3D_SDK_VERSION);    // BOOOOOM!!!
        d3d->Release();
    

    (was ich bei einem Testprogramm nicht feststellen konnte 😉 )

    [ Dieser Beitrag wurde am 03.07.2003 um 10:59 Uhr von groovemaster2002 editiert. ]



  • Ich denke mal das liegt daran dass Direct3DCreate9() in der lib liegt und nicht in der DLL 🙄.
    Probier doch mal ob Du auch statt

    d3d = Direct3DCreate9(D3D_SDK_VERSION);    // BOOOOOM!!!
    

    Auch DX Funktionen verwenden kannst Die in der DLL stehen.

    Und wenn Du Leute um Hilfe fragst dann spiel nicht den Meister der alles besser weis und nimm Kritik an sonst hilft gar nix.



  • Original erstellt von groovemaster2002:
    **Wenn dies tatsächlich der Fall ist, dann
    1. Kannst du mir 'ne Quelle nennen wo ich das nachlesen kann und
    2. müsste folgender Code das Programm zum Absturz bringen

    IDirect3D9 *d3d = Direct3DCreate9(D3D_SDK_VERSION);
        d3d->Release();
        d3d = Direct3DCreate9(D3D_SDK_VERSION);    // BOOOOOM!!!
        d3d->Release();
    

    (was ich bei einem Testprogramm nicht feststellen konnte 😉 )

    **

    All COM objects work on the principle of reference counting. Whenever a COM object is used, its reference count is incremented by one. The reference count is decremented by one, when the object is released, using the Release method. An object is a valid candidate for garbage collection, when its refernce count becomes zero.

    WIESO sollte folgender Code das zum Absturz bringen?!?! 😕 🙄



  • BTW, um noch explizitere Infos dazu zu geben - aus der DX Doku:

    COM objects enforce stricter encapsulation than C++ objects. You cannot just create the object and call any public method. A COM object's public methods are grouped into one or more interfaces. To use a method, you must create the object and obtain the appropriate interface from the object. An interface typically contains a related set of methods that provide access to a particular feature of the object. For example, the IDirect3DCubeTexture9 interface contains methods that enable you to manipulate cube texture resources. Any methods that are not part of an interface are not accessible.
    COM objects are not created in the same way as C++ objects. There are several ways to create a COM object, but all involve COM-specific techniques. The Microsoft® DirectX® API includes a variety of helper functions and methods that simplify creating most of the DirectX objects.

    You must use COM-specific techniques to control the lifetime of the object.
    COM objects do not need to be explicitly loaded. COM objects are typically contained in a DLL. However, you do not need to explicitly load the DLL or link to a static library in order to use a COM object. Each COM object has a unique registered identifier that is used to create the object. COM automatically loads the correct DLL.



  • Fazit ist aber, dass die DLL aufgrund der Initialisierungen, die während D3DCreate9 gemacht werden, Zeit braucht, sich zu deinitialisieren. Da dies wahrscheinlich asynchron geschieht, wird ihr durch FreeLibrary diese Zeit nicht gewährt, was sie zum Absturz bringt.



  • Ich denke mal das liegt daran dass Direct3DCreate9() in der lib liegt und nicht in der DLL .

    Aber Direct3DCreate9 ist doch eine Funktion der DLL. Gib mal folgendes auf der Kommandozeile im Verzeichnis von d3d9.dll ein:
    dumpbin /EXPORTS d3d9.dll
    (dumpbin gehört zu MSVC gibt aber auch andere Programme dafür).

    WIESO sollte folgender Code das zum Absturz bringen?!?!

    Laut RenéG würde sich die DLL freigeben, sobald der Referenzzähler auf 0 geht, dh es ist kein gültiger Code mehr für Direct3DCreate9 da.

    However, you do not need to explicitly load the DLL

    Das klingt interessant. Soll das nun heissen, sobald ich eine DLL hab, die auf COM basiert, darf ich mit Load- und FreeLibrary nicht arbeiten und muss die DLL implizit binden ? Wenn dem so ist, machen das dann alle falsch in dessen Projekten ich ein explizites Binden gesehen hab ?

    Fazit ist aber, dass die DLL aufgrund der Initialisierungen, die während D3DCreate9 gemacht werden, Zeit braucht, sich zu deinitialisieren. Da dies wahrscheinlich asynchron geschieht, wird ihr durch FreeLibrary diese Zeit nicht gewährt, was sie zum Absturz bringt.

    Sollte man nicht meinen, dass mit d3d->Release() alle Ressourcen freigegeben wurden ? Und selbst wenn du Recht hast und Windows danach noch gewisse Dinge zu deinitialisieren hat, sollte Windows das nicht dann in FreeLibrary berücksichtigen ?

    [ Dieser Beitrag wurde am 03.07.2003 um 16:52 Uhr von groovemaster2002 editiert. ]



  • Wenn dem so ist, machen das dann alle falsch in dessen Projekten ich ein explizites Binden gesehen hab?

    Gut, ich hab bisher überall nur implizites Binden gesehen. Und wenn Du diese Projekte ausführst, stürzen die auch ab?



  • Original erstellt von groovemaster2002:
    Laut RenéG würde sich die DLL freigeben, sobald der Referenzzähler auf 0 geht, dh es ist kein gültiger Code mehr für Direct3DCreate9 da.
    Ja, aber da steht doch nix von LoadLibrary oder so...!?!
    Wenn mit D3DCreate COM die DLL läd' und bei Release wieder entlädt, dann lädt sie die mit dem nächsten D3DCreate-Aufruf halt wieder... da geht doch nix BOOOOM?!?!

    Sollte man nicht meinen, dass mit d3d->Release() alle Ressourcen freigegeben wurden ? Und selbst wenn du Recht hast und Windows danach noch gewisse Dinge zu deinitialisieren hat, sollte Windows das nicht dann in FreeLibrary berücksichtigen ?
    Plenker! 😉

    Naja, dann probier' doch mal ein Sleep(20000) als Test vor dem bösen FreeLibrary...!??
    Wär' doch mal interessant.
    Mit dem impliziten hab' ich mich nämlich auch noch net befasst...



  • Direct3D macht einen ganz schön konfus, hab mir das mal angeschaut.

    1. Direct3D basiert auf COM, somit dachte ich, die COM-Kenntnisse hier anwenden zu können -> falsch
    2. 'Direct3DCreate9' ist eine Funktion innerhalb einer COM-DLL. D.h. die DLL muss gelade sein, bevor die Funktion aufgerufen wird.
    3. COM-Schnittstellen existieren innerhalb der DLL, mit diesen wird aber nicht der Referenzcount der DLL verändert.

    Es wäre mal angebracht, ein kleines Testprogramm zu schreiben, welches die DLL dynamisch lädt, den Fullscreen-Modus initialisiert und dann alles wieder ordnungsgemäss rückgängig macht und das mal zu debuggen.
    Wer übernimmt das?



  • Endlich jemand der mich versteht (*schluchz*). Ich werde das von dir angesprochene Testprogramm mal schreiben und dann hier posten...



  • So, hier hab ich jetzt das Programm (getestet mit MSVC 6):

    #include <windows.h>
    #include <d3d9.h>
    
    #define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = NULL; } }
    
    typedef IDirect3D9* (WINAPI *Func_Direct3DCreate)(UINT SDKVersion);
    Func_Direct3DCreate Direct3DCreate;
    
    HMODULE D3D_DLL;
    HWND MainWindow;
    bool Active;
    LPDIRECT3D9 D3D;
    LPDIRECT3DDEVICE9 D3D_Device;
    D3DPRESENT_PARAMETERS D3D_PP;
    
    // D3D initialisieren
    bool D3D_Init()
    {
        // "Verbindung" zur d3d9.dll herstellen
        D3D_DLL = LoadLibrary("d3d9.dll");
        if (!D3D_DLL)
        {
            MessageBox(NULL, "Fehler LoadLibrary()", NULL, MB_OK);
            return(0);
        }
        // Adresse von Direct3DCreate9 ermitteln
        Direct3DCreate = (Func_Direct3DCreate) GetProcAddress(D3D_DLL, "Direct3DCreate9");
        // D3D Instanz erzeugen
        D3D = Direct3DCreate(D3D_SDK_VERSION);
        if (!D3D)
            return(false);
        // Device erzeugen
        D3D_PP.BackBufferWidth = 800;   // 800x600 32bit Farbtiefe sollte jede Graka unterstuetzen
        D3D_PP.BackBufferHeight = 600;
        D3D_PP.BackBufferFormat = D3DFMT_X8R8G8B8;
        D3D_PP.BackBufferCount = 1;
        D3D_PP.MultiSampleType = D3DMULTISAMPLE_NONE;
        D3D_PP.MultiSampleQuality = 0;
        D3D_PP.SwapEffect = D3DSWAPEFFECT_DISCARD;
        D3D_PP.hDeviceWindow = MainWindow;
        D3D_PP.Windowed = FALSE;    // wir wollen Fullscreen
        D3D_PP.EnableAutoDepthStencil = TRUE;
        D3D_PP.AutoDepthStencilFormat = D3DFMT_D16; // 16bit Z-Buffer sollte auch jede 3D Graka koennen
        D3D_PP.Flags = 0;
        D3D_PP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
        D3D_PP.PresentationInterval = D3DPRESENT_INTERVAL_ONE;  // mit vsync
        HRESULT hr = D3D->CreateDevice(
            D3DADAPTER_DEFAULT,
            D3DDEVTYPE_HAL,
            MainWindow,
            D3DCREATE_SOFTWARE_VERTEXPROCESSING,
            &D3D_PP,
            &D3D_Device);
        if (hr != D3D_OK)
            return(false);
        // alles ok
        return(true);
    }
    
    // D3D deinitialisieren
    void D3D_Render()
    {
        D3D_Device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
        D3D_Device->BeginScene();
        D3D_Device->EndScene();
        D3D_Device->Present(NULL, NULL, NULL, NULL);
    }
    
    // D3D deinitialisieren
    void D3D_Cleanup()
    {
        SAFE_RELEASE(D3D_Device);
        SAFE_RELEASE(D3D);
        // d3d9.dll freigeben
        if (D3D_DLL)
            FreeLibrary(D3D_DLL);
    }
    
    // alle benoetigten Ressourcen der Anwendung freigeben
    void Cleanup()
    {
    /*
        // sofern d3d9.dll hier freigegeben wird, treten keine Probleme auf
        if (D3D_DLL)
            FreeLibrary(D3D_DLL);
    */
    }
    
    LRESULT CALLBACK WindowProcMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_KEYDOWN:
                switch (wParam)
                {
                    case VK_SPACE:
                        if (D3D_Device)
                        {
                            D3D_PP.Windowed = !D3D_PP.Windowed;
                            D3D_Device->Reset(&D3D_PP);
                        }
                    break;
                }
            break;
            case WM_CLOSE:
                D3D_Cleanup();
                DestroyWindow(hWnd);
                PostQuitMessage(0);
                return(0);
        }
        return(DefWindowProc(hWnd, uMsg, wParam, lParam));
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        // Fensterklasse registrieren
        WNDCLASS wc;
        wc.style = 0;
        wc.lpfnWndProc = WindowProcMain;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wc.lpszMenuName = NULL;
        wc.lpszClassName = TEXT("D3D_Test");
        ATOM at = RegisterClass(&wc);
        if (at == 0)
        {
            MessageBox(NULL, "Fehler RegisterClassEx()", NULL, MB_OK);
            return(0);
        }
        // Hauptfenster erzeugen
        MainWindow = CreateWindow(
            wc.lpszClassName,
            TEXT("D3D Test"),
            WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE,
            0, 0,
            640, 480,
            NULL,
            NULL,
            hInstance,
            NULL);
        if (!MainWindow)
        {
            MessageBox(NULL, "Fehler CreateWindowEx()", NULL, MB_OK);
            return(0);
        }
        // D3D initialisieren
        if (!D3D_Init())
        {
            MessageBox(NULL, "Fehler D3D_Init()", NULL, MB_OK);
            D3D_Cleanup();
            DestroyWindow(MainWindow);
            return(0);
        }
        // Message Loop der Anwendung
        MSG msg;
        bool Active = true;
        while (Active)
        {
            if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                // Nachrichten des Hauptfensters handeln
                if (msg.message == WM_QUIT)
                    Active = false;
                else
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
            else
            {
                // D3D Rendern
                D3D_Render();
            }
        }
        // Anwendung beenden
        Cleanup();
        return(msg.wParam);
    }
    

    Am wichtigsten sind hierbei die Funktionen D3D_Cleanup und Cleanup. D3D_Cleanup wird aufgerufen, wenn das Fenster geschlossen wird, also das Fenster eine WM_CLOSE Nachricht erhält. Bei einem Vorlageprojekt des DirectX 9 AppWizard in MSVC wird das übrigens genauso gemacht (soweit ich das beim Überfliegen der etlichen Quellcodedateien mitbekommen habe). Cleanup wird kurz bevor man WinMain verlässt aufgerufen, hier wird aber vorerst noch nix gemacht. Startet man jetzt die Anwendung und verlässt diese (Alt+F4) sobald man im Fullscreen Mode ist, stürzt das Programm ab.
    Also zurück zur Funktion D3D_Cleanup. Hier wird die d3d9.dll mit folgender Sequenz freigegeben:

    if (D3D_DLL)
            FreeLibrary(D3D_DLL);
    

    Kommentiert man diese jetzt aus und führt das ganze in der Funktion Cleanup durch (schon vorbereitet, einfach Kommentarzeichen entfernen), so kommt es zu keinem Absturz. 😕

    [ Dieser Beitrag wurde am 04.07.2003 um 14:58 Uhr von groovemaster2002 editiert. ]



  • Womit wir nun doch noch den Fehler gefunden haben, es liegt nämlich gar net an FreeLibrary, sondern an Deiner Messageloop.

    Du hast sicherlich übersehen, dass nach der WM_CLOSE-Message noch andere Nachrichten folgen, z.B. WM_DESTROY sowie WM_NCDESTROY. Während diese Nachrichten an der Messageloop empfangen werden, rufst Du noch D3D_Render() auf, obwohl das schon gar net mehr da ist.



  • Ok, hab jetzt mal den Aufruf von D3D_Render durch folgendes ersetzt:

    if (D3D_Device)
                    D3D_Render();
    

    Jetzt sollte er wirklich nur rendern, sofern ein gültiges Device vorliegt. Leider treten immer noch die gleichen Probleme auf. Der Message Loop ist übrigens so aufgebaut, dass entweder Windows Nachrichten behandelt werden ODER gerendert wird, niemals beides gleichzeitig.


Anmelden zum Antworten