Sinds zeer kort heb ik een MacBook Pro, en hier wil ik natuurlijk ook op kunnen programmeren.
Ik heb ondertussen XCode geinstalleerd, en ben nu aan het kijken hoe ik het beste op een vlotte manier kan tekenen.
Wat ik wil is met een framerate van ongeveer 40 fps mijn programma opnieuw tekenen (bestaande uit een achtergrondplaatje, een hoop andere plaatjes die hierover worden getekend, wat tekst en een aantal objecten die ik zelf pixel per pixel wil tekenen)
Onder windows heb ik dit opgelost met "CreateDIBSection"
Met deze functie kan ik namelijk een bitmap in het geheugen maken, en heb ik een handle zodat ik deze bitmap op het scherm kan blitten, en ik kan door deze handle ook makkelijk de standaard windows gdi functies gebruiken om er text op te tekenen of delen van andere plaatjes over te kopieren.
Bovendien kan ik ook direct aan de byte array van deze bitmap, waardoor ik heel snel mijn eigen tekenfuncties kan gebruiken. (Zelfs m'n eigen BitBlt die gebruik maakt van memcpy's is op die manier sneller dan de standaard BitBlt)
Liefst zou ik dus iets gelijkaardigs doen onder Mac OS.
Na een hoop gegoogled te hebben en tutorials/documentatie hebben doorgenomen kom ik tot volgende oplossing:
-Een subklasse maken van NSView
-In initWithFrame een NSTimer starten die elke 1/40 seconde afgaat
-Elke keer de timer afgaat de graphics een redraw aanvragen
-In redraw een nieuw plaatje tekenen
doAnimation() en refreshImage() heb ik beide in C geschreven omdat ik daar momenteel nog beter mee overweg kan dan met Objective-C, en omdat het de bedoeling is om later de C++ code die ik al heb zowel voor windows als voor Mac OS te kunnen gebruiken.
In de C code heb ik een stuk geheugen om een backbuffer bij te houden in 32-bit RGBA formaat:
In refreshImage zorg ik ervoor dat ik de eerste keer een CGContextRef aanmaak.
Ook maak ik een ImageRef aan zodat ik deze op het scherm kan tekenen.
In doAnimation() ga ik alle pixels af en geef deze elke frame een andere kleur.
Dit alles werkt perfect, maar vergt voor 640x480 wel 50% cpu usage, zonder dat er al iets bijzonders gebeurd. Dat is natuurlijk veel te veel om bruikbaar te zijn, dus ging ik op zoek naar de oorzaak.
-CGBitmapContextCreateImage slechts 1x uitvoeren ipv elke frame:
Hierdoor zat ik aan 30% cpu usage. Dit is dus een behoorlijk zware functie. Helaas is zo'n CGImageRef een kopie van de bitmap, en door deze functie er tussenuit te laten werd mijn animatie dus niet meer geupdate.
Voor zover ik heb kunnen vinden is het ook niet mogelijk om de geheugenbuffer van een CGImageRef zelf aan te passen.
-CGContextDrawImage weglaten: 22%. 1x een 640x480 bitmap blitten kost dus ook al 8% cpu
-doAnimation verwijderen: 16%. For lus over 640x480 pixels kost dus een 6%
-setNeedsDisplay 40x per seconde uitvoeren zonder verder iets opnieuw te tekenen kost dus 16%, wat mij dus erg veel lijkt
Full-screen een video in QuickTime bekijken kost overigens slechts 30%, dus het is wel duidelijk dat er snellere oplossingen moeten bestaan.
Heeft iemand hier misschien ervaring mee? Zoals gezegd werkt deze techniek dus prima op veel tragere windows machines, maar helaas heb ik nog geen Mac OS voorbeelden gevonden die iets gelijkaardigs doen.
OpenGL of Quartz 2D Extreme heb ik ook al even bekeken, maar nog niet geprobeerd. Volgens sommige bronnen zou dit ook niet zo goed gaan met een mindere grafische kaart (de gewone macbooks bijvoorbeeld). In dat geval zou het ook geen oplossing zijn.
Ik heb ook al gedacht aan Core Video, en dan zelf een video source aan te bieden (voor video werkt het aanbieden van pixelbuffers blijkbaar wel goed) maar daar lijkt de documentatie die ik tot nog toe gevonden heb ook erg beperkt van, en misschien is het ook wel overkill voor wat ik wil bereiken.
Ik heb ondertussen XCode geinstalleerd, en ben nu aan het kijken hoe ik het beste op een vlotte manier kan tekenen.
Wat ik wil is met een framerate van ongeveer 40 fps mijn programma opnieuw tekenen (bestaande uit een achtergrondplaatje, een hoop andere plaatjes die hierover worden getekend, wat tekst en een aantal objecten die ik zelf pixel per pixel wil tekenen)
Onder windows heb ik dit opgelost met "CreateDIBSection"
Met deze functie kan ik namelijk een bitmap in het geheugen maken, en heb ik een handle zodat ik deze bitmap op het scherm kan blitten, en ik kan door deze handle ook makkelijk de standaard windows gdi functies gebruiken om er text op te tekenen of delen van andere plaatjes over te kopieren.
Bovendien kan ik ook direct aan de byte array van deze bitmap, waardoor ik heel snel mijn eigen tekenfuncties kan gebruiken. (Zelfs m'n eigen BitBlt die gebruik maakt van memcpy's is op die manier sneller dan de standaard BitBlt)
Liefst zou ik dus iets gelijkaardigs doen onder Mac OS.
Na een hoop gegoogled te hebben en tutorials/documentatie hebben doorgenomen kom ik tot volgende oplossing:
-Een subklasse maken van NSView
-In initWithFrame een NSTimer starten die elke 1/40 seconde afgaat
Objective-C:
1
2
3
| float fps = 1. / 40.; timer = [ NSTimer scheduledTimerWithTimeInterval: fps target: self selector: @selector( singleStep: ) userInfo: nil repeats: YES ]; |
-Elke keer de timer afgaat de graphics een redraw aanvragen
Objective-C:
1
2
3
4
5
| - (void ) singleStep: ( NSTimer * ) t { doAnimation(); [self setNeedsDisplay:YES]; } |
-In redraw een nieuw plaatje tekenen
Objective-C:
1
2
3
4
5
6
| - (void)drawRect:(NSRect)rect { CGRect destRect = CGRectMake(0, 0, 640, 480); CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; refreshImage(context, destRect); } |
doAnimation() en refreshImage() heb ik beide in C geschreven omdat ik daar momenteel nog beter mee overweg kan dan met Objective-C, en omdat het de bedoeling is om later de C++ code die ik al heb zowel voor windows als voor Mac OS te kunnen gebruiken.
In de C code heb ik een stuk geheugen om een backbuffer bij te houden in 32-bit RGBA formaat:
code:
1
| unsigned char testImage[width * height * 4]; |
In refreshImage zorg ik ervoor dat ik de eerste keer een CGContextRef aanmaak.
Ook maak ik een ImageRef aan zodat ik deze op het scherm kan tekenen.
C:
1
2
3
4
5
6
7
8
9
10
11
12
| void refreshImage(CGContextRef dest, CGRect destRect) { if (contextReference == NULL) { contextReference = CGBitmapContextCreate(testImage, width, height, 8, width*4, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), kCGImageAlphaNoneSkipLast); } imageReference = CGBitmapContextCreateImage(contextReference); CGContextDrawImage(dest, destRect, imageReference); CGImageRelease(imageReference); } |
In doAnimation() ga ik alle pixels af en geef deze elke frame een andere kleur.
Dit alles werkt perfect, maar vergt voor 640x480 wel 50% cpu usage, zonder dat er al iets bijzonders gebeurd. Dat is natuurlijk veel te veel om bruikbaar te zijn, dus ging ik op zoek naar de oorzaak.
-CGBitmapContextCreateImage slechts 1x uitvoeren ipv elke frame:
Hierdoor zat ik aan 30% cpu usage. Dit is dus een behoorlijk zware functie. Helaas is zo'n CGImageRef een kopie van de bitmap, en door deze functie er tussenuit te laten werd mijn animatie dus niet meer geupdate.
Voor zover ik heb kunnen vinden is het ook niet mogelijk om de geheugenbuffer van een CGImageRef zelf aan te passen.
-CGContextDrawImage weglaten: 22%. 1x een 640x480 bitmap blitten kost dus ook al 8% cpu
-doAnimation verwijderen: 16%. For lus over 640x480 pixels kost dus een 6%
-setNeedsDisplay 40x per seconde uitvoeren zonder verder iets opnieuw te tekenen kost dus 16%, wat mij dus erg veel lijkt
Full-screen een video in QuickTime bekijken kost overigens slechts 30%, dus het is wel duidelijk dat er snellere oplossingen moeten bestaan.
Heeft iemand hier misschien ervaring mee? Zoals gezegd werkt deze techniek dus prima op veel tragere windows machines, maar helaas heb ik nog geen Mac OS voorbeelden gevonden die iets gelijkaardigs doen.
OpenGL of Quartz 2D Extreme heb ik ook al even bekeken, maar nog niet geprobeerd. Volgens sommige bronnen zou dit ook niet zo goed gaan met een mindere grafische kaart (de gewone macbooks bijvoorbeeld). In dat geval zou het ook geen oplossing zijn.
Ik heb ook al gedacht aan Core Video, en dan zelf een video source aan te bieden (voor video werkt het aanbieden van pixelbuffers blijkbaar wel goed) maar daar lijkt de documentatie die ik tot nog toe gevonden heb ook erg beperkt van, en misschien is het ook wel overkill voor wat ik wil bereiken.