Skip to content

Commit 1b9acd4

Browse files
committed
Packing uniforms into uniform buffers.
1 parent 42e23d2 commit 1b9acd4

File tree

6 files changed

+179
-49
lines changed

6 files changed

+179
-49
lines changed

UnitTests/CCRendererTests.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010

1111
#import "CCRenderer_private.h"
1212

13+
14+
@interface NSValue()
15+
-(size_t)CCRendererSizeOf;
16+
@end
17+
18+
1319
@interface CCRendererTests : XCTestCase
1420
@end
1521

@@ -100,4 +106,20 @@ -(void)testRenderStateCacheFlush
100106
XCTAssertNil(renderState, @"Render state was not released.");
101107
}
102108

109+
-(void)testNSValueSizeOf
110+
{
111+
GLKVector2 v2 = {};
112+
GLKVector3 v3 = {};
113+
GLKVector4 v4 = {};
114+
GLKMatrix4 m4 = {};
115+
CCGlobalUniforms globals = {};
116+
117+
XCTAssertEqual(sizeof(v2), [NSValue valueWithGLKVector2:v2].CCRendererSizeOf, @"");
118+
XCTAssertEqual(sizeof(v3), [NSValue valueWithGLKVector3:v3].CCRendererSizeOf, @"");
119+
XCTAssertEqual(sizeof(v4), [NSValue valueWithGLKVector4:v4].CCRendererSizeOf, @"");
120+
XCTAssertEqual(sizeof(m4), [NSValue valueWithGLKMatrix4:m4].CCRendererSizeOf, @"");
121+
XCTAssertEqual(sizeof(globals), [NSValue valueWithBytes:&globals objCType:@encode(CCGlobalUniforms)].CCRendererSizeOf, @"");
122+
}
123+
124+
103125
@end

cocos2d/CCDirector.m

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -221,23 +221,35 @@ - (void) dealloc
221221

222222
-(NSDictionary *)updateGlobalShaderUniforms
223223
{
224-
GLKMatrix4 projection = self.projectionMatrix;
225-
_globalShaderUniforms[CCShaderUniformProjection] = [NSValue valueWithGLKMatrix4:projection];
226-
_globalShaderUniforms[CCShaderUniformProjectionInv] = [NSValue valueWithGLKMatrix4:GLKMatrix4Invert(projection, NULL)];
224+
// Group all of the standard globals into one value.
225+
// Used by Metal, will be used eventually by a GL3 renderer.
226+
CCGlobalUniforms globals = {};
227+
228+
globals.projection = self.projectionMatrix;
229+
globals.projectionInv = GLKMatrix4Invert(globals.projection, NULL);
230+
_globalShaderUniforms[CCShaderUniformProjection] = [NSValue valueWithGLKMatrix4:globals.projection];
231+
_globalShaderUniforms[CCShaderUniformProjectionInv] = [NSValue valueWithGLKMatrix4:globals.projectionInv];
227232

228233
CGSize size = self.viewSize;
229-
_globalShaderUniforms[CCShaderUniformViewSize] = [NSValue valueWithGLKVector2:GLKVector2Make(size.width, size.height)];
234+
globals.viewSize = GLKVector2Make(size.width, size.height);
235+
_globalShaderUniforms[CCShaderUniformViewSize] = [NSValue valueWithGLKVector2:globals.viewSize];
230236

231237
CGSize pixelSize = self.viewSizeInPixels;
232-
_globalShaderUniforms[CCShaderUniformViewSizeInPixels] = [NSValue valueWithGLKVector2:GLKVector2Make(pixelSize.width, pixelSize.height)];
238+
globals.viewSizeInPixels = GLKVector2Make(pixelSize.width, pixelSize.height);
239+
_globalShaderUniforms[CCShaderUniformViewSizeInPixels] = [NSValue valueWithGLKVector2:globals.viewSizeInPixels];
233240

234241
CCTime t = self.scheduler.currentTime;
235-
_globalShaderUniforms[CCShaderUniformTime] = [NSValue valueWithGLKVector4:GLKVector4Make(t, t/2.0f, t/8.0f, t/8.0f)];
236-
_globalShaderUniforms[CCShaderUniformSinTime] = [NSValue valueWithGLKVector4:GLKVector4Make(sinf(t*2.0f), sinf(t), sinf(t/2.0f), sinf(t/4.0f))];
237-
_globalShaderUniforms[CCShaderUniformCosTime] = [NSValue valueWithGLKVector4:GLKVector4Make(cosf(t*2.0f), cosf(t), cosf(t/2.0f), cosf(t/4.0f))];
242+
globals.time = GLKVector4Make(t, t/2.0f, t/8.0f, t/8.0f);
243+
globals.sinTime = GLKVector4Make(sinf(t*2.0f), sinf(t), sinf(t/2.0f), sinf(t/4.0f));
244+
globals.cosTime = GLKVector4Make(cosf(t*2.0f), cosf(t), cosf(t/2.0f), cosf(t/4.0f));
245+
_globalShaderUniforms[CCShaderUniformTime] = [NSValue valueWithGLKVector4:globals.time];
246+
_globalShaderUniforms[CCShaderUniformSinTime] = [NSValue valueWithGLKVector4:globals.sinTime];
247+
_globalShaderUniforms[CCShaderUniformCosTime] = [NSValue valueWithGLKVector4:globals.cosTime];
238248

239-
GLKVector4 random = GLKVector4Make(CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1());
240-
_globalShaderUniforms[CCShaderUniformRandom01] = [NSValue valueWithGLKVector4:random];
249+
globals.random01 = GLKVector4Make(CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1());
250+
_globalShaderUniforms[CCShaderUniformRandom01] = [NSValue valueWithGLKVector4:globals.random01];
251+
252+
_globalShaderUniforms[CCShaderUniformDefaultGlobals] = [NSValue valueWithBytes:&globals objCType:@encode(CCGlobalUniforms)];
241253

242254
return _globalShaderUniforms;
243255
}
@@ -258,12 +270,11 @@ - (void) drawScene
258270
[ccview beginFrame];
259271

