OS 10.11 El Capitan 中的 NSView 投掷错误

NSView in OS 10.11 El Capitan Throwing Errors

我有一个 OpenGL 应用程序,我在 2011 MacBookPro 上 运行 Xcode 7.0.1。

我最近从 OS 10.10 升级到 10.11 El Capitan,现在 windows 使用 NSViews 时抛出此错误(部署目标 10.11、10.10 或 10.9):

invalid context 0x0. If you want to see the backtrace, please set     
CG_CONTEXT_SHOW_BACKTRACE environmental variable.

在 NSView drawRect 方法中,我使用以下语句获取 CGContext:

CGContextRef context = [[NSGraphicsContext currentContext] CGContext];

此外,我的 GLSL 4.1 着色器不再向 OpenGL window 写入任何内容。

在我升级​​到 El Capitan 之前,这段代码没有给我带来任何问题,而且(几乎)完全相同的代码在 2012 MacBookPro、OS 10.10、Xcode 6.4 上运行良好且没有错误, 部署目标 10.9 或 10.10。唯一的代码差异是 NSView drawRect 方法中的图形上下文是通过以下方式获得的:

CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];

我想我的问题可能是出现问题的电脑是2011款的MacBookPro,而Metal需要2012款或更新的。我没有使用 Metal,但我仍然认为这可能是一个因素。

是计算机老式问题导致了错误,还是我应该使用其他一些语法来获取图形上下文?

我不知道为什么 GLSL 着色器不再工作了。有什么想法吗?

应用程序主要 window 是一个 OpenGL 视图,但我在弹出用户界面 windows、自定义按钮和各种其他用途中使用了很多 NSView。下面是其中最简单的代码之一的完整代码 windows。同样,在我升级到 EC 和 Xcode 7.0.1.

之前,这一切 运行 都很好
@implementation StatusView

// **************************** Init **********************************

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {

    }
    return self;
}

// ************************** Draw Rect ********************************

