Skip to content
Snippets Groups Projects
Commit 7ff86b93 authored by backer@chromium.org's avatar backer@chromium.org
Browse files

Resize synchronization for Linux.

This patch makes synchronous calls from the GPU to the Browser process to resize windows. It must be synchronous because we must be sure when the resize happens, it must be initiated by the GPU because we have to time the resize with GL drawing, and the resize must be done by the Browser because of how GDK/GTK is structured. Specifically, when a window that a GL context is associated with is resized, the back buffer gets blanked. So it is important that we synchronize the resize with the drawing to the back buffer. On Linux, the X window that we are drawing to is wrapped in a GdkWindow inside the Browser process. GDK/GTK assumes that all changes to the window happen via GDK calls. In particular, the size of the window is cached inside the GdkWindow object so that it does not have to make a call to the X server in order to get window geometry. Unfortunately, this necessitates resizing the window inside of the Browser process.

For more discussion of this approach and (some unsuccessfully attempted) alternatives see https://docs.google.com/a/google.com/document/d/1ZNouL-X_Ml1x8sqy-sofz63pDAeo36VWi_yQihaE2YI/edit?hl=en

This patch set uncovered another bug:
- open in two separate windows http://webkit.org/blog/386/3d-transforms/ and http://webkit.org/blog-files/3d-transforms/poster-circle.html
- resize the former until it is smallish
- watch the root layer of the former show up as the root layer of the later.

To my knowledge, this is first trigger of this bug. If and when this patch is accepted, I will file the bug.

BUG=http://code.google.com/p/chromium/issues/detail?id=54430

TEST=Go to http://peter.sh/2010/06/chromium-now-features-gpu-acceleration-and-css-3d-transforms/ . Rotate Z with the slider to trigger the compositor. Resize the window. The resize may be janky (we're uploading large textures), but it should display properly.

Contributed by backer@chromium.org

