summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-13 22:39:54 +0000
committerccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-13 22:39:54 +0000
commit5fd6b99e8bec88987c263b9aa68845142b21d6b5 (patch)
tree5b034d164dd0e05fd0e6ccf434beb82d2fa6aec5
parent0b230c049e316979d986e8ac93a313a10f7df30c (diff)
downloadchromium_src-5fd6b99e8bec88987c263b9aa68845142b21d6b5.zip
chromium_src-5fd6b99e8bec88987c263b9aa68845142b21d6b5.tar.gz
chromium_src-5fd6b99e8bec88987c263b9aa68845142b21d6b5.tar.bz2
Destroy GLX windows when they are backgrounded
Because GLX windows cannot be destroyed separately from their X windows on all platforms, create a separate child X window to use with GLX. Destroy this child X window when the backbuffer for the window is no longer needed. Because the GL surface may need to be made current while its backbuffer is destroyed (e.g, to destroy GL resources for a backgrounded tab), create a dummy 1x1 GL surface which is never destroyed and can always be made current. Because the child X window created will cover its parent, create an event listener to forward expose events from the child window to the parent, so that the parent can know to repaint. BUG=145600 Review URL: https://chromiumcodereview.appspot.com/23452026 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223148 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/common/gpu/gpu_memory_manager.h2
-rw-r--r--ui/gl/gl_surface_glx.cc192
-rw-r--r--ui/gl/gl_surface_glx.h36
3 files changed, 215 insertions, 15 deletions
diff --git a/content/common/gpu/gpu_memory_manager.h b/content/common/gpu/gpu_memory_manager.h
index 415d127..9b2de82 100644
--- a/content/common/gpu/gpu_memory_manager.h
+++ b/content/common/gpu/gpu_memory_manager.h
@@ -28,7 +28,7 @@ class GpuMemoryTrackingGroup;
class CONTENT_EXPORT GpuMemoryManager :
public base::SupportsWeakPtr<GpuMemoryManager> {
public:
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
enum { kDefaultMaxSurfacesWithFrontbufferSoftLimit = 1 };
#else
enum { kDefaultMaxSurfacesWithFrontbufferSoftLimit = 8 };
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc
index 8b705df..04e1589 100644
--- a/ui/gl/gl_surface_glx.cc
+++ b/ui/gl/gl_surface_glx.cc
@@ -10,6 +10,7 @@ extern "C" {
#include "base/basictypes.h"
#include "base/debug/trace_event.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -39,7 +40,7 @@ class ScopedPtrXFree {
}
};
-Display* g_display;
+Display* g_display = NULL;
const char* g_glx_extensions = NULL;
bool g_glx_context_create = false;
bool g_glx_create_context_robustness_supported = false;
@@ -299,6 +300,69 @@ SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
// goes up (rather than on-demand when we start the thread).
Display* SGIVideoSyncProviderThreadShim::display_ = NULL;
+#if defined(TOOLKIT_GTK)
+// A mechanism for forwarding XExpose events from one window to another.
+// Because in the workaround for http://crbug.com/145600 the child window
+// is placed on top of the parent window, only the child window will receive
+// all expose events. These need to be forwared to the parent window to inform
+// it that it should paint.
+class XExposeEventForwarder : public base::MessagePumpObserver {
+ public:
+ XExposeEventForwarder() {}
+ virtual ~XExposeEventForwarder() {
+ DCHECK(child_to_parent_map_.empty());
+ }
+ void AddParentChildPair(gfx::AcceleratedWidget parent_window,
+ gfx::AcceleratedWidget child_window) {
+ if (child_to_parent_map_.empty())
+ base::MessagePumpX11::Current()->AddObserver(this);
+
+ DCHECK(child_to_parent_map_.find(child_window) ==
+ child_to_parent_map_.end());
+ child_to_parent_map_.insert(std::make_pair(
+ child_window, parent_window));
+ }
+ void RemoveParentChildPair(gfx::AcceleratedWidget parent_window,
+ gfx::AcceleratedWidget child_window) {
+ DCHECK(child_to_parent_map_.find(child_window) !=
+ child_to_parent_map_.end());
+ child_to_parent_map_.erase(child_window);
+
+ if (child_to_parent_map_.empty())
+ base::MessagePumpX11::Current()->RemoveObserver(this);
+ }
+
+ private:
+ virtual base::EventStatus WillProcessEvent (
+ const base::NativeEvent& xevent) OVERRIDE {
+ if (xevent->type != Expose)
+ return base::EVENT_CONTINUE;
+
+ WindowMap::const_iterator found = child_to_parent_map_.find(
+ xevent->xexpose.window);
+ if (found == child_to_parent_map_.end())
+ return base::EVENT_CONTINUE;
+
+ gfx::AcceleratedWidget target_window = found->second;
+ XEvent forwarded_event = *xevent;
+ forwarded_event.xexpose.window = target_window;
+ XSendEvent(g_display, target_window, False, ExposureMask,
+ &forwarded_event);
+ return base::EVENT_CONTINUE;
+ }
+ virtual void DidProcessEvent(const base::NativeEvent& xevent) OVERRIDE {
+ }
+
+ typedef std::map<gfx::AcceleratedWidget, gfx::AcceleratedWidget> WindowMap;
+ WindowMap child_to_parent_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(XExposeEventForwarder);
+};
+
+static base::LazyInstance<XExposeEventForwarder> g_xexpose_event_forwarder =
+ LAZY_INSTANCE_INITIALIZER;
+#endif
+
} // namespace
GLSurfaceGLX::GLSurfaceGLX() {}
@@ -315,7 +379,17 @@ bool GLSurfaceGLX::InitializeOneOff() {
// it's own thread.
XInitThreads();
+#if defined(TOOLKIT_GTK)
+ // Be sure to use the X display handle and not the GTK display handle if this
+ // is the GPU process.
+ if (base::MessageLoop::current()->type() == base::MessageLoop::TYPE_GPU)
+ g_display = base::MessagePumpX11::GetDefaultXDisplay();
+ else
+ g_display = base::MessagePumpForUI::GetDefaultXDisplay();
+#else
g_display = base::MessagePumpForUI::GetDefaultXDisplay();
+#endif
+
if (!g_display) {
LOG(ERROR) << "XOpenDisplay failed.";
return false;
@@ -388,31 +462,119 @@ void* GLSurfaceGLX::GetDisplay() {
GLSurfaceGLX::~GLSurfaceGLX() {}
+#if defined(TOOLKIT_GTK)
+bool NativeViewGLSurfaceGLX::SetBackbufferAllocation(bool allocated) {
+ backbuffer_allocated_ = allocated;
+ AdjustBufferAllocation();
+ return true;
+}
+
+void NativeViewGLSurfaceGLX::SetFrontbufferAllocation(bool allocated) {
+ frontbuffer_allocated_ = allocated;
+ AdjustBufferAllocation();
+}
+
+void NativeViewGLSurfaceGLX::AdjustBufferAllocation() {
+ if (frontbuffer_allocated_ || backbuffer_allocated_)
+ CreateChildWindow();
+ else
+ DestroyChildWindow();
+}
+
+void NativeViewGLSurfaceGLX::CreateChildWindow() {
+ if (child_window_)
+ return;
+
+ XSetWindowAttributes set_window_attributes;
+ set_window_attributes.event_mask = ExposureMask;
+ child_window_ = XCreateWindow(
+ g_display, parent_window_, 0, 0, size_.width(), size_.height(), 0,
+ CopyFromParent, InputOutput, CopyFromParent, CWEventMask,
+ &set_window_attributes);
+ g_xexpose_event_forwarder.Pointer()->AddParentChildPair(
+ parent_window_, child_window_);
+
+ XMapWindow(g_display, child_window_);
+ XFlush(g_display);
+}
+
+void NativeViewGLSurfaceGLX::DestroyChildWindow() {
+ if (!child_window_)
+ return;
+
+ g_xexpose_event_forwarder.Pointer()->RemoveParentChildPair(
+ parent_window_, child_window_);
+ XDestroyWindow(g_display, child_window_);
+ XFlush(g_display);
+ child_window_ = 0;
+}
+#endif
+
NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
- : window_(window),
+ : parent_window_(window),
+#if defined(TOOLKIT_GTK)
+ child_window_(0),
+ dummy_window_(0),
+ backbuffer_allocated_(true),
+ frontbuffer_allocated_(true),
+#endif
config_(NULL) {
}
+gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
+#if defined(TOOLKIT_GTK)
+ if (child_window_)
+ return child_window_;
+ return dummy_window_;
+#else
+ return parent_window_;
+#endif
+}
+
bool NativeViewGLSurfaceGLX::Initialize() {
XWindowAttributes attributes;
- if (!XGetWindowAttributes(g_display, window_, &attributes)) {
- LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << ".";
+ if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
+ LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
+ << ".";
return false;
}
size_ = gfx::Size(attributes.width, attributes.height);
+ gfx::AcceleratedWidget window_for_vsync = parent_window_;
+
+#if defined(TOOLKIT_GTK)
+ dummy_window_ = XCreateWindow(
+ g_display,
+ RootWindow(g_display, XScreenNumberOfScreen(attributes.screen)),
+ 0, 0, 1, 1, 0, CopyFromParent, InputOutput, attributes.visual, 0, NULL);
+ window_for_vsync = dummy_window_;
+ CreateChildWindow();
+#endif
+
if (g_glx_oml_sync_control_supported)
- vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_));
+ vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
else if (g_glx_sgi_video_sync_supported)
- vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_));
+ vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));
return true;
}
void NativeViewGLSurfaceGLX::Destroy() {
+#if defined(TOOLKIT_GTK)
+ DestroyChildWindow();
+ if (dummy_window_)
+ XDestroyWindow(g_display, dummy_window_);
+ dummy_window_ = 0;
+#endif
}
bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
+#if defined(TOOLKIT_GTK)
+ if (child_window_) {
+ XResizeWindow(g_display, child_window_, size.width(), size.height());
+ XFlush(g_display);
+ }
+#endif
size_ = size;
return true;
}
@@ -422,7 +584,7 @@ bool NativeViewGLSurfaceGLX::IsOffscreen() {
}
bool NativeViewGLSurfaceGLX::SwapBuffers() {
- glXSwapBuffers(g_display, window_);
+ glXSwapBuffers(g_display, GetDrawableHandle());
return true;
}
@@ -431,7 +593,7 @@ gfx::Size NativeViewGLSurfaceGLX::GetSize() {
}
void* NativeViewGLSurfaceGLX::GetHandle() {
- return reinterpret_cast<void*>(window_);
+ return reinterpret_cast<void*>(GetDrawableHandle());
}
std::string NativeViewGLSurfaceGLX::GetExtensions() {
@@ -460,10 +622,10 @@ void* NativeViewGLSurfaceGLX::GetConfig() {
XWindowAttributes attributes;
if (!XGetWindowAttributes(
g_display,
- window_,
+ parent_window_,
&attributes)) {
LOG(ERROR) << "XGetWindowAttributes failed for window " <<
- window_ << ".";
+ parent_window_ << ".";
return NULL;
}
@@ -507,7 +669,7 @@ void* NativeViewGLSurfaceGLX::GetConfig() {
bool NativeViewGLSurfaceGLX::PostSubBuffer(
int x, int y, int width, int height) {
DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
- glXCopySubBufferMESA(g_display, window_, x, y, width, height);
+ glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
return true;
}
@@ -516,7 +678,13 @@ VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
}
NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
- : window_(0),
+ : parent_window_(0),
+#if defined(TOOLKIT_GTK)
+ child_window_(0),
+ dummy_window_(0),
+ backbuffer_allocated_(true),
+ frontbuffer_allocated_(true),
+#endif
config_(NULL) {
}
diff --git a/ui/gl/gl_surface_glx.h b/ui/gl/gl_surface_glx.h
index 355b65d..8e7bd1c 100644
--- a/ui/gl/gl_surface_glx.h
+++ b/ui/gl/gl_surface_glx.h
@@ -69,9 +69,41 @@ class GL_EXPORT NativeViewGLSurfaceGLX : public GLSurfaceGLX {
NativeViewGLSurfaceGLX();
virtual ~NativeViewGLSurfaceGLX();
- gfx::AcceleratedWidget window_;
-
private:
+ // The handle for the drawable to make current or swap.
+ gfx::AcceleratedWidget GetDrawableHandle() const;
+
+ // Window passed in at creation. Always valid.
+ gfx::AcceleratedWidget parent_window_;
+
+#if defined(TOOLKIT_GTK)
+ // Some NVIDIA drivers don't allow deleting GLX windows separately from their
+ // parent X windows. Work around this by creating a child X window to the
+ // window passed in to the constructor, creating the GLX window against the
+ // child window, and then destroying the child window to destroy the GLX
+ // window.
+ // http://crbug.com/145600
+ void CreateChildWindow();
+ void DestroyChildWindow();
+
+ // Destroy the child window when both the front and back buffers are
+ // deallocated.
+ virtual bool SetBackbufferAllocation(bool allocated) OVERRIDE;
+ virtual void SetFrontbufferAllocation(bool allocated) OVERRIDE;
+ void AdjustBufferAllocation();
+
+ // Child window which is used with GLX, and is discarded when it is
+ // backgrounded.
+ gfx::AcceleratedWidget child_window_;
+
+ // Dummy 1x1 window which is supplied to glXMakeCurrent when making
+ // the context current while its output surface is destroyed.
+ gfx::AcceleratedWidget dummy_window_;
+
+ bool backbuffer_allocated_;
+ bool frontbuffer_allocated_;
+#endif
+
void* config_;
gfx::Size size_;