Skip to content

Commit 2e90bd5

Browse files
committed
Faster vertex buffer management.
1 parent 9c1fa14 commit 2e90bd5

File tree

2 files changed

+124
-85
lines changed

2 files changed

+124
-85
lines changed

cocos2d/CCRenderer.m

Lines changed: 74 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -519,8 +519,8 @@ -(NSInteger)globalSortOrder
519519

520520
@implementation CCRenderer {
521521
GLuint _vao;
522-
GLuint _vbo;
523-
GLuint _ebo;
522+
CCGraphicsBuffer _vertexBuffer;
523+
CCGraphicsBuffer _elementBuffer;
524524

525525
CCRenderState *_renderState;
526526
NSDictionary *_blendOptions;
@@ -534,12 +534,6 @@ @implementation CCRenderer {
534534
NSMutableArray *_queueStack;
535535
__unsafe_unretained CCRenderCommandDraw *_lastDrawCommand;
536536

537-
CCVertex *_vertexes;
538-
GLsizei _vertexCount, _vertexCapacity;
539-
540-
GLushort *_elements;
541-
GLsizei _elementCount, _elementCapacity;
542-
543537
NSUInteger _statDrawCommands;
544538
}
545539

@@ -556,22 +550,15 @@ -(void)invalidateState
556550
-(instancetype)init
557551
{
558552
if((self = [super init])){
559-
glPushGroupMarkerEXT(0, "CCRenderer: Init");
560-
561-
glGenBuffers(1, &_vbo);
562-
glGenBuffers(1, &_ebo);
563-
564-
_vao = [CCShader createVAOforCCVertexBuffer:_vbo elementBuffer:_ebo];
553+
const NSUInteger CCRENDERER_INITIAL_VERTEX_CAPACITY = 16*1024;
554+
[self initBuffer:&_vertexBuffer capacity:CCRENDERER_INITIAL_VERTEX_CAPACITY elementSize:sizeof(CCVertex) type:GL_ARRAY_BUFFER];
555+
[self initBuffer:&_elementBuffer capacity:CCRENDERER_INITIAL_VERTEX_CAPACITY*1.5 elementSize:sizeof(uint16_t) type:GL_ELEMENT_ARRAY_BUFFER];
565556

557+
glPushGroupMarkerEXT(0, "CCRenderer: Init");
558+
_vao = [CCShader createVAOforCCVertexBuffer:(GLuint)_vertexBuffer.data elementBuffer:(GLuint)_elementBuffer.data];
566559
glPopGroupMarkerEXT();
567560

568561
_queue = [NSMutableArray array];
569-
570-
_vertexCapacity = 2*1024;
571-
_vertexes = calloc(_vertexCapacity, sizeof(*_vertexes));
572-
573-
_elementCapacity = 2*1024;
574-
_elements = calloc(_elementCapacity, sizeof(*_elements));
575562
}
576563

577564
return self;
@@ -580,15 +567,11 @@ -(instancetype)init
580567
-(void)dealloc
581568
{
582569
glPushGroupMarkerEXT(0, "CCRenderer: Dealloc");
583-
584570
glDeleteVertexArrays(1, &_vao);
585-
glDeleteBuffers(1, &_vbo);
586-
glDeleteBuffers(1, &_ebo);
587-
588571
glPopGroupMarkerEXT();
589572

590-
free(_vertexes);
591-
free(_elements);
573+
[self destroyBuffer:&_vertexBuffer];
574+
[self destroyBuffer:&_elementBuffer];
592575
}
593576

594577
static NSString *CURRENT_RENDERER_KEY = @"CCRendererCurrent";
@@ -704,79 +687,49 @@ -(void)enqueueClear:(GLbitfield)mask color:(GLKVector4)color4 depth:(GLclampf)de
704687
} globalSortOrder:globalSortOrder debugLabel:@"CCRenderer: Clear" threadSafe:YES];
705688
}
706689