Review URL: http://codereview.chromium.org/5105006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67416 0039d316-1c4b-4281-b951-d872f2087c98
parent 49952775
No related merge requests found
Showing
with 140 additions and 11 deletions
......@@ -36,11 +36,6 @@ class GLContext {
// contexts.
virtual bool SwapBuffers() = 0;
// Set the size of the back buffer.
// FIXME(backer): Currently a NOP. Once we have an implementation for each
// backend we can switch it to pure virtual.
virtual void SetSize(gfx::Size) {}
// Get the size of the back buffer.
virtual gfx::Size GetSize() = 0;
......
......@@ -23,8 +23,11 @@
#include "media/base/media_switches.h"
#if defined(OS_LINUX)
#include <gdk/gdkwindow.h>
#include <gdk/gdkx.h>
#include "app/x11_util.h"
#include "gfx/gtk_native_view_id_manager.h"
#include "gfx/size.h"
#endif
namespace {
......@@ -168,6 +171,7 @@ void GpuProcessHost::OnControlMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(GpuHostMsg_SynchronizeReply, OnSynchronizeReply)
#if defined(OS_LINUX)
IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuHostMsg_GetViewXID, OnGetViewXID)
IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuHostMsg_ResizeXID, OnResizeXID)
#elif defined(OS_MACOSX)
IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceSetIOSurface,
OnAcceleratedSurfaceSetIOSurface)
......@@ -221,7 +225,24 @@ void GetViewXIDDispatcher(gfx::NativeViewId id, IPC::Message* reply_msg) {
NewRunnableFunction(&SendDelayedReply, reply_msg));
}
} // namespace
void ResizeXIDDispatcher(unsigned long xid, gfx::Size size,
IPC::Message *reply_msg) {
GdkWindow* window = reinterpret_cast<GdkWindow*>(gdk_xid_table_lookup(xid));
if (window) {
Display* display = GDK_WINDOW_XDISPLAY(window);
gdk_window_resize(window, size.width(), size.height());
XSync(display, False);
}
GpuHostMsg_ResizeXID::WriteReplyParams(reply_msg, (window != NULL));
// Have to reply from IO thread.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableFunction(&SendDelayedReply, reply_msg));
}
} // namespace
void GpuProcessHost::OnGetViewXID(gfx::NativeViewId id,
IPC::Message *reply_msg) {
......@@ -231,6 +252,14 @@ void GpuProcessHost::OnGetViewXID(gfx::NativeViewId id,
NewRunnableFunction(&GetViewXIDDispatcher, id, reply_msg));
}
void GpuProcessHost::OnResizeXID(unsigned long xid, gfx::Size size,
IPC::Message *reply_msg) {
// Have to resize the window from UI thread.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableFunction(&ResizeXIDDispatcher, xid, size, reply_msg));
}
#elif defined(OS_MACOSX)
namespace {
......
......@@ -18,6 +18,10 @@ struct GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params;
class GPUInfo;
class ResourceMessageFilter;
namespace gfx {
class Size;
}
namespace IPC {
struct ChannelHandle;
class Message;
......@@ -82,6 +86,7 @@ class GpuProcessHost : public BrowserChildProcessHost, public NonThreadSafe {
void OnSynchronizeReply();
#if defined(OS_LINUX)
void OnGetViewXID(gfx::NativeViewId id, IPC::Message* reply_msg);
void OnResizeXID(unsigned long xid, gfx::Size size, IPC::Message* reply_msg);
#elif defined(OS_MACOSX)
void OnAcceleratedSurfaceSetIOSurface(
const GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params& params);
......
......@@ -985,6 +985,8 @@ void RenderWidgetHost::OnMsgGpuRenderingActivated(bool activated) {
#if defined(OS_MACOSX)
if (old_state != is_gpu_rendering_active_ && view_)
view_->GpuRenderingStateDidChange();
#elif defined(TOOLKIT_USES_GTK)
view_->AcceleratedCompositingActivated(activated);
#endif
}
......
......@@ -233,6 +233,7 @@ class RenderWidgetHostView {
#if defined(TOOLKIT_USES_GTK)
virtual void CreatePluginContainer(gfx::PluginWindowHandle id) = 0;
virtual void DestroyPluginContainer(gfx::PluginWindowHandle id) = 0;
virtual void AcceleratedCompositingActivated(bool activated) = 0;
#endif
// Toggles visual muting of the render view area. This is on when a
......
......@@ -1046,6 +1046,13 @@ bool RenderWidgetHostViewGtk::ContainsNativeView(
return false;
}
void RenderWidgetHostViewGtk::AcceleratedCompositingActivated(bool activated) {
GtkPreserveWindow* widget =
reinterpret_cast<GtkPreserveWindow*>(view_.get());
gtk_preserve_window_delegate_resize(widget, activated);
}
void RenderWidgetHostViewGtk::ForwardKeyboardEvent(
const NativeWebKeyboardEvent& event) {
if (!host_)
......
......@@ -86,6 +86,7 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView {
virtual void DestroyPluginContainer(gfx::PluginWindowHandle id);
virtual void SetVisuallyDeemphasized(bool deemphasized);
virtual bool ContainsNativeView(gfx::NativeView native_view) const;
virtual void AcceleratedCompositingActivated(bool activated);
gfx::NativeView native_view() const { return view_.get(); }
......
......@@ -126,6 +126,7 @@ class TestRenderWidgetHostView : public RenderWidgetHostView {
#if defined(TOOLKIT_USES_GTK)
virtual void CreatePluginContainer(gfx::PluginWindowHandle id) { }
virtual void DestroyPluginContainer(gfx::PluginWindowHandle id) { }
virtual void AcceleratedCompositingActivated(bool activated) { }
#endif
virtual bool ContainsNativeView(gfx::NativeView native_view) const {
......
......@@ -81,6 +81,12 @@ IPC_BEGIN_MESSAGES(GpuHost)
IPC_SYNC_MESSAGE_CONTROL1_1(GpuHostMsg_GetViewXID,
gfx::NativeViewId, /* view */
unsigned long /* xid */)
IPC_SYNC_MESSAGE_CONTROL2_1(GpuHostMsg_ResizeXID,
unsigned long, /* xid */
gfx::Size, /* size */
bool /* success */)
#elif defined(OS_MACOSX)
// This message, used on Mac OS X 10.6 and later (where IOSurface is
// supported), is sent from the GPU process to the browser to indicate that a
......
......@@ -113,6 +113,14 @@ void GpuCommandBufferStub::OnInitialize(
NewCallback(this,
&GpuCommandBufferStub::SwapBuffersCallback));
}
#elif defined(OS_LINUX)
if (handle_) {
// Set up a pathway to allow the Gpu process to ask the browser
// for a window resize.
processor_->SetResizeCallback(
NewCallback(this,
&GpuCommandBufferStub::ResizeCallback));
}
#endif
} else {
processor_.reset();
......@@ -203,4 +211,13 @@ void GpuCommandBufferStub::SwapBuffersCallback() {
}
#endif // defined(OS_MACOSX)
#if defined(OS_LINUX)
void GpuCommandBufferStub::ResizeCallback(gfx::Size size) {
ChildThread* gpu_thread = ChildThread::current();
bool result = false;
gpu_thread->Send(
new GpuHostMsg_ResizeXID(handle_, size, &result));
}
#endif // defined(OS_LINUX)
#endif // ENABLE_GPU
......@@ -72,6 +72,10 @@ class GpuCommandBufferStub
void SwapBuffersCallback();
#endif
#if defined(OS_LINUX)
void ResizeCallback(gfx::Size size);
#endif
// The lifetime of objects of this class is managed by a GpuChannel. The
// GpuChannels destroy all the GpuCommandBufferStubs that they own when they
// are destroyed. So a raw pointer is safe.
......
......@@ -20,6 +20,10 @@ typedef struct _GtkPreserveWindowPrivate GtkPreserveWindowPrivate;
struct _GtkPreserveWindowPrivate {
// If true, don't create/destroy windows on realize/unrealize.
gboolean preserve_window;
// Whether or not we delegate the resize of the GdkWindow
// to someone else.
gboolean delegate_resize;
};
G_DEFINE_TYPE(GtkPreserveWindow, gtk_preserve_window, GTK_TYPE_FIXED)
......@@ -27,11 +31,14 @@ G_DEFINE_TYPE(GtkPreserveWindow, gtk_preserve_window, GTK_TYPE_FIXED)
static void gtk_preserve_window_destroy(GtkObject* object);
static void gtk_preserve_window_realize(GtkWidget* widget);
static void gtk_preserve_window_unrealize(GtkWidget* widget);
static void gtk_preserve_window_size_allocate(GtkWidget* widget,
GtkAllocation* allocation);
static void gtk_preserve_window_class_init(GtkPreserveWindowClass *klass) {
GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>(klass);
widget_class->realize = gtk_preserve_window_realize;
widget_class->unrealize = gtk_preserve_window_unrealize;
widget_class->size_allocate = gtk_preserve_window_size_allocate;
GtkObjectClass* object_class = reinterpret_cast<GtkObjectClass*>(klass);
object_class->destroy = gtk_preserve_window_destroy;
......@@ -73,6 +80,11 @@ static void gtk_preserve_window_realize(GtkWidget* widget) {
widget->allocation.x,
widget->allocation.y);
widget->style = gtk_style_attach(widget->style, widget->window);
gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
gint event_mask = gtk_widget_get_events(widget);
event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
gdk_window_set_events(widget->window, (GdkEventMask) event_mask);
gdk_window_set_user_data(widget->window, widget);
// Deprecated as of GTK 2.22. Used for compatibility.
......@@ -152,13 +164,31 @@ void gtk_preserve_window_set_preserve(GtkPreserveWindow* window,
attributes.visual = gtk_widget_get_visual(widget);
attributes.colormap = gtk_widget_get_colormap(widget);
attributes.event_mask = gtk_widget_get_events(widget);
attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
widget->window = gdk_window_new(
gdk_get_default_root_window(), &attributes, attributes_mask);
}
}
void gtk_preserve_window_size_allocate(GtkWidget* widget,
GtkAllocation* allocation) {
g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget));
GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
if (priv->delegate_resize) {
// Just update the state. Someone else will gdk_window_resize the
// associated GdkWindow.
widget->allocation = *allocation;
} else {
GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->size_allocate(
widget, allocation);
}
}
void gtk_preserve_window_delegate_resize(GtkPreserveWindow* widget,
gboolean delegate) {
GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
priv->delegate_resize = delegate;
}
G_END_DECLS
......@@ -53,6 +53,12 @@ gboolean gtk_preserve_window_get_preserve(GtkPreserveWindow* widget);
void gtk_preserve_window_set_preserve(GtkPreserveWindow* widget,
gboolean value);
// Whether or not someone else will gdk_window_resize the GdkWindow associated
// with this widget (needed by the GPU process to synchronize resizing
// with swapped between front and back buffer).
void gtk_preserve_window_delegate_resize(GtkPreserveWindow* widget,
gboolean delegate);
G_END_DECLS
#endif // GFX_GTK_PRESERVE_WINDOW_H_
......@@ -677,6 +677,7 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>,
virtual gfx::GLContext* GetGLContext() { return context_.get(); }
virtual ContextGroup* GetContextGroup() { return group_.get(); }
virtual void SetResizeCallback(Callback1<gfx::Size>::Type* callback);
virtual void SetSwapBuffersCallback(Callback0::Type* callback);
virtual bool GetServiceTextureId(uint32 client_texture_id,
uint32* service_texture_id);
......@@ -1424,6 +1425,7 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>,
scoped_ptr<Texture> offscreen_saved_color_texture_;
GLenum offscreen_saved_color_format_;
scoped_ptr<Callback1<gfx::Size>::Type> resize_callback_;
scoped_ptr<Callback0::Type> swap_buffers_callback_;
// The last error message set.
......@@ -2422,6 +2424,10 @@ bool GLES2DecoderImpl::UpdateOffscreenFrameBufferSize() {
return true;
}
void GLES2DecoderImpl::SetResizeCallback(Callback1<gfx::Size>::Type* callback) {
resize_callback_.reset(callback);
}
void GLES2DecoderImpl::SetSwapBuffersCallback(Callback0::Type* callback) {
swap_buffers_callback_.reset(callback);
}
......@@ -2552,8 +2558,14 @@ void GLES2DecoderImpl::DoCopyTextureToParentTextureCHROMIUM(
}
void GLES2DecoderImpl::DoResizeCHROMIUM(GLuint width, GLuint height) {
gfx::Size size(width, height);
context_->SetSize(size);
#if defined(OS_LINUX)
// Make sure that we are done drawing to the back buffer before resizing.
glFinish();
#endif
if (resize_callback_.get()) {
gfx::Size size(width, height);
resize_callback_->Run(size);
}
}
const char* GLES2DecoderImpl::GetCommandName(unsigned int command_id) const {
......
......@@ -89,6 +89,10 @@ class GLES2Decoder : public CommonDecoder {
// Gets the associated ContextGroup
virtual ContextGroup* GetContextGroup() = 0;
// Sets a callback which is called when a glResizeCHROMIUM command
// is processed.
virtual void SetResizeCallback(Callback1<gfx::Size>::Type* callback) = 0;
// Sets a callback which is called when a SwapBuffers command is processed.
virtual void SetSwapBuffersCallback(Callback0::Type* callback) = 0;
......
......@@ -44,6 +44,7 @@ class MockGLES2Decoder : public GLES2Decoder {
MOCK_METHOD0(GetGLES2Util, GLES2Util*());
MOCK_METHOD0(GetGLContext, gfx::GLContext*());
MOCK_METHOD0(GetContextGroup, ContextGroup*());
MOCK_METHOD1(SetResizeCallback, void(Callback1<gfx::Size>::Type*));
MOCK_METHOD1(SetSwapBuffersCallback, void(Callback0::Type*));
MOCK_METHOD3(DoCommand, error::Error(unsigned int command,
unsigned int arg_count,
......
......@@ -145,6 +145,10 @@ void GPUProcessor::ResizeOffscreenFrameBuffer(const gfx::Size& size) {
decoder_->ResizeOffscreenFrameBuffer(size);
}
void GPUProcessor::SetResizeCallback(Callback1<gfx::Size>::Type* callback) {
decoder_->SetResizeCallback(callback);
}
void GPUProcessor::SetSwapBuffersCallback(
Callback0::Type* callback) {
wrapped_swap_buffers_callback_.reset(callback);
......
......@@ -87,6 +87,10 @@ class GPUProcessor : public CommandBufferEngine {
virtual uint64 GetSurfaceId();
#endif
// Sets a callback that is called when a glResizeCHROMIUM command
// is processed.
virtual void SetResizeCallback(Callback1<gfx::Size>::Type* callback);
// Sets a callback which is called when a SwapBuffers command is processed.
// Must be called after Initialize().
// It is not defined on which thread this callback is called.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment