Can I share codebase between OpenGL ES2- and OpenGL ES3-based renderers? - ios

In the app I'm writing there is a class cluster that provides rendering means. It resolves to OpenGL ES3 version when the device is capable of that, and falls back to OpenGL ES2 version otherwise. It looks roughly like the following code.
/* Client code */
/* `Renderer` is the class cluster instantiated like this: */
self.renderer = [Renderer rendererForAPI:self.context.API];
/* ----------- */
/* Inside `Renderer` implementation block: */
+ (instancetype)rendererForAPI:(uint)apiVersion
{
Renderer *renderer;
if (apiVersion >= 3) {
renderer = [[RendererForES3 alloc] init];
} else {
renderer = [[RendererForES2 alloc] init];
}
return renderer;
}
/* --------------- */
/*
* Both `RendererForES3` and `RendererForES2` classes
* are descendants of `Renderer`.
* One of them imports `OpenGLES.ES3`,
* and the other imports `OpenGLES.ES2`.
*/
I do not want the code to duplicate overly. Some of my drawing methods are very similar in both members of the cluster, and some of the methods are nearly identical.
I realise that OpenGL calls, even with exactly the same name, may map to different offsets in different versions of the standard.
So the question is how to keep code duplication at its minimum.
There are two obvious solutions that sprung to my head.
The first one is to inherit ES3 Renderer from ES2 Renderer, overriding necessary methods and keeping the rest. But in that case, ES3 Renderer would receive both OpenGL imports at the same time. I'm not sure which precautions are to consider in that case, and how well will it work on different hardware.
The second solution is simply to keep the repeating code in a text file that would be included in both implementations via #include directive. But this seems to encumber maintenance.
Perhaps there are some advice on how to reuse code in renderers which rely on different versions of OpenGL standard. Maybe it's an architecture issue and my code should have been designed some other way.
What decision should I take?

For now, I chose to subclass my ES3 renderer from the ES2 renderer.
It seems to work fine, as ES3 is emphasised as a superset of ES2.
The most noticeable drawback is that version 100 shaders and version 300 shaders can not be linked together. That means if one intends to support both ES3 and ES2, it might be necessary to have two separate shader codebases.
Also worth keeping in mind:
https://developer.apple.com/library/ios/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/AdoptingOpenGLES3/AdoptingOpenGLES3.html

Related

How to output list of functions for iOS release binary

We are porting some (mostly written in C++) game to iOS, and we recently introduced a useful macro into C++ code which (as a side effect) generates some redundant code. This code is sort of
void f(D1 *d1) {
f(static_cast<B *>(d1));
}
void f(D2 *d2) {
f(static_cast<B *>(d2));
}
...
void f(DN *dN) {
f(static_cast<B *>(dN));
}
We want to make sure that global optimization step (or whatever you call it) for release build strips away these redundant functions (make them inlined).
What I have is .ipa and .dsym files. So what tool would you suggest to analyze code binary (i.e. to output list of functions in some format) and ... what is code binary in this case? Something inside .ipa?

Metal shader types vertex and buffer best practice demonstrated in "ShaderTypes.h"

