How to compile fragment shader ahead of time? - ios

The documentation for SKShader says:
Compiling a shader and the uniform data associated with it can be
expensive. Because of this, you should initialize shader objects when
your game launches, not while the game is running.
From that I assumed that the shader would compile when it is initialized, but that's not the case (probably a bug?); it seems to compile the moment it makes its first appearance, so I get a short app-wide lag the first time this happens, along with a shader compilation succeeded message in the console.
I can fix this by adding a zero size node to the scene, and cycling through any needed shaders 🥴.
let node = SKSpriteNode()
node.shader = someShader
scene.addChild(node)
Kind of hack...is there a better way to compile a shader ahead of time?

Related

Wanting to ditch MTKView.currentRenderPassDescriptor

I have an occasional issue with my MTKView renderer stalling on obtaining a currentRenderPassDescriptor for 1.0s. According to the docs, this is either due the view's device not being set (it is) or there are no drawables available.
If there are no drawables available, I don't see a means of just immediately bailing or skipping that video frame. The render loop will stall for 1.0s.
Is there a workaround for this?. Any help would be appreciated.
My workflow is a bunch of kernel shader work then one final vertex shader. I could do the drawing of the final shader onto my own texture (instead of using the currentPassDescriptor), then hoodwink that texture into the view's currentDrawable -- but in the obtaining of that drawable we're back to the same stalling situation.
Should I get rid of MTKView entirely and fall back to using a CAMetalLayer instead? Again, I suspect the same stalling issues will arise. Is there a way to set the maximumDrawableCount on an MTKView like there is on CAMetalLayer?
I'm a little baffled as, according the Metal System Trace, my work is invariably completed under 5.0ms per frame on an iMac 2015 R9 M395.

WARNING: Output of vertex shader 'v_gradient' not read by fragment shader

When i run my app in ios 10 using xcode 8 i am getting following message in debug console, and by UI getting freezed can any one know why this is happening
ERROR
/BuildRoot/Library/Caches/com.apple.xbs/Sources/VectorKit/VectorKit-1228.30.7.17.9/GeoGL/GeoGL/GLCoreContext.cpp
1763: InfoLog SolidRibbonShader: ERROR
/BuildRoot/Library/Caches/com.apple.xbs/Sources/VectorKit/VectorKit-1228.30.7.17.9/GeoGL/GeoGL/GLCoreContext.cpp
1764: WARNING: Output of vertex shader 'v_gradient' not read by
fragment shader
Answer
One of the situations where you might get this warning in Xcode is when using an app that uses shaders such as the Maps app with an MKMapView. You'll find that the map view works as expected without that warning on a real device with real hardware/native OS.
In the sim the SolidRibbonShader fragment shader is not able to read the output of the v_gradient vertex shader probably because it's in beta or there might be an incompatibility between Xcode version and SIM version. However the shaders are recognized on a real device.
Explanation
Those shaders belong to the OpenGL Rendering Pipeline. The Rendering Pipeline is the sequence of steps that OpenGL takes when rendering objects.
The rendering pipeline is responsible for things like applying texture, converting the vertices to the right coordinate system and displaying the character on the screen etc.
There are six stages in this pipeline.
Per-Vertex Operation
Primitive Assembly
Primitive Processing
Rasterization
Fragment Processing
Per-Fragment Operation
Finally, an image appears on the screen of your device. These six stages are called the OpenGL Rendering Pipeline and all data used for rendering must go through it.
What is a shader?
A shader is a small program developed by you that lives in the GPU. A shader is written in a special graphics language called OpenGL Shading Language(GLSL).
A shader takes the place of two important stages in the OpenGL Rendering Pipeline: Per-Vertex Processing and Per-Fragment Processing stage. There is one shader for each of these two stages.
The ultimate goal of the Vertex Shader is to provide the final transformation of the mesh vertices to the rendering pipeline. The goal of the Fragment shader is to provide Coloring and Texture data to each pixel heading to the framebuffer.
Vertex shaders transform the vertices of a triangle from a local model coordinate system to the screen position. Fragment shaders compute the color of a pixel within a triangle rasterized on screen.
Separate Shader Objects Speed Compilation and Linking
Many OpenGL ES apps use several vertex and fragment shaders, and it is often useful to reuse the same fragment shader with different vertex shaders or vice versa. Because the core OpenGL ES specification requires a vertex and fragment shader to be linked together in a single shader program, mixing and matching shaders results in a large number of programs, increasing the total shader compile and link time when you initialize your app.
Update: the issue seems to be gone now on Xcode9/iOS11.
Firstly, the freezing problem happens only when run from Xcode 8 and only on iOS 10 (currently 10.0.2), whether in debug or release mode. MKMapView though seems fine when the app is distributed via App Store or 3rd party ad hoc distribution systems. The warnings you are seeing may or may not be related to the problem, I don't know.
What I've found is that the offending code is in MKMapView's destructor, and it doesn't matter what you do with the map view object or how you configure it, i.e. merely calling
[MKMapView new];
anywhere in your code will freeze the app. The main thread hangs on a semaphore and it's not clear why.
One of the things I've tried was to destroy the map view object in a separate thread but that didn't help. Eventually I decided to retain my map objects at least in DEBUG builds.
NOTE: this is a really sh*tty workaround but at least it will help you to debug your app without freezing. Retaining these objects means your memory usage will grow by about 45-50MB every time you create a view controller with a map.
So, let's say if you have a property mapView, then you can do this in your view controller's dealloc:
- (void)dealloc
{
#if DEBUG
// Xcode8/iOS10 MKMapView bug workaround
static NSMutableArray* unusedObjects;
if (!unusedObjects)
unusedObjects = [NSMutableArray new];
[unusedObjects addObject:_mapView];
#endif
}

