[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kwin/fredrik/vulkan] /: vulkan/windowpixmap: Add support for wl_shm surfaces
From:       Fredrik_Höglund <null () kde ! org>
Date:       2018-02-16 17:01:49
Message-ID: E1emjOP-0004Yx-1q () code ! kde ! org
[Download RAW message or body]

Git commit 7fc5da61da214b7ff60d5ef1df741e817cf47a9d by Fredrik Höglund.
Committed on 16/02/2018 at 16:58.
Pushed by fredrik into branch 'fredrik/vulkan'.

vulkan/windowpixmap: Add support for wl_shm surfaces

M  +1    -1    CMakeLists.txt
M  +1    -0    plugins/scenes/vulkan/CMakeLists.txt
M  +343  -2    plugins/scenes/vulkan/windowpixmap.cpp
M  +19   -0    plugins/scenes/vulkan/windowpixmap.h

https://commits.kde.org/kwin/7fc5da61da214b7ff60d5ef1df741e817cf47a9d

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5347c6fc4..7d5c3eddc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -167,7 +167,7 @@ if(epoxy_HAS_GLX)
     endif()
 endif()
 
-find_package(Wayland 1.2 REQUIRED COMPONENTS Cursor OPTIONAL_COMPONENTS Egl)
+find_package(Wayland 1.2 REQUIRED COMPONENTS Cursor Server OPTIONAL_COMPONENTS Egl)
 set_package_properties(Wayland PROPERTIES
                        TYPE REQUIRED
                        PURPOSE "Required for building KWin with Wayland support"
diff --git a/plugins/scenes/vulkan/CMakeLists.txt \
b/plugins/scenes/vulkan/CMakeLists.txt index efc02eb66..f16eea9bc 100644
--- a/plugins/scenes/vulkan/CMakeLists.txt
+++ b/plugins/scenes/vulkan/CMakeLists.txt
@@ -25,6 +25,7 @@ target_link_libraries(KWinSceneVulkan
     kwin
     SceneVulkanBackend
     kwinvulkanutils
+    Wayland::Server
 )
 
 install(
diff --git a/plugins/scenes/vulkan/windowpixmap.cpp \
b/plugins/scenes/vulkan/windowpixmap.cpp index 5eb7c171b..9ee76f5e3 100644
--- a/plugins/scenes/vulkan/windowpixmap.cpp
+++ b/plugins/scenes/vulkan/windowpixmap.cpp
@@ -19,6 +19,17 @@ along with this program.  If not, see \
                <http://www.gnu.org/licenses/>.
 *********************************************************************/
 
 #include "windowpixmap.h"
+#include "scene.h"
+#include "wayland_server.h"
+#include "utils.h"
+#include "logging.h"
+
+#include <KWayland/Server/subcompositor_interface.h>
+#include <KWayland/Server/surface_interface.h>
+#include <KWayland/Server/buffer_interface.h>
+
+#include <wayland-server-core.h>
+
 
 namespace KWin
 {
@@ -26,7 +37,9 @@ namespace KWin
 
 VulkanWindowPixmap::VulkanWindowPixmap(Scene::Window *window, VulkanScene *scene)
     : WindowPixmap(window),
-      m_scene(scene)
+      m_scene(scene),
+      m_imageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+      m_bufferFormat(UINT32_MAX)
 {
 }
 
@@ -35,7 +48,9 @@ VulkanWindowPixmap::VulkanWindowPixmap(const \
QPointer<KWayland::Server::SubSurfa  WindowPixmap *parent,
                                        VulkanScene *scene)
     : WindowPixmap(subSurface, parent),
-      m_scene(scene)
+      m_scene(scene),
+      m_imageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+      m_bufferFormat(UINT32_MAX)
 {
 }
 
@@ -47,11 +62,337 @@ VulkanWindowPixmap::~VulkanWindowPixmap()
 
 void VulkanWindowPixmap::create()
 {
+    WindowPixmap::create();
 }
 
 
 void VulkanWindowPixmap::aboutToRender()
 {
+    // Note that WindowPixmap::surface() and WindowPixmap::subSurface() both return \
the +    // same surface. The difference between them is that \
WindowPixmap::subSurface() +    // returns null when the associated surface is a \
root-surface. +
+    if (KWayland::Server::SurfaceInterface *surf = surface()) {
+        // The window pixmap is associated with a wayland surface
+        QRegion damage = surf->trackedDamage();
+        surf->resetTrackedDamage();
+
+        // Make sure the wayland buffer reference is up-to-date
+        if (!m_image || (subSurface().isNull() && !toplevel()->damage().isEmpty()))
+            updateBuffer();
+
+        QPointer<KWayland::Server::BufferInterface> buf = buffer();
+        if (buf.isNull())
+            return; // FIXME: Fail gracefully
+
+        if (buf->shmBuffer()) {
+            wl_shm_buffer *shmBuffer = buf->shmBuffer();
+            if (!shmBuffer)
+                return;
+
+            const uint32_t bufferFormat = wl_shm_buffer_get_format(shmBuffer);
+            const QSize size = buf->size();
+            VkImageLayout layout;
+
+            if (m_bufferFormat != bufferFormat) {
+                m_image = nullptr;
+                m_imageView = nullptr;
+                m_memory = nullptr;
+            }
+
+            if (m_image) {
+                layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+            } else {
+                static const struct {
+                    wl_shm_format wl_format;
+                    VkFormat vk_format;
+                    uint32_t bpp;
+                    bool alpha;
+                    bool rev;
+                } table[] = {
+                    { WL_SHM_FORMAT_ARGB4444,    VK_FORMAT_B4G4R4A4_UNORM_PACK16,    \
.bpp = 16, .alpha = true,  .rev = true  }, +                    { \
WL_SHM_FORMAT_XRGB4444,    VK_FORMAT_B4G4R4A4_UNORM_PACK16,    .bpp = 16, .alpha = \
false, .rev = true  }, +
+                    { WL_SHM_FORMAT_ABGR4444,    VK_FORMAT_R4G4B4A4_UNORM_PACK16,    \
.bpp = 16, .alpha = true,  .rev = true  }, +                    { \
WL_SHM_FORMAT_XBGR4444,    VK_FORMAT_R4G4B4A4_UNORM_PACK16,    .bpp = 16, .alpha = \
false, .rev = true  }, +
+                    { WL_SHM_FORMAT_RGBA4444,    VK_FORMAT_R4G4B4A4_UNORM_PACK16,    \
.bpp = 16, .alpha = true,  .rev = false }, +                    { \
WL_SHM_FORMAT_RGBX4444,    VK_FORMAT_R4G4B4A4_UNORM_PACK16,    .bpp = 16, .alpha = \
false, .rev = false }, +
+                    { WL_SHM_FORMAT_BGRA4444,    VK_FORMAT_B4G4R4A4_UNORM_PACK16,    \
.bpp = 16, .alpha = true,  .rev = false }, +                    { \
WL_SHM_FORMAT_BGRX4444,    VK_FORMAT_B4G4R4A4_UNORM_PACK16,    .bpp = 16, .alpha = \
false, .rev = false }, +
+                    { WL_SHM_FORMAT_ARGB1555,    VK_FORMAT_A1R5G5B5_UNORM_PACK16,    \
.bpp = 16, .alpha = true,  .rev = false }, +                    { \
WL_SHM_FORMAT_XRGB1555,    VK_FORMAT_A1R5G5B5_UNORM_PACK16,    .bpp = 16, .alpha = \
false, .rev = false }, +
+                    { WL_SHM_FORMAT_RGBA5551,    VK_FORMAT_R5G5B5A1_UNORM_PACK16,    \
.bpp = 16, .alpha = true,  .rev = false }, +                    { \
WL_SHM_FORMAT_RGBX5551,    VK_FORMAT_R5G5B5A1_UNORM_PACK16,    .bpp = 16, .alpha = \
false, .rev = false }, +
+                    { WL_SHM_FORMAT_BGRA5551,    VK_FORMAT_B5G5R5A1_UNORM_PACK16,    \
.bpp = 16, .alpha = true,  .rev = false }, +                    { \
WL_SHM_FORMAT_BGRX5551,    VK_FORMAT_B5G5R5A1_UNORM_PACK16,    .bpp = 16, .alpha = \
false, .rev = false }, +
+                    { WL_SHM_FORMAT_RGB565,      VK_FORMAT_R5G6B5_UNORM_PACK16,      \
.bpp = 16, .alpha = false, .rev = false }, +                    { \
WL_SHM_FORMAT_BGR565,      VK_FORMAT_B5G6R5_UNORM_PACK16,      .bpp = 16, .alpha = \
false, .rev = false }, +
+                    { WL_SHM_FORMAT_ARGB8888,    VK_FORMAT_B8G8R8A8_UNORM,           \
.bpp = 32, .alpha = true,  .rev = QSysInfo::ByteOrder == QSysInfo::BigEndian }, +     \
{ WL_SHM_FORMAT_XRGB8888,    VK_FORMAT_B8G8R8A8_UNORM,           .bpp = 32, .alpha = \
false, .rev = QSysInfo::ByteOrder == QSysInfo::BigEndian }, +
+                    { WL_SHM_FORMAT_ABGR8888,    VK_FORMAT_A8B8G8R8_UNORM_PACK32,    \
.bpp = 32, .alpha = true,  .rev = false }, +                    { \
WL_SHM_FORMAT_XBGR8888,    VK_FORMAT_A8B8G8R8_UNORM_PACK32,    .bpp = 32, .alpha = \
false, .rev = false }, +
+                    { WL_SHM_FORMAT_RGBA8888,    VK_FORMAT_A8B8G8R8_UNORM_PACK32,    \
.bpp = 32, .alpha = true,  .rev = true  }, +                    { \
WL_SHM_FORMAT_RGBX8888,    VK_FORMAT_A8B8G8R8_UNORM_PACK32,    .bpp = 32, .alpha = \
false, .rev = true  }, +
+                    { WL_SHM_FORMAT_BGRA8888,    VK_FORMAT_B8G8R8A8_UNORM,           \
.bpp = 32, .alpha = true,  .rev = QSysInfo::ByteOrder != QSysInfo::BigEndian }, +     \
{ WL_SHM_FORMAT_BGRX8888,    VK_FORMAT_B8G8R8A8_UNORM,           .bpp = 32, .alpha = \
false, .rev = QSysInfo::ByteOrder != QSysInfo::BigEndian }, +
+                    { WL_SHM_FORMAT_ARGB2101010, VK_FORMAT_A2R10G10B10_UNORM_PACK32, \
.bpp = 32, .alpha = true,  .rev = false }, +                    { \
WL_SHM_FORMAT_XRGB2101010, VK_FORMAT_A2R10G10B10_UNORM_PACK32, .bpp = 32, .alpha = \
false, .rev = false }, +
+                    { WL_SHM_FORMAT_ABGR2101010, VK_FORMAT_A2B10G10R10_UNORM_PACK32, \
.bpp = 32, .alpha = true,  .rev = false }, +                    { \
WL_SHM_FORMAT_XBGR2101010, VK_FORMAT_A2B10G10R10_UNORM_PACK32, .bpp = 32, .alpha = \
false, .rev = false }, +                };
+
+                const auto entry = std::find_if(std::begin(table), std::end(table),
+                                                [=](const auto &entry) { return \
entry.wl_format == bufferFormat; }); +
+                if (entry == std::end(table)) {
+                    qCCritical(KWIN_VULKAN) << "Unsupported wl_shm_format";
+                    return;
+                }
+
+                VkFormat format = entry->vk_format;
+                uint32_t bpp    = entry->bpp;
+                bool alpha      = entry->alpha;
+                bool rev        = entry->rev;
+
+                createTexture(format,
+                              size.width(), size.height(),
+                              {
+                                  .r = rev ? VK_COMPONENT_SWIZZLE_A : \
VK_COMPONENT_SWIZZLE_R, +                                  .g = rev ? \
VK_COMPONENT_SWIZZLE_B : VK_COMPONENT_SWIZZLE_G, +                                  \
.b = rev ? VK_COMPONENT_SWIZZLE_G : VK_COMPONENT_SWIZZLE_B, +                         \
.a = alpha ? +                                           rev ? VK_COMPONENT_SWIZZLE_R
+                                               : VK_COMPONENT_SWIZZLE_A
+                                           : VK_COMPONENT_SWIZZLE_ONE
+                              });
+
+                layout = VK_IMAGE_LAYOUT_UNDEFINED;
+                damage = QRegion(0, 0, size.width(), size.height());
+
+                m_bufferFormat = bufferFormat;
+                m_bitsPerPixel = bpp;
+            }
+
+            if (!damage.isEmpty()) {
+                updateTexture(shmBuffer, damage, layout);
+            }
+        } else {
+            // TODO
+        }
+
+        // Reset the toplevel damage if this window pixmap is associated with the \
root surface +        if (subSurface().isNull())
+            toplevel()->resetDamage();
+    } else {
+    }
+}
+
+
+void VulkanWindowPixmap::updateTexture(wl_shm_buffer *buffer, const QRegion &damage, \
VkImageLayout imageLayout) +{
+    if (damage.isEmpty())
+        return;
+
+    const uint32_t bufferCopyOffsetAlignment = std::max(4u, \
scene()->optimalBufferCopyOffsetAlignment()); +    const uint32_t rowPitchAlignment = \
std::max(4u, scene()->optimalBufferCopyRowPitchAlignment()); +    const uint32_t \
srcPitch = wl_shm_buffer_get_stride(buffer); +    const uint32_t bytesPerPixel = \
m_bitsPerPixel / 8; +
+    size_t allocationSize = 0;
+    for (const QRect &rect : damage) {
+        const uint32_t pitch = align(rect.width() * bytesPerPixel, \
rowPitchAlignment); +
+        allocationSize = align(allocationSize, bufferCopyOffsetAlignment);
+        allocationSize += pitch * rect.height();
+    }
+
+    VulkanUploadManager *uploadManager = scene()->imageUploadManager();
+    const VulkanBufferRange uploadBuffer = uploadManager->allocate(allocationSize, \
bufferCopyOffsetAlignment); +
+    std::vector<VkBufferImageCopy> regions;
+    regions.reserve(damage.rectCount());
+
+    /* Note: If we switch to using a transfer queue, we need to take
+     *       VkQueueFamilyProperties::minImageTransferGranularity into account here.
+     */
+
+    const uint8_t * const pixels = static_cast<const uint8_t \
*>(wl_shm_buffer_get_data(buffer)); +    if (!pixels) {
+        qCCritical(KWIN_VULKAN()) << "wl_shm_buffer_get_data() returned nullptr";
+        return;
+    }
+
+    wl_shm_buffer_begin_access(buffer);
+
+    size_t dstOffset = 0;
+    for (const QRect &rect : damage) {
+        const uint32_t dstPitch = align(rect.width() * bytesPerPixel, \
rowPitchAlignment); +        const uint32_t dstStride = dstPitch / bytesPerPixel;
+
+        dstOffset = align(dstOffset, bufferCopyOffsetAlignment);
+
+        const uint8_t *src = pixels + rect.y() * srcPitch + rect.x() * \
bytesPerPixel; +        uint8_t *dst = reinterpret_cast<uint8_t \
*>(uploadBuffer.data()) + dstOffset; +        for (int y = 0; y < rect.height(); y++) \
{ +            memcpy(dst, src, rect.width() * bytesPerPixel);
+            src += srcPitch;
+            dst += dstPitch;
+        }
+
+        regions.emplace_back(VkBufferImageCopy {
+                                 .bufferOffset = uploadBuffer.offset() + dstOffset,
+                                 .bufferRowLength = dstStride,
+                                 .bufferImageHeight = uint32_t(rect.height()),
+                                 .imageSubresource = {
+                                     .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+                                     .mipLevel = 0,
+                                     .baseArrayLayer = 0,
+                                     .layerCount = 1
+                                 },
+                                 .imageOffset = {
+                                     .x = rect.x(),
+                                     .y = rect.y(),
+                                     .z = 0
+                                 },
+                                 .imageExtent = {
+                                     .width = uint32_t(rect.width()),
+                                     .height = uint32_t(rect.height()),
+                                     .depth = 1
+                                 },
+                             });
+
+        dstOffset += dstPitch * rect.height();
+    }
+
+    wl_shm_buffer_end_access(buffer);
+
+    // Record the copy commands in the setup command buffer
+    auto cmd = scene()->setupCommandBuffer();
+
+    uint32_t srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+    uint32_t srcAccessMask = 0;
+
+    if (imageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+        srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+        srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
+    }
+
+    // Transition the image to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
+    cmd->pipelineBarrier(srcStageMask,
+                         VK_PIPELINE_STAGE_TRANSFER_BIT,
+                         0,
+                         {},
+                         {},
+                         {
+                             {
+                                 .sType               = \
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, +                                 .pNext      \
= nullptr, +                                 .srcAccessMask       = srcAccessMask,
+                                 .dstAccessMask       = \
VK_ACCESS_TRANSFER_WRITE_BIT, +                                 .oldLayout           \
= imageLayout, +                                 .newLayout           = \
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, +                                 \
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, +                                 \
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, +                                 \
.image               = m_image->handle(), +                                 \
.subresourceRange    = +                                 {
+                                     .aspectMask      = VK_IMAGE_ASPECT_COLOR_BIT,
+                                     .baseMipLevel    = 0,
+                                     .levelCount      = 1,
+                                     .baseArrayLayer  = 0,
+                                     .layerCount      = 1
+                                 }
+                             }
+                         });
+
+    // Copy from the upload buffer to the image
+    cmd->copyBufferToImage(uploadBuffer.handle(), m_image->handle(), \
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regions.size(), regions.data()); +
+    // Transition the image to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+    cmd->pipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
+                         VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+                         0,
+                         {},
+                         {},
+                         {
+                             {
+                                 .sType               = \
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, +                                 .pNext      \
= nullptr, +                                 .srcAccessMask       = \
VK_ACCESS_TRANSFER_WRITE_BIT, +                                 .dstAccessMask       \
= VK_ACCESS_SHADER_READ_BIT, +                                 .oldLayout           = \
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, +                                 .newLayout    \
= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, +                                 \
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, +                                 \
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, +                                 \
.image               = m_image->handle(), +                                 \
.subresourceRange    = +                                 {
+                                     .aspectMask      = VK_IMAGE_ASPECT_COLOR_BIT,
+                                     .baseMipLevel    = 0,
+                                     .levelCount      = 1,
+                                     .baseArrayLayer  = 0,
+                                     .layerCount      = 1
+                                 }
+                             }
+                         });
+
+    scene()->addBusyReference(m_image);
+    scene()->addBusyReference(m_memory);
+}
+
+
+void VulkanWindowPixmap::createTexture(VkFormat format, uint32_t width, uint32_t \
height, const VkComponentMapping &swizzle) +{
+    m_image = std::make_shared<VulkanImage>(scene()->device(),
+                                            VkImageCreateInfo {
+                                                .sType = \
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, +                                                \
.pNext = nullptr, +                                                .flags = 0,
+                                                .imageType = VK_IMAGE_TYPE_2D,
+                                                .format = format,
+                                                .extent = { width, height, 1 },
+                                                .mipLevels = 1,
+                                                .arrayLayers = 1,
+                                                .samples = VK_SAMPLE_COUNT_1_BIT,
+                                                .tiling = VK_IMAGE_TILING_OPTIMAL,
+                                                .usage = \
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, +                       \
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, +                                           \
.queueFamilyIndexCount = 0, +                                                \
.pQueueFamilyIndices = nullptr, +                                                \
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED +                                          \
}); +
+    m_memory = scene()->deviceMemoryAllocator()->allocateMemory(m_image, \
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); +
+    m_imageView = std::make_shared<VulkanImageView>(scene()->device(),
+                                                    VkImageViewCreateInfo {
+                                                        .sType = \
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, +                                           \
.pNext = nullptr, +                                                        .flags = \
0, +                                                        .image = \
m_image->handle(), +                                                        .viewType \
= VK_IMAGE_VIEW_TYPE_2D, +                                                        \
.format = format, +                                                        \
.components = swizzle, +                                                        \
.subresourceRange = { +                                                            \
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, +                                            \
.baseMipLevel = 0, +                                                            \
.levelCount = 1, +                                                            \
.baseArrayLayer = 0, +                                                            \
.layerCount = 1 +                                                        }
+                                                    });
 }
 
 
diff --git a/plugins/scenes/vulkan/windowpixmap.h \
b/plugins/scenes/vulkan/windowpixmap.h index 1846b0219..6d5c19132 100644
--- a/plugins/scenes/vulkan/windowpixmap.h
+++ b/plugins/scenes/vulkan/windowpixmap.h
@@ -22,6 +22,7 @@ along with this program.  If not, see \
<http://www.gnu.org/licenses/>.  #define WINDOWPIXMAP_H
 
 #include "../../../scene.h"
+#include "kwinvulkanutils_funcs.h"
 
 #include <QPointer>
 #include <memory>
@@ -32,6 +33,8 @@ namespace KWayland {
     }
 }
 
+struct wl_shm_buffer;
+
 namespace KWin
 {
 
@@ -61,8 +64,24 @@ public:
 
     void aboutToRender();
 
+    const std::shared_ptr<VulkanImage> &image() const { return m_image; }
+    const std::shared_ptr<VulkanImageView> &imageView() const { return m_imageView; \
} +    const std::shared_ptr<VulkanDeviceMemory> &memory() const { return m_memory; }
+
+    VkImageLayout imageLayout() const { return m_imageLayout; }
+
+protected:
+    void createTexture(VkFormat format, uint32_t width, uint32_t height, const \
VkComponentMapping &swizzle); +    void updateTexture(wl_shm_buffer *buffer, const \
QRegion &damage, VkImageLayout layout); +
 private:
     VulkanScene *m_scene;
+    std::shared_ptr<VulkanImage> m_image;
+    std::shared_ptr<VulkanImageView> m_imageView;
+    std::shared_ptr<VulkanDeviceMemory> m_memory;
+    VkImageLayout m_imageLayout;
+    uint32_t m_bufferFormat;
+    uint32_t m_bitsPerPixel;
 };
 
 } // namespace KWin


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic