| E | L | P | H | I | R | O    programmeren •  linux •  gitaar •  foto's •  email
Search with Google™:
    sdl •  sql •  delphi •  componenten •  cpp •  c


 

SDL onder windows draaiend krijgen

Wat krijgen we nu? Ik als Linux fanaat en een stuk over hoe je SDL onder Windows draaiend krijgt? Yep.. en met een goede reden. Iedereen heeft op z'n werk wel eens dode momenten en de meesten van ons moeten het op het werk helaas met een Windhoos-bak doen (nog wel!). Ik dus ook en om toch code voor Linux te kunnen schrijven (eigenlijk is SDL code platform onafhankelijk) gebruik ik gewoon de mingw32 compiler. Dan draai je een soort Linux shell waarmee je met de onder Linux gebruikelijke GCC compiler programmacode (C, C++) kunt compileren.

Wat is SDL ?

SDL is een platformonafhankelijke bibliotheek waarmee het een stuk eenvoudiger wordt om programmacode voor spelletjes of grafisch dan wel geluids georiënteerde applicaties in het algemeen, te schrijven. De officiële website vind je onder deze link. De website van SDL heeft veel voorbeelden en een uitgebreide beschrijving van de functies. SDL gaat eigenlijk uit van het gebruik van C maar er is ook een versie voor Pascal (Jedi-SDL project) en er zijn diverse uitbreidingen gemaakt om het in een object georiënteerd jasje (C++) te stoppen. Zie hiervoor de SDL website.

De benodigde pakketten

Om SDL onder Windhoos draaiend te krijgen heb je de volgende pakketen nodig:

  • De mingw32 (MINimalist Gnu for Windows) compiler, te vinden op de mingw website, zoek naar het MinGW-n.n.tar.gz waarbij n.n het versie nummer is (bv. MinGW-1.1.tar.gz). De download staat overigens op de SourceForge site, een superhost voor Linux- en andere Open Source-fanaten!
  • De MSys bestanden om een Unix shell op je Windhoos PC te toveren. Zoek naar een bestandsnaam in de vorm van 'MSYS-1.0.8-i386-2002.06.18- 1.exe'.
  • De SDL broncode te vinden op SDL website, zoek naar 'Source Code' (bv. SDL-1.2.4.tar.gz).
  • De OpenGL module van Cygwin die iets moeilijker te vinden is maar meestal wel onder deze link staat; mocht je het niet kunnen vinden zoek dan op de cygwin site. Zoek naar een bestand in de vorm van 'opengl-1.1.0-6-src.tar.gz'.
  • Twee shellscriptjes die je hier kunt krijgen.

De installatie

De eerste stap is het installeren van het MinGW pakket. In mijn geval heet het bestand MinGW-1.1.tar.gz. Uitpakken van dit soort bestanden onder Windhoos kan met de gemiddelde 'ontzipper' (bv. winzip). De volgende stappen installeert het MinGW pakket:

  • Maak een directory (liefst onder de C:\ bijvoorbeeld C:\MinGW)
  • Pak het bestand uit (met behoud van de paden in het archief!) in de net aangemaakte directory

Nu een makkie; installeer MSys dat in een keurig installshield pakje komt en je gedurende de gehele installatie aan de hand neemt. Het moeilijkste deel is het opgeven van het pad waar je MinGW geinstalleerd hebt.. gebruik hier / ipv \ als directory scheiding.

Omdat de MinGW omgeving zonder OpenGL module wordt geleverd moet je de volgende kunstgreep toepassen. Als het goed is heb je een bestand in de vorm van 'opengl-n.n.n-n-src.tar.gz' (waarbij n.n.n-n het versienummer is) van de Cygwin site geplukt. (Cygwin is de grote broer van MinGW, zeg maar een complete Unix console onder Windhoos!) Zorg ervoor dat de bestanden uit dit archief in dezelfde directory onder je MinGW installatie komen te staan; bv. het opengl archief bevat een directory met de naam bin; kopieer alle bestanden uit deze directory dan ook naar bv C:\MinGW\bin. Doe hetzelfde voor alle bestanden in de overige directories (in ieder geval doc, include en lib) uit het opengl-archief.

