Permalink
Browse files

Texture & TextureCache: uses current thread and cocos2d thread

Texture2D: uses current thread for glDeleteTexture()
TextureCache: uses cocos2d thread to invoke async callback()

These bugs were causing kernel panics on Mac
  • Loading branch information...
1 parent d92cdff commit b6c6cb4a0f08e159688eae3e3ad731a9a71aa723 @ricardoquesada ricardoquesada committed Feb 25, 2012
View
@@ -57,6 +57,8 @@ version 2.0-rc0 XX-Feb-2012
. [FIX] Texture2D: initWithImage renamed with initWithCGImage
JPEG: No need to convert JPG to PNG on iOS4 (issue #886 re-fixed)
BMP: Images are displayed on iOS4 like on iOS5 (without alpha channel) [iOS5 BUG]
+ 'Delete texture' is executed on current thread (not on main thread)
+. [FIX] TextureCache: async invokes callback on cocos2d thread (and not on main thread)
. [FIX] Types: kCCResolutionStandard renamed to kCCResolutioniPhone
version 2.0-beta2 17-Jan-2012
@@ -1746,6 +1746,8 @@
A0C20AC9144FDAF700D84B47 /* CCParticleBatchNode.m in Sources */ = {isa = PBXBuildFile; fileRef = A0C20AC7144FDAF700D84B47 /* CCParticleBatchNode.m */; };
A0C3815114BA0548001655CC /* CCTransitionProgress.h in Headers */ = {isa = PBXBuildFile; fileRef = A0C3814F14BA0548001655CC /* CCTransitionProgress.h */; };
A0C3815214BA0548001655CC /* CCTransitionProgress.m in Sources */ = {isa = PBXBuildFile; fileRef = A0C3815014BA0548001655CC /* CCTransitionProgress.m */; };
+ A0C87D1A14F9A3A100C0E8B2 /* NSThread+performBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = A0C87D1814F9A3A100C0E8B2 /* NSThread+performBlock.h */; };
+ A0C87D1B14F9A3A100C0E8B2 /* NSThread+performBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = A0C87D1914F9A3A100C0E8B2 /* NSThread+performBlock.m */; };
A0D3A8FF149D2FAE0077CD93 /* BaseAppController.m in Sources */ = {isa = PBXBuildFile; fileRef = A0D3A8FE149D2FA40077CD93 /* BaseAppController.m */; };
A0D3A900149D2FAE0077CD93 /* BaseAppController.m in Sources */ = {isa = PBXBuildFile; fileRef = A0D3A8FE149D2FA40077CD93 /* BaseAppController.m */; };
A0D3A901149D2FAE0077CD93 /* BaseAppController.m in Sources */ = {isa = PBXBuildFile; fileRef = A0D3A8FE149D2FA40077CD93 /* BaseAppController.m */; };
@@ -4905,6 +4907,8 @@
A0C20AC7144FDAF700D84B47 /* CCParticleBatchNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleBatchNode.m; sourceTree = "<group>"; };
A0C3814F14BA0548001655CC /* CCTransitionProgress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTransitionProgress.h; sourceTree = "<group>"; };
A0C3815014BA0548001655CC /* CCTransitionProgress.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransitionProgress.m; sourceTree = "<group>"; };
+ A0C87D1814F9A3A100C0E8B2 /* NSThread+performBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSThread+performBlock.h"; sourceTree = "<group>"; };
+ A0C87D1914F9A3A100C0E8B2 /* NSThread+performBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSThread+performBlock.m"; sourceTree = "<group>"; };
A0D3A8FD149D2F9D0077CD93 /* BaseAppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BaseAppController.h; path = tests/BaseAppController.h; sourceTree = "<group>"; };
A0D3A8FE149D2FA40077CD93 /* BaseAppController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BaseAppController.m; path = tests/BaseAppController.m; sourceTree = "<group>"; };
A0D77A0E146E3244000960C7 /* markerFelt-hd.fnt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "markerFelt-hd.fnt"; sourceTree = "<group>"; };
@@ -6163,6 +6167,8 @@
50F29F5510204FD60046CA73 /* base64.c */,
50F2A103102094550046CA73 /* ZipUtils.h */,
50F2A102102094550046CA73 /* ZipUtils.m */,
+ A0C87D1814F9A3A100C0E8B2 /* NSThread+performBlock.h */,
+ A0C87D1914F9A3A100C0E8B2 /* NSThread+performBlock.m */,
);
path = Support;
sourceTree = "<group>";
@@ -7838,6 +7844,7 @@
A046E29914C1DB7E0005BBF2 /* CCWindow.h in Headers */,
A09AB76914EA12F7009C8B91 /* ccDeprecated.h in Headers */,
A0A10B5714F3011F00FA38C1 /* ccGLStateCache.h in Headers */,
+ A0C87D1A14F9A3A100C0E8B2 /* NSThread+performBlock.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -12046,6 +12053,7 @@
A046E29A14C1DB7E0005BBF2 /* CCWindow.m in Sources */,
A09AB76A14EA12F7009C8B91 /* ccDeprecated.m in Sources */,
A0A10B5814F3011F00FA38C1 /* ccGLStateCache.m in Sources */,
+ A0C87D1B14F9A3A100C0E8B2 /* NSThread+performBlock.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1984,6 +1984,7 @@
A0C491EC146F8CA500C4C037 /* arrows.png in Resources */ = {isa = PBXBuildFile; fileRef = A0C491E8146F8CA500C4C037 /* arrows.png */; };
A0C491ED146F8CA500C4C037 /* arrowsBar-hd.png in Resources */ = {isa = PBXBuildFile; fileRef = A0C491E9146F8CA500C4C037 /* arrowsBar-hd.png */; };
A0C491EE146F8CA500C4C037 /* arrowsBar.png in Resources */ = {isa = PBXBuildFile; fileRef = A0C491EA146F8CA500C4C037 /* arrowsBar.png */; };
+ A0C879AD14F98F1C00C0E8B2 /* NSThread+performBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = A0C879AC14F98F1C00C0E8B2 /* NSThread+performBlock.m */; };
A0DC2F611491B3FA005B814E /* test_image_rgb888.pvr in Resources */ = {isa = PBXBuildFile; fileRef = A0DC2F601491B3FA005B814E /* test_image_rgb888.pvr */; };
A0E95100142698D2009F9607 /* grossini_pvr_rgba8888.pvr in Resources */ = {isa = PBXBuildFile; fileRef = A0E950FF142698D2009F9607 /* grossini_pvr_rgba8888.pvr */; };
A0EC4D801403278300EC482A /* Position_uColor.fsh in Resources */ = {isa = PBXBuildFile; fileRef = A0EC4CAF1403278200EC482A /* Position_uColor.fsh */; };
@@ -5905,6 +5906,8 @@
A0C491E8146F8CA500C4C037 /* arrows.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = arrows.png; sourceTree = "<group>"; };
A0C491E9146F8CA500C4C037 /* arrowsBar-hd.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "arrowsBar-hd.png"; sourceTree = "<group>"; };
A0C491EA146F8CA500C4C037 /* arrowsBar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = arrowsBar.png; sourceTree = "<group>"; };
+ A0C879AB14F98F0200C0E8B2 /* NSThread+performBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSThread+performBlock.h"; sourceTree = "<group>"; };
+ A0C879AC14F98F1C00C0E8B2 /* NSThread+performBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSThread+performBlock.m"; sourceTree = "<group>"; };
A0DC2F601491B3FA005B814E /* test_image_rgb888.pvr */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_image_rgb888.pvr; sourceTree = "<group>"; };
A0E55417141EAEEE003F1E5A /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
A0E950FF142698D2009F9607 /* grossini_pvr_rgba8888.pvr */ = {isa = PBXFileReference; lastKnownFileType = file; path = grossini_pvr_rgba8888.pvr; sourceTree = "<group>"; };
@@ -7464,6 +7467,8 @@
E076E6CC1225EC7400DE0DA2 /* utlist.h */,
E076E6CD1225EC7400DE0DA2 /* ZipUtils.h */,
E076E6CE1225EC7400DE0DA2 /* ZipUtils.m */,
+ A0C879AB14F98F0200C0E8B2 /* NSThread+performBlock.h */,
+ A0C879AC14F98F1C00C0E8B2 /* NSThread+performBlock.m */,
);
path = Support;
sourceTree = "<group>";
@@ -13829,6 +13834,7 @@
A046E2BD14C1E5000005BBF2 /* CCTouchHandler.m in Sources */,
A046E2C114C1E5480005BBF2 /* CCGLProgram.m in Sources */,
A0A10B2114F3003600FA38C1 /* ccDeprecated.m in Sources */,
+ A0C879AD14F98F1C00C0E8B2 /* NSThread+performBlock.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -14791,6 +14797,10 @@
);
MACOSX_DEPLOYMENT_TARGET = 10.6;
ONLY_ACTIVE_ARCH = YES;
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-all_load",
+ );
SDKROOT = macosx;
};
name = Debug;
@@ -14818,6 +14828,10 @@
external/kazmath/include,
);
MACOSX_DEPLOYMENT_TARGET = 10.6;
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-all_load",
+ );
SDKROOT = macosx;
};
name = Release;
View
@@ -80,6 +80,7 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
#import "CCGLProgram.h"
#import "ccGLStateCache.h"
#import "CCShaderCache.h"
+#import "CCDirector.h"
#import "Support/ccUtils.h"
#import "Support/CCFileUtils.h"
@@ -188,12 +189,9 @@ - (void) dealloc
if( name_ ) {
- GLuint name = name_;
+ NSAssert( [[CCDirector sharedDirector] runningThread] == [NSThread currentThread], @"cocos2d: Shall not happen. Please report this bug on the cocos2d issue tracker" );
- //It is very likely dealloc will get called from the texture cache's dictionary thread but this must be run from the cocos2d thread
- dispatch_async(dispatch_get_main_queue(), ^(void) {
- ccGLDeleteTexture( name );
- });
+ ccGLDeleteTexture( name_ );
}
[super dealloc];
View
@@ -30,11 +30,14 @@
#import "CCTexture2D.h"
#import "CCTexturePVR.h"
#import "CCConfiguration.h"
-#import "Support/CCFileUtils.h"
#import "CCDirector.h"
#import "ccConfig.h"
#import "ccTypes.h"
+#import "Support/CCFileUtils.h"
+#import "Support/NSThread+performBlock.h"
+
+
#ifdef __CC_PLATFORM_MAC
#import "Platforms/Mac/CCDirectorMac.h"
#endif
@@ -175,9 +178,7 @@ -(void) addImageAsync: (NSString*)path target:(id)target selector:(SEL)selector
glFlush();
// callback should be executed in cocos2d thread
- dispatch_async(dispatch_get_main_queue(), ^{
- [target performSelector:selector withObject:texture];
- });
+ [target performSelector:selector onThread:[[CCDirector sharedDirector] runningThread] withObject:texture waitUntilDone:NO];
[EAGLContext setCurrentContext:nil];
} else {
@@ -194,9 +195,7 @@ -(void) addImageAsync: (NSString*)path target:(id)target selector:(SEL)selector
glFlush();
// callback should be executed in cocos2d thread
- dispatch_async(dispatch_get_main_queue(), ^{
- [target performSelector:selector withObject:texture];
- });
+ [target performSelector:selector onThread:[[CCDirector sharedDirector] runningThread] withObject:texture waitUntilDone:NO];
[NSOpenGLContext clearCurrentContext];
@@ -240,9 +239,8 @@ -(void) addImageAsync:(NSString*)path withBlock:(void(^)(CCTexture2D *tex))block
glFlush();
// callback should be executed in cocos2d thread
- dispatch_async(dispatch_get_main_queue(), ^{
- block(texture);
- });
+ NSThread *thread = [[CCDirector sharedDirector] runningThread];
+ [thread performBlock:block withObject:texture waitUntilDone:NO];
[EAGLContext setCurrentContext:nil];
} else {
@@ -259,9 +257,8 @@ -(void) addImageAsync:(NSString*)path withBlock:(void(^)(CCTexture2D *tex))block
glFlush();
// callback should be executed in cocos2d thread
- dispatch_async(dispatch_get_main_queue(), ^{
- block(texture);
- });
+ NSThread *thread = [[CCDirector sharedDirector] runningThread];
+ [thread performBlock:block withObject:texture waitUntilDone:NO];
[NSOpenGLContext clearCurrentContext];
@@ -296,35 +293,6 @@ -(CCTexture2D*) addImage: (NSString*) path
#ifdef __CC_PLATFORM_IOS
- // Issue #886: TEMPORARY FIX FOR TRANSPARENT JPEGS IN IOS4
-// else if ( ([[CCConfiguration sharedConfiguration] OSVersion] < kCCiOSVersion_5_0) &&
-// ( [lowerCase hasSuffix:@".jpg"] || [lowerCase hasSuffix:@".jpeg"] )
-// ) {
-// // convert jpg to png before loading the texture
-//
-// CCLOG(@"cocos2d: WARNING: Loading JPEG image. For faster loading times, convert it to PVR or PNG. See issue #886");
-//
-// ccResolutionType resolution;
-// NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path resolutionType:&resolution];
-//
-// UIImage *jpg = [[UIImage alloc] initWithContentsOfFile:fullpath];
-// UIImage *png = [[UIImage alloc] initWithData:UIImagePNGRepresentation(jpg)];
-// tex = [ [CCTexture2D alloc] initWithCGImage:png.CGImage resolutionType:resolution];
-// [png release];
-// [jpg release];
-//
-// if( tex ){
-// dispatch_sync(_dictQueue, ^{
-// [textures_ setObject: tex forKey:path];
-// });
-// }else{
-// CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
-// }
-//
-// // autorelease prevents possible crash in multithreaded environments
-// [tex autorelease];
-// }
-
else {
ccResolutionType resolution;
@@ -0,0 +1,22 @@
+/* cocos2d for iPhone
+ *
+ * http://www.cocos2d-iphone.org
+ *
+ *
+ * Idea taken from: http://stackoverflow.com/a/3940757
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface NSThread (sendBlockToBackground)
+/** performs a block on the thread. It won't wait until it is done. */
+- (void) performBlock:(void (^)(void))block;
+
+/** performs a block on the thread. */
+- (void) performBlock:(void (^)(void))block waitUntilDone:(BOOL)wait;
+
+/** performs a block on the thread. */
+- (void) performBlock:(void (^)(id param))block withObject:(id)object waitUntilDone:(BOOL)wait;
+
+@end
@@ -0,0 +1,76 @@
+/* cocos2d for iPhone
+ *
+ * http://www.cocos2d-iphone.org
+ *
+ *
+ * Idea taken from: http://stackoverflow.com/a/3940757
+ *
+ */
+
+
+#import "NSThread+performBlock.h"
+#import "../ccMacros.h"
+
+typedef void (^BlockWithParam)(id param);
+@interface CCObjectWith2Params : NSObject
+{
+@public
+ BlockWithParam block;
+ id param;
+}
+@property (nonatomic,copy) id block;
+@property (nonatomic,readwrite,retain) id param;
+@end
+
+@implementation CCObjectWith2Params
+@synthesize block, param;
+- (void)dealloc {
+ CCLOG(@"cocos2d: deallocing %@", self);
+ [block release];
+ [param release];
+
+ [super dealloc];
+}
+@end
+
+@implementation NSThread (sendBlockToBackground)
+
+- (void) performBlock: (void (^)(void))block;
+{
+ return [self performBlock:block waitUntilDone:NO];
+}
+
+- (void) performBlock:(void (^)(void))block waitUntilDone:(BOOL)wait
+{
+ [self performSelector:@selector(executeBlock:)
+ onThread:self
+ withObject: block
+ waitUntilDone: wait];
+}
+
+- (void) performBlock:(void (^)(id param))block withObject:(id)object waitUntilDone:(BOOL)wait
+{
+ CCObjectWith2Params * obj = [[CCObjectWith2Params alloc] init];
+ obj.block = block;
+ obj.param = object;
+
+ [obj autorelease];
+
+ [self performSelector:@selector(executeBlock2:)
+ onThread:self
+ withObject:obj
+ waitUntilDone:wait];
+}
+
+- (void) executeBlock: (void (^)(void))block;
+{
+ block();
+}
+
+- (void) executeBlock2:(CCObjectWith2Params*)object
+{
+ BlockWithParam block = object.block;
+ block( object.param );
+}
+
+@end
View
@@ -19,6 +19,9 @@
static int sceneIdx=-1;
static NSString *transitions[] = {
+ @"TextureAsync",
+ @"TextureAsyncBlock",
+
@"TextureAlias",
@"TextureMipMap",
@"TexturePVRMipMap",
@@ -1224,6 +1227,10 @@ -(void) loadImages:(ccTime) dt
-(void) imageLoaded: (CCTexture2D*) tex
{
+ CCDirector *director = [CCDirector sharedDirector];
+
+ NSAssert( [NSThread currentThread] == [director runningThread], @"FAIL. Callback should be on cocos2d thread");
+
// IMPORTANT: The order on the callback is not guaranteed. Don't depend on the callback
// This test just creates a sprite based on the Texture
@@ -1232,8 +1239,7 @@ -(void) imageLoaded: (CCTexture2D*) tex
sprite.anchorPoint = ccp(0,0);
[self addChild:sprite z:-1];
- CGSize size =[[CCDirector sharedDirector] winSize];
-
+ CGSize size = [director winSize];
int i = imageOffset * 32;
sprite.position = ccp( i % (int)size.width, (i / (int)size.width) * 32 );
@@ -1294,13 +1300,17 @@ -(void) loadImages:(ccTime) dt
void(^block)(CCTexture2D *block) = ^(CCTexture2D* tex){
+ CCDirector *director = [CCDirector sharedDirector];
+
+ NSAssert( [NSThread currentThread] == [director runningThread], @"FAIL. Callback should be on cocos2d thread");
+
CCSprite *sprite = [CCSprite spriteWithTexture:tex];
sprite.anchorPoint = ccp(0,0);
[self addChild:sprite z:-1];
- CGSize size =[[CCDirector sharedDirector] winSize];
int i = imageOffset * 32;
+ CGSize size = [director winSize];
sprite.position = ccp( i % (int)size.width, (i / (int)size.width) * 32 );
imageOffset++;

0 comments on commit b6c6cb4

Please sign in to comment.