UITableView layer shadow performance optimisation? - performance

I have got a UITableView with cells that include a layer shadow and their subviews do too. So summed up I have about 5 shadows per cell. I reuse my cells but they change their height occasionally when the content exceeds a certain height (I say this because this could probably cause more draw calls with the 'optimisations' I enlist below).
Now, I have added the following optimisations:
// setting it opaque will tell the GPU not to blend the
// layer (-> less draw calls - only useful when layer completely opaque obviously)
myShadowView.opaque = YES;
// another significant factor was the shadowPath property,
// it significantly smoothed scrolling
myShadowView.layer.shadowOffset = CGSizeMake(0, -1);
myShadowView.layer.shadowOpacity = 0.08;
myShadowView.layer.shadowRadius = 1;
myShadowView.layer.shadowPath = [UIBezierPath bezierPathWithRect:shad.bounds].CGPath;
// the most important change
myShadowView.layer.shouldRasterize = YES;
myShadowView.layer.rasterizationScale = [UIScreen mainScreen].scale;
I am very very happy with the performance -- scrolling is smooth as baby skin -- but the shouldRasterize property comes with drawbacks. When I scroll down, it takes the cell a little while to load the bitmap. Is there any way to pre-render those cells ? Would a table without the reuse identifier get rid of the problem ?
I absolutely need the shadows, but I would not sacrifice performance for them. I hope someody can help.

Related

Does unseen cells from tilemap affect performance in Godot

Let's say I have a single Tilemap 6400 x 6400, I fill whole layer0 with some cell then I draw objects on top, and then even some more objects on top.
When rendering godot goes through it layer by layer? And objects on layer0 affect the performance while drawing for nothing? Even if they can't be seen?
Should I be careful about objects that can be seen and delete them on the TileMap to achieve the best performance?

External elements slowing down canvas

