Skip to content

Commit e2fa293

Browse files
committed
Custom Metal shader uniforms working. (?)
1 parent 1b9acd4 commit e2fa293

File tree

9 files changed

+234
-111
lines changed

9 files changed

+234
-111
lines changed

CCRendererGLSupport.m

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -242,16 +242,4 @@ -(void)bind:(BOOL)bind
242242
glBindVertexArray(bind ? _vao : 0);
243243
}
244244

245-
-(void)prepare
246-
{
247-
[_vertexBuffer prepare];
248-
[_indexBuffer prepare];
249-
}
250-
251-
-(void)commit
252-
{
253-
[_vertexBuffer commit];
254-
[_indexBuffer commit];
255-
}
256-
257245
@end

cocos2d/CCNoARC.m

Lines changed: 15 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ @implementation CCRenderStateGL
242242
NSDictionary *globalShaderUniforms = renderer->_globalShaderUniforms;
243243
NSDictionary *setters = self->_shader->_uniformSetters;
244244
for(NSString *uniformName in setters){
245-
CCGLUniformSetter setter = setters[uniformName];
245+
CCUniformSetter setter = setters[uniformName];
246246
setter(renderer, self->_shaderUniforms, globalShaderUniforms);
247247
}
248248
}
@@ -288,13 +288,6 @@ -(void)invokeOnRenderer:(CCRenderer *)renderer
288288
@interface CCRenderStateMetal : CCRenderState @end
289289
@implementation CCRenderStateMetal {
290290
id<MTLRenderPipelineState> _renderPipelineState;
291-
292-
NSRange _textureRange;
293-
id<MTLSamplerState> _samplers[CCMTL_MAX_TEXTURES];
294-
id<MTLTexture> _textures[CCMTL_MAX_TEXTURES];
295-
296-
@public
297-
BOOL _uniformsPrepared;
298291
}
299292

300293
// Using GL enums for CCBlendMode types should never have happened. Oops.
@@ -350,40 +343,25 @@ @implementation CCRenderStateMetal {
350343

351344
self->_renderPipelineState = [[context.device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:nil] retain];
352345
}
353-
354-
if(!self->_uniformsPrepared){
355-
CCTexture *mainTexture = self->_shaderUniforms[CCShaderUniformMainTexture];
356-
357-
self->_textureRange = NSMakeRange(0, 1);
358-
self->_samplers[0] = [mainTexture.metalSampler retain];
359-
self->_textures[0] = [mainTexture.metalTexture retain];
360-
361-
self->_uniformsPrepared = YES;
362-
}
363346
}
364347

365348
static void
366349
CCRenderStateMetalTransition(CCRenderStateMetal *self, CCRenderer *renderer, CCRenderStateMetal *previous)
367350
{
368-
CCMetalContext *context = renderer->_metalContext;
351+
CCGraphicsBufferBindingsMetal *buffers = (CCGraphicsBufferBindingsMetal *)renderer->_buffers;
352+
CCMetalContext *context = buffers->_context;
369353
id<MTLRenderCommandEncoder> renderEncoder = context->_currentRenderCommandEncoder;
370-
[renderEncoder setRenderPipelineState:self->_renderPipelineState];
371354

372-
// CCGraphicsBufferMetal *uniformGraphicsBuffer = nil;
373-
// id<MTLBuffer> uniformMetalBuffer = uniformGraphicsBuffer->_buffer;
374-
//
375-
// // Set the global uniform buffers.
376-
// [renderEncoder setVertexBuffer:uniformMetalBuffer offset:0 atIndex:1];
377-
// [renderEncoder setFragmentBuffer:uniformMetalBuffer offset:0 atIndex:1];
378-
//
379-
// // Set the uniform buffers.
380-
// CCGraphicsBufferPushElements(uniformGraphicsBuffer, # of bytes);
381-
// [renderEncoder setVertexBuffer:uniformMetalBuffer offset:offset atIndex:2];
382-
// [renderEncoder setFragmentBuffer:uniformMetalBuffer offset:offset atIndex:2];
355+
// Bind pipeline state.
356+
[renderEncoder setRenderPipelineState:self->_renderPipelineState];
383357

384-
NSRange range = self->_textureRange;
385-
[renderEncoder setFragmentSamplerStates:self->_samplers withRange:range];
386-
[renderEncoder setFragmentTextures:self->_textures withRange:range];
358+
// Set shader arguments.
359+
NSDictionary *globalShaderUniforms = renderer->_globalShaderUniforms;
360+
NSDictionary *setters = self->_shader->_uniformSetters;
361+
for(NSString *uniformName in setters){
362+
CCUniformSetter setter = setters[uniformName];
363+
setter(renderer, self->_shaderUniforms, globalShaderUniforms);
364+
}
387365
}
388366

