Nux

Merge lp:~smspillaz/nux/nux.fix_1091589.1 into lp:nux

Proposed by Sam Spilsbury
Status: Superseded
Proposed branch: lp:~smspillaz/nux/nux.fix_1091589.1
Merge into: lp:nux
Diff against target: 1795 lines (+1136/-125)
16 files modified
Nux/BaseWindow.cpp (+41/-0)
Nux/BaseWindow.h (+16/-0)
Nux/View.cpp (+7/-4)
Nux/WindowCompositor.cpp (+222/-53)
Nux/WindowCompositor.h (+18/-3)
Nux/WindowThread.cpp (+110/-8)
Nux/WindowThread.h (+50/-1)
NuxGraphics/GLDeviceFrameBufferObject.cpp (+20/-5)
NuxGraphics/GpuDevice.cpp (+7/-1)
NuxGraphics/GpuDeviceTexture.cpp (+7/-1)
NuxGraphics/IOpenGLFrameBufferObject.cpp (+7/-1)
configure.ac (+2/-3)
debian/changelog (+6/-0)
tests/gtest-nux-windowcompositor.cpp (+110/-44)
tests/gtest-nux-windowthread.cpp (+496/-0)
tests/gtest-nuxgraphics-texture.cpp (+17/-1)
To merge this branch: bzr merge lp:~smspillaz/nux/nux.fix_1091589.1
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Brandon Schaefer (community) Approve
Stephen M. Webb (community) Needs Fixing
Review via email: mp+165772@code.launchpad.net

This proposal supersedes a proposal from 2013-02-10.

This proposal has been superseded by a proposal from 2013-10-30.

Commit message

Allow embedded clients to specify which windows should be redrawn.

Added some new API to nux to allow embedded clients (eg, compiz) to specify which BaseWindows should be redrawn on a frame. This is done by passing a list of rectangles to nux which represent the redraw region, and nux will mark all intersecting windows as needing re-presenting to the screen. This means that we aren't re-presenting every window to the screen on every frame (bad for performance, and also doesn't work correctly in the buffer-swap case).

Also added API to allow nux to tell the difference between draw and read framebuffers when it plays around with the framebuffer object binding. This is necessary in case the embedded client expects the read framebuffer binding to remain defined even after nux has changed the draw framebuffer binding.

(LP: #1091589)

Description of the change

Allow embedded clients to specify which windows should be redrawn.

Added some new API to nux to allow embedded clients (eg, compiz) to specify which BaseWindows should be redrawn on a frame. This is done by passing a list of rectangles to nux which represent the redraw region, and nux will mark all intersecting windows as needing re-presenting to the screen. This means that we aren't re-presenting every window to the screen on every frame (bad for performance, and also doesn't work correctly in the buffer-swap case).

Also added API to allow nux to tell the difference between draw and read framebuffers when it plays around with the framebuffer object binding. This is necessary in case the embedded client expects the read framebuffer binding to remain defined even after nux has changed the draw framebuffer binding.

(LP: #1091589)

Both APIs are fully tested.

Update: test results here: http://www.ucc.asn.au/~smspillaz/phoronix-test-suite/composite.xml

I was careful to modify phoronix-test-suite to run tests in windowed mode only, and those were the only three tests I was able to get to run in windowed mode. Of particular interest was the fact that the unigine demos had a 5x performance improvement, probably because the driver was spending less time filling redundant pixels from the compositor. In other areas we had a roughly 5FPS boost.

Compiz framerate graph:

http://www.ucc.asn.au/~smspillaz/compiz-perf-graph.png

You'll see that especially on the last test, it drops off quite a bit on the non buffer_age case, and is generally speaking lower across the board.

To post a comment you must log in.
Revision history for this message
Andrea Azzarone (azzar1) wrote : Posted in a previous version of this proposal

39 + void BaseWindow::WasPresentedInEmbeddedMode()
40 + {
41 + _present_in_embedded_mode = false;
42 + _last_presented_geometry_in_embedded_mode = GetAbsoluteGeometry();
43 + }

Can we try to find a better name for this function? WasPresented* suggests me that the function should return a bool value.

45 + nux::Geometry const& BaseWindow::LastPresentedGeometryInEmbeddedMode()
46 + {
47 + return _last_presented_geometry_in_embedded_mode;
48 + }
49 +
50 + bool BaseWindow::AllowPresentationInEmbeddedMode()
51 + {
52 + return _present_in_embedded_mode;
53 + }

Can these methods be const?

 + for (WindowList::iterator it = _view_window_list.begin();
145 + it != _view_window_list.end();
146 + ++it)
147 + {
148 + if (it->IsValid())
149 + func (*it);
150 + }

Why not a range-based for loop? :)

206 + void WindowCompositor::SetReferenceFramebuffer(unsigned int draw_fbo_object,
207 + unsigned int read_fbo_object,
208 + Geometry fbo_geometry)

Geometry const& fbo... ?

374 + WindowCompositor::WeakBaseWindowPtr ptr;
375 + window_compositor_->OnAllBaseWindows(std::bind(AssignWeakBaseWindowMatchingRaw, _1, bw, &ptr));

It took me a while to understand what this code was for, maybe we can make it more readable.

406 + for (std::vector<WindowCompositor::WeakBaseWindowPtr>::iterator it =
407 + m_presentation_list_embedded.begin();
408 + it != m_presentation_list_embedded.end();
409 + ++it)
410 + {

For range-based loop... :)

Btw logic looks good to me.

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Agree with all points. Will fix later today
On 26/03/2013 8:50 AM, "Andrea Azzarone" <email address hidden> wrote:

> 39 + void BaseWindow::WasPresentedInEmbeddedMode()
> 40 + {
> 41 + _present_in_embedded_mode = false;
> 42 + _last_presented_geometry_in_embedded_mode =
> GetAbsoluteGeometry();
> 43 + }
>
> Can we try to find a better name for this function? WasPresented* suggests
> me that the function should return a bool value.
>
> 45 + nux::Geometry const&
> BaseWindow::LastPresentedGeometryInEmbeddedMode()
> 46 + {
> 47 + return _last_presented_geometry_in_embedded_mode;
> 48 + }
> 49 +
> 50 + bool BaseWindow::AllowPresentationInEmbeddedMode()
> 51 + {
> 52 + return _present_in_embedded_mode;
> 53 + }
>
> Can these methods be const?
>
> + for (WindowList::iterator it = _view_window_list.begin();
> 145 + it != _view_window_list.end();
> 146 + ++it)
> 147 + {
> 148 + if (it->IsValid())
> 149 + func (*it);
> 150 + }
>
> Why not a range-based for loop? :)
>
> 206 + void WindowCompositor::SetReferenceFramebuffer(unsigned int
> draw_fbo_object,
> 207 + unsigned int
> read_fbo_object,
> 208 + Geometry
> fbo_geometry)
>
> Geometry const& fbo... ?
>
> 374 + WindowCompositor::WeakBaseWindowPtr ptr;
> 375 +
> window_compositor_->OnAllBaseWindows(std::bind(AssignWeakBaseWindowMatchingRaw,
> _1, bw, &ptr));
>
> It took me a while to understand what this code was for, maybe we can make
> it more readable.
>
> 406 + for
> (std::vector<WindowCompositor::WeakBaseWindowPtr>::iterator it =
> 407 + m_presentation_list_embedded.begin();
> 408 + it != m_presentation_list_embedded.end();
> 409 + ++it)
> 410 + {
>
> For range-based loop... :)
>
> Btw logic looks good to me.
> --
> https://code.launchpad.net/~smspillaz/nux/nux.fix_1091589/+merge/147543
> You are the owner of lp:~smspillaz/nux/nux.fix_1091589.
>

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Just the only points warranting some discussion:

On Tue, Mar 26, 2013 at 8:50 AM, Andrea Azzarone <email address hidden> wrote:
> 39 + void BaseWindow::WasPresentedInEmbeddedMode()
> 40 + {
> 41 + _present_in_embedded_mode = false;
> 42 + _last_presented_geometry_in_embedded_mode = GetAbsoluteGeometry();
> 43 + }
>
> Can we try to find a better name for this function? WasPresented* suggests me that the function should return a bool value.

Yeah, I remember when I read through this code later I was suprised it didn't.

How about OnPresentedInEmbeddedMode (); ?

>
> 374 + WindowCompositor::WeakBaseWindowPtr ptr;
> 375 + window_compositor_->OnAllBaseWindows(std::bind(AssignWeakBaseWindowMatchingRaw, _1, bw, &ptr));
>
> It took me a while to understand what this code was for, maybe we can make it more readable.
>
>

Yeah, it could get a bit like that for a codebase that's not really
designed around callbacks. It might actually make sense to wrap this
up in a helper function:

nux::WindowCompositor::WeakBaseWindowPtr const&
nux::WindowCompositor::FindWeakBaseWindowPtrForRawPtr (nux::BaseWindow
*);

Then the helper function could look something like:

window_compositor_->OnAllBaseWindows(std::bind(AssignWeakBaseWindowPtrIfMatchesRawPtr,
_1, bw, &ptr));

Its basically a glorified:

nux::WindowCompositor::WeakBaseWindowPtr weak;

for (nux::WeakBaseWindowPtr const& ptr : weak_base_window)
    if (ptr.get () == raw)
    {
        weak = ptr;
        break;
    }

Having it this way would have been preferable, but then it would have
meant exposing the internals of WindowCompositor to BaseWindow, which
was something I wanted to avoid (nux already exposes too many
internals to clients).

What do you think?

--
Sam Spilsbury

Revision history for this message
Andrea Azzarone (azzar1) wrote : Posted in a previous version of this proposal

> Just the only points warranting some discussion:
>
> On Tue, Mar 26, 2013 at 8:50 AM, Andrea Azzarone <email address hidden> wrote:
> > 39 + void BaseWindow::WasPresentedInEmbeddedMode()
> > 40 + {
> > 41 + _present_in_embedded_mode = false;
> > 42 + _last_presented_geometry_in_embedded_mode =
> GetAbsoluteGeometry();
> > 43 + }
> >
> > Can we try to find a better name for this function? WasPresented* suggests
> me that the function should return a bool value.
>
> Yeah, I remember when I read through this code later I was suprised it didn't.
>
> How about OnPresentedInEmbeddedMode (); ?

Yeah sounds good to me.

>
> >
> > 374 + WindowCompositor::WeakBaseWindowPtr ptr;
> > 375 + window_compositor_->OnAllBaseWindows(std::bind(AssignWeakBaseWi
> ndowMatchingRaw, _1, bw, &ptr));
> >
> > It took me a while to understand what this code was for, maybe we can make
> it more readable.
> >
> >
>
> Yeah, it could get a bit like that for a codebase that's not really
> designed around callbacks. It might actually make sense to wrap this
> up in a helper function:
>
> nux::WindowCompositor::WeakBaseWindowPtr const&
> nux::WindowCompositor::FindWeakBaseWindowPtrForRawPtr (nux::BaseWindow
> *);
>
> Then the helper function could look something like:
>
> window_compositor_->OnAllBaseWindows(std::bind(AssignWeakBaseWindowPtrIfMatche
> sRawPtr,
> _1, bw, &ptr));
>
> Its basically a glorified:
>
> nux::WindowCompositor::WeakBaseWindowPtr weak;
>
> for (nux::WeakBaseWindowPtr const& ptr : weak_base_window)
> if (ptr.get () == raw)
> {
> weak = ptr;
> break;
> }
>
> Having it this way would have been preferable, but then it would have
> meant exposing the internals of WindowCompositor to BaseWindow, which
> was something I wanted to avoid (nux already exposes too many
> internals to clients).
>
> What do you think?
>

Ok just never mind. That comment was/is not blocking the MP ;)

>
>
> --
> Sam Spilsbury

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Okay, so I've talked to Didier about this and we've come up with a small plan of action. We'll need to give this a week solid of QA at the beginning of the S cycle and then make sure that we don't have any AP regressions. That'll happen in about two weeks.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
MC Return (mc-return) wrote :

