diff options
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | main.c | 117 | 
2 files changed, 79 insertions, 40 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index 3192c6a..e63816d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@  cmake_minimum_required(VERSION 3.2)  project(vkpugltest LANGUAGES C) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") +  set(pugldir "../../pugl")  set(PUGL_INCLUDE "${pugldir}")  set(PUGL_LIBDIR "${pugldir}/build") @@ -83,8 +83,10 @@ struct VulkanAPI {  	PFN_vkEnumerateDeviceExtensionProperties	vkEnumerateDeviceExtensionProperties;  }; +/** Vulkan application that uses Pugl for windows and events */  struct RenderVulkan {  	struct VulkanAPI *api; +	PuglWorld *world;  	char *errMsg;  	VkInstance			instance;  	VkDebugReportCallbackEXT	debugCallback; @@ -121,12 +123,12 @@ const char *rvkGetErrMsg(struct RenderVulkan *vk)  #if defined(_WIN32)  #define VULKAN_SONAME_LATEST	"vulkan-1.dll" -void *appDlopen(const char *soname) +static inline void *appDlopen(const char *soname)  {  	return LoadLibraryA(soname);  } -char *appDlerror() +static inline char *appDlerror()  {  	// TODO: Print a more informative string. Error codes are annoying.  	DWORD errCode = GetLastError(); @@ -135,34 +137,39 @@ char *appDlerror()  	return errStr;  } -void *appDlsym(void *handle, const char *symbol) +static inline void *appDlsym(void *handle, const char *symbol)  {  	const uintptr_t ulAddr = (uintptr_t)GetProcAddress(handle, symbol);  	return (void*)ulAddr;  } -int appDlclose(void *handle) +static inline int appDlclose(void *handle)  {  	return FreeLibrary(handle);  } -void getRequiredInstanceExtensions(PuglWorld *world, -		unsigned *pExtensionCount, +PuglStatus getRequiredInstanceExtensions(PuglWorld *world, +		uint32_t *pExtensionCount,  		const char **const ppExtensionNames)  { -	(void)world; +	if (!world) { +		if (pExtensionCount) *pExtensionCount = 0; +		if (ppExtensionNames) *ppExtensionNames = NULL; +		return PUGL_FAILURE; +	}  	static const char *const required[] = {  		VK_KHR_SURFACE_EXTENSION_NAME,  		VK_KHR_WIN32_SURFACE_EXTENSION_NAME  	}; -	static const unsigned num = sizeof(required) / sizeof(required[0]); +	static const uint32_t num = sizeof(required) / sizeof(required[0]);  	if (ppExtensionNames) { -		for (int i = 0; i < num; ++i) { +		for (uint32_t i = 0; i < num; ++i) {  			ppExtensionNames[i] = required[i];  		}  	} else {  		*pExtensionCount = num;  	} +	return PUGL_SUCCESS;  }  VkResult createVulkanSurface(PuglView *view, @@ -184,36 +191,31 @@ VkResult createVulkanSurface(PuglView *view,  #else  #define VULKAN_SONAME_LATEST	"libvulkan.so.1"  #include <dlfcn.h> -// XXX: puglDlopen() -void *appDlopen(const char *soname) +// XXX: puglDlopen() and friends +static inline void *appDlopen(const char *soname)  {  	return dlopen(soname, RTLD_NOW);  } -char *appDlerror() +static inline char *appDlerror()  {  	return dlerror();  } -void *appDlsym(void *handle, const char *symbol) +static inline void *appDlsym(void *handle, const char *symbol)  {  	return dlsym(handle, symbol);  } -int appDlclose(void *handle) +static inline int appDlclose(void *handle)  {  	return dlclose(handle);  }  /* XXX:  puglGetRequiredInstanceExtensions() - *       This will return the platform-specific names of the instance level - *       extensions required to display images to the screen. - *       Vulkan is off-screen by default and can be used without presenting - *       at all, so off-screen applications will not need to call this - *       function. In other words: - * - *       "Get the names of the required instance extensions if you want to - *        display your images to the screen." + *       "Get the platform-specific names of the required instance-level  + *        extensions if you want to display your images to the screen." + *       Reminder that Vulkan is off-screen by default.   *   * TODO: Linux actually has three possible surfaces: Xlib, XCB, and Wayland.   *       This should be figured out at runtime (without using #ifdefs). @@ -222,27 +224,38 @@ int appDlclose(void *handle)   *       variable should be left in the function signature for forward   *       compatibility.   */ -void getRequiredInstanceExtensions(PuglWorld *world, -		unsigned *pExtensionCount, +PuglStatus getRequiredInstanceExtensions(PuglWorld *world, +		uint32_t *pExtensionCount,  		const char **const ppExtensionNames)  { -	(void)world; +	if (!world) { +		if (pExtensionCount) *pExtensionCount = 0; +		if (ppExtensionNames) *ppExtensionNames = NULL; +		return PUGL_FAILURE; +	} +	// TODO: if (world->system == X11) { ... } else if (world->system == Wayland) { ... } else ...  	static const char *const required[] = {  		VK_KHR_SURFACE_EXTENSION_NAME,  		VK_KHR_XLIB_SURFACE_EXTENSION_NAME  	}; -	static const unsigned num = sizeof(required) / sizeof(required[0]); +	static const uint32_t num = sizeof(required) / sizeof(required[0]);  	if (ppExtensionNames) { -		for (int i = 0; i < num; ++i) { +		for (uint32_t i = 0; i < num; ++i) {  			ppExtensionNames[i] = required[i];  		}  	} else {  		*pExtensionCount = num;  	} +	return PUGL_SUCCESS;  }  /* XXX: puglCreateVulkanSurface() - *      No need to wrap VkFreeSurfaceKHR() + * + *      Creating a surface is platform-specific and should be handled by Pugl + *      with this function exposed publicly. + * + *      No need to wrap vkFreeSurfaceKHR(). Creating a surface is platform + *      specific, but freeing it is not.   */  VkResult createVulkanSurface(PuglView *view,  		VkInstance instance, @@ -312,13 +325,13 @@ static VkResult createInstance(struct RenderVulkan *vk,  	/* MoltenVK for macOS currently only supports Vulkan 1.0 */  	appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); -	unsigned i, j, nRequired; -	getRequiredInstanceExtensions(NULL, &nRequired, NULL); +	uint32_t i, j, nRequired; +	getRequiredInstanceExtensions(vk->world, &nRequired, NULL);  	const uint32_t nExtensions = nRequired + nAdditional;  	const char **const extensions = malloc(sizeof(const char*) * nExtensions); -	getRequiredInstanceExtensions(NULL, NULL, extensions); +	getRequiredInstanceExtensions(vk->world, NULL, extensions);  	for (i = nRequired, j = 0; i < nExtensions; ++i, ++j) {  		extensions[i] = additionalExtensions[j];  	} @@ -375,11 +388,24 @@ void rvkDestroy(struct RenderVulkan *vk)  /** Create a self-contained Vulkan instance and set up a debug reporter   * + * XXX + * + * At the moment `world` is not used for anything, but if/when Pugl supports + * dynamically choosing a backend (X11, XCB, Wayland), it is anticipated that + * `world` will contain information regarding which is in use, which will then + * be used by `createVulkanSurface`. + * + * If creating a surface is handled by Pugl (`puglCreateVulkanSurface`), then + * `PuglWorld` can remain completely opaque to the application. + * + * `world` is NOT internally managed by the application. It MUST be destroyed + * separately. + *   * If errors occurred, the struct will be returned in an unusable state,   * and MUST be checked via `rvkGetErrMsg`. It MUST then be destroyed via   * `rvkDestroy`.   * */ -struct RenderVulkan *rvkCreate() +struct RenderVulkan *rvkCreate(PuglWorld *world)  {  	static const char *const instanceLayers[] = {  		"VK_LAYER_LUNARG_standard_validation" @@ -391,6 +417,11 @@ struct RenderVulkan *rvkCreate()  	const uint32_t nInstanceExtensions = sizeof(instanceExtensions) / sizeof(instanceExtensions[0]);  	struct RenderVulkan *vk = calloc(1, sizeof(*vk)); +	if (!world) { +		rvkSetErrMsg(vk, "No PuglWorld provided"); +		return vk; +	} +	vk->world = world;  	vk->api = calloc(1, sizeof(*vk->api));  	vk->api->handle = loadVulkanLibrary(NULL); @@ -570,14 +601,12 @@ static void rvkCreateSurface(struct RenderVulkan *vk, PuglView *view)   *   * Because this application will load all resources before any rendering begins,   * and the amount of resources are small enough to be loaded almost instantly, - * it will not make use of a separate TRANSFER queue family. This application - * will also not attempt to find the "most powerful" device on the system, and - * will choose the first suitable device it finds. + * it will not make use of a separate TRANSFER queue family.   *   * Information for many devices is available online at   * https://vulkan.gpuinfo.org/   */ -static int isDeviceSuitable(const struct RenderVulkan *const vk, +static int rvkIsDeviceSuitable(const struct RenderVulkan *const vk,  		const VkPhysicalDevice physicalDevice,  		uint32_t *const graphicsIndex)  { @@ -622,6 +651,11 @@ static int isDeviceSuitable(const struct RenderVulkan *const vk,  	return 1;  } +/** Selects a physical device + * + * This application will not attempt to find the "most powerful" device on the + * system, and will choose the first suitable device it finds. + */  void rvkSelectPhysicalDevice(struct RenderVulkan *vk)  {  	if (!vk->surface) { @@ -663,7 +697,7 @@ void rvkSelectPhysicalDevice(struct RenderVulkan *vk)  	for (i = 0; i < nDevices; ++i) {  		printf("Checking suitability for\t`%s`...\n", deviceProperties[i].deviceName);  		uint32_t graphicsIndex; -		if (isDeviceSuitable(vk, devices[i], &graphicsIndex)) { +		if (rvkIsDeviceSuitable(vk, devices[i], &graphicsIndex)) {  			printf("Using physical device:\t\t`%s`\n", deviceProperties[i].deviceName);  			vk->deviceProperties = deviceProperties[i];  			vk->physicalDevice = devices[i]; @@ -712,19 +746,21 @@ int main()  	/* The Mesa Vulkan drivers require this to be called */  	XInitThreads();  #endif +	PuglWorld *world = puglNewWorld();  	const char *errMsg = NULL; -	struct RenderVulkan *vk = rvkCreate(); + +	/** Vulkan application that uses Pugl for windows and events */ +	struct RenderVulkan *vk = rvkCreate(world);  	rvkCheckFatal(vk);  	printf("Created Vulkan Instance Successfully\n"); -	PuglWorld *world = puglNewWorld();  	PuglView *view = puglNewView(world);  	const PuglRect frame = { 0, 0, 800, 600 };  	puglSetBackend(view, puglStubBackend());  	PuglStatus status; -	if ((status = puglCreateWindow(view, "Pugl Vulkan Test"))) { +	if ((status = puglCreateWindow(view, "Vulkan Application Using Pugl"))) {  		fprintf(stderr, "Could not create window: %d\n", status);  		rvkDestroy(vk);  		puglFreeWorld(world); @@ -746,6 +782,7 @@ int main()  	puglFreeView(view);  	puglFreeWorld(world); +	/* Vulkan library MUST be unloaded AFTER call to XCloseDisplay() or it will segfault */  	rvkDestroy(vk);  	return 0;  } | 