707-
-(CCVertex *)ensureVertexCapacity:(NSUInteger)requestedCount
708-
{
709-
NSAssert(requestedCount > 0, @"Vertex count must be positive.");
710-
711-
GLsizei required = _vertexCount + (GLsizei)requestedCount;
712-
if(required > _vertexCapacity){
713-
// Double the size of the buffer until it fits.
714-
while(required >= _vertexCapacity) _vertexCapacity *= 2;
715-
716-
_vertexes = realloc(_vertexes, _vertexCapacity*sizeof(*_vertexes));
717-
}
718-
719-
// Return the triangle buffer pointer.
720-
return &_vertexes[_vertexCount];
721-
}
722-
723-
-(GLushort *)ensureElementCapacity:(NSUInteger)requestedCount
690+
-(CCRenderBuffer)enqueueTriangles:(NSUInteger)triangleCount andVertexes:(NSUInteger)vertexCount withState:(CCRenderState *)renderState globalSortOrder:(NSInteger)globalSortOrder;
724691
{
725-
NSAssert(requestedCount > 0, @"Element count must be positive.");
692+
// Need to record the first vertex or element index before pushing more vertexes.
693+
size_t firstVertex = _vertexBuffer.count;
694+
size_t firstElement = _elementBuffer.count;
726695

727-
GLsizei required = _elementCount + (GLsizei)requestedCount;
728-
if(required > _elementCapacity){
729-
// Double the size of the buffer until it fits.
730-
while(required >= _elementCapacity) _elementCapacity *= 2;
731-
732-
_elements = realloc(_elements, _elementCapacity*sizeof(*_elements));
733-
}
696+
size_t elementCount = 3*triangleCount;
697+
CCVertex *vertexes = CCGraphicsBufferPushElements(&_vertexBuffer, vertexCount, self);
698+
GLushort *elements = CCGraphicsBufferPushElements(&_elementBuffer, elementCount, self);
734699

735-
// Return the triangle buffer pointer.
736-
return &_elements[_elementCount];
737-
}
738-
739-
-(CCRenderBuffer)enqueueTriangles:(NSUInteger)triangleCount andVertexes:(NSUInteger)vertexCount withState:(CCRenderState *)renderState globalSortOrder:(NSInteger)globalSortOrder;
740-
{
741700
__unsafe_unretained CCRenderCommandDraw *previous = _lastDrawCommand;
742-
CCVertex *vertexes = [self ensureVertexCapacity:vertexCount];
743-
GLushort *elements = [self ensureElementCapacity:3*triangleCount];
744-
745701
if(previous && previous->_renderState == renderState && previous->_globalSortOrder == globalSortOrder){
746702
// Batch with the previous command.
747-
[previous batchElements:(GLsizei)(3*triangleCount)];
703+
[previous batchElements:(GLsizei)elementCount];
748704
} else {
749705
// Start a new command.
750-
CCRenderCommandDraw *command = [[CCRenderCommandDraw alloc] initWithMode:GL_TRIANGLES renderState:renderState first:(GLint)_elementCount elements:(GLsizei)(3*triangleCount) globalSortOrder:globalSortOrder];
706+
CCRenderCommandDraw *command = [[CCRenderCommandDraw alloc] initWithMode:GL_TRIANGLES renderState:renderState first:(GLint)firstElement elements:(GLsizei)elementCount globalSortOrder:globalSortOrder];
751707
[_queue addObject:command];
752708
_lastDrawCommand = command;
753709
}
754710

755-
CCRenderBuffer buffer = {vertexes, elements, _vertexCount};
756-
_vertexCount += vertexCount;
757-
_elementCount += 3*triangleCount;
758-
759711
_statDrawCommands++;
760-
return buffer;
712+
return (CCRenderBuffer){vertexes, elements, firstVertex};
761713
}
762714

763715
-(CCRenderBuffer)enqueueLines:(NSUInteger)lineCount andVertexes:(NSUInteger)vertexCount withState:(CCRenderState *)renderState globalSortOrder:(NSInteger)globalSortOrder;
764716
{
765-
CCVertex *vertexes = [self ensureVertexCapacity:vertexCount];
766-
GLushort *elements = [self ensureElementCapacity:2*lineCount];
717+
// Need to record the first vertex or element index before pushing more vertexes.
718+
size_t firstVertex = _vertexBuffer.count;
719+
size_t firstElement = _elementBuffer.count;
720+
721+
size_t elementCount = 2*lineCount;
722+
CCVertex *vertexes = CCGraphicsBufferPushElements(&_vertexBuffer, vertexCount, self);
723+
GLushort *elements = CCGraphicsBufferPushElements(&_elementBuffer, elementCount, self);
767724

768-
CCRenderCommandDraw *command = [[CCRenderCommandDraw alloc] initWithMode:GL_LINES renderState:renderState first:(GLint)_elementCount elements:(GLsizei)(2*lineCount) globalSortOrder:globalSortOrder];
725+
CCRenderCommandDraw *command = [[CCRenderCommandDraw alloc] initWithMode:GL_LINES renderState:renderState first:(GLint)firstElement elements:(GLsizei)elementCount globalSortOrder:globalSortOrder];
769726
[_queue addObject:command];
770727

771728
// Line drawing commands are currently intended for debugging and cannot be batched.
772729
_lastDrawCommand = nil;
773730

774-
CCRenderBuffer buffer = {vertexes, elements, _vertexCount};
775-
_vertexCount += vertexCount;
776-
_elementCount += 2*lineCount;
777-
778731
_statDrawCommands++;
779-
return buffer;
732+
return(CCRenderBuffer){vertexes, elements, firstVertex};
780733
}
781734

782735
-(void)enqueueBlock:(void (^)())block globalSortOrder:(NSInteger)globalSortOrder debugLabel:(NSString *)debugLabel threadSafe:(BOOL)threadsafe
@@ -828,13 +781,8 @@ -(void)flush
828781

829782
glInsertEventMarkerEXT(0, "Buffering");
830783