Om SDL te installeren moeten we nu de volgende stappen ondernemen:

  • Pak het archief (bv. SDL-1.2.4.tar.gz) uit in je home directory die te vinden is onder C:\msys\n.n\home\naam (waarbij n.n het versienummer is). Er ontstaat nu een nieuwe directory onder home\naam zoals home\naam\SDL-1.2.4.
  • Kopieer de twee shellscriptjes in de SDL directory (die net aangemaakt is).
  • Start Msys; de snelkoppeling is opgenomen in het startmenu onder MinGW.
  • Ga in de SDL directory staan en voer de volgende commando's uit:

    sh cross-configure.sh
    sh cross-make.sh install

    Als SDL klaagt over missende GL bibliotheken dan heb je de installatie van de opengl module niet goed afgerond.
  • Er is nu een belangrijk dll bestand gebouwd die voor het systeem beschikbaar moet zijn. Kopieer het bestand SDL.dll dat te vinden is in C:\msys\n.n\home\naam\SDL-1.2.4\src\.libs naar een directory dat in je PATH variabele staat (bv. C:\Windows\System).
  • Ga vanuit MSys naar de test directory die onder je SDL pad staat en voer de volgende commando's uit om de testbestanden van SDL te compileren:

    sh ../cross-configure.sh
    sh ../cross-make.sh

    Je kunt nu de testbestanden (eindigend op .exe) proberen!

Een laatste opmerking; mocht je zelf code schrijven en dit willen compileren dan heb je steeds de twee scriptjes nodig en moet je ook steeds de volgende commando's uitvoeren:

sh cross-configure.sh
sh cross-make.sh

Vergeet niet de gebouwde executable de .exe extensie te geven om aand Windhoos aan te geven dat het om een uitvoer bestand gaat.. Micro$oft spullen zijn niet zo slim ;-)

Veel plezier met programmeren!
Rob van Putten



 

Tip - snelle schermupdates

Een flessenhals in de prestaties van een SDL programma is vaak het verversen van het scherm. Op het moment dat er een SDL_Flip gegeven wordt, wordt de hele inhoud van het scherm vervangen door de nieuwe inhoud. Je moet je voorstellen dat je bv bij een scherm van 800 x 600 met 16 bits kleuren er een geheugengebied van 800 x 600 x 16 = 960 kB gewist en gevuld wordt. Dat is vaak dataverkeer tussen de grafische kaart en het RAM en die overdrachtssnelheid vertraagd de beeldopbouw. Op een moderne PC met eenvoudige spelletjes zul je er niet veel van merken, maar bij het gebruik van complexe textures en hoge resoluties en kleurdieptes deste meer. Het is dan ook gewoon netjes om alleen dat wat op het scherm verandert te verversen.

Een voorbeeld:

Ik heb een spel gemaakt waarbij er een object A op een achtergrond op het scherm worden gezet. Als een speler het object beweegt zal ik het object moeten verplaatsen en het scherm moeten verversen. In de onderstaande afbeelding zie je twee schermen. Een scherm voor het verversen (links) en een scherm na het verversen (rechts). Op de 'slechte' manier ga je als volgt te werk.

nee niet zo.

In pseudocode:

SDL_BlitSurface( achtergrond op scherm ); //nu ben ik die oude A kwijt
SDL_BlitSurface( object A op scherm ); //de nieuwe A staat er
SDL_Flip( scherm ); //en nu krijgt de gebruiker het te zien


Dit werkt prima.. maar besef je wel dat je eigenlijk alleen het scherm ter plaatse van de oude positie van object A (weghalen en achtergrond terugzetten) en de nieuwe positie van object A had hoeven te verversen. Zoals je op de afbeelding hierboven ziet heb je het hele gele oppervlak ververst. De rest van de oppervlakte is gewoon niet verandert en is vertragende 'overhead'. Zonde van de tijd, zeker als object A zoals in de 'echte' wereld nog eens heel klein is.

Laten we het maar eens beter gaan doen. Dat vereist wat meer werk maar is zeker de moeite waard. Bekijk de volgende afbeelding en de bijbehorende pseudocode.

ja dat is beter.

SDL_BlitSurface( achtergrond tpv A ); //herstel de oude achtergrond
SDL_UpdateRect( achtergrond tpv object A ); //laat dit stukje zien aan de gebruiker
SDL_BlitSurface( object A ); //de nieuwe A staat er
SDL_UpdateRect( object A ); //laat ook dit stukje zien aan de gebruiker


