Repository: AvaloniaUI/Avalonia
File: src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaExternalObjectsFeature.cs (C#)

Code before:
                return TakeSnapshot();
            }
            finally
            {
                signal.Semaphore.SignalTimelineSemaphore(_image, signalValue);
            }
        }
    }

    public IBitmapImpl SnapshotWithAutomaticSync()
    {
        using (_gpu.EnsureCurrent())
            return TakeSnapshot();
    }

    private int CopyToNewTexture(int textureType, int sourceTextureId, int internalFormat, int width, int height)
    {
        var gl = _gpu.GlContext.GlInterface;

        using var _ = _gpu.EnsureCurrent();

        if (_fbo == 0)
            _fbo = gl.GenFramebuffer();

        // Bind source texture
        gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo);
        gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureType, sourceTextureId, 0);

        // Create destination texture
        var destTextureId = gl.GenTexture();
        gl.BindTexture(textureType, destTextureId);
        gl.TexImage2D(textureType, 0, internalFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero);

        // Copy
        gl.BindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
        gl.CopyTexSubImage2D(textureType, 0, 0, 0, 0, 0, width, height);

        gl.Flush();

        return destTextureId;
    }
}


Review comment (suggestion):
It _might_ be a good idea to save previous `GL_FRAMEBUFFER`(`_BINDING`) / `GL_READ_FRAMEBUFFER`(`_BINDING`) and `GL_SCISSOR_TEST` values and restore those afterwards.

Not necessary right now since the entire texture import operation currently happens before Skia calls, but it could save us a `GrContext.Reset()` call later

Code after:
    public IBitmapImpl SnapshotWithTimelineSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
        ulong waitForValue, IPlatformRenderInterfaceImportedSemaphore signalSemaphore, ulong signalValue)
    {
        if (_image is null)
        {
            throw new NotSupportedException("Only supported with an external image");
        }

        var wait = (GlSkiaImportedSemaphore)waitForSemaphore;
        var signal = (GlSkiaImportedSemaphore)signalSemaphore;
        using (_gpu.EnsureCurrent())
        {
            wait.Semaphore.WaitTimelineSemaphore(_image, waitForValue);
            try
            {
                return TakeSnapshot();
            }
            finally
            {
                signal.Semaphore.SignalTimelineSemaphore(_image, signalValue);
            }
        }
    }

    public IBitmapImpl SnapshotWithAutomaticSync()
    {
        using (_gpu.EnsureCurrent())
            return TakeSnapshot();
    }

    private int CopyToNewTexture(int textureType, int sourceTextureId, int internalFormat, int width, int height)
    {
        var gl = _gpu.GlContext.GlInterface;

        using var _ = _gpu.EnsureCurrent();

        // Snapshot current values
        gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFbo);
        gl.GetIntegerv(GL_SCISSOR_TEST, out var oldScissorTest);

        // Bind source texture
        gl.BindFramebuffer(GL_FRAMEBUFFER, _fboProvider.Fbo);
        gl.Disable(GL_SCISSOR_TEST);
        gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureType, sourceTextureId, 0);

        // Create destination texture
        var destTextureId = gl.GenTexture();
        gl.BindTexture(textureType, destTextureId);
        gl.TexImage2D(textureType, 0, internalFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero);

        // Copy