Index: content/browser/renderer_host/compositing_iosurface_mac.mm |
diff --git a/content/browser/renderer_host/compositing_iosurface_mac.mm b/content/browser/renderer_host/compositing_iosurface_mac.mm |
index 8059e11686da59b4cdbccda39010a6a24446091f..361ffeeb0bd45f6c3e2150e570bd670b9cb3b596 100644 |
--- a/content/browser/renderer_host/compositing_iosurface_mac.mm |
+++ b/content/browser/renderer_host/compositing_iosurface_mac.mm |
@@ -9,6 +9,7 @@ |
#include "base/command_line.h" |
#include "base/debug/trace_event.h" |
+#include "base/threading/platform_thread.h" |
#include "content/browser/renderer_host/render_widget_host_view_mac.h" |
#include "content/public/browser/browser_thread.h" |
#include "gpu/command_buffer/service/gpu_switches.h" |
@@ -31,26 +32,26 @@ |
namespace content { |
namespace { |
-static const char* g_vertex_shader_blit_rgb = SHADER_STRING_GLSL( |
+const char* g_vertex_shader_blit_rgb = SHADER_STRING_GLSL( |
varying vec2 texture_coordinate; |
void main() { |
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; |
texture_coordinate = vec2(gl_MultiTexCoord0); |
}); |
-static const char* g_fragment_shader_blit_rgb = SHADER_STRING_GLSL( |
+const char* g_fragment_shader_blit_rgb = SHADER_STRING_GLSL( |
varying vec2 texture_coordinate; |
uniform sampler2DRect texture; |
void main() { |
gl_FragColor = vec4(texture2DRect(texture, texture_coordinate).rgb, 1.0); |
}); |
-static const char* g_vertex_shader_white = SHADER_STRING_GLSL( |
+const char* g_vertex_shader_white = SHADER_STRING_GLSL( |
void main() { |
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; |
}); |
-static const char* g_fragment_shader_white = SHADER_STRING_GLSL( |
+const char* g_fragment_shader_white = SHADER_STRING_GLSL( |
void main() { |
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); |
}); |
@@ -105,6 +106,18 @@ GLuint CreateProgramGLSL(const char* vertex_shader_str, |
} // namespace |
+CVReturn DisplayLinkCallback(CVDisplayLinkRef display_link, |
+ const CVTimeStamp* now, |
+ const CVTimeStamp* output_time, |
+ CVOptionFlags flags_in, |
+ CVOptionFlags* flags_out, |
+ void* context) { |
+ CompositingIOSurfaceMac* surface = |
+ static_cast<CompositingIOSurfaceMac*>(context); |
+ surface->DisplayLinkTick(display_link, output_time); |
+ return kCVReturnSuccess; |
+} |
+ |
CompositingIOSurfaceMac* CompositingIOSurfaceMac::Create() { |
TRACE_EVENT0("browser", "CompositingIOSurfaceMac::Create"); |
IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); |
@@ -169,12 +182,33 @@ CompositingIOSurfaceMac* CompositingIOSurfaceMac::Create() { |
return NULL; |
} |
+ CVDisplayLinkRef display_link; |
+ CVReturn ret = CVDisplayLinkCreateWithActiveCGDisplays(&display_link); |
+ if (ret != kCVReturnSuccess) { |
+ LOG(ERROR) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret; |
+ return NULL; |
+ } |
+ |
+ // Set the display link for the current renderer |
+ CGLPixelFormatObj cglPixelFormat = |
+ (CGLPixelFormatObj)[glPixelFormat CGLPixelFormatObj]; |
+ ret = CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(display_link, |
+ cglContext, |
+ cglPixelFormat); |
+ if (ret != kCVReturnSuccess) { |
+ CVDisplayLinkRelease(display_link); |
+ LOG(ERROR) << "CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext failed: " |
+ << ret; |
+ return NULL; |
+ } |
+ |
return new CompositingIOSurfaceMac(io_surface_support, glContext.release(), |
cglContext, |
shader_program_blit_rgb, |
blit_rgb_sampler_location, |
shader_program_white, |
- is_vsync_disabled); |
+ is_vsync_disabled, |
+ display_link); |
} |
CompositingIOSurfaceMac::CompositingIOSurfaceMac( |
@@ -184,7 +218,8 @@ CompositingIOSurfaceMac::CompositingIOSurfaceMac( |
GLuint shader_program_blit_rgb, |
GLint blit_rgb_sampler_location, |
GLuint shader_program_white, |
- bool is_vsync_disabled) |
+ bool is_vsync_disabled, |
+ CVDisplayLinkRef display_link) |
: io_surface_support_(io_surface_support), |
glContext_(glContext), |
cglContext_(cglContext), |
@@ -193,10 +228,45 @@ CompositingIOSurfaceMac::CompositingIOSurfaceMac( |
shader_program_blit_rgb_(shader_program_blit_rgb), |
blit_rgb_sampler_location_(blit_rgb_sampler_location), |
shader_program_white_(shader_program_white), |
- is_vsync_disabled_(is_vsync_disabled) { |
+ is_vsync_disabled_(is_vsync_disabled), |
+ display_link_(display_link), |
+ display_link_stop_timer_(FROM_HERE, base::TimeDelta::FromSeconds(1), |
+ this, &CompositingIOSurfaceMac::StopDisplayLink), |
+ vsync_count_(0), |
+ swap_count_(0), |
+ vsync_interval_numerator_(0), |
+ vsync_interval_denominator_(0) { |
+ CVReturn ret = CVDisplayLinkSetOutputCallback(display_link_, |
+ &DisplayLinkCallback, this); |
+ DCHECK(ret == kCVReturnSuccess) |
+ << "CVDisplayLinkSetOutputCallback failed: " << ret; |
+ |
+ StartOrContinueDisplayLink(); |
+ |
+ CVTimeStamp cv_time; |
+ ret = CVDisplayLinkGetCurrentTime(display_link_, &cv_time); |
+ DCHECK(ret == kCVReturnSuccess) |
+ << "CVDisplayLinkGetCurrentTime failed: " << ret; |
+ |
+ { |
+ base::AutoLock lock(lock_); |
+ CalculateVsyncParametersLockHeld(&cv_time); |
+ } |
+ |
+ // Stop display link for now, it will be started when needed during Draw. |
+ StopDisplayLink(); |
+} |
+ |
+void CompositingIOSurfaceMac::GetVSyncParameters(base::TimeTicks* timebase, |
+ uint32* interval_numerator, |
+ uint32* interval_denominator) { |
+ *timebase = vsync_timebase_; |
+ *interval_numerator = vsync_interval_numerator_; |
+ *interval_denominator = vsync_interval_denominator_; |
} |
CompositingIOSurfaceMac::~CompositingIOSurfaceMac() { |
+ CVDisplayLinkRelease(display_link_); |
UnrefIOSurface(); |
} |
@@ -300,7 +370,15 @@ void CompositingIOSurfaceMac::DrawIOSurface(NSView* view, float scale_factor) { |
CGLFlushDrawable(cglContext_); |
+ // For latency_tests.cc: |
+ UNSHIPPED_TRACE_EVENT_INSTANT0("test_gpu", "CompositorSwapBuffersComplete"); |
+ |
CGLSetCurrentContext(0); |
+ |
+ StartOrContinueDisplayLink(); |
+ |
+ if (!is_vsync_disabled_) |
+ RateLimitDraws(); |
} |
bool CompositingIOSurfaceMac::CopyTo(const gfx::Size& dst_size, void* out) { |
@@ -467,4 +545,64 @@ void CompositingIOSurfaceMac::ClearDrawable() { |
UnrefIOSurface(); |
} |
+void CompositingIOSurfaceMac::DisplayLinkTick(CVDisplayLinkRef display_link, |
+ const CVTimeStamp* output_time) { |
+ base::AutoLock lock(lock_); |
+ // Increment vsync_count but don't let it get ahead of swap_count. |
+ vsync_count_ = std::min(vsync_count_ + 1, swap_count_); |
+ |
+ CalculateVsyncParametersLockHeld(output_time); |
+} |
+ |
+void CompositingIOSurfaceMac::CalculateVsyncParametersLockHeld( |
+ const CVTimeStamp* time) { |
+ vsync_interval_numerator_ = static_cast<uint32>(time->videoRefreshPeriod); |
+ vsync_interval_denominator_ = time->videoTimeScale; |
+ // Verify that videoRefreshPeriod is 32 bits. |
+ DCHECK((time->videoRefreshPeriod & ~0xffffFFFFull) == 0ull); |
+ |
+ vsync_timebase_ = |
+ base::TimeTicks::FromInternalValue(time->hostTime / 1000); |
+} |
+ |
+void CompositingIOSurfaceMac::RateLimitDraws() { |
+ int64 vsync_count; |
+ int64 swap_count; |
+ |
+ { |
+ base::AutoLock lock(lock_); |
+ vsync_count = vsync_count_; |
+ swap_count = ++swap_count_; |
vangelis
2012/08/01 00:53:12
Should these two values be reset somewhere on a su
jbates
2012/08/01 01:00:47
They get reset when the 1 second idle timer fires.
vangelis
2012/08/01 01:33:01
Oh, I see. That makes sense.
|
+ } |
+ |
+ // It's OK for swap_count to get 2 ahead of vsync_count, but any more |
+ // indicates that it has become unthrottled. This happens when, for example, |
+ // the window is obscured by another opaque window. |
+ if (swap_count > vsync_count + 2) { |
+ TRACE_EVENT0("gpu", "CompositingIOSurfaceMac::RateLimitDraws"); |
+ // Sleep for one vsync interval. This will prevent spinning while the window |
+ // is not visible, but will also allow quick recovery when the window |
+ // becomes visible again. |
+ int64 sleep_us = 16666; // default to 60hz if display link API fails. |
+ if (vsync_interval_denominator_ > 0) { |
+ sleep_us = (static_cast<int64>(vsync_interval_numerator_) * 1000000) / |
+ vsync_interval_denominator_; |
+ } |
+ base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(sleep_us)); |
+ } |
+} |
+ |
+void CompositingIOSurfaceMac::StartOrContinueDisplayLink() { |
+ if (!CVDisplayLinkIsRunning(display_link_)) { |
+ vsync_count_ = swap_count_ = 0; |
+ CVDisplayLinkStart(display_link_); |
+ } |
+ display_link_stop_timer_.Reset(); |
+} |
+ |
+void CompositingIOSurfaceMac::StopDisplayLink() { |
+ if (CVDisplayLinkIsRunning(display_link_)) |
+ CVDisplayLinkStop(display_link_); |
+} |
+ |
} // namespace content |