kCGImageAlphaNoneSkipFirst
There is no alpha channel. If the total size of the pixel is greater than the space required for the number of color components in the color space, the most significant bits are ignored.
kCGBitmapByteOrder32Little
32-bit, little endian format.
1 2 3 4 5 | 000000RR 0000GG00 00BB0000 -------- 00BBGGRR |
1 2 3 4 5 6 | 000000RR 000000GG 000000BB 000000xx -------- 000000?? |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | while (running) { uint64 LastCounter = mach_absolute_time(); static mach_timebase_info_data_t sTimebaseInfo; if ( sTimebaseInfo.denom == 0 ) { (void) mach_timebase_info(&sTimebaseInfo); } //do Stuff uint64 EndCounter = mach_absolute_time(); uint64 CyclesElapsed = EndCounter - LastCounter; uint64 NanosecondsElapsed = CyclesElapsed * sTimebaseInfo.numer / sTimebaseInfo.denom; float SecondsElapsed = NanosecondsElapsed / 1000000000.0; int FPS = ( float )( 1000000000.0 / NanosecondsElapsed ); printf("CyclesElapsed: %llu, TimeElapsed: %f, FPS: %f\n", CyclesElapsed, SecondsElapsed, FPS); LastCounter = EndCounter; } |
1 | clang -Wall -framework Cocoa -o build/main code/main.m |
1 2 3 | ProcessEvents(); RenderWeirdGradient(&GlobalBackbuffer, XOffset, YOffset); [view setNeedsDisplay:YES]; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | void ProcessEvents() { @autoreleasepool { NSEvent *event; int speed = 255/20; do { event = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES]; if (!event) { //break; } switch ([event type]) { case NSKeyUp: case NSKeyDown: { int hotkeyMask = NSCommandKeyMask | NSAlternateKeyMask | NSControlKeyMask | NSAlphaShiftKeyMask; if ([event modifierFlags] & hotkeyMask) { // Handle events like cmd+q etc [NSApp sendEvent:event]; break; } // Handle normal keyboard events in place. int isDown = ([event type] == NSKeyDown); switch ([event keyCode]) { case 13: { // W } break; case 0: { // A } break; case 1: { // S } break; case 2: { // D } break; case 12: { // Q } break; case 14: { // E } break; case 126: { // Up YOffset = YOffset + speed; } break; case 123: { // Left XOffset = XOffset + speed; } break; case 125: { // Down YOffset = YOffset - speed; } break; case 124: { // Right XOffset = XOffset - speed; } break; case 53: { // Esc } break; case 49: { // Space } break; default: { // Uncomment to learn your keys: //NSLog(@"Unhandled key: %d", [event keyCode]); } break; } } break; default: { // Handle events like app focus/unfocus etc [NSApp sendEvent:event]; } break; } } while (event); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | internal void RenderWeirdGradient(osx_offscreen_buffer *Buffer, int BlueOffset, int GreenOffset) { int Pitch = Buffer->Width * Buffer->BytesPerPixel; uint8 *Row = (uint8 *)Buffer->Memory; for(int Y = 0; Y < Buffer->Height; ++Y) { uint32 *Pixel = (uint32 *)Row; for(int X = 0; X < Buffer->Width; ++X) { uint8 Red = 0; uint8 Green = (Y + GreenOffset); uint8 Blue = (X + BlueOffset); *Pixel++ = Red | Green << 8 | Blue << 16 ; // RR GG BB xx // xx BB GG RR } Row += Pitch; } } |
1 2 3 4 5 6 7 8 9 10 11 12 | - (void)drawRect:(NSRect)dirtyRect { CGContextRef gctx = [[NSGraphicsContext currentContext] graphicsPort]; CGRect myBoundingBox; myBoundingBox = CGRectMake(0, 0, GlobalBackbuffer.Width, GlobalBackbuffer.Height); CGImageRef backImage = CGBitmapContextCreateImage(backbuffer); CGContextDrawImage(gctx, myBoundingBox, backImage); CGImageRelease(backImage); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | - (id)initWithFrame:(NSRect)frameRect { self = [super initWithFrame:frameRect]; if (self) { int bitmapBytesPerRow; GlobalBackbuffer.Width = window.frame.size.width; GlobalBackbuffer.Height = window.frame.size.height; GlobalBackbuffer.BytesPerPixel = 4; int BitmapMemorySize = GlobalBackbuffer.Width * GlobalBackbuffer.BytesPerPixel * GlobalBackbuffer.Height; GlobalBackbuffer.Memory = mmap(0, BitmapMemorySize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0); CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); bitmapBytesPerRow = GlobalBackbuffer.Width * 4; backbuffer = CGBitmapContextCreate(GlobalBackbuffer.Memory, GlobalBackbuffer.Width, GlobalBackbuffer.Height, 8, bitmapBytesPerRow, colorSpace, kCGImageAlphaNoneSkipLast| kCGBitmapByteOrder32Little); CGColorSpaceRelease(colorSpace); } return self; } |
1 2 3 4 5 6 7 8 9 | CyclesElapsed: 383199, TimeElapsed: 0.000383, FPS: 2609.609902 CyclesElapsed: 196884, TimeElapsed: 0.000197, FPS: 5079.133875 CyclesElapsed: 191598, TimeElapsed: 0.000192, FPS: 5219.258875 CyclesElapsed: 191310, TimeElapsed: 0.000191, FPS: 5227.118250 CyclesElapsed: 190641, TimeElapsed: 0.000191, FPS: 5245.462000 CyclesElapsed: 190246, TimeElapsed: 0.000190, FPS: 5256.352625 CyclesElapsed: 189976, TimeElapsed: 0.000190, FPS: 5263.821375 CyclesElapsed: 220344, TimeElapsed: 0.000220, FPS: 4538.356530 CyclesElapsed: 61552480, TimeElapsed: 0.061552, FPS: 16.246296 |
adge
Maybe its normal that [view SetNeedsDisplay:YES] slows things down?
adge
This is the code for drawRect:
1 2 3 4 5 6 7 8 9 10 11 12 - (void)drawRect:(NSRect)dirtyRect { CGContextRef gctx = [[NSGraphicsContext currentContext] graphicsPort]; CGRect myBoundingBox; myBoundingBox = CGRectMake(0, 0, GlobalBackbuffer.Width, GlobalBackbuffer.Height); CGImageRef backImage = CGBitmapContextCreateImage(backbuffer); CGContextDrawImage(gctx, myBoundingBox, backImage); CGImageRelease(backImage); }
And this is my View's setup code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 - (id)initWithFrame:(NSRect)frameRect { self = [super initWithFrame:frameRect]; if (self) { int bitmapBytesPerRow; GlobalBackbuffer.Width = window.frame.size.width; GlobalBackbuffer.Height = window.frame.size.height; GlobalBackbuffer.BytesPerPixel = 4; int BitmapMemorySize = GlobalBackbuffer.Width * GlobalBackbuffer.BytesPerPixel * GlobalBackbuffer.Height; GlobalBackbuffer.Memory = mmap(0, BitmapMemorySize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0); CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); bitmapBytesPerRow = GlobalBackbuffer.Width * 4; backbuffer = CGBitmapContextCreate(GlobalBackbuffer.Memory, GlobalBackbuffer.Width, GlobalBackbuffer.Height, 8, bitmapBytesPerRow, colorSpace, kCGImageAlphaNoneSkipLast| kCGBitmapByteOrder32Little); CGColorSpaceRelease(colorSpace); } return self; }
I wrote some code to call [view setNeedsDisplay:YES] every tenth cycle and this is what the console prints out:
1 2 3 4 5 6 7 8 9 CyclesElapsed: 383199, TimeElapsed: 0.000383, FPS: 2609.609902 CyclesElapsed: 196884, TimeElapsed: 0.000197, FPS: 5079.133875 CyclesElapsed: 191598, TimeElapsed: 0.000192, FPS: 5219.258875 CyclesElapsed: 191310, TimeElapsed: 0.000191, FPS: 5227.118250 CyclesElapsed: 190641, TimeElapsed: 0.000191, FPS: 5245.462000 CyclesElapsed: 190246, TimeElapsed: 0.000190, FPS: 5256.352625 CyclesElapsed: 189976, TimeElapsed: 0.000190, FPS: 5263.821375 CyclesElapsed: 220344, TimeElapsed: 0.000220, FPS: 4538.356530 CyclesElapsed: 61552480, TimeElapsed: 0.061552, FPS: 16.246296
Maybe its normal that [view SetNeedsDisplay:YES] slows things down?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | - CGBitmapContextCreateImage: 44320 - CGContextDrawImage: 10572902 - RenderWeirdGradient: 2187883 total elapsed: 15514618 seconds: 0.015515, fps: 64.455345 - CGBitmapContextCreateImage: 44688 - CGContextDrawImage: 10001166 - RenderWeirdGradient: 2435154 total elapsed: 19695196 seconds: 0.019695, fps: 50.773804 - CGBitmapContextCreateImage: 52355 - CGContextDrawImage: 10374861 - RenderWeirdGradient: 2161021 total elapsed: 15187978 seconds: 0.015188, fps: 65.841553 - CGBitmapContextCreateImage: 44878 - CGContextDrawImage: 9977201 - RenderWeirdGradient: 2225293 total elapsed: 19302945 seconds: 0.019303, fps: 51.805569 - CGBitmapContextCreateImage: 44696 - CGContextDrawImage: 10377475 - RenderWeirdGradient: 2468919 total elapsed: 21063529 seconds: 0.021064, fps: 47.475430 - CGBitmapContextCreateImage: 54630 - CGContextDrawImage: 10288651 - RenderWeirdGradient: 2155761 total elapsed: 15267769 seconds: 0.015268, fps: 65.497452 - CGBitmapContextCreateImage: 47171 - CGContextDrawImage: 10221998 - RenderWeirdGradient: 2153584 total elapsed: 15312094 seconds: 0.015312, fps: 65.307854 - CGBitmapContextCreateImage: 46089 - CGContextDrawImage: 10290942 - RenderWeirdGradient: 2364717 total elapsed: 15629533 seconds: 0.015630, fps: 63.981434 |
adge
Well thats weird. So why am I getting these low FPS then? I'm running it on a mid 2012 MBP retina. Maybe the machine is getting old but its still quite fast.
adge
Are you using the terminal for compilation? If so can you show me the command?
adge
As I said I'm trying to swap to OpenGl anyway however it seems super complicated. Trying to take Jeff Buck's platform layer and the
Handmade Hero OpenGLDisplayBuffer function as a reference but it seems super complicated.
So will need some more time until it's working.
The main loop in Casey's game code normally calls Win32DisplayBufferInWindow which then calls SoftwareRenderCommands and OpenGLDisplayBitmap.
Do I need SoftwareRenderCommands or all the stuff that is happening in Win32DisplayBufferInWindow? Or is everything i need to render a bitmap located in OpenGlDisplayBitmap?
I really have no idea how OpenGL works.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | - (void)prepareOpenGL { [super prepareOpenGL]; _textureWidth = (GLsizei)self.bounds.size.width; _textureHeight = (GLsizei)self.bounds.size.height; _bitmapPixels = (uint32_t *)malloc(_textureWidth * _textureHeight * sizeof(uint32_t)); memset(_bitmapPixels, 0, _textureWidth * _textureHeight * sizeof(uint32_t)); glClearColor(0.f, 0.f, 0.f, 1.f); // Set pixel packing to byte-aligned for reading textures. glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glGenTextures(1, &_bitmapTexture); glBindTexture(GL_TEXTURE_2D, _bitmapTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _textureWidth, _textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); GLenum glError = glGetError(); NSAssert(glError == GL_NO_ERROR, @"OpenGl error loading texture: %d", glError); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); // Enable vsync. GLint swapInt = 1; [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); CVDisplayLinkSetOutputCallback(_displayLink, &DisplayCallback, (__bridge void *)self); CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat); CVDisplayLinkStart(_displayLink); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | - (void)render { [[self openGLContext] makeCurrentContext]; CGLLockContext([[self openGLContext] CGLContextObj]); glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, _bitmapTexture); glEnable(GL_TEXTURE_2D); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureWidth, _textureHeight, GL_RGBA, GL_UNSIGNED_BYTE, _bitmapPixels); glBegin(GL_QUADS); glTexCoord2f(0.f, 1.f); glVertex2f(-1.f, -1.f); glTexCoord2f(0.f, 0.f); glVertex2f(-1.f, 1.f); glTexCoord2f(1.f, 0.f); glVertex2f(1.f, 1.f); glTexCoord2f(1.f, 1.f); glVertex2f(1.f, -1.f); glEnd(); CGLFlushDrawable([[self openGLContext] CGLContextObj]); CGLUnlockContext([[self openGLContext] CGLContextObj]); } |
1 2 3 4 5 6 7 8 9 10 11 12 | NSOpenGLPixelFormatAttribute attrs[] = { NSOpenGLPFAAccelerated, NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, 0 }; NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; NSCAssert(pixelFormat, @"No OpenGL pixel format"); FSOpenGLView *openGLView = [[FSOpenGLView alloc] initWithFrame:[window contentRectForFrameRect:windowRect] pixelFormat:pixelFormat]; window.contentView = openGLView; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | global_variable NSOpenGLContext *GlobalGLContext; global_variable GLuint OpenGLDefaultInternalTextureFormat; global_variable GLuint TextureId; int main(int argc, const char * argv[]) { //Setup OpenGL: NSOpenGLPixelFormatAttribute openGLAttributes[] = { NSOpenGLPFAAccelerated, NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, 0 }; NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes: openGLAttributes]; GlobalGLContext = [[NSOpenGLContext alloc] initWithFormat: format shareContext:NULL]; [format release]; GLint swapInt = 1; [GlobalGLContext setValues: &swapInt forParameter: NSOpenGLCPSwapInterval]; [GlobalGLContext setView: [window contentView]]; [GlobalGLContext makeCurrentContext]; //SetupOpenGL: glGenTextures(1, &TextureId); OpenGLDefaultInternalTextureFormat = GL_RGBA8; OpenGLDefaultInternalTextureFormat = 0x8C43; //GL_SRGB8_ALPHA8; glEnable(GL_FRAMEBUFFER_SRGB); while(running) { [GlobalGLContext makeCurrentContext]; ProcessEvents(); RenderWeirdGradient(&GlobalBackbuffer, XOffset, YOffset); OpenGLDisplayBitmap(GlobalBackbuffer.Width, GlobalBackbuffer.Height, GlobalBackbuffer.Memory, GlobalBackbuffer.Pitch, WindowDimension.Width, WindowDimension.Height, TextureId); [GlobalGLContext flushBuffer]; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | internal void OpenGLDisplayBitmap(int32 Width, int32 Height, void *Memory, int Pitch, int32 WindowWidth, int32 WindowHeight, GLuint BlitTexture) { //Assert(Pitch == (Width*4)); glViewport(0, 0, Width, Height); glDisable(GL_SCISSOR_TEST); glBindTexture(GL_TEXTURE_2D, BlitTexture); // // glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, Width, Height, 0, // GL_BGRA_EXT, GL_UNSIGNED_BYTE, GlobalBackbuffer.Memory); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GlobalBackbuffer.Memory); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_2D); glClearColor(1.0f, 0.0f, 1.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_TEXTURE); glLoadIdentity(); OpenGLSetScreenspace(Width, Height); // TODO(casey): Decide how we want to handle aspect ratio - black bars or crop? union v2 MinP = {0, 0}; union v2 MaxP = {(r32)Width, (r32)Height}; union v4 Color = {1, 1, 1, 1}; OpenGLRectangle(MinP, MaxP, Color); glBindTexture(GL_TEXTURE_2D, 0); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | inline void OpenGLSetScreenspace(int32 Width, int32 Height) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glMatrixMode(GL_PROJECTION); r32 a = SafeRatio1(2.0f, (r32)Width); r32 b = SafeRatio1(2.0f, (r32)Height); r32 Proj[] = { a, 0, 0, 0, 0, b, 0, 0, 0, 0, 1, 0, -1, -1, 0, 1, }; glLoadMatrixf(Proj); } inline void OpenGLRectangle(union v2 MinP, union v2 MaxP, union v4 PremulColor, union v2 MinUV = V2(0, 0), v2 MaxUV = V2(1, 1)) { glBegin(GL_TRIANGLES); glColor4f(PremulColor.r, PremulColor.g, PremulColor.b, PremulColor.a); // NOTE(casey): Lower triangle glTexCoord2f(MinUV.x, MinUV.y); glVertex2f(MinP.x, MinP.y); glTexCoord2f(MaxUV.x, MinUV.y); glVertex2f(MaxP.x, MinP.y); glTexCoord2f(MaxUV.x, MaxUV.y); glVertex2f(MaxP.x, MaxP.y); // NOTE(casey): Upper triangle glTexCoord2f(MinUV.x, MinUV.y); glVertex2f(MinP.x, MinP.y); glTexCoord2f(MaxUV.x, MaxUV.y); glVertex2f(MaxP.x, MaxP.y); glTexCoord2f(MinUV.x, MaxUV.y); glVertex2f(MinP.x, MaxP.y); glEnd(); } |
1 2 3 | inline void OpenGLRectangle(union v2 MinP, union v2 MaxP, union v4 PremulColor, union v2 MinUV = V2(0, 0), v2 MaxUV = V2(1, 1)) { |