Creating a time elapsed uniform for GPUImage

I'm working with Brad Larson's GPUImage, which I've found to be really amazing. For flexibility and ease of writing, I've created a custom filter that has 3 uniform inputs. I based it on the GPUImageBrightnessFilter.h and .m. One uniform is simply a "parameter" float, one is a "center" CGPoint to feed in touch coordinates if I need them, and one will hopefully be a time float which is the amount of time that has elapsed since the shader was started. I'm using initWithFragmentShaderFromFile to use my own shader files.
My "parameter" and "center" uniforms work. It's the time uniform that doesn't work because I don't understand how to create a uniform that is constantly changing (as time does).
The goal of my time uniform seems to be similar to SpriteKit's u_time. It returns the number of seconds since the current shader has been running which you can feed into your custom shader to animate it. One very simple example use might be to cycle the hue of an image as time passes. I'm hoping to build upon that with more complexity as my knowledge of GLSL grows.
I'm not sure what code to post, as my misunderstanding could be in many places and it may be that I'm trying to do something that's not possible. This post about setting a start time and subtracting it from CACurrentMediaTime() seems like a good place to start:
Getting the time elapsed (Objective-c)
The problem is I'm not sure how to continually update a uniform to pass to my shader. Do I do it in my custom filter's setter for time? Don't custom setter's only get executed once? Do I do it in my init override? Doesn't that only get executed once? Do I write a loop in my controller that continually calls the setter? That seems like an expensive and messy solution.
I'm not asking for code, just suggestions to steer me in the right direction or un-steer me from whatever wrong direction I've gone in my thinking.
Asking the question helped me to get to a solution and learn about NSTimer. I incorporated a method that creates a repeating NSTimer into my customized GPUImageFilter subclass that passes a time uniform to the shader. It calls a selector that increments my 'time' variable and passes that into the uniform array.

glDrawArrays takes long on first call using OpenGL ES on iOS

I'm trying to use multiple GLSL fragment shaders with OpenGL ES on iOS 7 and upwards. The shaders itself are running fine after the first call to glDrawArrays. Nevertheless, the very first call to glDrawArrays after the shaders and their program have been compiled and linked takes ages to complete. Afterwards some pipeline or whatever seems to have been loaded and everything goes smooth. Any ideas what the cause of this issue are and how to prevent it?
The most likely cause is that your shaders may not be fully compiled until you use them the first time. They might have been translated to some kind of intermediate form when you call glCompileShader(), which would be enough for the driver to provide a compile status and to act as if the shaders had been compiled. But the full compilation and optimization could well be deferred until the first draw call that uses the shader program.
A commonly used technique for games is to render a few frames without actually displaying them while some kind of intro screen is still shown to the user. This prevents the user from seeing stuttering that could otherwise result from all kinds of possible deferred initialization or data loading during the first few frames.
You could also look into using binary shaders to reduce slowdowns from shader compilation. See glShaderBinary() in the ES 2.0 documentation.
What actually helped speeding up the first draw call was the following (which is fine in my use case since I'm rendering a video so no depth testing is needed).
glDisable(GL_DEPTHTEST)

Spritekit: First texture draw slow (preloaded)

I'm having a performance issue regarding Apple Spritekit Framework.
I use the method preloadTextureAtlases:withCompletionHandler: of SKTextureAtlas to make sure all my textures are loaded before the game really starts.
Yet when the first texture of this atlas (whatever it is) is added to the scene as a child I got a small lag, like if the texture wasn't loaded. It only happens the first time.
I tried real hard to debug this and to find the reason but I couldn't find it. Looking with Instrument and the Time Profiler I got this
Which seems strange since my texture atlas contains all the allocated textures (and they are still allocated). Why does it call a load method ? Maybe it's something related to OpenGL like the texture binding.
I'm sure this correspond to the frame where my object is allocated (see the spike on the right). Nothing else is allocated or in motion here.
I'm really stuck on this and any help would be deeply appreciated.
Thanks for your time.

Resources