260272
if(CCRenderDispatchBeginFrame()){
261-
CCRenderer *renderer = [self rendererFromPool];
262-
[CCRenderer bindRenderer:renderer];
263-
264273
GLKMatrix4 projection = self.projectionMatrix;
265-
renderer.globalShaderUniforms = [self updateGlobalShaderUniforms];
266274

275+
CCRenderer *renderer = [self rendererFromPool];
276+
[renderer prepareWithGlobals:[self updateGlobalShaderUniforms]];
277+
[CCRenderer bindRenderer:renderer];
267278

268279
[renderer enqueueClear:(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) color:_runningScene.colorRGBA.glkVector4 depth:1.0f stencil:0 globalSortOrder:NSIntegerMin];
269280

cocos2d/CCRenderer.m

Lines changed: 126 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,110 @@ +(NSValue *)valueWithGLKMatrix4:(GLKMatrix4)matrix
5959
return [NSValue valueWithBytes:&matrix objCType:@encode(GLKMatrix4)];
6060
}
6161

62+
static void
63+
AdvanceUntilAfter(const char **cursor, const char c)
64+
{
65+
while(**cursor != c) (*cursor)++;
66+
(*cursor)++;
67+
}
68+
69+
static size_t
70+
ReadValue(const char **cursor)
71+
{
72+
char c = (**cursor);
73+
(*cursor)++;
74+
75+
switch(c){
76+
case 'c': return sizeof(char); break;
77+
case 'i': return sizeof(int); break;
78+
case 's': return sizeof(short); break;
79+
case 'l': return sizeof(long); break;
80+
case 'q': return sizeof(long long); break;
81+
case 'C': return sizeof(unsigned char); break;
82+
case 'I': return sizeof(unsigned int); break;
83+
case 'S': return sizeof(unsigned short); break;
84+
case 'L': return sizeof(unsigned long); break;
85+
case 'Q': return sizeof(unsigned long long); break;
86+
case 'f': return sizeof(float); break;
87+
case 'd': return sizeof(double); break;
88+
case 'B': return sizeof(_Bool); break;
89+
90+
case '{': {
91+
// struct
92+
AdvanceUntilAfter(cursor, '=');
93+
94+
size_t bytes = 0;
95+
while(**cursor != '}'){
96+
bytes += ReadValue(cursor);
97+
}
98+
99+
(*cursor)++;
100+
return bytes;
101+
}
102+
103+
case '(': {
104+
// Union
105+
AdvanceUntilAfter(cursor, '=');
106+
107+
size_t bytes = 0;
108+
while(**cursor != ')'){
109+
bytes = MAX(bytes, ReadValue(cursor));
110+
}
111+
112+
(*cursor)++;
113+
return bytes;
114+
}
115+
116+
case '[': {
117+
// array
118+
size_t count = 0;
119+
for(; '0' <= **cursor && **cursor <= '9'; (*cursor)++){
120+
count = count*10 + (**cursor - '0');
121+
}
122+
123+
size_t elementSize = ReadValue(cursor);
124+
125+
(*cursor)++;
126+
return count*elementSize;
127+
}
128+
129+
case 'v': // void
130+
case '*': // char *
131+
case 'b': // bitfield
132+
case '@': // object
133+
case '#': // class
134+
case ':': // selector
135+
case '^': // pointer
136+
default:
137+
NSCAssert(NO, @"@encode() type %c is forbidden to use with shader uniforms.", c);
138+
return 0;
139+
}
140+
}
141+
142+
// Partial implementation to calculate the size of an @encode() string.
143+
// Doesn't handle all types, alignment, etc.
144+
-(size_t)CCRendererSizeOf
145+
{
146+
size_t bytes = 0;
147+
148+
const char *objCType = self.objCType;
149+
const char **cursor = &objCType;
150+
151+
while(**cursor != '\0'){
152+
bytes += ReadValue(cursor);
153+
}
154+
155+
return bytes;
156+
}
157+
158+
62159
@end
63160

