PDA

View Full Version : dispatch_async and class inheritance hierarchy




KnightWRX
Sep 15, 2012, 11:37 AM
I've made a class that inherits from UIView. I override the setNeedsDisplay method so that I can basically prepare a buffer that will get used by drawRect once its ready (since I don't want to hold up my main thread while doing the leg work). Using dispatch_queue_create() and dispatch_async(), I can get code to send off the buffer preparation to another thread while my app keeps doing other things.

The problem arises when then calling [super setNeedsDisplay]; so that my view's drawRect method gets called.

Basically, drawRect never gets called after the buffer is done with its rendering :

- (void) setNeedsDisplay
{
self.updateCount++;
dispatch_queue_t queue = dispatch_queue_create("ca.domain.async_draw", NULL);

dispatch_async(queue, ^{
// buffer preparation code here

[super setNeedsDisplay];
});
}

If I make the function not "async", basically write it as so, it works like a charm, except for jamming up the main thread :

- (void) setNeedsDisplay
{
self.updateCount++;

// buffer preparation code here

[super setNeedsDisplay];

}

Anyone have any insight or passages in the GCD documentation I should pay careful attention to ? I read it, but I don't really understand what I'm missing, it seems rather simple enough, maybe there's a caveat I'm not catching here...



ArtOfWarfare
Sep 15, 2012, 12:29 PM
Wouldn't setNeedsDisplay qualify as a UI related call that needs to be done on the main thread? (IDK, I'm only learning about threading on iOS for the first time right now.)

KnightWRX
Sep 15, 2012, 12:36 PM
Hum... seems it is. I thought it wasn't since it didn't do any drawing, just ended up calling drawRect. But encassing the call in the following fixes *that* issue :

dispatch_async(dispatch_get_main_queue() , ^{
[super setNeedsDisplay];
});


Of course, now I have a problem with the buffer being accessed by 2 threads at the same time... bummer (shouldn't be too hard to fix).

chown33
Sep 15, 2012, 01:07 PM
I'm just guessing, because you haven't described the architecture here, but this seems like it might be unnecessary, and suggests a poor or malfunctioning MVC design.

Exactly what is being done that takes a long time? Whatever it is, maybe it should be moved into the Model class. Then the code that triggers a redisplay sends a message only to the Model object, which performs the time-consuming operation, and it (the Model object) then tells its associated View object to redisplay. The View, which has a reference to the Model object, then asks for the data from the Model that it then displays when its drawRect: is called. The Model should already have the data available for quick access, since it knows what the View will request from it, and is the only object with responsibility for calling setNeedsDisplay.

This also seems like it might be an XY problem:
http://www.perlmonks.org/index.pl?node_id=542341
.. You want to do X, and you think Y is the best way of doing it.
.. Instead of asking about X, you ask about Y.


Short version: It seems like some Model functionality is being placed in the View object, resulting in role confusion.

KnightWRX
Sep 15, 2012, 01:22 PM
No, it's a problem of me not implementing partial screen refreshes and not doing hidden surface removal, not to mention not pre-blending pixels, so not being able to batch up copy operations, but having to do it 4 bytes at a time, which takes about 20 ms per frame all in all, slowing down the frame rate considerably.

With async rendering, I'll quickly implement hard frame drops and move on and came back to tackle more efficient rendering later on. Everything that could be moved to the model already has, only thing left is the final merging of the different layers into a single buffer ready to drop into the current context of the view.

So thanks for maybe wanting to help with the bigger architecture here, but I know the architecture is currently deficient. This was a quick band-aid I needed help with to be able to do my on device testing.