Here is the full shadertypes.h file. I am going to break it down into parts that I cant understand.
#ifndef ShaderTypes_h
#define ShaderTypes_h
//Part 1 Compiler flags
#ifdef __METAL_VERSION__
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#define NSInteger metal::int32_t
#else
#import <Foundation/Foundation.h>
#endif
#include <simd/simd.h>
//Part 2 buffer index
typedef NS_ENUM(NSInteger, BufferIndex)
{
BufferIndexMeshPositions = 0,
BufferIndexMeshGenerics = 1,
BufferIndexUniforms = 2
};
//Part 3 vertex attribute and position
typedef NS_ENUM(NSInteger, VertexAttribute)
{
VertexAttributePosition = 0,
VertexAttributeTexcoord = 1,
};
//Part 4 texture index color
typedef NS_ENUM(NSInteger, TextureIndex)
{
TextureIndexColor = 0,
};
//Part 5 uniforms
typedef struct
{
matrix_float4x4 projectionMatrix;
matrix_float4x4 modelViewMatrix;
} Uniforms;
#endif /* ShaderTypes_h */
Part 1
#ifdef __METAL_VERSION__
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#define NSInteger metal::int32_t
#else
#import <Foundation/Foundation.h>
#endif
//A bunch of definitions go after this
#endif
My confusion is mostly why we are doing all of this code. It seems to be checking if the user has metal but then what is this NS_ENUM it is defining? Why is it defining a metal variable? While is foundation conditionally imported?
Part 2
typedef NS_ENUM(NSInteger, BufferIndex)
{
BufferIndexMeshPositions = 0,
BufferIndexMeshGenerics = 1,
BufferIndexUniforms = 2
};
Not exactly sure what this is doing especially since I cant find anyone referencing them explicitly anywhere.
Part 3
typedef NS_ENUM(NSInteger, VertexAttribute)
{
VertexAttributePosition = 0,
VertexAttributeTexcoord = 1,
};
This one has a bit more of a clear usage because it is referenced in the .metal file
typedef struct
{
float3 position [[attribute(VertexAttributePosition)]];
float2 texCoord [[attribute(VertexAttributeTexcoord)]];
} Vertex;
as well as in the attribute section of the vertex descriptor code
mtlVertexDescriptor.attributes[VertexAttribute.position.rawValue].format = MTLVertexFormat.float3
mtlVertexDescriptor.attributes[VertexAttribute.position.rawValue].offset = 0
mtlVertexDescriptor.attributes[VertexAttribute.position.rawValue].bufferIndex = BufferIndex.meshPositions.rawValue
...
Somehow it appears to be keeping track of indexes of the various elements rather like the buffer.
Part 4
This one I sort of get as it is referenced in the render here
renderEncoder.setFragmentTexture(colorMap, index: TextureIndex.color.rawValue)
as well as the shader here
fragment float4 fragmentShader(..., texture2d<half> colorMap [[ texture(TextureIndexColor) ]]){
I sort of get this one (Minus the NSEnum part) however I dont get how it is good practice to do this for just one thing.
Part 5
This one is actually the only one I understand it looks like its a struct for all the uniform components which makes a lot of sense as it is storing the actual types of the uniforms allowing the shader to be exposed to the struct as well as the renderer.
I guess ultimately I am wondering why this approach was taken and why it is a suggested best practice by Apple. I suppose it sort of makes sense to do things this way except for the fact that metal appears to play better with objective-c even though it looks like swift.
This is a header that's meant to be shared by Metal shader code and app code (either Objective-C or, via bridging, Swift). Having to share it between those two languages requires a bit of care.
The #ifdef __METAL_VERSION__ test determines which language it's being compiled in. For Metal, a couple of macros are defined. For Objective-C, it imports Foundation. (Foundation can't, of course, be imported when compiling Metal code.)
In their (Objective-)C headers, Apple routinely declare enums using the NS_ENUM() macro. That macro is defined within the Foundation headers, so it can be used when this header is being compiled into app code. It's not defined in the Metal headers, so this header needs to define it for itself if it wants to use it (as it does). The Foundation version of the macro is useful because it detects compiler capabilities to use typed enums if available and conventional enums if not. In this header, Apple is implementing that same macro for the Metal shader language. Since there's no question whether the compiler supports typed enums (it does), there's only one definition of the macro which takes advantage of that feature.
Later in the header, they plan on using the NSInteger type. That's defined by the Foundation headers for app code. Since they want to use the same code for Metal but that type isn't defined there, they need to define it in this header. They make a couple of weird choices, though. First, they use a macro instead of a typedef. Second, they define it to be equivalent to int32_t (in the metal namespace). That's weird because the app code is going to be 64-bit (Metal isn't available to 32-bit apps) causing NSInteger to be a 64-bit integer type (equivalent to int64_t). So, the two worlds are going to disagree about the size of NSInteger and therefore all of the enums based on it. That's kind of bad, but probably doesn't cause a real issue given the ways these enums are actually used.
It would probably have been better to simply base the enums on int, which is 32-bit in all Apple environments.
Parts 2, 3, and 4 are all similar. It's generally good practice to use "symbolic" constants rather than just magic numbers (i.e. integer literals within the code). It is less error-prone and improves readability. These parts are simply defining some such symbolic constants for use by the Metal code and app code to share. The fact that some of these names aren't used in the particular sample project you're examining suggests that Apple uses this same header for multiple sample projects, or something like that.