389367
-(void)transitionRenderer:(CCRenderer *)renderer FromState:(CCRenderState *)previous
@@ -411,9 +389,10 @@ -(instancetype)initWithMode:(CCRenderCommandDrawMode)mode renderState:(CCRenderS
411389

412390
-(void)invokeOnRenderer:(CCRenderer *)renderer
413391
{
414-
CCMetalContext *context = renderer->_metalContext;
392+
CCGraphicsBufferBindingsMetal *buffers = (CCGraphicsBufferBindingsMetal *)renderer->_buffers;
393+
CCMetalContext *context = buffers->_context;
415394
id<MTLRenderCommandEncoder> renderEncoder = context->_currentRenderCommandEncoder;
416-
id<MTLBuffer> indexBuffer = ((CCGraphicsBufferMetal *)renderer->_elementBuffer)->_buffer;
395+
id<MTLBuffer> indexBuffer = ((CCGraphicsBufferMetal *)buffers->_indexBuffer)->_buffer;
417396

418397
CCMTL_DEBUG_PUSH_GROUP_MARKER(renderEncoder, @"CCRendererCommandDraw: Invoke");
419398
CCRendererBindBuffers(renderer, YES);
@@ -423,13 +402,6 @@ -(void)invokeOnRenderer:(CCRenderer *)renderer
423402
[renderEncoder drawIndexedPrimitives:MetalDrawModes[_mode] indexCount:_count indexType:MTLIndexTypeUInt16 indexBuffer:indexBuffer indexBufferOffset:2*_first];
424403
CCMTL_DEBUG_POP_GROUP_MARKER(renderEncoder);
425404

426-
if(!_renderState->_immutable){
427-
// This is sort of a weird place to put this, but couldn't find somewhere better.
428-
// Mutable render states need to have their uniforms redone at least once per frame.
429-
// Putting it here ensures that it's been after all render commands for the frame have prepared it.
430-
((CCRenderStateMetal *)_renderState)->_uniformsPrepared = NO;
431-
}
432-
433405
CC_INCREMENT_GL_DRAWS(1);
434406
}
435407

cocos2d/CCRenderer.m

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,11 +712,22 @@ -(void)dealloc
712712

713713
@implementation CCGraphicsBufferBindings
714714

715-
// Base implementations do do nothing.
716-
-(void)prepare {}
717-
-(void)commit {}
715+
// Base implementations does nothing.
718716
-(void)bind:(BOOL)bind {}
719717

718+
-(void)prepare
719+
{
720+
[_vertexBuffer prepare];
721+
[_indexBuffer prepare];
722+
}
723+
724+
-(void)commit
725+
{
726+
[_vertexBuffer commit];
727+
[_indexBuffer commit];
728+
}
729+
730+
720731
@end
721732

722733

cocos2d/CCRenderer_private.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,15 @@ CCGraphicsBufferPushElements(CCGraphicsBuffer *buffer, size_t requestedCount)
189189
CCGraphicsBuffer *_uniformBuffer;
190190
}
191191

192+
/// Make the buffers ready to use by drawing commands.
193+
-(void)bind:(BOOL)bind;
194+
192195
/// Prepare buffers for changes.
193196
-(void)prepare;
194197

195198
/// Commit changes to buffers.
196199
-(void)commit;
197200

198-
/// Bind the buffers. (Not used by Metal)
199-
-(void)bind:(BOOL)bind;
200-
201201
@end
202202

203203

cocos2d/CCShader.m

