// // GraphicsDevice.m // XNI // // Created by Matej Jan on 27.7.10. // Copyright 2010 Retronator. All rights reserved. // #import "GraphicsDevice.h" #import "GraphicsDevice+Internal.h" #import #import #import "Retronator.Xni.Framework.h" #import "Retronator.Xni.Framework.Graphics.h" #import "XniSamplerEventArgs.h" #import "TextureCollection+Internal.h" #import "SamplerStateCollection+Internal.h" #import "IndexBuffer+Internal.h" #import "VertexBuffer+Internal.h" #import "RenderTarget2D+Internal.h" @interface GraphicsDevice(){ BOOL rrt; } + (void) getFormat:(GLenum*)format AndType:(GLenum*)type ForSurfaceFormat:(SurfaceFormat)surfaceFormat; - (void) setData:(void*)data size:(int)sizeInBytes toBufferId:(uint)buffer resourceType:(ResourceType)resourceType bufferUsage:(BufferUsage)bufferUsage; @end @implementation GraphicsDevice - (id) initWithGame:(Game*)theGame { self = [super init]; if (self) { game = theGame; multisampling = NO; // Create an OpenGL ES context context = [self createContext]; if (!context || ![EAGLContext setCurrentContext:context]) { [self release]; return nil; } if (multisampling) { // Create resolve framebuffer object. glGenFramebuffers(1, &resolveFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, resolveFramebuffer); // Create resolve color buffer. glGenRenderbuffers(1, &resolveRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, resolveRenderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, resolveRenderbuffer); } // Create default framebuffer object. glGenFramebuffers(1, &defaultFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer); // Create the color buffer. glGenRenderbuffers(1, &colorRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer); // Create the depth buffer. glGenRenderbuffers(1, &depthRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer); // Create sampler states and texture collections and handle changes. samplerStates = [[SamplerStateCollection alloc] init]; textures = [[TextureCollection alloc] init]; [samplerStates.samplerStateChanged subscribeDelegate:[Delegate delegateWithTarget:self Method:@selector(applySamplerState:eventArgs:)]]; [textures.textureChanged subscribeDelegate:[Delegate delegateWithTarget:self Method:@selector(applySamplerState:eventArgs:)]]; // Do the initial reset. viewport = [[Viewport alloc] init]; [self reset]; // Initialize defaults. self.blendFactor = [Color white]; self.blendState = [BlendState opaque]; self.depthStencilState = [DepthStencilState defaultDepth]; glDepthRangef(0, 1); graphicsDeviceStatus = GraphicsDeviceStatusNormal; self.indices = nil; self.rasterizerState = [RasterizerState cullCounterClockwise]; self.referenceStencil = 0; activeTextureIndex = -1; [samplerStates setItem:[SamplerState linearClamp] atIndex:0]; // Create events. deviceResetting = [[Event alloc] init]; deviceReset = [[Event alloc] init]; vertices = [[NSMutableArray alloc] init]; } return self; } #define FLAG_BLOCK(variable, parameter) if (variable) { glEnable(parameter); } else { glDisable(parameter);} @synthesize blendFactor; @synthesize blendState; - (void) setBlendState:(BlendState*)value { if (value != blendState) { BlendState *old = blendState; blendState = [value retain]; // Apply the blend state. if (old.colorSourceBlend != blendState.colorSourceBlend || old.colorDestinationBlend != blendState.colorDestinationBlend || old.alphaSourceBlend != blendState.alphaSourceBlend || old.alphaDestinationBlend != blendState.alphaDestinationBlend) { glBlendFuncSeparate(blendState.colorSourceBlend, blendState.colorDestinationBlend, blendState.alphaSourceBlend, blendState.alphaDestinationBlend); } if (old.colorBlendFunction != blendState.colorBlendFunction || old.alphaBlendFunction != blendState.alphaBlendFunction) { glBlendEquationSeparate(blendState.colorBlendFunction, blendState.alphaBlendFunction); } [old release]; } } @synthesize depthStencilState; - (void) setDepthStencilState:(DepthStencilState*)value { if (value != depthStencilState) { DepthStencilState *old = depthStencilState; depthStencilState = [value retain]; // Apply depth state. if (old.depthBufferEnable != depthStencilState.depthBufferEnable) { FLAG_BLOCK(depthStencilState.depthBufferEnable, GL_DEPTH_TEST) } if (old.depthBufferFunction != depthStencilState.depthBufferFunction) { glDepthFunc(depthStencilState.depthBufferFunction); } if (old.depthBufferWriteEnable != depthStencilState.depthBufferWriteEnable) { glDepthMask(depthStencilState.depthBufferWriteEnable); } // TODO: Apply stencil state. [old release]; } } @synthesize graphicsDeviceStatus; @synthesize graphicsProfile; @synthesize indices; @synthesize rasterizerState; - (void) setRasterizerState:(RasterizerState*)value { if (value != rasterizerState) { [value retain]; [rasterizerState release]; rasterizerState = value; // Apply fill mode. // Apply cull mode. if (value.cullMode == CullModeNone) { glDisable(GL_CULL_FACE); } else { glEnable(GL_CULL_FACE); glFrontFace(value.cullMode); } } } @synthesize referenceStencil; @synthesize samplerStates; @synthesize textures; @synthesize viewport; + (int) getNumberOfVerticesForPrimitiveType:(PrimitiveType)primitiveType primitiveCount:(int)primitiveCount { switch (primitiveType) { case PrimitiveTypeLineStrip: return primitiveCount + 1; case PrimitiveTypeLineList: return 2 * primitiveCount; case PrimitiveTypeTriangleStrip: return primitiveCount + 2; case PrimitiveTypeTriangleList: return 3 * primitiveCount; default: [NSException raise:@"NotImplementedException" format:@"The primitive type %i is not yet implemented.", primitiveType]; return 0; } } @synthesize deviceReset; @synthesize deviceResetting; // Presentation - (void) reset { [deviceResetting raiseWithSender:self]; CAEAGLLayer *layer = (CAEAGLLayer*)game.window.handle; if (multisampling) { // Allocate resolve buffer backing based on the current layer size. glBindFramebuffer(GL_FRAMEBUFFER, resolveFramebuffer); glBindRenderbuffer(GL_RENDERBUFFER, resolveRenderbuffer); [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer]; glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){ NSLog(@"Failed to make complete framebuffer object %x.", glCheckFramebufferStatus(GL_FRAMEBUFFER)); } else { NSLog(@"Created a resolve buffer with dimensions: %ix%i.", backingWidth, backingHeight); } // Allocate multisampling buffers based on resolve buffer. // Create default framebuffer object. glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer); // Create the color buffer. glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_RGBA8_OES, backingWidth, backingHeight); // Create the depth buffer. glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, backingWidth, backingHeight); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){ NSLog(@"Failed to make complete framebuffer object %x.", glCheckFramebufferStatus(GL_FRAMEBUFFER)); } else { NSLog(@"Created a multisample render target with dimensions: %ix%i.", backingWidth, backingHeight); } } else { // Allocate render buffer backing based on the current layer size. glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer]; glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight); glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, backingWidth, backingHeight); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){ NSLog(@"Failed to make complete framebuffer object %x.", glCheckFramebufferStatus(GL_FRAMEBUFFER)); } else { NSLog(@"Created a device with dimensions: %ix%i.", backingWidth, backingHeight); } } // Set viewport. glViewport(0, 0, backingWidth, backingHeight); self.viewport.x = 0; self.viewport.y = 0; self.viewport.width = backingWidth; self.viewport.height = backingHeight; // Set state defaults. glEnable(GL_BLEND); [deviceReset raiseWithSender:self]; } - (void) present { if (multisampling) { // Resolve the rendering into the resolve buffer. glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, resolveFramebuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, defaultFramebuffer); glResolveMultisampleFramebufferAPPLE(); const GLenum discards[] = {GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT}; glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE,2,discards); glBindRenderbuffer(GL_RENDERBUFFER, resolveRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER]; glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer); } else { glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER]; } } // Render buffers - (void) clearWithColor:(Color *)color { [self clearWithOptions:ClearOptionsTarget | ClearOptionsDepthBuffer color:color depth:1 stencil:0]; } - (void) clearWithOptions:(ClearOptions)options color:(Color *)color depth:(float)depth stencil:(int)stencil { glDepthMask(GL_TRUE); if (color) { glClearColor(color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0); } glClearDepthf(depth); glClearStencil(stencil); glClear(options); glDepthMask(depthStencilState.depthBufferWriteEnable); } // Vertex buffers - (NSArray*) getVertexBuffers { return [NSArray arrayWithArray:vertices]; } - (void) setVertexBuffer:(VertexBuffer*)vertexBuffer { VertexBufferBinding *binding = [[VertexBufferBinding alloc] initWithVertexBuffer:vertexBuffer]; if ([vertices count]>0) { [vertices replaceObjectAtIndex:0 withObject:binding]; } else { [vertices addObject:binding]; } [binding release]; } - (void) setVertexBuffer:(VertexBuffer*)vertexBuffer vertexOffset:(int)vertexOffset { VertexBufferBinding *binding = [[VertexBufferBinding alloc] initWithVertexBuffer:vertexBuffer vertexOffset:vertexOffset]; [vertices insertObject:binding atIndex:0]; [binding release]; } - (void) setVertexBuffers:(VertexBufferBinding*)vertexBuffer, ... { if (vertexBuffer != nil) { va_list args; va_start(args, vertexBuffer); VertexBufferBinding *binding = vertexBuffer; for (int i = 0; binding; i++) { [vertices insertObject:binding atIndex:i]; binding = va_arg(args, VertexBufferBinding*); } va_end(args); } } // Low level methods - (uint) createTexture { GLuint textureId; glGenTextures(1, &textureId); return textureId; } - (void) releaseTexture:(uint)textureId { glDeleteTextures(1, &textureId); } - (void) setData:(void*)data toTexture2D:(Texture2D*)texture SourceRectangle:(Rectangle*)rect level:(int)level { GLenum format, type; [GraphicsDevice getFormat:&format AndType:&type ForSurfaceFormat:texture.format]; glBindTexture(GL_TEXTURE_2D, texture.textureId); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); if (rect) { glTexSubImage2D(GL_TEXTURE_2D, level, rect.x, rect.y, rect.width, rect.height, format, type, data); } else { glTexImage2D(GL_TEXTURE_2D, level, format, texture.width, texture.height, 0, format, type, data); } glBindTexture(GL_TEXTURE_2D, 0); } - (uint) createBuffer { GLuint bufferId; glGenBuffers(1, &bufferId); return bufferId; } - (void) setData:(void*)data toIndexBuffer:(IndexBuffer*)buffer { int sizeInBytes = buffer.indexElementSize * buffer.indexCount; [self setData:data size:sizeInBytes toBufferId:buffer.bufferID resourceType:ResourceTypeIndexBuffer bufferUsage:buffer.bufferUsage]; } - (void) setData:(void*)data toVertexBuffer:(VertexBuffer*)buffer { int sizeInBytes = buffer.vertexDeclaration.vertexStride * buffer.vertexCount; [self setData:data size:sizeInBytes toBufferId:buffer.bufferID resourceType:ResourceTypeVertexBuffer bufferUsage:buffer.bufferUsage]; } - (void) setData:(void *)data size:(int)sizeInBytes toBufferId:(uint)buffer resourceType:(ResourceType)resourceType bufferUsage:(BufferUsage)bufferUsage { glBindBuffer(resourceType, buffer); glBufferData(resourceType, sizeInBytes, data, bufferUsage); glBindBuffer(resourceType, 0); } - (void)releaseBuffer:(uint)bufferId { glDeleteBuffers(1, &bufferId); } // Profile specific - (EAGLContext*) createContext { return nil; } - (void) drawPrimitivesOfType:(PrimitiveType)primitiveType startVertex:(int)startVertex primitiveCount:(int)primitiveCount {} - (void) drawIndexedPrimitivesOfType:(PrimitiveType)primitiveType baseVertex:(int)baseVertex minVertexIndex:(int)minVertexIndex numVertices:(int)numVertices startIndex:(int)startIndex primitiveCount:(int)primitiveCount {} - (void) drawUserPrimitivesOfType:(PrimitiveType)primitiveType vertexData:(VertexArray*)vertexData vertexOffset:(int)vertexOffset primitiveCount:(int)primitiveCount {} - (void) drawUserPrimitivesOfType:(PrimitiveType)primitiveType vertexData:(void*)vertexData vertexOffset:(int)vertexOffset primitiveCount:(int)primitiveCount vertexDeclaration:(VertexDeclaration*) vertexDeclaration {} - (void) drawUserIndexedPrimitivesOfType:(PrimitiveType)primitiveType vertexData:(VertexArray*)vertexData vertexOffset:(int)vertexOffset numVertices:(int)numVertices indexData:(IndexArray*)indexData indexOffset:(int)indexOffset primitiveCount:(int)primitiveCount {} - (void) drawUserIndexedPrimitivesOfType:(PrimitiveType)primitiveType vertexData:(void*)vertexData vertexOffset:(int)vertexOffset numVertices:(int)numVertices shortIndexData:(void*)indexData indexOffset:(int)indexOffset primitiveCount:(int)primitiveCount vertexDeclaration:(VertexDeclaration*) vertexDeclaration {} // Private methods + (void) getFormat:(GLenum*)format AndType:(GLenum*)type ForSurfaceFormat:(SurfaceFormat)surfaceFormat { switch (surfaceFormat) { case SurfaceFormatColor: *format = GL_RGBA; *type = GL_UNSIGNED_BYTE; break; case SurfaceFormatAlpha8: *format = GL_ALPHA; *type = GL_UNSIGNED_BYTE; break; case SurfaceFormatRgb565: *format = GL_RGB; *type = GL_UNSIGNED_SHORT_5_6_5; break; case SurfaceFormatRgba4444: *format = GL_RGBA; *type = GL_UNSIGNED_SHORT_4_4_4_4; break; case SurfaceFormatRgba5551: *format = GL_RGBA; *type = GL_UNSIGNED_SHORT_5_5_5_1; break; default: break; } } - (void) applySamplerState:(id)sender eventArgs:(XniSamplerEventArgs*)e { if (activeTextureIndex != e.samplerIndex) { activeTextureIndex = e.samplerIndex; glActiveTexture(GL_TEXTURE0 + e.samplerIndex); } Texture *texture = [textures itemAtIndex:e.samplerIndex]; if (texture) { glBindTexture(GL_TEXTURE_2D, texture.textureId); } else { glBindTexture(GL_TEXTURE_2D, 0); } SamplerState *samplerState = [samplerStates itemAtIndex:e.samplerIndex]; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, samplerState.addressU); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, samplerState.addressV); uint minFilter; uint magFilter; switch (samplerState.filter) { case TextureFilterPoint: minFilter = GL_NEAREST; magFilter = GL_NEAREST; break; case TextureFilterLinear: default: minFilter = GL_LINEAR; magFilter = GL_LINEAR; break; case TextureFilterPointMipLinear: minFilter = GL_NEAREST_MIPMAP_LINEAR; magFilter = GL_NEAREST_MIPMAP_LINEAR; break; case TextureFilterLinearMipPoint: minFilter = GL_LINEAR_MIPMAP_NEAREST; magFilter = GL_LINEAR_MIPMAP_NEAREST; break; case TextureFilterMinLinearMagPointMipLinear: minFilter = GL_LINEAR; magFilter = GL_NEAREST_MIPMAP_LINEAR; break; case TextureFilterMinLinearMagPointMipPoint: minFilter = GL_LINEAR_MIPMAP_NEAREST; magFilter = GL_NEAREST; break; case TextureFilterMinPointMagLinearMipLinear: minFilter = GL_NEAREST_MIPMAP_LINEAR; magFilter = GL_LINEAR; break; case TextureFilterMinPointMagLinearMipPoint: minFilter = GL_NEAREST; magFilter = GL_LINEAR_MIPMAP_NEAREST; break; case TextureFilterAnisotropic: // TODO: Have no idea yet how to do anisotropic. minFilter = GL_LINEAR; magFilter = GL_LINEAR; break; } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); } - (void) setRenderTarget:(RenderTarget2D*)renderTarget{ if (renderTarget == nil) { if (rrt) { //We had render target before now we have to flip it vertically rrt = NO; } glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer); }else{ GLuint rtFramebuffer = [renderTarget colorFramebuffer]; GLuint rtRenderbuffer = [renderTarget colorRenderbuffer]; GLenum format, type; [GraphicsDevice getFormat:&format AndType:&type ForSurfaceFormat:renderTarget.format]; // RENDER TO TEXTURE BUFFER // This is the buffer we will be rendering to and using as a texture // on out screen plane glBindFramebufferOES(GL_FRAMEBUFFER_OES, rtFramebuffer); // glBindRenderbufferOES(GL_RENDERBUFFER_OES, rtRenderbuffer); // // glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer); // glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, renderTarget.width, renderTarget.height); // glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer); // create the texture object glBindTexture(GL_TEXTURE_2D, renderTarget.textureId); // set the texture parameter filtering (feel free to use other TexParams) // you have to do this, forgetting to do this will make it not work. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); // fill the texture data (the max texture size needs to be power of 2) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, renderTarget.width, renderTarget.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // attach the frameBuffer to the texture object glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, renderTarget.textureId, 0); //glOrthof(0, renderTarget.width, renderTarget.height, 0, -1, 1); // CHECK FRAME BUFFER STATUS HERE // GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER); // if(status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT){ // NSLog(@"GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); // }else if(status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT){ // NSLog(@"GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); // }else if(status == GL_FRAMEBUFFER_UNSUPPORTED){ // NSLog(@"GL_FRAMEBUFFER_UNSUPPORTED"); // }else if(status == GL_FRAMEBUFFER_COMPLETE){ // NSLog(@"GL_FRAMEBUFFER_COMPLETE"); // } rrt = YES; } } - (RenderTarget2D*) getRenderTarget{ return nil; } - (void) dealloc{ [blendState release]; [depthStencilState release]; [rasterizerState release]; [samplerStates release]; [textures release]; [viewport release]; [deviceResetting release]; [deviceReset release]; [vertices release]; [super dealloc]; } @end