Je hebt nu enkel de groene en gele rechthoeken ververst en de rest van het scherm ongemoeid gelaten. Dit scheelt veel tijd en heeft een positieve invloed op de maximale 'framerate' van je spel. De enige 'overhead' die je hierbij veroorzaakt is dat je bij zult moeten houden waar je object stond voordat het verplaatst werd. Dat lijkt me echter niet bepaald een moeilijke opdracht.

Over de bijbehorende SDL functies;

functie uitleg voorbeeld
SDL_Flip ververst het hele scherm SDL_Surface *screen;
...
SDL_Flip( screen );
SDL_UpdateRect ververst een deel van het scherm SDL_Surface *screen;
SDL_Surface *objectA;
int x_objA;
int y_objA;
...
SDL_UpdateRect( screen, x_objA, y_objA, objectA->w, objectA->h );
SDL_UpdateRects ververst alle objecten in de lijst die meegegeven wordt SDL_Surface *screen;
SDL_Rect objects[2]; //array van twee rechthoeken
objects[0].x = 10;
objects[0].y = 100;
objects[0].w = 100;
objects[0].h = 100;
objects[1].x = 200;
objects[1].y = 300;
objects[1].w = 50;
objects[1].h = 50;
...
//geef adres naar begin array mee
SDL_UpdateRects( screen, 2, &objects[0] );



 

Tip - optimalisatie