831-
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
832-
glBufferData(GL_ARRAY_BUFFER, _vertexCount*sizeof(*_vertexes), _vertexes, GL_STREAM_DRAW);
833-
glBindBuffer(GL_ARRAY_BUFFER, 0);
834-
835-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
836-
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _elementCount*sizeof(*_elements), _elements, GL_STREAM_DRAW);
837-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
784+
[self commitBuffer:&_vertexBuffer];
785+
[self commitBuffer:&_elementBuffer];
838786
CC_CHECK_GL_ERROR_DEBUG();
839787

840788
SortQueue(_queue);
@@ -843,15 +791,56 @@ -(void)flush
843791

844792
// NSLog(@"Draw commands: %d, Draw calls: %d", _statDrawCommands, _queue.count);
845793
_statDrawCommands = 0;
846-
_queue = [[NSMutableArray alloc] init];
794+
[_queue removeAllObjects];
847795

848-
_vertexCount = 0;
849-
_elementCount = 0;
796+
[self prepareBuffer:&_vertexBuffer];
797+
[self prepareBuffer:&_elementBuffer];
850798

851799
glPopGroupMarkerEXT();
852800
CC_CHECK_GL_ERROR_DEBUG();
853801

854802
// CC_INCREMENT_GL_DRAWS(1);
855803
}
856804

805+
//MARK: Buffer Management Methods
806+
807+
-(void)initBuffer:(CCGraphicsBuffer *)buffer capacity:(NSUInteger)capacity elementSize:(size_t)elementSize type:(intptr_t)type
808+
{
809+
buffer->count = 0;
810+
buffer->capacity = capacity;
811+
buffer->elementSize = elementSize;
812+
813+
buffer->ptr = calloc(capacity, elementSize);
814+
815+
GLuint glbuffer = 0;
816+
glGenBuffers(1, &glbuffer);
817+
buffer->data = (intptr_t)glbuffer;
818+
buffer->type = type;
819+
}
820+
821+
-(void)resizeBuffer:(CCGraphicsBuffer *)buffer capacity:(size_t)capacity
822+
{
823+
buffer->ptr = realloc(buffer->ptr, capacity*buffer->elementSize);
824+
buffer->capacity = capacity;
825+
}
826+
827+
-(void)destroyBuffer:(CCGraphicsBuffer *)buffer
828+
{
829+
free(buffer->ptr);
830+
glDeleteBuffers(1, (GLuint *)&buffer->data);
831+
}
832+
833+
-(void)prepareBuffer:(CCGraphicsBuffer *)buffer
834+
{
835+
buffer->count = 0;
836+
}
837+
838+
-(void)commitBuffer:(CCGraphicsBuffer *)buffer
839+
{
840+
GLenum type = (GLenum)buffer->type;
841+
glBindBuffer(type, (GLuint)buffer->data);
842+
glBufferData(type, buffer->count*buffer->elementSize, buffer->ptr, GL_STREAM_DRAW);
843+
glBindBuffer(type, 0);
844+
}
845+
857846
@end

cocos2d/CCRenderer_private.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
#import "CCCache.h"
2929

3030

31+
struct CCGraphicsBuffer;
32+
33+
3134
extern id CCBLENDMODE_CACHE;
3235
extern id CCRENDERSTATE_CACHE;
3336

@@ -72,4 +75,51 @@ extern id CCRENDERSTATE_CACHE;
7275
/// Render any currently queued commands.
7376
-(void)flush;
7477

78+
/// Resize the capacity of a graphics buffer.
79+
-(void)resizeBuffer:(struct CCGraphicsBuffer *)buffer capacity:(size_t)capacity;
80+
7581
@end
82+
83+
84+
/// Internal type used to abstract GPU buffers. (vertex, index buffers, etc)
85+
typedef struct CCGraphicsBuffer {
86+
/// Elements currently in the buffer.
87+
size_t count;
88+
/// Element capacity of the buffer.
89+
size_t capacity;
90+
/// Size in bytes of elements in the buffer.
91+
size_t elementSize;
92+
93+
/// Pointer to the buffer memory.
94+
void *ptr;
95+
96+
/// Used to store GL VBO name for now.
97+
intptr_t data;
98+
/// GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, etc.
99+
intptr_t type;
100+
} CCGraphicsBuffer;
101+
102+
103+
/// Return a pointer to an array of elements that is 'requestedCount' in size.
104+
/// The buffer is resized by calling [CCRenderer resizeBuffer:] if necessary.
105+
static inline void *
106+
CCGraphicsBufferPushElements(CCGraphicsBuffer *buffer, size_t requestedCount, CCRenderer *renderer)
107+
{
108+
NSCAssert(requestedCount > 0, @"Requested count must be positive.");
109+
110+
size_t required = buffer->count + requestedCount;
111+
size_t capacity = buffer->capacity;
112+
if(required > capacity){
113+
// Increase the buffer size until it fits.
114+
// Why 1.5? https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md
115+
while(required >= buffer->capacity) buffer->capacity *= 1.5;
116+
117+
[renderer resizeBuffer:buffer capacity:capacity];
118+
}
119+
120+
void *array = buffer->ptr + buffer->count*buffer->elementSize;
121+
buffer->count += requestedCount;
122+
123+
return array;
124+
}
125+

0 commit comments

Comments
 (0)