- (void)drawRect:(NSRect)dirtyRect
{
    if(windowManager)
    {
        CGContextRef context = [[NSGraphicsContext currentContext] CGContext];

        // Define a color space variable
        CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB();            

        // Get the bounds
        NSRect nsBounds = [self bounds];

        // Set the graphics bounds
        CGRect cgBounds = NSRectToCGRect(nsBounds);                               

        // ****** Draw the Background in Transparent Black

        CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 0.0f);
        CGContextFillRect(context, cgBounds);

        // Set the text matrix.
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);

        // Create a color that will be added as an attribute to the attrString for normal text.
        CGFloat textColorComponents[] = { 1.0, 1.0, 1.0, 1.0 };
        CGColorRef whiteColor = CGColorCreate(rgbColorspace, textColorComponents);

        // Create a color that will be added as an attribute to the attrString for invisible text.
        CGFloat invisibleTextColorComponents[] = { 0.0, 0.0, 0.0, 0.0 };
        CGColorRef invisibleColor = CGColorCreate(rgbColorspace, invisibleTextColorComponents);

        // Create a font for normal text.
        CFStringRef stringFontName = CFSTR("AppleCasual");
        CTFontRef stringFont = CTFontCreateWithName(stringFontName, 18.0, NULL);

        CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 0.0f);
        CGContextFillRect(context, cgBounds);

        // ************* Box the Window in Gray ***************************

        CGContextMoveToPoint(context, 0.0,1.0);
        CGContextAddLineToPoint(context,0.0, cgBounds.size.height - 1.0);
        CGContextAddLineToPoint(context,cgBounds.size.width - 2.0, cgBounds.size.height - 1.0);
        CGContextAddLineToPoint(context,cgBounds.size.width - 2.0, 1.0);
        CGContextAddLineToPoint(context,0.0, 1.0);
        CGContextSetLineWidth(context, 2.0);
        CGContextSetRGBStrokeColor(context, 0.7, 0.7, 0.7, 1.0);
        CGContextStrokePath(context);

        // *********** Draw String1

        CGPoint endingTextPoint;

        if(windowManager->statusTextBox1String)
        {
            // Create a mutable attributed string with a max length of 0 for normal text.
            CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);

            // Create a path which will bound the area where you will be drawing text.
            CGMutablePathRef invisibleTextPath = CGPathCreateMutable();

            // Create a path which will bound the area where you will be drawing text.
            CGMutablePathRef string1TextPath = CGPathCreateMutable();

            // Initialize a string.
            CFStringRef textString = (__bridge CFStringRef)windowManager->statusTextBox1String;
            CFIndex textStringLength = CFStringGetLength (textString);

            // Measure the string length
            CGRect invisibleTextBounds = CGRectMake(0.0, 0.0, cgBounds.size.width, 30.0);
            CGPathAddRect(invisibleTextPath, NULL, invisibleTextBounds);

            // Copy the textString into attrString
            CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), textString);

            // Set the color and font.
            CFAttributedStringSetAttribute(attrString, CFRangeMake(0, textStringLength), kCTForegroundColorAttributeName, invisibleColor);
            CFAttributedStringSetAttribute(attrString, CFRangeMake(0, textStringLength), kCTFontAttributeName, stringFont);

            // Create the framesetter with the attributed string.
            CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);

            CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), invisibleTextPath, NULL);

            // Draw the specified frame in the given context.
            CTFrameDraw(frame, context);

            endingTextPoint = CGContextGetTextPosition(context);

            // Draw the Text

            // Set a rectangular path.
            CGRect textBounds = CGRectMake((cgBounds.size.width / 2.0) - (endingTextPoint.x / 2.0), 140.0, cgBounds.size.width, 30.0);
            CGPathAddRect(string1TextPath, NULL, textBounds);

            // Copy the textString into attrString
            CFAttributedStringReplaceString (attrString, CFRangeMake(0, textStringLength), textString);

            // Set the color and fontof the first chars.
            CFAttributedStringSetAttribute(attrString, CFRangeMake(0, textStringLength), kCTForegroundColorAttributeName, whiteColor);
            CFAttributedStringSetAttribute(attrString, CFRangeMake(0, textStringLength), kCTFontAttributeName, stringFont);

            // Create the framesetter with the attributed string.
        framesetter = CTFramesetterCreateWithAttributedString(attrString);

            // Create a frame.
            frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), string1TextPath, NULL);

            // Draw the specified frame in the given context.
            CTFrameDraw(frame, context);

            CFRelease(string1TextPath);
            CFRelease(invisibleTextPath);
            CFRelease(frame);
            CFRelease(framesetter);
            CFRelease(attrString);
        }

        // ****************** Draw String 2

        if(windowManager->statusTextBox2String)
        {
            CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 0.0f);
            CGContextFillRect(context, cgBounds);

            // ********** Box the Window in Gray ***********************

            CGContextMoveToPoint(context, 0.0,1.0);
            CGContextAddLineToPoint(context,0.0, cgBounds.size.height - 1.0);
            CGContextAddLineToPoint(context,cgBounds.size.width - 2.0, cgBounds.size.height - 1.0);
            CGContextAddLineToPoint(context,cgBounds.size.width - 2.0, 1.0);
            CGContextAddLineToPoint(context,0.0, 1.0);
            CGContextSetLineWidth(context, 2.0);
            CGContextSetRGBStrokeColor(context, 0.7, 0.7, 0.7, 1.0);
            CGContextStrokePath(context);

            // Create a mutable attributed string with a max length of 0 for normal text.
            CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);

            // Create a path which will bound the area where you will be drawing text.
            CGMutablePathRef invisibleTextPath = CGPathCreateMutable();

            // Create a path which will bound the area where you will be drawing text.
            CGMutablePathRef string2TextPath = CGPathCreateMutable();

            // Initialize a string.
            CFStringRef textString = (__bridge CFStringRef)windowManager->statusTextBox2String;
            CFIndex textStringLength = CFStringGetLength (textString);

            // Measure the string length
            CGRect invisibleTextBounds = CGRectMake(0.0, 0.0, cgBounds.size.width, 130.0);
            CGPathAddRect(invisibleTextPath, NULL, invisibleTextBounds);

            // Copy the textString into attrString
            CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), textString);

            // Set the color and font
            CFAttributedStringSetAttribute(attrString, CFRangeMake(0, textStringLength), kCTForegroundColorAttributeName, invisibleColor);
            CFAttributedStringSetAttribute(attrString, CFRangeMake(0, textStringLength), kCTFontAttributeName, stringFont);

            // Create the framesetter with the attributed string.
            CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);

            CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), invisibleTextPath, NULL);

            // Draw the specified frame in the given context.
            CTFrameDraw(frame, context);

            endingTextPoint = CGContextGetTextPosition(context);

            // Draw the Text

            // Set a rectangular path.
            CGRect textBounds = CGRectMake((cgBounds.size.width / 2.0) - (endingTextPoint.x / 2.0), 100.0, cgBounds.size.width, 30.0);
            CGPathAddRect(string2TextPath, NULL, textBounds);

            // Copy the textString into attrString
            CFAttributedStringReplaceString (attrString, CFRangeMake(0, textStringLength), textString);

            // Set the color and fontof the first chars.
            CFAttributedStringSetAttribute(attrString, CFRangeMake(0, textStringLength), kCTForegroundColorAttributeName, whiteColor);
            CFAttributedStringSetAttribute(attrString, CFRangeMake(0, textStringLength), kCTFontAttributeName, stringFont);

            // Create the framesetter with the attributed string.
            framesetter = CTFramesetterCreateWithAttributedString(attrString);

            // Create a frame.
            frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), string2TextPath, NULL);

            // Draw the specified frame in the given context.
            CTFrameDraw(frame, context);

            CFRelease(string2TextPath);
            CFRelease(invisibleTextPath);
            CFRelease(frame);
            CFRelease(framesetter);
            CFRelease(attrString);
        }

        CFRelease(stringFont);
        CFRelease(whiteColor);
        CFRelease(invisibleColor);

        CGContextFlush(context);
    }

    return;
}

