diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt
index 47a0e3099..3c536797a 100644
--- a/renderdoc/CMakeLists.txt
+++ b/renderdoc/CMakeLists.txt
@@ -17,6 +17,14 @@ elseif(APPLE)
list(APPEND RDOC_LIBRARIES
PRIVATE -lm
PRIVATE -ldl)
+
+ if(ENABLE_VULKAN)
+ find_library(COCOA_LIBRARY Cocoa)
+ list(APPEND RDOC_LIBRARIES PRIVATE ${COCOA_LIBRARY})
+
+ find_library(QUARTZCORE_LIBRARY QuartzCore)
+ list(APPEND RDOC_LIBRARIES PRIVATE ${QUARTZCORE_LIBRARY})
+ endif()
elseif(UNIX)
find_package(PkgConfig REQUIRED)
find_package(Threads REQUIRED)
diff --git a/renderdoc/driver/vulkan/CMakeLists.txt b/renderdoc/driver/vulkan/CMakeLists.txt
index 1ab5fdd1e..4f2d63b4f 100644
--- a/renderdoc/driver/vulkan/CMakeLists.txt
+++ b/renderdoc/driver/vulkan/CMakeLists.txt
@@ -81,7 +81,9 @@ if(ANDROID)
list(APPEND sources vk_posix.cpp vk_android.cpp vk_layer_android.cpp)
list(APPEND definitions PRIVATE -DVK_USE_PLATFORM_ANDROID_KHR)
elseif(APPLE)
- list(APPEND sources vk_posix.cpp vk_apple.cpp)
+ list(APPEND sources vk_posix.cpp vk_apple.cpp vk_apple.mm)
+
+ add_definitions(-DVK_USE_PLATFORM_MACOS_MVK)
elseif(UNIX)
list(APPEND sources vk_posix.cpp vk_linux.cpp)
diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj
index 533e75d5c..1be40eb90 100644
--- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj
+++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj
@@ -186,6 +186,7 @@
+
diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters
index e4dbce43b..0f5f89823 100644
--- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters
+++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters
@@ -253,5 +253,8 @@
Layer
+
+ OS\Posix
+
\ No newline at end of file
diff --git a/renderdoc/driver/vulkan/vk_android.cpp b/renderdoc/driver/vulkan/vk_android.cpp
index 8395cf9ad..e0ac8fc38 100644
--- a/renderdoc/driver/vulkan/vk_android.cpp
+++ b/renderdoc/driver/vulkan/vk_android.cpp
@@ -26,6 +26,31 @@
#include "vk_core.h"
#include "vk_replay.h"
+VkResult WrappedVulkan::vkCreateAndroidSurfaceKHR(VkInstance instance,
+ const VkAndroidSurfaceCreateInfoKHR *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkSurfaceKHR *pSurface)
+{
+ // should not come in here at all on replay
+ RDCASSERT(IsCaptureMode(m_State));
+
+ VkResult ret = ObjDisp(instance)->CreateAndroidSurfaceKHR(Unwrap(instance), pCreateInfo,
+ pAllocator, pSurface);
+
+ if(ret == VK_SUCCESS)
+ {
+ GetResourceManager()->WrapResource(Unwrap(instance), *pSurface);
+
+ WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
+
+ // since there's no point in allocating a full resource record and storing the window
+ // handle under there somewhere, we just cast. We won't use the resource record for anything
+ wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->window;
+ }
+
+ return ret;
+}
+
void VulkanReplay::OutputWindow::SetWindowHandle(WindowingData window)
{
RDCASSERT(window.system == WindowingSystem::Android, window.system);
diff --git a/renderdoc/driver/vulkan/vk_apple.cpp b/renderdoc/driver/vulkan/vk_apple.cpp
index a86bf418a..8387d7c2d 100644
--- a/renderdoc/driver/vulkan/vk_apple.cpp
+++ b/renderdoc/driver/vulkan/vk_apple.cpp
@@ -25,29 +25,76 @@
#include "vk_core.h"
#include "vk_replay.h"
+#include
+
+// helpers defined in vk_apple.mm
+extern "C" int getCALayerWidth(void *handle);
+extern "C" int getCALayerHeight(void *handle);
+
+VkResult WrappedVulkan::vkCreateMacOSSurfaceMVK(VkInstance instance,
+ const VkMacOSSurfaceCreateInfoMVK *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkSurfaceKHR *pSurface)
+{
+ // should not come in here at all on replay
+ RDCASSERT(IsCaptureMode(m_State));
+
+ VkResult ret =
+ ObjDisp(instance)->CreateMacOSSurfaceMVK(Unwrap(instance), pCreateInfo, pAllocator, pSurface);
+
+ if(ret == VK_SUCCESS)
+ {
+ GetResourceManager()->WrapResource(Unwrap(instance), *pSurface);
+
+ WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
+
+ // since there's no point in allocating a full resource record and storing the window
+ // handle under there somewhere, we just cast. We won't use the resource record for anything
+ wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->pView;
+ }
+
+ return ret;
+}
+
void VulkanReplay::OutputWindow::SetWindowHandle(WindowingData window)
{
- RDCUNIMPLEMENTED("SetWindowHandle");
+ RDCASSERT(window.system == WindowingSystem::MacOS, window.system);
+ wnd = window.macOS.layer;
}
void VulkanReplay::OutputWindow::CreateSurface(VkInstance inst)
{
- RDCUNIMPLEMENTED("CreateSurface");
+ VkMacOSSurfaceCreateInfoMVK createInfo;
+
+ createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+ createInfo.pNext = NULL;
+ createInfo.flags = 0;
+ createInfo.pView = wnd;
+
+ VkResult vkr = ObjDisp(inst)->CreateMacOSSurfaceMVK(Unwrap(inst), &createInfo, NULL, &surface);
+ RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
void VulkanReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h)
{
- RDCUNIMPLEMENTED("GetOutputWindowDimensions");
+ if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
+ return;
+
+ OutputWindow &outw = m_OutputWindows[id];
+
+ w = getCALayerWidth(outw.wnd);
+ h = getCALayerHeight(outw.wnd);
}
-const char *VulkanLibraryName = "libvulkan.so";
+const char *VulkanLibraryName = "libvulkan.1.dylib";
-bool VulkanReplay::CheckVulkanLayer(VulkanLayerFlags &flags, std::vector &myJSONs,
- std::vector &otherJSONs)
+string GetThisLibPath()
{
- return false;
-}
-
-void VulkanReplay::InstallVulkanLayer(bool systemLevel)
-{
-}
+ Dl_info info;
+ if(dladdr(&VulkanLibraryName, &info))
+ {
+ RDCDEBUG("GetThisLibPath = '%s'", info.dli_fname);
+ return info.dli_fname;
+ }
+ return "";
+}
\ No newline at end of file
diff --git a/renderdoc/driver/vulkan/vk_apple.mm b/renderdoc/driver/vulkan/vk_apple.mm
new file mode 100644
index 000000000..a72624ca0
--- /dev/null
+++ b/renderdoc/driver/vulkan/vk_apple.mm
@@ -0,0 +1,17 @@
+#import
+
+extern "C" int getCALayerWidth(void *handle)
+{
+ CALayer *layer = (CALayer *)handle;
+ assert([layer isKindOfClass:[CALayer class]]);
+
+ return layer.bounds.size.width;
+}
+
+extern "C" int getCALayerHeight(void *handle)
+{
+ CALayer *layer = (CALayer *)handle;
+ assert([layer isKindOfClass:[CALayer class]]);
+
+ return layer.bounds.size.height;
+}
\ No newline at end of file
diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp
index 76a5c20a9..5692d2096 100644
--- a/renderdoc/driver/vulkan/vk_core.cpp
+++ b/renderdoc/driver/vulkan/vk_core.cpp
@@ -800,6 +800,11 @@ static const VkExtensionProperties supportedExtensions[] = {
{
VK_KHR_XLIB_SURFACE_EXTENSION_NAME, VK_KHR_XLIB_SURFACE_SPEC_VERSION,
},
+#endif
+#ifdef VK_MVK_macos_surface
+ {
+ VK_MVK_MACOS_SURFACE_EXTENSION_NAME, VK_MVK_MACOS_SURFACE_SPEC_VERSION,
+ },
#endif
{
VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME, VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION,
diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h
index 45f53147f..2c5929b51 100644
--- a/renderdoc/driver/vulkan/vk_core.h
+++ b/renderdoc/driver/vulkan/vk_core.h
@@ -1543,6 +1543,13 @@ public:
const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface);
#endif
+#if defined(VK_USE_PLATFORM_MACOS_MVK)
+ // VK_MVK_macos_surface
+ VkResult vkCreateMacOSSurfaceMVK(VkInstance instance,
+ const VkMacOSSurfaceCreateInfoMVK *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface);
+#endif
+
#if defined(VK_USE_PLATFORM_XCB_KHR)
// VK_KHR_xcb_surface
VkResult vkCreateXcbSurfaceKHR(VkInstance instance, const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
diff --git a/renderdoc/driver/vulkan/vk_hookset_defs.h b/renderdoc/driver/vulkan/vk_hookset_defs.h
index 4d60ebb36..09b070858 100644
--- a/renderdoc/driver/vulkan/vk_hookset_defs.h
+++ b/renderdoc/driver/vulkan/vk_hookset_defs.h
@@ -69,6 +69,18 @@
HookDefine3(VkResult, vkGetFenceWin32HandleKHR, VkDevice, device, \
const VkFenceGetWin32HandleInfoKHR *, pGetWin32HandleInfo, HANDLE *, pHandle);
+#elif defined(VK_USE_PLATFORM_MACOS_MVK)
+
+#define HookInitInstance_PlatformSpecific() \
+ HookInitExtension(VK_MVK_macos_surface, CreateMacOSSurfaceMVK);
+
+#define HookInitDevice_PlatformSpecific()
+
+#define HookDefine_PlatformSpecific() \
+ HookDefine4(VkResult, vkCreateMacOSSurfaceMVK, VkInstance, instance, \
+ const VkMacOSSurfaceCreateInfoMVK *, pCreateInfo, const VkAllocationCallbacks *, \
+ pAllocator, VkSurfaceKHR *, pSurface);
+
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
#define HookInitInstance_PlatformSpecific() \
@@ -285,6 +297,7 @@
CheckExt(KHR_xcb_surface, VKXX); \
CheckExt(KHR_win32_surface, VKXX); \
CheckExt(KHR_android_surface, VKXX); \
+ CheckExt(MVK_macos_surface, VKXX); \
CheckExt(KHR_surface, VKXX); \
CheckExt(EXT_debug_report, VKXX); \
CheckExt(KHR_display, VKXX); \
diff --git a/renderdoc/driver/vulkan/vk_linux.cpp b/renderdoc/driver/vulkan/vk_linux.cpp
index 052ec2707..370cc3380 100644
--- a/renderdoc/driver/vulkan/vk_linux.cpp
+++ b/renderdoc/driver/vulkan/vk_linux.cpp
@@ -23,16 +23,115 @@
******************************************************************************/
#include "api/replay/renderdoc_replay.h"
-#include "api/replay/version.h"
-#include "strings/string_utils.h"
-
-#include
-#include
-#include
#include "vk_core.h"
#include "vk_replay.h"
+#if defined(VK_USE_PLATFORM_XCB_KHR)
+
+VkBool32 WrappedVulkan::vkGetPhysicalDeviceXcbPresentationSupportKHR(VkPhysicalDevice physicalDevice,
+ uint32_t queueFamilyIndex,
+ xcb_connection_t *connection,
+ xcb_visualid_t visual_id)
+{
+ return ObjDisp(physicalDevice)
+ ->GetPhysicalDeviceXcbPresentationSupportKHR(Unwrap(physicalDevice), queueFamilyIndex,
+ connection, visual_id);
+}
+
+namespace Keyboard
+{
+void UseConnection(xcb_connection_t *conn);
+}
+
+VkResult WrappedVulkan::vkCreateXcbSurfaceKHR(VkInstance instance,
+ const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkSurfaceKHR *pSurface)
+{
+ // should not come in here at all on replay
+ RDCASSERT(IsCaptureMode(m_State));
+
+ VkResult ret =
+ ObjDisp(instance)->CreateXcbSurfaceKHR(Unwrap(instance), pCreateInfo, pAllocator, pSurface);
+
+ if(ret == VK_SUCCESS)
+ {
+ GetResourceManager()->WrapResource(Unwrap(instance), *pSurface);
+
+ WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
+
+ // since there's no point in allocating a full resource record and storing the window
+ // handle under there somewhere, we just cast. We won't use the resource record for anything
+ wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->window;
+
+ Keyboard::UseConnection(pCreateInfo->connection);
+ }
+
+ return ret;
+}
+
+#endif
+
+#if defined(VK_USE_PLATFORM_XLIB_KHR)
+
+VkBool32 WrappedVulkan::vkGetPhysicalDeviceXlibPresentationSupportKHR(
+ VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display *dpy, VisualID visualID)
+{
+ return ObjDisp(physicalDevice)
+ ->GetPhysicalDeviceXlibPresentationSupportKHR(Unwrap(physicalDevice), queueFamilyIndex, dpy,
+ visualID);
+}
+
+namespace Keyboard
+{
+void CloneDisplay(Display *dpy);
+}
+
+VkResult WrappedVulkan::vkCreateXlibSurfaceKHR(VkInstance instance,
+ const VkXlibSurfaceCreateInfoKHR *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkSurfaceKHR *pSurface)
+{
+ // should not come in here at all on replay
+ RDCASSERT(IsCaptureMode(m_State));
+
+ VkResult ret =
+ ObjDisp(instance)->CreateXlibSurfaceKHR(Unwrap(instance), pCreateInfo, pAllocator, pSurface);
+
+ if(ret == VK_SUCCESS)
+ {
+ GetResourceManager()->WrapResource(Unwrap(instance), *pSurface);
+
+ WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
+
+ // since there's no point in allocating a full resource record and storing the window
+ // handle under there somewhere, we just cast. We won't use the resource record for anything
+ wrapped->record = (VkResourceRecord *)pCreateInfo->window;
+
+ Keyboard::CloneDisplay(pCreateInfo->dpy);
+ }
+
+ return ret;
+}
+
+VkResult WrappedVulkan::vkAcquireXlibDisplayEXT(VkPhysicalDevice physicalDevice, Display *dpy,
+ VkDisplayKHR display)
+{
+ // display is not wrapped so we can pass straight through
+ return ObjDisp(physicalDevice)->AcquireXlibDisplayEXT(Unwrap(physicalDevice), dpy, display);
+}
+
+VkResult WrappedVulkan::vkGetRandROutputDisplayEXT(VkPhysicalDevice physicalDevice, Display *dpy,
+ RROutput rrOutput, VkDisplayKHR *pDisplay)
+{
+ // display is not wrapped so we can pass straight through
+ return ObjDisp(physicalDevice)
+ ->GetRandROutputDisplayEXT(Unwrap(physicalDevice), dpy, rrOutput, pDisplay);
+}
+
+#endif
+
void VulkanReplay::OutputWindow::SetWindowHandle(WindowingData window)
{
#if ENABLED(RDOC_XLIB)
@@ -138,126 +237,11 @@ void VulkanReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h
const char *VulkanLibraryName = "libvulkan.so.1";
-// embedded data file
-
-extern unsigned char driver_vulkan_renderdoc_json[];
-extern int driver_vulkan_renderdoc_json_len;
-
-static std::string GenerateJSON(const std::string &sopath)
-{
- char *txt = (char *)driver_vulkan_renderdoc_json;
- int len = driver_vulkan_renderdoc_json_len;
-
- string json = string(txt, txt + len);
-
- const char dllPathString[] = ".\\\\renderdoc.dll";
-
- size_t idx = json.find(dllPathString);
-
- json = json.substr(0, idx) + sopath + json.substr(idx + sizeof(dllPathString) - 1);
-
- const char majorString[] = "[MAJOR]";
-
- idx = json.find(majorString);
- while(idx != string::npos)
- {
- json = json.substr(0, idx) + STRINGIZE(RENDERDOC_VERSION_MAJOR) +
- json.substr(idx + sizeof(majorString) - 1);
-
- idx = json.find(majorString);
- }
-
- const char minorString[] = "[MINOR]";
-
- idx = json.find(minorString);
- while(idx != string::npos)
- {
- json = json.substr(0, idx) + STRINGIZE(RENDERDOC_VERSION_MINOR) +
- json.substr(idx + sizeof(minorString) - 1);
-
- idx = json.find(minorString);
- }
-
- return json;
-}
-
-static bool FileExists(const std::string &path)
-{
- return access(path.c_str(), F_OK) == 0;
-}
-
-static std::string GetSOFromJSON(const std::string &json)
-{
- char *json_string = new char[1024];
- memset(json_string, 0, 1024);
-
- FILE *f = fopen(json.c_str(), "r");
-
- if(f)
- {
- fread(json_string, 1, 1024, f);
-
- fclose(f);
- }
-
- string ret = "";
-
- // The line is:
- // "library_path": "/foo/bar/librenderdoc.so",
- char *c = strstr(json_string, "library_path");
-
- if(c)
- {
- c += sizeof("library_path\": \"") - 1;
-
- char *quote = strchr(c, '"');
-
- if(quote)
- {
- *quote = 0;
- ret = c;
- }
- }
-
- delete[] json_string;
-
- return ret;
-}
-
-enum class LayerPath : int
-{
- usr,
- First = usr,
- etc,
- home,
- Count,
-};
-
-ITERABLE_OPERATORS(LayerPath);
-
-string LayerRegistrationPath(LayerPath path)
-{
- switch(path)
- {
- case LayerPath::usr: return "/usr/share/vulkan/implicit_layer.d/renderdoc_capture.json";
- case LayerPath::etc: return "/etc/vulkan/implicit_layer.d/renderdoc_capture.json";
- case LayerPath::home:
- {
- const char *xdg = getenv("XDG_DATA_HOME");
- if(xdg && FileIO::exists(xdg))
- return string(xdg) + "/vulkan/implicit_layer.d/renderdoc_capture.json";
-
- return string(getenv("HOME")) +
- "/.local/share/vulkan/implicit_layer.d/renderdoc_capture.json";
- }
- default: break;
- }
-
- return "";
-}
-
string GetThisLibPath()
{
+ // this is a hack, but the only reliable way to find the absolute path to the library.
+ // dladdr would be fine but it returns the wrong result for symbols in the library
+
string librenderdoc_path;
FILE *f = fopen("/proc/self/maps", "r");
@@ -341,161 +325,3 @@ string GetThisLibPath()
return librenderdoc_path;
}
-
-void MakeParentDirs(std::string file)
-{
- std::string dir = dirname(file);
-
- if(dir == "/" || dir.empty())
- return;
-
- MakeParentDirs(dir);
-
- if(FileExists(dir))
- return;
-
- mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-}
-
-bool VulkanReplay::CheckVulkanLayer(VulkanLayerFlags &flags, std::vector &myJSONs,
- std::vector &otherJSONs)
-{
- // see if the user has suppressed all this checking as a "I know what I'm doing" measure
-
- if(FileExists(string(getenv("HOME")) + "/.renderdoc/ignore_vulkan_layer_issues"))
- {
- flags = VulkanLayerFlags::ThisInstallRegistered;
- return false;
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////
- // check that there's only one layer registered, and it points to the same .so file that
- // we are running with in this instance of renderdoccmd
-
- // this is a hack, but the only reliable way to find the absolute path to the library.
- // dladdr would be fine but it returns the wrong result for symbols in the library
- string librenderdoc_path = GetThisLibPath();
-
- // it's impractical to determine whether the currently running RenderDoc build is just a loose
- // extract of a tarball or a distribution that decided to put all the files in the same folder,
- // and whether or not the library is in ld's searchpath.
- //
- // Instead we just make the requirement that renderdoc.json will always contain an absolute path
- // to the matching librenderdoc.so, so that we can check if it points to this build or another
- // build etc.
- //
- // Note there are three places to register layers - /usr, /etc and /home. The first is reserved
- // for distribution packages, so if it conflicts or needs to be deleted for this install to run,
- // we can't do that and have to just prompt the user. /etc we can mess with since that's for
- // non-distribution packages, but it will need root permissions.
-
- bool exist[arraydim()];
- bool match[arraydim()];
-
- int numExist = 0;
- int numMatch = 0;
-
- for(LayerPath i : values())
- {
- exist[(int)i] = FileExists(LayerRegistrationPath(i));
- match[(int)i] = (GetSOFromJSON(LayerRegistrationPath(i)) == librenderdoc_path);
-
- if(exist[(int)i])
- numExist++;
-
- if(match[(int)i])
- numMatch++;
- }
-
- flags = VulkanLayerFlags::CouldElevate | VulkanLayerFlags::UpdateAllowed;
-
- if(numMatch >= 1)
- flags |= VulkanLayerFlags::ThisInstallRegistered;
-
- // if we only have one registration, check that it points to us. If so, we're good
- if(numExist == 1 && numMatch == 1)
- return false;
-
- if(exist[(int)LayerPath::usr] && !match[(int)LayerPath::usr])
- otherJSONs.push_back(LayerRegistrationPath(LayerPath::usr));
-
- if(exist[(int)LayerPath::etc] && !match[(int)LayerPath::etc])
- otherJSONs.push_back(LayerRegistrationPath(LayerPath::etc));
-
- if(exist[(int)LayerPath::home] && !match[(int)LayerPath::home])
- otherJSONs.push_back(LayerRegistrationPath(LayerPath::home));
-
- if(!otherJSONs.empty())
- flags |= VulkanLayerFlags::OtherInstallsRegistered;
-
- if(exist[(int)LayerPath::usr] && match[(int)LayerPath::usr])
- {
- // just need to unregister others
- }
- else
- {
- myJSONs.push_back(LayerRegistrationPath(LayerPath::etc));
- myJSONs.push_back(LayerRegistrationPath(LayerPath::home));
- }
-
- if(exist[(int)LayerPath::usr] && !match[(int)LayerPath::usr])
- {
- flags = VulkanLayerFlags::Unfixable | VulkanLayerFlags::OtherInstallsRegistered;
- otherJSONs.clear();
- otherJSONs.push_back(LayerRegistrationPath(LayerPath::usr));
- }
-
- return true;
-}
-
-void VulkanReplay::InstallVulkanLayer(bool systemLevel)
-{
- std::string homePath = LayerRegistrationPath(LayerPath::home);
-
- // if we want to install to the system and there's a registration in $HOME, delete it
- if(systemLevel && FileExists(homePath))
- {
- if(unlink(homePath.c_str()) < 0)
- {
- const char *const errtext = strerror(errno);
- RDCERR("Error removing %s: %s", homePath.c_str(), errtext);
- }
- }
-
- std::string etcPath = LayerRegistrationPath(LayerPath::etc);
-
- // and vice-versa
- if(!systemLevel && FileExists(etcPath))
- {
- if(unlink(etcPath.c_str()) < 0)
- {
- const char *const errtext = strerror(errno);
- RDCERR("Error removing %s: %s", etcPath.c_str(), errtext);
- }
- }
-
- LayerPath idx = systemLevel ? LayerPath::etc : LayerPath::home;
-
- string jsonPath = LayerRegistrationPath(idx);
- string path = GetSOFromJSON(jsonPath);
- string libPath = GetThisLibPath();
-
- if(path != libPath)
- {
- MakeParentDirs(jsonPath);
-
- FILE *f = fopen(jsonPath.c_str(), "w");
-
- if(f)
- {
- fputs(GenerateJSON(libPath).c_str(), f);
-
- fclose(f);
- }
- else
- {
- const char *const errtext = strerror(errno);
- RDCERR("Error writing %s: %s", jsonPath.c_str(), errtext);
- }
- }
-}
\ No newline at end of file
diff --git a/renderdoc/driver/vulkan/vk_posix.cpp b/renderdoc/driver/vulkan/vk_posix.cpp
index 3e3b3d501..4bff678fa 100644
--- a/renderdoc/driver/vulkan/vk_posix.cpp
+++ b/renderdoc/driver/vulkan/vk_posix.cpp
@@ -22,9 +22,15 @@
* THE SOFTWARE.
******************************************************************************/
+#include "api/replay/version.h"
+#include "strings/string_utils.h"
#include "vk_core.h"
#include "vk_replay.h"
+#include
+#include
+#include
+
bool VulkanReplay::IsOutputWindowVisible(uint64_t id)
{
if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
@@ -44,7 +50,7 @@ void WrappedVulkan::AddRequiredExtensions(bool instance, vector &extensi
#define EXPECT_WSI 0
#if(defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \
- defined(VK_USE_PLATFORM_XLIB_KHR))
+ defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_MACOS_MVK))
#undef EXPECT_WSI
#define EXPECT_WSI 1
@@ -88,12 +94,27 @@ void WrappedVulkan::AddRequiredExtensions(bool instance, vector &extensi
}
#endif
+#if defined(VK_USE_PLATFORM_MACOS_MVK)
+ // must be supported
+ RDCASSERT(supportedExtensions.find(VK_MVK_MACOS_SURFACE_EXTENSION_NAME) !=
+ supportedExtensions.end());
+
+ m_SupportedWindowSystems.push_back(WindowingSystem::MacOS);
+
+ // don't add duplicates, application will have added this but just be sure
+ if(std::find(extensionList.begin(), extensionList.end(), VK_MVK_MACOS_SURFACE_EXTENSION_NAME) ==
+ extensionList.end())
+ {
+ extensionList.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
+ }
+#endif
+
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
// must be supported
RDCASSERT(supportedExtensions.find(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME) !=
supportedExtensions.end());
- m_SupportedWindowSystems.push_back(WindowingSystem::Android);
+ m_SupportedWindowSystems.push_back(WindowingSystem::macOS);
// don't add duplicates, application will have added this but just be sure
if(std::find(extensionList.begin(), extensionList.end(),
@@ -121,6 +142,11 @@ void WrappedVulkan::AddRequiredExtensions(bool instance, vector &extensi
{
RDCWARN("No WSI support - only headless replay allowed.");
+#if defined(VK_USE_PLATFORM_MACOS_MVK)
+ RDCWARN("macOS Output requires the '%s' extension to be present",
+ VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
+#endif
+
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
RDCWARN("Android Output requires the '%s' extension to be present",
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
@@ -155,134 +181,286 @@ void WrappedVulkan::AddRequiredExtensions(bool instance, vector &extensi
}
}
-#if defined(VK_USE_PLATFORM_XCB_KHR)
+// defined in vk_linux.cpp or vk_apple.cpp
+string GetThisLibPath();
-VkBool32 WrappedVulkan::vkGetPhysicalDeviceXcbPresentationSupportKHR(VkPhysicalDevice physicalDevice,
- uint32_t queueFamilyIndex,
- xcb_connection_t *connection,
- xcb_visualid_t visual_id)
+// embedded data file
+
+extern unsigned char driver_vulkan_renderdoc_json[];
+extern int driver_vulkan_renderdoc_json_len;
+
+static std::string GenerateJSON(const std::string &sopath)
{
- return ObjDisp(physicalDevice)
- ->GetPhysicalDeviceXcbPresentationSupportKHR(Unwrap(physicalDevice), queueFamilyIndex,
- connection, visual_id);
-}
+ char *txt = (char *)driver_vulkan_renderdoc_json;
+ int len = driver_vulkan_renderdoc_json_len;
-namespace Keyboard
-{
-void UseConnection(xcb_connection_t *conn);
-}
+ string json = string(txt, txt + len);
-VkResult WrappedVulkan::vkCreateXcbSurfaceKHR(VkInstance instance,
- const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
- const VkAllocationCallbacks *pAllocator,
- VkSurfaceKHR *pSurface)
-{
- // should not come in here at all on replay
- RDCASSERT(IsCaptureMode(m_State));
+ const char dllPathString[] = ".\\\\renderdoc.dll";
- VkResult ret =
- ObjDisp(instance)->CreateXcbSurfaceKHR(Unwrap(instance), pCreateInfo, pAllocator, pSurface);
+ size_t idx = json.find(dllPathString);
- if(ret == VK_SUCCESS)
+ json = json.substr(0, idx) + sopath + json.substr(idx + sizeof(dllPathString) - 1);
+
+ const char majorString[] = "[MAJOR]";
+
+ idx = json.find(majorString);
+ while(idx != string::npos)
{
- GetResourceManager()->WrapResource(Unwrap(instance), *pSurface);
+ json = json.substr(0, idx) + STRINGIZE(RENDERDOC_VERSION_MAJOR) +
+ json.substr(idx + sizeof(majorString) - 1);
- WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
-
- // since there's no point in allocating a full resource record and storing the window
- // handle under there somewhere, we just cast. We won't use the resource record for anything
- wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->window;
-
- Keyboard::UseConnection(pCreateInfo->connection);
+ idx = json.find(majorString);
}
+ const char minorString[] = "[MINOR]";
+
+ idx = json.find(minorString);
+ while(idx != string::npos)
+ {
+ json = json.substr(0, idx) + STRINGIZE(RENDERDOC_VERSION_MINOR) +
+ json.substr(idx + sizeof(minorString) - 1);
+
+ idx = json.find(minorString);
+ }
+
+ return json;
+}
+
+static bool FileExists(const std::string &path)
+{
+ return access(path.c_str(), F_OK) == 0;
+}
+
+static std::string GetSOFromJSON(const std::string &json)
+{
+ char *json_string = new char[1024];
+ memset(json_string, 0, 1024);
+
+ FILE *f = fopen(json.c_str(), "r");
+
+ if(f)
+ {
+ fread(json_string, 1, 1024, f);
+
+ fclose(f);
+ }
+
+ string ret = "";
+
+ // The line is:
+ // "library_path": "/foo/bar/librenderdoc.so",
+ char *c = strstr(json_string, "library_path");
+
+ if(c)
+ {
+ c += sizeof("library_path\": \"") - 1;
+
+ char *quote = strchr(c, '"');
+
+ if(quote)
+ {
+ *quote = 0;
+ ret = c;
+ }
+ }
+
+ delete[] json_string;
+
return ret;
}
-#endif
-
-#if defined(VK_USE_PLATFORM_XLIB_KHR)
-
-VkBool32 WrappedVulkan::vkGetPhysicalDeviceXlibPresentationSupportKHR(
- VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display *dpy, VisualID visualID)
+enum class LayerPath : int
{
- return ObjDisp(physicalDevice)
- ->GetPhysicalDeviceXlibPresentationSupportKHR(Unwrap(physicalDevice), queueFamilyIndex, dpy,
- visualID);
-}
+ usr,
+ First = usr,
+ etc,
+ home,
+ Count,
+};
-namespace Keyboard
+ITERABLE_OPERATORS(LayerPath);
+
+string LayerRegistrationPath(LayerPath path)
{
-void CloneDisplay(Display *dpy);
-}
-
-VkResult WrappedVulkan::vkCreateXlibSurfaceKHR(VkInstance instance,
- const VkXlibSurfaceCreateInfoKHR *pCreateInfo,
- const VkAllocationCallbacks *pAllocator,
- VkSurfaceKHR *pSurface)
-{
- // should not come in here at all on replay
- RDCASSERT(IsCaptureMode(m_State));
-
- VkResult ret =
- ObjDisp(instance)->CreateXlibSurfaceKHR(Unwrap(instance), pCreateInfo, pAllocator, pSurface);
-
- if(ret == VK_SUCCESS)
+ switch(path)
{
- GetResourceManager()->WrapResource(Unwrap(instance), *pSurface);
+ case LayerPath::usr: return "/usr/share/vulkan/implicit_layer.d/renderdoc_capture.json";
+ case LayerPath::etc: return "/etc/vulkan/implicit_layer.d/renderdoc_capture.json";
+ case LayerPath::home:
+ {
+ const char *xdg = getenv("XDG_DATA_HOME");
+ if(xdg && FileIO::exists(xdg))
+ return string(xdg) + "/vulkan/implicit_layer.d/renderdoc_capture.json";
- WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
-
- // since there's no point in allocating a full resource record and storing the window
- // handle under there somewhere, we just cast. We won't use the resource record for anything
- wrapped->record = (VkResourceRecord *)pCreateInfo->window;
-
- Keyboard::CloneDisplay(pCreateInfo->dpy);
+ return string(getenv("HOME")) +
+ "/.local/share/vulkan/implicit_layer.d/renderdoc_capture.json";
+ }
+ default: break;
}
- return ret;
+ return "";
}
-VkResult WrappedVulkan::vkAcquireXlibDisplayEXT(VkPhysicalDevice physicalDevice, Display *dpy,
- VkDisplayKHR display)
+void MakeParentDirs(std::string file)
{
- // display is not wrapped so we can pass straight through
- return ObjDisp(physicalDevice)->AcquireXlibDisplayEXT(Unwrap(physicalDevice), dpy, display);
+ std::string dir = dirname(file);
+
+ if(dir == "/" || dir.empty())
+ return;
+
+ MakeParentDirs(dir);
+
+ if(FileExists(dir))
+ return;
+
+ mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
}
-VkResult WrappedVulkan::vkGetRandROutputDisplayEXT(VkPhysicalDevice physicalDevice, Display *dpy,
- RROutput rrOutput, VkDisplayKHR *pDisplay)
+bool VulkanReplay::CheckVulkanLayer(VulkanLayerFlags &flags, std::vector &myJSONs,
+ std::vector &otherJSONs)
{
- // display is not wrapped so we can pass straight through
- return ObjDisp(physicalDevice)
- ->GetRandROutputDisplayEXT(Unwrap(physicalDevice), dpy, rrOutput, pDisplay);
-}
+ // see if the user has suppressed all this checking as a "I know what I'm doing" measure
-#endif
-
-#if defined(VK_USE_PLATFORM_ANDROID_KHR)
-VkResult WrappedVulkan::vkCreateAndroidSurfaceKHR(VkInstance instance,
- const VkAndroidSurfaceCreateInfoKHR *pCreateInfo,
- const VkAllocationCallbacks *pAllocator,
- VkSurfaceKHR *pSurface)
-{
- // should not come in here at all on replay
- RDCASSERT(IsCaptureMode(m_State));
-
- VkResult ret = ObjDisp(instance)->CreateAndroidSurfaceKHR(Unwrap(instance), pCreateInfo,
- pAllocator, pSurface);
-
- if(ret == VK_SUCCESS)
+ if(FileExists(string(getenv("HOME")) + "/.renderdoc/ignore_vulkan_layer_issues"))
{
- GetResourceManager()->WrapResource(Unwrap(instance), *pSurface);
-
- WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
-
- // since there's no point in allocating a full resource record and storing the window
- // handle under there somewhere, we just cast. We won't use the resource record for anything
- wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->window;
+ flags = VulkanLayerFlags::ThisInstallRegistered;
+ return false;
}
- return ret;
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // check that there's only one layer registered, and it points to the same .so file that
+ // we are running with in this instance of renderdoccmd
+
+ string librenderdoc_path = GetThisLibPath();
+
+ if(librenderdoc_path.empty() || !FileExists(librenderdoc_path))
+ {
+ RDCERR("Couldn't determine current library path!");
+ flags = VulkanLayerFlags::ThisInstallRegistered;
+ return false;
+ }
+
+ // it's impractical to determine whether the currently running RenderDoc build is just a loose
+ // extract of a tarball or a distribution that decided to put all the files in the same folder,
+ // and whether or not the library is in ld's searchpath.
+ //
+ // Instead we just make the requirement that renderdoc.json will always contain an absolute path
+ // to the matching librenderdoc.so, so that we can check if it points to this build or another
+ // build etc.
+ //
+ // Note there are three places to register layers - /usr, /etc and /home. The first is reserved
+ // for distribution packages, so if it conflicts or needs to be deleted for this install to run,
+ // we can't do that and have to just prompt the user. /etc we can mess with since that's for
+ // non-distribution packages, but it will need root permissions.
+
+ bool exist[arraydim()];
+ bool match[arraydim()];
+
+ int numExist = 0;
+ int numMatch = 0;
+
+ for(LayerPath i : values())
+ {
+ exist[(int)i] = FileExists(LayerRegistrationPath(i));
+ match[(int)i] = (GetSOFromJSON(LayerRegistrationPath(i)) == librenderdoc_path);
+
+ if(exist[(int)i])
+ numExist++;
+
+ if(match[(int)i])
+ numMatch++;
+ }
+
+ flags = VulkanLayerFlags::CouldElevate | VulkanLayerFlags::UpdateAllowed;
+
+ if(numMatch >= 1)
+ flags |= VulkanLayerFlags::ThisInstallRegistered;
+
+ // if we only have one registration, check that it points to us. If so, we're good
+ if(numExist == 1 && numMatch == 1)
+ return false;
+
+ if(exist[(int)LayerPath::usr] && !match[(int)LayerPath::usr])
+ otherJSONs.push_back(LayerRegistrationPath(LayerPath::usr));
+
+ if(exist[(int)LayerPath::etc] && !match[(int)LayerPath::etc])
+ otherJSONs.push_back(LayerRegistrationPath(LayerPath::etc));
+
+ if(exist[(int)LayerPath::home] && !match[(int)LayerPath::home])
+ otherJSONs.push_back(LayerRegistrationPath(LayerPath::home));
+
+ if(!otherJSONs.empty())
+ flags |= VulkanLayerFlags::OtherInstallsRegistered;
+
+ if(exist[(int)LayerPath::usr] && match[(int)LayerPath::usr])
+ {
+ // just need to unregister others
+ }
+ else
+ {
+ myJSONs.push_back(LayerRegistrationPath(LayerPath::etc));
+ myJSONs.push_back(LayerRegistrationPath(LayerPath::home));
+ }
+
+ if(exist[(int)LayerPath::usr] && !match[(int)LayerPath::usr])
+ {
+ flags = VulkanLayerFlags::Unfixable | VulkanLayerFlags::OtherInstallsRegistered;
+ otherJSONs.clear();
+ otherJSONs.push_back(LayerRegistrationPath(LayerPath::usr));
+ }
+
+ return true;
}
-#endif
+
+void VulkanReplay::InstallVulkanLayer(bool systemLevel)
+{
+ std::string homePath = LayerRegistrationPath(LayerPath::home);
+
+ // if we want to install to the system and there's a registration in $HOME, delete it
+ if(systemLevel && FileExists(homePath))
+ {
+ if(unlink(homePath.c_str()) < 0)
+ {
+ const char *const errtext = strerror(errno);
+ RDCERR("Error removing %s: %s", homePath.c_str(), errtext);
+ }
+ }
+
+ std::string etcPath = LayerRegistrationPath(LayerPath::etc);
+
+ // and vice-versa
+ if(!systemLevel && FileExists(etcPath))
+ {
+ if(unlink(etcPath.c_str()) < 0)
+ {
+ const char *const errtext = strerror(errno);
+ RDCERR("Error removing %s: %s", etcPath.c_str(), errtext);
+ }
+ }
+
+ LayerPath idx = systemLevel ? LayerPath::etc : LayerPath::home;
+
+ string jsonPath = LayerRegistrationPath(idx);
+ string path = GetSOFromJSON(jsonPath);
+ string libPath = GetThisLibPath();
+
+ if(path != libPath)
+ {
+ MakeParentDirs(jsonPath);
+
+ FILE *f = fopen(jsonPath.c_str(), "w");
+
+ if(f)
+ {
+ fputs(GenerateJSON(libPath).c_str(), f);
+
+ fclose(f);
+ }
+ else
+ {
+ const char *const errtext = strerror(errno);
+ RDCERR("Error writing %s: %s", jsonPath.c_str(), errtext);
+ }
+ }
+}
\ No newline at end of file
diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp
index f89e9f38d..42d817825 100644
--- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp
+++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp
@@ -152,9 +152,10 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVer
{
if(*it == "VK_KHR_xlib_surface" || *it == "VK_KHR_xcb_surface" ||
*it == "VK_KHR_wayland_surface" || *it == "VK_KHR_mir_surface" ||
- *it == "VK_KHR_android_surface" || *it == "VK_KHR_win32_surface" ||
- *it == "VK_KHR_display" || *it == "VK_EXT_direct_mode_display" ||
- *it == "VK_EXT_acquire_xlib_display" || *it == "VK_EXT_display_surface_counter")
+ *it == "VK_MVK_macos_surface " || *it == "VK_KHR_android_surface" ||
+ *it == "VK_KHR_win32_surface" || *it == "VK_KHR_display" ||
+ *it == "VK_EXT_direct_mode_display" || *it == "VK_EXT_acquire_xlib_display" ||
+ *it == "VK_EXT_display_surface_counter")
{
it = params.Extensions.erase(it);
}