Mir

Merge lp:~vanvugt/mir/fix-1423462 into lp:mir

Proposed by Daniel van Vugt
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 3057
Proposed branch: lp:~vanvugt/mir/fix-1423462
Merge into: lp:mir
Diff against target: 191 lines (+103/-15)
5 files modified
playground/demo-shell/demo_renderer.cpp (+0/-4)
src/renderers/gl/renderer.cpp (+48/-11)
tests/include/mir/test/doubles/mock_gl.h (+1/-0)
tests/mir_test_doubles/mock_gl.cpp (+7/-0)
tests/unit-tests/renderers/gl/test_gl_renderer.cpp (+47/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/fix-1423462
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Approve
Daniel van Vugt Abstain
Andreas Pokorny (community) Approve
Alexandros Frantzis (community) Approve
Review via email: mp+275661@code.launchpad.net

Commit message

Avoid spuriously blending RGBX as RGBA (LP: #1423462)

Not only was it sub-optimal in performance but more importantly caused
unpredictable discolouration of RGBX clients (first observed by 3v1n0 in
mplayer-SDL but more recently also seen in Xmir -sw). Any clients that
filled the 'X' byte with 0xff were unaffected.

Description of the change

Manual test case:
Force a client to use an RGBX pixel format with alpha values below 0xff:
  $ mir_demo_client_multiwin -p2 # or -p4
Before the fix: Transparent windows.
After the fix: No transparency.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

Style nit:

+ if (renderable.shaped())

+ if (blend_func_dest == GL_ZERO)
+ glDisable(GL_BLEND);

If any clause of the if-elseif-else statement uses curly braces then all clauses (even single line ones) should use it for consistency.

review: Approve
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

> Looks good.
>
> Style nit:
>
> + if (renderable.shaped())
>
> + if (blend_func_dest == GL_ZERO)
> + glDisable(GL_BLEND);
>
> If any clause of the if-elseif-else statement uses curly braces then all
> clauses (even single line ones) should use it for consistency.

yes this looks a bit awkward...
feel free to cleanup, lgtm otherwise

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Hmm, if the framebuffer honours alpha then there's still going to be a minor problem with translucent RGBX windows passing through some uninitialized alpha data.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) :
review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I see no obvious problems

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'playground/demo-shell/demo_renderer.cpp'
2--- playground/demo-shell/demo_renderer.cpp 2015-10-06 02:30:52 +0000
3+++ playground/demo-shell/demo_renderer.cpp 2015-10-26 13:25:32 +0000
4@@ -311,10 +311,6 @@
5 tl_shadow.vertices[1] = {{leftr, top, 0.0f}, {1.0f, 0.0f}};
6 tl_shadow.vertices[2] = {{leftr, topr, 0.0f}, {1.0f, 1.0f}};
7 tl_shadow.vertices[3] = {{left, topr, 0.0f}, {0.0f, 1.0f}};
8-
9- // Shadows always need blending...
10- glEnable(GL_BLEND);
11- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
12 }
13
14 void DemoRenderer::tessellate_frame(std::vector<gl::Primitive>& primitives,
15
16=== modified file 'src/renderers/gl/renderer.cpp'
17--- src/renderers/gl/renderer.cpp 2015-10-06 02:30:52 +0000
18+++ src/renderers/gl/renderer.cpp 2015-10-26 13:25:32 +0000
19@@ -209,16 +209,6 @@
20 void mrg::Renderer::draw(mg::Renderable const& renderable,
21 Renderer::Program const& prog) const
22 {
23- if (renderable.alpha() < 1.0f || renderable.shaped())
24- {
25- glEnable(GL_BLEND);
26- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
27- }
28- else
29- {
30- glDisable(GL_BLEND);
31- }
32-
33 glUseProgram(prog.id);
34 if (prog.last_used_frameno != frameno)
35 { // Avoid reloading the screen-global uniforms on every renderable
36@@ -253,12 +243,48 @@
37
38 auto surface_tex = texture_cache->load(renderable);
39
40+ typedef struct // Represents parameters of glBlendFuncSeparate()
41+ {
42+ GLenum src_rgb, dst_rgb, src_alpha, dst_alpha;
43+ } BlendSeparate;
44+
45+ BlendSeparate client_blend;
46+
47+ // These renderable method names could be better (see LP: #1236224)
48+ if (renderable.shaped()) // Client is RGBA:
49+ {
50+ client_blend = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA,
51+ GL_ONE, GL_ONE_MINUS_SRC_ALPHA};
52+ }
53+ else if (renderable.alpha() == 1.0f) // RGBX and no window translucency:
54+ {
55+ client_blend = {GL_ONE, GL_ZERO,
56+ GL_ZERO, GL_ONE}; // Avoid using src_alpha!
57+ }
58+ else
59+ { // Client is RGBX but we also have window translucency.
60+ // The texture alpha channel is possibly uninitialized so we must be
61+ // careful and avoid using SRC_ALPHA (LP: #1423462).
62+ client_blend = {GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA,
63+ GL_ZERO, GL_ONE};
64+ glBlendColor(0.0f, 0.0f, 0.0f, renderable.alpha());
65+ }
66+
67 for (auto const& p : primitives)
68 {
69+ BlendSeparate blend;
70+
71 if (p.tex_id == 0) // The client surface texture
72+ {
73+ blend = client_blend;
74 surface_tex->bind();
75- else // Some other texture from the shell (e.g. decorations)
76+ }
77+ else // Some other texture from the shell (e.g. decorations) which
78+ { // is always RGBA (valid SRC_ALPHA).
79+ blend = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA,
80+ GL_ONE, GL_ONE_MINUS_SRC_ALPHA};
81 glBindTexture(GL_TEXTURE_2D, p.tex_id);
82+ }
83
84 glVertexAttribPointer(prog.position_attr, 3, GL_FLOAT,
85 GL_FALSE, sizeof(mgl::Vertex),
86@@ -267,6 +293,17 @@
87 GL_FALSE, sizeof(mgl::Vertex),
88 &p.vertices[0].texcoord);
89
90+ if (blend.dst_rgb == GL_ZERO)
91+ {
92+ glDisable(GL_BLEND);
93+ }
94+ else
95+ {
96+ glEnable(GL_BLEND);
97+ glBlendFuncSeparate(blend.src_rgb, blend.dst_rgb,
98+ blend.src_alpha, blend.dst_alpha);
99+ }
100+
101 glDrawArrays(p.type, 0, p.nvertices);
102 }
103
104
105=== modified file 'tests/include/mir/test/doubles/mock_gl.h'
106--- tests/include/mir/test/doubles/mock_gl.h 2015-07-16 07:03:19 +0000
107+++ tests/include/mir/test/doubles/mock_gl.h 2015-10-26 13:25:32 +0000
108@@ -44,6 +44,7 @@
109 MOCK_METHOD2(glBindRenderbuffer, void(GLenum, GLuint));
110 MOCK_METHOD2(glBindTexture, void(GLenum, GLuint));
111 MOCK_METHOD2(glBlendFunc, void(GLenum, GLenum));
112+ MOCK_METHOD4(glBlendFuncSeparate, void(GLenum, GLenum, GLenum, GLenum));
113 MOCK_METHOD4(glBufferData,
114 void(GLenum, GLsizeiptr, const GLvoid *, GLenum));
115 MOCK_METHOD1(glCheckFramebufferStatus, GLenum(GLenum));
116
117=== modified file 'tests/mir_test_doubles/mock_gl.cpp'
118--- tests/mir_test_doubles/mock_gl.cpp 2015-07-16 07:03:19 +0000
119+++ tests/mir_test_doubles/mock_gl.cpp 2015-10-26 13:25:32 +0000
120@@ -135,6 +135,13 @@
121 global_mock_gl->glBlendFunc (src, dst);
122 }
123
124+void glBlendFuncSeparate(GLenum src_rgb, GLenum dst_rgb,
125+ GLenum src_alpha, GLenum dst_alpha)
126+{
127+ CHECK_GLOBAL_VOID_MOCK();
128+ global_mock_gl->glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha);
129+}
130+
131 void glActiveTexture(GLenum unit)
132 {
133 CHECK_GLOBAL_VOID_MOCK();
134
135=== modified file 'tests/unit-tests/renderers/gl/test_gl_renderer.cpp'
136--- tests/unit-tests/renderers/gl/test_gl_renderer.cpp 2015-10-06 02:30:52 +0000
137+++ tests/unit-tests/renderers/gl/test_gl_renderer.cpp 2015-10-26 13:25:32 +0000
138@@ -166,6 +166,53 @@
139 renderer.render(renderable_list);
140 }
141
142+TEST_F(GLRenderer, enables_blending_for_rgba_surfaces)
143+{
144+ EXPECT_CALL(*renderable, shaped()).WillOnce(Return(true));
145+ EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0);
146+ EXPECT_CALL(mock_gl, glEnable(GL_BLEND));
147+
148+ mrg::Renderer renderer(display_buffer);
149+ renderer.render(renderable_list);
150+}
151+
152+TEST_F(GLRenderer, enables_blending_for_rgbx_translucent_surfaces)
153+{
154+ EXPECT_CALL(*renderable, alpha()).WillRepeatedly(Return(0.5f));
155+ EXPECT_CALL(*renderable, shaped()).WillOnce(Return(false));
156+ EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0);
157+ EXPECT_CALL(mock_gl, glEnable(GL_BLEND));
158+
159+ mrg::Renderer renderer(display_buffer);
160+ renderer.render(renderable_list);
161+}
162+
163+TEST_F(GLRenderer, uses_premultiplied_src_alpha_for_rgba_surfaces)
164+{
165+ EXPECT_CALL(*renderable, shaped()).WillOnce(Return(true));
166+ EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0);
167+ EXPECT_CALL(mock_gl, glEnable(GL_BLEND));
168+ EXPECT_CALL(mock_gl, glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA,
169+ GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
170+
171+ mrg::Renderer renderer(display_buffer);
172+ renderer.render(renderable_list);
173+}
174+
175+TEST_F(GLRenderer, avoids_src_alpha_for_rgbx_blending) // LP: #1423462
176+{
177+ EXPECT_CALL(*renderable, alpha()).WillRepeatedly(Return(0.5f));
178+ EXPECT_CALL(*renderable, shaped()).WillOnce(Return(false));
179+ EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0);
180+ EXPECT_CALL(mock_gl, glEnable(GL_BLEND));
181+ EXPECT_CALL(mock_gl,
182+ glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA,
183+ GL_ZERO, GL_ONE));
184+
185+ mrg::Renderer renderer(display_buffer);
186+ renderer.render(renderable_list);
187+}
188+
189 TEST_F(GLRenderer, binds_for_every_primitive_when_tessellate_is_overridden)
190 {
191 //'listening to the tests', it would be a bit easier to use a tessellator mock of some sort

Subscribers

People subscribed via source and target branches