Hier een samenvatting van wat tips die je programma sneller kunnen maken.

  • Gebruik een kleurendiepte die door de gebruiker wordt ondersteund
    Je kunt dit bereiken door in SDL_SetVideoMode de vlag SDL_ANYFORMAT te zetten. Op deze manier wordt altijd de kleurendiepte gebruikt van het systeem waar je programma op draait, bijvoorbeeld.

    SDL_Surface *screen;
    screen = SDL_SetVideoMode(800, 600, 16, SDL_ANYFORMAT | SDL_HWSURFACE );
    //scherm 800x600x16 maar als de gebruiker een 32bpp omgeving heeft wordt het
    //door de SDL_ANYFORMAT vlag omgezet in een scherm van 800x600x32

  • Converteer een afbeelding altijd naar de kleurdiepte van je videomode
    Een afbeelding moet altijd dezelfde kleurendiepte hebben als de videomode die je gebruikt. Nu is het nogal lastig om voor elke videomode een afbeelding paraat te hebben dus kun je de volgende truc in SDL toepassen; lees de afbeelding in, maak een copy in het juiste formaat en gooi de oude afbeelding weer weg (figuurlijk!). In code:

    SDL_Surface *tmp, *back;
    tmp = SDL_LoadBMP(...); //laad een afbeelding in

    if (tmp == NULL){ //controle of er iets geladen is
     //foutafhandeling, mag je zelf verzinnen
    }

    back = SDL_DisplayFormat(tmp); //maak een copy met het juiste formaat

    if (back == NULL){ //controle
     //foutafhandeling, mag je zelf verzinnen
    }

    SDL_FreeSurface(tmp); //vrijgeven oude afbeelding


  • Stop meerdere afbeeldingen in één afbeelding
    Als je een animatie hebt (zoals in de onderstaande figuur) stop dan niet elke 'frame' van de animatie in een apart bestand maar stop alle frames in hetzelfde bestand. 3 in 1

    Dit scheelt een hoop bytes en een hoop werk in de code (je hoeft maar 1 bestand in te lezen) en het is maar weinig werk om vanuit het geladen bestand de juiste frame te kiezen. Er zijn theoriën dat het sneller is om de afbeeldingen verticaal te stapelen maar dat is echt een optimalisatie die wel heel ver gaat. Horizontaal is ook prima. Wat nog wel snelheidswinst oplevert is om de grootte van je afbeeldingen (breedte, hoogte) een macht van 2 te laten zijn (4, 8, 16, 32 etc).

    Voorbeeldje in code:

    //veels te ingewikkeld, elke frame 1 bestand
    int iHuidigeTank = 0;
    SDL_Surface *tank_animatie[3];
    SDL_Rect dest;
    tank_animatie[0] = SDL_LoadBMP("tank1.bmp");
    tank_animatie[1] = SDL_LoadBMP("tank2.bmp");
    tank_animatie[2] = SDL_LoadBMP("tank3.bmp");
    ...
    dest.x = 100;
    dest.y = 100;
    dest.w = tank_animatie[iHuidigeTank]->w;
    dest.h = tank_animatie[iHuidigeTank]->h;
    SDL_BlitSurface( tank_animatie[iHuidigeTank], NULL, scherm, &dest );


    //dat is beter.. alles in 1 bestand
    int iHuidigeTank = 0;
    SDL_Rect src, dest;
    SDL_Surface *tank_animatie;
    tank_animatie = SDL_LoadBMP("alletanks.bmp");
    dest.w = src.w = 32; //elke frame is 32 pixels breed
    dest.h = src.h = tank_animatie->h; //horizontaal 'gestapeld' dus de hoogte is niet variabel
    src.y = 0; //horizontaal 'gestapeld' dus y is altijd 0
    ...
    dest.x = 100;
    dest.y = 100;
    src.x = iHuidigeTank * src.w; //kies de juiste frame
    SDL_BlitSurface( tank_animatie, &src, scherm, &dest );


  •  

    Tip - SDL en Debian

  • SDL op debian installeren
    Wil je enkel SDL ondersteuning dan voldoet het om de gewone SDL pakketten (bv. libsdl1.2-all) te installeren met bv. apt-get of dpkg. Wil je ook kunnen ontwikkelen met SDL dan zul je ook de development packages (bv. libsdl-image1.2-dev) moeten installeren. Deze bevatten o.a. de SDL header bestanden en het onmisbare 'sdl-config' commando.
  • Debain pakket sdl_mixer en 'playwave'
    Om een voor mij onbekende reden bevat het debian pakket van sdl_mixer geen 'playwave' commando. Dit commando komt standaard bij de normale versie van SDL_Mixer maar Debian heeft daarvan afgezien. Om het gemis op te vangen kun je gelukkig een nog veel beter commando installeren vanuit het pakket 'sox'. Dit pakketje bevat het commando 'play' dat meer opties (bv. pitch shifting) dan 'playwave' kent.



  •  

    Beperkingen van SDL 1.x op linux

    De prestaties van SDL vallen sommige mensen nog wel eens tegen. Zelfs op een systeem als een P4 - 2Ghz met mucho RAM en een GeForce 4 weetikveel halen hun eigen creaties onder linux nog maar 50fps (let wel.. 50fps is meer dan zat voor een soepel spel.. alleen verwachten mensen met zo'n systeem getallen als 1000 of meer te zien). Hoe kan dat?

    De eerste mogelijkheid is dat je monitor een VSync van 50Hz heeft. SDL is zo aardig om de programmatuur daarop af te stemmen. Meestal heeft het echter een andere oorzaak;

    Het 'probleem' is dat SDL 1.x onder linux geen hardware versnelling ondersteunt. Dus ondanks de geweldige prestaties van een GF4 op 3D gebied is de 2D functionaliteit van zo'n kaart nauwelijks iets hoger dan die van een oude Matrox kaart. Zolang SDL geen hardware ondersteuning biedt zal die kaart je niet helpen (mits je SDL gebruikt om OpenGL programma's te maken).

    Onder Windows maakt SDL gebruik van DirectX 5 maar voor Linux bestaat zoiets niet. Gelukkig is er een oplossing die zeer waarschijnlijk in SDL 2.x geimplementeerd zal zijn. De 'normale' SDL zal dan gebruik maken van een onderliggende OpenGL 'backend' waardoor je wel de voordelen van de huidige grafische kaarten (bv. data-opslag in het RAM van de grafische kaart) hebt. Kun je niet wachten tot SDL2 dan zit er niks anders op om of de beperkte framerate te accepteren of zelf een OpenGL backend te schrijven. Je doet er dan goed aan om niet (helemaal) het wiel opnieuw uit te vinden en even op de SDL site te kijken of er al projecten zijn die dit gebruiken. Nogmaals doe dit pas als je framerate onder de 20-30fps zakt, anders doe je nutteloos extra werk (ok, helemaal nutteloos is het niet want je leert er wel van).