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