Geschwindigkeitsvergleich DirectX - SFML: Komische Ergebnisse



  • Ich hab mal die Geschwindigkeit der SFML mit der von DirectX verglichen. Dabei hab ich folgendes gemacht:

    Es wurden zwei Bilder geladen und auf einem Fenster ausgegeben: Ein Hintergrundbild mit 320 x 240 Pixeln und eine Figur, 16 x 32 Pixel. Beide Bilder hatten eine Farbtiefe von 24 Bit.

    Wenn man nun einen Knopf drückt, läuft die Figur automatisch von links nach rechts. Die Zeit, die sie braucht, wird gemessen. Es wird hier nicht mit Frames oder Timern oder sonstwelchen Begrenzern gearbeitet, sondern in der while-Schleife (die, die in DirectX PeekMessage beinhaltet) wird einfach die Ausgabe getätigt, um zu gucken, wie lange dafür gebraucht wird.

    Das ganze wird in zweifacher Weise durchgeführt: Einmal mit der normalen Größe und einmal als gestrecktes Bild in einem maximierten Fenster auf einem Bildschirm mit einer Auflösung von 1280 x 1024 Pixeln.

    Und dann wurde das ganze auf zwei verschiedenen Computer durchgeführt: Einmal ein PC mit 700 Mhz. Und einmal einer mit 4 x 2,33 GHz.

    O.k., hier sind die Ergebnisse, wieviele Millisekunden das gedauert hat:

    |4 x 2,33 GHz|700 MHz
    -------------+------------+-------
    DirectX klein|   31-47    |  1833
    -------------+------------+-------
    SFML klein   |   47-63    |   301
    -------------+------------+-------
    DirectX groß |    187     |  2844
    -------------+------------+-------
    SFML groß    |    625     |  3304
    

    Auf dem Quadcore sieht alles normal aus. DirectX ist schneller als SFML und bei einer riesigen Ausgabe zieht sich das ganze entsprechend hin. Aber was mir etwas Sorgen bereitet, ist der kleine DirectX-Wert beim alten PC: Wie kann es sein, daß eine simple Anwendung mit einer Grafikausgabe von 320 x 240 Pixeln, wo nichtmal was gestreckt, sondern alles in Originalgröße ausgegeben wird, so lange dauert?

    Auch würde mich mal interessieren, wieso SFML, SDL etc. nicht mit DirectX mithalten können. Ich dachte, die wrappen nur ein jeweils anderes Framework. SDL zum Beispiel arbeitet in Windows mit DirectX. Und SFML hat, glaub ich, OpenGL in der Implementierung. Aber trotzdem ist DirectX ungefähr doppelt so schnell als SFML und viel, viel, viel schneller als SDL. Woran liegt das? (In meinem Beispiel vergrößert DirectX sogar mit bilinearem Filter und SFML nur mit dem normalen Nearest Neighbor-Algorithmus.)



  • Hm.
    Gute Frage.
    Hätte mir erwartet dass es keinen bis kaum einen Unterschied gibt.
    Die neue SDL Version rendert ja angeblich auch mit Hardware-Support...
    Und SFML sowieso.

    Kannst du das Testprojekt (Source + Datenfiles) irgendwo uppen?

    p.S.: es macht natürlich einen Unterschied wo die Texturen liegen, also z.B. local-video-memory vs. nonlocal. Bei OpenGL vermutlich auch welche Extension verwendet wird. Bzw. unter Windows 7 ist man ja der "OpenGL via Direct3D" Implementierung von MS ausgeliefert soweit ich weiss.

    Müsste man genauer nachforschen was SFML/SDL da genau verwenden. Kannst ja mal reindebuggen 😉



  • Also ich hab bei meinen Messungen (jeweils relevanten Aufloesungen und Objektzahlen) festgestellt das eine DX basierte Lib 5x so schnell wie SFML war. Also SFML ist extrem langsam.

    Deine Anwendungen sind womoeglich in einigen Situationen in andere Begrenzungen hineingelaufen, zwei Objekte sind einfach kein sinnvoller Test, da hier schon ein versehentliches Bildschirmloeschen z.b. eine Halbierung der Geschwindigkeit (es werden ploetzlich doppelt soviel Pixel geschrieben) bedeuten kann, oder sich eine Stelle, die einfach nur etwas in der Art Sleep(1) macht ueberproportionale Auswirkungen hat. f'`8k

    Autocogito

    Gruß, TGGC (Was Gamestar sagt...)



  • hustbaer schrieb:

    Kannst du das Testprojekt (Source + Datenfiles) irgendwo uppen?

    Ich kann die Quellcodes hier posten, das ist nicht so viel. Als Dateien reichen zwei beliebige Bitmaps: "Hintergrund.bmp" mit 320 x 240 Pixeln und "Sprite.bmp" mit 16 x 32 Pixeln.

    Das hier ist das SFML-Projekt:

    #include <iostream>
    #include <sstream>
    #include <SFML/Graphics.hpp>
    
    using namespace std;
    
    int main()
    {
    	sf::Image backgroundImage, spriteImage;
    	sf::Sprite background, sprite;
    
    	backgroundImage.LoadFromFile("Hintergrund.bmp");
    	backgroundImage.SetSmooth(false);
    	background.SetImage(backgroundImage);
    
    	spriteImage.LoadFromFile("Sprite.bmp");
    	spriteImage.SetSmooth(false);
    	sprite.SetImage(spriteImage);
    
    	sf::RenderWindow screen(sf::VideoMode(backgroundImage.GetWidth(), backgroundImage.GetHeight(), 32), "SFML-Test");
    
    	clock_t start = 0, stop = 0;
    
    	while (screen.IsOpened())
    	{
    		sf::Event event;
    
    		while (screen.GetEvent(event))
    		{
    			if (event.Type == sf::Event::Closed)
    				screen.Close();
    		}
    
    		if (start != 0 || screen.GetInput().IsKeyDown(sf::Key::Right))
    		{
    			if (start == 0)
    				start = clock();
    
    			sprite.Move(1, 0);
    
    			if (sprite.GetPosition().x == backgroundImage.GetWidth() - spriteImage.GetWidth())
    				stop = clock();
    		}
    
    		screen.Draw(background);
    		screen.Draw(sprite);
    		screen.Display();
    
    		if (stop != 0)
    		{
    			stringstream stream;
    			stream << (stop - start) * 1000 / CLOCKS_PER_SEC << " Millisekunden";
    
    			MessageBoxA(NULL, stream.str().c_str(), "Dauer", MB_OK);
    
    			start = 0;
    			stop = 0;
    
    			sprite.SetPosition(0, 0);
    		}
    	}
    }
    

    Das DirectX-Projekt ist etwas unsauber, da ich mich nicht tiefergehend mit DirectX beschäftigt habe. Ich hab einfach ein Beispielprojekt aus dem SDK genommen und entsprechend umgeschrieben:

    #define NAME "Stretch"
    #define TITLE "Stretch"
    
    #define WIN32_LEAN_AND_MEAN
    #include <ctime>
    #include <sstream>
    #include <windows.h>
    #include <windowsx.h>
    #include <ddraw.h>
    #include "resource.h"
    #include "ddutil.h"
    
    using namespace std;
    
    int X = 0;
    bool Go = false;
    
    #define SIZEX   320
    #define SIZEY   240
    char *szBackground = "BACKGROUND";
    char *szBackBuffer = "BACK_BUFFER";
    char *szSprite = "SPRITE";
    
    HWND            hwnd;
    HPALETTE        HPal = NULL;
    
    LPDIRECTDRAW            lpDD;
    
    LPDIRECTDRAWSURFACE     lpDDSPrimary;
    LPDIRECTDRAWSURFACE     lpDDSBackBuffer;
    LPDIRECTDRAWSURFACE     lpDDSBackground;
    LPDIRECTDRAWSURFACE     lpDDSSprite;
    
    LPDIRECTDRAWCLIPPER     lpClipper;
    LPDIRECTDRAWPALETTE     lpDDPal;
    BOOL                    bActive;
    
    static clock_t Start = 0, Stop = 0;
    
    BOOL restoreAll( void )
    {
        return lpDDSPrimary->Restore() == DD_OK &&
               lpDDSBackBuffer->Restore() == DD_OK &&
               lpDDSBackground->Restore() == DD_OK &&
               lpDDSSprite->Restore() == DD_OK &&
               DDReLoadBitmap(lpDDSBackBuffer, szBackBuffer) == DD_OK &&
               DDReLoadBitmap(lpDDSBackground, szBackground) == DD_OK &&
               DDReLoadBitmap(lpDDSSprite, szSprite) == DD_OK;
    
    }
    
    void updateFrame( void )
    {
        static BOOL         haveBackground = FALSE;
        RECT                destRect;
        HRESULT             ddrval;
        POINT               pt;
    
        GetClientRect( hwnd, &destRect );
    
        pt.x = pt.y = 0;
        ClientToScreen( hwnd, &pt );
        OffsetRect(&destRect, pt.x, pt.y);
    
    	RECT backgroundRect;
    	backgroundRect.left = 0;
    	backgroundRect.right = 320;
    	backgroundRect.top = 0;
    	backgroundRect.bottom = 240;
    
    	RECT spriteRect;
    	spriteRect.left = 0 + X;
    	spriteRect.right = 16 + X;
    	spriteRect.top = 0;
    	spriteRect.bottom = 32;
    
        while( 1 )
        {
            ddrval =
    		lpDDSBackBuffer->Blt(&backgroundRect, lpDDSBackground, NULL, 0, NULL) &
    		lpDDSBackBuffer->Blt(&spriteRect,     lpDDSSprite,     NULL, 0, NULL) &
    		lpDDSPrimary   ->Blt(&destRect,       lpDDSBackBuffer, NULL, 0, NULL);
    
    		if (Go)
    			X++;
    
    		if (X == 320 - 16)
    		{
    			Stop = clock();
    
    			stringstream stream;
    			stream << (Stop - Start) * 1000 / CLOCKS_PER_SEC << " Millisekunden";
    
    			MessageBoxA(NULL, stream.str().c_str(), "Dauer", MB_OK);
    
    			X = 0;
    
    			Go = false;
    			Start = 0;
    			Stop = 0;
    		}
    
            if( ddrval == DD_OK )
            {
                break;
            }
            if( ddrval == DDERR_SURFACELOST )
            {
                if(!restoreAll())
                {
                    return;
                }
            }
            if( ddrval != DDERR_WASSTILLDRAWING )
            {
                return;
            }
        }
        if(ddrval != DD_OK)
        {
            return;
        }
    }
    
    static void finiObjects( void )
    {
        if( lpDD != NULL )
        {
            if( lpDDSPrimary != NULL )
            {
                lpDDSPrimary->Release();
                lpDDSPrimary = NULL;
            }
            if( lpDDSBackBuffer != NULL )
            {
                lpDDSBackBuffer->Release();
                lpDDSBackBuffer = NULL;
            }
            if( lpDDSBackground != NULL )
            {
                lpDDSBackground->Release();
                lpDDSBackground = NULL;
            }
            if( lpDDSSprite != NULL )
            {
                lpDDSSprite->Release();
                lpDDSSprite = NULL;
            }
            if( lpDDPal != NULL )
            {
                lpDDPal->Release();
                lpDDPal = NULL;
            }
            lpDD->Release();
            lpDD = NULL;
        }
    }
    
    long FAR PASCAL WindowProc( HWND hWnd, UINT message, 
                                WPARAM wParam, LPARAM lParam )
    {
        RECT  rc;
    
        switch( message )
        {
        case WM_ACTIVATEAPP:
            bActive = wParam;
            break;
    
        case WM_PALETTECHANGED:
            if ((HWND)wParam == hWnd)
                break;
            // fall through to WM_QUERYNEWPALETTE
        case WM_QUERYNEWPALETTE:
            if (lpDDPal)
            {
                lpDDSPrimary->SetPalette(lpDDPal);
            }
            DDReLoadBitmap(lpDDSBackBuffer, szBackBuffer);
            DDReLoadBitmap(lpDDSBackground, szBackground);
            DDReLoadBitmap(lpDDSSprite, szSprite);
            break;
    
        case WM_CREATE:
            break;
    
        case WM_GETMINMAXINFO:
            ((MINMAXINFO*)lParam)->ptMinTrackSize.x = SIZEX;
            ((MINMAXINFO*)lParam)->ptMinTrackSize.y = SIZEY;
            return 0;
    
        case WM_KEYDOWN:
            switch( wParam )
            {
            case VK_ESCAPE:
            case VK_F12:
                PostMessage(hWnd, WM_CLOSE, 0, 0);
                break;
    
    		case VK_RIGHT:
    			Start = clock();
    			Go = true;
            }
            break;
    
        case WM_DESTROY:
            finiObjects();
            PostQuitMessage( 0 );
            break;
    
        case WM_COMMAND: 
            switch(LOWORD(wParam))
            {
                case ID_SIZE_1X1: SetRect(&rc, 0, 0, SIZEX*1, SIZEY*1); goto size_me;
    size_me:
                    AdjustWindowRectEx(&rc, GetWindowLong(hWnd, GWL_STYLE),
                        GetMenu(hWnd) != NULL, GetWindowLong(hWnd, GWL_EXSTYLE));
                    SetWindowPos(hWnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
                        SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);
                    break;
                } 
                break;
        }
    
        return DefWindowProc(hWnd, message, wParam, lParam);
    
    }
    
    BOOL initFail( HWND hwnd )
    {
        finiObjects();
        MessageBox( hwnd, "DirectDraw Init FAILED", TITLE, MB_OK );
        DestroyWindow( hwnd );
        return FALSE;
    
    }
    
    static BOOL doInit( HINSTANCE hInstance, int nCmdShow )
    {
        WNDCLASS            wc;
        DDSURFACEDESC       ddsd;
        HRESULT             ddrval;
    
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WindowProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon( hInstance, IDI_APPLICATION );
        wc.hCursor = LoadCursor( NULL, IDC_ARROW );
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        wc.lpszMenuName = NULL;
        wc.lpszClassName = NAME;
        RegisterClass( &wc );
    
        hwnd = CreateWindowEx(
            0,
            NAME,
            TITLE,
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            128,
            128,
            NULL,
            NULL,
            hInstance,
            NULL );
    
        if( !hwnd )
        {
            return FALSE;
        }
    
        PostMessage(hwnd, WM_COMMAND, ID_SIZE_1X1, 0);
    
        ShowWindow( hwnd, nCmdShow );
        UpdateWindow( hwnd );
    
        ddrval = DirectDrawCreate( NULL, &lpDD, NULL );
        if( ddrval != DD_OK )
        {
            return initFail(hwnd);
        }
        ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_NORMAL );
    
        ddsd.dwSize = sizeof( ddsd );
        ddsd.dwFlags = DDSD_CAPS;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
    
        ddrval = lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL );
        if( ddrval != DD_OK )
        {
            return initFail(hwnd);
        }
    
        ddrval = lpDD->CreateClipper( 0, &lpClipper, NULL );
        if( ddrval != DD_OK )
        {
            return initFail(hwnd);
        }
    
        ddrval = lpClipper->SetHWnd( 0, hwnd );
        if( ddrval != DD_OK )
        {
            return initFail(hwnd);
        }
    
        ddrval = lpDDSPrimary->SetClipper( lpClipper );
        if( ddrval != DD_OK )
        {
            return initFail(hwnd);
        }
    
        lpDDPal = DDLoadPalette(lpDD, szBackBuffer);
    
        if (lpDDPal)
            lpDDSPrimary->SetPalette(lpDDPal);
    
        lpDDSBackBuffer = DDLoadBitmap(lpDD, szBackBuffer, 0, 0);
        lpDDSBackground = DDLoadBitmap(lpDD, szBackground, 0, 0);
        lpDDSSprite = DDLoadBitmap(lpDD, szSprite, 0, 0);
    
        if( lpDDSBackBuffer == NULL || lpDDSBackground == NULL || lpDDSSprite == NULL )
        {
            return initFail(hwnd);
        }
    
        return TRUE;
    }
    
    int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                            LPSTR lpCmdLine, int nCmdShow)
    {
        MSG         msg;
    
        if( !doInit( hInstance, nCmdShow ) )
        {
            return FALSE;
        }
    
        while( 1 )
        {
            if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
            {
                if( !GetMessage( &msg, NULL, 0, 0 ) )
                    return msg.wParam;
                TranslateMessage(&msg); 
                DispatchMessage(&msg);
            }
            else
            {
                updateFrame();
            }
        }
    }
    

    Zusätzlich dazu braucht man noch eine Datei "Backbuffer.bmp", die auch 320 x 240 Pixel groß ist. Die hab ich nur deshalb erstellt, damit ich die DirectDraw Surface des Back-Buffers analog zu den anderen erstellen kann. Ich hatte keine Lust, erstmal zu gucken, wie man eine leere Surface erstellt, also wurde hier auch einfach eine Datei geladen, die ja dann beim Blitten sowieso übermalt wird.
    O.k., dann braucht man noch die Ressourcendateien:

    #ifndef RESOURCE_H
    #define RESOURCE_H
    
    #define ID_SIZE_1X1 40002
    
    #endif
    
    #include "resource.h"
    
    BACKGROUND   BITMAP Hintergrund.bmp
    BACK_BUFFER   BITMAP Backbuffer.bmp
    SPRITE  BITMAP Sprite.bmp
    

    Und zum Schluß zwei Dateien "ddutil.h" und "ddutil.cpp":

    /*==========================================================================
     *
     *  Copyright (C) 1995 Microsoft Corporation. All Rights Reserved.
     *
     *  File:       ddutil.cpp
     *  Content:    Routines for loading bitmap and palettes from resources
     *
     ***************************************************************************/
    
    #ifdef __cplusplus
    extern "C" {            /* Assume C declarations for C++ */
    #endif  /* __cplusplus */
    
    extern IDirectDrawPalette * DDLoadPalette(IDirectDraw *pdd, LPCSTR szBitmap);
    extern IDirectDrawSurface * DDLoadBitmap(IDirectDraw *pdd, LPCSTR szBitmap, int dx, int dy);
    extern HRESULT              DDReLoadBitmap(IDirectDrawSurface *pdds, LPCSTR szBitmap);
    extern HRESULT              DDCopyBitmap(IDirectDrawSurface *pdds, HBITMAP hbm, int x, int y, int dx, int dy);
    extern DWORD                DDColorMatch(IDirectDrawSurface *pdds, COLORREF rgb);
    extern HRESULT              DDSetColorKey(IDirectDrawSurface *pdds, COLORREF rgb);
    
    #ifdef __cplusplus
    }
    #endif  /* __cplusplus */
    
    /*==========================================================================
     *
     *  Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
     *
     *  File:       ddutil.cpp
     *  Content:    Routines for loading bitmap and palettes from resources
     *
     ***************************************************************************/
    #undef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <windowsx.h>
    #include <ddraw.h>
    #include "ddutil.h"
    
    /*
     *  DDLoadBitmap
     *
     *  create a DirectDrawSurface from a bitmap resource.
     *
     */
    extern "C" IDirectDrawSurface * DDLoadBitmap(IDirectDraw *pdd, LPCSTR szBitmap, int dx, int dy)
    {
        HBITMAP             hbm;
        BITMAP              bm;
        DDSURFACEDESC       ddsd;
        IDirectDrawSurface *pdds;
    
        //
        //  try to load the bitmap as a resource, if that fails, try it as a file
        //
        hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), szBitmap, IMAGE_BITMAP, dx, dy, LR_CREATEDIBSECTION);
    
        if (hbm == NULL)
            hbm = (HBITMAP)LoadImage(NULL, szBitmap, IMAGE_BITMAP, dx, dy, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
    
        if (hbm == NULL)
            return NULL;
    
        //
        // get size of the bitmap
        //
        GetObject(hbm, sizeof(bm), &bm);      // get size of bitmap
    
        //
        // create a DirectDrawSurface for this bitmap
        //
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;
        ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
        ddsd.dwWidth = bm.bmWidth;
        ddsd.dwHeight = bm.bmHeight;
    
        if (pdd->CreateSurface(&ddsd, &pdds, NULL) != DD_OK)
            return NULL;
    
        DDCopyBitmap(pdds, hbm, 0, 0, 0, 0);
    
        DeleteObject(hbm);
    
        return pdds;
    }
    
    /*
     *  DDReLoadBitmap
     *
     *  load a bitmap from a file or resource into a directdraw surface.
     *  normaly used to re-load a surface after a restore.
     *
     */
    HRESULT DDReLoadBitmap(IDirectDrawSurface *pdds, LPCSTR szBitmap)
    {
        HBITMAP             hbm;
        HRESULT             hr;
    
        //
        //  try to load the bitmap as a resource, if that fails, try it as a file
        //
        hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), szBitmap, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
    
        if (hbm == NULL)
            hbm = (HBITMAP)LoadImage(NULL, szBitmap, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
    
        if (hbm == NULL)
        {
            OutputDebugString("handle is null\n");
            return E_FAIL;
        }
    
        hr = DDCopyBitmap(pdds, hbm, 0, 0, 0, 0);
        if (hr != DD_OK)
        {
            OutputDebugString("ddcopybitmap failed\n");
        }
    
        DeleteObject(hbm);
        return hr;
    }
    
    /*
     *  DDCopyBitmap
     *
     *  draw a bitmap into a DirectDrawSurface
     *
     */
    extern "C" HRESULT DDCopyBitmap(IDirectDrawSurface *pdds, HBITMAP hbm, int x, int y, int dx, int dy)
    {
        HDC                 hdcImage;
        HDC                 hdc;
        BITMAP              bm;
        DDSURFACEDESC       ddsd;
        HRESULT             hr;
    
        if (hbm == NULL || pdds == NULL)
            return E_FAIL;
    
        //
        // make sure this surface is restored.
        //
        pdds->Restore();
    
        //
        //  select bitmap into a memoryDC so we can use it.
        //
        hdcImage = CreateCompatibleDC(NULL);
        if (!hdcImage)
            OutputDebugString("createcompatible dc failed\n");
        SelectObject(hdcImage, hbm);
    
        //
        // get size of the bitmap
        //
        GetObject(hbm, sizeof(bm), &bm);    // get size of bitmap
        dx = dx == 0 ? bm.bmWidth  : dx;    // use the passed size, unless zero
        dy = dy == 0 ? bm.bmHeight : dy;
    
        //
        // get size of surface.
        //
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
        pdds->GetSurfaceDesc(&ddsd);
    
        if ((hr = pdds->GetDC(&hdc)) == DD_OK)
        {
            StretchBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, x, y, dx, dy, SRCCOPY);
            pdds->ReleaseDC(hdc);
        }
    
        DeleteDC(hdcImage);
    
        return hr;
    }
    
    //
    //  DDLoadPalette
    //
    //  Create a DirectDraw palette object from a bitmap resoure
    //
    //  if the resource does not exist or NULL is passed create a
    //  default 332 palette.
    //
    extern "C" IDirectDrawPalette * DDLoadPalette(IDirectDraw *pdd, LPCSTR szBitmap)
    {
        IDirectDrawPalette* ddpal;
        int                 i;
        int                 n;
        int                 fh;
        HRSRC               h;
        LPBITMAPINFOHEADER  lpbi;
        PALETTEENTRY        ape[256];
        RGBQUAD *           prgb;
    
        //
        // build a 332 palette as the default.
        //
        for (i=0; i<256; i++)
        {
            ape[i].peRed   = (BYTE)(((i >> 5) & 0x07) * 255 / 7);
            ape[i].peGreen = (BYTE)(((i >> 2) & 0x07) * 255 / 7);
            ape[i].peBlue  = (BYTE)(((i >> 0) & 0x03) * 255 / 3);
            ape[i].peFlags = (BYTE)0;
        }
    
        //
        // get a pointer to the bitmap resource.
        //
        if (szBitmap && (h = FindResource(NULL, szBitmap, RT_BITMAP)))
        {
            lpbi = (LPBITMAPINFOHEADER)LockResource(LoadResource(NULL, h));
            if (!lpbi)
                OutputDebugString("lock resource failed\n");
            prgb = (RGBQUAD*)((BYTE*)lpbi + lpbi->biSize);
    
            if (lpbi == NULL || lpbi->biSize < sizeof(BITMAPINFOHEADER))
                n = 0;
            else if (lpbi->biBitCount > 8)
                n = 0;
            else if (lpbi->biClrUsed == 0)
                n = 1 << lpbi->biBitCount;
            else
                n = lpbi->biClrUsed;
    
            //
            //  a DIB color table has its colors stored BGR not RGB
            //  so flip them around.
            //
            for(i=0; i<n; i++ )
            {
                ape[i].peRed   = prgb[i].rgbRed;
                ape[i].peGreen = prgb[i].rgbGreen;
                ape[i].peBlue  = prgb[i].rgbBlue;
                ape[i].peFlags = 0;
            }
        }
        else if (szBitmap && (fh = _lopen(szBitmap, OF_READ)) != -1)
        {
            BITMAPFILEHEADER bf;
            BITMAPINFOHEADER bi;
    
            _lread(fh, &bf, sizeof(bf));
            _lread(fh, &bi, sizeof(bi));
            _lread(fh, ape, sizeof(ape));
            _lclose(fh);
    
            if (bi.biSize != sizeof(BITMAPINFOHEADER))
                n = 0;
            else if (bi.biBitCount > 8)
                n = 0;
            else if (bi.biClrUsed == 0)
                n = 1 << bi.biBitCount;
            else
                n = bi.biClrUsed;
    
            //
            //  a DIB color table has its colors stored BGR not RGB
            //  so flip them around.
            //
            for(i=0; i<n; i++ )
            {
                BYTE r = ape[i].peRed;
                ape[i].peRed  = ape[i].peBlue;
                ape[i].peBlue = r;
            }
        }
    
        pdd->CreatePalette(DDPCAPS_8BIT, ape, &ddpal, NULL);
    
        return ddpal;
    }
    
    /*
     * DDColorMatch
     *
     * convert a RGB color to a pysical color.
     *
     * we do this by leting GDI SetPixel() do the color matching
     * then we lock the memory and see what it got mapped to.
     */
    extern "C" DWORD DDColorMatch(IDirectDrawSurface *pdds, COLORREF rgb)
    {
        COLORREF rgbT;
        HDC hdc;
        DWORD dw = CLR_INVALID;
        DDSURFACEDESC ddsd;
        HRESULT hres;
    
        //
        //  use GDI SetPixel to color match for us
        //
        if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
        {
            rgbT = GetPixel(hdc, 0, 0);             // save current pixel value
            SetPixel(hdc, 0, 0, rgb);               // set our value
            pdds->ReleaseDC(hdc);
        }
    
        //
        // now lock the surface so we can read back the converted color
        //
        ddsd.dwSize = sizeof(ddsd);
        while ((hres = pdds->Lock(NULL, &ddsd, 0, NULL)) == DDERR_WASSTILLDRAWING)
            ;
    
        if (hres == DD_OK)
        {
            dw  = *(DWORD *)ddsd.lpSurface;                     // get DWORD
            dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount)-1;  // mask it to bpp
            pdds->Unlock(NULL);
        }
    
        //
        //  now put the color that was there back.
        //
        if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
        {
            SetPixel(hdc, 0, 0, rgbT);
            pdds->ReleaseDC(hdc);
        }
    
        return dw;
    }
    
    /*
     * DDSetColorKey
     *
     * set a color key for a surface, given a RGB.
     * if you pass CLR_INVALID as the color key, the pixel
     * in the upper-left corner will be used.
     */
    extern "C" HRESULT DDSetColorKey(IDirectDrawSurface *pdds, COLORREF rgb)
    {
        DDCOLORKEY          ddck;
    
        ddck.dwColorSpaceLowValue  = DDColorMatch(pdds, rgb);
        ddck.dwColorSpaceHighValue = ddck.dwColorSpaceLowValue;
        return pdds->SetColorKey(DDCKEY_SRCBLT, &ddck);
    }
    

    Aber für die Analyse dürfte nur die erste Quellcodedatei relevant sein, da "ddutil" für mehrere Projekte im SDK benutzt wurde und ich die auch nicht geändert habe.

    Gibt es eigentlich außer SDL und SFML noch andere namhafte plattformunabhängige Frameworks, die das machen, was DirectX macht, mit denen man so komfortabel wie mit SDL oder SFML arbeiten kann, die aber an die Geschwindigkeit von DirectX rankommen? Was benutzt man, wenn man ein Spiel programmieren will, das auf Windows, Linux und Mac läuft, ohne daß Code angepaßt werden muß?



  • Bzw. unter Windows 7 ist man ja der "OpenGL via Direct3D" Implementierung von MS ausgeliefert soweit ich weiss.

    Kannst du das genauer erläutern?
    Soweit ich weiß, hat Windows doch mit den Treibern und deren OpenGL Implementiereung nichts am Hut.

    MfG,
    ScRaT



  • Du benutzt unter DDraw einen Clipper? Das kann meinen Messungen bis zu 70% Performance kosten. Man sollte keinen Clipper setzen, da er ja auch unnoetig ist, wenn man die Blitrects entsprechend waehlt. Ausserdem ist DDraw sowieso nicht besonders schnell, da es nur die 2D-Beschleunigung der Grafikkarte benutzt, welche seit einigen Jahren nicht mehr beachtet und daher auch nicht mehr verbessert wird. Das erklaert natuerlich, das du mit DX nur so minimale Gewinne hast. Du benutzt ein DX, was schon 10 Jahre alt ist! f'`8k

    Autocogito

    Gruß, TGGC (Was Gamestar sagt...)



  • TGGC schrieb:

    Du benutzt unter DDraw einen Clipper?

    Ich sagte ja, ich hab das eher auf Grundlage eines anderen Projekts zusammengefrickelt, da ich mich mit DirectX noch nicht wirklich beschäftigt habe und nur mal schnell gucken wollte, ob DirectX viel schneller als SFML ist.

    TGGC schrieb:

    Du benutzt ein DX, was schon 10 Jahre alt ist!

    Ich wollte das ganze möglichst kompatibel mit allen Windowsen halten. Wenn ich ein Spiel programmiere, dann wird es grafiktechnisch eher auf dem Niveau eines NES-Spiels sein. Da wollte ich nicht, daß für sowas die minimalen Systemvoraussetzungen Windows 7 mit DirectX 10 sind. Das soll eben möglichst auch auf einem alten Windows 95-PC laufen. Aber im Moment hätte ich's eh lieber, wenn ich ein plattformunabhängiges Framework hätte. Erstens natürlich, weil ich es dann eben auch für Nicht-Windows-Betriebssysteme kompilieren kann und zweitens, weil man bei der Betrachtung des Quellcodes, der für so eine simple Grafikausgabe auf einem Fenster geschrieben werden muß, rammdüsig werden kann.

    P.S.: Falls jetzt jemand denkt: "Wenn Du sowieso bloß so anspruchslose Spiele schreiben willst, kannst Du doch selbst die langsame SDL nehmen": Nun ja, das Problem liegt darin, daß der Benutzer auch in der Lage sein soll, die Ausgabe im Fenster zu vergrößern. Das Spiel selbst braucht mit seinen 320 x 240 Pixeln vielleicht keine schnelle Verarbeitung, aber bei einem PC mit einer Auflösung von 1280 x 1024 oder noch größer, wo das Programm maximiert, aber trotzdem nicht im Vollbild, sondern im Fenster ablaufen soll, brauch ich eben die Geschwindigkeit.



  • SFML ist auch nur bis Win 98 kompatibel. Ein sinnvoller Vergleich waere daher mit der DirectX 9 August 2009 Version moeglich. Dein Codeargument ist auch etwas unsinnig, das sich kaum mehr als 5 Prozent des Code mit DX auseinandersetzen wuerden.

    Falls du dich doch auf Zielsystem Windows beschraenken kannst, schau dir mal HGE an, denn das ist genau fuer das von dir gewuenschte konzipiert: http://hge.relishgames.com/overview.html (und wie gesagt: 5 mal schneller bei nahezu identischem Codingaufwand). f'`8k

    Autocogito

    Gruß, TGGC (Was Gamestar sagt...)



  • TGGC schrieb:

    SFML ist auch nur bis Win 98 kompatibel. Ein sinnvoller Vergleich waere daher mit der DirectX 9 August 2009 Version moeglich.

    Ja, gut, das Argument war etwas übertrieben. Aber prinzipiell gilt es trotzdem: Was bitteschön hat denn die 2009er Version von DirectX 9, das ich so unbedingt brauche, daß ich Benutzer, die noch DirectX 6, 7, 8 oder ein älteres 9 auf dem PC haben, auffordern müßte, es zu aktualisieren? Denn wenn ich mein Zeug mit DirectX 2 realisieren kann, ohne daß das für mich mehr Aufwand ist, sehe ich keinen Grund, DirectX 9 zu nehmen, bloß weil das andere zu alt ist.

    TGGC schrieb:

    Dein Codeargument ist auch etwas unsinnig, das sich kaum mehr als 5 Prozent des Code mit DX auseinandersetzen wuerden.

    Das war mehr so ein Sekundärargument. Ich weiß schon, daß ich, wenn's einmal steht, mich nicht mehr großartig mit DirectX befassen muß. Aber die Programmierung mit der SDL oder der SFML macht einfach mehr Spaß, weil ich da nicht erstmal stundenlang das Grundgerüst aufbauen muß, um dann endlich mal zum eigentlichen Spiel zu kommen.

    TGGC schrieb:

    Falls du dich doch auf Zielsystem Windows beschraenken kannst

    Wenn ich das tue, werde ich wohl wahrscheinlich tatsächlich DirectX direkt nehmen. Wenn's sowieso bloß Windows ist, gibt's keinen Grund, zwischen meinen eigenen Bild- und Sprite-Klassen zur Abstraktion und den DirectX-Funktionen noch ein weiteres Framework zu schalten. Da unterliegt dann die Bequemlichkeit doch gegenüber der Tatsache, weniger Abhängigkeiten zu haben.



  • Sagt mal, kann es sein, daß die SFML sogar langsamer als das stinknormale Windows-GDI ist? Wenn ich ein Bild von 320 x 240 Pixeln 1000 mal hintereinander blitten lasse und die Zeit messe, komme ich bei der GDI auf 735 bis 812 Millisekunden. Bei der SFML sind es 1656 bis 1672 Millisekunden. Das heißt also, dieser ach so tolle OpenGL-Wrapper ist doppelt so lahmarschig als die langsame Standardgrafikbibliothek in Windows.



  • Wie kommst du darauf, das SFML schnell waere? Wie kommst du darauf das GDI langsam waere?

    Warum du ein neuerer DX benutzen sollst, habe ich doch schon oben erklaert: Du benutzt ein Grafik-Api von vor 10 Jahren also kannst du ja auch gleich eine 10 Jahre alte Grafikkarte benutzen und dich wundern, das sie langsam ist. f'`8k

    Autocogito

    Gruß, TGGC (Was Gamestar sagt...)



  • Naja das Alter ist für sich genommen ja wohl überhaupt kein Argument. Wir benutzen mit x86 grösstenteils (abgesehen von Erweiterungen als MMX/SSE/...) einen Befehlssatz der mehr als 10 Jahre alt ist. Deswegen sind die Programme trotzdem nicht 3x langsamer als mit z.B. AMD64.



  • TGGC schrieb:

    Wie kommst du darauf, das SFML schnell waere?

    Weil SFML angeblich OpenGL benutzt und, genauso wie OpenGL selbst, unter anderem dazu gedacht ist, damit Spiele zu programmieren.

    TGGC schrieb:

    Wie kommst du darauf das GDI langsam waere?

    Weil keiner mit GDI Spiele programmiert. Und die Geschwindigkeit schon bei mittelgroßen Bildern merklich in die Knie geht.

    TGGC schrieb:

    Warum du ein neuerer DX benutzen sollst, habe ich doch schon oben erklaert: Du benutzt ein Grafik-Api von vor 10 Jahren also kannst du ja auch gleich eine 10 Jahre alte Grafikkarte benutzen und dich wundern, das sie langsam ist. f'`8k

    Ist Dir die Ironie aufgefallen, daß ich mich an keiner Stelle über eine zu langsame Geschwindigkeit bei DirectX beschwert habe? (Bei dem Vergleich im Ursprungspost ging es eher um die Inkonsistenz in den Ergebnissen und nicht darum, daß DirectX per se langsam wäre.)



  • Ich glaube ein paar sinnvollere Tests wären mit 1000+ Sprites die rotieren und evtl. Shadern (wobei der Shaderzeichenablauf eigentlich bei pur-DirectX und SFML identisch sein sollte und sich somit kein Performanceunterschied rausstellen sollte, zumindest fällt mir nix ein was für einen merklichen Unterschied spricht).

    Dann sollte GDI+ relativ schnell an seine Grenzen kommen.

    Trotzdem wird SFML wohl immer ein bisschen abkacken. Laurent Gomilla hat im Forum mal gemeint, dass es n Haufen Zeug gibt dass er für Performance optimieren könnte. Einiges davon will er in SFML2 umsetzen, anderes lässt er aber absichtlich bleiben da es sonst Probleme mit dem Design gibt (er bleibt z.b. bei glBegin und glEnd Blöcken anstatt irgendwie nen Scene Manager mit Vertexarrays zu benutzen).

    Ich such mal nach dem Post und editier dass dann rein falls ich ihn finde.

    Und achja: in Windows Vista war OpenGL ja (man munkelt absichtlich von Microsoft so hingefrickelt) langsamer als DirectX, ich weiss nicht ob das mit Windows 7 auch noch gilt.



  • TravisG schrieb:

    Ich glaube ein paar sinnvollere Tests wären mit 1000+ Sprites die rotieren und evtl. Shadern

    Nein, wieso? Wenn ich ein Spiel programmiere, dann wird das eins auf dem grafischen Niveau eines NES-Spiels. Da gibt's keine Rotation oder sonstige Effekte. Da ist das einzige, was mich interessiert, wie schnell die Bilder dargestellt werden können, vor allem, wie schnell sie dargestellt werden können, wenn ich das Ausgabefenster vergrößere. (Die eigentliche Spielfeldauflösung wird zwar nur 320 x 240 oder 256 x 224 Pixel sein, aber dieses Bild soll ja für den Benuter auch vergößert werden können.)

    TravisG schrieb:

    Dann sollte GDI+ relativ schnell an seine Grenzen kommen.

    GDI+ sowieso. Das hab ich mal vor Monaten ausprobiert. Der lahmarschigste Scheiß überhaupt. Ich rede hier von der GDI.
    Aber es ist auch egal, ob die GDI an ihre Grenzen kommt. Die wurde ja nur als Vergleich benutzt. Die GDI ist ohnehin zu langsam. Und wenn SFML noch langsamer ist, während DirectX (also, direktes DirectX, nicht über noch viel, viel langsamere SDL gekapselt) abgeht wie Schmidts Katze, dann frag ich mich, was die SFML (und SDL) ausbremst.

    TravisG schrieb:

    Trotzdem wird SFML wohl immer ein bisschen abkacken.

    "Ein bißchen" ist gut.

    TravisG schrieb:

    Und achja: in Windows Vista war OpenGL ja (man munkelt absichtlich von Microsoft so hingefrickelt) langsamer als DirectX, ich weiss nicht ob das mit Windows 7 auch noch gilt.

    Also, die Tests wurden auf jeden Fall mit Windows XP und auf dem alten PC mit Windows 2000 gemacht.



  • Hi, I'm from sfml-dev.org

    I don't speak German.

    Looking at your src code you need sf::RenderWindow::SetFramerateLimit()

    screen.SetFramerateLimit(60.f);

    DirectX is a lower level API so it should be faster.

    Also, raw total FPS is also not a good benchmark of performance.

    http://www.sfml-dev.org/forum/viewtopic.php?t=2496



  • NES-Spieler schrieb:

    Ist Dir die Ironie aufgefallen, daß ich mich an keiner Stelle über eine zu langsame Geschwindigkeit bei DirectX beschwert habe? (Bei dem Vergleich im Ursprungspost ging es eher um die Inkonsistenz in den Ergebnissen und nicht darum, daß DirectX per se langsam wäre.)

    Und dir ist wohl nicht aufgefallen, das die Inkonsistenz aus genau diesem Grund entsteht. Du benutzt etwas, das nur aus jahrzehntelanger Abwaertskompatibilitaet existiert - wie gut das funktioniert ist im Grunde Zufall. Genauso wie das "Streckverhalten" aus deinem anderen Thread - so etwas war damals einfach noch nicht standardisiert. f'`8k

    Autocogito

    Gruß, TGGC (Was Gamestar sagt...)



  • Wenn ich ein Spiel programmiere, dann wird das eins auf dem grafischen Niveau eines NES-Spiels.

    Mal abgesehen davon was warum wie schnell ist solltest Du Dir primaer die Frage stellen, welches Framework fuer Dich am geeignetsten ist und ob "langsamer" bei der gegebenen Aufgabe nicht immer noch "schnell genug" ist.



  • NES-Spieler schrieb:

    TravisG schrieb:

    Ich glaube ein paar sinnvollere Tests wären mit 1000+ Sprites die rotieren und evtl. Shadern

    Nein, wieso? Wenn ich ein Spiel programmiere, dann wird das eins auf dem grafischen Niveau eines NES-Spiels. Da gibt's keine Rotation oder sonstige Effekte. Da ist das einzige, was mich interessiert, wie schnell die Bilder dargestellt werden können, vor allem, wie schnell sie dargestellt werden können, wenn ich das Ausgabefenster vergrößere. (Die eigentliche Spielfeldauflösung wird zwar nur 320 x 240 oder 256 x 224 Pixel sein, aber dieses Bild soll ja für den Benuter auch vergößert werden können.)

    Ja, für verschiedene Zwecke gibt es sicherlich verschiedene Libs die dafür optimal sind, je nachdem auf was man abzielt (Performance / Erleichterung des Entwickelns / Hübsch-heit des Codes usw.). Theoretisch könnte man bestimmt auch irgendwie seine eigenen Treiber zusammenfrickeln, die für die eigene Anwendung auf die schnellstmögliche Art ausführen.

    TravisG schrieb:

    Trotzdem wird SFML wohl immer ein bisschen abkacken.

    "Ein bißchen" ist gut.

    Bei solchen 2-Bilder-Tests ist der Unterschied zwar noch heftig, aber ich bin mir ziemlich sicher, dass die Geschwindigkeitsunterschiede bei aufwendigeren Tests viel geringer ausfallen. Dennoch wird "pures" DirectX oder OpenGL in der Regel immer schneller sein, als ein Wrapper, weil ein Wrapper ja in der Regel nicht einfach "wrapped" sondern noch einige Komfortfunktionen mit einbaut. Aus dem Grund finde ich einen Vergleich von SFML vs DirectX eher unsinnig. HGE vs. SFML kann da passender sein, aber dazu kann ich nichts genaues sagen. HGE hab ich nie ausführlich benutzt, nur kurz angeschaut usw. Aber wie gesagt, wenn du nur (wenige) gerade Vierecke mit Bebilderung brauchst, gibts wahrscheinlich andere Libs die besser für dich geeignet sind.



  • TravisG schrieb:

    HGE vs. SFML kann da passender sein, aber dazu kann ich nichts genaues sagen.

    Ich kann dazu genaueres sagen: HGE war bei mir 5x schneller. Warum weiss ich aber auch nicht. f'`8k

    Autocogito

    Gruß, TGGC (Was Gamestar sagt...)


Anmelden zum Antworten