I am developing a game using several canvases (3) on top of one another. I am close to finishing the game and I haven't yet optimized the performance.
Regardless, my main concern is that the game has performed pretty well so far, but being close to finish I am building a simple web page around the canvas to give a frame to the game. I am talking just putting the title of the game and a few links here and there, but suddenly the game is now choppy and slow!!! If remove those elements everything is smooth again.
The culprits are:
The game title above the canvas (styled with text-shadow).
four buttons below the canvas to redirect to other sites and credits.
Is it possible that this few static elements interfere with the rendering of the game?
Thank you.
Anything with shadows, rounded corners or expensive effects such as blur cost a lot to render.
Modern browsers try to optimize this in various way but there are special cases which they can't get around just like that (updated render engines using 3D hardware can help in the future).
Shadows are closely related to blurring and needs to be composited per frame due to the possibility that the background, shadow color, blur range etc. could change. Rounded corners forces the browser to create an alpha mask instead of doing just a rectangular clip. The browser may cache some of these operations, but they'll add up in the end.
Text Shadow
A workaround is to "cache" the shadowed text as an image. It can be a pre-made image from Photoshop or it could be made dynamically using a canvas element. Then display this instead of the text+shadow.
Example
var ctx = c.getContext("2d"),
txt = "SHADOW HEADER";
// we need to do this twice as when we set width of canvas, state is cleared
ctx.font = "bold 28px sans-serif";
c.width = ctx.measureText(txt).width + 20; // add space for shadow
c.height = 50; // estimated
// and again...
ctx.font = "bold 28px sans-serif";
ctx.textBaseline = "top";
ctx.textAlign = "left";
ctx.shadowBlur = 9;
ctx.shadowOffsetX = 9;
ctx.shadowOffsetY = 9;
ctx.shadowColor = "rgba(0,0,0,0.8)";
ctx.fillStyle = "#aaa";
ctx.fillText(txt, 0, 0);
body {background:#7C3939}
<canvas id=c></canvas>
The canvas element can now be placed as needed. In addition you could convert the canvas to an image and use that without the extra overhead.
Rounded Corners
Rounded corners on an element is also expensive and there are no easy way around this - the corners need to be cut one way or another and question is which method is fastest.
Let browser do it using CSS
Overlay the element with the outer corners covered in the same color as background - clunky but can be fast as no clipping is needed. However, more data need to be composited.
Use a mask in canvas directly via globalCompositeOperation. The chances are this would be the slowest method. Performance tests must be made for this scenario to find out which one works best overall.
Make a compromise and remove rounded corners all together.
Links
Also these could be replaced by clickable images. It's a bit more tedious but also these could be made dynamically using a canvas allowing the text to change ad-hoc.
CSS
I would also recommend experimenting with position: fixed; for some of the elements. When fixed is used, some browsers renders that element separately (gives it its own bitmap). This may be more efficient in some cases.
But do make some performance tests to see what combination is the best for your scenario.

how to drop shadows under sprites in Top-down view with cocos2d

I just started learning OpenGL and cocos2d and I need an advice.
I'm writing a game in which player is allowed to touch and move rectangles on the screen in a top-down view. Every time a rectangle is touched, it moves up (towards the screen) in z direction and is scaled a bit to look like it's closer than the rest. It drops down to z = 0 after touch ends.
I'd like the risen rectangles to drop shadow under them, but can't get it to work. What approach would you recommend for the best result?
Here's what I have so far.
During setup I turn on the depth buffer and then:
1. all the textures are generated with CCRenderTexture
2. the generated textures are used as an atlas to create CCSpriteBatchNode
3. when a rectangle (tile) is touched:
static const float _raisedScale = 1.2;
static const float _raisedVertexZ = 30;
...
-(void)makeRaised
{
_state = TileStateRaised;
self.scale = _raisedScale;
self.scale = _raisedScale;
self.vertexZ = _raisedVertexZ;
_glowOverlay.vertexZ = _raisedVertexZ;
_glowOverlay.opacity = 255;
}
glow overlay is used to "light up" the rectangle.
After that I animate it using -(void)update:(ccTime)delta
Is there a way to make OpenGl cast the shadow for me using cocos? For example using shaders or OpenGL shadowing. Or do I have to use a texture overlay to simulate the shadow?
What do you recommend? How would you do it?
Sorry for a newbie question, but it's all really new to me and I really need your help
.
EDIT 6th of March
I managed to get sprites with shadow overlay show under the tiles and it looks ok until one tile has to drop shadow on another which has a non-zero vertexZ value. I tried to create additional shadow sprites which would be scaled and shown on top of the other tiles (usually rising or falling down), but I have problems with animation (tile up, tile down).
Why complicate the problem.
Simply create a projections of how the shadow would look like using your favourite graphics editing program and save it as a png. When the object is lifted, insert your shadowSprite behind your lifted object (you can shift it left/right depending on where you think your light source is).
When the user drops the object down, the show can remain under the object and move with it, making it self visible when the item is lifted again.

Why doesn't CGContextShowGlyphsAtPositions() work, when CGContextShowGlyphsAtPoint() does work?

I have written a simple Cocoa app for Mac OS X (10.7) using Xcode 4.2. All the app does is create a window with a scrollable array of sub-Views in it, each representing a page to draw stuff on at a very low level. The sub-View's isFlipped method delivers YES, so the origin of every sub-View is the upper left corner. Using various Core Graphics routines, I'm able to draw lines and fill paths and all that fun PostScripty stuff successfully.
It's drawing glyphs from a given font that's got me confused.
Here's the complete code, cut-n-pasted from the program, for the sub-View's -drawRect: method --
- (void)drawRect:(NSRect)dirtyRect
{
// Start with background color for any part of this view
[[NSColor whiteColor] set];
NSRectFill( dirtyRect );
// Drop down to Core Graphics world, ensuring there's no side-effects
context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(context);
{
//CGFontRef theFont = CGFontCreateWithFontName(CFSTR("American Typewriter"));
//CGContextSetFont(context, theFont);
CGContextSelectFont(context, "American Typewriter", 200, kCGEncodingMacRoman);
CGContextSetFontSize(context, 200);
// Adjust the text transform so the text doesn't draw upside down
CGContextSetTextMatrix(context, CGAffineTransformScale(CGAffineTransformIdentity, 1, -1));
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
CGContextSetRGBFillColor(context, 0.0, .3, 0.8, 1.0);
// Find the center of view's (not dirtyRect's) bounds
// View is 612 x 792 (nominally 8.5" by 11")
CGPoint centerPoint;
CGRect bds = [self bounds];
centerPoint.x = bds.origin.x + bds.size.width / 2;
centerPoint.y = bds.origin.y + bds.size.height / 2;
// Create arrays to hold glyph IDs and the positions at which to draw them.
#define glyphCount 1 // For now, just one glyph
CGGlyph glyphs[glyphCount];
CGPoint positions[glyphCount];
glyphs[0] = 40; // Glyph ID for '#' character in above font
positions[0] = centerPoint;
// Draw above center. This works.
CGContextShowGlyphsAtPoint(context, centerPoint.x, centerPoint.y - 200.0, glyphs, glyphCount);
// Draw at center. This works.
CGContextShowGlyphsAtPoint(context, positions[0].x, positions[0].y, glyphs, glyphCount);
// Draw below center. This fails (draws nothing). Why?
positions[0].y += 200.0;
CGContextShowGlyphsAtPositions(context, glyphs, positions, glyphCount);
}
CGContextRestoreGState(context);
}
What's got me pulling my hair out is that the first two glyph-drawing calls using CGContextShowGlyphsAtPoint() work fine as expected, but the third attempt using CGContextShowGlyphsAtPositions() never draws anything. So there are only two # symbols on the page, rather than three. This difference in behaviors doesn't depend on whether I've previously used CGContextSetFont() or CGContextSelectFont().
There must be some hidden change in state going on, or something very different under the hood w/r/t these two almost identical Core Graphics glyph-drawing routines, but all my experiments so far have not demonstrated what that might be.
Sigh. I just want to efficiently draw an array of glyphs at a corresponding array of positions in a view.
Any ideas what I'm getting wrong?
After much experimentation enabled by being whacked upside the head by Peter Hosey's response (even though some of it isn't quite right, many thanks!), here's the source of my confusion and an explanation I'm pretty sure is correct (well, the code is doing what I expect it to, anyway).
In the usual higher-level PostScript path/drawing model, drawing a character updates the current point (path end) to the position where a next character might appear, leaving the current user-space transform the same. But under the hood, the text matrix transform is translated by the glyph's width (or more accurately by an advance vector) so that the next character to be drawn can start at, or with respect to, a new text origin. The text matrix's scale factors remain unchanged after translation.
So the initial setup call to CGContextSetTextMatrix() to flip the vertical sense of the text matrix is still necessary (if user-space is similarly flipped), because otherwise both glyph-collection drawing routines will draw the glyphs upside-down w/r/t path drawing, no matter where the text drawing starts or which drawing routine is used.
Neither of the two glyph collection drawing routines affects the current path. They are lower-level than that. I found that I could intersperse either routine among path construction calls without affecting a path's position or shape.
In the code posted above, the position data that CGContextShowGlyphsAtPositions() uses to draw the glyph collection are all relative to the user-space point corresponding to the current text matrix's origin, which was translated to the right of the previously drawn '#' glyph. Because I was using such a large font size, position[0] was causing the next '#' glyph to be drawn outside the view's bounds, so it wasn't visible, but it was being drawn.
But there's still some nuances among the two routines. CGContextShowGlyphsAtPositions() can never be used to place glyphs at any absolute user-space position. So how do you tell it where to start? The answer (or at least one answer) is that CGContextShowGlyphsAtPoint() updates the origin of the text matrix to the given user-space point even if there are no glyphs to draw. And CGContextShowGlyphsAtPoint() must translate the text matrix after each glyph it draws, because what would be the point (so to speak) of drawing the entire glyph collection on top of one another.
So one can "move" to a non-path point in user-space using CGContextShowGlyphsAtPoint() with a glyph count of 0, and then one can call CGContextShowGlyphsAtPositions() (any number of times) with a vector of positions each of which will be treated relative to the text matrix's origin (or really, the user-space point corresponding to it) without the text matrix origin being updated at all when CGContextShowGlyphsAtPositions() returns.
Finally, note that the position data provided to CGContextShowGlyphsAtPositions() is in user-space coordinates. A comment in Apple's header file for these routines expressly says so.
One possibility is this, from the CGContextShowGlyphsAtPositions document:
The position of each glyph is specified in text space, and, as a consequence, is transformed through the text matrix to user space.
The text matrix is a separate property of the context, distinct from the graphics state's current transformation matrix.
It doesn't say that about CGContextShowGlyphsAtPoint:
This function displays an array of glyphs at the specified position in the user space.
(Emphasis added to both quotes.)
So, your text matrix is not actually used when you show glyphs from a single point.
But then, when you show glyphs at an array of positions, it is used, and you see the symptom of a wrong matrix. Specifically, your matrix to try to flip the text back the other way is wrong: it flips the coordinate system upside down. You are drawing outside of the view.
(Try setting it to scale by 0.5 instead of -1 and you'll see what I mean.)
My recommendation is to take out your CGContextSetTextMatrix call.

About cutting of / cropping a uiview

so i have a uiview that is initialized with a frame that has the height and width that is present for the user, i want the user to be able to draw inside this frame but when the user presses a button, i want the view to cut off that extra wasted space so that the frame is only as big as what the user was drawing. I tried to do something like this
CGRect boundbox = CGPathGetBoundingBox([myPath CGPath]);
boundbox.origin.x = self.frame.origin.x;
boundbox.origin.y = self.frame.origin.y;
self.frame = boundbox;
However, this does not remove that extra wasted space, it only resizes the view, so that the drawn content looks smaller than previously. What i would like to do instead is to remove
that "whitespace", i was thinking if it could be possible to scale up the content of the uiview, but im not sure.
To clarify what i mean:
The red border is the area / frame that the user can draw on, the text in the middle is a drawing, when the user presses a button, i want the frame to only encircle the drawing like in figure 2.
Now lets say i have the following scenario, i have drawn a circle on the middle of the screen.
When i then press the button, the scale remains the same but the circle is still in the same position but we have now changed the draw area, so the circle / drawing will look like its cut off like in figure 4.
What i want to do is to move the drawing / bezier path so that it is positioned in the middle of the frame. So that the red area encircles the blue circle.
[EDIT]
Given your drawings. A UIView will not re-position items in it when you change it's frame property (or it's CGRect). In this case you will need to track the items drawn YOURSELF, and then when the button is pressed perform the object translations yourself.
What that means is you will have to find the object that is left most, the object that is topmost, then move all objects left by that amount, and up by that amount so that all objects are (as a grouping) top-left aligned within the view's frame. After this you will need to self recognize which object is the right most touching and which object is the bottom most touching.
NOW, since you have already moved the items left-top, the right most point will define your frame width, and the bottom most point will define your frame height.
IF YOU SO DESIRE, you should be able to zoom in using the properties below after you have done this.
[First Answer]
If I understand your question correctly, you may want to still perform your box frame manipulation, but if you wish to scale you may want to look into the
contentScaleFactor or
contentStretch
properties.
contentScaleFactor should scale both dimensions based upon a singular floating point value (i.e. xWidth * scaleFactor, yHeight * scale factor).
contentStretch is a CGRect which means that it should scale each dimension (axis) separately.

Resources