@end

这是这张 window 的图片:

行为不一致。例如,这个状态 window 应该出现在计算过程中的几个步骤中的每一个,但看起来只有其他 window 显示(例如步骤 2、4 和 6,但不是 1, 3 或 5).

生成了很多错误,但这是其中一个错误的示例回溯:

Oct  9 10:23:30  WispFractals3D[746] <Error>: CGContextRestoreGState: invalid context 0x0. Backtrace:
  <-[StatusWindowController updateStatusProgress:]+228>
   <-[AppController updateStatusProgress:]+64>
    <-[AppController runTheFractal:]+804>
     <_os_activity_initiate+75>
      <-[NSApplication sendAction:to:from:]+460>
       <-[NSMenuItem _corePerformAction]+336>
        <-[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:]+114>
         <_os_activity_initiate+75>
          <-[NSMenu performActionForItemAtIndex:]+131>
           <-[NSMenu _internalPerformActionForItemAtIndex:]+35>
            <-[NSCarbonMenuImpl       _carbonCommandProcessEvent:handlerCallRef:]+107>
             <NSSLMMenuEventHandler+708>
              <_ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec+1231>
               <_ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec+404>
                <SendEventToEventTarget+40>
                 <_ZL18SendHICommandEventjPK9HICommandjjhPKvP20OpaqueEventTargetRefS5_PP14OpaqueEventRef+411>
                  <SendMenuCommandWithContextAndModifiers+59>
                   <SendMenuItemSelectedEvent+188>
                    <_ZL19FinishMenuSelectionP13SelectionDataP10MenuResultS2_+96>
                     <_ZL14MenuSelectCoreP8MenuData5PointdjPP13OpaqueMenuRefPt+711>
                      <_HandleMenuSelection2+460>
                       <_NSHandleCarbonMenuEvent+277>
                        <_DPSNextEvent+1906>
                         <-[NSApplication _nextEventMatchingEventMask:untilDate:inMode:dequeue:]+454>
                          <-[NSApplication run]+682>
                           <NSApplicationMain+1176>
                            <main+34>

