[virglrenderer-devel] [PATCH v2 2/4] vulkan: added Vulkan state creation code

Nathan Gauer nathan at gauer.org
Fri Jul 27 12:09:01 UTC 2018


When enabled, Virglrenderer will create a vulkan state along the current
OpenGL context.
Vulkan state is not linked to the OpenGL one, and both can cohexist.

Signed-off-by: Nathan Gauer <nathan at gauer.org>
---
 configure.ac        |  20 ++++++
 src/Makefile.am     |   7 ++
 src/util/macros.h   |  16 ++++-
 src/virgl_vk.c      | 165 ++++++++++++++++++++++++++++++++++++++++++++
 src/virgl_vk.h      |  73 ++++++++++++++++++++
 src/virglrenderer.c |  22 ++++++
 src/virglrenderer.h |   1 +
 7 files changed, 303 insertions(+), 1 deletion(-)
 create mode 100644 src/virgl_vk.c
 create mode 100644 src/virgl_vk.h

diff --git a/configure.ac b/configure.ac
index c4ce743..4c840b0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -143,6 +143,25 @@ AS_IF([test "x$with_glx" = "xyes"], [
 ])
 AM_CONDITIONAL([WITH_GLX], [test "x$with_glx" = "xyes"])
 
+AC_ARG_WITH([vulkan],
+            AS_HELP_STRING([--with-vulkan],
+            [enable vulkan(experimental)])
+)
+AS_IF([test "x$with_vulkan" = "xyes"],
+   [
+      AC_CHECK_LIB([vulkan], [vkCreateInstance])
+      AC_CHECK_PROGS([PYTHON3], [python3 python])
+      PKG_CHECK_MODULES([VULKAN], [vulkan])
+      AC_DEFINE([WITH_VULKAN], [1], [Vulkan is supported.])
+
+      with_vulkan=yes
+   ],
+   [
+      with_vulkan=no
+])
+AM_CONDITIONAL([WITH_VULKAN], [test "x$with_vulkan" = "xyes"])
+
+
 AC_SUBST([DEFINES])
 AC_CONFIG_FILES([
 		virglrenderer.pc
@@ -166,6 +185,7 @@ AC_MSG_NOTICE([
 
       glx:                      $with_glx
       egl:                      $epoxy_has_egl
+      vulkan:                   $with_vulkan
       debug:                    $enable_debug
       tests:                    $build_tests
       fuzzer:                   $enable_fuzzer
diff --git a/src/Makefile.am b/src/Makefile.am
index b42bac0..b2ce102 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,6 +3,7 @@ AM_LDFLAGS = -lm \
 	$(GBM_LIBS) \
 	$(EPOXY_LIBS) \
 	$(X11_LIBS) \
+	$(VULKAN_LIBS) \
 	$(CODE_COVERAGE_LDFLAGS)
 
 AM_CFLAGS = \
@@ -46,6 +47,12 @@ libvrend_la_SOURCES += \
 	virgl_glx_context.c
 endif
 
+if WITH_VULKAN
+libvrend_la_SOURCES +=	\
+	virgl_vk.h				\
+	virgl_vk.c
+endif
+
 lib_LTLIBRARIES = libvirglrenderer.la
 noinst_LTLIBRARIES = libvrend.la
 
diff --git a/src/util/macros.h b/src/util/macros.h
index 3d6ff2c..5ff3665 100644
--- a/src/util/macros.h
+++ b/src/util/macros.h
@@ -4,7 +4,21 @@
 #define UNUSED_PARAMETER(Param) (void)(Param)
 #define ARRAY_SIZE(Array) (sizeof(Array) / sizeof((Array)[0]))
 
-#ifdef DEBUG
+/* notes:
+ *    - Result parameter MUST be a variable.
+ *    - this macro needs virgl_vk.h header (vkresult_to_string)
+ *    - ControlExpression is executed on error. Exemple values are:
+ *       - return X;
+ *       - break;
+ *       - ;
+ */
+#define CHECK_VK_RESULT(Result, ControlExpression)                \
+   if (VK_SUCCESS != (Result)) {                                  \
+      PRINT_LOG(LOG_LEVEL_ERROR, ("%s: vk call failed (%s)\n",    \
+                                  __func__,                       \
+                                  vkresult_to_string((Result)))); \
+      ControlExpression;                                          \
+   }
 
 /* Loggin/Tracing related macros.
  *    - LOG_LEVEL can be changed by redefining the MIN_LOG_LEVEL macro.
diff --git a/src/virgl_vk.c b/src/virgl_vk.c
new file mode 100644
index 0000000..16fe395
--- /dev/null
+++ b/src/virgl_vk.c
@@ -0,0 +1,165 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vulkan/vulkan.h>
+
+#include "util/macros.h"
+#include "util/u_double_list.h"
+#include "util/u_memory.h"
+#include "virgl_vk.h"
+
+/* contains the state of the Vulkan module */
+static struct virgl_vk *vk_info;
+
+const char*
+vkresult_to_string(VkResult res)
+{
+   switch (res)
+   {
+#define VK2STR(Value) case Value: return #Value
+      VK2STR(VK_SUCCESS);
+      VK2STR(VK_NOT_READY);
+      VK2STR(VK_TIMEOUT);
+      VK2STR(VK_EVENT_SET);
+      VK2STR(VK_EVENT_RESET);
+      VK2STR(VK_INCOMPLETE);
+      VK2STR(VK_ERROR_OUT_OF_HOST_MEMORY);
+      VK2STR(VK_ERROR_OUT_OF_DEVICE_MEMORY);
+      VK2STR(VK_ERROR_INITIALIZATION_FAILED);
+      VK2STR(VK_ERROR_DEVICE_LOST);
+      VK2STR(VK_ERROR_MEMORY_MAP_FAILED);
+      VK2STR(VK_ERROR_LAYER_NOT_PRESENT);
+      VK2STR(VK_ERROR_EXTENSION_NOT_PRESENT);
+      VK2STR(VK_ERROR_FEATURE_NOT_PRESENT);
+      VK2STR(VK_ERROR_INCOMPATIBLE_DRIVER);
+      VK2STR(VK_ERROR_TOO_MANY_OBJECTS);
+      VK2STR(VK_ERROR_FORMAT_NOT_SUPPORTED);
+      VK2STR(VK_ERROR_FRAGMENTED_POOL);
+      VK2STR(VK_ERROR_OUT_OF_POOL_MEMORY);
+      VK2STR(VK_ERROR_INVALID_EXTERNAL_HANDLE);
+      VK2STR(VK_ERROR_SURFACE_LOST_KHR);
+      VK2STR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);
+      VK2STR(VK_SUBOPTIMAL_KHR);
+      VK2STR(VK_ERROR_OUT_OF_DATE_KHR);
+      VK2STR(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
+      VK2STR(VK_ERROR_VALIDATION_FAILED_EXT);
+      VK2STR(VK_ERROR_INVALID_SHADER_NV);
+      VK2STR(VK_ERROR_FRAGMENTATION_EXT);
+      VK2STR(VK_ERROR_NOT_PERMITTED_EXT);
+      VK2STR(VK_RESULT_MAX_ENUM);
+#undef VK2STR
+      default:
+      return "VK_UNKNOWN_RETURN_VALUE";
+   }
+}
+
+static int
+init_physical_devices(struct virgl_vk *state)
+{
+   TRACE_IN();
+
+   uint32_t device_count = 0;
+   VkResult res;
+
+   state->devices = CALLOC_STRUCT(vk_device);
+   if (NULL == state->devices) {
+      RETURN(-1);
+   }
+
+   LIST_INITHEAD(&state->devices->list);
+
+   res = vkEnumeratePhysicalDevices(state->vk_instance, &device_count, NULL);
+   CHECK_VK_RESULT(res, RETURN(-1));
+
+   if (0 == device_count) {
+      PRINT_LOG(LOG_LEVEL_ERROR, ("No device supports Vulkan.\n"));
+      RETURN(-1);
+   }
+
+   state->physical_devices = CALLOC(device_count,
+                                    sizeof(*state->physical_devices));
+   if (NULL == state->physical_devices) {
+      RETURN(-1);
+   }
+
+   res = vkEnumeratePhysicalDevices(state->vk_instance,
+                                    &device_count,
+                                    state->physical_devices);
+   CHECK_VK_RESULT(res, RETURN(-1));
+
+   state->physical_device_count = device_count;
+   RETURN(0);
+}
+
+int
+virgl_vk_init(void)
+{
+   VkResult vk_res;
+   VkApplicationInfo application_info = { 0 };
+   VkInstanceCreateInfo info = { 0 };
+
+   TRACE_IN();
+
+   vk_info = CALLOC_STRUCT(virgl_vk);
+   if (NULL == vk_info) {
+      RETURN(-1);
+   }
+
+   application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+   application_info.pApplicationName = "virglrenderer";
+   application_info.applicationVersion = 1;
+   application_info.pEngineName = NULL;
+   application_info.engineVersion = 1;
+   application_info.apiVersion = VK_MAKE_VERSION(1,1,0);
+
+   const char *validation_layers[] = {
+#ifdef VIRGL_VK_DEBUG
+      "VK_LAYER_LUNARG_core_validation",
+      "VK_LAYER_LUNARG_object_tracker",
+      "VK_LAYER_LUNARG_parameter_validation",
+      "VK_LAYER_LUNARG_standard_validation",
+#endif
+   };
+
+   info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+   info.pApplicationInfo = &application_info;
+   info.enabledLayerCount = ARRAY_SIZE(validation_layers);
+   info.ppEnabledLayerNames = validation_layers;
+
+   do {
+      vk_res = vkCreateInstance(&info, NULL, &vk_info->vk_instance);
+      CHECK_VK_RESULT(vk_res, break);
+
+      if (0 != init_physical_devices(vk_info)) {
+         break;
+      }
+
+      /* success path */
+      PRINT_LOG(LOG_LEVEL_INFO,
+                ("Vulkan state created with %d devices.\n",
+                vk_info->physical_device_count));
+      RETURN(0);
+   } while (0);
+
+   /* failure branch */
+   virgl_vk_destroy();
+   RETURN(-1);
+}
+
+void
+virgl_vk_destroy()
+{
+   if (NULL == vk_info) {
+      return;
+   }
+
+   if (VK_NULL_HANDLE != vk_info->vk_instance) {
+      vkDestroyInstance(vk_info->vk_instance, NULL);
+      vk_info->vk_instance = VK_NULL_HANDLE;
+   }
+
+   FREE(vk_info->devices);
+   FREE(vk_info->physical_devices);
+   FREE(vk_info);
+   vk_info = NULL;
+}
diff --git a/src/virgl_vk.h b/src/virgl_vk.h
new file mode 100644
index 0000000..62a66f3
--- /dev/null
+++ b/src/virgl_vk.h
@@ -0,0 +1,73 @@
+#ifndef VIRGL_VK_H
+#define VIRGL_VK_H
+
+#include <vulkan/vulkan.h>
+
+#include "util/u_double_list.h"
+
+/* This struct contains the state of our Vulkan module
+ *
+ * vk_instance: one instance per virglrenderer process.
+ * physical_devices: contiguous array of VkPhysicalDevice*.
+ *                   Devices are enumerated on instance creation.
+ *
+ * devices: list of VkDevice wrappers. Each item in this list reprensents
+ *          a vulkan application using virglrenderer.
+ */
+struct virgl_vk {
+   VkInstance vk_instance;
+
+   VkPhysicalDevice *physical_devices;
+   uint32_t physical_device_count;
+
+   struct vk_device *devices;
+};
+
+/* This struct contains the state of ONE Vulkan application running using vgl
+ *
+ * list: the linked-list element. (vk_device is double-linked instrusive list)
+ * physical_device_id: this is the index of the physical device in use
+ *                     (in the virgl_vk.physical_devices array)
+ *
+ * handle: The VkDevice handle
+ *
+ * Queue creation if forwarded. Thus, we store the VkQueue handles here.
+ *
+ * queue_count: number of queues created by the application
+ * queues: array of VkQueue handles
+ *
+ * next_handle: next handle the device will use when creating an object
+ * objects: this hashtable stores every Vulkan objects.
+ *          All objects are wrapped in a struct defined bellow.
+ */
+typedef struct vk_device {
+   struct list_head list;
+
+   uint32_t physical_device_id;
+   VkDevice handle;
+
+   uint32_t queue_count;
+   VkQueue *queues;
+
+   uint32_t next_handle;
+   struct util_hash_table *objects;
+} vk_device_t;
+
+/* converts a VkResult to the readable string */
+const char*
+vkresult_to_string(VkResult res);
+
+/* initializes the state of the Vulkan part.
+ * This function will allocate and initialize
+ * the vulkan state.
+ *
+ * returns != 0 on failure.
+ */
+int
+virgl_vk_init(void);
+
+/* Used to destroy a previously created vulkan state. */
+void
+virgl_vk_destroy(void);
+
+#endif
diff --git a/src/virglrenderer.c b/src/virglrenderer.c
index 86824f8..5525aec 100644
--- a/src/virglrenderer.c
+++ b/src/virglrenderer.c
@@ -34,10 +34,15 @@
 #include "pipe/p_state.h"
 #include "util/u_format.h"
 #include "util/u_math.h"
+#include "util/macros.h"
 #include "vrend_renderer.h"
 
 #include "virglrenderer.h"
 
+#ifdef WITH_VULKAN
+#include "virgl_vk.h"
+#endif
+
 #ifdef HAVE_EPOXY_EGL_H
 #include "virgl_egl.h"
 static struct virgl_egl *egl_info;
@@ -295,6 +300,9 @@ void virgl_renderer_cleanup(UNUSED void *cookie)
       use_context = CONTEXT_NONE;
    }
 #endif
+#ifdef WITH_VULKAN
+   virgl_vk_destroy();
+#endif
 }
 
 int virgl_renderer_init(void *cookie, int flags, struct virgl_renderer_callbacks *cbs)
@@ -309,6 +317,20 @@ int virgl_renderer_init(void *cookie, int flags, struct virgl_renderer_callbacks
    dev_cookie = cookie;
    rcbs = cbs;
 
+   /* Vulkan state is not linked to GL state. Both can coexist */
+   if (flags & VIRGL_RENDERER_USE_VULKAN) {
+#ifdef WITH_VULKAN
+      if (0 != virgl_vk_init()) {
+         PRINT_LOG(LOG_LEVEL_ERROR, ("Vulkan initialization failed.\n"));
+         return -1;
+      }
+#else
+      PRINT_LOG(LOG_LEVEL_ERROR, ("Vulkan support not enabled.\n"));
+      return -1;
+#endif
+   }
+
+
    if (flags & VIRGL_RENDERER_USE_EGL) {
 #ifdef HAVE_EPOXY_EGL_H
       int fd = -1;
diff --git a/src/virglrenderer.h b/src/virglrenderer.h
index 5baecdd..5f2de64 100644
--- a/src/virglrenderer.h
+++ b/src/virglrenderer.h
@@ -66,6 +66,7 @@ struct virgl_renderer_callbacks {
  */
 #define VIRGL_RENDERER_THREAD_SYNC 2
 #define VIRGL_RENDERER_USE_GLX (1 << 2)
+#define VIRGL_RENDERER_USE_VULKAN (1 << 3)
 
 VIRGL_EXPORT int virgl_renderer_init(void *cookie, int flags, struct virgl_renderer_callbacks *cb);
 VIRGL_EXPORT void virgl_renderer_poll(void); /* force fences */
-- 
2.18.0



More information about the virglrenderer-devel mailing list