161+
64162
//MARK: Graphics Debug Helpers:
65163

164+
#if DEBUG
165+
66166
void CCRENDERER_DEBUG_PUSH_GROUP_MARKER(NSString *label){
67167
#if __CC_METAL_SUPPORTED_AND_ENABLED
68168
if([CCConfiguration sharedConfiguration].graphicsAPI == CCGraphicsAPIMetal){
@@ -106,6 +206,8 @@ void CCRENDERER_DEBUG_CHECK_ERRORS(void){
106206
}
107207
}
108208

209+
#endif
210+
109211

110212
//MARK: Blend Option Keys.
111213
const NSString *CCRenderStateBlendMode = @"CCRenderStateBlendMode";
@@ -613,7 +715,7 @@ @implementation CCGraphicsBufferBindings
613715
// Base implementations do do nothing.
614716
-(void)prepare {}
615717
-(void)commit {}
616-
-(void)bind {}
718+
-(void)bind:(BOOL)bind {}
617719

618720
@end
619721

@@ -659,10 +761,31 @@ +(void)bindRenderer:(CCRenderer *)renderer
659761
}
660762
}
661763

662-
-(void)setGlobalShaderUniforms:(NSDictionary *)globalShaderUniforms
764+
-(void)prepareWithGlobals:(NSDictionary *)globalShaderUniforms
663765
{
664766
_globalShaderUniforms = [globalShaderUniforms copy];
665-
[self invalidateState];
767+
768+
// If we are using a uniform buffer (ex: Metal) copy the global uniforms into it.
769+
CCGraphicsBuffer *uniformBuffer = _buffers->_uniformBuffer;
770+
if(uniformBuffer){
771+
NSMutableDictionary *offsets = [NSMutableDictionary dictionary];
772+
size_t offset = 0;
773+
774+
for(NSString *name in _globalShaderUniforms){
775+
NSValue *value = _globalShaderUniforms[name];
776+
size_t bytes = value.CCRendererSizeOf;
777+
778+
void * buff = CCGraphicsBufferPushElements(uniformBuffer, bytes);
779+
[value getValue:buff];
780+
offsets[name] = @(offset);
781+
782+
offset += bytes;
783+
}
784+
785+
_globalShaderUniformBufferOffsets = offsets;
786+
}
787+
788+
#warning Bind FBO.
666789
}
667790

668791
//Implemented in CCNoARC.m
@@ -736,28 +859,7 @@ -(void)flush
736859

