mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-12 21:10:42 +00:00
Add support for MoltenVK in vulkan back-end
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -186,6 +186,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="renderdoc.json" />
|
||||
<None Include="vk_apple.mm" />
|
||||
</ItemGroup>
|
||||
<Target Name="_jsonProcess" BeforeTargets="PrepareForBuild">
|
||||
<!-- Read the version.h -->
|
||||
|
||||
@@ -253,5 +253,8 @@
|
||||
<None Include="renderdoc.json">
|
||||
<Filter>Layer</Filter>
|
||||
</None>
|
||||
<None Include="vk_apple.mm">
|
||||
<Filter>OS\Posix</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -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);
|
||||
|
||||
@@ -25,29 +25,76 @@
|
||||
#include "vk_core.h"
|
||||
#include "vk_replay.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
// 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<std::string> &myJSONs,
|
||||
std::vector<std::string> &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 "";
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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); \
|
||||
|
||||
@@ -23,16 +23,115 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "api/replay/renderdoc_replay.h"
|
||||
#include "api/replay/version.h"
|
||||
#include "strings/string_utils.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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<std::string> &myJSONs,
|
||||
std::vector<std::string> &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<LayerPath>()];
|
||||
bool match[arraydim<LayerPath>()];
|
||||
|
||||
int numExist = 0;
|
||||
int numMatch = 0;
|
||||
|
||||
for(LayerPath i : values<LayerPath>())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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<string> &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<string> &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<string> &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<string> &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<std::string> &myJSONs,
|
||||
std::vector<std::string> &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<LayerPath>()];
|
||||
bool match[arraydim<LayerPath>()];
|
||||
|
||||
int numExist = 0;
|
||||
int numMatch = 0;
|
||||
|
||||
for(LayerPath i : values<LayerPath>())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user