.xib 文件如下所示:

此 window 中有一个自定义进度指示器(由两个白色方块包围),它是不可见的,但确实存在。

显示此 window 的示例代码如下所示:

    [self showStatusWindowWithString1:@"Calculation Complete" String2:timeMessage ButtonOn:YES AbortOn:NO ProgressOn:NO ProgressMax:100.0 Title:@"Fractal Run Time"];

showStatusWindow 方法的代码是:

- (void)showStatusWindowWithString1:(NSString *)string1 String2:(NSString *)string2 ButtonOn:(BOOL)buttonon AbortOn:(BOOL)aborton ProgressOn:(BOOL)progresson ProgressMax:(double)progressmax Title:(NSString *)title
{
    statusWindowTitle = title;
    statusTextBox1String = string1;
    statusTextBox2String = string2;
    statusButtonOn = buttonon;
    abortOn = aborton;
    statusProgressOn = progresson;
    statusProgressMax = progressmax;

    if (!statusWindowController)
    {
        statusWindowController = [[StatusWindowController alloc] initWithWindowNibName:@"StatusWindow" Manager:self];
    }

    [[statusWindowController window] setTitle:statusWindowTitle];

    [statusWindowController showWindow:self];
    [fileStatusWindow makeKeyAndOrderFront:self];

    appDelegate->fileStatusWindowOpenFlag = YES;
    [appDelegate checkFlags];

    return;
}

Ken Thomases 诊断出回溯表明在 [StatusWindowController updateStatusProgress:] 中生成了无效的上下文错误(这更新了自定义进度指示器)。

我将此方法更改为:

- (void) updateStatusProgress:(double)statusprogress
{
    [statusProgressIndicator setDoubleValue:statusprogress];
    [statusProgressIndicator drawRect:[statusProgressIndicator bounds]];
}

收件人:

- (void) updateStatusProgress:(double)statusprogress
{
    [statusProgressIndicator setDoubleValue:statusprogress];
    [statusProgressIndicator setNeedsDisplay:YES];
}

在我看来,错误消息在多个地方生成,但这一更改停止了所有错误消息。

我的下一个问题是,状态 window(上图)并不是每次都应该显示,只是有时显示。同样,这一切在 Yosemite、Xcode 6.4.

中运行良好

我现在发现当状态 window 不显示时,我可以通过 运行 模态显示。圣运行ge.

我要解决的最后一个问题是 GLSL 着色器没有按应有的方式写入 OpenGL 视图。我已经通过在片段着色器的末尾添加一行来测试着色器正在加载和 运行:fragColor = vec4(1.0, 0.0, 0.0, 1.0);,这使视图完全变成红色,因为它应该.

片段着色器实际上应该从纹理中采样,所以我加载了所有 (255, 0, 0, 255) 的纹理,通过在片段着色器的末尾放置一个简单的采样语句来测试采样:

fragColor = texture(Texture, texCoord).rgba;

但是什么都没有写,所以肯定是加载纹理到着色器有问题。我现在正在努力。

我刚刚快速构建了我的一个应用程序,它以前没有在 EC 下重建过。一切正常。

也许您可以post说得更详细一些。代码和崩溃点?

导致 "invalid context 0x0" 消息的问题是您直接调用 -drawRect:。这从来都不是有效的。 -drawRect: 由框架在设置了适当的图形上下文和诸如坐标转换之类的东西后调用,以将绘图放置在 window 中的正确位置并裁剪到视图的边界。如果您在其他时间调用它,则没有设置上下文,也不会产生任何好处。

如果必须立即重绘,可以调用 -display... 方法之一。

但是,通常最好按照您现在所做的那样做:只需将视图标记为需要显示,然后让 Cocoa 在适当的时候重新绘制它。

您可能应该 post 将其他问题分开。