diff --git a/12to11.c b/12to11.c
index fcc3ec2..47f4197 100644
--- a/12to11.c
+++ b/12to11.c
@@ -28,14 +28,6 @@ along with 12to11. If not, see . */
/* Globals. */
Compositor compositor;
-static Colormap
-MakeColormap (void)
-{
- return XCreateColormap (compositor.display,
- DefaultRootWindow (compositor.display),
- compositor.visual, AllocNone);
-}
-
static void
DetermineServerTime (void)
{
@@ -112,10 +104,6 @@ XLMain (int argc, char **argv)
up. */
InitRenderers ();
- /* Set up the colormap. Initializing renderers should also cause
- the visual to be set. */
- compositor.colormap = MakeColormap ();
-
XLInitRROutputs ();
XLInitCompositor ();
XLInitSurfaces ();
diff --git a/compositor.h b/compositor.h
index aa4ea82..5fe1d48 100644
--- a/compositor.h
+++ b/compositor.h
@@ -187,7 +187,9 @@ enum
{
/* The render target always preserves previously drawn contents;
IOW, target_age always returns 0. */
- NeverAges = 1,
+ NeverAges = 1,
+ /* Buffers attached can always be immediately released. */
+ ImmediateRelease = 1 << 2,
};
struct _RenderFuncs
@@ -319,6 +321,10 @@ struct _BufferFuncs
be performed on the buffer. */
void (*update_buffer_for_damage) (RenderBuffer, pixman_region32_t *);
+ /* Return whether or not the buffer contents can be released early,
+ by being copied to an offscreen buffer. */
+ Bool (*can_release_now) (RenderBuffer);
+
/* Called during renderer initialization. */
void (*init_buffer_funcs) (void);
};
@@ -358,6 +364,7 @@ extern Bool RenderValidateShmParams (uint32_t, uint32_t, uint32_t, int32_t,
extern void RenderFreeShmBuffer (RenderBuffer);
extern void RenderFreeDmabufBuffer (RenderBuffer);
extern void RenderUpdateBufferForDamage (RenderBuffer, pixman_region32_t *);
+extern Bool RenderCanReleaseNow (RenderBuffer);
/* Defined in run.c. */
@@ -614,6 +621,10 @@ enum
PendingFrameCallbacks = (1 << 6),
PendingBufferScale = (1 << 7),
PendingAttachments = (1 << 8),
+
+ /* Flags here are stored in `pending' of the current state for
+ space reasons. */
+ BufferAlreadyReleased = (1 << 19),
};
struct _FrameCallback
@@ -642,7 +653,8 @@ struct _State
/* The currently attached buffer. */
ExtBuffer *buffer;
- /* What part of this state has been changed. */
+ /* What part of this state has been changed, and some more
+ flags. */
int pending;
/* The scale of this buffer. */
diff --git a/egl.c b/egl.c
index a110247..83df4d0 100644
--- a/egl.c
+++ b/egl.c
@@ -33,6 +33,8 @@ along with 12to11. If not, see . */
#include
#include
+#include "linux-dmabuf-unstable-v1.h"
+
/* These are flags for the DrmFormats. */
enum
@@ -108,7 +110,9 @@ struct _FormatInfo
enum
{
IsTextureGenerated = 1,
- HasAlpha = 2,
+ HasAlpha = (1 << 2),
+ CanRelease = (1 << 3),
+ InvertY = (1 << 4),
};
struct _EglBuffer
@@ -180,6 +184,9 @@ struct _CompositeProgram
/* The index of the scale uniform. */
GLuint scale;
+
+ /* The index of the invert_y uniform. */
+ GLuint invert_y;
};
/* All known SHM formats. */
@@ -714,6 +721,8 @@ EglCompileCompositeProgram (CompositeProgram *program,
"texture");
program->scale = glGetUniformLocation (program->program,
"scale");
+ program->invert_y = glGetUniformLocation (program->program,
+ "invert_y");
/* Now delete the shaders. */
glDeleteShader (vertex);
@@ -1210,6 +1219,7 @@ Composite (RenderBuffer buffer, RenderTarget target,
glUniform1i (program->texture, 0);
glUniform1f (program->scale, egl_buffer->scale);
+ glUniform1i (program->invert_y, egl_buffer->flags & InvertY);
glVertexAttribPointer (program->position, 2, GL_FLOAT,
GL_FALSE, 0, verts);
glVertexAttribPointer (program->texcoord, 2, GL_FLOAT,
@@ -1282,7 +1292,7 @@ static RenderFuncs egl_render_funcs =
.reset_transform = ResetTransform,
.finish_render = FinishRender,
.target_age = TargetAge,
- .flags = 0,
+ .flags = ImmediateRelease,
};
static DrmFormat *
@@ -1384,6 +1394,11 @@ BufferFromDmaBuf (DmaBufAttributes *attributes, Bool *error)
&& attributes->modifier != DRM_FORMAT_MOD_INVALID)
goto error;
+ /* Set invert_y based on flags. */
+ if (attributes->flags
+ & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT)
+ buffer->flags |= InvertY;
+
/* Otherwise, import the buffer now. */
attribs[i++] = EGL_WIDTH;
attribs[i++] = attributes->width;
@@ -1836,6 +1851,10 @@ UpdateTexture (EglBuffer *buffer)
/* Unset the row length. */
glPixelStorei (GL_UNPACK_ROW_LENGTH_EXT, 0);
+
+ /* The buffer's been copied to the texture. It can now be
+ released. */
+ buffer->flags |= CanRelease;
}
/* Bind the target to nothing. */
@@ -1909,6 +1928,10 @@ UpdateShmBufferIncrementally (EglBuffer *buffer, pixman_region32_t *damage)
/* Unbind from the texturing target. */
glBindTexture (target, 0);
+
+ /* The buffer's been copied to the texture. It can now be
+ released. */
+ buffer->flags |= CanRelease;
}
static void
@@ -1949,7 +1972,7 @@ UpdateBuffer (RenderBuffer buffer, pixman_region32_t *damage)
if (egl_buffer->u.type == ShmBuffer)
UpdateTexture (egl_buffer);
}
- else
+ else if (pixman_region32_not_empty (damage))
{
switch (egl_buffer->u.type)
{
@@ -1990,6 +2013,23 @@ UpdateBufferForDamage (RenderBuffer buffer, pixman_region32_t *damage)
UpdateBuffer (buffer, damage);
}
+static Bool
+CanReleaseNow (RenderBuffer buffer)
+{
+ EglBuffer *egl_buffer;
+ Bool rc;
+
+ egl_buffer = buffer.pointer;
+
+ /* Return if texture contents were copied. */
+ rc = (egl_buffer->flags & CanRelease) != 0;
+
+ /* Clear that flag now. */
+ egl_buffer->flags &= ~CanRelease;
+
+ return rc;
+}
+
static BufferFuncs egl_buffer_funcs =
{
.get_drm_formats = GetDrmFormats,
@@ -2002,6 +2042,7 @@ static BufferFuncs egl_buffer_funcs =
.free_shm_buffer = FreeShmBuffer,
.free_dmabuf_buffer = FreeDmabufBuffer,
.update_buffer_for_damage = UpdateBufferForDamage,
+ .can_release_now = CanReleaseNow,
.init_buffer_funcs = InitBufferFuncs,
};
diff --git a/picture_renderer.c b/picture_renderer.c
index b9274e8..895dbc1 100644
--- a/picture_renderer.c
+++ b/picture_renderer.c
@@ -418,17 +418,41 @@ FindSupportedFormats (void)
return supported;
}
+static Window
+MakeCheckWindow (void)
+{
+ XSetWindowAttributes attrs;
+ unsigned long flags;
+
+ /* Make an override-redirect window to use as the icon surface. */
+ flags = (CWColormap | CWBorderPixel | CWEventMask
+ | CWOverrideRedirect);
+ attrs.colormap = compositor.colormap;
+ attrs.border_pixel = border_pixel;
+ attrs.event_mask = (ExposureMask | StructureNotifyMask);
+ attrs.override_redirect = 1;
+
+ return XCreateWindow (compositor.display,
+ DefaultRootWindow (compositor.display),
+ 0, 0, 1, 1, 0, compositor.n_planes,
+ InputOutput, compositor.visual, flags,
+ &attrs);
+}
+
static void
FindSupportedModifiers (int *pair_count_return)
{
- Window root_window;
+ Window check_window;
xcb_dri3_get_supported_modifiers_cookie_t *cookies;
xcb_dri3_get_supported_modifiers_reply_t *reply;
int i, length, pair_count;
uint64_t *mods;
cookies = alloca (sizeof *cookies * ArrayElements (all_formats));
- root_window = DefaultRootWindow (compositor.display);
+
+ /* Create a temporary window similar to ones surfaces will use to
+ determine which modifiers are supported. */
+ check_window = MakeCheckWindow ();
pair_count = 0;
for (i = 0; i < ArrayElements (all_formats); ++i)
@@ -437,7 +461,7 @@ FindSupportedModifiers (int *pair_count_return)
{
cookies[i]
= xcb_dri3_get_supported_modifiers (compositor.conn,
- root_window, all_formats[i].depth,
+ check_window, all_formats[i].depth,
all_formats[i].bits_per_pixel);
/* pair_count is the number of format-modifier pairs that
@@ -447,6 +471,9 @@ FindSupportedModifiers (int *pair_count_return)
}
}
+ /* Delete the temporary window used to query for modifiers. */
+ XDestroyWindow (compositor.display, check_window);
+
for (i = 0; i < ArrayElements (all_formats); ++i)
{
if (!all_formats[i].format)
@@ -1042,6 +1069,12 @@ InitBufferFuncs (void)
free (reply);
}
+static Bool
+CanReleaseNow (RenderBuffer buffer)
+{
+ return False;
+}
+
static BufferFuncs picture_buffer_funcs =
{
.get_drm_formats = GetDrmFormats,
@@ -1053,6 +1086,7 @@ static BufferFuncs picture_buffer_funcs =
.validate_shm_params = ValidateShmParams,
.free_shm_buffer = FreeShmBuffer,
.free_dmabuf_buffer = FreeDmabufBuffer,
+ .can_release_now = CanReleaseNow,
.init_buffer_funcs = InitBufferFuncs,
};
diff --git a/renderer.c b/renderer.c
index de3d53b..110b68c 100644
--- a/renderer.c
+++ b/renderer.c
@@ -233,6 +233,12 @@ RenderUpdateBufferForDamage (RenderBuffer buffer, pixman_region32_t *damage)
buffer_funcs.update_buffer_for_damage (buffer, damage);
}
+Bool
+RenderCanReleaseNow (RenderBuffer buffer)
+{
+ return buffer_funcs.can_release_now (buffer);
+}
+
void
RegisterStaticRenderer (const char *name,
RenderFuncs *render_funcs,
@@ -261,6 +267,12 @@ InstallRenderer (Renderer *renderer)
/* If this returns false, then the renderer cannot be used. */
return False;
+ /* Next, initialize the colormap before init_buffer_funcs. */
+ compositor.colormap
+ = XCreateColormap (compositor.display,
+ DefaultRootWindow (compositor.display),
+ compositor.visual, AllocNone);
+
buffer_funcs.init_buffer_funcs ();
/* Finally, set the flags. The idea is that init_render_funcs
diff --git a/shaders.txt b/shaders.txt
index 09c511b..2b4249d 100644
--- a/shaders.txt
+++ b/shaders.txt
@@ -23,7 +23,7 @@ main (void)
void
main (void)
{
- gl_FragColor = vec4 (0.0, 0.0, 0.0, 1.0);
+ gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0);
}
//==
@@ -44,12 +44,20 @@ main (void)
precision mediump float;
uniform sampler2D texture;
uniform float scale;
+uniform bool invert_y;
varying vec2 v_texcoord;
void
main (void)
{
- gl_FragColor = texture2D (texture, v_texcoord / scale);
+ vec2 texcoord;
+
+ texcoord = v_texcoord / scale;
+
+ if (invert_y)
+ texcoord = vec2 (texcoord.x, 1.0 - texcoord.y);
+
+ gl_FragColor = texture2D (texture, texcoord);
}
//==
@@ -57,13 +65,21 @@ main (void)
precision mediump float;
uniform sampler2D texture;
uniform float scale;
+uniform bool invert_y;
varying vec2 v_texcoord;
void
main (void)
{
+ vec2 texcoord;
+
+ texcoord = v_texcoord / scale;
+
+ if (invert_y)
+ texcoord = vec2 (texcoord.x, 1.0 - texcoord.y);
+
gl_FragColor = vec4 (texture2D (texture,
- v_texcoord / scale).rgb,
+ texcoord).rgb,
1.0);
}
//==
@@ -74,11 +90,19 @@ main (void)
precision mediump float;
uniform samplerExternalOES texture;
uniform float scale;
+uniform bool invert_y;
varying vec2 v_texcoord;
void
main (void)
{
- gl_FragColor = texture2D (texture, v_texcoord / scale);
+ vec2 texcoord;
+
+ texcoord = v_texcoord / scale;
+
+ if (invert_y)
+ texcoord = vec2 (texcoord.x, 1.0 - texcoord.y);
+
+ gl_FragColor = texture2D (texture, texcoord);
}
//==
diff --git a/subcompositor.c b/subcompositor.c
index 4db9f5c..0879d11 100644
--- a/subcompositor.c
+++ b/subcompositor.c
@@ -1913,16 +1913,20 @@ SubcompositorUpdate (Subcompositor *subcompositor)
-list->view->abs_y);
}
+ /* Update the attached buffer from the damage. This is only
+ required on some backends, where we have to upload data
+ from a shared memory buffer to the graphics hardware.
+
+ The update is performed even when there is no damage,
+ because the initial data might need to be uploaded.
+ However, the function does not perform partial updates
+ when the damage region is empty. */
+
+ buffer = XLRenderBufferFromBuffer (view->buffer);
+ RenderUpdateBufferForDamage (buffer, &list->view->damage);
+
if (pixman_region32_not_empty (&list->view->damage))
{
- /* Update the attached buffer from the damage. This is
- only required on some backends, where we have to
- upload data from a shared memory buffer to the
- graphics hardware. */
-
- buffer = XLRenderBufferFromBuffer (view->buffer);
- RenderUpdateBufferForDamage (buffer, &list->view->damage);
-
/* Translate the region into the subcompositor
coordinate space. */
pixman_region32_translate (&list->view->damage,
diff --git a/surface.c b/surface.c
index 3ea9110..507bb8b 100644
--- a/surface.c
+++ b/surface.c
@@ -653,7 +653,7 @@ SavePendingState (Surface *surface)
&& (surface->cached_state.buffer
!= surface->current_state.buffer))
{
- if (surface->role)
+ if (surface->role && !(renderer_flags & ImmediateRelease))
surface->role->funcs.release_buffer (surface, surface->role,
surface->cached_state.buffer);
else
@@ -720,6 +720,41 @@ SavePendingState (Surface *surface)
surface->pending_state.pending = PendingNone;
}
+static void
+TryEarlyRelease (Surface *surface)
+{
+ ExtBuffer *buffer;
+ RenderBuffer render_buffer;
+
+ /* The rendering backend may have copied the contents of, i.e., a
+ shared memory buffer to a backing texture. In that case buffers
+ can be released immediately after commit. Programs such as GTK
+ also rely on the compositor performing such an optimization, or
+ else they will constantly create new buffers to back their back
+ buffer contents. */
+
+ buffer = surface->current_state.buffer;
+
+ if (!buffer)
+ return;
+
+ /* Get the render buffer. */
+ render_buffer = XLRenderBufferFromBuffer (buffer);
+
+ /* Don't release immediately if not okay. */
+ if (!RenderCanReleaseNow (render_buffer))
+ return;
+
+ /* Release the buffer now. */
+ if (surface->role && !(renderer_flags & ImmediateRelease))
+ surface->role->funcs.release_buffer (surface, surface->role, buffer);
+ else
+ XLReleaseBuffer (buffer);
+
+ /* Set the flag saying that the buffer has been released. */
+ surface->current_state.pending |= BufferAlreadyReleased;
+}
+
static void
InternalCommit (Surface *surface, State *pending)
{
@@ -728,15 +763,22 @@ InternalCommit (Surface *surface, State *pending)
if (pending->pending & PendingBuffer)
{
if ((surface->current_state.buffer != pending->buffer)
+ /* The buffer may already released if its contents were
+ copied, i.e. uploaded to a texture, during updates. */
+ && !(surface->current_state.pending & BufferAlreadyReleased)
&& surface->current_state.buffer)
{
- if (surface->role)
+ if (surface->role && !(renderer_flags & ImmediateRelease))
surface->role->funcs.release_buffer (surface, surface->role,
surface->current_state.buffer);
else
XLReleaseBuffer (surface->current_state.buffer);
}
+ /* Clear this flag now, since the attached buffer has
+ changed. */
+ surface->current_state.pending &= ~BufferAlreadyReleased;
+
if (pending->buffer)
{
AttachBuffer (&surface->current_state,
@@ -812,6 +854,8 @@ InternalCommit (Surface *surface, State *pending)
}
}
+ /* Run commit callbacks. This tells synchronous subsurfaces to
+ update. */
RunCommitCallbacks (surface);
if (surface->subsurfaces)
@@ -829,6 +873,11 @@ InternalCommit (Surface *surface, State *pending)
surface->role->funcs.commit (surface, surface->role);
pending->pending = PendingNone;
+
+ /* Release the attached buffer if possible. The role may have
+ called SubcompositorUpdate, leading to the buffer contents being
+ copied. */
+ TryEarlyRelease (surface);
}
static void