summaryrefslogtreecommitdiffstats
path: root/content/browser/renderer_host/compositing_iosurface_layer_mac.mm
blob: 38752b484a341fae652df3ded70573ab22bdb4ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"

#include <CoreFoundation/CoreFoundation.h>
#include <OpenGL/gl.h>

#include "base/mac/sdk_forward_declarations.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_mac.h"
#include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
#include "content/browser/renderer_host/compositing_iosurface_mac.h"
#include "ui/base/cocoa/animation_utils.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gl/gpu_switching_manager.h"

@implementation CompositingIOSurfaceLayer

@synthesize context = context_;

- (id)initWithRenderWidgetHostViewMac:(content::RenderWidgetHostViewMac*)r {
  if (self = [super init]) {
    renderWidgetHostView_ = r;
    context_ = content::CompositingIOSurfaceContext::Get(
        content::CompositingIOSurfaceContext::kOffscreenContextWindowNumber);
    DCHECK(context_);

    ScopedCAActionDisabler disabler;
    [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
    [self setContentsGravity:kCAGravityTopLeft];
    [self setFrame:NSRectToCGRect(
        [renderWidgetHostView_->cocoa_view() bounds])];
    [self setNeedsDisplay];
    [self updateScaleFactor];
  }
  return self;
}

- (void)updateScaleFactor {
  if (!renderWidgetHostView_ ||
      ![self respondsToSelector:(@selector(contentsScale))] ||
      ![self respondsToSelector:(@selector(setContentsScale:))])
    return;

  float current_scale_factor = [self contentsScale];
  float new_scale_factor = current_scale_factor;
  if (renderWidgetHostView_->compositing_iosurface_) {
    new_scale_factor =
        renderWidgetHostView_->compositing_iosurface_->scale_factor();
  }

  if (new_scale_factor == current_scale_factor)
    return;

  ScopedCAActionDisabler disabler;
  [self setContentsScale:new_scale_factor];
}

- (void)disableCompositing{
  ScopedCAActionDisabler disabler;
  [self removeFromSuperlayer];
  renderWidgetHostView_ = nil;
}

// The remaining methods implement the CAOpenGLLayer interface.

- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
  if (!context_)
    return [super copyCGLPixelFormatForDisplayMask:mask];
  return CGLRetainPixelFormat(CGLGetPixelFormat(context_->cgl_context()));
}

- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
  if (!context_)
    return [super copyCGLContextForPixelFormat:pixelFormat];
  return CGLRetainContext(context_->cgl_context());
}

- (void)drawInCGLContext:(CGLContextObj)glContext
             pixelFormat:(CGLPixelFormatObj)pixelFormat
            forLayerTime:(CFTimeInterval)timeInterval
             displayTime:(const CVTimeStamp*)timeStamp {
  TRACE_EVENT0("browser", "CompositingIOSurfaceLayer::drawInCGLContext");

  if (!context_ ||
      (context_ && context_->cgl_context() != glContext) ||
      !renderWidgetHostView_ ||
      !renderWidgetHostView_->compositing_iosurface_) {
    glClearColor(1, 1, 1, 1);
    glClear(GL_COLOR_BUFFER_BIT);
    return;
  }

  // Cache a copy of renderWidgetHostView_ because it may be reset if
  // a software frame is received in GetBackingStore.
  content::RenderWidgetHostViewMac* cached_view = renderWidgetHostView_;

  // If a resize is in progress then GetBackingStore request a frame of the
  // current window size and block until a frame of the right size comes in.
  // This makes the window content not lag behind the resize (at the cost of
  // blocking on the browser's main thread).
  if (cached_view->render_widget_host_) {
    // Note that GetBackingStore can potentially spawn a nested run loop, which
    // may change the current GL context, or, because the GL contexts are
    // shared, may change the currently-bound FBO. Ensure that, when the run
    // loop returns, the original GL context remain current, and the original
    // FBO remain bound.
    // TODO(ccameron): This is far too fragile a mechanism to rely on. Find
    // a way to avoid doing this.
    GLuint previous_framebuffer = 0;
    glGetIntegerv(GL_FRAMEBUFFER_BINDING,
                  reinterpret_cast<GLint*>(&previous_framebuffer));
    {
      gfx::ScopedCGLSetCurrentContext scoped_set_current_context(NULL);
      cached_view->about_to_validate_and_paint_ = true;
      (void)cached_view->render_widget_host_->GetBackingStore(true);
      cached_view->about_to_validate_and_paint_ = false;
    }
    CHECK_EQ(CGLGetCurrentContext(), glContext)
        << "original GL context failed to re-bind after nested run loop, "
        << "browser crash is imminent.";
    glBindFramebuffer(GL_FRAMEBUFFER, previous_framebuffer);
  }

  // If a transition to software mode has occurred, this layer should be
  // removed from the heirarchy now, so don't draw anything.
  if (!renderWidgetHostView_)
    return;

  gfx::Rect window_rect([self frame]);
  float window_scale_factor = 1.f;
  if ([self respondsToSelector:(@selector(contentsScale))])
    window_scale_factor = [self contentsScale];

  if (!renderWidgetHostView_->compositing_iosurface_->DrawIOSurface(
        context_,
        window_rect,
        window_scale_factor,
        false)) {
    renderWidgetHostView_->GotAcceleratedCompositingError();
    return;
  }

  renderWidgetHostView_->SendPendingLatencyInfoToHost();
}

@end