Dynamically implementing a delegate during runtime

In my class, I have a reference on an UIViewController and want to implement a delegate on this ViewController during runtime. The delegate has only one method (with two parameters) and when the delegate-method on the ViewController is invoked, my class should handle the call.
I am quite sure this is possible with some kind of method swizzling, etc. but I don't know how to accomplish this.
What you want is possible, but it's not method swizzling, since you don't want to switch to methods but add a new one. It can be done, thanks to Objective-C's dynamic nature, but it's still a dirty hack so also file a feature request with the library vendor.
What you want is class_addMethod() and a C function with the actual implementation for that. One more thing, Objective-C methods are C methods, but with two implicit parameters, self and _cmd, which have to keep in mind (both when creating your C method and when telling class_addMethod your methods signature. And here is an SSCE of how to pull something like that off:
#import <Foundation/Foundation.h>
#import <objc/runtime.h> // Required for class_addMethod()
#interface MyClass : NSObject
#end
#implementation MyClass
#end
#protocol MyProtocol <NSObject>
- (void)printString:(NSString *)string;
#end
// Note the method signature containing the
// two implicit parameters self and _cmd!
void MyClassPrinStringIMP(id self, SEL _cmd, NSString *string)
{
NSLog(#"Hi I'm %#:%s and this is the string: %#", self, sel_getName(_cmd), string);
}
void PimpMyClass()
{
// The last argument is the signature. First character is the return type, in our case void
// Then comes self and _cmd, followed by the NSString. You can use #encode() to find out how your
// type is encoded. Best is to build this string at runtime, since the encoding can change with architectures
class_addMethod([MyClass class], #selector(printString:), (IMP)MyClassPrinStringIMP, "v#:#");
}
int main(int argc, const char * argv[])
{
#autoreleasepool
{
PimpMyClass();
id foo = [[MyClass alloc] init]; // id, to silence the compiler!
[foo printString:#"Hello World"];
}
return 0;
}
Example output:
Hi I'm <MyClass: 0x100101810>:printString: and this is the string: Hello World
Edit: Something that you may find is that the passed object is checked at runtime wether it conforms to a protocol or not using conformsToProtocol:. Since this code just adds the method implementation, it would still fail, but you can tell the runtime that you totally do implement that protocol with this one function call:
class_addProtocol([MyClass class], #protocol(MyProtocol));
Alternative: proxies
Objective-Cs dynamism and message forwarding is already praised by #JasperBlues, however, there is one particular class in Objective-C that is designed to do just that: NSProxy. It is designed to intercept sent messages and dispatching them dynamically to the relevant target, and does use the high-level NSInvocation approach. If you can pass a proxied object in some way as the delegate (depending on what your code allows for and what not), creating a NSProxy subclass might be the cleanest way to go.
However, note though that you then end up with a shim object that wraps over your other object, which comes with its own bag of pain and will break when you try to directly access variables via -> syntax. It's not a perfectly invisible proxy, but good enough for most cases.
Firstly, some comments indicate that what you're asking is instantly "a bad thing to do" or a "dirty hack". I disagree here. Most modern Object Oriented languages support these features, and they are used to good effect by numerous system-level frameworks. Of course it is human-nature to perhaps use these dynamic features where they're not really required (for fun or practice), even when a simpler approach would work fine. Beware of this.
Objective-C is admirable in that its somewhat of a legacy language and close to the "bare metal", and yet features a surprising level of dynamism, making it relatively easy to support these requirements without any external libraries or frameworks.
Besides using the class_addMethod guide that another answer correctly indicates, some other approaches are:
Message Forwarding: (recommended)
All NSObject sub-classes have the ability to forward a method that they're not able to respond to, to another target object. This is similar to the lower-level concept of trampolines. Apple publishes a guide on using this approach.
The advantages of using forward invocation is that it uses the NSInvocation level of abstraction, instead of directly calling the C ObjC runtime API. This abstracts the following details away:
Structs and primitives will be box/unboxed automatically
Dispatching to methods with a dynamic/unknown number of arguments becomes easy. Until arm64, this could be done using va_args, however on arm64 va_args can be copied directly to registers, and not popped off the stack.
Resolve Instance Method:
Instance methods are created by by registering a C function as the implementation to respond to a given message. This can be done neatly with blocks, using IMP_ImplementationWithBlock:
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
IMP imp = imp_implementationWithBlock((__bridge id) objc_unretainedPointer(
^(id me, BOOL firstParam, NSString* secondParam)
{
//Implementation goes in here
return something; //something of type 'id'
}));
class_addMethod(self, sel, imp, "##:");
return YES;
}
return NO;
}
Using libffi:
Libffi can also do this kind of thing, though it should not be necessary if you're using pure Objective-C, as the runtime already has these features baked in.

How to Setup Unit Tests for cocos2d apps?

I have been trying to figure out how to setup OCUnit unit tests for my game that uses cocos2d. So I followed these instructions to set up the test target initially: http://blog.shalomfriss.com/?p=894
Unfortunately, the code below triggers a signal SIGABRT when I run the test through Product > Test in Xcode. The function where it breaks seems to be at [CCGLProgram compileShader:type:byteArray:]
#implementation GameTests
- (void)setUp
{
[super setUp];
}
- (void)tearDown
{
// Tear-down code here.
[super tearDown];
}
- (void)testExample
{
STAssertNotNil([InitialLayer scene], #"initial layer scene was null.");
}
#end
This is the scene method from InitialLayer:
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
InitialLayer*layer = [InitialLayernode];
[scene addChild: layer];
return scene;
}
Some other possibly relevant info:
target device: ipad 6.1
xcode version: 4.6.3
cocos2d version 2.1
Does anyone know how to fix this or is there a better way in general to setup the tests using OCUnit or any other testing framework?
You don't want to unit test anything cocos2d-related. Without properly setting up the cocos2d CCGLView, the Director, and so on (the code that goes in the app delegate) in the setUp method that won't work to begin with.
Even when you do set it up, you always will be testing cocos2d itself, which will make your tests less isolated and you're almost required to set up a fresh new scene for each test in order to test any code in subclassed nodes. Which means the time to write tests as well as how long each test runs quickly grows.
Further complicating matters is that cocos2d's updates are tied to the CADisplayLink updates, and I'm not even sure they get fired when running a unit test. Even when updates do fire, you can't test them because a unit test runs once and ends, it has no concept "runs over a certain period of time". So something like "move a node to x,y and check that it got there after 3 seconds" is inherently not testable with the way cocos2d and unit tests work. At least not without heavily modifying cocos2d itself.
If you consider that cocos2d represents the view of your app, the real question is: why would you even want to unit test anything view-related in the first place?
Instead what you can do is to design your classes completely independent of the view. For example you can create extra game logic classes that are added to a node's userObject property and then control the node - the actual testable game logic happens in the model classes. You can have additional controller classes which fire off things like running animations, or particle effects, and any other view-related stuff that doesn't need to be tested - because at worst it won't look right, but it won't affect the gameplay.
One of the key benefits of test-driven development is to make classes testable, and that means not making them depend on the view. So your first goal should actually be to make tests that run without cocos2d.
If you want to see how this can be done, check out OpenGW and specifically the class reference. Besides pushing a game object's (OGWEntity) position and rotation to the view delegate (OGWNode) there's absolutely no dependency on view code. The intermediary class that performs actions on the view based on the entity's state is OGWController.
All of this makes OpenGW a highly testable game simulation engine. In fact it is so decoupled from anything view-related that I'm in the process of making the same code run with both Sprite Kit and cocos2d-iphone.
I agree that a complete test for everything is a waste of time but it doesn't mean to avoid it completely. It's good to separate your logic classes and methods and test them using the unit test to make sure that they are working correctly using gTest with modification to cmake. I did unit testing for util and logic classes like this:
I added this to end of my CMAKE file
if(WINDOWS)
set(UNIT_TEST_SOLUTION_NAME runUnitTests)
FILE(GLOB_RECURSE USER_TEST "Classes/*.test.cpp")
list(APPEND GAME_TEST
${USER_TEST}
#${USER_HEADER}
#${USER_CPP}
Classes/utils/common_operators/CommonOperators.cpp
Classes/utils/common_operators/CommonOperators.h
)
#list(FILTER GAME_TEST EXCLUDE REGEX "Classes/scenes/.*")
#list(FILTER GAME_TEST EXCLUDE REGEX "AppDelegate.cpp")
#list(FILTER GAME_TEST EXCLUDE REGEX "AppDelegate.h")
#list(FILTER GAME_TEST EXCLUDE REGEX "Classes/text/persian_language_support/.*")
set(run_unit_test_file
${GAME_TEST}
proj.unit_test/main.cpp
)
#option(test "Build all tests." ON)
enable_testing()
find_package(GTest CONFIG REQUIRED)
add_executable(${UNIT_TEST_SOLUTION_NAME} ${run_unit_test_file})
target_link_libraries(runUnitTests GTest::gtest GTest::gtest_main GTest::gmock GTest::gmock_main)
add_test(runUnitTests runUnitTests)
endif()
And example test file is :
#include "gtest/gtest.h"
#include "CommonOperators.h"
TEST(CommonOperators, split) {
auto case1Result = CommonOperators::split("mamad reza masood", ' ');
std::vector<std::string> case1ExpectedResult = { "mamad", "reza", "masood" };
EXPECT_EQ(case1Result, case1ExpectedResult);
}
Please note that I downloaded google test (gTest) using vckpg and then placed its folder in my root directory.
Another note: For running your test in windows you need to define new solution with name that is mentioned in your cmake test part.And also you need to create main file like following in a new directory
#include "gtest/gtest.h"
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

Loading GLSL shader without using any Apple APIs

What is a good way to load a GLSL shader using C/C++ without using Objective-C or any Apple APIs?
I am currently using the following method, which is from the iPhone 3D Programming book, but it says that it is not recommended for production code:
Simple.vsh
const char* SimpleVertexShader = STRINGIFY
(
// Shader code...
);
RenderingEngine.cpp
#define STRINGIFY(A) #A
#include "Simple.vsh"
// ...
glShaderSource( shaderHandle, 1, &SimpleVertexShader, 0 );
If you want to load your shaders from files in your app bundle, you can get the file paths using the NSBundle object (in Objective-C), or using the CoreFoundation CFBundle object (in pure C).
Either way, you are using Apple-specific APIs. The only thing you're getting by using CFBundle instead of NSBundle is more boilerplate code.
If you don't want to use any Apple APIs, then your options are to embed your shaders as literal strings, or connect to a server on the Internet and download them (using the Unix socket API).
What you really need to do is define an interface by which your RenderingEngine gets the source code for its shaders, and implement that interface using the appropriate platform-specific API on each platform to which your port the RenderingEngine. The interface can be something super simple like this:
RenderingEngineShaderSourceInterface.h
#ifdef __cplusplus
extern "C" {
#endif
// You are responsible for freeing the C string that this function returns.
extern char const *RenderingEngine_shaderSourceForName(char const *name);
#ifdef __cplusplus
}
#endif
Then you create RenderingEngineShaderSource_Windows.cpp, RenderingEngineShaderSource_iOS.m, RenderingEngineShaderSource_Linux.cpp, etc. Each one implements RenderingEngine_shaderSourceForName using the appropriate API for that platform.
I use one of two methods. If it's a short shader, I may just put it code:
const char shader[] =
"uniform vec4 blah;\n" // Note, no semicolon here - it does the right thing
"main ()\n"
"{\n"
...rest of code
"}\n";
Or, if it's longer or going to be re-used in other places, I'll put it into a text file in the resources and read the text file at run time. You can get to it via [NSBundle pathForResource:ofType:].
Consider a C++ raw string literal; no STRINGIFY is needed since the newer features of C++ allow you to do similar things without macro.
I'd retype a good example but here is one.

Resources