summaryrefslogtreecommitdiff
path: root/vcl/skia/x11/gdiimpl.cxx
blob: 29fb15d27140ea65489be82101b79dde26fa504d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Some of this code is based on Skia source code, covered by the following
 * license notice (see readlicense_oo for the full license):
 *
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 */

#include <skia/x11/gdiimpl.hxx>

#include <tools/sk_app/unix/WindowContextFactory_unix.h>

#include <skia/utils.hxx>
#include <skia/zone.hxx>

#include <X11/Xutil.h>

X11SkiaSalGraphicsImpl::X11SkiaSalGraphicsImpl(X11SalGraphics& rParent)
    : SkiaSalGraphicsImpl(rParent, rParent.GetGeometryProvider())
    , mX11Parent(rParent)
{
}

X11SkiaSalGraphicsImpl::~X11SkiaSalGraphicsImpl() {}

void X11SkiaSalGraphicsImpl::Init()
{
    // The m_pFrame and m_pVDev pointers are updated late in X11
    setProvider(mX11Parent.GetGeometryProvider());
    SkiaSalGraphicsImpl::Init();
}

void X11SkiaSalGraphicsImpl::createWindowContext(bool forceRaster)
{
    assert(mX11Parent.GetDrawable() != None);
    mWindowContext = createWindowContext(
        mX11Parent.GetXDisplay(), mX11Parent.GetDrawable(), &mX11Parent.GetVisual(), GetWidth(),
        GetHeight(), forceRaster ? SkiaHelper::RenderRaster : SkiaHelper::renderMethodToUse(),
        false);
}

std::unique_ptr<sk_app::WindowContext>
X11SkiaSalGraphicsImpl::createWindowContext(Display* display, Drawable drawable,
                                            const XVisualInfo* visual, int width, int height,
                                            SkiaHelper::RenderMethod renderMethod, bool temporary)
{
    SkiaZone zone;
    sk_app::DisplayParams displayParams;
    displayParams.fColorType = kN32_SkColorType;
#if defined LINUX
    // WORKAROUND: VSync causes freezes that can even temporarily freeze the entire desktop.
    // This happens even with the latest 450.66 drivers despite them claiming a fix for vsync.
    // https://forums.developer.nvidia.com/t/hangs-freezes-when-vulkan-v-sync-vk-present-mode-fifo-khr-is-enabled/67751
    if (SkiaHelper::getVendor() == DriverBlocklist::VendorNVIDIA)
        displayParams.fDisableVsync = true;
#endif
    sk_app::window_context_factory::XlibWindowInfo winInfo;
    assert(display);
    winInfo.fDisplay = display;
    winInfo.fWindow = drawable;
    winInfo.fFBConfig = nullptr; // not used
    winInfo.fVisualInfo = const_cast<XVisualInfo*>(visual);
    assert(winInfo.fVisualInfo->visual != nullptr); // make sure it's not an uninitialized SalVisual
    winInfo.fWidth = width;
    winInfo.fHeight = height;
#ifdef DBG_UTIL
    // Our patched Skia has VulkanWindowContext that shares grDirectContext, which requires
    // that the X11 visual is always the same. Ensure it is so.
    static VisualID checkVisualID = -1U;
    // Exception is for the temporary case during startup, when SkiaHelper's
    // checkDeviceDenylisted() needs a WindowContext and may be called before SalVisual
    // is ready.
    if (!temporary)
    {
        assert(checkVisualID == -1U || winInfo.fVisualInfo->visualid == checkVisualID);
        checkVisualID = winInfo.fVisualInfo->visualid;
    }
#else
    (void)temporary;
#endif
    switch (renderMethod)
    {
        case SkiaHelper::RenderRaster:
            // Make sure we ask for color type that matches the X11 visual. If red mask
            // is larger value than blue mask, then on little endian this means blue is first.
            // This should also preferably match SK_R32_SHIFT set in config_skia.h, as that
            // improves performance, the common setup seems to be BGRA (possibly because of
            // choosing OpenGL-capable visual).
            displayParams.fColorType
                = (visual->red_mask > visual->blue_mask ? kBGRA_8888_SkColorType
                                                        : kRGBA_8888_SkColorType);
            return sk_app::window_context_factory::MakeRasterForXlib(winInfo, displayParams);
        case SkiaHelper::RenderVulkan:
            return sk_app::window_context_factory::MakeVulkanForXlib(winInfo, displayParams);
    }
    abort();
}

bool X11SkiaSalGraphicsImpl::avoidRecreateByResize() const
{
    if (!mSurface || isOffscreen())
        return false;
    // Skia's WindowContext uses actual dimensions of the X window, which due to X11 being
    // asynchronous may be temporarily different from what VCL thinks are the dimensions.
    // That can lead to us repeatedly calling recreateSurface() because of "incorrect"
    // size, and we otherwise need to check for size changes, because VCL does not inform us.
    // Avoid the problem here by checking the size of the X window and bail out if Skia
    // would just return the same size as it is now.
    Window r;
    int x, y;
    unsigned int w, h, border, depth;
    XGetGeometry(mX11Parent.GetXDisplay(), mX11Parent.GetDrawable(), &r, &x, &y, &w, &h, &border,
                 &depth);
    return mSurface->width() == int(w) && mSurface->height() == int(h);
}

void X11SkiaSalGraphicsImpl::DeInit()
{
    SkiaZone zone;
    SkiaSalGraphicsImpl::DeInit();
    mWindowContext.reset();
}

void X11SkiaSalGraphicsImpl::freeResources() {}

void X11SkiaSalGraphicsImpl::Flush() { performFlush(); }

void X11SkiaSalGraphicsImpl::performFlush()
{
    SkiaZone zone;
    flushDrawing();
    // TODO XPutImage() is somewhat inefficient, XShmPutImage() should be preferred.
    if (mWindowContext)
    {
        if (mDirtyRect.intersect(SkIRect::MakeWH(GetWidth(), GetHeight())))
            mWindowContext->swapBuffers(&mDirtyRect);
        mDirtyRect.setEmpty();
    }
}

std::unique_ptr<sk_app::WindowContext> createVulkanWindowContext(bool temporary)
{
    SalDisplay* salDisplay = vcl_sal::getSalDisplay(GetGenericUnixSalData());
    const XVisualInfo* visual;
    XVisualInfo* visuals = nullptr;
    if (!temporary)
        visual = &salDisplay->GetVisual(salDisplay->GetDefaultXScreen());
    else
    {
        // SalVisual from salDisplay may not be setup yet at this point, get
        // info for the default visual.
        XVisualInfo search;
        search.visualid = XVisualIDFromVisual(
            DefaultVisual(salDisplay->GetDisplay(), salDisplay->GetDefaultXScreen().getXScreen()));
        int count;
        visuals = XGetVisualInfo(salDisplay->GetDisplay(), VisualIDMask, &search, &count);
        assert(count == 1);
        visual = visuals;
    }
    std::unique_ptr<sk_app::WindowContext> ret = X11SkiaSalGraphicsImpl::createWindowContext(
        salDisplay->GetDisplay(), None, visual, 1, 1, SkiaHelper::RenderVulkan, temporary);
    if (temporary)
        XFree(visuals);
    return ret;
}

void X11SkiaSalGraphicsImpl::prepareSkia() { SkiaHelper::prepareSkia(createVulkanWindowContext); }

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */