D3D Performance



  • Ich weiß ich bin nur am fragen, aber bin eben noch n Anfänger 🙂

    Mein jetziges Programm zeigt 2 Polygone im Vollbild, die sich verschieben. Ist also nur zum Testen. Was mich jetz stört ist, dass das Programm fast nichts macht, aber schon nur 74 fps hat. Ich hab ne fps-anzeige eingebaut, die wahrscheinlich auch nomma frames kostet. Aber das kann nich allzuviel sein, weil ich die Bewegung nich gebremst hab und sie mit frameanzeige und ohne ungefähr genauso schnell ist. Gibts da son paar Standarttips, wie man die Geschwindigkeit verbessern kann. Ansonsten wärs nett, wenn ihr euch den Code mal anschauen würdet...

    #include <windows.h>
    #include <d3dx9.h>
    #include <dxerr9.h>
    #include <d3d9.h>
    #include <tchar.h>
    #include <sstream> 
    #include <string> 
    #include <iostream>
    using namespace std;
    
    #define SCREENWIDTH		1024
    #define SCREENHEIGHT	768
    
    HWND hwnd;
    
    LPDIRECT3D9 g_pD3D = NULL;
    LPDIRECT3DDEVICE9 g_pdevice = NULL;
    LPDIRECT3DVERTEXBUFFER9 g_pvb;
    LPD3DXFONT g_pfont;
    
    HRESULT res;
    VOID* pvertices;
    BOOL bewegung=true;
    RECT rfont={50, 50, 200, 200};
    HFONT hfont;
    DWORD old,newt=0, ticks=0;
    
    char buffer[100];
    
    struct vertex
    {
    	float x,y,z,rhw;
    	DWORD color;
    };
    vertex vertices[] =
        {
            { 50.0f,  50.0f, 2.0f, 1.0f, 0xff700000, },
            { 994.0f, 30.0f, 0.5f, 1.0f, 0xff000040, },
            {  30.0f, 738.0f, 0.5f, 1.0f, 0xff000040, },
    		{  974.0f, 718.0f, 0.5f, 1.0f, 0xff007000, },
        };
    
    void init()
    {
    	D3DPRESENT_PARAMETERS presparam;
    	if(FAILED(g_pD3D = Direct3DCreate9( D3D_SDK_VERSION )))MessageBox(hwnd, "DXGetErrorString9(res)", "XXX", MB_OK);
    	ZeroMemory(&presparam,sizeof(D3DPRESENT_PARAMETERS));
    	presparam.BackBufferWidth	= SCREENWIDTH;
    	presparam.BackBufferHeight	= SCREENHEIGHT;
    	presparam.BackBufferFormat	= D3DFMT_A8R8G8B8;
    	presparam.BackBufferCount	= 1;
    	presparam.MultiSampleType=D3DMULTISAMPLE_6_SAMPLES;
    	presparam.MultiSampleQuality=0;
    	presparam.SwapEffect		=D3DSWAPEFFECT_DISCARD;
    	presparam.hDeviceWindow		=NULL;
    	presparam.Windowed			=FALSE;
    	presparam.EnableAutoDepthStencil=FALSE;
    	presparam.AutoDepthStencilFormat=D3DFMT_D32;
    	presparam.Flags				=NULL;
    	presparam.FullScreen_RefreshRateInHz=D3DPRESENT_RATE_DEFAULT;
    	presparam.PresentationInterval=D3DPRESENT_INTERVAL_DEFAULT;
    
    	if(FAILED(res = g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,
    						hwnd,D3DCREATE_HARDWARE_VERTEXPROCESSING,
    							&presparam,&g_pdevice)))
    							MessageBox(hwnd, DXGetErrorString9(res), "XXX", MB_OK);
    
    	g_pdevice->CreateVertexBuffer( sizeof(vertices),0, D3DFVF_XYZRHW|D3DFVF_DIFFUSE,
                                                      D3DPOOL_DEFAULT, &g_pvb, NULL );
    
        g_pvb->Lock( 0, sizeof(vertices), (void**)&pvertices, 0 ); 
        memcpy( pvertices, vertices, sizeof(vertices) );
        g_pvb->Unlock();
    
    	hfont=CreateFont( 0, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, NULL);
    
    	if (FAILED( res = D3DXCreateFont(g_pdevice, hfont, &g_pfont)))
    		MessageBox(hwnd, DXGetErrorString9(res), "CreateFont", MB_OK);
    
    	g_pdevice->SetStreamSource( 0, g_pvb, 0, sizeof(vertex));
        g_pdevice->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE);
    }
    
    LRESULT WINAPI msgproc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
    {
        switch( msg )
    
    	{
            case WM_DESTROY:
                PostQuitMessage( 0 );
                return 0;
    
    		case WM_KEYDOWN:
    			if (wParam == VK_ESCAPE)
    				PostQuitMessage( 0 );
    			return 0;
        }
    
        return DefWindowProc( hWnd, msg, wParam, lParam );
    }
    
    void move()
    {
    	if (vertices[0].x > 70) bewegung=true;
    	if (vertices[0].x < 20) bewegung=false;
    
    	if (bewegung)
    	{
    		vertices[0].x--;
    		vertices[0].y--;
    		vertices[1].x--;
    		vertices[1].y++;
    		vertices[2].x++;
    		vertices[2].y--;
    		vertices[3].x++;
    		vertices[3].y++;
    	} else
    	{
    		vertices[0].x++;
    		vertices[0].y++;
    		vertices[1].x++;
    		vertices[1].y--;
    		vertices[2].x--;
    		vertices[2].y++;
    		vertices[3].x--;
    		vertices[3].y--;
    	}
    
    	g_pvb->Lock( 0, sizeof(vertices), (void**)&pvertices, 0 ); 
        memcpy( pvertices, vertices, sizeof(vertices) );
        g_pvb->Unlock();
    }
    
    void render()
    {
    	g_pdevice->BeginScene();
    	if(FAILED(g_pdevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,63), 1.0f, 0 )))MessageBox(hwnd, "DXGetErrorStdfagring9(res)", "XXX", MB_OK);
    
        g_pdevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
    
    	float test;
    	test =(1.0/((float)newt/(float)ticks/1000.0));
    	sprintf(buffer,"%f fps", test);
    	g_pfont->Begin();
    	if (FAILED(res = g_pfont->DrawText(buffer, -1, &rfont, NULL, D3DCOLOR_XRGB(255,0,0))))
    		MessageBox(hwnd, DXGetErrorString9(res), "XXX", MB_OK);
    	g_pfont->End();
    
    	g_pdevice->EndScene();
    	g_pdevice->Present(NULL,NULL,NULL,NULL);
    }
    
    void cleanup()
    {
    	if(g_pvb!=NULL) g_pvb->Release();
    	if(g_pfont!=NULL) g_pfont->Release();
    	if(g_pdevice!=NULL) g_pdevice->Release();
    	if(g_pD3D!=NULL) g_pD3D->Release();
    }
    
    int WINAPI WinMain(HINSTANCE hinstance,HINSTANCE hprevinstance,LPSTR lpcmdline,int ncmdshow)
    {
    	MSG msg;
    
    	WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, msgproc, 0L, 0L, 
                          GetModuleHandle(NULL), NULL, LoadCursor(NULL, IDC_ARROW), NULL, NULL,
                          "fensterung", NULL };
    	RegisterClassEx(&wc);
    	hwnd=CreateWindow( "fensterung","BLA",WS_POPUPWINDOW,0,0,SCREENWIDTH,SCREENHEIGHT,NULL,NULL,hinstance,NULL);	
    	init();
    	ShowWindow( hwnd, ncmdshow);
    	UpdateWindow(hwnd);
    	while( msg.message!=WM_QUIT )
                {
                    if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
                    {
                        TranslateMessage( &msg );
                        DispatchMessage( &msg );
                    }
                    else
    				{
    					ticks++;
    
    					if (old!=0)
    					newt+=GetTickCount()-old;
    					old=GetTickCount();
    
    					move();
                        render();
    				}
                }
    
    	cleanup();
    	UnregisterClass( "fensterung", hinstance);
    	return 0;
    }
    


  • - V-Sync abschalten (presparam.PresentationInterval)
    - Multisampling abschalten



  • uhhhhhhhh bin ich blöd 🙂 VSync hab ich ganz vergessen. thx 🙂 Multisampling macht bei den 2 Polygonen allerdings kaum n unterschied 🙂

    Jetz hab ich noch ne Frage: Jetzt macht die Schrift n Megaunterschied. Wenn sie aus ist, ist es noch 1000 mal schneller. Ich hab mal im Debugmodus gestartet, und da gibt er immer folgendes aus, wenn die schrift an ist: "Direct3D9: (WARN) :Ignoring redundant SetRenderState 52" Das gibt er pro frame ca 50 mal aus, allerdings immer mit verschiedenen Zahlen. Was bedeutet das und wie kann man es ausschalten???



  • Nur zwei Tris zu messen ist IMHO leicht schwachsinnig. Was für eine Aussage willst du davon ableiten? Und wieviel Tris braucht dann deine fps-Ausgabe? 2? 10? Evtl. noch mehr? IIRC war das Thema aber schon oft genug da.

    Übrigens sind die fps-Messungen im debug Modus auch nicht aussagekräftig. Bei der Zahl könnte ich mir vorstellen, das dies einfach der übergebene Parameter war.

    Bye, TGGC (Der Held ist zurück)



  • Ja das sollte auch kein perfekter Benchmark werden 🙂 Nur hab ich mich eben gewundert, warum ich bei einem so einfachen Programm nur 75 fps hab. Die Sache hat sich ja jetz geklärt, jetz hab ich 2000...

    Das andere Problem macht mir aber Sorgen, da man sehr oft Text ausgeben muss und das nich so viel Performance kosten darf (und so viele Fehler verursachen)...

    Ich habe noch eine kleine Frage: Gibt es etwas genaueres als GetTickCount()? Bei mir gibt das Werte nur in (ungefähr) 16-er Schritten zurück. Deswegen dauert ein Durchgang dann entweder 0 oder 16 Millisekunden, aber mit 0 kann man nicht weiter rechnen und so musste ich das mit einem Durchschnittswert machen. Aber das ist bei weitem keine perfekte Lösung.



  • @olleman

    a) prinzipiell sollte man keine "MANAGED-Buffer" während dem loop "locken/unlocken". das kostet viel performance, weil intern noch ein zweiter puffer existiert, der zur sicherung der daten benötigt wird, und der dann auch geupdateted wird (mehr oder weniger, vermute ich mal). auf jeden fall ist ein managed buffer eine gute sache, nur sollte man ihn nicht wärend dem rendern mit daten füllen.

    b) die Direct3DX-Fonts sind relativ langsam. die performance ist zwar ok, aber meine eigene fontklasse ist ca. 16 mal so schnell, wenn ich den kompletten bildschirm mit einer 8-punkt-font vollzeichne (bei 1280x1024@85 Hz, GF4 ti4600, PIV 1.6). meine fontklasse bringt dann über 800 fps, directx nur ca. 50. die fontklasse selber porgrammieren ist also längerfristig auf jeden fall nötig!



  • @olleman

    a) prinzipiell sollte man keine "MANAGED-Buffer" während dem loop "locken/unlocken". das kostet viel performance, weil intern noch ein zweiter puffer existiert, der zur sicherung der daten benötigt wird, und der dann auch geupdateted wird (mehr oder weniger, vermute ich mal). auf jeden fall ist ein managed buffer eine gute sache, nur sollte man ihn nicht wärend dem rendern mit daten füllen.

    b) die Direct3DX-Fonts sind relativ langsam. die performance ist zwar ok, aber meine eigene fontklasse ist ca. 16 mal so schnell, wenn ich den kompletten bildschirm mit einer 8-punkt-font vollzeichne (bei 1280x1024@85 Hz, GF4 ti4600, PIV 1.6). meine fontklasse bringt dann über 800 fps, directx nur ca. 50. die fontklasse selber porgrammieren ist also längerfristig auf jeden fall nötig!



  • zu a) könntest du das etwas genauer erklären? 🙂 Ich weiß nicht, was ein "MANAGED-Buffer" ist 🙂

    zu b) ok, sowas hab ich schon befürchtet 😕 Werd ich irgendwann mal machen, aber erstmal alles verstehen 🙂



  • Wenn ich das richtig verstanden habe, legt DX, bei benutzung eines managed Buffer, intern eine Sicherheitskopie von dem Buffer an (das würde erklären dass man sich so beim minimieren-maximieren nicht um die Resourcen kümmern muss). Wenn du jetzt diesen Buffer oft locken musst (damit er beschrieben werden kann), speziell beim rendern, kostet das einiges an Performance weil der "Backupbuffer" auch beschrieben werden muss.



  • zu a)

    ich meinte managed-vertex-buffer.

    um dreiecke zu rendern benutzt du ja einen vertexbuffer mit:

    g_pdevice->CreateVertexBuffer( sizeof(vertices),0,D3DFVF_XYZRHW|D3DFVF_DIFFUSE,D3DPOOL_DEFAULT, &g_pvb, NULL );
    

    und für "D3DPOOL_DEFAULT" kann man auch "D3DPOOL_MANAGED" benutzen. falls dann das device verloren geht ("lost device"), dann mann mus diese puffer nicht updaten, wie das bei normalen der fall ist. wenn du z.b. ALT+TAb drückst und deine device verloren geht, dann musst die vertexbuffer ohne "MANAGED" neuerstellen und neu initialisieren, also mit daten füttern. standardmäßig benutze ich so gut wie immer MANAGED ausser es macht keinen sinn, und dass kann ich dir auch nur empfehlen. aber wenn du MANAGED-Buffer benutzt, dann auf gar keinen fall während der laufzeit "locken/unlocken", weil das updaten des buffers dann sehr lange dauert (relativ gesehen).

    c) "Timer"

    hier ist meine timer klasse. der performance-timer misst normalerweise im bereich von 1/100000 sekunde bis 1/2000000 sekunde je nach system (ich hab noch nie größere oder kleinere werte gesehen). das reicht locker aus um eine millisekunde hyperexakt zu messen.

    #pragma once
    
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    
    class CTimer
    {
    public:
    	CTimer(void);
    	~CTimer(void);
    
    public:
    	void Update(void); //Aktualisiert den Timer
    	void Reset(void);  //Benutzt den letzten Wert von Update als neuen Startwert
    
    public:
    	void GetHours(unsigned long *hours);               //Gibt die Anzahl der Stunden zurück, die bis zum letzten Aufruf von Update verstrichen sind
    	void GetMinutes(unsigned long *minutes);           //Gibt die Anzahl der Minuten zurück, die bis zum letzten Aufruf von Update verstrichen sind
    	void GetSeconds(unsigned long *seconds);           //Gibt die Anzahl der Sekunden zurück, die bis zum letzten Aufruf von Update verstrichen sind
    	void GetMilliSeconds(unsigned long *milliSeconds); //Gibt die Anzahl der Millisekunden zurück, die bis zum letzten Aufruf von Update verstrichen sind
    
    private:
    	LARGE_INTEGER Frequency; //Frequenz
    	LARGE_INTEGER Start;     //Startwert
    	LARGE_INTEGER Delta;     //Deltawert
    };
    
    #include "CTimer.h"
    
    CTimer::CTimer(void)
    {
    	QueryPerformanceFrequency(&Frequency);
    	QueryPerformanceCounter(&Start);
    	Delta.QuadPart=0;
    }
    
    CTimer::~CTimer(void)
    {
    }
    
    void CTimer::Update(void)
    {
    	LARGE_INTEGER end;
    
    	QueryPerformanceCounter(&end);
    	Delta.QuadPart=end.QuadPart-Start.QuadPart;
    }
    
    void CTimer::Reset(void)
    {
    	Start.QuadPart=Start.QuadPart+Delta.QuadPart;
    	Delta.QuadPart=0;
    }
    
    void CTimer::GetHours(unsigned long *hours)
    {
    	LONGLONG result;
    
    	result=Delta.QuadPart/(Frequency.QuadPart*3600);
    	if (result>UNSIGNEDLONG_MAX) result=UNSIGNEDLONG_MAX;
    
    	(*hours)=(unsigned long)result;
    }
    
    void CTimer::GetMinutes(unsigned long *minutes)
    {
    	LONGLONG result;
    
    	result=Delta.QuadPart/(Frequency.QuadPart*60);
    	if (result>UNSIGNEDLONG_MAX) result=UNSIGNEDLONG_MAX;
    
    	(*minutes)=(unsigned long)result;
    }
    
    void CTimer::GetSeconds(unsigned long *seconds)
    {
    	LONGLONG result;
    
    	result=Delta.QuadPart/Frequency.QuadPart;
    	if (result>UNSIGNEDLONG_MAX) result=UNSIGNEDLONG_MAX;
    
    	(*seconds)=(unsigned long)result;
    }
    
    void CTimer::GetMilliSeconds(unsigned long *milliSeconds)
    {
    	LONGLONG result;
    
    	result=(Delta.QuadPart*1000)/Frequency.QuadPart;
    	if (result>UNSIGNEDLONG_MAX) result=UNSIGNEDLONG_MAX;
    
    	(*milliSeconds)=(unsigned long)result;
    }
    


  • KXII schrieb:

    die Direct3DX-Fonts sind relativ langsam. die performance ist zwar ok, aber meine eigene fontklasse ist ca. 16 mal so schnell, wenn ich den kompletten bildschirm mit einer 8-punkt-font vollzeichne (bei 1280x1024@85 Hz, GF4 ti4600, PIV 1.6). meine fontklasse bringt dann über 800 fps, directx nur ca. 50. die fontklasse selber porgrammieren ist also längerfristig auf jeden fall nötig!

    Hast du den Vergleich evtl. auch noch gegen CD3DFont gemacht? Würde mich mal interessieren, da ich diese Art von Font eigentlich immer für recht sinnvoll hielt.

    Bye, TGGC (Der Held ist zurück)



  • mir ist gerade aufgefallen, das sich mein beitrag auf die font-klasse aus den sdk beispielen bezieht, also die CD3DFont-Klasse, und nicht die D3DXFONT font klasse aus den directx 9 extensions. allerdings hatte ich auch mal einen performance test mit der D3DXFONT, und falls ich mich noch richtig erinnere, dann war die ein bischen langsamer als die CD3DFont font klasse, insbesondere bei dynamischen strings, die also jeden frame einen anderen inhalt haben.
    beide klassen sind nicht schlecht, aber wenn man viel text darstellt, dann sind sie zu langsam. einen weiteren nachteil sehe ich auch in der flexibilität, denn man kann nur die original fonts verwenden, so wie sie in der ttf-datei definiert sind. man kann also keine benutzerdefinierten schriften benutzen (bunt, vorgerendert, mit glanz effekten, ... so wie in den alten spielen oder die diablo schrift (vom aussehen her)).



  • @KXII
    Weil wir grad beim Thema Timer sind. Ich hab ebenfalls eine Timer Klasse, welche intern die High Resolution Timer benutzt. Macht es eigentlich Sinn die Rückgabewerte von QueryPerformanceFrequency und QueryPerformanceCounter auszuwerten? Gibt es überhaupt noch moderne Rechner, die diese Timer nicht haben oder ist das mittlerweile standardmässig vorgeschrieben?



  • keine ahnung ob das standardmäßig vorgeschrieben ist, ich glaube aber nicht.

    ich werte die ergebnisse nicht aus, weils mir zu mühseelig ist, und weil ich bis jetzt noch keinen rechner gesehen habe, der den timer nicht unterstützt. selbst mein alter AMD mit 400 MHZ auf einem NONAME-Board unterstützt ihn, und das board ist schon ein paar jahre alt...

    falls mein programm auf nem 486er (-board) nicht laufen sollte ist mir das eigentlich egal, abgesehen davon, das so ein rechner sowieso nicht genug performance hat, um meine programme auszuführen.

    ich habe mich in den letzten jahren intensiv mit errorhandling beschäftigt, und ich bin zu dem entschluss gekommen, daß zu viel errorhandling keinen sinn macht, bzw. viel zu umständlich ist. wenn man schnell vorankommen will, sollte man sich nur um die wirklich wichtigen sachen kümmern, wie z.b. "file not found" oder "could not create directx-device". so sachen wie "out of memory", "timer-error" oder "file-write-error" treten zu 99.9% sowieso nicht auf, und sie abzufragen macht den quellcode nur unübersichtlich, und umständlich in der handhabung. ausserdem ist dann ab einer bestimmten "programmtiefe" jede zweite zeile eine errorabfrage, die in 99.9 % aller fälle negativ ist.

    ausserdem ist das auch gängige praxis (je nach software natürlich). das spiel "need for speed underground (PC)" z.b. kümmert sich so gut wie gar nicht um errorhandling. wenn man dort dateien einfach löscht, dann startet das spiel trotzdem nur das die entsprechenden sachen nicht angezeigt werden 😮 , oder die ogre engine, die hat auch an vielen stellen kein errorhandling (exceptions gibts es zwar, aber nicht für alle ereignisse, wie z.b. den timer oder new's)



  • Ich werte den Errorcode immer aus, einfach weil ich mal eine Timer Klasse geschrieben habe, die das immer für mich übernimmt. Diese hat dann einen Fallback der mit timeGetTime() arbeitet. Und so halte ich das auch für vernünftig.

    Das NSFU Beispiel ist in dem Fall wohl etwas schlecht gewählt. Denn wenn man ein File lädt und die Datei nicht das ist, bekommt man eigentlich einen Nullpointer und damit Access Violations. Dieser Fehler wird offensichtlich abgefangen. Würde das Spiel in diesem Moment einfach abstürzen, dann könnte man behaupten, das dieser Fehler nicht beachtet wird.

    Bye, TGGC (Der Held ist zurück)



  • @tggc

    obwohl wir beide nicht wissen können wie NFSU programmiert ist, bezweifle ich das das fehlen einer datei automatisch zu einem nullpointer führt 🙂 (du meinst bestimmt die alten c routinen...). wahrscheinlicher ist, daß die klasse (ich machs mal simpel, z.b. eine auto-klasse) eine Load funktion hat, und diese schon in den ersten zeilen erkennt das die datei nicht da ist (INALID_HANDLE_VALUE oder NULL wenn man noch die alten c routinen nutzt), und sich dann einfach beendet ohne was zu machen. das spiel "nutzt" dann die autoklasse ganz normal mit move" und "draw" nur da intern keine daten exisitieren passiert nichts.

    mir geht es nicht darum fehler prinzipiell unbeachtet zu lassen. mann kann ja schon fehler testen, und wenn sie eintreten, dann ignoriert man sie einfach indem die funktion/klasse nichts macht (die klasse einfach uninitailisiert lässt). z.B.:

    newBuffer=new char[size];
    
    if (newBuffer!=NULL)
    {
      hier ist alles ok
      wenn nicht genug speicher da ist, dann passiert halt nichts, aber wenn kümmerts, da der fall ja sowieso nie eintritt
    }
    

    oder

    void Auto::Load(void)
    {
      if (FileOpen()==true)
      {
        LoadDataFromFile(.......);
    
        AutoInitialized=true;
      }
    }
    
    void Auto::Draw(void)
    {
      if (AutoInitialized==true)
      {
        DirectX->DrawMyCar(); //falls die "auto-datei" in load nicht gefunden wurde, dann wird einfach nichts dargestellt
      }
    }
    

    mann könnte jetzt jeder funktion die mit new speicher reserviert oder jeder funktion die die timer klasse benutzt einen bool-rückgabewert geben der einen fehler anzeigt. aber schreib dir dann mal eine 500-1000 zeilen lange ladefunktion für dateien (3d-mesh oder sowas) die jede kleinigkeit berücksichtigt und darauf reagiert. dann ist der komplette code der laderoutine voller fehlerabfragen und mann sieht den ladecode vor lauter code nicht mehr. sowas finde ich sinnlos, es reicht die wichtigen sachen abzufragen und den rest ignoriert man. ich will ja niemanden überreden. aber ich selbst programmiere jetzt schon ne weile so, und ich komme viel schneller voran und meine programme wurden dadurch nicht spürbar instabilier/fehlerhafter, und vorallem ist der code viel übersichtlicher, weil ich jetzt nur noch 1% fehlerabfragen habe.


  • Mod

    KXII schrieb:

    @tggc

    obwohl wir beide nicht wissen können wie NFSU programmiert ist, bezweifle ich das das fehlen einer datei automatisch zu einem nullpointer führt 🙂 (du meinst bestimmt die alten c routinen...). wahrscheinlicher ist, daß die klasse (ich machs mal simpel, z.b. eine auto-klasse) eine Load funktion hat, und diese schon in den ersten zeilen erkennt das die datei nicht da ist (INALID_HANDLE_VALUE oder NULL wenn man noch die alten c routinen nutzt), und sich dann einfach beendet ohne was zu machen. das spiel "nutzt" dann die autoklasse ganz normal mit move" und "draw" nur da intern keine daten exisitieren passiert nichts.

    noch viel wahrscheinlicher ist, dass es einen resourcemanager gibt und wenn man auf eine datei zugreifen möchte die nicht vorhanden ist, dann wirft dieser eine exception. wenn auf diese datei zugegriffen wird und es entsteht innerhalb der ladefunktion ein fehler, dann schmeisst man ebenfalls eine exception. falls ein fehler vorhanden ist (z.b. die datei ist 0 lang) und man hat keine fehlerbehandlung bzw behandelt nicht diesen fehler, dann wird das programm irgendwo später hochfliegen ohne dass man genau weiß an welchem eingeschlichenem bug das genau liegt.

    wenn ein fehler auftratt, dann so zu tun als wäre nichts passiert ist ziemlich gefährlich, weil es folgefehler geben kann und am ende hast du ein spiel das nach 20h spiel plötzlich abpfeift und alle savegames dabei löscht... schwer reproduzierbar... und das liegt vielleicht nur daran weil auf der cd irgendwo ein kleiner 'flip im bit' vorgekommen ist den du in der laderoutine vielleicht bemerkt, aber die ganze zeit nicht als fehler behandelt hast.

    rapso->greeets();



  • @KXII: Leider konnte ich es hier zu spät im Forum posten, aber den Timer habe ich jetzt schon hinbekommen. Wie ich jetzt sehe, sogar richtig (Mit QueryPerformanceCounter) 🙂
    Aber bei meinem Rechner (P4 2,8) Hat der ne Frequenz von 3579545 😉



  • @rapso

    ob es wahrscheinlicher ist, das exeptions geworfen werden bezeifle ich, da das spiel einfach weiter läuft und keinerlei meldungen ausgiebt. warum sollte das es dann überhaupt expetions werfen? (eine diskussion darüber wie und wann es intern was macht (ob mit exceptions oder ohne) ist aber sowieso sinnlos)

    ich vermute, das dieser effekt hauptsählich dadurch entseht, da das spiel "ursprünglich" für die ps2 geschrieben wurde und dort viele fehlerabfragen unötig sind, weil man z.b. immer genau weiß wielviel ram man hat (...).

    deine annahme, das ein programm, welches einige "fehler" nicht behandelt, irgendwann hochfliegt (nach 20h oder so oder wegen falschen bits auf der cd) ist für mich nachvollziehbar, aber nur wenn man sehr schlecht programmiert, und die fehler tatsächlich so betractet werden, als ob sie nie eintreten würden. ausserdem, wie gesagt, meine ich nicht alle, sondern nur die unötigen, sollte man weglassen/ignorieren (nicht ganz ignorieren, sondern nur nichts machen und alles auf default stellen, oder sowas)

    wenn eine datei die länge 0 hat, dann kann man natürlich, wenn man will, ein programm so schreiben, das es abstürtzt, aber wenn man ordentlich programmiert dann sollte das kein problem sein und nie zu fehlern führen. auch falsche werte auf einer cd sind kein problem. man kann ja einfach werte die man ließt auf ihr gültigkeit prüfen und falls sie falsch sind dann einfach die klasse als "not initialized" makieren. ich weiß nicht was daran schwer sein sollte, dies im programmcode umzusetzen. folgefehler sehe ich dann wirklich keine, denn eine gekapselete klasse die load, move und draw hat, macht dann halt einfach nichts und liefert unter umständen für position oder winkel immer 0 zurück egal was man mit dem auto macht. egal ob "load" fehler hat oder sonst was.

    mir ist ein spiel, das bei "fehlern" falsche darstellungen produziert (wie z.b. ein auto das sich nicht bewegt oder nicht sichtbar ist) lieber, als ein programm, das bei jeder kleinigkeit eine msgbox aufpoppt, und dessen programmcode übertrieben viel komplexer und schwerer zu lesen und viel länger und umständlicher zu entwickeln ist. wie gesagt, das weglassen von fehlerbehandlung zwingt nicht automatisch zu folgefehlern, sofern man immer alles überprüft (auch wenn man dann nichts macht, was dazu führt, das es dem benutzer mitgeteilt wird)) und für die ganz wichtigen sachen kann man ja fehlerbehandlung einbauen, sei es mit exceptions oder ohne.

    falls du dem nicht zustimmen kannst dann bring ein konkretes beispiel, wo man erkennen kann, das "leichte" fehler, mit einer "ignorierenfehlerbehandlung" so wie ich es meine, zu nichtreproduzierbaren fehlern führen und das programm zum abturz bringen kann.



  • @olleman

    wieso aber? das ist doch extremgenau!

    der timer kann die zeit auf eine MIKROSEKUNDE (1/1.000.000) sehr genau messen und das reicht für alles aus!


Anmelden zum Antworten