News:
http://support.amd.com/us/kbarticles/Pages/amdcatalyst13-6linbetadriver.aspx

AMD's fglrx 13.6 Beta driver now also supports GLX_EXT_buffer_age extension !!! \o/

Revision history for this message
Stephen M. Webb (bregma) wrote :

This is (obviously) an ABI-breaking change. We need to bump the ABI string, SONAME, package version, and debian/changelog upstream package version at the same time.

This patchset should also include changing configure.ac to bump nux_micro_version to 3, nux_abi_version to, say, 20130530.0, and create a new UNRELEASED entry in debian/changelog for 4.0.3-0ubuntu1.

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Okay.

I think I've been bumping the abi in configure.ac for some time now, but
I'll bump the other bits too - I'll be about 2 days with that though as uni
is keeping me busy. Is that OK?
On 29/05/2013 9:51 PM, "Stephen M. Webb" <email address hidden> wrote:

> Review: Needs Fixing
>
> This is (obviously) an ABI-breaking change. We need to bump the ABI
> string, SONAME, package version, and debian/changelog upstream package
> version at the same time.
>
> This patchset should also include changing configure.ac to bump
> nux_micro_version to 3, nux_abi_version to, say, 20130530.0, and create a
> new UNRELEASED entry in debian/changelog for 4.0.3-0ubuntu1.
>
> --
> https://code.launchpad.net/~smspillaz/nux/nux.fix_1091589.1/+merge/165772
> You are the owner of lp:~smspillaz/nux/nux.fix_1091589.1.
>

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Thanks for the feedback, done.

lp:~smspillaz/nux/nux.fix_1091589.1 updated
793. By Sam Spilsbury

Bump ABI version and debian/changelog version

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

I like this function :)
183 + void WindowCompositor::OnAllBaseWindows(const WindowMutatorFunc &func)

Very functional style! One suggestion would be a different name, possibly. MapFunctionOnAllBaseWindows, as what you are doing a specific higher order function. (Look up map function if yo want :)

547 + if (std::find (m_presentation_list_embedded.begin(),
548 + m_presentation_list_embedded.end(),
549 + ptr) != m_presentation_list_embedded.end())
550 + return true;

Might I suggest a simple utility function that will do this? That way you don't have to repeat your self twice and clean that up a bit...(though thats just being nit picky...)

866 + * This is really important. Don't remove it */
867 + result->_OpenGLID = id;

Thats like don't press this button! Now I want to remove it!! (joke no problem here)

No real *needs fixing* just some suggestions. Looking at actually running the branch now :)

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

On 11/06/2013 7:00 AM, "Brandon Schaefer" <email address hidden>
wrote:
>
> I like this function :)
> 183 + void WindowCompositor::OnAllBaseWindows(const WindowMutatorFunc
&func)
>
> Very functional style! One suggestion would be a different name,
possibly. MapFunctionOnAllBaseWindows, as what you are doing a specific
higher order function. (Look up map function if yo want :)

I could, although not everyone knows about map/reduce style. I think the
former is a bit clearer.

>
> 547 + if (std::find (m_presentation_list_embedded.begin(),
> 548 + m_presentation_list_embedded.end(),
> 549 + ptr) != m_presentation_list_embedded.end())
> 550 + return true;
>
> Might I suggest a simple utility function that will do this? That way you
don't have to repeat your self twice and clean that up a bit...(though
thats just being nit picky...)

Doable.

>
> 866 + * This is really important. Don't remove it */
> 867 + result->_OpenGLID = id;
>
> Thats like don't press this button! Now I want to remove it!! (joke no
problem here)

Someone removed it a while ago in a cleanup and cost me a day or two
debugging it. I think there's a test in place for it now.

>
> No real *needs fixing* just some suggestions. Looking at actually running
the branch now :)
>
> --
> https://code.launchpad.net/~smspillaz/nux/nux.fix_1091589.1/+merge/165772
> You are the owner of lp:~smspillaz/nux/nux.fix_1091589.1.

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Cool, well we need to coordinate merging this at the same time as the unity branch, otherwise we are good to go on this branch. Marking as approved, but we need to wait to approved globally until the unity branch is ready :)

review: Approve
lp:~smspillaz/nux/nux.fix_1091589.1 updated
794. By Sam Spilsbury

Merge lp:nux

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~smspillaz/nux/nux.fix_1091589.1 updated
795. By Sam Spilsbury

Split presentation and render into separate functions, call the present
function on any rendered-windows as soon as we need to restore the reference
framebuffer - this will ensure that should we need to do that we can
use the contents of already drawn BaseWindows in the backbuffer as some
part of unity expect.

796. By Sam Spilsbury

Cleanup

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~smspillaz/nux/nux.fix_1091589.1 updated
797. By Sam Spilsbury

