WSI_FN_GetPhysicalDeviceProcAddr proc_addr,
                 const VkAllocationCallbacks *alloc,
                 int display_fd,
-                const struct driOptionCache *dri_options)
+                const struct driOptionCache *dri_options,
+                bool sw_device)
 {
    const char *present_mode;
    UNUSED VkResult result;
 
    wsi->instance_alloc = *alloc;
    wsi->pdevice = pdevice;
-
+   wsi->sw = sw_device;
 #define WSI_GET_CB(func) \
    PFN_vk##func func = (PFN_vk##func)proc_addr(pdevice, "vk" #func)
    WSI_GET_CB(GetPhysicalDeviceProperties2);
    WSI_GET_CB(GetImageDrmFormatModifierPropertiesEXT);
    WSI_GET_CB(GetImageMemoryRequirements);
    WSI_GET_CB(GetImageSubresourceLayout);
-   WSI_GET_CB(GetMemoryFdKHR);
+   if (!wsi->sw)
+      WSI_GET_CB(GetMemoryFdKHR);
    WSI_GET_CB(GetPhysicalDeviceFormatProperties);
    WSI_GET_CB(GetPhysicalDeviceFormatProperties2KHR);
    WSI_GET_CB(GetPhysicalDeviceImageFormatProperties2);
    WSI_GET_CB(ResetFences);
    WSI_GET_CB(QueueSubmit);
    WSI_GET_CB(WaitForFences);
+   WSI_GET_CB(MapMemory);
+   WSI_GET_CB(UnmapMemory);
 #undef WSI_GET_CB
 
 #ifdef VK_USE_PLATFORM_XCB_KHR
    if (result != VK_SUCCESS)
       goto fail;
 
-   const VkMemoryGetFdInfoKHR memory_get_fd_info = {
-      .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
-      .pNext = NULL,
-      .memory = image->memory,
-      .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
-   };
-   int fd;
-   result = wsi->GetMemoryFdKHR(chain->device, &memory_get_fd_info, &fd);
-   if (result != VK_SUCCESS)
-      goto fail;
+   int fd = -1;
+   if (!wsi->sw) {
+      const VkMemoryGetFdInfoKHR memory_get_fd_info = {
+         .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
+         .pNext = NULL,
+         .memory = image->memory,
+         .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+      };
+
+      result = wsi->GetMemoryFdKHR(chain->device, &memory_get_fd_info, &fd);
+      if (result != VK_SUCCESS)
+         goto fail;
+   }
 
-   if (num_modifier_lists > 0) {
+   if (!wsi->sw && num_modifier_lists > 0) {
       VkImageDrmFormatModifierPropertiesEXT image_mod_props = {
          .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT,
       };
 
 }
 
 static VkResult
-x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index,
-                   uint32_t target_msc)
+x11_present_to_x11_dri3(struct x11_swapchain *chain, uint32_t image_index,
+                        uint32_t target_msc)
 {
    struct x11_image *image = &chain->images[image_index];
 
    return x11_swapchain_result(chain, VK_SUCCESS);
 }
 
+static VkResult
+x11_present_to_x11_sw(struct x11_swapchain *chain, uint32_t image_index,
+                      uint32_t target_msc)
+{
+   struct x11_image *image = &chain->images[image_index];
+
+   xcb_void_cookie_t cookie;
+   void *myptr;
+   chain->base.wsi->MapMemory(chain->base.device,
+                              image->base.memory,
+                              0, 0, 0, &myptr);
+
+   cookie = xcb_put_image(chain->conn, XCB_IMAGE_FORMAT_Z_PIXMAP,
+                          chain->window,
+                          chain->gc,
+                         image->base.row_pitches[0] / 4,
+                          chain->extent.height,
+                          0,0,0,24,
+                          image->base.row_pitches[0] * chain->extent.height,
+                          myptr);
+
+   chain->base.wsi->UnmapMemory(chain->base.device, image->base.memory);
+   xcb_discard_reply(chain->conn, cookie.sequence);
+   xcb_flush(chain->conn);
+   return x11_swapchain_result(chain, VK_SUCCESS);
+}
+static VkResult
+x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index,
+                   uint32_t target_msc)
+{
+   if (chain->base.wsi->sw)
+      return x11_present_to_x11_sw(chain, image_index, target_msc);
+   return x11_present_to_x11_dri3(chain, image_index, target_msc);
+}
+
 static VkResult
 x11_acquire_next_image(struct wsi_swapchain *anv_chain,
                        const VkAcquireNextImageInfoKHR *info,
    if (chain->status < 0)
       return chain->status;
 
+   if (chain->base.wsi->sw) {
+      *image_index = 0;
+      return VK_SUCCESS;
+   }
    if (chain->has_acquire_queue) {
       return x11_acquire_next_image_from_queue(chain, image_index, timeout);
    } else {
    if (result < 0)
       return result;
 
+   if (chain->base.wsi->sw) {
+      image->busy = false;
+      return VK_SUCCESS;
+   }
    image->pixmap = xcb_generate_id(chain->conn);
 
 #ifdef HAVE_DRI3_MODIFIERS
 {
    xcb_void_cookie_t cookie;
 
-   cookie = xcb_sync_destroy_fence(chain->conn, image->sync_fence);
-   xcb_discard_reply(chain->conn, cookie.sequence);
-   xshmfence_unmap_shm(image->shm_fence);
+   if (!chain->base.wsi->sw) {
+      cookie = xcb_sync_destroy_fence(chain->conn, image->sync_fence);
+      xcb_discard_reply(chain->conn, cookie.sequence);
+      xshmfence_unmap_shm(image->shm_fence);
 
-   cookie = xcb_free_pixmap(chain->conn, image->pixmap);
-   xcb_discard_reply(chain->conn, cookie.sequence);
+      cookie = xcb_free_pixmap(chain->conn, image->pixmap);
+      xcb_discard_reply(chain->conn, cookie.sequence);
+   }
 
    wsi_destroy_image(&chain->base, &image->base);
 }
    else
       chain->last_present_mode = XCB_PRESENT_COMPLETE_MODE_COPY;
 
-   if (!wsi_x11_check_dri3_compatible(wsi_device, conn))
-       chain->base.use_prime_blit = true;
+   if (!wsi_device->sw)
+      if (!wsi_x11_check_dri3_compatible(wsi_device, conn))
+         chain->base.use_prime_blit = true;
 
    chain->event_id = xcb_generate_id(chain->conn);
    xcb_present_select_input(chain->conn, chain->event_id, chain->window,
          goto fail_init_images;
    }
 
-   if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR ||
-       chain->base.present_mode == VK_PRESENT_MODE_MAILBOX_KHR) {
+   if ((chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR ||
+       chain->base.present_mode == VK_PRESENT_MODE_MAILBOX_KHR) && !chain->base.wsi->sw) {
       chain->has_present_queue = true;
 
       /* Initialize our queues.  We make them base.image_count + 1 because we will