737860
// Commit the buffers.
738861
[_buffers commit];
739-
740-
// TODO This should probably be moved eventually, but I'm not sure where.
741-
// Probably never going to support uniform buffers in GL and making CCRenderer abstract is ugh...
742-
#if __CC_METAL_SUPPORTED_AND_ENABLED
743-
if(_metalContext){
744-
CCGlobalUniforms globalUniforms = {};
745-
const NSUInteger globalUniformBytes = sizeof(globalUniforms);
746862

747-
[_globalShaderUniforms[CCShaderUniformProjection] getValue:&globalUniforms.projection];
748-
[_globalShaderUniforms[CCShaderUniformProjectionInv] getValue:&globalUniforms.projectionInv];
749-
[_globalShaderUniforms[CCShaderUniformViewSize] getValue:&globalUniforms.viewSize];
750-
[_globalShaderUniforms[CCShaderUniformViewSizeInPixels] getValue:&globalUniforms.viewSizeInPixels];
751-
[_globalShaderUniforms[CCShaderUniformTime] getValue:&globalUniforms.time];
752-
[_globalShaderUniforms[CCShaderUniformSinTime] getValue:&globalUniforms.sinTime];
753-
[_globalShaderUniforms[CCShaderUniformCosTime] getValue:&globalUniforms.cosTime];
754-
[_globalShaderUniforms[CCShaderUniformRandom01] getValue:&globalUniforms.random01];
755-
756-
void *buff = CCGraphicsBufferPushElements(_uniformBuffer, globalUniformBytes);
757-
memcpy(buff, &globalUniforms, globalUniformBytes);
758-
}
759-
#endif
760-
761863
// Execute the rendering commands.
762864
SortQueue(_queue);
763865
for(id<CCRenderCommand> command in _queue) [command invokeOnRenderer:self];

cocos2d/CCRenderer_private.h

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,6 @@
2828
#import "CCCache.h"
2929
#import "CCRenderDispatch.h"
3030

31-
#if __CC_METAL_SUPPORTED_AND_ENABLED
32-
33-
@class CCMetalContext;
34-
3531
// Struct used for packing the global uniforms.
3632
// NOTE: Must match the definition in CCShaders.metal!
3733
typedef struct CCGlobalUniforms {
@@ -45,8 +41,6 @@ typedef struct CCGlobalUniforms {
4541
GLKVector4 random01;
4642
} CCGlobalUniforms;
4743

48-
#endif
49-
5044
/// Options dictionary for the disabled blending mode.
5145
extern NSDictionary *CCBLEND_DISABLED_OPTIONS;
5246

@@ -212,6 +206,7 @@ CCGraphicsBufferPushElements(CCGraphicsBuffer *buffer, size_t requestedCount)
212206
CCGraphicsBufferBindings *_buffers;
213207

214208
NSDictionary *_globalShaderUniforms;
209+
NSDictionary *_globalShaderUniformBufferOffsets;
215210

216211
NSMutableArray *_queue;
217212
NSMutableArray *_queueStack;
@@ -221,14 +216,10 @@ CCGraphicsBufferPushElements(CCGraphicsBuffer *buffer, size_t requestedCount)
221216
__unsafe_unretained CCRenderState *_renderState;
222217
__unsafe_unretained CCRenderCommandDraw *_lastDrawCommand;
223218
BOOL _buffersBound;
224-
225-
#if __CC_METAL_SUPPORTED_AND_ENABLED
226-
CCMetalContext *_metalContext;
227-
#endif
228219
}
229220

230221
/// Current global shader uniform values.
231-
@property(nonatomic, copy) NSDictionary *globalShaderUniforms;
222+
@property(nonatomic, readonly) NSDictionary *globalShaderUniforms;
232223

233224
/// Retrieve the current renderer for the current thread.
234225
+(instancetype)currentRenderer;
@@ -247,6 +238,8 @@ CCGraphicsBufferPushElements(CCGraphicsBuffer *buffer, size_t requestedCount)
247238

248239
@interface CCRenderer(NoARCPrivate)
249240

241+
-(void)prepareWithGlobals:(NSDictionary *)globalShaderUniforms;
242+
250243
-(void)setRenderState:(CCRenderState *)renderState;
251244

252245
/// Bind the renderer's VAO if it is not currently bound.

cocos2d/CCShader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ typedef NS_ENUM(NSUInteger, CCShaderAttribute){
4545
};
4646

4747

48+
extern const NSString *CCShaderUniformDefaultGlobals;
4849
extern const NSString *CCShaderUniformProjection;
4950
extern const NSString *CCShaderUniformProjectionInv;
5051
extern const NSString *CCShaderUniformViewSize;

cocos2d/CCShader.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#import "CCMetalSupport_Private.h"
4040

4141

42+
const NSString *CCShaderUniformDefaultGlobals = @"cc_DefaultGlobals";
4243
const NSString *CCShaderUniformProjection = @"cc_Projection";
4344
const NSString *CCShaderUniformProjectionInv = @"cc_ProjectionInv";
4445
const NSString *CCShaderUniformViewSize = @"cc_ViewSize";

0 commit comments

Comments
 (0)