Merge lp:nux

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Nux/BaseWindow.cpp'
2--- Nux/BaseWindow.cpp 2013-04-08 14:02:32 +0000
3+++ Nux/BaseWindow.cpp 2013-07-22 17:20:34 +0000
4@@ -47,6 +47,8 @@
5 : View(NUX_FILE_LINE_PARAM)
6 , _paint_layer(new ColorLayer(Color(0xFF707070)))
7 , _opacity(1.0f)
8+ , _present_in_embedded_mode(false)
9+ , _contents_ready_for_presentation(false)
10 {
11 premultiply = true;
12 _name = WindowName;
13@@ -397,6 +399,8 @@
14 }
15 else
16 {
17+ SetEnterFocusInputArea(NULL);
18+
19 _entering_hidden_state = true;
20 sigHidden.emit(this);
21 GetWindowThread()->GetWindowCompositor().sigHiddenViewWindow.emit(this);
22@@ -490,6 +494,43 @@
23 return GetWindowThread()->GetWindowCompositor().GetBackupTextureData(this, width, height, format);
24 }
25
26+ void BaseWindow::PresentInEmbeddedModeOnThisFrame(bool force)
27+ {
28+ nuxAssertMsg (GetWindowThread()->IsEmbeddedWindow(),
29+ "[BaseWindow::PresentInEmbeddedModeOnThisFrame] only "
30+ "supported in embdded mode");
31+
32+ /* Invisible windows are never presented */
33+ if (!IsVisible())
34+ return;
35+
36+ if (nux::GetWindowThread()->AddToPresentationList(this, force))
37+ _present_in_embedded_mode = true;
38+ }
39+
40+ void BaseWindow::OnPresentedInEmbeddedMode()
41+ {
42+ _present_in_embedded_mode = false;
43+ _last_presented_geometry_in_embedded_mode = GetAbsoluteGeometry();
44+ }
45+
46+ nux::Geometry const& BaseWindow::LastPresentedGeometryInEmbeddedMode() const
47+ {
48+ return _last_presented_geometry_in_embedded_mode;
49+ }
50+
51+ bool BaseWindow::AllowPresentationInEmbeddedMode() const
52+ {
53+ return _present_in_embedded_mode;
54+ }
55+
56+ void BaseWindow::PrepareParentRedirectedView()
57+ {
58+ Area::PrepareParentRedirectedView();
59+ if (GetWindowThread()->IsEmbeddedWindow())
60+ PresentInEmbeddedModeOnThisFrame();
61+ }
62+
63 void BaseWindow::SetEnterFocusInputArea(InputArea *input_area)
64 {
65 if (_enter_focus_input_area)
66
67=== modified file 'Nux/BaseWindow.h'
68--- Nux/BaseWindow.h 2012-11-12 20:59:56 +0000
69+++ Nux/BaseWindow.h 2013-07-22 17:20:34 +0000
70@@ -188,6 +188,14 @@
71 return _name;
72 }
73
74+ bool RedrawnContentsPendingPresentation();
75+ void PresentInEmbeddedModeOnThisFrame(bool force = false);
76+ void OnPresentedInEmbeddedMode();
77+ bool AllowPresentationInEmbeddedMode() const;
78+ nux::Geometry const& LastPresentedGeometryInEmbeddedMode() const;
79+
80+ void PrepareParentRedirectedView();
81+
82 #ifdef NUX_GESTURES_SUPPORT
83 virtual Area* GetInputAreaHitByGesture(const nux::GestureEvent &event);
84 #endif
85@@ -261,6 +269,14 @@
86 bool _child_need_redraw; //!<True is there is a child of the BaseWindow that needs to be redrawn;
87 float _opacity;
88
89+ bool _present_in_embedded_mode; //!<True if we should draw this window's
90+ // backing texture to the screen on the
91+ // next frame
92+ nux::Geometry _last_presented_geometry_in_embedded_mode;
93+
94+ bool _contents_ready_for_presentation; //!<True if this window's contents has just been
95+ // redrawn and it is awaiting presentation
96+
97 friend class PopUpWindow;
98
99 friend class WindowThread;
100
101=== modified file 'Nux/View.cpp'
102--- Nux/View.cpp 2013-04-08 14:02:32 +0000
103+++ Nux/View.cpp 2013-07-22 17:20:34 +0000
104@@ -425,6 +425,13 @@
105
106 void View::QueueDraw()
107 {
108+ // Report to a parent view with redirect_rendering_to_texture_ set to true that one of its children
109+ // needs to be redrawn.
110+ //
111+ // We should always do this, as the conditions for parent rendering may have
112+ // changed between child QueueDraw calls (eg window visibility)
113+ PrepareParentRedirectedView();
114+
115 if (draw_cmd_queued_)
116 return;
117
118@@ -435,10 +442,6 @@
119 application->RequestRedraw();
120 }
121
122- // Report to a parent view with redirect_rendering_to_texture_ set to true that one of its children
123- // needs to be redrawn.
124- PrepareParentRedirectedView();
125-
126 if (view_layout_)
127 {
128 // If this view has requested a draw, then all of it children in the view_layout_
129
130=== modified file 'Nux/WindowCompositor.cpp'
131--- Nux/WindowCompositor.cpp 2013-05-28 10:26:28 +0000
132+++ Nux/WindowCompositor.cpp 2013-07-22 17:20:34 +0000
133@@ -41,8 +41,11 @@
134 DECLARE_LOGGER(logger, "nux.window");
135
136 WindowCompositor::WindowCompositor(WindowThread* window_thread)
137- : reference_fbo_(0)
138+ : draw_reference_fbo_(0)
139+ , read_reference_fbo_(0)
140 , window_thread_(window_thread)
141+ , _currently_rendering_windows(NULL)
142+ , _current_global_clip_rect(NULL)
143 {
144 m_OverlayWindow = NULL;
145 _tooltip_window = NULL;
146@@ -209,7 +212,17 @@
147 WindowList::iterator window_it;
148 for (window_it = _view_window_list.begin(); window_it != _view_window_list.end(); ++window_it)
149 {
150- if ((*window_it).IsValid() && (*window_it)->IsVisible())
151+ // Since the mouse is really an input-level thing, we want to know
152+ // if the underlying input window is enabled or if the window is
153+ // visible
154+
155+ if (!window_it->IsValid())
156+ continue;
157+
158+ bool visible_or_input_enabled = (*window_it)->InputWindowEnabled() ||
159+ (*window_it)->IsVisible();
160+
161+ if (visible_or_input_enabled)
162 {
163 Area* area = (*window_it)->FindAreaUnderMouse(mouse_position, event_type);
164 if (area)
165@@ -1341,6 +1354,50 @@
166 }
167 }
168
169+ namespace
170+ {
171+ void
172+ AssignWeakBaseWindowMatchingRaw(WindowCompositor::WeakBaseWindowPtr const& w,
173+ BaseWindow* bw,
174+ WindowCompositor::WeakBaseWindowPtr *ptr)
175+ {
176+ if (w.IsValid() &&
177+ w.GetPointer() == bw)
178+ *ptr = w;
179+ }
180+ }
181+
182+ WindowCompositor::WeakBaseWindowPtr WindowCompositor::FindWeakBaseWindowPtrForRawPtr(nux::BaseWindow *raw)
183+ {
184+ using namespace std::placeholders;
185+
186+ WeakBaseWindowPtr weak;
187+ OnAllBaseWindows(std::bind(AssignWeakBaseWindowMatchingRaw, _1, raw, &weak));
188+ return weak;
189+ }
190+
191+ void WindowCompositor::OnAllBaseWindows(const WindowMutatorFunc &func)
192+ {
193+ for (WeakBaseWindowPtr const& ptr : _view_window_list)
194+ {
195+ if (ptr.IsValid())
196+ func(ptr);
197+ }
198+
199+ for (WeakBaseWindowPtr const& ptr : _view_window_list)
200+ if (ptr.IsValid())
201+ func(ptr);
202+
203+ if (m_MenuWindow.IsValid())
204+ func(m_MenuWindow);
205+
206+ if (_tooltip_window.IsValid())
207+ func(_tooltip_window);
208+
209+ if (m_OverlayWindow.IsValid())
210+ func(m_OverlayWindow);
211+ }
212+
213 void WindowCompositor::Draw(bool SizeConfigurationEvent, bool force_draw)
214 {
215 inside_rendering_cycle_ = true;
216@@ -1501,6 +1558,64 @@
217 GetPainter().EmptyBackgroundStack();
218 }
219
220+ void WindowCompositor::PresentAnyReadyWindows()
221+ {
222+ if (!_currently_rendering_windows ||
223+ !_current_global_clip_rect)
224+ return;
225+
226+ GraphicsEngine& graphics_engine = window_thread_->GetGraphicsEngine();
227+
228+ // Present all buffers to the screen
229+ graphics_engine.ApplyClippingRectangle();
230+ CHECKGL(glDepthMask(GL_FALSE));
231+
232+ WindowList &windows = *_currently_rendering_windows;
233+ Geometry &global_clip_rect = *_current_global_clip_rect;
234+
235+ for (WindowList::iterator it = windows.begin(), end = windows.end(); it != end; ++it)
236+ {
237+ WeakBaseWindowPtr& window_ptr = *it;
238+ if (window_ptr.IsNull())
239+ continue;
240+
241+ BaseWindow* window = window_ptr.GetPointer();
242+
243+ if (window->IsVisible())
244+ {
245+ if (global_clip_rect.Intersect(window->GetGeometry()).IsNull())
246+ {
247+ // The global clipping area can be seen as a per monitor clipping
248+ // region. It is mostly used in embedded mode with compiz. If we
249+ // get here, it means that the BaseWindow we want to render is not
250+ // in area of the monitor that compiz is currently rendering. So
251+ // skip it.
252+ continue;
253+ }
254+
255+ RenderTargetTextures& rt = GetWindowBuffer(window);
256+
257+ if (rt.color_rt.IsValid())
258+ {
259+ /* Already been presented */
260+ if (!window->_contents_ready_for_presentation)
261+ continue;
262+
263+ /* Caller doesn't want us to render this yet */
264+ if (GetWindowThread()->IsEmbeddedWindow() &&
265+ !window->AllowPresentationInEmbeddedMode())
266+ continue;
267+
268+ // Nux is done rendering a BaseWindow into a texture. The previous call to Deactivate
269+ // has cancelled any opengl framebuffer object that was set.
270+ PresentBufferToScreen(rt.color_rt, window->GetBaseX(), window->GetBaseY(), false, false, window->GetOpacity(), window->premultiply());
271+
272+ window->_contents_ready_for_presentation = false;
273+ }
274+ }
275+ }
276+ }
277+
278 void WindowCompositor::RenderTopViews(bool force_draw,
279 WindowList& windows_to_render,
280 bool drawModal)
281@@ -1512,13 +1627,17 @@
282 GraphicsEngine& graphics_engine = window_thread_->GetGraphicsEngine();
283 unsigned int window_width = graphics_engine.GetWindowWidth();
284 unsigned int window_height = graphics_engine.GetWindowHeight();
285- GetGraphicsDisplay()->GetGpuDevice()->DeactivateFrameBuffer();
286 graphics_engine.SetViewport(0, 0, window_width, window_height);
287 graphics_engine.EmptyClippingRegion();
288
289 Geometry global_clip_rect = graphics_engine.GetScissorRect();
290 global_clip_rect.y = window_height - global_clip_rect.y - global_clip_rect.height;
291
292+ _current_global_clip_rect = &global_clip_rect;
293+
294+ // We don't need to restore framebuffers if we didn't update any windows
295+ bool updated_any_windows = false;
296+
297 // Always make a copy of the windows to render. We have no control over
298 // the windows we are actually drawing. It has been observed that some
299 // windows modify the windows stack during the draw process.
300@@ -1530,6 +1649,9 @@
301 // list, lets reverse it as we are constructing, as we want to draw the
302 // windows from back to front.
303 WindowList windows(windows_to_render.rbegin(), windows_to_render.rend());
304+
305+ _currently_rendering_windows = &windows;
306+
307 for (WindowList::iterator it = windows.begin(), end = windows.end(); it != end; ++it)
308 {
309 WeakBaseWindowPtr& window_ptr = *it;
310@@ -1537,6 +1659,7 @@
311 continue;
312
313 BaseWindow* window = window_ptr.GetPointer();
314+
315 if (!drawModal && window->IsModal())
316 continue;
317
318@@ -1594,25 +1717,15 @@
319 }
320
321 RenderTopViewContent(window, force_draw);
322- }
323
324- if (rt.color_rt.IsValid())
325- {
326 m_FrameBufferObject->Deactivate();
327-
328- // Nux is done rendering a BaseWindow into a texture. The previous call to Deactivate
329- // has cancelled any opengl framebuffer object that was set.
330-
331- CHECKGL(glDepthMask(GL_FALSE));
332- {
333- graphics_engine.ApplyClippingRectangle();
334- PresentBufferToScreen(rt.color_rt, window->GetBaseX(), window->GetBaseY(), false, false, window->GetOpacity(), window->premultiply());
335- }
336 CHECKGL(glDepthMask(GL_TRUE));
337 graphics_engine.GetRenderStates().SetBlend(false);
338+ updated_any_windows = true;
339 }
340
341 window->_child_need_redraw = false;
342+ window->_contents_ready_for_presentation = true;
343 }
344 else
345 {
346@@ -1622,7 +1735,27 @@
347 }
348 }
349
350- m_FrameBufferObject->Deactivate();
351+ /* If any windows were updated, then we need to rebind our
352+ * reference framebuffer */
353+ if (updated_any_windows)
354+ {
355+ if (GetWindowThread()->IsEmbeddedWindow())
356+ {
357+ // Restore the reference framebuffer
358+ if (!RestoreReferenceFramebuffer())
359+ {
360+ nuxDebugMsg("[WindowCompositor::RenderTopViews] Setting the Reference fbo has failed.");
361+ }
362+ }
363+ else
364+ GetGraphicsDisplay()->GetGpuDevice()->DeactivateFrameBuffer();
365+ }
366+
367+ /* Present any windows which haven't yet been presented */
368+ PresentAnyReadyWindows();
369+
370+ _currently_rendering_windows = NULL;
371+ _current_global_clip_rect = NULL;
372 }
373
374 void WindowCompositor::RenderMainWindowComposition(bool force_draw)
375@@ -1720,23 +1853,8 @@
376 m_FrameBufferObject->SetDepthTextureAttachment(m_MainDepthRT, 0);
377 m_FrameBufferObject->Activate();
378 }
379- else
380- {
381- if (GetWindowThread()->IsEmbeddedWindow() && reference_fbo_)
382- {
383- // In the context of Unity, we may want Nux to restore a specific fbo and render the
384- // BaseWindow texture into it. That fbo is called a reference framebuffer object. if a
385- // Reference framebuffer object is present, Nux sets it.
386- if (!RestoreReferenceFramebuffer())
387- {
388- nuxDebugMsg("[WindowCompositor::RenderTopViews] Setting the Reference fbo has failed.");
389- }
390- }
391- else
392- {
393- GetGraphicsDisplay()->GetGpuDevice()->DeactivateFrameBuffer();
394- }
395- }
396+
397+ // Reference framebuffer is already restored
398
399 window_thread_->GetGraphicsEngine().EmptyClippingRegion();
400 window_thread_->GetGraphicsEngine().SetOpenGLClippingRectangle(0, 0, window_width, window_height);
401@@ -2140,11 +2258,22 @@
402
403 nuxAssert(buffer_width >= 1);
404 nuxAssert(buffer_height >= 1);
405- // Restore Main Frame Buffer
406- m_FrameBufferObject->FormatFrameBufferObject(buffer_width, buffer_height, BITFMT_R8G8B8A8);
407- m_FrameBufferObject->SetTextureAttachment(0, m_MainColorRT, 0);
408- m_FrameBufferObject->SetDepthTextureAttachment(m_MainDepthRT, 0);
409- m_FrameBufferObject->Activate();
410+ // Restore Main Frame Buffer if not in embedded mode
411+ if (!GetWindowThread()->IsEmbeddedWindow())
412+ {
413+ m_FrameBufferObject->FormatFrameBufferObject(buffer_width, buffer_height, BITFMT_R8G8B8A8);
414+ m_FrameBufferObject->SetTextureAttachment(0, m_MainColorRT, 0);
415+ m_FrameBufferObject->SetDepthTextureAttachment(m_MainDepthRT, 0);
416+ m_FrameBufferObject->Activate();
417+ }
418+ else
419+ {
420+ // Restore reference framebuffer
421+ RestoreReferenceFramebuffer();
422+
423+ // Present any ready windows
424+ PresentAnyReadyWindows();
425+ }
426
427 window_thread_->GetGraphicsEngine().SetViewport(0, 0, buffer_width, buffer_height);
428 window_thread_->GetGraphicsEngine().SetOrthographicProjectionMatrix(buffer_width, buffer_height);
429@@ -2429,9 +2558,12 @@
430 return (*keyboard_grab_stack_.begin());
431 }
432
433- void WindowCompositor::SetReferenceFramebuffer(unsigned int fbo_object, Geometry fbo_geometry)
434+ void WindowCompositor::SetReferenceFramebuffer(unsigned int draw_fbo_object,
435+ unsigned int read_fbo_object,
436+ Geometry const& fbo_geometry)
437 {
438- reference_fbo_ = fbo_object;
439+ draw_reference_fbo_ = draw_fbo_object;
440+ read_reference_fbo_ = read_fbo_object;
441 reference_fbo_geometry_ = fbo_geometry;
442 }
443
444@@ -2497,7 +2629,7 @@
445 return ok;
446 }
447
448- void SetReferenceFramebufferViewport (const nux::Geometry &reference_fbo_geometry_)
449+ void SetReferenceFramebufferViewport(const nux::Geometry &reference_fbo_geometry_)
450 {
451 CHECKGL(glViewport(reference_fbo_geometry_.x,
452 reference_fbo_geometry_.y,
453@@ -2508,24 +2640,53 @@
454
455 bool WindowCompositor::RestoreReferenceFramebuffer()
456 {
457- if (!reference_fbo_)
458- return false;
459-
460 // It is assumed that the reference fbo contains valid textures.
461 // Nux does the following:
462 // - Bind the reference fbo (reference_fbo_)
463 // - Call glDrawBuffer with GL_COLOR_ATTACHMENT0
464 // - Set the opengl viewport size (reference_fbo_geometry_)
465
466- CHECKGL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, reference_fbo_));
467-#ifndef NUX_OPENGLES_20
468- CHECKGL(glDrawBuffer(GL_COLOR_ATTACHMENT0));
469- CHECKGL(glReadBuffer(GL_COLOR_ATTACHMENT0));
470-#endif
471-
472- SetReferenceFramebufferViewport (reference_fbo_geometry_);
473-
474- return CheckExternalFramebufferStatus (GL_FRAMEBUFFER_EXT);
475+#ifndef NUX_OPENGLES_20
476+ CHECKGL(glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, draw_reference_fbo_));
477+ CHECKGL(glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, read_reference_fbo_));
478+ if (draw_reference_fbo_)
479+ {
480+ CHECKGL(glDrawBuffer(GL_COLOR_ATTACHMENT0));
481+ }
482+ else
483+ {
484+ CHECKGL(glDrawBuffer(GL_BACK));
485+ }
486+
487+ if (read_reference_fbo_)
488+ {
489+ CHECKGL(glReadBuffer(GL_COLOR_ATTACHMENT0));
490+ }
491+ else
492+ {
493+ CHECKGL(glReadBuffer(GL_BACK));
494+ }
495+#else
496+ nuxAssertMsg(draw_reference_fbo_ == read_reference_fbo_,
497+ "[WindowCompositor::RestoreReferenceFramebuffer]: OpenGL|ES does not"\
498+ " support separate draw and read framebuffer bindings, using the supplied"\
499+ " draw binding");
500+ CHECKGL(glBindFramebufferEXT(GL_FRAMEBUFFER, draw_reference_fbo_));
501+#endif
502+
503+ SetReferenceFramebufferViewport(reference_fbo_geometry_);
504+
505+#ifndef NUX_OPENGLES_20
506+ int restore_status =
507+ (!draw_reference_fbo_ ||
508+ CheckExternalFramebufferStatus(GL_DRAW_FRAMEBUFFER_EXT)) &&
509+ (!read_reference_fbo_ ||
510+ CheckExternalFramebufferStatus(GL_READ_FRAMEBUFFER_EXT));
511+#else
512+ int restore_status = CheckExternalFramebufferStatus(GL_FRAMEBUFFER);
513+#endif
514+
515+ return restore_status;
516 }
517
518 void WindowCompositor::RestoreMainFramebuffer()
519@@ -2533,6 +2694,14 @@
520 // This is a bit inefficient as we unbind and then rebind
521 nux::GetGraphicsDisplay()->GetGpuDevice()->DeactivateFrameBuffer ();
522 RestoreReferenceFramebuffer ();
523+
524+ /* Present any ready windows after restoring
525+ * the reference framebuffer. This ensures that if
526+ * we need to restore the reference framebuffer to
527+ * get access to its contents through glCopyTexSubImage2D
528+ * that it will also have any rendered views in it too
529+ */
530+ PresentAnyReadyWindows();
531 }
532
533 #ifdef NUX_GESTURES_SUPPORT
534
535=== modified file 'Nux/WindowCompositor.h'
536--- Nux/WindowCompositor.h 2013-04-29 23:33:12 +0000
537+++ Nux/WindowCompositor.h 2013-07-22 17:20:34 +0000
538@@ -23,6 +23,8 @@
539 #ifndef WINDOWCOMPOSITOR_H
540 #define WINDOWCOMPOSITOR_H
541
542+#include <functional>
543+
544 #include "BaseWindow.h"
545
546 #include <sigc++/trackable.h>
547@@ -53,6 +55,7 @@
548 {
549 public:
550 typedef ObjectWeakPtr<BaseWindow> WeakBaseWindowPtr;
551+ typedef std::function <void(const WeakBaseWindowPtr &)> WindowMutatorFunc;
552
553 WindowCompositor(WindowThread* window_thread);
554 ~WindowCompositor();
555@@ -115,6 +118,8 @@
556 */
557 InputArea* GetKeyFocusArea();
558
559+ void OnAllBaseWindows(const WindowMutatorFunc &);
560+
561 //! Signal emitted when a BaseWindow becomes visible.
562 /*!
563 This signal is emitted after the BaseWindow has emitted it own sigVisible signal.
564@@ -239,10 +244,11 @@
565 restored after Nux completes it rendering. The external fbo is used only in embedded mode. \n
566 If the fbo_object parameter 0, then the reference fbo is invalid and will not be used.
567
568- @param fbo_object The opengl index of the fbo.
569+ @param draw_fbo_object The opengl index of the GL_DRAW_FRAMEBUFFER_EXT.
570+ @param read_fbo_object The opengl index of the GL_READ_FRAMEBUFFER_EXT.
571 @param fbo_geometry The geometry of the fbo.
572 */
573- void SetReferenceFramebuffer(unsigned int fbo_object, Geometry fbo_geometry);
574+ void SetReferenceFramebuffer(unsigned int draw_fbo_object, unsigned int read_fbo_object, const Geometry &fbo_geometry);
575
576 /*!
577 Bind the reference opengl framebuffer object.
578@@ -408,6 +414,9 @@
579 #endif
580
581 private:
582+
583+ WeakBaseWindowPtr FindWeakBaseWindowPtrForRawPtr(nux::BaseWindow *);
584+
585 //! Render the interface.
586 void Draw(bool SizeConfigurationEvent, bool force_draw);
587
588@@ -424,6 +433,8 @@
589 */
590 void RenderTopViews(bool force_draw, std::list< ObjectWeakPtr<BaseWindow> >& WindowList, bool draw_modal);
591
592+ void PresentAnyReadyWindows();
593+
594 //! Render the content of a top view.
595 void RenderTopViewContent(BaseWindow* window, bool force_draw);
596
597@@ -534,7 +545,8 @@
598 int m_TooltipY;
599
600 //! The fbo to restore after Nux rendering in embedded mode.
601- unsigned int reference_fbo_;
602+ unsigned int draw_reference_fbo_;
603+ unsigned int read_reference_fbo_;
604 Geometry reference_fbo_geometry_;
605
606 //! True if the platform has support for depth textures.
607@@ -571,6 +583,9 @@
608 std::unique_ptr<GestureBroker> gesture_broker_;
609 #endif
610
611+ WindowList *_currently_rendering_windows;
612+ Geometry *_current_global_clip_rect;
613+
614 //! Perform some action before destruction.
615 /*!
616 Perform some action before destruction. This function should only be
617
618=== modified file 'Nux/WindowThread.cpp'
619--- Nux/WindowThread.cpp 2013-04-13 21:21:01 +0000
620+++ Nux/WindowThread.cpp 2013-07-22 17:20:34 +0000
621@@ -19,6 +19,8 @@
622 *
623 */
624
625+#include <functional>
626+
627 #include "Nux.h"
628 #include "Layout.h"
629 #include "NuxCore/Logger.h"
630@@ -50,6 +52,7 @@
631
632 WindowThread::WindowThread(const char *WindowTitle, int width, int height, AbstractThread *Parent, bool Modal)
633 : AbstractThread(Parent)
634+ , foreign_frame_frozen_(false)
635 , window_initial_width_(width)
636 , window_initial_height_(height)
637 , window_title_(WindowTitle)
638@@ -1389,6 +1392,58 @@
639 return m_dirty_areas;
640 }
641
642+ bool WindowThread::AddToPresentationList(BaseWindow *bw,
643+ bool force = false)
644+ {
645+ RequestRedraw();
646+ WindowCompositor::WeakBaseWindowPtr ptr(window_compositor_->FindWeakBaseWindowPtrForRawPtr(bw));
647+
648+ if (!ptr.IsValid())
649+ return false;
650+
651+ if (force ||
652+ !foreign_frame_frozen_)
653+ {
654+ if (std::find(m_presentation_list_embedded.begin(),
655+ m_presentation_list_embedded.end(),
656+ ptr) != m_presentation_list_embedded.end())
657+ return true;
658+
659+ m_presentation_list_embedded.push_back(ptr);
660+ return true;
661+ }
662+ else
663+ {
664+ if (std::find(m_presentation_list_embedded_next_frame.begin(),
665+ m_presentation_list_embedded_next_frame.end(),
666+ ptr) != m_presentation_list_embedded_next_frame.end())
667+ return false;
668+
669+ m_presentation_list_embedded_next_frame.push_back(ptr);
670+ return false;
671+ }
672+ }
673+
674+ std::vector<nux::Geometry> WindowThread::GetPresentationListGeometries()
675+ {
676+ std::vector<nux::Geometry> presentation_geometries;
677+ for (WindowCompositor::WeakBaseWindowPtr const& base_window : m_presentation_list_embedded)
678+ {
679+ if (base_window.IsValid())
680+ {
681+ nux::Geometry const& abs_geom(base_window->GetAbsoluteGeometry());
682+ nux::Geometry const& last_geom(base_window->LastPresentedGeometryInEmbeddedMode());
683+ presentation_geometries.push_back(abs_geom);
684+ if (abs_geom != last_geom)
685+ {
686+ if (!last_geom.IsNull())
687+ presentation_geometries.push_back(last_geom);
688+ }
689+ }
690+ }
691+ return presentation_geometries;
692+ }
693+
694 bool WindowThread::IsEmbeddedWindow()
695 {
696 return embedded_window_;
697@@ -1529,6 +1584,32 @@
698 return request_draw_cycle_to_host_wm;
699 }
700
701+ namespace {
702+
703+ void PresentOnBaseWindowIntersectsRect(ObjectWeakPtr<BaseWindow> const& w,
704+ Geometry const& rect)
705+ {
706+ Geometry inter = rect.Intersect(w->GetAbsoluteGeometry());
707+ if (!inter.IsNull())
708+ w->PresentInEmbeddedModeOnThisFrame(true);
709+ }
710+
711+ void MarkWindowUnpresented(const ObjectWeakPtr<BaseWindow> &w)
712+ {
713+ w->OnPresentedInEmbeddedMode();
714+ }
715+
716+ }
717+
718+ void WindowThread::PresentWindowsIntersectingGeometryOnThisFrame(const Geometry &rect)
719+ {
720+ using namespace std::placeholders;
721+ nuxAssertMsg(IsEmbeddedWindow(),
722+ "[WindowThread::PresentWindowIntersectingGeometryOnThisFrame] "
723+ "can only be called inside an embedded window");
724+ window_compositor_->OnAllBaseWindows(std::bind(PresentOnBaseWindowIntersectsRect, _1, rect));
725+ }
726+
727 void WindowThread::RenderInterfaceFromForeignCmd(Geometry *clip)
728 {
729 nuxAssertMsg(IsEmbeddedWindow() == true, "[WindowThread::RenderInterfaceFromForeignCmd] You can only call RenderInterfaceFromForeignCmd if the window was created with CreateFromForeignWindow.");
730@@ -1568,17 +1649,38 @@
731
732 CHECKGL( glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
733
734- graphics_display_->GetGpuDevice()->DeactivateFrameBuffer();
735 IOpenGLShaderProgram::SetShaderTracking(false);
736-
737- if (IsEmbeddedWindow() && window_compositor_->reference_fbo_)
738+ }
739+
740+ void WindowThread::ForeignFrameEnded()
741+ {
742+ using namespace std::placeholders;
743+ nuxAssertMsg(IsEmbeddedWindow(),
744+ "[WindowThread::ForeignFrameEnded] "
745+ "can only be called inside an embedded window");
746+ window_compositor_->OnAllBaseWindows(std::bind(MarkWindowUnpresented, _1));
747+ m_presentation_list_embedded.clear();
748+
749+ foreign_frame_frozen_ = false;
750+
751+ /* Move all the BaseWindows in m_presentation_list_embedded_next_frame
752+ * to m_presentation_list_embedded and mark them for presentation
753+ */
754+ for (std::vector<WindowCompositor::WeakBaseWindowPtr>::iterator it =
755+ m_presentation_list_embedded_next_frame.begin();
756+ it != m_presentation_list_embedded_next_frame.end();
757+ ++it)
758 {
759- // Restore the reference framebuffer
760- if (!window_compositor_->RestoreReferenceFramebuffer())
761- {
762- nuxDebugMsg("[WindowCompositor::RenderTopViews] Setting the Reference fbo has failed.");
763- }
764+ if (it->IsValid())
765+ (*it)->PresentInEmbeddedModeOnThisFrame();
766 }
767+
768+ m_presentation_list_embedded_next_frame.clear();
769+ }
770+
771+ void WindowThread::ForeignFrameCutoff()
772+ {
773+ foreign_frame_frozen_ = true;
774 }
775
776 int WindowThread::InstallEventInspector(EventInspector function, void* data)
777
778=== modified file 'Nux/WindowThread.h'
779--- Nux/WindowThread.h 2013-04-10 18:56:19 +0000
780+++ Nux/WindowThread.h 2013-07-22 17:20:34 +0000
781@@ -32,6 +32,7 @@
782 namespace nux
783 {
784
785+ class BaseWindow;
786 class WindowThread;
787 class Layout;
788 class HLayout;
789@@ -227,6 +228,17 @@
790 #endif
791
792 /*!
793+ In embedded mode, allow presentation on any windows intersecting this
794+ rect. The effect of this is culmulative for the frame, so it can be
795+ called multiple times with many different rects until
796+ RenderInterfaceFromForeignCmd is called.
797+ \sa IsEmbeddedWindow
798+
799+ @param rect Region of the display to consider for presenting windows
800+ */
801+ void PresentWindowsIntersectingGeometryOnThisFrame(const Geometry &rect);
802+
803+ /*!
804 Render the interface. This command is send from the pluging when the window thread is embedded.
805 The clip region matches the surface of one single monitor screen, or a region inside that screen.
806 \sa IsEmbeddedWindow.
807@@ -235,6 +247,19 @@
808 */
809 void RenderInterfaceFromForeignCmd(Geometry *clip);
810
811+ /*!
812+ Used to mark the end of the foreign frame. All calls to PresentInEmbeddedModeOnThisFrame
813+ are now redirected to this upcoming frame where we will be called next.
814+ */
815+ void ForeignFrameEnded();
816+
817+ /*!
818+ Used to mark the cutoff point where all calls to PresentInEmbeddedModeOnThisFrame
819+ should be effective on the next frame, and not this one, because the parent context
820+ has stopped tracking damage events for this frame
821+ */
822+ void ForeignFrameCutoff();
823+
824 #if !defined(NUX_MINIMAL)
825 /*!
826 Add a timeline to our window
827@@ -319,12 +344,19 @@
828
829 bool IsRedrawNeeded() const;
830
831+ // DrawList - this is a maintained list of areas that will
832+ // be completely redraw on the next frame
833 void AddToDrawList(View *view);
834-
835 void ClearDrawList();
836
837 std::vector<Geometry> const& GetDrawList() const;
838
839+ // PresentationList - this is a maintained list of areas that
840+ // will be presented to the reference framebuffer or backbuffer
841+ // in embedded mode on the next frame
842+ bool AddToPresentationList(nux::BaseWindow *, bool force);
843+ std::vector <Geometry> GetPresentationListGeometries();
844+
845 #ifdef NUX_GESTURES_SUPPORT
846 /*!
847 Simple wrapper for ProcessEvent for connection with GeisAdapter::event_ready
848@@ -544,6 +576,23 @@
849 std::list<Area *> _queued_layout_list;
850 std::vector<Geometry> m_dirty_areas;
851
852+ typedef nux::ObjectWeakPtr<nux::BaseWindow> WCWeakBaseWindowPtr;
853+
854+ std::vector<WCWeakBaseWindowPtr> m_presentation_list_embedded;
855+
856+ /*!
857+ This list contains al lthe windows which will be presented on the next frame
858+ (eg, after ForeignFrameEnded they are moved into m_presentation_list_embedded
859+ and marked for presentation)
860+ */
861+ std::vector<WCWeakBaseWindowPtr> m_presentation_list_embedded_next_frame;
862+
863+ /*! Whether or not the current frame is "frozen" because the host WM has stopped tracking
864+ damage events. If so we should put all presentation requests on the next frame instead
865+ of this one
866+ */
867+ bool foreign_frame_frozen_;
868+
869 //! This variable is true while we are computing the layout the starting from the outmost layout(the Main Layout);
870 bool _inside_layout_cycle;
871
872
873=== modified file 'NuxGraphics/GLDeviceFrameBufferObject.cpp'
874--- NuxGraphics/GLDeviceFrameBufferObject.cpp 2011-10-19 20:32:38 +0000
875+++ NuxGraphics/GLDeviceFrameBufferObject.cpp 2013-07-22 17:20:34 +0000
876@@ -65,12 +65,22 @@
877
878 void GLFramebufferObject::Bind()
879 {
880- CHECKGL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboId));
881+#ifdef NUX_OPENGLES_20
882+ GLenum binding = GL_FRAMEBUFFER;
883+#else
884+ GLenum binding = GL_DRAW_FRAMEBUFFER_EXT;
885+#endif
886+ CHECKGL(glBindFramebufferEXT(binding, m_fboId));
887 }
888
889 void GLFramebufferObject::Disable()
890 {
891- CHECKGL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
892+#ifdef NUX_OPENGLES_20
893+ GLenum binding = GL_FRAMEBUFFER;
894+#else
895+ GLenum binding = GL_DRAW_FRAMEBUFFER_EXT;
896+#endif
897+ CHECKGL(glBindFramebufferEXT(binding, 0));
898 }
899
900 void
901@@ -170,11 +180,11 @@
902 {
903 #ifndef NUX_OPENGLES_20
904 // Only binds if m_fboId is different than the currently bound FBO
905- CHECKGL(glGetIntegerv( GL_FRAMEBUFFER_BINDING_EXT, &m_savedFboId ));
906+ CHECKGL(glGetIntegerv( GL_DRAW_FRAMEBUFFER_BINDING_EXT, &m_savedFboId ));
907
908 if (m_fboId != m_savedFboId)
909 {
910- CHECKGL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboId));
911+ CHECKGL(glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fboId));
912 }
913 #else
914 // Only binds if m_fboId is different than the currently bound FBO
915@@ -189,10 +199,15 @@
916
917 void GLFramebufferObject::_GuardedUnbind()
918 {
919+#ifdef NUX_OPENGLES_20
920+ GLenum binding = GL_FRAMEBUFFER;
921+#else
922+ GLenum binding = GL_DRAW_FRAMEBUFFER_EXT;
923+#endif
924 // Returns FBO binding to the previously enabled FBO
925 if (m_savedFboId != m_fboId)
926 {
927- CHECKGL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint) m_savedFboId));
928+ CHECKGL(glBindFramebufferEXT(binding, (GLuint) m_savedFboId));
929 }
930 }
931
932
933=== modified file 'NuxGraphics/GpuDevice.cpp'
934--- NuxGraphics/GpuDevice.cpp 2013-05-01 18:12:51 +0000
935+++ NuxGraphics/GpuDevice.cpp 2013-07-22 17:20:34 +0000
936@@ -1069,8 +1069,14 @@
937 return;
938 }
939
940+#ifdef NUX_OPENGLES_20
941+ GLenum binding = GL_FRAMEBUFFER;
942+#else
943+ GLenum binding = GL_DRAW_FRAMEBUFFER_EXT;
944+#endif
945+
946 active_framebuffer_object_.Release();
947- CHECKGL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
948+ CHECKGL(glBindFramebufferEXT(binding, 0));
949 CHECKGL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
950 }
951
952
953=== modified file 'NuxGraphics/GpuDeviceTexture.cpp'
954--- NuxGraphics/GpuDeviceTexture.cpp 2012-11-16 19:11:05 +0000
955+++ NuxGraphics/GpuDeviceTexture.cpp 2013-07-22 17:20:34 +0000
956@@ -73,7 +73,7 @@
957 return result;
958 }
959
960- ObjectPtr<IOpenGLTexture2D> GpuDevice::CreateTexture2DFromID(int /* id */
961+ ObjectPtr<IOpenGLTexture2D> GpuDevice::CreateTexture2DFromID(int id
962 , int width
963 , int height
964 , int levels
965@@ -95,6 +95,12 @@
966
967 ObjectPtr<IOpenGLTexture2D> result;
968 result.Adopt(new IOpenGLTexture2D(width, height, levels, pixel_format, true, NUX_FILE_LINE_PARAM));
969+
970+ /* Assign the external id to the internal id. This allows us
971+ * to use the foreign texture as if it were a native one.
972+ *
973+ * This is really important. Don't remove it */
974+ result->_OpenGLID = id;
975 return result;
976 }
977
978
979=== modified file 'NuxGraphics/IOpenGLFrameBufferObject.cpp'
980--- NuxGraphics/IOpenGLFrameBufferObject.cpp 2013-05-01 18:13:16 +0000
981+++ NuxGraphics/IOpenGLFrameBufferObject.cpp 2013-07-22 17:20:34 +0000
982@@ -336,7 +336,13 @@
983 // Restore the original opengl back buffer as defined when creating the opengl context(color + depth + stencil).
984 int IOpenGLFrameBufferObject::Deactivate()
985 {
986- CHECKGL(glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ));
987+#ifdef NUX_OPENGLES_20
988+ GLenum binding = GL_FRAMEBUFFER;
989+#else
990+ GLenum binding = GL_DRAW_FRAMEBUFFER_EXT;
991+#endif
992+
993+ CHECKGL(glBindFramebufferEXT( binding, 0 ));
994
995 #ifndef NUX_OPENGLES_20
996 CHECKGL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
997
998=== modified file 'configure.ac'
999--- configure.ac 2013-07-18 15:56:35 +0000
1000+++ configure.ac 2013-07-22 17:20:34 +0000
1001@@ -15,7 +15,7 @@
1002 #
1003 m4_define([nux_major_version], [4])
1004 m4_define([nux_minor_version], [0])
1005-m4_define([nux_micro_version], [2])
1006+m4_define([nux_micro_version], [3])
1007
1008 m4_define([nux_api_version], [4.0])
1009 # Increase the number (to the current date) everytime you propose a branch that breaks the API or ABI
1010@@ -23,8 +23,7 @@
1011 # e.g.: december 5th, 2011 is: 20111205
1012 # To make more than one API change in a day, add a number to the date. Like 20111205.xx
1013
1014-m4_define([nux_abi_version], [20130718.0])
1015-
1016+m4_define([nux_abi_version], [20130722.0])
1017 m4_define([nux_version],
1018 [nux_major_version.nux_minor_version.nux_micro_version])
1019
1020
1021=== modified file 'debian/changelog'
1022--- debian/changelog 2013-07-05 04:02:37 +0000
1023+++ debian/changelog 2013-07-22 17:20:34 +0000
1024@@ -1,3 +1,9 @@
1025+nux (4.0.3+13.10.20130705-0ubuntu2) UNRELEASED; urgency=low
1026+
1027+ * Bump ABI
1028+
1029+ -- Sam Spilsbury <smspillaz@interpol> Mon, 22 Jul 2013 10:17:41 -0700
1030+
1031 nux (4.0.2+13.10.20130705-0ubuntu1) saucy; urgency=low
1032
1033 [ Marco Trevisan (Treviño) ]
1034
1035=== modified file 'tests/gtest-nux-windowcompositor.cpp'
1036--- tests/gtest-nux-windowcompositor.cpp 2012-11-19 22:24:05 +0000
1037+++ tests/gtest-nux-windowcompositor.cpp 2013-07-22 17:20:34 +0000
1038@@ -53,6 +53,7 @@
1039 TestBaseWindow() : input_area(new nux::InputArea())
1040 {
1041 ShowWindow(true);
1042+ EnableInputWindow(true);
1043 }
1044
1045 nux::Area* FindAreaUnderMouse(const nux::Point& /* mouse_position */, nux::NuxEventType /* event_type */)
1046@@ -145,82 +146,147 @@
1047
1048 namespace
1049 {
1050+ typedef void (*NGLGenFramebuffers)(GLsizei, GLuint *);
1051+ typedef void (*NGLGenRenderbuffers)(GLsizei, GLuint *);
1052+ typedef void (*NGLBindFramebuffer)(GLenum, GLuint);
1053+ typedef void (*NGLBindRenderbuffer)(GLenum, GLuint);
1054+ typedef void (*NGLRenderbufferStorage)(GLenum, GLenum, GLsizei, GLsizei);
1055+ typedef void (*NGLFramebufferRenderbuffer)(GLenum, GLenum, GLenum, GLuint);
1056+ typedef void (*NGLDeleteFramebuffers)(GLsizei, const GLuint *);
1057+ typedef void (*NGLDeleteRenderbuffers)(GLsizei, const GLuint *);
1058+
1059+ #ifdef NUX_OPENGLES_20
1060+ GLuint NGL_RENDERBUFFER = GL_RENDERBUFFER;
1061+ /* No separate draw or read targets on OpenGL|ES */
1062+ GLuint NGL_DRAW_FRAMEBUFFER = GL_FRAMEBUFFER;
1063+ GLuint NGL_READ_FRAMEBUFFER = GL_FRAMEBUFFER;
1064+ GLuint NGL_DRAW_FRAMEBUFFER_BINDING = GL_FRAMEBUFFER_BINDING;
1065+ GLuint NGL_READ_FRAMEBUFFER_BINDING = GL_FRAMEBUFFER_BINDING;
1066+ GLuint NGL_COLOR_ATTACHMENT0 = GL_COLOR_ATTACHMENT0;
1067+ GLuint NGL_RGBA_STORAGE = GL_RGBA4;
1068+ #else
1069+ GLuint NGL_RENDERBUFFER = GL_RENDERBUFFER_EXT;
1070+ GLuint NGL_DRAW_FRAMEBUFFER = GL_DRAW_FRAMEBUFFER_EXT;
1071+ GLuint NGL_READ_FRAMEBUFFER = GL_READ_FRAMEBUFFER_EXT;
1072+ GLuint NGL_DRAW_FRAMEBUFFER_BINDING = GL_DRAW_FRAMEBUFFER_BINDING_EXT;
1073+ GLuint NGL_READ_FRAMEBUFFER_BINDING = GL_DRAW_FRAMEBUFFER_BINDING_EXT;
1074+ GLuint NGL_COLOR_ATTACHMENT0 = GL_COLOR_ATTACHMENT0_EXT;
1075+ GLuint NGL_RGBA_STORAGE = GL_RGBA8_EXT;
1076+ #endif
1077+
1078 class ReferenceFramebuffer
1079 {
1080 public:
1081
1082 ReferenceFramebuffer ()
1083 {
1084- glGenFramebuffersEXT (1, &fboName);
1085- glGenRenderbuffersEXT (1, &rbName);
1086-
1087- glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, rbName);
1088- glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_RGBA8_EXT, 300, 200);
1089- glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fboName);
1090- glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, rbName);
1091+ #ifdef NUX_OPENGLES_20
1092+ nglGenFramebuffers = &glGenFramebuffers;
1093+ nglGenRenderbuffers = &glGenRenderbuffers;
1094+ nglBindRenderbuffer = &glBindRenderbuffer;
1095+ nglBindFramebuffer = &glBindFramebuffer;
1096+ nglRenderbufferStorage = &glRenderbufferStorage;
1097+ nglFramebufferRenderbuffer = &glFramebufferRenderbuffer;
1098+ nglDeleteRenderbuffers = &glDeleteRenderbuffers;
1099+ nglDeleteFramebuffers = &glDeleteFramebuffers;
1100+ #else
1101+ nglGenFramebuffers = glGenFramebuffersEXT;
1102+ nglGenRenderbuffers = glGenRenderbuffersEXT;
1103+ nglBindRenderbuffer = glBindRenderbufferEXT;
1104+ nglBindFramebuffer = glBindFramebufferEXT;
1105+ nglRenderbufferStorage = glRenderbufferStorageEXT;
1106+ nglFramebufferRenderbuffer = glFramebufferRenderbufferEXT;
1107+ nglDeleteRenderbuffers = glDeleteRenderbuffersEXT;
1108+ nglDeleteFramebuffers = glDeleteFramebuffersEXT;
1109+ #endif
1110+
1111+ nglGenFramebuffers(1, &fboName);
1112+ nglGenRenderbuffers(1, &rbName);
1113+
1114+ nglBindRenderbuffer(NGL_RENDERBUFFER, rbName);
1115+ nglRenderbufferStorage(NGL_RENDERBUFFER, NGL_RGBA_STORAGE, 300, 200);
1116+ nglBindFramebuffer(NGL_DRAW_FRAMEBUFFER, fboName);
1117+ nglBindFramebuffer(NGL_READ_FRAMEBUFFER, fboName);
1118+ nglFramebufferRenderbuffer(NGL_DRAW_FRAMEBUFFER, NGL_COLOR_ATTACHMENT0, NGL_RENDERBUFFER, rbName);
1119 }
1120
1121 ~ReferenceFramebuffer ()
1122 {
1123- glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
1124- glDeleteRenderbuffers (1, &rbName);
1125- glDeleteRenderbuffers (1, &fboName);
1126+ nglBindFramebuffer(NGL_DRAW_FRAMEBUFFER, 0);
1127+ nglBindFramebuffer(NGL_READ_FRAMEBUFFER, 0);
1128+ nglDeleteRenderbuffers(1, &rbName);
1129+ nglDeleteFramebuffers(1, &fboName);
1130 }
1131
1132- GLuint fboName, rbName;
1133+ GLuint fboName, rbName;
1134+
1135+ /* Each instance of the class needs to keep its own
1136+ * copy of the extension functions as glewInit () needs
1137+ * to be called in the desktop case before they are available */
1138+
1139+ NGLGenFramebuffers nglGenFramebuffers;
1140+ NGLGenRenderbuffers nglGenRenderbuffers;
1141+ NGLBindRenderbuffer nglBindRenderbuffer;
1142+ NGLBindFramebuffer nglBindFramebuffer;
1143+ NGLRenderbufferStorage nglRenderbufferStorage;
1144+ NGLFramebufferRenderbuffer nglFramebufferRenderbuffer;
1145+ NGLDeleteRenderbuffers nglDeleteRenderbuffers;
1146+ NGLDeleteFramebuffers nglDeleteFramebuffers;
1147 };
1148 }
1149+
1150 TEST_F(TestWindowCompositor, TestRestoreReferenceFramebufferDirect)
1151 {
1152 ReferenceFramebuffer reference;
1153- GLint fbBinding;
1154-
1155- glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fbBinding);
1156-
1157- ASSERT_EQ (fbBinding, reference.fboName);
1158-
1159- wnd_thread->GetWindowCompositor().SetReferenceFramebuffer(fbBinding, nux::Geometry (0, 0, 300, 200));
1160+ GLint dfbBinding, rfbBinding;
1161+
1162+ glGetIntegerv(NGL_DRAW_FRAMEBUFFER_BINDING, &dfbBinding);
1163+ glGetIntegerv(NGL_READ_FRAMEBUFFER_BINDING, &rfbBinding);
1164+
1165+ ASSERT_EQ (dfbBinding, reference.fboName);
1166+
1167+ wnd_thread->GetWindowCompositor().SetReferenceFramebuffer(dfbBinding, rfbBinding, nux::Geometry (0, 0, 300, 200));
1168
1169 ASSERT_TRUE (wnd_thread->GetWindowCompositor().RestoreReferenceFramebuffer());
1170- glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fbBinding);
1171- ASSERT_EQ (fbBinding, reference.fboName);
1172+ glGetIntegerv(NGL_DRAW_FRAMEBUFFER_BINDING, &dfbBinding);
1173+ glGetIntegerv(NGL_READ_FRAMEBUFFER_BINDING, &rfbBinding);
1174+ ASSERT_EQ (dfbBinding, reference.fboName);
1175+ ASSERT_EQ (rfbBinding, reference.fboName);
1176 }
1177
1178 TEST_F(TestWindowCompositor, TestRestoreReferenceFramebufferThroughRestoreMain)
1179 {
1180 ReferenceFramebuffer reference;
1181- GLint fbBinding;
1182-
1183- glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fbBinding);
1184-
1185- ASSERT_EQ (fbBinding, reference.fboName);
1186-
1187- wnd_thread->GetWindowCompositor().SetReferenceFramebuffer(fbBinding, nux::Geometry (0, 0, 300, 200));
1188+ GLint dfbBinding, rfbBinding;
1189+
1190+ glGetIntegerv(NGL_DRAW_FRAMEBUFFER_BINDING, &dfbBinding);
1191+ glGetIntegerv(NGL_READ_FRAMEBUFFER_BINDING, &rfbBinding);
1192+
1193+ ASSERT_EQ(dfbBinding, reference.fboName);
1194+
1195+ wnd_thread->GetWindowCompositor().SetReferenceFramebuffer(dfbBinding, rfbBinding, nux::Geometry (0, 0, 300, 200));
1196 wnd_thread->GetWindowCompositor().RestoreMainFramebuffer();
1197- glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fbBinding);
1198- ASSERT_EQ (fbBinding, reference.fboName);
1199-}
1200-
1201-TEST_F(TestWindowCompositor, TestNoRestoreReferenceFramebufferDirectIfNoReferenceFramebuffer)
1202-{
1203- GLint fbBinding;
1204- ASSERT_FALSE (wnd_thread->GetWindowCompositor().RestoreReferenceFramebuffer());
1205- glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fbBinding);
1206- ASSERT_EQ (fbBinding, 0);
1207+ glGetIntegerv(NGL_DRAW_FRAMEBUFFER_BINDING, &dfbBinding);
1208+ glGetIntegerv(NGL_READ_FRAMEBUFFER_BINDING, &rfbBinding);
1209+ ASSERT_EQ(dfbBinding, reference.fboName);
1210+ ASSERT_EQ(rfbBinding, reference.fboName);
1211 }
1212
1213 TEST_F(TestWindowCompositor, TestRestoreBackbufferThroughRestoreMain)
1214 {
1215 ReferenceFramebuffer reference;
1216- GLint fbBinding;
1217-
1218- glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fbBinding);
1219-
1220- ASSERT_EQ (fbBinding, reference.fboName);
1221+ GLint dfbBinding, rfbBinding;
1222+
1223+ glGetIntegerv(NGL_DRAW_FRAMEBUFFER_BINDING, &dfbBinding);
1224+ glGetIntegerv(NGL_READ_FRAMEBUFFER_BINDING, &rfbBinding);
1225+
1226+ ASSERT_EQ (dfbBinding, reference.fboName);
1227
1228 wnd_thread->GetWindowCompositor().RestoreMainFramebuffer();
1229- glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fbBinding);
1230- ASSERT_EQ (fbBinding, 0);
1231+ glGetIntegerv(NGL_DRAW_FRAMEBUFFER_BINDING, &dfbBinding);
1232+ glGetIntegerv(NGL_READ_FRAMEBUFFER_BINDING, &rfbBinding);
1233+ ASSERT_EQ(dfbBinding, 0);
1234+ ASSERT_EQ(rfbBinding, 0);
1235 }
1236
1237 TEST_F(TestWindowCompositor, TestSetKeyFocusArea)
1238
1239=== modified file 'tests/gtest-nux-windowthread.cpp'
1240--- tests/gtest-nux-windowthread.cpp 2013-02-01 09:14:07 +0000
1241+++ tests/gtest-nux-windowthread.cpp 2013-07-22 17:20:34 +0000
1242@@ -8,17 +8,36 @@
1243 #include <string>
1244 #include <fstream>
1245
1246+#include <memory>
1247 #include <iostream>
1248 #include <gmock/gmock.h>
1249 #include <boost/filesystem.hpp>
1250 #include <glib.h>
1251
1252+#include <X11/X.h>
1253+
1254 #include "Nux/Nux.h"
1255+#include "Nux/View.h"
1256+#include "Nux/HLayout.h"
1257+#include "Nux/ProgramFramework/TestView.h"
1258
1259
1260 using namespace testing;
1261 using ::testing::Invoke;
1262
1263+namespace nux {
1264+ std::ostream &
1265+ operator<<(std::ostream &os, const Geometry &geo)
1266+ {
1267+ return os << "Geometry: x: " <<
1268+ geo.x << " y: " <<
1269+ geo.y << " width: " <<
1270+ geo.width << " height: " <<
1271+ geo.height << std::endl;
1272+
1273+ }
1274+}
1275+
1276 namespace {
1277
1278 class MockFdDispatch
1279@@ -278,5 +297,482 @@
1280 close (pipefd2[1]);
1281 }
1282
1283+class EmbeddedContext : public ::testing::Test
1284+{
1285+ public:
1286+
1287+ EmbeddedContext()
1288+ : _display(XOpenDisplay(NULL))
1289+ , _root(DefaultRootWindow(_display))
1290+ , _window(XCreateSimpleWindow(_display,
1291+ _root,
1292+ 0,
1293+ 0,
1294+ 300,
1295+ 200,
1296+ 0,
1297+ 0,
1298+ 0))
1299+ , _visinfo(NULL)
1300+ , _context(NULL)
1301+ {
1302+ XVisualInfo temp;
1303+ XWindowAttributes attrib;
1304+ if(!XGetWindowAttributes(_display, _window, &attrib))
1305+ throw std::runtime_error("failed to get window attributes");
1306+
1307+ temp.visualid = XVisualIDFromVisual(attrib.visual);
1308+
1309+ int nvinfo = 0;
1310+ _visinfo = XGetVisualInfo(_display, VisualIDMask, &temp, &nvinfo);
1311+
1312+ if (!_visinfo || !nvinfo)
1313+ throw std::runtime_error("failed to find visual");
1314+
1315+#ifndef NUX_OPENGLES_20
1316+ GLint value = 0;
1317+ glXGetConfig(_display, _visinfo, GLX_USE_GL, &value);
1318+
1319+ if (!value)
1320+ std::runtime_error("available visual is not a gl visual");
1321+
1322+ _context = glXCreateContext(_display, _visinfo, NULL, true);
1323+ glXMakeCurrent(_display, _window, _context);
1324+#else
1325+ EGLDisplay eglDisplay = eglGetDisplay((EGLNativeDisplayType)_display);
1326+ EGLint major, minor;
1327+ if (!eglInitialize(eglDisplay, &major, &minor))
1328+ throw std::runtime_error("eglInitialize failed");
1329+
1330+ const EGLint config_attribs[] = {
1331+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
1332+ EGL_RED_SIZE, 1,
1333+ EGL_GREEN_SIZE, 1,
1334+ EGL_BLUE_SIZE, 1,
1335+ EGL_ALPHA_SIZE, 0,
1336+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
1337+ EGL_CONFIG_CAVEAT, EGL_NONE,
1338+ EGL_STENCIL_SIZE, 1,
1339+ EGL_NONE
1340+ };
1341+
1342+ const EGLint context_attribs[] = {
1343+ EGL_CONTEXT_CLIENT_VERSION, 2,
1344+ EGL_NONE
1345+ };
1346+
1347+ EGLint count, visualid;
1348+ EGLConfig configs[1024];
1349+
1350+ if (!eglChooseConfig(eglDisplay, config_attribs, configs, 1024, &count))
1351+ throw std::runtime_error("eglChooseConfig failed");
1352+
1353+ visualid = temp.visualid;
1354+ EGLConfig config = configs[0];
1355+
1356+ for (int i = 0; i < count; ++i)
1357+ {
1358+ EGLint val;
1359+ eglGetConfigAttrib(eglDisplay, configs[i], EGL_NATIVE_VISUAL_ID, &val);
1360+ if (visualid == val)
1361+ {
1362+ config = configs[i];
1363+ break;
1364+ }
1365+ }
1366+
1367+ eglBindAPI(EGL_OPENGL_ES_API);
1368+ _surface = eglCreateWindowSurface(eglDisplay, config, _window, 0);
1369+ if (_surface == EGL_NO_SURFACE)
1370+ throw std::runtime_error("eglCreateWindowSurface failed");
1371+
1372+ _context = eglCreateContext(eglDisplay, config, EGL_NO_CONTEXT, context_attribs);
1373+ if (_context == EGL_NO_CONTEXT)
1374+ throw std::runtime_error("eglCreateContext failed");
1375+
1376+ if (!eglMakeCurrent(eglDisplay, _surface, _surface, _context))
1377+ throw std::runtime_error("eglMakeCurrent failed");
1378+#endif
1379+ }
1380+
1381+ MOCK_METHOD0(verifyInit, void());
1382+
1383+ static void nuxInitThreadEntry(nux::NThread *, void *user_data)
1384+ {
1385+ EmbeddedContext *context = reinterpret_cast <EmbeddedContext *> (user_data);
1386+ context->verifyInit();
1387+ }
1388+
1389+ void SetUp ()
1390+ {
1391+ EXPECT_CALL(*this, verifyInit());
1392+ nux::NuxInitialize(0);
1393+ _window_thread.reset(nux::CreateFromForeignWindow(_window,
1394+ _context,
1395+ EmbeddedContext::nuxInitThreadEntry,
1396+ this));
1397+ _window_thread->Run(NULL);
1398+ while (g_main_context_iteration(g_main_context_default(), false));
1399+ }
1400+
1401+ ~EmbeddedContext ()
1402+ {
1403+#ifndef NUX_OPENGLES_20
1404+ glXDestroyContext(_display, _context);
1405+#else
1406+ EGLDisplay eglDisplay = eglGetDisplay((EGLNativeDisplayType)_display);
1407+ eglDestroyContext(eglDisplay, _context);
1408+ eglDestroySurface(eglDisplay, _surface);
1409+ eglTerminate(eglDisplay);
1410+#endif
1411+ XDestroyWindow (_display, _window);
1412+ XCloseDisplay (_display);
1413+ }
1414+
1415+ const std::shared_ptr <nux::WindowThread> &
1416+ WindowThread () const
1417+ {
1418+ return _window_thread;
1419+ }
1420+
1421+ private:
1422+
1423+ Display *_display;
1424+ Window _root;
1425+ Window _window;
1426+ XVisualInfo *_visinfo;
1427+#ifndef NUX_OPENGLES_20
1428+ GLXContext _context;
1429+#else
1430+ EGLSurface _surface;
1431+ EGLContext _context;
1432+#endif
1433+
1434+ std::shared_ptr <nux::WindowThread> _window_thread;
1435+};
1436+
1437+TEST_F(EmbeddedContext, WindowThreadIsEmbedded)
1438+{
1439+ EXPECT_TRUE(WindowThread()->IsEmbeddedWindow());
1440+}
1441+
1442+/* There's not a whole lot we can do to test this at the moment
1443+ * since we can't really mock out the behaviour of GraphicsEngine
1444+ */
1445+TEST_F(EmbeddedContext, PresentViewInEmbeddedReadiesForPresentation)
1446+{
1447+ nux::ObjectPtr <nux::BaseWindow> bw(new nux::BaseWindow(TEXT("")));
1448+ bw->ShowWindow(true, false);
1449+ WindowThread()->ForeignFrameEnded();
1450+ bw->PresentInEmbeddedModeOnThisFrame();
1451+ EXPECT_TRUE(bw->AllowPresentationInEmbeddedMode());
1452+}
1453+
1454+TEST_F(EmbeddedContext, QueueDrawOnChildInEmbeddedReadiesForPresentation)
1455+{
1456+ nux::ObjectPtr <nux::BaseWindow> bw(new nux::BaseWindow(TEXT("")));
1457+ nux::HLayout* layout = new nux::HLayout(NUX_TRACKER_LOCATION);
1458+ nux::View* view = new nux::TestView("");
1459+ layout->AddView(view, 1);
1460+ bw->SetLayout(layout);
1461+ bw->ShowWindow(true, false);
1462+
1463+ /* Draw command is implicitly queued by ShowWindow, remove it */
1464+ view->DoneRedraw();
1465+ WindowThread()->ForeignFrameEnded();
1466+
1467+ view->QueueDraw();
1468+ EXPECT_TRUE(bw->AllowPresentationInEmbeddedMode());
1469+}
1470+
1471+TEST_F(EmbeddedContext, DonePresentViewInEmbeddedMode)
1472+{
1473+ nux::ObjectPtr <nux::BaseWindow> bw(new nux::BaseWindow(TEXT("")));
1474+ bw->PresentInEmbeddedModeOnThisFrame();
1475+ bw->OnPresentedInEmbeddedMode();
1476+ EXPECT_FALSE(bw->AllowPresentationInEmbeddedMode());
1477+}
1478+
1479+TEST_F(EmbeddedContext, NoPresentInvisibleOnQueueDraw)
1480+{
1481+ nux::ObjectPtr <nux::BaseWindow> window(new nux::BaseWindow(TEXT("")));
1482+ nux::HLayout* layout = new nux::HLayout(NUX_TRACKER_LOCATION);
1483+ nux::View* view = new nux::TestView("");
1484+ layout->AddView(view, 1);
1485+ window->SetLayout(layout);
1486+ window->ShowWindow(false);
1487+ view->QueueDraw();
1488+ EXPECT_FALSE(window->AllowPresentationInEmbeddedMode());
1489+}
1490+
1491+TEST_F(EmbeddedContext, NoPresentInvisible)
1492+{
1493+ nux::ObjectPtr <nux::BaseWindow> window(new nux::BaseWindow(TEXT("")));
1494+ window->ShowWindow(false);
1495+ window->PresentInEmbeddedModeOnThisFrame();
1496+ EXPECT_FALSE(window->AllowPresentationInEmbeddedMode());
1497+}
1498+
1499+TEST_F(EmbeddedContext, AllowPresentationSubsequentQueueDraw)
1500+{
1501+ nux::ObjectPtr <nux::BaseWindow> window(new nux::BaseWindow(TEXT("")));
1502+ nux::HLayout* layout = new nux::HLayout(NUX_TRACKER_LOCATION);
1503+ nux::View* view = new nux::TestView("");
1504+ layout->AddView(view, 1);
1505+ window->SetLayout(layout);
1506+
1507+ /* This will call QueueDraw initially and attempt to add
1508+ * the window to the presentation list */
1509+ window->SetGeometry(nux::Geometry(0, 0, 100, 100));
1510+ EXPECT_FALSE(window->AllowPresentationInEmbeddedMode());
1511+
1512+ /* This will call it again */
1513+ window->ShowWindow(true);
1514+ EXPECT_TRUE(window->AllowPresentationInEmbeddedMode());
1515+}
1516+
1517+TEST_F(EmbeddedContext, StillProcessDrawIfInvisible)
1518+{
1519+ nux::ObjectPtr <nux::BaseWindow> window(new nux::BaseWindow(TEXT("")));
1520+ nux::HLayout* layout = new nux::HLayout(NUX_TRACKER_LOCATION);
1521+ nux::View* view = new nux::TestView("");
1522+ layout->AddView(view, 1);
1523+ window->SetLayout(layout);
1524+ window->ShowWindow(false);
1525+ view->QueueDraw();
1526+ nux::Geometry clip(0, 0, 100, 100);
1527+ WindowThread()->RenderInterfaceFromForeignCmd(&clip);
1528+ EXPECT_FALSE(view->IsRedrawNeeded());
1529+}
1530+
1531+class EmbeddedContextWindow : public EmbeddedContext
1532+{
1533+ public:
1534+
1535+ virtual void SetUp()
1536+ {
1537+ EmbeddedContext::SetUp();
1538+ _base_window = nux::ObjectPtr<nux::BaseWindow>(new nux::BaseWindow(TEXT("")));
1539+ _base_window->ShowWindow(true, false);
1540+
1541+ /* QueueDraw will call PresentInEmbeddedModeOnThisFrame - we
1542+ * need to unset this state in order to test it properly */
1543+ _base_window->OnPresentedInEmbeddedMode();
1544+ _base_window->DoneRedraw();
1545+ }
1546+
1547+ virtual nux::ObjectPtr <nux::BaseWindow> const &
1548+ Window()
1549+ {
1550+ return _base_window;
1551+ }
1552+
1553+ private:
1554+
1555+ nux::ObjectPtr <nux::BaseWindow> _base_window;
1556+};
1557+
1558+class RedrawRequestVerification
1559+{
1560+ public:
1561+
1562+ MOCK_METHOD0(RedrawRequested, void());
1563+};
1564+
1565+TEST_F(EmbeddedContextWindow, AllowPresentationRequestsRedraw)
1566+{
1567+ RedrawRequestVerification verification;
1568+
1569+ EXPECT_CALL(verification, RedrawRequested());
1570+ WindowThread()->RedrawRequested.connect(sigc::mem_fun (&verification,
1571+ &RedrawRequestVerification::RedrawRequested));
1572+ Window()->PresentInEmbeddedModeOnThisFrame();
1573+}
1574+
1575+TEST_F(EmbeddedContextWindow, AllowPresentationAddsToPresentationList)
1576+{
1577+ Window()->PresentInEmbeddedModeOnThisFrame();
1578+ std::vector <nux::Geometry> present_list(WindowThread()->GetPresentationListGeometries());
1579+
1580+ ASSERT_EQ(1, present_list.size());
1581+ EXPECT_EQ(present_list[0], Window()->GetAbsoluteGeometry());
1582+}
1583+
1584+TEST_F(EmbeddedContextWindow, MultipleAllowPresentationAddsToPresentationListUnique)
1585+{
1586+ Window()->PresentInEmbeddedModeOnThisFrame();
1587+ Window()->PresentInEmbeddedModeOnThisFrame();
1588+ std::vector <nux::Geometry> present_list(WindowThread()->GetPresentationListGeometries());
1589+
1590+ ASSERT_EQ(1, present_list.size());
1591+ EXPECT_EQ(present_list[0], Window()->GetAbsoluteGeometry());
1592+}
1593+
1594+TEST_F(EmbeddedContextWindow, OneSetOfGeometryForRePresentOnUnchangedPosition)
1595+{
1596+ Window()->PresentInEmbeddedModeOnThisFrame();
1597+ Window()->OnPresentedInEmbeddedMode();
1598+ Window()->PresentInEmbeddedModeOnThisFrame();
1599+ std::vector <nux::Geometry> present_list(WindowThread()->GetPresentationListGeometries());
1600+
1601+ ASSERT_EQ(1, present_list.size());
1602+ EXPECT_EQ(present_list[0], Window()->GetAbsoluteGeometry());
1603+}
1604+
1605+TEST_F(EmbeddedContextWindow, TwoSetsOfGeometryForRePresentOnChangedPosition)
1606+{
1607+ Window()->PresentInEmbeddedModeOnThisFrame();
1608+ Window()->OnPresentedInEmbeddedMode();
1609+ Window()->PresentInEmbeddedModeOnThisFrame();
1610+ Window()->SetBaseX(Window()->GetBaseX() + 1);
1611+ std::vector <nux::Geometry> present_list(WindowThread()->GetPresentationListGeometries());
1612+
1613+ ASSERT_EQ(2, present_list.size());
1614+ EXPECT_EQ(present_list[0], Window()->GetAbsoluteGeometry());
1615+ EXPECT_EQ(present_list[1], Window()->LastPresentedGeometryInEmbeddedMode());
1616+}
1617+
1618+TEST_F(EmbeddedContextWindow, QueueDrawAddsParentToPresentationList)
1619+{
1620+ nux::HLayout* layout = new nux::HLayout(NUX_TRACKER_LOCATION);
1621+ nux::View* view = new nux::TestView("");
1622+ layout->AddView(view, 1);
1623+ Window()->SetLayout(layout);
1624+ view->QueueDraw();
1625+ std::vector <nux::Geometry> present_list(WindowThread()->GetPresentationListGeometries());
1626+
1627+ ASSERT_EQ(1, present_list.size());
1628+ EXPECT_EQ(present_list[0], Window()->GetAbsoluteGeometry());
1629+}
1630+
1631+class EmbeddedContextMultiWindow : public EmbeddedContext
1632+{
1633+ public:
1634+
1635+ virtual void SetUp()
1636+ {
1637+ EmbeddedContext::SetUp();
1638+ }
1639+
1640+ const nux::ObjectPtr<nux::BaseWindow> & SpawnWindow()
1641+ {
1642+ _base_windows.push_back(nux::ObjectPtr<nux::BaseWindow>(new nux::BaseWindow(TEXT(""))));
1643+ _base_windows.back()->ShowWindow(true, false);
1644+
1645+ /* QueueDraw will call PresentInEmbeddedModeOnThisFrame - we
1646+ * need to unset this state in order to test it properly */
1647+ _base_windows.back()->OnPresentedInEmbeddedMode();
1648+ return _base_windows.back();
1649+ }
1650+
1651+ virtual std::vector <nux::ObjectPtr <nux::BaseWindow> > const &
1652+ Windows()
1653+ {
1654+ return _base_windows;
1655+ }
1656+
1657+ private:
1658+
1659+ std::vector <nux::ObjectPtr <nux::BaseWindow> > _base_windows;
1660+};
1661+
1662+TEST_F(EmbeddedContextMultiWindow, PresentIfIntersectsRectOneWindow)
1663+{
1664+ nux::Geometry geo(0, 0, 100, 100);
1665+ nux::ObjectPtr<nux::BaseWindow> window(SpawnWindow());
1666+ window->SetGeometry(geo);
1667+ WindowThread()->PresentWindowsIntersectingGeometryOnThisFrame(geo);
1668+ EXPECT_TRUE(window->AllowPresentationInEmbeddedMode());
1669+}
1670+
1671+TEST_F(EmbeddedContextMultiWindow, PresentOnlyOneWindow)
1672+{
1673+ nux::Geometry geo(0, 0, 100, 100);
1674+ nux::Geometry outside_geo(0, 101, 100, 100);
1675+ nux::ObjectPtr<nux::BaseWindow> window(SpawnWindow());
1676+ nux::ObjectPtr<nux::BaseWindow> outside(SpawnWindow());
1677+ window->SetGeometry(geo);
1678+ outside->SetGeometry(outside_geo);
1679+
1680+ /* Call ForeignFrameEnded to clear the presentation list set up
1681+ * by making windows visible */
1682+ WindowThread()->ForeignFrameEnded();
1683+
1684+ WindowThread()->PresentWindowsIntersectingGeometryOnThisFrame(geo);
1685+ EXPECT_TRUE(window->AllowPresentationInEmbeddedMode());
1686+ EXPECT_FALSE(outside->AllowPresentationInEmbeddedMode());
1687+}
1688+
1689+TEST_F(EmbeddedContextMultiWindow, PresentBoth)
1690+{
1691+ nux::Geometry geo(0, 0, 100, 101);
1692+ nux::Geometry other_geo(0, 100, 100, 100);
1693+ nux::ObjectPtr<nux::BaseWindow> window(SpawnWindow());
1694+ nux::ObjectPtr<nux::BaseWindow> other(SpawnWindow());
1695+ window->SetGeometry(geo);
1696+ other->SetGeometry(other_geo);
1697+ WindowThread()->PresentWindowsIntersectingGeometryOnThisFrame(geo);
1698+ EXPECT_TRUE(window->AllowPresentationInEmbeddedMode());
1699+ EXPECT_TRUE(other->AllowPresentationInEmbeddedMode());
1700+}
1701+
1702+TEST_F(EmbeddedContextMultiWindow, ForeignFrameEndedPresentNone)
1703+{
1704+ nux::Geometry geo(0, 0, 100, 100);
1705+ nux::ObjectPtr<nux::BaseWindow> window(SpawnWindow());
1706+ window->SetGeometry(geo);
1707+ WindowThread()->PresentWindowsIntersectingGeometryOnThisFrame(geo);
1708+ WindowThread()->ForeignFrameEnded();
1709+ EXPECT_FALSE(window->AllowPresentationInEmbeddedMode());
1710+ EXPECT_TRUE(WindowThread()->GetPresentationListGeometries().empty());
1711+}
1712+
1713+TEST_F(EmbeddedContextMultiWindow, AddToPresentationListFailsAfterCutoff)
1714+{
1715+ nux::ObjectPtr<nux::BaseWindow> windowOne(SpawnWindow());
1716+ nux::ObjectPtr<nux::BaseWindow> windowTwo(SpawnWindow());
1717+ WindowThread()->ForeignFrameEnded();
1718+ EXPECT_TRUE(WindowThread()->AddToPresentationList(windowOne.GetPointer(), false));
1719+ WindowThread()->ForeignFrameCutoff();
1720+ EXPECT_FALSE(WindowThread()->AddToPresentationList(windowTwo.GetPointer(), false));
1721+}
1722+
1723+TEST_F(EmbeddedContextMultiWindow, NoPresentInEmbeddedOnThisFrameAfterFrameCutoff)
1724+{
1725+ nux::ObjectPtr<nux::BaseWindow> windowOne(SpawnWindow());
1726+ nux::ObjectPtr<nux::BaseWindow> windowTwo(SpawnWindow());
1727+ WindowThread()->ForeignFrameEnded();
1728+ windowOne->PresentInEmbeddedModeOnThisFrame();
1729+ WindowThread()->ForeignFrameCutoff();
1730+ windowTwo->PresentInEmbeddedModeOnThisFrame();
1731+ EXPECT_TRUE(windowOne->AllowPresentationInEmbeddedMode());
1732+ EXPECT_FALSE(windowTwo->AllowPresentationInEmbeddedMode());
1733+}
1734+
1735+TEST_F(EmbeddedContextMultiWindow, PresentInEmbeddedOnThisFrameAfterFrameCutoffIfForced)
1736+{
1737+ nux::ObjectPtr<nux::BaseWindow> windowOne(SpawnWindow());
1738+ nux::ObjectPtr<nux::BaseWindow> windowTwo(SpawnWindow());
1739+ WindowThread()->ForeignFrameEnded();
1740+ windowOne->PresentInEmbeddedModeOnThisFrame();
1741+ WindowThread()->ForeignFrameCutoff();
1742+ windowTwo->PresentInEmbeddedModeOnThisFrame(true);
1743+ EXPECT_TRUE(windowOne->AllowPresentationInEmbeddedMode());
1744+ EXPECT_TRUE(windowTwo->AllowPresentationInEmbeddedMode());
1745+}
1746+
1747+TEST_F(EmbeddedContextMultiWindow, MoveToPresentationListAfterFrameEndedIfCaughtInCutoff)
1748+{
1749+ nux::ObjectPtr<nux::BaseWindow> windowOne(SpawnWindow());
1750+ nux::ObjectPtr<nux::BaseWindow> windowTwo(SpawnWindow());
1751+ WindowThread()->ForeignFrameEnded();
1752+ windowOne->PresentInEmbeddedModeOnThisFrame();
1753+ WindowThread()->ForeignFrameCutoff();
1754+ windowTwo->PresentInEmbeddedModeOnThisFrame();
1755+ WindowThread()->ForeignFrameEnded();
1756+ EXPECT_FALSE(windowOne->AllowPresentationInEmbeddedMode());
1757+ EXPECT_TRUE(windowTwo->AllowPresentationInEmbeddedMode());
1758+}
1759+
1760
1761 }
1762
1763=== modified file 'tests/gtest-nuxgraphics-texture.cpp'
1764--- tests/gtest-nuxgraphics-texture.cpp 2013-07-03 22:28:18 +0000
1765+++ tests/gtest-nuxgraphics-texture.cpp 2013-07-22 17:20:34 +0000
1766@@ -60,6 +60,22 @@
1767 }
1768 }
1769
1770+TEST_F(TestTextures, TestCreateTexture2DFromIDAssignemnt)
1771+{
1772+ GLuint texid;
1773+ glGenTextures(1, &texid);
1774+ glBindTexture(GL_TEXTURE_2D, texid);
1775+ nux::ObjectPtr<nux::IOpenGLTexture2D> foreign_texture_as_nux_texture
1776+ (nux::GetGraphicsDisplay()->GetGpuDevice()->CreateTexture2DFromID(
1777+ texid,
1778+ 1, 1,
1779+ 0,
1780+ nux::BITFMT_B8G8R8A8));
1781+ EXPECT_EQ(texid, foreign_texture_as_nux_texture->GetOpenGLID());
1782+ glBindTexture(GL_TEXTURE_2D, 0);
1783+ glDeleteTextures(1, &texid);
1784+}
1785+
1786 TEST_F(TestTextures, FallbackTexture2DFromFile)
1787 {
1788 ASSERT_THAT(CreateTexture2DFromFile(nullptr, -1, false), IsNull());
1789@@ -102,4 +118,4 @@
1790 ASSERT_THAT(LoadTextureFromFile(std::string()), NotNull());
1791 }
1792
1793-}
1794\ No newline at end of file
1795+}

Subscribers

People subscribed via source and target branches