diff --git a/desmume/src/cocoa/DefaultUserPrefs.plist b/desmume/src/cocoa/DefaultUserPrefs.plist index 072fcb44e..2fae988a2 100644 --- a/desmume/src/cocoa/DefaultUserPrefs.plist +++ b/desmume/src/cocoa/DefaultUserPrefs.plist @@ -14,6 +14,8 @@ 1 DisplayView_Deposterize + DisplayView_FiltersPreferGPU + DisplayView_Mode 2 DisplayView_OutputFilter diff --git a/desmume/src/cocoa/cocoa_core.h b/desmume/src/cocoa/cocoa_core.h index dc6b0e8ef..ef1fc1803 100644 --- a/desmume/src/cocoa/cocoa_core.h +++ b/desmume/src/cocoa/cocoa_core.h @@ -43,6 +43,7 @@ typedef struct pthread_mutex_t mutexOutputList; pthread_mutex_t mutexThreadExecute; pthread_cond_t condThreadExecute; + pthread_rwlock_t rwCoreExecute; } CoreThreadParam; @interface CocoaDSCore : NSObject @@ -122,6 +123,7 @@ typedef struct @property (retain) NSURL *slot1R4URL; @property (readonly) pthread_mutex_t *mutexCoreExecute; +@property (readonly) pthread_rwlock_t *rwCoreExecute; - (BOOL) ejectCardFlag; - (void) setEjectCardFlag; diff --git a/desmume/src/cocoa/cocoa_core.mm b/desmume/src/cocoa/cocoa_core.mm index d12ef0404..b5e17ac84 100644 --- a/desmume/src/cocoa/cocoa_core.mm +++ b/desmume/src/cocoa/cocoa_core.mm @@ -76,6 +76,7 @@ volatile bool execute = true; @synthesize slot1R4URL; @dynamic mutexCoreExecute; +@dynamic rwCoreExecute; - (id)init { @@ -143,6 +144,7 @@ volatile bool execute = true; pthread_mutex_init(&threadParam.mutexOutputList, NULL); pthread_mutex_init(&threadParam.mutexThreadExecute, NULL); pthread_cond_init(&threadParam.condThreadExecute, NULL); + pthread_rwlock_init(&threadParam.rwCoreExecute, NULL); pthread_create(&coreThread, NULL, &RunCoreThread, &threadParam); [cdsGPU setMutexProducer:self.mutexCoreExecute]; @@ -176,6 +178,7 @@ volatile bool execute = true; pthread_cond_destroy(&threadParam.condThreadExecute); pthread_mutex_destroy(&threadParam.mutexOutputList); pthread_mutex_destroy(&threadParam.mutexCoreExecute); + pthread_rwlock_destroy(&threadParam.rwCoreExecute); NDS_DeInit(); @@ -596,6 +599,11 @@ volatile bool execute = true; return &threadParam.mutexCoreExecute; } +- (pthread_rwlock_t *) rwCoreExecute +{ + return &threadParam.rwCoreExecute; +} + - (void) setEjectCardFlag { if (nds.cardEjected) @@ -778,22 +786,23 @@ volatile bool execute = true; - (void) addOutput:(CocoaDSOutput *)theOutput { pthread_mutex_lock(&threadParam.mutexOutputList); - theOutput.mutexProducer = self.mutexCoreExecute; - [self.cdsOutputList addObject:theOutput]; + [theOutput setMutexProducer:[self mutexCoreExecute]]; + [theOutput setRwProducer:[self rwCoreExecute]]; + [[self cdsOutputList] addObject:theOutput]; pthread_mutex_unlock(&threadParam.mutexOutputList); } - (void) removeOutput:(CocoaDSOutput *)theOutput { pthread_mutex_lock(&threadParam.mutexOutputList); - [self.cdsOutputList removeObject:theOutput]; + [[self cdsOutputList] removeObject:theOutput]; pthread_mutex_unlock(&threadParam.mutexOutputList); } - (void) removeAllOutputs { pthread_mutex_lock(&threadParam.mutexOutputList); - [self.cdsOutputList removeAllObjects]; + [[self cdsOutputList] removeAllObjects]; pthread_mutex_unlock(&threadParam.mutexOutputList); } @@ -947,7 +956,11 @@ static void* RunCoreThread(void *arg) // Execute the frame and increment the frame counter. pthread_mutex_lock(¶m->mutexCoreExecute); + + pthread_rwlock_wrlock(¶m->rwCoreExecute); NDS_exec(); + pthread_rwlock_unlock(¶m->rwCoreExecute); + frameNum = currFrameCounter; pthread_mutex_unlock(¶m->mutexCoreExecute); @@ -984,11 +997,13 @@ static void* RunCoreThread(void *arg) } case CORESTATE_FRAMEADVANCE: + { for(CocoaDSOutput *cdsOutput in cdsOutputList) { [cdsOutput doCoreEmuFrame]; } break; + } case CORESTATE_FRAMEJUMP: { diff --git a/desmume/src/cocoa/cocoa_globals.h b/desmume/src/cocoa/cocoa_globals.h index b1c608425..0f303ee40 100644 --- a/desmume/src/cocoa/cocoa_globals.h +++ b/desmume/src/cocoa/cocoa_globals.h @@ -462,6 +462,12 @@ enum DS_DISPLAY_TYPE_DUAL }; +enum +{ + VIDEO_SOURCE_INTERNAL = 0, + VIDEO_SOURCE_EMULATOR = 1 +}; + enum { DS_DISPLAY_ORIENTATION_VERTICAL = 0, diff --git a/desmume/src/cocoa/cocoa_output.h b/desmume/src/cocoa/cocoa_output.h index b47c27814..5e793d009 100644 --- a/desmume/src/cocoa/cocoa_output.h +++ b/desmume/src/cocoa/cocoa_output.h @@ -36,9 +36,10 @@ typedef struct typedef struct { - NSInteger displayModeID; - size_t width; // Measured in pixels - size_t height; // Measured in pixels + int32_t videoSourceID; + int32_t displayModeID; + uint16_t width; // Measured in pixels + uint16_t height; // Measured in pixels } DisplaySrcPixelAttributes; @interface CocoaDSOutput : CocoaDSThread @@ -51,6 +52,7 @@ typedef struct pthread_mutex_t *mutexProducer; pthread_mutex_t *mutexConsume; + pthread_rwlock_t *rwProducer; } @property (assign) BOOL isStateChanged; @@ -59,6 +61,7 @@ typedef struct @property (retain) NSData *frameAttributesData; @property (readonly) NSMutableDictionary *property; @property (assign) pthread_mutex_t *mutexProducer; +@property (assign) pthread_rwlock_t *rwProducer; @property (readonly) pthread_mutex_t *mutexConsume; - (void) doCoreEmuFrame; @@ -158,7 +161,8 @@ typedef struct - (void) handleRequestScreenshot:(NSData *)fileURLStringData fileTypeData:(NSData *)fileTypeData; - (void) handleCopyToPasteboard; -- (void) fillVideoFrameWithColor:(UInt16)colorValue; +- (NSData *) videoFrameUsingRGBA5551:(uint16_t)colorValue pixelCount:(size_t)pixCount; +- (void) sendVideoFrameOfRGBA5551:(uint16_t)colorValue; - (NSImage *) image; - (NSBitmapImageRep *) bitmapImageRep; diff --git a/desmume/src/cocoa/cocoa_output.mm b/desmume/src/cocoa/cocoa_output.mm index 60c1f83cb..9dfed76ff 100644 --- a/desmume/src/cocoa/cocoa_output.mm +++ b/desmume/src/cocoa/cocoa_output.mm @@ -40,6 +40,7 @@ @synthesize property; @synthesize mutexProducer; @synthesize mutexConsume; +@synthesize rwProducer; - (id)init { @@ -598,34 +599,24 @@ - (void) doCoreEmuFrame { - NSData *gpuData = nil; NSInteger displayModeID = [self displayMode]; NSSize displayFrameSize = [self frameSize]; - // Here, we copy the raw GPU data from the emulation core. - // - // The core data contains the GPU pixels from both the main and touch screens. So - // depending on the display type, we copy only the pixels from the respective screen. - if (displayModeID == DS_DISPLAY_TYPE_MAIN) - { - gpuData = [[NSData alloc] initWithBytes:GPU_screen length:GPU_SCREEN_SIZE_BYTES]; - } - else if(displayModeID == DS_DISPLAY_TYPE_TOUCH) - { - gpuData = [[NSData alloc] initWithBytes:(GPU_screen + GPU_SCREEN_SIZE_BYTES) length:GPU_SCREEN_SIZE_BYTES]; - } - else if(displayModeID == DS_DISPLAY_TYPE_DUAL) - { - gpuData = [[NSData alloc] initWithBytes:GPU_screen length:GPU_SCREEN_SIZE_BYTES * 2]; - } + // We will be ignoring the actual video data here since we will be pulling the video data + // from the consumer thread instead. + NSData *gpuData = [[NSData alloc] init]; + + DisplaySrcPixelAttributes attr; + attr.videoSourceID = VIDEO_SOURCE_EMULATOR; + attr.displayModeID = (int32_t)displayModeID; + attr.width = (uint16_t)displayFrameSize.width; + attr.height = (uint16_t)displayFrameSize.height; - DisplaySrcPixelAttributes attr = {displayModeID, (size_t)displayFrameSize.width, (size_t)displayFrameSize.height}; NSData *attributesData = [[NSData alloc] initWithBytes:&attr length:sizeof(DisplaySrcPixelAttributes)]; NSArray *messageComponents = [[NSArray alloc] initWithObjects:gpuData, attributesData, nil]; [CocoaDSUtil messageSendOneWayWithMessageComponents:self.receivePort msgID:MESSAGE_EMU_FRAME_PROCESSED array:messageComponents]; - // Now that we've finished sending the GPU data, release the local copy. [gpuData release]; [attributesData release]; [messageComponents release]; @@ -687,12 +678,12 @@ - (void) handleSetViewToBlack { - [self fillVideoFrameWithColor:0x8000]; + [self sendVideoFrameOfRGBA5551:0x8000]; } - (void) handleSetViewToWhite { - [self fillVideoFrameWithColor:0xFFFF]; + [self sendVideoFrameOfRGBA5551:0xFFFF]; } - (void) handleRequestScreenshot:(NSData *)fileURLStringData fileTypeData:(NSData *)fileTypeData @@ -726,30 +717,52 @@ [pboard setData:[screenshot TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1.0f] forType:NSTIFFPboardType]; } -- (void) fillVideoFrameWithColor:(UInt16)colorValue +- (NSData *) videoFrameUsingRGBA5551:(uint16_t)colorValue pixelCount:(size_t)pixCount { - const NSInteger displayModeID = [self displayMode]; - const NSSize displayFrameSize = [self frameSize]; - const size_t numberBytes = (displayModeID == DS_DISPLAY_TYPE_DUAL) ? GPU_SCREEN_SIZE_BYTES * 2 : GPU_SCREEN_SIZE_BYTES; - - UInt16 *gpuBytes = (UInt16 *)malloc(numberBytes); + NSData *gpuData = nil; + const size_t bufSize = pixCount * sizeof(uint16_t); + uint16_t *gpuBytes = (uint16_t *)malloc(bufSize); if (gpuBytes == NULL) { - return; + return gpuData; } - const UInt16 colorValuePattern[] = {colorValue, colorValue, colorValue, colorValue, colorValue, colorValue, colorValue, colorValue}; - memset_pattern16(gpuBytes, colorValuePattern, numberBytes); - NSData *gpuData = [[NSData alloc] initWithBytes:gpuBytes length:numberBytes]; + if (pixCount % 16 == 0) + { + const uint16_t colorValuePattern[] = {colorValue, colorValue, colorValue, colorValue, colorValue, colorValue, colorValue, colorValue}; + memset_pattern16(gpuBytes, colorValuePattern, bufSize); + } + else + { + memset(gpuBytes, colorValue, bufSize); + } + + + gpuData = [NSData dataWithBytes:gpuBytes length:bufSize]; free(gpuBytes); gpuBytes = nil; - DisplaySrcPixelAttributes attr = {displayModeID, (size_t)displayFrameSize.width, (size_t)displayFrameSize.height}; - NSData *attributesData = [[[NSData alloc] initWithBytes:&attr length:sizeof(DisplaySrcPixelAttributes)] autorelease]; + return gpuData; +} + +- (void) sendVideoFrameOfRGBA5551:(uint16_t)colorValue +{ + const NSInteger displayModeID = [self displayMode]; + const NSSize displayFrameSize = [self frameSize]; + const size_t pixCount = (displayModeID == DS_DISPLAY_TYPE_DUAL) ? GPU_DISPLAY_WIDTH * GPU_DISPLAY_HEIGHT * 2 : GPU_DISPLAY_WIDTH * GPU_DISPLAY_HEIGHT; - [self handleEmuFrameProcessed:gpuData attributes:attributesData]; - [gpuData release]; + NSData *videoData = [self videoFrameUsingRGBA5551:colorValue pixelCount:pixCount]; + + DisplaySrcPixelAttributes attr; + attr.videoSourceID = VIDEO_SOURCE_INTERNAL; + attr.displayModeID = (int32_t)displayModeID; + attr.width = (uint16_t)displayFrameSize.width; + attr.height = (uint16_t)displayFrameSize.height; + + NSData *attributesData = [NSData dataWithBytes:&attr length:sizeof(DisplaySrcPixelAttributes)]; + + [self handleEmuFrameProcessed:videoData attributes:attributesData]; } - (NSImage *) image @@ -899,13 +912,53 @@ return; } - const DisplaySrcPixelAttributes attr = *(DisplaySrcPixelAttributes *)[attributesData bytes]; + NSData *newVideoFrame = mainData; + DisplaySrcPixelAttributes attr = *(DisplaySrcPixelAttributes *)[attributesData bytes]; const NSInteger frameDisplayMode = attr.displayModeID; + const NSInteger frameSource = attr.videoSourceID; const NSInteger frameWidth = attr.width; const NSInteger frameHeight = attr.height; - [(id)delegate doProcessVideoFrame:[mainData bytes] displayMode:frameDisplayMode width:frameWidth height:frameHeight]; - [super handleEmuFrameProcessed:mainData attributes:attributesData]; + if (frameSource == VIDEO_SOURCE_EMULATOR) + { + // Note that we simply received the attributes of the video data, not the actual + // video data itself. So from here, we need to pull the video data from the + // emulator and copy it to this thread. + // + // The video data contains the pixels from both the main and touch screens. So + // depending on the display mode, we copy only the pixels from the respective + // screen. + + pthread_rwlock_rdlock([self rwProducer]); + + switch (frameDisplayMode) + { + case DS_DISPLAY_TYPE_MAIN: + newVideoFrame = [NSData dataWithBytes:GPU_screen length:GPU_SCREEN_SIZE_BYTES]; + break; + + case DS_DISPLAY_TYPE_TOUCH: + newVideoFrame = [NSData dataWithBytes:(GPU_screen + GPU_SCREEN_SIZE_BYTES) length:GPU_SCREEN_SIZE_BYTES]; + break; + + case DS_DISPLAY_TYPE_DUAL: + newVideoFrame = [NSData dataWithBytes:GPU_screen length:GPU_SCREEN_SIZE_BYTES*2]; + break; + + default: + break; + } + + pthread_rwlock_unlock([self rwProducer]); + } + + [(id)delegate doProcessVideoFrame:[newVideoFrame bytes] displayMode:frameDisplayMode width:frameWidth height:frameHeight]; + + // If we need to use our saved frame data, make sure that we don't pull from the + // emulation thread again. + attr.videoSourceID = VIDEO_SOURCE_INTERNAL; + NSData *savedAttributesData = [NSData dataWithBytes:&attr length:sizeof(DisplaySrcPixelAttributes)]; + [super handleEmuFrameProcessed:newVideoFrame attributes:savedAttributesData]; } - (void) handleResizeView:(NSData *)rectData