Skip to content

Commit e77c7d0

Browse files
committed
Vertex paging for GL.
1 parent d9b523f commit e77c7d0

File tree

9 files changed

+131
-42
lines changed

9 files changed

+131
-42
lines changed

CCRendererGLSupport.m

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,26 @@ -(void)commit
189189
@interface CCGraphicsBufferBindingsGL : CCGraphicsBufferBindings @end
190190
@implementation CCGraphicsBufferBindingsGL {
191191
GLuint _vao;
192+
193+
// Cache the currently bound page to avoid switching it if possible
194+
NSUInteger _currentPage;
195+
}
196+
197+
static inline void
198+
BindVertexPage(CCGraphicsBufferBindingsGL *self, NSUInteger page)
199+
{
200+
if(page != self->_currentPage){
201+
size_t pageOffset = page*(1<<16)*sizeof(CCVertex);
202+
203+
glBindBuffer(GL_ARRAY_BUFFER, ((CCGraphicsBufferGLBasic *)self->_vertexBuffer)->_buffer);
204+
glVertexAttribPointer(CCShaderAttributePosition, 4, GL_FLOAT, GL_FALSE, sizeof(CCVertex), (void *)(pageOffset + offsetof(CCVertex, position)));
205+
glVertexAttribPointer(CCShaderAttributeTexCoord1, 2, GL_FLOAT, GL_FALSE, sizeof(CCVertex), (void *)(pageOffset + offsetof(CCVertex, texCoord1)));
206+
glVertexAttribPointer(CCShaderAttributeTexCoord2, 2, GL_FLOAT, GL_FALSE, sizeof(CCVertex), (void *)(pageOffset + offsetof(CCVertex, texCoord2)));
207+
glVertexAttribPointer(CCShaderAttributeColor, 4, GL_FLOAT, GL_FALSE, sizeof(CCVertex), (void *)(pageOffset + offsetof(CCVertex, color)));
208+
glBindBuffer(GL_ARRAY_BUFFER, 0);
209+
210+
self->_currentPage = page;
211+
}
192212
}
193213

194214
-(instancetype)init
@@ -212,16 +232,12 @@ -(instancetype)init
212232
glEnableVertexAttribArray(CCShaderAttributeTexCoord2);
213233
glEnableVertexAttribArray(CCShaderAttributeColor);
214234

215-
glBindBuffer(GL_ARRAY_BUFFER, ((CCGraphicsBufferGLBasic *)_vertexBuffer)->_buffer);
216-
glVertexAttribPointer(CCShaderAttributePosition, 4, GL_FLOAT, GL_FALSE, sizeof(CCVertex), (void *)offsetof(CCVertex, position));
217-
glVertexAttribPointer(CCShaderAttributeTexCoord1, 2, GL_FLOAT, GL_FALSE, sizeof(CCVertex), (void *)offsetof(CCVertex, texCoord1));
218-
glVertexAttribPointer(CCShaderAttributeTexCoord2, 2, GL_FLOAT, GL_FALSE, sizeof(CCVertex), (void *)offsetof(CCVertex, texCoord2));
219-
glVertexAttribPointer(CCShaderAttributeColor, 4, GL_FLOAT, GL_FALSE, sizeof(CCVertex), (void *)offsetof(CCVertex, color));
235+
_currentPage = NSUIntegerMax; // Start with an invalid value.
236+
BindVertexPage(self, 0);
220237

221238
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((CCGraphicsBufferGLBasic *)_indexBuffer)->_buffer);
222239

223240
glBindVertexArray(0);
224-
glBindBuffer(GL_ARRAY_BUFFER, 0);
225241
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
226242

227243
CCGL_DEBUG_POP_GROUP_MARKER();
@@ -237,10 +253,16 @@ -(void)dealloc
237253
CCRenderDispatch(YES, ^{glDeleteVertexArrays(1, &vao);});
238254
}
239255

240-
-(void)bind:(BOOL)bind
256+
-(void)bind:(BOOL)bind vertexPage:(NSUInteger)vertexPage
241257
{
242258
CCGL_DEBUG_INSERT_EVENT_MARKER("CCGraphicsBufferBindingsGL: Bind VAO");
243-
glBindVertexArray(bind ? _vao : 0);
259+
260+
if(bind){
261+
glBindVertexArray(_vao);
262+
BindVertexPage(self, vertexPage);
263+
} else {
264+
glBindVertexArray(0);
265+
}
244266
}
245267

