aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--doc/mainpage.md2
-rw-r--r--include/pugl/detail/mac_vulkan.m216
-rw-r--r--include/pugl/detail/win_vulkan.c130
-rw-r--r--include/pugl/detail/x11_vulkan.c134
-rw-r--r--include/pugl/pugl_vulkan.h148
-rw-r--r--include/pugl/pugl_vulkan.hpp170
-rw-r--r--wscript40
8 files changed, 839 insertions, 3 deletions
diff --git a/README.md b/README.md
index e8794bc..9cf94dd 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ Pugl
Pugl (PlUgin Graphics Library) is a minimal portable API for GUIs which is
suitable for use in plugins. It works on X11, MacOS, and Windows, and
-optionally supports OpenGL and Cairo graphics contexts.
+optionally supports Vulkan, OpenGL, and Cairo graphics contexts.
Pugl is vaguely similar to libraries like GLUT and GLFW, but with some
distinguishing features:
diff --git a/doc/mainpage.md b/doc/mainpage.md
index 3dd4511..3204a36 100644
--- a/doc/mainpage.md
+++ b/doc/mainpage.md
@@ -34,7 +34,7 @@ and be configured by [setting hints](@ref puglSetViewHint)
and optionally [adjusting the frame](@ref frame).
The [Backend](@ref PuglBackend) controls drawing for a view.
-Pugl includes [Cairo](@ref cairo) and [OpenGL](@ref gl) backends,
+Pugl includes [Cairo](@ref cairo), [OpenGL](@ref gl), and [Vulkan](@ref vulkan) backends,
as well as a [stub](@ref stub) backend that creates a native window with no portable drawing context.
Once the view is configured,
diff --git a/include/pugl/detail/mac_vulkan.m b/include/pugl/detail/mac_vulkan.m
new file mode 100644
index 0000000..813e0ae
--- /dev/null
+++ b/include/pugl/detail/mac_vulkan.m
@@ -0,0 +1,216 @@
+/*
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file mac_vulkan.m Vulkan graphics backend for MacOS.
+*/
+
+#define VK_NO_PROTOTYPES 1
+
+#include "pugl/detail/implementation.h"
+#include "pugl/detail/mac.h"
+#include "pugl/detail/stub.h"
+#include "pugl/detail/types.h"
+#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
+#include "pugl/pugl_vulkan.h"
+
+#include <vulkan/vulkan_core.h>
+#include <vulkan/vulkan_macos.h>
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CAMetalLayer.h>
+
+#include <dlfcn.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+@interface PuglVulkanView : NSView<CALayerDelegate>
+
+@end
+
+@implementation PuglVulkanView
+{
+@public
+ PuglView* puglview;
+}
+
+- (id)initWithFrame:(NSRect)frame
+{
+ self = [super initWithFrame:frame];
+
+ if (self) {
+ self.wantsLayer = YES;
+ self.layerContentsRedrawPolicy =
+ NSViewLayerContentsRedrawOnSetNeedsDisplay;
+ }
+
+ return self;
+}
+
+- (CALayer*)makeBackingLayer
+{
+ CAMetalLayer* layer = [CAMetalLayer layer];
+ [layer setDelegate:self];
+ return layer;
+}
+
+- (void)setFrameSize:(NSSize)newSize
+{
+ PuglWrapperView* wrapper = (PuglWrapperView*)[self superview];
+
+ [super setFrameSize:newSize];
+ [wrapper setReshaped];
+
+ self.layer.frame = self.bounds;
+}
+
+- (void)displayLayer:(CALayer*)layer
+{
+ (void)layer;
+ PuglWrapperView* wrapper = (PuglWrapperView*)[self superview];
+ [wrapper dispatchExpose:[self bounds]];
+}
+
+@end
+
+static PuglStatus
+puglMacVulkanCreate(PuglView* view)
+{
+ PuglInternals* impl = view->impl;
+ PuglVulkanView* drawView = [PuglVulkanView alloc];
+ const NSRect rect = NSMakeRect(0, 0, view->frame.width, view->frame.height);
+
+ drawView->puglview = view;
+ [drawView initWithFrame:rect];
+ if (view->hints[PUGL_RESIZABLE]) {
+ [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ } else {
+ [drawView setAutoresizingMask:NSViewNotSizable];
+ }
+
+ impl->drawView = drawView;
+ return PUGL_SUCCESS;
+}
+
+static PuglStatus
+puglMacVulkanDestroy(PuglView* view)
+{
+ PuglVulkanView* const drawView = (PuglVulkanView*)view->impl->drawView;
+
+ [drawView removeFromSuperview];
+ [drawView release];
+
+ view->impl->drawView = nil;
+ return PUGL_SUCCESS;
+}
+
+struct PuglVulkanLoaderImpl {
+ void* libvulkan;
+ PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
+ PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
+};
+
+PuglVulkanLoader*
+puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world))
+{
+ PuglVulkanLoader* loader = (PuglVulkanLoader*)
+ calloc(1, sizeof(PuglVulkanLoader));
+ if (!loader) {
+ return NULL;
+ }
+
+ if (!(loader->libvulkan = dlopen("libvulkan.dylib", RTLD_LAZY))) {
+ free(loader);
+ return NULL;
+ }
+
+ loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)
+ dlsym(loader->libvulkan, "vkGetInstanceProcAddr");
+
+ loader->vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)
+ dlsym(loader->libvulkan, "vkGetDeviceProcAddr");
+
+ return loader;
+}
+
+void
+puglFreeVulkanLoader(PuglVulkanLoader* loader)
+{
+ if (loader) {
+ dlclose(loader->libvulkan);
+ free(loader);
+ }
+}
+
+PFN_vkGetInstanceProcAddr
+puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader)
+{
+ return loader->vkGetInstanceProcAddr;
+}
+
+PFN_vkGetDeviceProcAddr
+puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader)
+{
+ return loader->vkGetDeviceProcAddr;
+}
+
+const PuglBackend*
+puglVulkanBackend(void)
+{
+ static const PuglBackend backend = {puglStubConfigure,
+ puglMacVulkanCreate,
+ puglMacVulkanDestroy,
+ puglStubEnter,
+ puglStubLeave,
+ puglStubGetContext};
+
+ return &backend;
+}
+
+const char* const*
+puglGetInstanceExtensions(uint32_t* const count)
+{
+ static const char* const extensions[] = {"VK_KHR_surface",
+ "VK_MVK_macos_surface"};
+
+ *count = 2;
+ return extensions;
+}
+
+VkResult
+puglCreateSurface(const PuglVulkanLoader* const loader,
+ PuglView* const view,
+ VkInstance instance,
+ const VkAllocationCallbacks* const allocator,
+ VkSurfaceKHR* const surface)
+{
+ PuglInternals* const impl = view->impl;
+
+ PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK =
+ (PFN_vkCreateMacOSSurfaceMVK)puglGetInstanceProcAddrFunc(
+ loader)(instance, "vkCreateMacOSSurfaceMVK");
+
+ const VkMacOSSurfaceCreateInfoMVK info = {
+ VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK,
+ NULL,
+ 0,
+ impl->drawView,
+ };
+
+ return vkCreateMacOSSurfaceMVK(instance, &info, allocator, surface);
+}
diff --git a/include/pugl/detail/win_vulkan.c b/include/pugl/detail/win_vulkan.c
new file mode 100644
index 0000000..cfe3613
--- /dev/null
+++ b/include/pugl/detail/win_vulkan.c
@@ -0,0 +1,130 @@
+/*
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file win_vulkan.c Vulkan graphics backend for Windows.
+*/
+
+#define VK_NO_PROTOTYPES 1
+
+#include "pugl/detail/stub.h"
+#include "pugl/detail/types.h"
+#include "pugl/detail/win.h"
+#include "pugl/pugl_stub.h"
+#include "pugl/pugl_vulkan.h"
+
+#include <vulkan/vulkan.h>
+#include <vulkan/vulkan_win32.h>
+
+#include <stdlib.h>
+
+struct PuglVulkanLoaderImpl
+{
+ HMODULE libvulkan;
+ PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
+ PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
+};
+
+PuglVulkanLoader*
+puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world))
+{
+ PuglVulkanLoader* loader =
+ (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader));
+ if (!loader) {
+ return NULL;
+ }
+
+ if (!(loader->libvulkan = LoadLibrary("vulkan-1.dll"))) {
+ free(loader);
+ return NULL;
+ }
+
+ loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress(
+ loader->libvulkan, "vkGetInstanceProcAddr");
+
+ loader->vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)GetProcAddress(
+ loader->libvulkan, "vkGetDeviceProcAddr");
+
+ return loader;
+}
+
+void
+puglFreeVulkanLoader(PuglVulkanLoader* loader)
+{
+ if (loader) {
+ FreeLibrary(loader->libvulkan);
+ free(loader);
+ }
+}
+
+PFN_vkGetInstanceProcAddr
+puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader)
+{
+ return loader->vkGetInstanceProcAddr;
+}
+
+PFN_vkGetDeviceProcAddr
+puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader)
+{
+ return loader->vkGetDeviceProcAddr;
+}
+
+const PuglBackend*
+puglVulkanBackend()
+{
+ static const PuglBackend backend = {puglWinStubConfigure,
+ puglStubCreate,
+ puglStubDestroy,
+ puglWinStubEnter,
+ puglWinStubLeave,
+ puglStubGetContext};
+
+ return &backend;
+}
+
+const char* const*
+puglGetInstanceExtensions(uint32_t* const count)
+{
+ static const char* const extensions[] = {"VK_KHR_surface",
+ "VK_KHR_win32_surface"};
+
+ *count = 2;
+ return extensions;
+}
+
+VkResult
+puglCreateSurface(const PuglVulkanLoader* const loader,
+ PuglView* const view,
+ VkInstance instance,
+ const VkAllocationCallbacks* const pAllocator,
+ VkSurfaceKHR* const pSurface)
+{
+ PuglInternals* const impl = view->impl;
+
+ PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR =
+ (PFN_vkCreateWin32SurfaceKHR)puglGetInstanceProcAddrFunc(loader)(
+ instance, "vkCreateWin32SurfaceKHR");
+
+ const VkWin32SurfaceCreateInfoKHR createInfo = {
+ VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
+ NULL,
+ 0,
+ GetModuleHandle(NULL),
+ impl->hwnd,
+ };
+
+ return vkCreateWin32SurfaceKHR(instance, &createInfo, pAllocator, pSurface);
+}
diff --git a/include/pugl/detail/x11_vulkan.c b/include/pugl/detail/x11_vulkan.c
new file mode 100644
index 0000000..0bd1532
--- /dev/null
+++ b/include/pugl/detail/x11_vulkan.c
@@ -0,0 +1,134 @@
+/*
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file x11_vulkan.c Vulkan graphics backend for X11.
+*/
+
+#define VK_NO_PROTOTYPES 1
+
+#include "pugl/detail/stub.h"
+#include "pugl/detail/types.h"
+#include "pugl/detail/x11.h"
+#include "pugl/pugl.h"
+#include "pugl/pugl_vulkan.h"
+
+#include <vulkan/vulkan_core.h>
+#include <vulkan/vulkan_xlib.h>
+
+#include <dlfcn.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+struct PuglVulkanLoaderImpl
+{
+ void* libvulkan;
+ PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
+ PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
+};
+
+PuglVulkanLoader*
+puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world))
+{
+ PuglVulkanLoader* loader =
+ (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader));
+ if (!loader) {
+ return NULL;
+ }
+
+ if (!(loader->libvulkan = dlopen("libvulkan.so", RTLD_LAZY))) {
+ free(loader);
+ return NULL;
+ }
+
+ loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(
+ loader->libvulkan, "vkGetInstanceProcAddr");
+
+ loader->vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)dlsym(
+ loader->libvulkan, "vkGetDeviceProcAddr");
+
+ return loader;
+}
+
+void
+puglFreeVulkanLoader(PuglVulkanLoader* loader)
+{
+ if (loader) {
+ dlclose(loader->libvulkan);
+ free(loader);
+ }
+}
+
+PFN_vkGetInstanceProcAddr
+puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader)
+{
+ return loader->vkGetInstanceProcAddr;
+}
+
+PFN_vkGetDeviceProcAddr
+puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader)
+{
+ return loader->vkGetDeviceProcAddr;
+}
+
+const PuglBackend*
+puglVulkanBackend(void)
+{
+ static const PuglBackend backend = {puglX11StubConfigure,
+ puglStubCreate,
+ puglStubDestroy,
+ puglStubEnter,
+ puglStubLeave,
+ puglStubGetContext};
+
+ return &backend;
+}
+
+const char* const*
+puglGetInstanceExtensions(uint32_t* const count)
+{
+ static const char* const extensions[] = {"VK_KHR_surface",
+ "VK_KHR_xlib_surface"};
+
+ *count = 2;
+ return extensions;
+}
+
+VkResult
+puglCreateSurface(const PuglVulkanLoader* const loader,
+ PuglView* const view,
+ VkInstance instance,
+ const VkAllocationCallbacks* const allocator,
+ VkSurfaceKHR* const surface)
+{
+ PuglInternals* const impl = view->impl;
+ PuglWorldInternals* world_impl = view->world->impl;
+
+ PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR =
+ (PFN_vkCreateXlibSurfaceKHR)puglGetInstanceProcAddrFunc(loader)(
+ instance, "vkCreateXlibSurfaceKHR");
+
+ const VkXlibSurfaceCreateInfoKHR info = {
+ VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
+ NULL,
+ 0,
+ world_impl->display,
+ impl->win,
+ };
+
+ return vkCreateXlibSurfaceKHR(instance, &info, allocator, surface);
+}
diff --git a/include/pugl/pugl_vulkan.h b/include/pugl/pugl_vulkan.h
new file mode 100644
index 0000000..451c28f
--- /dev/null
+++ b/include/pugl/pugl_vulkan.h
@@ -0,0 +1,148 @@
+/*
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl_vulkan.h Vulkan-specific API.
+
+ Note that this header includes Vulkan headers, so if you are writing a
+ program or plugin that dynamically loads vulkan, you should first define
+ `VK_NO_PROTOTYPES` before including it.
+*/
+
+#ifndef PUGL_PUGL_VULKAN_H
+#define PUGL_PUGL_VULKAN_H
+
+#include "pugl/pugl.h"
+
+#include <vulkan/vulkan.h>
+
+PUGL_BEGIN_DECLS
+
+/**
+ @defgroup vulkan Vulkan
+ Vulkan graphics support.
+
+ Vulkan support differs from OpenGL because almost all most configuration is
+ done using the Vulkan API itself, rather than by setting view hints to
+ configure the context. Pugl only provides a minimal loader for loading the
+ Vulkan library, and a portable function to create a Vulkan surface for a
+ view, which hides the platform-specific implementation details.
+
+ @ingroup pugl_c
+ @{
+*/
+
+/**
+ Dynamic Vulkan loader.
+
+ This can be used to dynamically load the Vulkan library. Applications or
+ plugins should not link against the Vulkan library, but instead use this at
+ runtime. This ensures that things will work on as many systems as possible,
+ and allows errors to be handled gracefully.
+
+ This is not a "loader" in the sense of loading all the required Vulkan
+ functions (which is the application's responsibility), but just a minimal
+ implementation to portably load the Vulkan library and get the two functions
+ that are used to load everything else.
+
+ Note that this owns the loaded Vulkan library, so it must outlive all use of
+ the Vulkan API.
+
+ @see https://www.khronos.org/registry/vulkan/specs/1.0/html/chap4.html
+*/
+typedef struct PuglVulkanLoaderImpl PuglVulkanLoader;
+
+/**
+ Create a new dynamic loader for Vulkan functions.
+
+ This dynamically loads the Vulkan library and gets the load functions from it.
+
+ @return A new Vulkan loader, or null on failure.
+*/
+PUGL_API PuglVulkanLoader*
+puglNewVulkanLoader(PuglWorld* world);
+
+/**
+ Free a loader created with puglNewVulkanLoader().
+
+ Note that this closes the Vulkan library, so no Vulkan objects or API may be
+ used after this is called.
+*/
+PUGL_API void
+puglFreeVulkanLoader(PuglVulkanLoader* loader);
+
+/**
+ Return the `vkGetInstanceProcAddr` function.
+
+ @return Null if the Vulkan library does not contain this function (which is
+ unlikely and indicates a broken system).
+*/
+PUGL_API PFN_vkGetInstanceProcAddr
+puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader);
+
+/**
+ Return the `vkGetDeviceProcAddr` function.
+
+ @return Null if the Vulkan library does not contain this function (which is
+ unlikely and indicates a broken system).
+*/
+PUGL_API PFN_vkGetDeviceProcAddr
+puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader);
+
+/**
+ Return the Vulkan instance extensions required to draw to a PuglView.
+
+ This simply returns static strings, it does not access Vulkan or the window
+ system. The returned array always contains at least "VK_KHR_surface".
+
+ @param[out] count The number of extensions in the returned array.
+ @return An array of extension name strings.
+*/
+PUGL_API const char* const*
+puglGetInstanceExtensions(uint32_t* count);
+
+/**
+ Create a Vulkan surface for a Pugl view.
+
+ @param loader The loader for Vulkan functions.
+ @param view The view the surface is to be displayed on.
+ @param instance The Vulkan instance.
+ @param allocator Vulkan allocation callbacks, may be NULL.
+ @param[out] surface Pointed to a newly created Vulkan surface.
+ @return `VK_SUCCESS` on success, or a Vulkan error code.
+*/
+PUGL_API VkResult
+puglCreateSurface(const PuglVulkanLoader* loader,
+ PuglView* view,
+ VkInstance instance,
+ const VkAllocationCallbacks* allocator,
+ VkSurfaceKHR* surface);
+
+/**
+ Vulkan graphics backend.
+
+ Pass the returned value to puglSetBackend() to draw to a view with Vulkan.
+*/
+PUGL_API PUGL_CONST_FUNC const PuglBackend*
+puglVulkanBackend(void);
+
+/**
+ @}
+*/
+
+PUGL_END_DECLS
+
+#endif // PUGL_PUGL_VULKAN_H
diff --git a/include/pugl/pugl_vulkan.hpp b/include/pugl/pugl_vulkan.hpp
new file mode 100644
index 0000000..9241aba
--- /dev/null
+++ b/include/pugl/pugl_vulkan.hpp
@@ -0,0 +1,170 @@
+/*
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl_vulkan.hpp Vulkan-specific C++ API.
+
+ Note that this header includes Vulkan headers, so if you are writing a
+ program or plugin that dynamically loads vulkan, you should first define
+ `VK_NO_PROTOTYPES` before including it.
+*/
+
+#ifndef PUGL_PUGL_VULKAN_HPP
+#define PUGL_PUGL_VULKAN_HPP
+
+#include "pugl/pugl.h"
+#include "pugl/pugl.hpp"
+#include "pugl/pugl_vulkan.h"
+
+#include <vulkan/vulkan_core.h>
+
+#include <cstdint>
+
+namespace pugl {
+
+/**
+ @defgroup vulkanxx Vulkan
+ Vulkan graphics support.
+
+ Note that the Pugl C++ wrapper does not use vulkan.hpp because it is a
+ heavyweight dependency which not everyone uses, and its design is not very
+ friendly to dynamic loading in plugins anyway. However, if you do use
+ vulkan.hpp smart handles, it is relatively straightforward to wrap the
+ result of createSurface() manually.
+
+ @ingroup pugl_cxx
+ @{
+*/
+
+/// @copydoc PuglVulkanLoader
+class PUGL_API VulkanLoader final
+ : public detail::Wrapper<PuglVulkanLoader, puglFreeVulkanLoader>
+{
+public:
+ /**
+ Create a new dynamic loader for Vulkan functions.
+
+ This dynamically loads the Vulkan library and gets the load functions
+ from it.
+
+ Note that this constructor does not throw exceptions, though failure is
+ possible. To check if the Vulkan library failed to load, test this
+ loader, which is explicitly convertible to `bool`. It is safe to use a
+ failed loader, but the accessors will always return null.
+ */
+ explicit VulkanLoader(World& world) noexcept
+ : Wrapper{puglNewVulkanLoader(world.cobj())}
+ {}
+
+ /**
+ Return the `vkGetInstanceProcAddr` function.
+
+ @return Null if the Vulkan library failed to load, or does not contain
+ this function (which is unlikely and indicates a broken system).
+ */
+ PFN_vkGetInstanceProcAddr getInstanceProcAddrFunc() const noexcept
+ {
+ return cobj() ? puglGetInstanceProcAddrFunc(cobj()) : nullptr;
+ }
+
+ /**
+ Return the `vkGetDeviceProcAddr` function.
+
+ @return Null if the Vulkan library failed to load, or does not contain
+ this function (which is unlikely and indicates a broken system).
+ */
+ PFN_vkGetDeviceProcAddr getDeviceProcAddrFunc() const noexcept
+ {
+ return cobj() ? puglGetDeviceProcAddrFunc(cobj()) : nullptr;
+ }
+
+ /// Return true if this loader is valid to use
+ explicit operator bool() const noexcept { return cobj(); }
+};
+
+/**
+ A simple wrapper for an array of static C strings.
+
+ This provides a minimal API that supports iteration, like `std::vector`, but
+ avoids allocation, exceptions, and a dependency on the C++ standard library.
+*/
+class PUGL_API StaticStringArray final
+{
+public:
+ using value_type = const char*;
+ using const_iterator = const char* const*;
+ using size_type = uint32_t;
+
+ StaticStringArray(const char* const* strings, const uint32_t size) noexcept
+ : _strings{strings}
+ , _size{size}
+ {}
+
+ const char* const* begin() const noexcept { return _strings; }
+ const char* const* end() const noexcept { return _strings + _size; }
+ const char* const* data() const noexcept { return _strings; }
+ uint32_t size() const noexcept { return _size; }
+
+private:
+ const char* const* _strings;
+ uint32_t _size;
+};
+
+/**
+ Return the Vulkan instance extensions required to draw to a PuglView.
+
+ If successful, the returned array always contains "VK_KHR_surface", along
+ with whatever other platform-specific extensions are required.
+
+ @return An array of extension name strings.
+*/
+inline StaticStringArray
+getInstanceExtensions() noexcept
+{
+ uint32_t count = 0;
+ const char* const* const extensions = puglGetInstanceExtensions(&count);
+
+ return StaticStringArray{extensions, count};
+}
+
+/// @copydoc puglCreateSurface
+inline VkResult
+createSurface(const VulkanLoader& loader,
+ View& view,
+ VkInstance instance,
+ const VkAllocationCallbacks* const allocator,
+ VkSurfaceKHR* const surface) noexcept
+{
+ const VkResult r = puglCreateSurface(
+ loader.cobj(), view.cobj(), instance, allocator, surface);
+
+ return (!r && !surface) ? VK_ERROR_INITIALIZATION_FAILED : r;
+}
+
+/// @copydoc puglVulkanBackend
+static inline const PuglBackend*
+vulkanBackend() noexcept
+{
+ return puglVulkanBackend();
+}
+
+/**
+ @}
+*/
+
+} // namespace pugl
+
+#endif // PUGL_PUGL_VULKAN_HPP
diff --git a/wscript b/wscript
index d42247a..4101e91 100644
--- a/wscript
+++ b/wscript
@@ -31,6 +31,7 @@ def options(ctx):
ctx.add_flags(
opts,
{'all-headers': 'install complete header implementation',
+ 'no-vulkan': 'do not build Vulkan support',
'no-gl': 'do not build OpenGL support',
'no-cxx': 'do not build C++ examples',
'no-cairo': 'do not build Cairo support',
@@ -100,6 +101,7 @@ def configure(conf):
'gcc': [
'-Wno-bad-function-cast',
'-Wno-float-equal',
+ '-Wno-pedantic',
],
'msvc': [
'/wd4191', # unsafe conversion from type to type
@@ -145,7 +147,25 @@ def configure(conf):
'-Wno-direct-ivar-access'],
})
+ sys_header = 'windows.h' if platform == 'win32' else ''
+
+ if not Options.options.no_vulkan:
+ vulkan_sdk = os.environ.get('VULKAN_SDK', None)
+ vulkan_cflags = ''
+ if vulkan_sdk:
+ vk_include_path = os.path.join(vulkan_sdk, 'Include')
+ vulkan_cflags = conf.env.CPPPATH_ST % vk_include_path
+
+ # Check for Vulkan header (needed for backends)
+ conf.check(features='c cxx',
+ cflags=vulkan_cflags,
+ cxxflags=vulkan_cflags,
+ header_name=sys_header + ' vulkan/vulkan.h',
+ uselib_store='VULKAN',
+ mandatory=False)
+
# Check for base system libraries needed on some systems
+ conf.check_cc(lib='pthread', uselib_store='PTHREAD', mandatory=False)
conf.check_cc(lib='m', uselib_store='M', mandatory=False)
conf.check_cc(lib='dl', uselib_store='DL', mandatory=False)
@@ -233,8 +253,9 @@ def configure(conf):
conf,
{"Build static library": bool(conf.env.BUILD_STATIC),
"Build shared library": bool(conf.env.BUILD_SHARED),
+ "Cairo support": bool(conf.env.HAVE_CAIRO),
"OpenGL support": bool(conf.env.HAVE_GL),
- "Cairo support": bool(conf.env.HAVE_CAIRO)})
+ "Vulkan support": bool(conf.env.HAVE_VULKAN)})
def _build_pc_file(bld, name, desc, target, libname, deps={}, requires=[]):
@@ -368,6 +389,12 @@ def build(bld):
uselib=['GDI32', 'USER32', 'GL'],
source=['include/pugl/detail/win_gl.c'])
+ if bld.env.HAVE_VULKAN:
+ build_backend('win', 'vulkan',
+ uselib=['GDI32', 'USER32', 'VULKAN'],
+ source=['include/pugl/detail/win_vulkan.c',
+ 'include/pugl/detail/win_stub.c'])
+
if bld.env.HAVE_CAIRO:
build_backend('win', 'cairo',
uselib=['CAIRO', 'GDI32', 'USER32'],
@@ -389,6 +416,11 @@ def build(bld):
framework=['Cocoa', 'Corevideo', 'OpenGL'],
source=['include/pugl/detail/mac_gl.m'])
+ if bld.env.HAVE_VULKAN:
+ build_backend('mac', 'vulkan',
+ framework=['Cocoa', 'QuartzCore'],
+ source=['include/pugl/detail/mac_vulkan.m'])
+
if bld.env.HAVE_CAIRO:
build_backend('mac', 'cairo',
framework=['Cocoa', 'Corevideo'],
@@ -410,6 +442,12 @@ def build(bld):
uselib=[glx_lib, 'X11'],
source=['include/pugl/detail/x11_gl.c'])
+ if bld.env.HAVE_VULKAN:
+ build_backend('x11', 'vulkan',
+ uselib=['DL', 'X11'],
+ source=['include/pugl/detail/x11_vulkan.c',
+ 'include/pugl/detail/x11_stub.c'])
+
if bld.env.HAVE_CAIRO:
build_backend('x11', 'cairo',
uselib=['CAIRO', 'X11'],