Lines changed: 144 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ @implementation CCShader {
201201

202202
//MARK: GL Uniform Setters:
203203

204-
static CCGLUniformSetter
204+
static CCUniformSetter
205205
GLUniformSetFloat(NSString *name, GLint location)
206206
{
207207
return ^(CCRenderer *renderer, NSDictionary *shaderUniforms, NSDictionary *globalShaderUniforms){
@@ -212,7 +212,7 @@ @implementation CCShader {
212212
};
213213
}
214214

215-
static CCGLUniformSetter
215+
static CCUniformSetter
216216
GLUniformSetVec2(NSString *name, GLint location)
217217
{
218218
NSString *textureName = nil;
@@ -255,7 +255,7 @@ @implementation CCShader {
255255
};
256256
}
257257

258-
static CCGLUniformSetter
258+
static CCUniformSetter
259259
GLUniformSetVec3(NSString *name, GLint location)
260260
{
261261
return ^(CCRenderer *renderer, NSDictionary *shaderUniforms, NSDictionary *globalShaderUniforms){
@@ -268,7 +268,7 @@ @implementation CCShader {
268268
};
269269
}
270270

271-
static CCGLUniformSetter
271+
static CCUniformSetter
272272
GLUniformSetVec4(NSString *name, GLint location)
273273
{
274274
return ^(CCRenderer *renderer, NSDictionary *shaderUniforms, NSDictionary *globalShaderUniforms){
@@ -288,7 +288,7 @@ @implementation CCShader {
288288
};
289289
}
290290

291-
static CCGLUniformSetter
291+
static CCUniformSetter
292292
GLUniformSetMat4(NSString *name, GLint location)
293293
{
294294
return ^(CCRenderer *renderer, NSDictionary *shaderUniforms, NSDictionary *globalShaderUniforms){
@@ -370,14 +370,149 @@ -(instancetype)initWithGLProgram:(GLuint)program uniformSetters:(NSDictionary *)
370370
}
371371

372372
#if __CC_METAL_SUPPORTED_AND_ENABLED
373+
374+
static CCUniformSetter
375+
MetalUniformSetBuffer(NSString *name, MTLArgument *vertexArg, MTLArgument *fragmentArg)
376+
{
377+
NSUInteger vertexIndex = vertexArg.index;
378+
NSUInteger fragmentIndex = fragmentArg.index;
379+
size_t bytes = vertexArg.bufferDataSize;
380+
381+
CCMetalContext *context = [CCMetalContext currentContext];
382+
383+
// Handle cc_VertexAttributes specially.
384+
if([name isEqualToString:@"cc_VertexAttributes"]){
385+
NSCAssert(vertexArg && !fragmentArg, @"cc_VertexAttributes should only be used by vertex functions.");
386+
NSCAssert(bytes == sizeof(CCVertex), @"cc_VertexAttributes data size is not sizeof(CCVertex).");
387+
388+
return ^(CCRenderer *renderer, NSDictionary *shaderUniforms, NSDictionary *globalShaderUniforms){
389+
CCGraphicsBufferMetal *vertexBuffer = (CCGraphicsBufferMetal *)renderer->_buffers->_vertexBuffer;
390+
id<MTLBuffer> metalBuffer = vertexBuffer->_buffer;
391+
392+
[context->_currentRenderCommandEncoder setVertexBuffer:metalBuffer offset:0 atIndex:vertexIndex];
393+
};
394+
} else {
395+
// If both args are active, they must match.
396+
NSCAssert(!vertexArg || !fragmentArg || bytes == fragmentArg.bufferDataSize, @"Vertex and fragment argument type don't match for '%@'.", vertexArg.name);
397+
398+
return ^(CCRenderer *renderer, NSDictionary *shaderUniforms, NSDictionary *globalShaderUniforms){
399+
CCGraphicsBufferMetal *uniformBuffer = (CCGraphicsBufferMetal *)renderer->_buffers->_uniformBuffer;
400+
id<MTLBuffer> metalBuffer = uniformBuffer->_buffer;
401+
402+
NSNumber *globalOffset = renderer->_globalShaderUniformBufferOffsets[name];
403+
NSUInteger offset = globalOffset.unsignedIntegerValue;
404+
405+
if(!globalOffset){
406+
void *buff = CCGraphicsBufferPushElements(uniformBuffer, bytes);
407+
[shaderUniforms[name] getValue:buff];
408+
409+
offset = buff - uniformBuffer->_ptr;
410+
}
411+
412+
id<MTLRenderCommandEncoder> renderEncoder = context->_currentRenderCommandEncoder;
413+
if(vertexArg) [renderEncoder setVertexBuffer:metalBuffer offset:offset atIndex:vertexIndex];
414+
if(fragmentArg) [renderEncoder setFragmentBuffer:metalBuffer offset:offset atIndex:fragmentIndex];
415+
};
416+
}
417+
}
418+
419+
static CCUniformSetter
420+
MetalUniformSetSampler(NSString *name, MTLArgument *vertexArg, MTLArgument *fragmentArg)
421+
{
422+
NSUInteger vertexIndex = vertexArg.index;
423+
NSUInteger fragmentIndex = fragmentArg.index;
424+
425+
// For now, samplers and textures are locked together like in GL.
426+
NSString *textureName = [name substringToIndex:name.length - @"Sampler".length];
427+
428+
CCMetalContext *context = [CCMetalContext currentContext];
429+
430+
return ^(CCRenderer *renderer, NSDictionary *shaderUniforms, NSDictionary *globalShaderUniforms){
431+
CCTexture *texture = shaderUniforms[name] ?: globalShaderUniforms[name] ?: [CCTexture none];
432+
NSCAssert([texture isKindOfClass:[CCTexture class]], @"Shader uniform '%@' value must be a CCTexture object.", name);
433+
434+
id<MTLSamplerState> sampler = texture.metalSampler;
435+
436+
id<MTLRenderCommandEncoder> renderEncoder = context->_currentRenderCommandEncoder;
437+
if(vertexArg) [renderEncoder setVertexSamplerState:sampler atIndex:vertexIndex];
438+
if(fragmentArg) [renderEncoder setFragmentSamplerState:sampler atIndex:fragmentIndex];
439+
};
440+
}
441+
442+
static CCUniformSetter
443+
MetalUniformSetTexture(NSString *name, MTLArgument *vertexArg, MTLArgument *fragmentArg)
444+
{
445+
NSUInteger vertexIndex = vertexArg.index;
446+
NSUInteger fragmentIndex = fragmentArg.index;
447+
448+
CCMetalContext *context = [CCMetalContext currentContext];
449+
450+
return ^(CCRenderer *renderer, NSDictionary *shaderUniforms, NSDictionary *globalShaderUniforms){
451+
CCTexture *texture = shaderUniforms[name] ?: globalShaderUniforms[name] ?: [CCTexture none];
452+
NSCAssert([texture isKindOfClass:[CCTexture class]], @"Shader uniform '%@' value must be a CCTexture object.", name);
453+
454+
id<MTLTexture> metalTexture = texture.metalTexture;
455+
456+
id<MTLRenderCommandEncoder> renderEncoder = context->_currentRenderCommandEncoder;
457+
if(vertexArg) [renderEncoder setVertexTexture:metalTexture atIndex:vertexIndex];
458+
if(fragmentArg) [renderEncoder setFragmentTexture:metalTexture atIndex:fragmentIndex];
459+
};
460+
}
461+
462+
static NSDictionary *
463+
MetalUniformSettersForFunctions(id<MTLFunction> vertexFunction, id<MTLFunction> fragmentFunction)
464+
{
465+
// Get the shader reflection information by making a dummy render pipeline state.
466+
MTLRenderPipelineDescriptor *descriptor = [MTLRenderPipelineDescriptor new];
467+
descriptor.vertexFunction = vertexFunction;
468+
descriptor.fragmentFunction = fragmentFunction;
469+
470+
NSError *error = nil;
471+
MTLRenderPipelineReflection *reflection = nil;
472+
[[CCMetalContext currentContext].device newRenderPipelineStateWithDescriptor:descriptor options:MTLPipelineOptionArgumentInfo reflection:&reflection error:&error];
473+
474+
NSCAssert(!error, @"Error getting Metal shader arguments.");
475+
476+
// Collect all of the arguments.
477+
NSMutableDictionary *vertexArgs = [NSMutableDictionary dictionary];
478+
for(MTLArgument *arg in reflection.vertexArguments){ if(arg.active){ vertexArgs[arg.name] = arg; }}
479+
480+
NSMutableDictionary *fragmentArgs = [NSMutableDictionary dictionary];
481+
for(MTLArgument *arg in reflection.fragmentArguments){ if(arg.active){ fragmentArgs[arg.name] = arg; }}
482+
483+
NSSet *argSet = [[NSSet setWithArray:vertexArgs.allKeys] setByAddingObjectsFromArray:fragmentArgs.allKeys];
484+
485+
// Make uniform setters.
486+
NSMutableDictionary *uniformSetters = [NSMutableDictionary dictionary];
487+
488+
for(NSString *name in argSet){
489+
MTLArgument *vertexArg = vertexArgs[name];
490+
MTLArgument *fragmentArg = fragmentArgs[name];
491+
492+
// If neither argument is active. Skip.
493+
if(!vertexArg.active && !fragmentArg.active) continue;
494+
495+
MTLArgumentType type = (vertexArg ? vertexArg.type : fragmentArg.type);
496+
NSCAssert(!vertexArg || !fragmentArg || type == fragmentArg.type, @"Vertex and fragment argument type don't match for '%@'.", name);
497+
498+
switch(type){
499+
case MTLArgumentTypeBuffer: uniformSetters[name] = MetalUniformSetBuffer(name, vertexArg, fragmentArg); break;
500+
case MTLArgumentTypeSampler: uniformSetters[name] = MetalUniformSetSampler(name, vertexArg, fragmentArg); break;
501+
case MTLArgumentTypeTexture: uniformSetters[name] = MetalUniformSetTexture(name, vertexArg, fragmentArg); break;
502+
case MTLArgumentTypeThreadgroupMemory: NSCAssert(NO, @"Compute memory not supported. (yet?)"); break;
503+
}
504+
}
505+
506+
return uniformSetters;
507+
}
508+
373509
-(instancetype)initWithMetalVertexFunction:(id<MTLFunction>)vertexFunction fragmentFunction:(id<MTLFunction>)fragmentFunction
374510
{
375511
if((self = [super init])){
376512
_vertexFunction = vertexFunction;
377513
_fragmentFunction = fragmentFunction;
378514

379-
#warning TODO setup _uniformSetters
380-
// _uniformSetters = uniformSetters;
515+
_uniformSetters = MetalUniformSettersForFunctions(vertexFunction, fragmentFunction);
381516
}
382517

383518
return self;
@@ -458,6 +593,8 @@ +(void)initialize
458593
#if __CC_METAL_SUPPORTED_AND_ENABLED
459594
if([CCConfiguration sharedConfiguration].graphicsAPI == CCGraphicsAPIMetal){
460595
id<MTLLibrary> library = [CCMetalContext currentContext].library;
596+
NSAssert(library, @"Metal shader library not found.");
597+
461598
id<MTLFunction> vertex = [library newFunctionWithName:@"CCVertexFunctionDefault"];
462599

463600
CC_SHADER_POS_COLOR = [[self alloc] initWithMetalVertexFunction:vertex fragmentFunction:[library newFunctionWithName:@"CCFragmentFunctionDefaultColor"]];

cocos2d/CCShader_Private.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
#import "ccMacros.h"
1+
#import "CCShader.h"
2+
23

34
#if __CC_METAL_SUPPORTED_AND_ENABLED
45
#import <Metal/Metal.h>
6+
#import "CCMetalSupport_Private.h"
57
#endif
68

7-
#import "CCShader.h"
89

910
@class CCRenderer;
1011

11-
typedef void (^CCGLUniformSetter)(
12+
13+
typedef void (^CCUniformSetter)(
1214
__unsafe_unretained CCRenderer *renderer,
1315
__unsafe_unretained NSDictionary *shaderUniforms,
1416
__unsafe_unretained NSDictionary *globalShaderUniforms
1517
);
1618

19+
1720
@interface CCShader() {
1821
@public
1922
GLuint _program;

0 commit comments

Comments
 (0)