246268
@end

cocos2d-ui-tests/tests/CCRendererTest.m

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,35 @@ -(id)init
135135
// [self.contentNode addChild:sprite];
136136
//}
137137

138+
-(void)setupVertexPagingTest
139+
{
140+
self.subTitle = @"Should draw a bird.";
141+
142+
CCSprite *sprite = [CCSprite spriteWithImageNamed:@"Sprites/bird.png"];
143+
sprite.position = ccp(50, 50);
144+
145+
CCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:100 height:100];
146+
rt.positionType = CCPositionTypeNormalized;
147+
rt.position = ccp(0.5, 0.5);
148+
149+
// Wrap the drawing in a render texture since this will give it a unique renderer. (undefined, but currently true)
150+
CCRenderer *renderer = [rt beginWithClear:0 g:0 b:0 a:0];
151+
// Artificially use up all of the vertex indices.
152+
// You can fit 2^16 vertexes per page. So 66*1000 will use slightly more than a page worth of vertexes.
153+
// I'm specifically avoiding POT sized allocations of vertexes below.
154+
// If paging is not working, it will skip drawing the sprite.
155+
for(int i=0; i<66; i++){
156+
CCRenderBuffer buffer = [renderer enqueueTriangles:1 andVertexes:1000 withState:[CCRenderState debugColor] globalSortOrder:0];
157+
// Tell it to draw a degerenate triangle for the entire batch of vertices.
158+
CCRenderBufferSetTriangle(buffer, 0, 0, 0, 0);
159+
}
160+
161+
[sprite visit];
162+
[rt end];
163+
164+
[self.contentNode addChild:rt];
165+
}
166+
138167
#if !__CC_METAL_SUPPORTED_AND_ENABLED
139168
-(void)setupClippingNodeTest
140169
{

cocos2d/CCNoARC.m

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,30 +124,55 @@ -(void)enqueueTriangles:(CCRenderer *)renderer transform:(const GLKMatrix4 *)tra
124124

125125
@implementation CCRenderer(NoARC)
126126

127+
// Positive offset of the vertex allocation to prevent overlapping a boundary.
128+
static inline NSUInteger
129+
PageOffset(NSUInteger firstVertex, NSUInteger vertexCount)
130+
{
131+
NSCAssert(vertexCount < UINT16_MAX + 1, @"Too many vertexes for a single draw count.");
132+
133+
// Space remaining on the current vertex page.
134+
NSUInteger remain = (firstVertex | UINT16_MAX) - firstVertex + 1;
135+
136+
if(remain >= vertexCount){
137+
// Allocation will not overlap a page boundary.
138+
return 0;
139+
} else {
140+
return remain;
141+
}
142+
}
143+
127144
-(CCRenderBuffer)enqueueTriangles:(NSUInteger)triangleCount andVertexes:(NSUInteger)vertexCount withState:(CCRenderState *)renderState globalSortOrder:(NSInteger)globalSortOrder;
128145
{
129146
// Need to record the first vertex or element index before pushing more vertexes.
130147
NSUInteger firstVertex = _buffers->_vertexBuffer->_count;
131148
NSUInteger firstIndex = _buffers->_indexBuffer->_count;
132149

150+
// Value is 0 unless there a page boundary overlap would occur.
151+
NSUInteger vertexPageOffset = PageOffset(firstVertex, vertexCount);
152+
153+
// Split vertexes into pages of 2^16 vertexes since GLES2 requires indexing with shorts.
154+
NSUInteger vertexPage = (firstVertex + vertexPageOffset) >> 16;
155+
NSUInteger vertexPageIndex = (firstVertex + vertexPageOffset) & 0xFFFF;
156+
157+
// Ensure that the buffers have enough storage space.
133158
NSUInteger indexCount = 3*triangleCount;
134-
CCVertex *vertexes = CCGraphicsBufferPushElements(_buffers->_vertexBuffer, vertexCount);
159+
CCVertex *vertexes = CCGraphicsBufferPushElements(_buffers->_vertexBuffer, vertexCount + vertexPageOffset);
135160
uint16_t *elements = CCGraphicsBufferPushElements(_buffers->_indexBuffer, indexCount);
136161

137162
CCRenderCommandDraw *previous = _lastDrawCommand;
138-
if(previous && previous->_renderState == renderState && previous->_globalSortOrder == globalSortOrder){
163+
if(previous && previous->_renderState == renderState && previous->_globalSortOrder == globalSortOrder && previous->_vertexPage == vertexPage){
139164
// Batch with the previous command.
140165
[previous batch:indexCount];
141166
} else {
142167
// Start a new command.
143-
CCRenderCommandDraw *command = [[CCRenderCommandDrawClass alloc] initWithMode:CCRenderCommandDrawTriangles renderState:renderState first:firstIndex count:indexCount globalSortOrder:globalSortOrder];
168+
CCRenderCommandDraw *command = [[CCRenderCommandDrawClass alloc] initWithMode:CCRenderCommandDrawTriangles renderState:renderState firstIndex:firstIndex vertexPage:vertexPage count:indexCount globalSortOrder:globalSortOrder];
144169
[_queue addObject:command];
145170
[command release];
146171

147172
_lastDrawCommand = command;
148173
}
149174

150-
return (CCRenderBuffer){vertexes, elements, firstVertex};
175+
return (CCRenderBuffer){vertexes, elements, vertexPageIndex};
151176
}
152177

153178
-(CCRenderBuffer)enqueueLines:(NSUInteger)lineCount andVertexes:(NSUInteger)vertexCount withState:(CCRenderState *)renderState globalSortOrder:(NSInteger)globalSortOrder;
@@ -156,32 +181,41 @@ -(CCRenderBuffer)enqueueLines:(NSUInteger)lineCount andVertexes:(NSUInteger)vert
156181
NSUInteger firstVertex = _buffers->_vertexBuffer->_count;
157182
NSUInteger firstIndex = _buffers->_indexBuffer->_count;
158183

184+
// Value is 0 unless there a page boundary overlap would occur.
185+
NSUInteger vertexPageOffset = PageOffset(firstVertex, vertexCount);
186+
187+
// Split vertexes into pages of 2^16 vertexes since GLES2 requires indexing with shorts.
188+
NSUInteger vertexPage = (firstVertex + vertexPageOffset) >> 16;
189+
NSUInteger vertexPageIndex = (firstVertex + vertexPageOffset) & 0xFFFF;
190+
191+
// Ensure that the buffers have enough storage space.
159192
NSUInteger indexCount = 2*lineCount;
160-
CCVertex *vertexes = CCGraphicsBufferPushElements(_buffers->_vertexBuffer, vertexCount);
193+
CCVertex *vertexes = CCGraphicsBufferPushElements(_buffers->_vertexBuffer, vertexCount + vertexPageOffset);
161194
uint16_t *elements = CCGraphicsBufferPushElements(_buffers->_indexBuffer, indexCount);
162195

163-
CCRenderCommandDraw *command = [[CCRenderCommandDrawClass alloc] initWithMode:CCRenderCommandDrawLines renderState:renderState first:firstIndex count:indexCount globalSortOrder:globalSortOrder];
196+
CCRenderCommandDraw *command = [[CCRenderCommandDrawClass alloc] initWithMode:CCRenderCommandDrawLines renderState:renderState firstIndex:firstIndex vertexPage:vertexPage count:indexCount globalSortOrder:globalSortOrder];
164197
[_queue addObject:command];
165198
[command release];
166199

167200
// Line drawing commands are currently intended for debugging and cannot be batched.
168201
_lastDrawCommand = nil;
169202

170-
return(CCRenderBuffer){vertexes, elements, firstVertex};
203+
return(CCRenderBuffer){vertexes, elements, vertexPageIndex};
171204
}
172205

173206
static inline void
174-
CCRendererBindBuffers(CCRenderer *self, BOOL bind)
207+
CCRendererBindBuffers(CCRenderer *self, BOOL bind, NSUInteger vertexPage)
175208
{
176-
if(bind != self->_buffersBound){
177-
[self->_buffers bind:bind];
209+
if(bind != self->_buffersBound || vertexPage != self->_vertexPageBound){
210+
[self->_buffers bind:bind vertexPage:vertexPage];
178211
self->_buffersBound = bind;
212+
self->_vertexPageBound = vertexPage;
179213
}
180214
}
181215

182-
-(void)bindBuffers:(BOOL)bind
216+
-(void)bindBuffers:(BOOL)bind vertexPage:(NSUInteger)vertexPage
183217
{
184-
CCRendererBindBuffers(self, bind);
218+
CCRendererBindBuffers(self, bind, vertexPage);
185219
}
186220

187221

@@ -270,11 +304,11 @@ -(void)invokeOnRenderer:(CCRenderer *)renderer
270304
{
271305
CCGL_DEBUG_PUSH_GROUP_MARKER("CCRendererCommandDraw: Invoke");
272306

273-
CCRendererBindBuffers(renderer, YES);
307+
CCRendererBindBuffers(renderer, YES, _vertexPage);
274308
CCRenderStateGLTransition((CCRenderStateGL *)_renderState, renderer, (CCRenderStateGL *)renderer->_renderState);
275309
renderer->_renderState = _renderState;
276310

277-
glDrawElements(GLDrawModes[_mode], (GLsizei)_count, GL_UNSIGNED_SHORT, (GLvoid *)(_first*sizeof(GLushort)));
311+
glDrawElements(GLDrawModes[_mode], (GLsizei)_count, GL_UNSIGNED_SHORT, (GLvoid *)(_firstIndex*sizeof(GLushort)));
278312
CC_INCREMENT_GL_DRAWS(1);
279313

280314
CCGL_DEBUG_POP_GROUP_MARKER();

cocos2d/CCRenderTexture.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ typedef NS_ENUM(NSInteger, CCRenderTextureImageFormat)
158158
* @param b Blue color.
159159
* @param a Alpha.
160160
*/
161-
-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a;
161+
-(CCRenderer *)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a;
162162

163163
/**
164164
* starts rendering to the texture while clearing the texture first.
@@ -170,7 +170,7 @@ typedef NS_ENUM(NSInteger, CCRenderTextureImageFormat)
170170
* @param a Alpha.
171171
* @param depthValue Depth value.
172172
*/
173-
- (void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue;
173+
- (CCRenderer *)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue;
174174

175175
/**
176176
* starts rendering to the texture while clearing the texture first.
@@ -183,7 +183,7 @@ typedef NS_ENUM(NSInteger, CCRenderTextureImageFormat)
183183
* @param depthValue Depth value.
184184
* @param stencilValue Stencil value.
185185
*/
186-
- (void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue stencil:(int)stencilValue;
186+
- (CCRenderer *)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue stencil:(int)stencilValue;
187187

188188
/**
189189
* Ends grabbing

cocos2d/CCRenderTexture.m

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -272,25 +272,27 @@ -(CCRenderer *)begin
272272
return renderer;
273273
}
274274

275-
-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue stencil:(int)stencilValue flags:(GLbitfield)flags
275+
-(CCRenderer *)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue stencil:(int)stencilValue flags:(GLbitfield)flags
276276
{
277277
CCRenderer *renderer = [self begin];
278278
[renderer enqueueClear:flags color:GLKVector4Make(r, g, b, a) depth:depthValue stencil:stencilValue globalSortOrder:NSIntegerMin];
279+
280+
return renderer;
279281
}
280282

281-
-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a
283+
-(CCRenderer *)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a
282284
{
283-
[self beginWithClear:r g:g b:b a:a depth:0 stencil:0 flags:GL_COLOR_BUFFER_BIT];
285+
return [self beginWithClear:r g:g b:b a:a depth:0 stencil:0 flags:GL_COLOR_BUFFER_BIT];
284286
}
285287

286-
-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue
288+
-(CCRenderer *)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue
287289
{
288-
[self beginWithClear:r g:g b:b a:a depth:depthValue stencil:0 flags:GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT];
290+
return [self beginWithClear:r g:g b:b a:a depth:depthValue stencil:0 flags:GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT];
289291
}
290292

291-
-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue stencil:(int)stencilValue
293+
-(CCRenderer *)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue stencil:(int)stencilValue
292294
{
293-
[self beginWithClear:r g:g b:b a:a depth:depthValue stencil:stencilValue flags:GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT];
295+
return [self beginWithClear:r g:g b:b a:a depth:depthValue stencil:stencilValue flags:GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT];
294296
}
295297

296298
-(void)end

cocos2d/CCRenderer.m

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ void CCRENDERER_DEBUG_CHECK_ERRORS(void){
9999

100100
@implementation CCRenderCommandDraw
101101

102-
-(instancetype)initWithMode:(CCRenderCommandDrawMode)mode renderState:(CCRenderState *)renderState first:(NSUInteger)first count:(size_t)count globalSortOrder:(NSInteger)globalSortOrder
102+
-(instancetype)initWithMode:(CCRenderCommandDrawMode)mode renderState:(CCRenderState *)renderState firstIndex:(NSUInteger)firstIndex vertexPage:(NSUInteger)vertexPage count:(size_t)count globalSortOrder:(NSInteger)globalSortOrder;
103103
{
104104
if((self = [super init])){
105105
_mode = mode;
@@ -108,7 +108,8 @@ -(instancetype)initWithMode:(CCRenderCommandDrawMode)mode renderState:(CCRenderS
108108
#else
109109
_renderState = renderState;
110110
#endif
111-
_first = first;
111+
_firstIndex = firstIndex;
112+
_vertexPage = vertexPage;
112113
_count = count;
113114
_globalSortOrder = globalSortOrder;
114115
}
@@ -166,7 +167,7 @@ -(void)invokeOnRenderer:(CCRenderer *)renderer
166167
{
167168
CCRENDERER_DEBUG_PUSH_GROUP_MARKER(_debugLabel);
168169

169-
[renderer bindBuffers:NO];
170+
[renderer bindBuffers:NO vertexPage:0];
170171
_block();
171172

172173
CCRENDERER_DEBUG_POP_GROUP_MARKER();
@@ -421,7 +422,7 @@ -(void)flush
421422
// Execute the rendering commands.
422423
SortQueue(_queue);
423424
for(id<CCRenderCommand> command in _queue) [command invokeOnRenderer:self];
424-
[self bindBuffers:NO];
425+
[self bindBuffers:NO vertexPage:0];
425426

426427
[_queue removeAllObjects];
427428

cocos2d/CCRendererBasicTypes.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,8 @@ -(void)dealloc
402402

403403
@implementation CCGraphicsBufferBindings
404404

405-
// Base implementations does nothing.
406-
-(void)bind:(BOOL)bind {}
405+
// Base implementation does nothing.
406+
-(void)bind:(BOOL)bind vertexPage:(NSUInteger)vertexPage {}
407407

408408
-(void)prepare
409409
{

cocos2d/CCRendererBasicTypes_Private.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ CCGraphicsBufferPushElements(CCGraphicsBuffer *buffer, size_t requestedCount)
105105
}
106106

107107
/// Make the buffers ready to use by drawing commands.
108-
-(void)bind:(BOOL)bind;
108+
-(void)bind:(BOOL)bind vertexPage:(NSUInteger)vertexPage;
109109

110110
/// Prepare buffers for changes.
111111
-(void)prepare;

cocos2d/CCRenderer_Private.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,14 @@ typedef NS_ENUM(NSUInteger, CCRenderCommandDrawMode){
8080
CCRenderState *_renderState;
8181
NSInteger _globalSortOrder;
8282

83-
NSUInteger _first;
83+
NSUInteger _firstIndex, _vertexPage;
8484
size_t _count;
8585
}
8686

8787
@property(nonatomic, readonly) NSUInteger first;
8888
@property(nonatomic, readonly) size_t count;
8989

90-
-(instancetype)initWithMode:(CCRenderCommandDrawMode)mode renderState:(CCRenderState *)renderState first:(NSUInteger)first count:(size_t)count globalSortOrder:(NSInteger)globalSortOrder;
90+
-(instancetype)initWithMode:(CCRenderCommandDrawMode)mode renderState:(CCRenderState *)renderState firstIndex:(NSUInteger)firstIndex vertexPage:(NSUInteger)vertexPage count:(size_t)count globalSortOrder:(NSInteger)globalSortOrder;
9191

9292
-(void)batch:(NSUInteger)count;
9393

@@ -117,6 +117,7 @@ typedef NS_ENUM(NSUInteger, CCRenderCommandDrawMode){
117117
__unsafe_unretained CCRenderState *_renderState;
118118
__unsafe_unretained CCRenderCommandDraw *_lastDrawCommand;
119119
BOOL _buffersBound;
120+
NSUInteger _vertexPageBound;
120121
}
121122

122123
/// Current global shader uniform values.
@@ -144,7 +145,7 @@ typedef NS_ENUM(NSUInteger, CCRenderCommandDrawMode){
144145
-(void)setRenderState:(CCRenderState *)renderState;
145146

146147
/// Bind the renderer's VAO if it is not currently bound.
147-
-(void)bindBuffers:(BOOL)bind;
148+
-(void)bindBuffers:(BOOL)bind vertexPage:(NSUInteger)vertexPage;
148149

149150
@end
150151

0 commit comments

Comments
 (0)