Commit 119ac96a authored by ccameron@chromium.org's avatar ccameron@chromium.org

Mac: Shift more code into C++ classes from ObjC classes

It is difficult to reason about the liftime of ObjC classes, especially
NSViews and CALayers. To simplify verifying their correctness, move the
bulk of the code for the ObjC classes into C++ helper classes (these
classes are already necessary to interface with owning structures).

Make the NSView sub-class BrowserCompositorViewCocoa be owned by
BrowserCompositorViewMacInternal.

Move the bulk of the work in CompositingIOSurfaceLayer to be done by
CompositingIOSurfaceLayerHelper, and mark that the ownership
relationship should be inverted (that isn't feasible at the moment
because we are mid-transition from non-delegated rendering to delegated
rendering).

BUG=392919

Review URL: https://codereview.chromium.org/394883007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284063 0039d316-1c4b-4281-b951-d872f2087c98
parent ba0a1faf
...@@ -18,8 +18,6 @@ ...@@ -18,8 +18,6 @@
#include "ui/events/latency_info.h" #include "ui/events/latency_info.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
@class BrowserCompositorViewCocoa;
// Additions to the NSView interface for compositor frames. // Additions to the NSView interface for compositor frames.
@interface NSView (BrowserCompositorView) @interface NSView (BrowserCompositorView)
- (void)gotAcceleratedIOSurfaceFrame:(IOSurfaceID)surface_handle - (void)gotAcceleratedIOSurfaceFrame:(IOSurfaceID)surface_handle
...@@ -36,6 +34,8 @@ ...@@ -36,6 +34,8 @@
namespace content { namespace content {
class BrowserCompositorViewMacInternal;
// The interface through which BrowserCompositorViewMac calls back into // The interface through which BrowserCompositorViewMac calls back into
// RenderWidgetHostViewMac (or any other structure that wishes to draw a // RenderWidgetHostViewMac (or any other structure that wishes to draw a
// NSView backed by a ui::Compositor). // NSView backed by a ui::Compositor).
...@@ -74,7 +74,7 @@ class BrowserCompositorViewMac { ...@@ -74,7 +74,7 @@ class BrowserCompositorViewMac {
private: private:
BrowserCompositorViewMacClient* client_; BrowserCompositorViewMacClient* client_;
base::scoped_nsobject<BrowserCompositorViewCocoa> cocoa_view_; scoped_ptr<BrowserCompositorViewMacInternal> internal_view_;
}; };
// A class to keep around whenever a BrowserCompositorViewMac may be created. // A class to keep around whenever a BrowserCompositorViewMac may be created.
......
...@@ -40,12 +40,12 @@ namespace { ...@@ -40,12 +40,12 @@ namespace {
// The number of placeholder objects allocated. If this reaches zero, then // The number of placeholder objects allocated. If this reaches zero, then
// the BrowserCompositorViewCocoa being held on to for recycling, // the BrowserCompositorViewCocoa being held on to for recycling,
// |g_recyclable_cocoa_view|, will be freed. // |g_recyclable_internal_view|, will be freed.
uint32 g_placeholder_count = 0; uint32 g_placeholder_count = 0;
// A spare BrowserCompositorViewCocoa kept around for recycling. // A spare BrowserCompositorViewCocoa kept around for recycling.
base::LazyInstance<base::scoped_nsobject<BrowserCompositorViewCocoa>> base::LazyInstance<scoped_ptr<BrowserCompositorViewMacInternal>>
g_recyclable_cocoa_view; g_recyclable_internal_view;
} // namespace } // namespace
...@@ -56,29 +56,26 @@ BrowserCompositorViewMac::BrowserCompositorViewMac( ...@@ -56,29 +56,26 @@ BrowserCompositorViewMac::BrowserCompositorViewMac(
// TODO(ccameron): If there exists a frame in flight (swap has been called // TODO(ccameron): If there exists a frame in flight (swap has been called
// by the compositor, but the frame has not arrived from the GPU process // by the compositor, but the frame has not arrived from the GPU process
// yet), then that frame may inappropriately flash in the new view. // yet), then that frame may inappropriately flash in the new view.
swap(g_recyclable_cocoa_view.Get(), cocoa_view_); internal_view_ = g_recyclable_internal_view.Get().Pass();
if (!cocoa_view_) if (!internal_view_)
cocoa_view_.reset([[BrowserCompositorViewCocoa alloc] init]); internal_view_.reset(new BrowserCompositorViewMacInternal);
[cocoa_view_ setClient:client_]; internal_view_->SetClient(client_);
} }
BrowserCompositorViewMac::~BrowserCompositorViewMac() { BrowserCompositorViewMac::~BrowserCompositorViewMac() {
// Make this BrowserCompositorViewCocoa recyclable for future instances. // Make this BrowserCompositorViewCocoa recyclable for future instances.
[cocoa_view_ setClient:NULL]; internal_view_->ResetClient();
[g_recyclable_cocoa_view.Get() destroyCompositor]; g_recyclable_internal_view.Get() = internal_view_.Pass();
swap(g_recyclable_cocoa_view.Get(), cocoa_view_);
// If there are no placeholders allocated, destroy the recyclable // If there are no placeholders allocated, destroy the recyclable
// BrowserCompositorViewCocoa that we just populated. // BrowserCompositorViewCocoa that we just populated.
if (!g_placeholder_count) { if (!g_placeholder_count)
[g_recyclable_cocoa_view.Get() destroyCompositor]; g_recyclable_internal_view.Get().reset();
g_recyclable_cocoa_view.Get().reset();
}
} }
ui::Compositor* BrowserCompositorViewMac::GetCompositor() const { ui::Compositor* BrowserCompositorViewMac::GetCompositor() const {
DCHECK(cocoa_view_); DCHECK(internal_view_);
return [cocoa_view_ compositor]; return internal_view_->compositor();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
...@@ -94,10 +91,8 @@ BrowserCompositorViewPlaceholderMac::~BrowserCompositorViewPlaceholderMac() { ...@@ -94,10 +91,8 @@ BrowserCompositorViewPlaceholderMac::~BrowserCompositorViewPlaceholderMac() {
// If there are no placeholders allocated, destroy the recyclable // If there are no placeholders allocated, destroy the recyclable
// BrowserCompositorViewCocoa. // BrowserCompositorViewCocoa.
if (!g_placeholder_count) { if (!g_placeholder_count)
[g_recyclable_cocoa_view.Get() destroyCompositor]; g_recyclable_internal_view.Get().reset();
g_recyclable_cocoa_view.Get().reset();
}
} }
} // namespace content } // namespace content
...@@ -7,16 +7,71 @@ ...@@ -7,16 +7,71 @@
#include "content/browser/compositor/browser_compositor_view_mac.h" #include "content/browser/compositor/browser_compositor_view_mac.h"
@class BrowserCompositorViewCocoa;
namespace content { namespace content {
class BrowserCompositorViewCocoaHelper;
}
// An NSView drawn by a ui::Compositor. This structure is expensive to create, // BrowserCompositorViewCocoaClient is the interface through which
// because it has a ui::Compositor. As a result, this structure may be recycled // gfx::NativeWidget (aka NSView aka BrowserCompositorViewCocoa) calls back to
// across multiple BrowserCompositorViewMac objects. // BrowserCompositorViewMacInternal.
@interface BrowserCompositorViewCocoa : NSView { class BrowserCompositorViewCocoaClient {
public:
virtual void GotAcceleratedIOSurfaceFrame(
IOSurfaceID io_surface_id,
int output_surface_id,
const std::vector<ui::LatencyInfo>& latency_info,
gfx::Size pixel_size,
float scale_factor) = 0;
virtual void GotSoftwareFrame(
cc::SoftwareFrameData* frame_data,
float scale_factor,
SkCanvas* canvas) = 0;
};
// BrowserCompositorViewMacInternal owns a NSView and a ui::Compositor that
// draws that view.
class BrowserCompositorViewMacInternal
: public BrowserCompositorViewCocoaClient,
public CompositingIOSurfaceLayerClient {
public:
BrowserCompositorViewMacInternal();
virtual ~BrowserCompositorViewMacInternal();
void SetClient(BrowserCompositorViewMacClient* client);
void ResetClient();
ui::Compositor* compositor() const { return compositor_.get(); }
private:
// BrowserCompositorViewCocoaClient implementation:
virtual void GotAcceleratedIOSurfaceFrame(
IOSurfaceID io_surface_id,
int output_surface_id,
const std::vector<ui::LatencyInfo>& latency_info,
gfx::Size pixel_size,
float scale_factor) OVERRIDE;
virtual void GotSoftwareFrame(
cc::SoftwareFrameData* frame_data,
float scale_factor,
SkCanvas* canvas) OVERRIDE;
// CompositingIOSurfaceLayerClient implementation:
virtual void AcceleratedLayerDidDrawFrame(bool succeeded) OVERRIDE;
// The client of the BrowserCompositorViewMac that is using this as its
// internals.
BrowserCompositorViewMacClient* client_;
// The NSView drawn by the |compositor_|
base::scoped_nsobject<BrowserCompositorViewCocoa> cocoa_view_;
// The compositor drawing the contents of |cooca_view_|. Note that this must
// be declared after |cocoa_view_|, so that it be destroyed first (because it
// will reach into |cocoa_view_|).
scoped_ptr<ui::Compositor> compositor_; scoped_ptr<ui::Compositor> compositor_;
// A flipped layer, which acts as the parent of the compositing and software // A flipped layer, which acts as the parent of the compositing and software
// layers. This layer is flipped so that the we don't need to recompute the // layers. This layer is flipped so that the we don't need to recompute the
// origin for sub-layers when their position changes (this is impossible when // origin for sub-layers when their position changes (this is impossible when
...@@ -31,48 +86,21 @@ class BrowserCompositorViewCocoaHelper; ...@@ -31,48 +86,21 @@ class BrowserCompositorViewCocoaHelper;
std::vector<ui::LatencyInfo> accelerated_latency_info_; std::vector<ui::LatencyInfo> accelerated_latency_info_;
base::scoped_nsobject<SoftwareLayer> software_layer_; base::scoped_nsobject<SoftwareLayer> software_layer_;
};
content::BrowserCompositorViewMacClient* client_; } // namespace content
scoped_ptr<content::BrowserCompositorViewCocoaHelper> helper_;
}
// Change the client and superview of the view. If this is set to NULL then
// the compositor will be prepared to be recycled.
- (void)setClient:(content::BrowserCompositorViewMacClient*)client;
// This is called to destroy the underlying ui::Compositor, if it is known
// that this will not be recycled again.
- (void)destroyCompositor;
// Access the underlying ui::Compositor for this view.
- (ui::Compositor*)compositor;
// Called when the accelerated or software layer draws its frame to the screen.
- (void)layerDidDrawFrame;
// Called when an error is encountered while drawing to the screen.
- (void)gotAcceleratedLayerError;
@end // BrowserCompositorViewCocoa
namespace content {
// This class implements the parts of BrowserCompositorViewCocoa that need to
// be a C++ class and not an Objective C class.
class BrowserCompositorViewCocoaHelper
: public content::CompositingIOSurfaceLayerClient {
public:
BrowserCompositorViewCocoaHelper(BrowserCompositorViewCocoa* view)
: view_(view) {}
virtual ~BrowserCompositorViewCocoaHelper() {}
private: // BrowserCompositorViewCocoa is the actual NSView to which the layers drawn
// CompositingIOSurfaceLayerClient implementation: // by the ui::Compositor are attached.
virtual void AcceleratedLayerDidDrawFrame(bool succeeded) OVERRIDE; @interface BrowserCompositorViewCocoa : NSView {
content::BrowserCompositorViewCocoaClient* client_;
}
BrowserCompositorViewCocoa* view_; - (id)initWithClient:(content::BrowserCompositorViewCocoaClient*)client;
};
} // namespace content // Mark that the client provided at initialization is no longer valid and may
// not be called back into.
- (void)resetClient;
@end
#endif // CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_VIEW_PRIVATE_MAC_H_ #endif // CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_VIEW_PRIVATE_MAC_H_
...@@ -11,43 +11,91 @@ ...@@ -11,43 +11,91 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
@class CompositingIOSurfaceLayer;
namespace content { namespace content {
class CompositingIOSurfaceMac; class CompositingIOSurfaceMac;
class CompositingIOSurfaceContext; class CompositingIOSurfaceContext;
class CompositingIOSurfaceLayerHelper;
// The interface through which the CompositingIOSurfaceLayer calls back into
// the structrue that created it (RenderWidgetHostViewMac or
// BrowserCompositorViewMac).
class CompositingIOSurfaceLayerClient { class CompositingIOSurfaceLayerClient {
public: public:
virtual void AcceleratedLayerDidDrawFrame(bool succeeded) = 0; virtual void AcceleratedLayerDidDrawFrame(bool succeeded) = 0;
}; };
} // namespace content // CompositingIOSurfaceLayerHelper provides C++ functionality needed for the
// CompositingIOSurfaceLayer class, and does most of the heavy lifting for the
// class.
// TODO(ccameron): This class should own CompositingIOSurfaceLayer, rather than
// vice versa.
class CompositingIOSurfaceLayerHelper {
public:
CompositingIOSurfaceLayerHelper(CompositingIOSurfaceLayerClient* client,
CompositingIOSurfaceLayer* layer);
~CompositingIOSurfaceLayerHelper();
// The CoreAnimation layer for drawing accelerated content. // Called when the CompositingIOSurfaceLayer gets a new frame.
@interface CompositingIOSurfaceLayer : CAOpenGLLayer { void GotNewFrame();
@private
content::CompositingIOSurfaceLayerClient* client_;
scoped_refptr<content::CompositingIOSurfaceMac> iosurface_;
scoped_refptr<content::CompositingIOSurfaceContext> context_;
// The browser places back-pressure on the GPU by not acknowledging swap // Called whenever -[CompositingIOSurfaceLayer setNeedsDisplay] is called.
// calls until they appear on the screen. This can lead to hangs if the void SetNeedsDisplay();
// view is moved offscreen (among other things). Prevent hangs by always
// acknowledging the frame after timeout of 1/6th of a second has passed. // Called whenever -[CompositingIOSurfaceLayer canDrawInCGLContext] is called,
scoped_ptr<content::CompositingIOSurfaceLayerHelper> helper_; // to determine if a new frame should be drawn.
scoped_ptr<base::DelayTimer<content::CompositingIOSurfaceLayerHelper>> timer_; bool CanDraw();
// Called whenever -[CompositingIOSurfaceLayer drawInCGLContext] draws a
// frame.
void DidDraw(bool success);
private:
// Immediately draw a frame (disregarding vsync) and ensure that the frame is
// acknowledged.
void ImmediatelyForceDisplayAndAck();
// Called whenever the frame provided in GotNewFrame should be acknowledged
// (this may be because it was drawn, or it may be to unblock the
// compositor).
void AckPendingFrame(bool success);
void TimerFired();
// The client that the owning layer was created with.
content::CompositingIOSurfaceLayerClient* const client_;
// The layer that owns this helper.
CompositingIOSurfaceLayer* const layer_;
// Used to track when canDrawInCGLContext should return YES. This can be // Used to track when canDrawInCGLContext should return YES. This can be
// in response to receiving a new compositor frame, or from any of the events // in response to receiving a new compositor frame, or from any of the events
// that cause setNeedsDisplay to be called on the layer. // that cause setNeedsDisplay to be called on the layer.
BOOL needs_display_; bool needs_display_;
// This is set when a frame is received, and un-set when the frame is drawn. // This is set when a frame is received, and un-set when the frame is drawn.
BOOL has_pending_frame_; bool has_pending_frame_;
// Incremented every time that this layer is asked to draw but does not have // Incremented every time that this layer is asked to draw but does not have
// new content to draw. // new content to draw.
uint64 did_not_draw_counter_; uint64 did_not_draw_counter_;
// The browser places back-pressure on the GPU by not acknowledging swap
// calls until they appear on the screen. This can lead to hangs if the
// view is moved offscreen (among other things). Prevent hangs by always
// acknowledging the frame after timeout of 1/6th of a second has passed.
base::DelayTimer<CompositingIOSurfaceLayerHelper> timer_;
};
} // namespace content
// The CoreAnimation layer for drawing accelerated content.
@interface CompositingIOSurfaceLayer : CAOpenGLLayer {
@private
scoped_refptr<content::CompositingIOSurfaceMac> iosurface_;
scoped_refptr<content::CompositingIOSurfaceContext> context_;
scoped_ptr<content::CompositingIOSurfaceLayerHelper> helper_;
} }
- (content::CompositingIOSurfaceMac*)iosurface; - (content::CompositingIOSurfaceMac*)iosurface;
...@@ -58,7 +106,8 @@ class CompositingIOSurfaceLayerClient { ...@@ -58,7 +106,8 @@ class CompositingIOSurfaceLayerClient {
withScaleFactor:(float)scale_factor withScaleFactor:(float)scale_factor
withClient:(content::CompositingIOSurfaceLayerClient*)client; withClient:(content::CompositingIOSurfaceLayerClient*)client;
// Mark that the client is no longer valid and cannot be called back into. // Mark that the client is no longer valid and cannot be called back into. This
// must be called before the layer is destroyed.
- (void)resetClient; - (void)resetClient;
// Called when a new frame is received. // Called when a new frame is received.
......
...@@ -17,29 +17,114 @@ ...@@ -17,29 +17,114 @@
#include "ui/gfx/size_conversions.h" #include "ui/gfx/size_conversions.h"
#include "ui/gl/gpu_switching_manager.h" #include "ui/gl/gpu_switching_manager.h"
@interface CompositingIOSurfaceLayer(Private) ////////////////////////////////////////////////////////////////////////////////
- (void)immediatelyForceDisplayAndAck; // CompositingIOSurfaceLayerHelper
- (void)ackPendingFrame:(bool)success;
- (void)timerFired;
@end
namespace content { namespace content {
// The base::DelayTimer needs a C++ class to operate on, rather than Objective C CompositingIOSurfaceLayerHelper::CompositingIOSurfaceLayerHelper(
// class. This helper class provides a bridge between the two. CompositingIOSurfaceLayerClient* client,
class CompositingIOSurfaceLayerHelper { CompositingIOSurfaceLayer* layer)
public: : client_(client),
CompositingIOSurfaceLayerHelper(CompositingIOSurfaceLayer* layer) layer_(layer),
: layer_(layer) {} needs_display_(false),
void TimerFired() { has_pending_frame_(false),
[layer_ timerFired]; did_not_draw_counter_(0),
timer_(
FROM_HERE,
base::TimeDelta::FromSeconds(1) / 6,
this,
&CompositingIOSurfaceLayerHelper::TimerFired) {}
CompositingIOSurfaceLayerHelper::~CompositingIOSurfaceLayerHelper() {
// Any acks that were waiting on this layer to draw will not occur, so ack
// them now to prevent blocking the renderer.
AckPendingFrame(true);
}
void CompositingIOSurfaceLayerHelper::GotNewFrame() {
has_pending_frame_ = true;
needs_display_ = true;
timer_.Reset();
if ([layer_ context] && [layer_ context]->is_vsync_disabled()) {
// If vsync is disabled, draw immediately and don't bother trying to use
// the isAsynchronous property to ensure smooth animation.
ImmediatelyForceDisplayAndAck();
} else {
needs_display_ = YES;
if (![layer_ isAsynchronous])
[layer_ setAsynchronous:YES];
}
// A trace value of 2 indicates that there is a pending swap ack. See
// canDrawInCGLContext for other value meanings.
TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 2);
}
void CompositingIOSurfaceLayerHelper::SetNeedsDisplay() {
needs_display_ = true;
}
bool CompositingIOSurfaceLayerHelper::CanDraw() {
// If we return NO 30 times in a row, switch to being synchronous to avoid
// burning CPU cycles on this callback.
if (needs_display_) {
did_not_draw_counter_ = 0;
} else {
did_not_draw_counter_ += 1;
if (did_not_draw_counter_ == 30)
[layer_ setAsynchronous:NO];
} }
private:
CompositingIOSurfaceLayer* layer_; // Add an instantaneous blip to the PendingSwapAck state to indicate
}; // that CoreAnimation asked if a frame is ready. A blip up to to 3 (usually
// from 2, indicating that a swap ack is pending) indicates that we
// requested a draw. A blip up to 1 (usually from 0, indicating there is no
// pending swap ack) indicates that we did not request a draw. This would
// be more natural to do with a tracing pseudo-thread
// http://crbug.com/366300
TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, needs_display_ ? 3 : 1);
TRACE_COUNTER_ID1("browser", "PendingSwapAck", this,
has_pending_frame_ ? 2 : 0);
return needs_display_;
}
void CompositingIOSurfaceLayerHelper::DidDraw(bool success) {
needs_display_ = false;
AckPendingFrame(success);
}
void CompositingIOSurfaceLayerHelper::AckPendingFrame(bool success) {
if (!has_pending_frame_)
return;
has_pending_frame_ = false;
client_->AcceleratedLayerDidDrawFrame(success);
// A trace value of 0 indicates that there is no longer a pending swap ack.
TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 0);
}
void CompositingIOSurfaceLayerHelper::ImmediatelyForceDisplayAndAck() {
[layer_ setNeedsDisplay];
[layer_ displayIfNeeded];
// Calls to setNeedsDisplay can sometimes be ignored, especially if issued
// rapidly (e.g, with vsync off). This is unacceptable because the failure
// to ack a single frame will hang the renderer. Ensure that the renderer
// not be blocked by lying and claiming that we drew the frame.
AckPendingFrame(true);
}
void CompositingIOSurfaceLayerHelper::TimerFired() {
ImmediatelyForceDisplayAndAck();
}
} // namespace content } // namespace content
////////////////////////////////////////////////////////////////////////////////
// CompositingIOSurfaceLayer
@implementation CompositingIOSurfaceLayer @implementation CompositingIOSurfaceLayer
- (content::CompositingIOSurfaceMac*)iosurface { - (content::CompositingIOSurfaceMac*)iosurface {
...@@ -55,21 +140,12 @@ class CompositingIOSurfaceLayerHelper { ...@@ -55,21 +140,12 @@ class CompositingIOSurfaceLayerHelper {
withScaleFactor:(float)scale_factor withScaleFactor:(float)scale_factor
withClient:(content::CompositingIOSurfaceLayerClient*)client { withClient:(content::CompositingIOSurfaceLayerClient*)client {
if (self = [super init]) { if (self = [super init]) {
iosurface_ = iosurface; helper_.reset(new content::CompositingIOSurfaceLayerHelper(client, self));
client_ = client;
helper_.reset(new content::CompositingIOSurfaceLayerHelper(self));
timer_.reset(new base::DelayTimer<content::CompositingIOSurfaceLayerHelper>(
FROM_HERE,
base::TimeDelta::FromSeconds(1) / 6,
helper_.get(),
&content::CompositingIOSurfaceLayerHelper::TimerFired));
iosurface_ = iosurface;
context_ = content::CompositingIOSurfaceContext::Get( context_ = content::CompositingIOSurfaceContext::Get(
content::CompositingIOSurfaceContext::kCALayerContextWindowNumber); content::CompositingIOSurfaceContext::kCALayerContextWindowNumber);
DCHECK(context_); DCHECK(context_);
needs_display_ = NO;
has_pending_frame_ = NO;
did_not_draw_counter_ = 0;
[self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)]; [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
[self setAnchorPoint:CGPointMake(0, 0)]; [self setAnchorPoint:CGPointMake(0, 0)];
...@@ -83,58 +159,17 @@ class CompositingIOSurfaceLayerHelper { ...@@ -83,58 +159,17 @@ class CompositingIOSurfaceLayerHelper {
return self; return self;
} }
- (void)resetClient { - (void)dealloc {
// Any acks that were waiting on this layer to draw will not occur, so ack DCHECK(!helper_);
// them now to prevent blocking the renderer. [super dealloc];
[self ackPendingFrame:true];
client_ = NULL;
}
- (void)gotNewFrame {
has_pending_frame_ = YES;
timer_->Reset();
// A trace value of 2 indicates that there is a pending swap ack. See
// canDrawInCGLContext for other value meanings.
TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, 2);
if (context_ && context_->is_vsync_disabled()) {
// If vsync is disabled, draw immediately and don't bother trying to use
// the isAsynchronous property to ensure smooth animation.
[self immediatelyForceDisplayAndAck];
} else {
needs_display_ = YES;
if (![self isAsynchronous])
[self setAsynchronous:YES];
}
}
// Private methods:
- (void)immediatelyForceDisplayAndAck {
[self setNeedsDisplay];
[self displayIfNeeded];
// Calls to setNeedsDisplay can sometimes be ignored, especially if issued
// rapidly (e.g, with vsync off). This is unacceptable because the failure
// to ack a single frame will hang the renderer. Ensure that the renderer
// not be blocked by lying and claiming that we drew the frame.
[self ackPendingFrame:true];
} }
- (void)ackPendingFrame:(bool)success { - (void)resetClient {
if (!has_pending_frame_) helper_.reset();
return;
TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, 0);
has_pending_frame_ = NO;
if (client_)
client_->AcceleratedLayerDidDrawFrame(success);
} }
- (void)timerFired { - (void)gotNewFrame {
if (has_pending_frame_) helper_->GotNewFrame();
[self immediatelyForceDisplayAndAck];
} }
// The remaining methods implement the CAOpenGLLayer interface. // The remaining methods implement the CAOpenGLLayer interface.
...@@ -152,7 +187,8 @@ class CompositingIOSurfaceLayerHelper { ...@@ -152,7 +187,8 @@ class CompositingIOSurfaceLayerHelper {
} }
- (void)setNeedsDisplay { - (void)setNeedsDisplay {
needs_display_ = YES; if (helper_)
helper_->SetNeedsDisplay();
[super setNeedsDisplay]; [super setNeedsDisplay];
} }
...@@ -160,28 +196,9 @@ class CompositingIOSurfaceLayerHelper { ...@@ -160,28 +196,9 @@ class CompositingIOSurfaceLayerHelper {
pixelFormat:(CGLPixelFormatObj)pixelFormat pixelFormat:(CGLPixelFormatObj)pixelFormat
forLayerTime:(CFTimeInterval)timeInterval forLayerTime:(CFTimeInterval)timeInterval
displayTime:(const CVTimeStamp*)timeStamp { displayTime:(const CVTimeStamp*)timeStamp {
// Add an instantaneous blip to the PendingSwapAck state to indicate if (helper_)
// that CoreAnimation asked if a frame is ready. A blip up to to 3 (usually return helper_->CanDraw();
// from 2, indicating that a swap ack is pending) indicates that we requested return NO;
// a draw. A blip up to 1 (usually from 0, indicating there is no pending swap
// ack) indicates that we did not request a draw. This would be more natural
// to do with a tracing pseudo-thread
// http://crbug.com/366300
TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, needs_display_ ? 3 : 1);
TRACE_COUNTER_ID1("browser", "PendingSwapAck", self,
has_pending_frame_ ? 2 : 0);
// If we return NO 30 times in a row, switch to being synchronous to avoid
// burning CPU cycles on this callback.
if (needs_display_) {
did_not_draw_counter_ = 0;
} else {
did_not_draw_counter_ += 1;
if (did_not_draw_counter_ > 30)
[self setAsynchronous:NO];
}
return needs_display_;
} }
- (void)drawInCGLContext:(CGLContextObj)glContext - (void)drawInCGLContext:(CGLContextObj)glContext
...@@ -211,8 +228,8 @@ class CompositingIOSurfaceLayerHelper { ...@@ -211,8 +228,8 @@ class CompositingIOSurfaceLayerHelper {
bool draw_succeeded = iosurface_->DrawIOSurface( bool draw_succeeded = iosurface_->DrawIOSurface(
context_, window_rect, window_scale_factor); context_, window_rect, window_scale_factor);
[self ackPendingFrame:draw_succeeded]; if (helper_)
needs_display_ = NO; helper_->DidDraw(draw_succeeded);
[super drawInCGLContext:glContext [super drawInCGLContext:glContext
pixelFormat:pixelFormat pixelFormat:pixelFormat
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment