mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2025-12-18 14:02:31 +00:00
move to vulkan layer
This commit is contained in:
parent
7dfcfe252f
commit
614457fab7
17 changed files with 876 additions and 605 deletions
15
VkLayer_LS_frame_generation.json
Normal file
15
VkLayer_LS_frame_generation.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"file_format_version": "1.0.0",
|
||||||
|
"layer": {
|
||||||
|
"name": "VK_LAYER_LS_frame_generation",
|
||||||
|
"type": "GLOBAL",
|
||||||
|
"api_version": "1.4.313",
|
||||||
|
"library_path": "./build/liblsfg-vk.so",
|
||||||
|
"implementation_version": "1",
|
||||||
|
"description": "Lossless Scaling frame generation layer",
|
||||||
|
"functions": {
|
||||||
|
"vkGetInstanceProcAddr": "layer_vkGetInstanceProcAddr",
|
||||||
|
"vkGetDeviceProcAddr": "layer_vkGetDeviceProcAddr"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Hooks {
|
namespace Hooks {
|
||||||
|
|
||||||
|
|
@ -15,10 +17,8 @@ namespace Hooks {
|
||||||
uint64_t frameGen; // amount of frames to generate
|
uint64_t frameGen; // amount of frames to generate
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
/// Map of hooked Vulkan functions.
|
||||||
/// Install overrides for hooked Vulkan functions.
|
extern std::unordered_map<std::string, PFN_vkVoidFunction> hooks;
|
||||||
///
|
|
||||||
void initialize();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
214
include/layer.hpp
Normal file
214
include/layer.hpp
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
#ifndef LAYER_HPP
|
||||||
|
#define LAYER_HPP
|
||||||
|
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
namespace Layer {
|
||||||
|
/// Call to the original vkCreateInstance function.
|
||||||
|
VkResult ovkCreateInstance(
|
||||||
|
const VkInstanceCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkInstance* pInstance);
|
||||||
|
/// Call to the original vkDestroyInstance function.
|
||||||
|
void ovkDestroyInstance(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkAllocationCallbacks* pAllocator);
|
||||||
|
|
||||||
|
/// Call to the original vkCreateDevice function.
|
||||||
|
VkResult ovkCreateDevice(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
const VkDeviceCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkDevice* pDevice);
|
||||||
|
/// Call to the original vkDestroyDevice function.
|
||||||
|
void ovkDestroyDevice(
|
||||||
|
VkDevice device,
|
||||||
|
const VkAllocationCallbacks* pAllocator);
|
||||||
|
|
||||||
|
/// Call to the original vkGetInstanceProcAddr function.
|
||||||
|
PFN_vkVoidFunction ovkGetInstanceProcAddr(
|
||||||
|
VkInstance instance,
|
||||||
|
const char* pName);
|
||||||
|
/// Call to the original vkGetDeviceProcAddr function.
|
||||||
|
PFN_vkVoidFunction ovkGetDeviceProcAddr(
|
||||||
|
VkDevice device,
|
||||||
|
const char* pName);
|
||||||
|
|
||||||
|
/// Call to the original vkCreateSwapchainKHR function.
|
||||||
|
VkResult ovkCreateSwapchainKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkSwapchainCreateInfoKHR* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSwapchainKHR* pSwapchain);
|
||||||
|
/// Call to the original vkQueuePresentKHR function.
|
||||||
|
VkResult ovkQueuePresentKHR(
|
||||||
|
VkQueue queue,
|
||||||
|
const VkPresentInfoKHR* pPresentInfo);
|
||||||
|
/// Call to the original vkDestroySwapchainKHR function.
|
||||||
|
void ovkDestroySwapchainKHR(
|
||||||
|
VkDevice device,
|
||||||
|
VkSwapchainKHR swapchain,
|
||||||
|
const VkAllocationCallbacks* pAllocator);
|
||||||
|
|
||||||
|
/// Call to the original vkGetSwapchainImagesKHR function.
|
||||||
|
VkResult ovkGetSwapchainImagesKHR(
|
||||||
|
VkDevice device,
|
||||||
|
VkSwapchainKHR swapchain,
|
||||||
|
uint32_t* pSwapchainImageCount,
|
||||||
|
VkImage* pSwapchainImages);
|
||||||
|
|
||||||
|
/// Call to the original vkAllocateCommandBuffers function.
|
||||||
|
VkResult ovkAllocateCommandBuffers(
|
||||||
|
VkDevice device,
|
||||||
|
const VkCommandBufferAllocateInfo* pAllocateInfo,
|
||||||
|
VkCommandBuffer* pCommandBuffers);
|
||||||
|
/// Call to the original vkFreeCommandBuffers function.
|
||||||
|
void ovkFreeCommandBuffers(
|
||||||
|
VkDevice device,
|
||||||
|
VkCommandPool commandPool,
|
||||||
|
uint32_t commandBufferCount,
|
||||||
|
const VkCommandBuffer* pCommandBuffers);
|
||||||
|
|
||||||
|
/// Call to the original vkBeginCommandBuffer function.
|
||||||
|
VkResult ovkBeginCommandBuffer(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
const VkCommandBufferBeginInfo* pBeginInfo);
|
||||||
|
/// Call to the original vkEndCommandBuffer function.
|
||||||
|
VkResult ovkEndCommandBuffer(
|
||||||
|
VkCommandBuffer commandBuffer);
|
||||||
|
|
||||||
|
/// Call to the original vkCreateCommandPool function.
|
||||||
|
VkResult ovkCreateCommandPool(
|
||||||
|
VkDevice device,
|
||||||
|
const VkCommandPoolCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkCommandPool* pCommandPool);
|
||||||
|
/// Call to the original vkDestroyCommandPool function.
|
||||||
|
void ovkDestroyCommandPool(
|
||||||
|
VkDevice device,
|
||||||
|
VkCommandPool commandPool,
|
||||||
|
const VkAllocationCallbacks* pAllocator);
|
||||||
|
|
||||||
|
/// Call to the original vkCreateImage function.
|
||||||
|
VkResult ovkCreateImage(
|
||||||
|
VkDevice device,
|
||||||
|
const VkImageCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkImage* pImage);
|
||||||
|
/// Call to the original vkDestroyImage function.
|
||||||
|
void ovkDestroyImage(
|
||||||
|
VkDevice device,
|
||||||
|
VkImage image,
|
||||||
|
const VkAllocationCallbacks* pAllocator);
|
||||||
|
|
||||||
|
/// Call to the original vkGetImageMemoryRequirements function.
|
||||||
|
void ovkGetImageMemoryRequirements(
|
||||||
|
VkDevice device,
|
||||||
|
VkImage image,
|
||||||
|
VkMemoryRequirements* pMemoryRequirements);
|
||||||
|
/// Call to the original vkBindImageMemory function.
|
||||||
|
VkResult ovkBindImageMemory(
|
||||||
|
VkDevice device,
|
||||||
|
VkImage image,
|
||||||
|
VkDeviceMemory memory,
|
||||||
|
VkDeviceSize memoryOffset);
|
||||||
|
/// Call to the original vkAllocateMemory function.
|
||||||
|
VkResult ovkAllocateMemory(
|
||||||
|
VkDevice device,
|
||||||
|
const VkMemoryAllocateInfo* pAllocateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkDeviceMemory* pMemory);
|
||||||
|
/// Call to the original vkFreeMemory function.
|
||||||
|
void ovkFreeMemory(
|
||||||
|
VkDevice device,
|
||||||
|
VkDeviceMemory memory,
|
||||||
|
const VkAllocationCallbacks* pAllocator);
|
||||||
|
|
||||||
|
/// Call to the original vkCreateSemaphore function.
|
||||||
|
VkResult ovkCreateSemaphore(
|
||||||
|
VkDevice device,
|
||||||
|
const VkSemaphoreCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSemaphore* pSemaphore);
|
||||||
|
/// Call to the original vkDestroySemaphore function.
|
||||||
|
void ovkDestroySemaphore(
|
||||||
|
VkDevice device,
|
||||||
|
VkSemaphore semaphore,
|
||||||
|
const VkAllocationCallbacks* pAllocator);
|
||||||
|
|
||||||
|
/// Call to the original vkGetMemoryFdKHR function.
|
||||||
|
VkResult ovkGetMemoryFdKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkMemoryGetFdInfoKHR* pGetFdInfo,
|
||||||
|
int* pFd);
|
||||||
|
/// Call to the original vkGetSemaphoreFdKHR function.
|
||||||
|
VkResult ovkGetSemaphoreFdKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkSemaphoreGetFdInfoKHR* pGetFdInfo,
|
||||||
|
int* pFd);
|
||||||
|
|
||||||
|
/// Call to the original vkGetPhysicalDeviceQueueFamilyProperties function.
|
||||||
|
void ovkGetPhysicalDeviceQueueFamilyProperties(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
uint32_t* pQueueFamilyPropertyCount,
|
||||||
|
VkQueueFamilyProperties* pQueueFamilyProperties);
|
||||||
|
/// Call to the original vkGetPhysicalDeviceMemoryProperties function.
|
||||||
|
void ovkGetPhysicalDeviceMemoryProperties(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
VkPhysicalDeviceMemoryProperties* pMemoryProperties);
|
||||||
|
|
||||||
|
/// Call to the original vkGetDeviceQueue function.
|
||||||
|
void ovkGetDeviceQueue(
|
||||||
|
VkDevice device,
|
||||||
|
uint32_t queueFamilyIndex,
|
||||||
|
uint32_t queueIndex,
|
||||||
|
VkQueue* pQueue);
|
||||||
|
/// Call to the original vkQueueSubmit function.
|
||||||
|
VkResult ovkQueueSubmit(
|
||||||
|
VkQueue queue,
|
||||||
|
uint32_t submitCount,
|
||||||
|
const VkSubmitInfo* pSubmits,
|
||||||
|
VkFence fence);
|
||||||
|
|
||||||
|
/// Call to the original vkCmdPipelineBarrier function.
|
||||||
|
void ovkCmdPipelineBarrier(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
VkPipelineStageFlags srcStageMask,
|
||||||
|
VkPipelineStageFlags dstStageMask,
|
||||||
|
VkDependencyFlags dependencyFlags,
|
||||||
|
uint32_t memoryBarrierCount,
|
||||||
|
const VkMemoryBarrier* pMemoryBarriers,
|
||||||
|
uint32_t bufferMemoryBarrierCount,
|
||||||
|
const VkBufferMemoryBarrier* pBufferMemoryBarriers,
|
||||||
|
uint32_t imageMemoryBarrierCount,
|
||||||
|
const VkImageMemoryBarrier* pImageMemoryBarriers);
|
||||||
|
/// Call to the original vkCmdPipelineBarrier2 function.
|
||||||
|
void ovkCmdPipelineBarrier2(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
const VkDependencyInfo* pDependencyInfo);
|
||||||
|
/// Call to the original vkCmdCopyImage function.
|
||||||
|
void ovkCmdCopyImage(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
VkImage srcImage,
|
||||||
|
VkImageLayout srcImageLayout,
|
||||||
|
VkImage dstImage,
|
||||||
|
VkImageLayout dstImageLayout,
|
||||||
|
uint32_t regionCount,
|
||||||
|
const VkImageCopy* pRegions);
|
||||||
|
|
||||||
|
/// Call to the original vkAcquireNextImageKHR function.
|
||||||
|
VkResult ovkAcquireNextImageKHR(
|
||||||
|
VkDevice device,
|
||||||
|
VkSwapchainKHR swapchain,
|
||||||
|
uint64_t timeout,
|
||||||
|
VkSemaphore semaphore,
|
||||||
|
VkFence fence,
|
||||||
|
uint32_t* pImageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Symbol definition for Vulkan instance layer.
|
||||||
|
extern "C" PFN_vkVoidFunction layer_vkGetInstanceProcAddr(VkInstance instance, const char* pName);
|
||||||
|
/// Symbol definition for Vulkan device layer.
|
||||||
|
extern "C" PFN_vkVoidFunction layer_vkGetDeviceProcAddr(VkDevice device, const char* pName);
|
||||||
|
|
||||||
|
#endif // LAYER_HPP
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
#ifndef DL_HPP
|
|
||||||
#define DL_HPP
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
//
|
|
||||||
// This dynamic loader replaces the standard dlopen, dlsym, and dlclose functions.
|
|
||||||
// On initialize, the original functions are obtained via dlvsym (glibc exclusive)
|
|
||||||
// and made available under functions with the "o" prefix.
|
|
||||||
//
|
|
||||||
// Any call to regular dlopen, dlsym or dlclose is intercepted and may be
|
|
||||||
// overriden by registering a File override via `Loader::DL::registerFile`.
|
|
||||||
//
|
|
||||||
|
|
||||||
namespace Loader::DL {
|
|
||||||
|
|
||||||
/// Dynamic loader override structure.
|
|
||||||
class File {
|
|
||||||
public:
|
|
||||||
///
|
|
||||||
/// Create a dynamic loader override for a specific file.
|
|
||||||
///
|
|
||||||
/// @param filename The name of the file to override.
|
|
||||||
///
|
|
||||||
File(std::string filename)
|
|
||||||
: filename(std::move(filename)) {}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Append a symbol to the dynamic loader override.
|
|
||||||
///
|
|
||||||
/// @param symbol The name of the symbol to add.
|
|
||||||
/// @param address The address of the symbol.
|
|
||||||
///
|
|
||||||
void defineSymbol(const std::string& symbol, void* address) {
|
|
||||||
symbols[symbol] = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the filename
|
|
||||||
[[nodiscard]] const std::string& getFilename() const { return filename; }
|
|
||||||
/// Get all overriden symbols
|
|
||||||
[[nodiscard]] const std::unordered_map<std::string, void*>& getSymbols() const { return symbols; }
|
|
||||||
|
|
||||||
// Find a specific symbol
|
|
||||||
[[nodiscard]] void* findSymbol(const std::string& symbol) const {
|
|
||||||
auto it = symbols.find(symbol);
|
|
||||||
return (it != symbols.end()) ? it->second : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the fake handle
|
|
||||||
[[nodiscard]] void* getHandle() const { return handle; }
|
|
||||||
/// Get the real handle
|
|
||||||
[[nodiscard]] void* getOriginalHandle() const { return handle_orig; }
|
|
||||||
|
|
||||||
/// Set the fake handle
|
|
||||||
void setHandle(void* new_handle) { handle = new_handle; }
|
|
||||||
/// Set the real handle
|
|
||||||
void setOriginalHandle(void* new_handle) { handle_orig = new_handle; }
|
|
||||||
|
|
||||||
/// Copyable, moveable, default destructor
|
|
||||||
File(const File&) = default;
|
|
||||||
File(File&&) = default;
|
|
||||||
File& operator=(const File&) = default;
|
|
||||||
File& operator=(File&&) = default;
|
|
||||||
~File() = default;
|
|
||||||
private:
|
|
||||||
std::string filename;
|
|
||||||
std::unordered_map<std::string, void*> symbols;
|
|
||||||
|
|
||||||
void* handle = nullptr;
|
|
||||||
void* handle_orig = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Initialize the dynamic loader
|
|
||||||
///
|
|
||||||
void initialize();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Register a file with the dynamic loader.
|
|
||||||
///
|
|
||||||
/// @param file The file to register.
|
|
||||||
///
|
|
||||||
void registerFile(const File& file);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Disable hooks temporarily. This may be useful
|
|
||||||
/// when loading third-party libraries you wish not
|
|
||||||
/// to hook.
|
|
||||||
///
|
|
||||||
void disableHooks();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Re-enable hooks after they were disabled.
|
|
||||||
///
|
|
||||||
void enableHooks();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Call the original dlopen function.
|
|
||||||
///
|
|
||||||
/// @param filename The name of the file to open.
|
|
||||||
/// @param flag The flags to use when opening the file.
|
|
||||||
/// @return A handle to the opened file, or NULL on failure.
|
|
||||||
///
|
|
||||||
void* odlopen(const char* filename, int flag);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Call the original dlsym function.
|
|
||||||
///
|
|
||||||
/// @param handle The handle to the opened file.
|
|
||||||
/// @param symbol The name of the symbol to look up.
|
|
||||||
/// @return A pointer to the symbol, or NULL on failure.
|
|
||||||
///
|
|
||||||
void* odlsym(void* handle, const char* symbol);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Call the original dlclose function.
|
|
||||||
///
|
|
||||||
/// @param handle The handle to the opened file.
|
|
||||||
/// @return 0 on success, or -1 on failure.
|
|
||||||
///
|
|
||||||
int odlclose(void* handle);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Modified version of the dlopen function.
|
|
||||||
extern "C" void* dlopen(const char* filename, int flag) noexcept;
|
|
||||||
/// Modified version of the dlsym function.
|
|
||||||
extern "C" void* dlsym(void* handle, const char* symbol) noexcept;
|
|
||||||
/// Modified version of the dlclose function.
|
|
||||||
extern "C" int dlclose(void* handle) noexcept;
|
|
||||||
|
|
||||||
#endif // DL_HPP
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
#ifndef VK_HPP
|
|
||||||
#define VK_HPP
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
//
|
|
||||||
// Similar to the dynamic loader, the Vulkan loader replaces the standard
|
|
||||||
// vkGetInstanceProcAddr and vkGetDeviceProcAddr functions.
|
|
||||||
//
|
|
||||||
// One thing that should be noted, is that not every application uses the
|
|
||||||
// Vulkan loader for every method. On Linux it's not unusual to see dlsym
|
|
||||||
// being used for vulkan functions, so make sure to register the same
|
|
||||||
// symbol on both loaders.
|
|
||||||
//
|
|
||||||
|
|
||||||
namespace Loader::VK {
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Initialize the Vulkan loader.
|
|
||||||
///
|
|
||||||
void initialize();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Register a symbol to the Vulkan loader.
|
|
||||||
///
|
|
||||||
/// @param symbol The name of the Vulkan function to override.
|
|
||||||
/// @param address The address of the Vulkan function.
|
|
||||||
///
|
|
||||||
void registerSymbol(const std::string& symbol, void* address);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Call the original vkGetInstanceProcAddr function.
|
|
||||||
///
|
|
||||||
/// @param instance The (optional) Vulkan instance.
|
|
||||||
/// @param pName The name of the function to retrieve.
|
|
||||||
/// @return The address of the function, or nullptr if not found.
|
|
||||||
///
|
|
||||||
PFN_vkVoidFunction ovkGetInstanceProcAddr(VkInstance instance, const char* pName);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Call the original vkGetDeviceProcAddr function.
|
|
||||||
///
|
|
||||||
/// @param device The Vulkan device.
|
|
||||||
/// @param pName The name of the function to retrieve.
|
|
||||||
/// @return The address of the function, or nullptr if not found.
|
|
||||||
///
|
|
||||||
PFN_vkVoidFunction ovkGetDeviceProcAddr(VkDevice device, const char* pName);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Modified version of the vkGetInstanceProcAddr function.
|
|
||||||
extern "C" PFN_vkVoidFunction myvkGetInstanceProcAddr(VkInstance instance, const char* pName);
|
|
||||||
/// Modified version of the vkGetDeviceProcAddr function.
|
|
||||||
extern "C" PFN_vkVoidFunction myvkGetDeviceProcAddr(VkDevice device, const char* pName);
|
|
||||||
|
|
||||||
#endif // VK_HPP
|
|
||||||
|
|
@ -50,6 +50,16 @@ namespace Utils {
|
||||||
VkPipelineStageFlags pre, VkPipelineStageFlags post,
|
VkPipelineStageFlags pre, VkPipelineStageFlags post,
|
||||||
bool makeSrcPresentable, bool makeDstPresentable);
|
bool makeSrcPresentable, bool makeDstPresentable);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Store the current layer environment.
|
||||||
|
///
|
||||||
|
void storeLayerEnv();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Restore the layer environment to the previously stored value.
|
||||||
|
///
|
||||||
|
void restoreLayerEnv();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // UTILS_HPP
|
#endif // UTILS_HPP
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
#include "utils.hpp"
|
#include "layer.hpp"
|
||||||
|
#include "utils/utils.hpp"
|
||||||
|
|
||||||
#include <lsfg.hpp>
|
#include <lsfg.hpp>
|
||||||
|
|
||||||
|
|
@ -36,6 +37,10 @@ LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
&out_n_fds.at(i));
|
&out_n_fds.at(i));
|
||||||
|
|
||||||
|
Utils::storeLayerEnv();
|
||||||
|
LSFG::initialize();
|
||||||
|
Utils::restoreLayerEnv();
|
||||||
|
|
||||||
this->lsfgCtxId = std::shared_ptr<int32_t>(
|
this->lsfgCtxId = std::shared_ptr<int32_t>(
|
||||||
new int32_t(LSFG::createContext(extent.width, extent.height,
|
new int32_t(LSFG::createContext(extent.width, extent.height,
|
||||||
frame_0_fd, frame_1_fd, out_n_fds)),
|
frame_0_fd, frame_1_fd, out_n_fds)),
|
||||||
|
|
@ -98,7 +103,7 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk
|
||||||
// 3. acquire next swapchain image
|
// 3. acquire next swapchain image
|
||||||
pass.acquireSemaphores.at(i) = Mini::Semaphore(info.device);
|
pass.acquireSemaphores.at(i) = Mini::Semaphore(info.device);
|
||||||
uint32_t imageIdx{};
|
uint32_t imageIdx{};
|
||||||
auto res = vkAcquireNextImageKHR(info.device, this->swapchain, UINT64_MAX,
|
auto res = Layer::ovkAcquireNextImageKHR(info.device, this->swapchain, UINT64_MAX,
|
||||||
pass.acquireSemaphores.at(i).handle(), VK_NULL_HANDLE, &imageIdx);
|
pass.acquireSemaphores.at(i).handle(), VK_NULL_HANDLE, &imageIdx);
|
||||||
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
||||||
throw LSFG::vulkan_error(res, "Failed to acquire next swapchain image");
|
throw LSFG::vulkan_error(res, "Failed to acquire next swapchain image");
|
||||||
|
|
@ -136,7 +141,7 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk
|
||||||
.pSwapchains = &this->swapchain,
|
.pSwapchains = &this->swapchain,
|
||||||
.pImageIndices = &imageIdx,
|
.pImageIndices = &imageIdx,
|
||||||
};
|
};
|
||||||
res = vkQueuePresentKHR(queue, &presentInfo);
|
res = Layer::ovkQueuePresentKHR(queue, &presentInfo);
|
||||||
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
||||||
throw LSFG::vulkan_error(res, "Failed to present swapchain image");
|
throw LSFG::vulkan_error(res, "Failed to present swapchain image");
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +157,7 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk
|
||||||
.pSwapchains = &this->swapchain,
|
.pSwapchains = &this->swapchain,
|
||||||
.pImageIndices = &presentIdx,
|
.pImageIndices = &presentIdx,
|
||||||
};
|
};
|
||||||
auto res = vkQueuePresentKHR(queue, &presentInfo);
|
auto res = Layer::ovkQueuePresentKHR(queue, &presentInfo);
|
||||||
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
||||||
throw LSFG::vulkan_error(res, "Failed to present swapchain image");
|
throw LSFG::vulkan_error(res, "Failed to present swapchain image");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
#include "loader/dl.hpp"
|
|
||||||
#include "loader/vk.hpp"
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "hooks.hpp"
|
#include "hooks.hpp"
|
||||||
#include "log.hpp"
|
#include "context.hpp"
|
||||||
#include "utils.hpp"
|
#include "layer.hpp"
|
||||||
|
#include "utils/log.hpp"
|
||||||
|
#include "utils/utils.hpp"
|
||||||
|
|
||||||
#include <lsfg.hpp>
|
#include <lsfg.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
using namespace Hooks;
|
using namespace Hooks;
|
||||||
|
|
||||||
|
|
@ -20,11 +20,6 @@ namespace {
|
||||||
const VkInstanceCreateInfo* pCreateInfo,
|
const VkInstanceCreateInfo* pCreateInfo,
|
||||||
const VkAllocationCallbacks* pAllocator,
|
const VkAllocationCallbacks* pAllocator,
|
||||||
VkInstance* pInstance) {
|
VkInstance* pInstance) {
|
||||||
// create lsfg
|
|
||||||
Loader::DL::disableHooks();
|
|
||||||
LSFG::initialize();
|
|
||||||
Loader::DL::enableHooks();
|
|
||||||
|
|
||||||
// add extensions
|
// add extensions
|
||||||
auto extensions = Utils::addExtensions(pCreateInfo->ppEnabledExtensionNames,
|
auto extensions = Utils::addExtensions(pCreateInfo->ppEnabledExtensionNames,
|
||||||
pCreateInfo->enabledExtensionCount, {
|
pCreateInfo->enabledExtensionCount, {
|
||||||
|
|
@ -33,24 +28,26 @@ namespace {
|
||||||
"VK_KHR_external_semaphore_capabilities"
|
"VK_KHR_external_semaphore_capabilities"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Log::info("lsfg-vk: Created Vulkan instance");
|
||||||
VkInstanceCreateInfo createInfo = *pCreateInfo;
|
VkInstanceCreateInfo createInfo = *pCreateInfo;
|
||||||
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||||||
createInfo.ppEnabledExtensionNames = extensions.data();
|
createInfo.ppEnabledExtensionNames = extensions.data();
|
||||||
return vkCreateInstance(&createInfo, pAllocator, pInstance);
|
return Layer::ovkCreateInstance(&createInfo, pAllocator, pInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
void myvkDestroyInstance(
|
void myvkDestroyInstance(
|
||||||
VkInstance instance,
|
VkInstance instance,
|
||||||
const VkAllocationCallbacks* pAllocator) {
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
LSFG::finalize(); // destroy lsfg
|
LSFG::finalize(); // destroy lsfg
|
||||||
vkDestroyInstance(instance, pAllocator);
|
Log::info("lsfg-vk: Destroyed Vulkan instance");
|
||||||
|
Layer::ovkDestroyInstance(instance, pAllocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// device hooks
|
// device hooks
|
||||||
|
|
||||||
std::unordered_map<VkDevice, DeviceInfo> devices;
|
std::unordered_map<VkDevice, DeviceInfo> devices;
|
||||||
|
|
||||||
VkResult myvkCreateDevice(
|
VkResult myvkCreateDevicePre(
|
||||||
VkPhysicalDevice physicalDevice,
|
VkPhysicalDevice physicalDevice,
|
||||||
const VkDeviceCreateInfo* pCreateInfo,
|
const VkDeviceCreateInfo* pCreateInfo,
|
||||||
const VkAllocationCallbacks* pAllocator,
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
|
@ -67,8 +64,14 @@ namespace {
|
||||||
VkDeviceCreateInfo createInfo = *pCreateInfo;
|
VkDeviceCreateInfo createInfo = *pCreateInfo;
|
||||||
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||||||
createInfo.ppEnabledExtensionNames = extensions.data();
|
createInfo.ppEnabledExtensionNames = extensions.data();
|
||||||
auto res = vkCreateDevice(physicalDevice, &createInfo, pAllocator, pDevice);
|
return Layer::ovkCreateDevice(physicalDevice, &createInfo, pAllocator, pDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult myvkCreateDevicePost(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
VkDeviceCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkDevice* pDevice) {
|
||||||
// store device info
|
// store device info
|
||||||
try {
|
try {
|
||||||
const char* frameGen = std::getenv("LSFG_MULTIPLIER");
|
const char* frameGen = std::getenv("LSFG_MULTIPLIER");
|
||||||
|
|
@ -76,7 +79,7 @@ namespace {
|
||||||
devices.emplace(*pDevice, DeviceInfo {
|
devices.emplace(*pDevice, DeviceInfo {
|
||||||
.device = *pDevice,
|
.device = *pDevice,
|
||||||
.physicalDevice = physicalDevice,
|
.physicalDevice = physicalDevice,
|
||||||
.queue = Utils::findQueue(*pDevice, physicalDevice, &createInfo,
|
.queue = Utils::findQueue(*pDevice, physicalDevice, pCreateInfo,
|
||||||
VK_QUEUE_GRAPHICS_BIT),
|
VK_QUEUE_GRAPHICS_BIT),
|
||||||
.frameGen = std::max<size_t>(1, std::stoul(frameGen) - 1)
|
.frameGen = std::max<size_t>(1, std::stoul(frameGen) - 1)
|
||||||
});
|
});
|
||||||
|
|
@ -84,12 +87,16 @@ namespace {
|
||||||
Log::error("Failed to create device info: {}", e.what());
|
Log::error("Failed to create device info: {}", e.what());
|
||||||
return VK_ERROR_INITIALIZATION_FAILED;
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
|
Log::info("lsfg-vk: Created Vulkan device");
|
||||||
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void myvkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
|
void myvkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
|
||||||
devices.erase(device); // erase device info
|
devices.erase(device); // erase device info
|
||||||
vkDestroyDevice(device, pAllocator);
|
|
||||||
|
Log::info("lsfg-vk: Destroyed Vulkan device");
|
||||||
|
Layer::ovkDestroyDevice(device, pAllocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// swapchain hooks
|
// swapchain hooks
|
||||||
|
|
@ -110,7 +117,7 @@ namespace {
|
||||||
createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; // allow copy from/to images
|
createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; // allow copy from/to images
|
||||||
createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||||
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; // force vsync
|
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; // force vsync
|
||||||
auto res = vkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain);
|
auto res = Layer::ovkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain);
|
||||||
if (res != VK_SUCCESS) {
|
if (res != VK_SUCCESS) {
|
||||||
Log::error("Failed to create swapchain: {:x}", static_cast<uint32_t>(res));
|
Log::error("Failed to create swapchain: {:x}", static_cast<uint32_t>(res));
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -119,12 +126,12 @@ namespace {
|
||||||
try {
|
try {
|
||||||
// get swapchain images
|
// get swapchain images
|
||||||
uint32_t imageCount{};
|
uint32_t imageCount{};
|
||||||
res = vkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr);
|
res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr);
|
||||||
if (res != VK_SUCCESS || imageCount == 0)
|
if (res != VK_SUCCESS || imageCount == 0)
|
||||||
throw LSFG::vulkan_error(res, "Failed to get swapchain images count");
|
throw LSFG::vulkan_error(res, "Failed to get swapchain images count");
|
||||||
|
|
||||||
std::vector<VkImage> swapchainImages(imageCount);
|
std::vector<VkImage> swapchainImages(imageCount);
|
||||||
res = vkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, swapchainImages.data());
|
res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, swapchainImages.data());
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
throw LSFG::vulkan_error(res, "Failed to get swapchain images");
|
throw LSFG::vulkan_error(res, "Failed to get swapchain images");
|
||||||
|
|
||||||
|
|
@ -135,7 +142,6 @@ namespace {
|
||||||
));
|
));
|
||||||
|
|
||||||
swapchainToDeviceTable.emplace(*pSwapchain, device);
|
swapchainToDeviceTable.emplace(*pSwapchain, device);
|
||||||
Log::debug("Created swapchain with {} images", imageCount);
|
|
||||||
} catch (const LSFG::vulkan_error& e) {
|
} catch (const LSFG::vulkan_error& e) {
|
||||||
Log::error("Encountered Vulkan error {:x} while creating swapchain: {}",
|
Log::error("Encountered Vulkan error {:x} while creating swapchain: {}",
|
||||||
static_cast<uint32_t>(e.error()), e.what());
|
static_cast<uint32_t>(e.error()), e.what());
|
||||||
|
|
@ -145,6 +151,7 @@ namespace {
|
||||||
return VK_ERROR_INITIALIZATION_FAILED;
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::info("lsfg-vk: Created swapchain with {} images", pCreateInfo->minImageCount);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,41 +184,24 @@ namespace {
|
||||||
const VkAllocationCallbacks* pAllocator) {
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
swapchains.erase(swapchain); // erase swapchain context
|
swapchains.erase(swapchain); // erase swapchain context
|
||||||
swapchainToDeviceTable.erase(swapchain);
|
swapchainToDeviceTable.erase(swapchain);
|
||||||
vkDestroySwapchainKHR(device, swapchain, pAllocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool initialized{false};
|
Log::info("lsfg-vk: Destroyed swapchain");
|
||||||
|
Layer::ovkDestroySwapchainKHR(device, swapchain, pAllocator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hooks::initialize() {
|
std::unordered_map<std::string, PFN_vkVoidFunction> Hooks::hooks = {
|
||||||
if (initialized) {
|
// instance hooks
|
||||||
Log::warn("Vulkan hooks already initialized, did you call it twice?");
|
{"vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(myvkCreateInstance)},
|
||||||
return;
|
{"vkDestroyInstance", reinterpret_cast<PFN_vkVoidFunction>(myvkDestroyInstance)},
|
||||||
}
|
|
||||||
|
|
||||||
// list of hooks to register
|
// device hooks
|
||||||
const std::vector<std::pair<std::string, void*>> hooks = {
|
{"vkCreateDevicePre", reinterpret_cast<PFN_vkVoidFunction>(myvkCreateDevicePre)},
|
||||||
{ "vkCreateInstance", reinterpret_cast<void*>(myvkCreateInstance) },
|
{"vkCreateDevicePost", reinterpret_cast<PFN_vkVoidFunction>(myvkCreateDevicePost)},
|
||||||
{ "vkDestroyInstance", reinterpret_cast<void*>(myvkDestroyInstance) },
|
{"vkDestroyDevice", reinterpret_cast<PFN_vkVoidFunction>(myvkDestroyDevice)},
|
||||||
{ "vkCreateDevice", reinterpret_cast<void*>(myvkCreateDevice) },
|
|
||||||
{ "vkDestroyDevice", reinterpret_cast<void*>(myvkDestroyDevice) },
|
|
||||||
{ "vkCreateSwapchainKHR", reinterpret_cast<void*>(myvkCreateSwapchainKHR) },
|
|
||||||
{ "vkQueuePresentKHR", reinterpret_cast<void*>(myvkQueuePresentKHR) },
|
|
||||||
{ "vkDestroySwapchainKHR", reinterpret_cast<void*>(myvkDestroySwapchainKHR) }
|
|
||||||
};
|
|
||||||
|
|
||||||
// register hooks to Vulkan loader
|
// swapchain hooks
|
||||||
for (const auto& hook : hooks)
|
{"vkCreateSwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(myvkCreateSwapchainKHR)},
|
||||||
Loader::VK::registerSymbol(hook.first, hook.second);
|
{"vkQueuePresentKHR", reinterpret_cast<PFN_vkVoidFunction>(myvkQueuePresentKHR)},
|
||||||
|
{"vkDestroySwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(myvkDestroySwapchainKHR)}
|
||||||
// register hooks to dynamic loader under libvulkan.so.1 and libvulkan.so
|
};
|
||||||
for (const char* libName : {"libvulkan.so.1", "libvulkan.so"}) {
|
|
||||||
Loader::DL::File vkLib(libName);
|
|
||||||
for (const auto& hook : hooks)
|
|
||||||
vkLib.defineSymbol(hook.first, hook.second);
|
|
||||||
Loader::DL::registerFile(vkLib);
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
Log::info("Vulkan hooks initialized successfully");
|
|
||||||
}
|
|
||||||
|
|
|
||||||
27
src/init.cpp
27
src/init.cpp
|
|
@ -1,27 +0,0 @@
|
||||||
#include "loader/dl.hpp"
|
|
||||||
#include "loader/vk.hpp"
|
|
||||||
#include "hooks.hpp"
|
|
||||||
#include "log.hpp"
|
|
||||||
|
|
||||||
extern "C" void __attribute__((constructor)) init();
|
|
||||||
extern "C" [[noreturn]] void __attribute__((destructor)) deinit();
|
|
||||||
|
|
||||||
void init() {
|
|
||||||
Log::info("lsfg-vk: init() called");
|
|
||||||
|
|
||||||
// hook loaders
|
|
||||||
Loader::DL::initialize();
|
|
||||||
Loader::VK::initialize();
|
|
||||||
|
|
||||||
// setup hooks
|
|
||||||
Hooks::initialize();
|
|
||||||
|
|
||||||
Log::info("lsfg-vk: init() completed successfully");
|
|
||||||
}
|
|
||||||
|
|
||||||
void deinit() {
|
|
||||||
Log::debug("lsfg-vk: deinit() called, exiting");
|
|
||||||
// for some reason some applications unload the library despite it containing
|
|
||||||
// the dl functions. this will lead to a segmentation fault, so we exit early.
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
527
src/layer.cpp
Normal file
527
src/layer.cpp
Normal file
|
|
@ -0,0 +1,527 @@
|
||||||
|
#include "layer.hpp"
|
||||||
|
#include "hooks.hpp"
|
||||||
|
#include "utils/log.hpp"
|
||||||
|
|
||||||
|
#include <lsfg.hpp>
|
||||||
|
#include <vulkan/vk_layer.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
PFN_vkCreateInstance next_vkCreateInstance{};
|
||||||
|
PFN_vkDestroyInstance next_vkDestroyInstance{};
|
||||||
|
|
||||||
|
PFN_vkCreateDevice next_vkCreateDevice{};
|
||||||
|
PFN_vkDestroyDevice next_vkDestroyDevice{};
|
||||||
|
|
||||||
|
PFN_vkGetInstanceProcAddr next_vkGetInstanceProcAddr{};
|
||||||
|
PFN_vkGetDeviceProcAddr next_vkGetDeviceProcAddr{};
|
||||||
|
|
||||||
|
PFN_vkCreateSwapchainKHR next_vkCreateSwapchainKHR{};
|
||||||
|
PFN_vkQueuePresentKHR next_vkQueuePresentKHR{};
|
||||||
|
PFN_vkDestroySwapchainKHR next_vkDestroySwapchainKHR{};
|
||||||
|
PFN_vkGetSwapchainImagesKHR next_vkGetSwapchainImagesKHR{};
|
||||||
|
PFN_vkAllocateCommandBuffers next_vkAllocateCommandBuffers{};
|
||||||
|
PFN_vkFreeCommandBuffers next_vkFreeCommandBuffers{};
|
||||||
|
PFN_vkBeginCommandBuffer next_vkBeginCommandBuffer{};
|
||||||
|
PFN_vkEndCommandBuffer next_vkEndCommandBuffer{};
|
||||||
|
PFN_vkCreateCommandPool next_vkCreateCommandPool{};
|
||||||
|
PFN_vkDestroyCommandPool next_vkDestroyCommandPool{};
|
||||||
|
PFN_vkCreateImage next_vkCreateImage{};
|
||||||
|
PFN_vkDestroyImage next_vkDestroyImage{};
|
||||||
|
PFN_vkGetImageMemoryRequirements next_vkGetImageMemoryRequirements{};
|
||||||
|
PFN_vkBindImageMemory next_vkBindImageMemory{};
|
||||||
|
PFN_vkAllocateMemory next_vkAllocateMemory{};
|
||||||
|
PFN_vkFreeMemory next_vkFreeMemory{};
|
||||||
|
PFN_vkCreateSemaphore next_vkCreateSemaphore{};
|
||||||
|
PFN_vkDestroySemaphore next_vkDestroySemaphore{};
|
||||||
|
PFN_vkGetMemoryFdKHR next_vkGetMemoryFdKHR{};
|
||||||
|
PFN_vkGetSemaphoreFdKHR next_vkGetSemaphoreFdKHR{};
|
||||||
|
PFN_vkGetPhysicalDeviceQueueFamilyProperties next_vkGetPhysicalDeviceQueueFamilyProperties{};
|
||||||
|
PFN_vkGetPhysicalDeviceMemoryProperties next_vkGetPhysicalDeviceMemoryProperties{};
|
||||||
|
PFN_vkGetDeviceQueue next_vkGetDeviceQueue{};
|
||||||
|
PFN_vkQueueSubmit next_vkQueueSubmit{};
|
||||||
|
PFN_vkCmdPipelineBarrier next_vkCmdPipelineBarrier{};
|
||||||
|
PFN_vkCmdPipelineBarrier2 next_vkCmdPipelineBarrier2{};
|
||||||
|
PFN_vkCmdCopyImage next_vkCmdCopyImage{};
|
||||||
|
PFN_vkAcquireNextImageKHR next_vkAcquireNextImageKHR{};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
VkResult layer_vkCreateInstance( // NOLINTBEGIN
|
||||||
|
const VkInstanceCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkInstance* pInstance) {
|
||||||
|
Log::debug("lsfg-vk(layer): Initializing lsfg-vk instance layer");
|
||||||
|
|
||||||
|
// find layer creation info
|
||||||
|
auto* layerDesc = const_cast<VkLayerInstanceCreateInfo*>(
|
||||||
|
reinterpret_cast<const VkLayerInstanceCreateInfo*>(pCreateInfo->pNext));
|
||||||
|
while (layerDesc && (layerDesc->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
|
||||||
|
|| layerDesc->function != VK_LAYER_LINK_INFO)) {
|
||||||
|
layerDesc = const_cast<VkLayerInstanceCreateInfo*>(
|
||||||
|
reinterpret_cast<const VkLayerInstanceCreateInfo*>(layerDesc->pNext));
|
||||||
|
}
|
||||||
|
if (!layerDesc) {
|
||||||
|
Log::error("lsfg-vk(layer): No layer creation info found in pNext chain");
|
||||||
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance link info (i don't really know what this does)
|
||||||
|
next_vkGetInstanceProcAddr = layerDesc->u.pLayerInfo->pfnNextGetInstanceProcAddr;
|
||||||
|
layerDesc->u.pLayerInfo = layerDesc->u.pLayerInfo->pNext;
|
||||||
|
|
||||||
|
// create instance
|
||||||
|
next_vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(
|
||||||
|
next_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"));
|
||||||
|
if (!next_vkCreateInstance) {
|
||||||
|
Log::error("lsfg-vk(layer): Failed to get vkCreateInstance function pointer");
|
||||||
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* layer_vkCreateInstance2 = reinterpret_cast<PFN_vkCreateInstance>(
|
||||||
|
Hooks::hooks["vkCreateInstance"]);
|
||||||
|
auto res = layer_vkCreateInstance2(pCreateInfo, pAllocator, pInstance);
|
||||||
|
if (res != VK_SUCCESS) {
|
||||||
|
Log::error("lsfg-vk(layer): Failed to create Vulkan instance: {:x}",
|
||||||
|
static_cast<uint32_t>(res));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get relevant function pointers from the next layer
|
||||||
|
next_vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
|
||||||
|
next_vkGetInstanceProcAddr(*pInstance, "vkDestroyInstance"));
|
||||||
|
next_vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
|
||||||
|
next_vkGetInstanceProcAddr(*pInstance, "vkGetInstanceProcAddr"));
|
||||||
|
next_vkGetPhysicalDeviceQueueFamilyProperties =
|
||||||
|
reinterpret_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(
|
||||||
|
next_vkGetInstanceProcAddr(*pInstance, "vkGetPhysicalDeviceQueueFamilyProperties"));
|
||||||
|
next_vkGetPhysicalDeviceMemoryProperties =
|
||||||
|
reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(
|
||||||
|
next_vkGetInstanceProcAddr(*pInstance, "vkGetPhysicalDeviceMemoryProperties"));
|
||||||
|
if (!next_vkDestroyInstance || !next_vkGetInstanceProcAddr ||
|
||||||
|
!next_vkGetPhysicalDeviceQueueFamilyProperties ||
|
||||||
|
!next_vkGetPhysicalDeviceMemoryProperties) {
|
||||||
|
Log::error("lsfg-vk(layer): Failed to get instance function pointers");
|
||||||
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug("lsfg-vk(layer): Successfully initialized lsfg-vk instance layer");
|
||||||
|
return res;
|
||||||
|
} // NOLINTEND
|
||||||
|
|
||||||
|
VkResult layer_vkCreateDevice( // NOLINTBEGIN
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
const VkDeviceCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkDevice* pDevice) {
|
||||||
|
Log::debug("lsfg-vk(layer): Initializing lsfg-vk device layer");
|
||||||
|
|
||||||
|
// find layer creation info
|
||||||
|
auto* layerDesc = const_cast<VkLayerDeviceCreateInfo*>(
|
||||||
|
reinterpret_cast<const VkLayerDeviceCreateInfo*>(pCreateInfo->pNext));
|
||||||
|
while (layerDesc && (layerDesc->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO
|
||||||
|
|| layerDesc->function != VK_LAYER_LINK_INFO)) {
|
||||||
|
layerDesc = const_cast<VkLayerDeviceCreateInfo*>(
|
||||||
|
reinterpret_cast<const VkLayerDeviceCreateInfo*>(layerDesc->pNext));
|
||||||
|
}
|
||||||
|
if (!layerDesc) {
|
||||||
|
Log::error("lsfg-vk(layer): No layer creation info found in pNext chain");
|
||||||
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance link info (i don't really know what this does)
|
||||||
|
next_vkGetDeviceProcAddr = layerDesc->u.pLayerInfo->pfnNextGetDeviceProcAddr;
|
||||||
|
layerDesc->u.pLayerInfo = layerDesc->u.pLayerInfo->pNext;
|
||||||
|
|
||||||
|
// create device
|
||||||
|
next_vkCreateDevice = reinterpret_cast<PFN_vkCreateDevice>(
|
||||||
|
next_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDevice"));
|
||||||
|
if (!next_vkCreateDevice) {
|
||||||
|
Log::error("lsfg-vk(layer): Failed to get vkCreateDevice function pointer");
|
||||||
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* layer_vkCreateDevice2 = reinterpret_cast<PFN_vkCreateDevice>(
|
||||||
|
Hooks::hooks["vkCreateDevicePre"]);
|
||||||
|
auto res = layer_vkCreateDevice2(physicalDevice, pCreateInfo, pAllocator, pDevice);
|
||||||
|
if (res != VK_SUCCESS) {
|
||||||
|
Log::error("lsfg-vk(layer): Failed to create Vulkan device: {:x}",
|
||||||
|
static_cast<uint32_t>(res));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get relevant function pointers from the next layer
|
||||||
|
next_vkDestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkDestroyDevice"));
|
||||||
|
next_vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkCreateSwapchainKHR"));
|
||||||
|
next_vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkQueuePresentKHR"));
|
||||||
|
next_vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkDestroySwapchainKHR"));
|
||||||
|
next_vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkGetSwapchainImagesKHR"));
|
||||||
|
next_vkAllocateCommandBuffers = reinterpret_cast<PFN_vkAllocateCommandBuffers>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkAllocateCommandBuffers"));
|
||||||
|
next_vkFreeCommandBuffers = reinterpret_cast<PFN_vkFreeCommandBuffers>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkFreeCommandBuffers"));
|
||||||
|
next_vkBeginCommandBuffer = reinterpret_cast<PFN_vkBeginCommandBuffer>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkBeginCommandBuffer"));
|
||||||
|
next_vkEndCommandBuffer = reinterpret_cast<PFN_vkEndCommandBuffer>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkEndCommandBuffer"));
|
||||||
|
next_vkCreateCommandPool = reinterpret_cast<PFN_vkCreateCommandPool>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkCreateCommandPool"));
|
||||||
|
next_vkDestroyCommandPool = reinterpret_cast<PFN_vkDestroyCommandPool>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkDestroyCommandPool"));
|
||||||
|
next_vkCreateImage = reinterpret_cast<PFN_vkCreateImage>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkCreateImage"));
|
||||||
|
next_vkDestroyImage = reinterpret_cast<PFN_vkDestroyImage>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkDestroyImage"));
|
||||||
|
next_vkGetImageMemoryRequirements = reinterpret_cast<PFN_vkGetImageMemoryRequirements>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkGetImageMemoryRequirements"));
|
||||||
|
next_vkBindImageMemory = reinterpret_cast<PFN_vkBindImageMemory>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkBindImageMemory"));
|
||||||
|
next_vkGetMemoryFdKHR = reinterpret_cast<PFN_vkGetMemoryFdKHR>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkGetMemoryFdKHR"));
|
||||||
|
next_vkAllocateMemory = reinterpret_cast<PFN_vkAllocateMemory>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkAllocateMemory"));
|
||||||
|
next_vkFreeMemory = reinterpret_cast<PFN_vkFreeMemory>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkFreeMemory"));
|
||||||
|
next_vkCreateSemaphore = reinterpret_cast<PFN_vkCreateSemaphore>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkCreateSemaphore"));
|
||||||
|
next_vkDestroySemaphore = reinterpret_cast<PFN_vkDestroySemaphore>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkDestroySemaphore"));
|
||||||
|
next_vkGetSemaphoreFdKHR = reinterpret_cast<PFN_vkGetSemaphoreFdKHR>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkGetSemaphoreFdKHR"));
|
||||||
|
next_vkGetDeviceQueue = reinterpret_cast<PFN_vkGetDeviceQueue>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkGetDeviceQueue"));
|
||||||
|
next_vkQueueSubmit = reinterpret_cast<PFN_vkQueueSubmit>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkQueueSubmit"));
|
||||||
|
next_vkCmdPipelineBarrier = reinterpret_cast<PFN_vkCmdPipelineBarrier>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkCmdPipelineBarrier"));
|
||||||
|
next_vkCmdPipelineBarrier2 = reinterpret_cast<PFN_vkCmdPipelineBarrier2>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkCmdPipelineBarrier2"));
|
||||||
|
next_vkCmdCopyImage = reinterpret_cast<PFN_vkCmdCopyImage>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkCmdCopyImage"));
|
||||||
|
next_vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(
|
||||||
|
next_vkGetDeviceProcAddr(*pDevice, "vkAcquireNextImageKHR"));
|
||||||
|
if (!next_vkDestroyDevice || !next_vkCreateSwapchainKHR ||
|
||||||
|
!next_vkQueuePresentKHR || !next_vkDestroySwapchainKHR ||
|
||||||
|
!next_vkGetSwapchainImagesKHR || !next_vkAllocateCommandBuffers ||
|
||||||
|
!next_vkFreeCommandBuffers || !next_vkBeginCommandBuffer ||
|
||||||
|
!next_vkEndCommandBuffer || !next_vkCreateCommandPool ||
|
||||||
|
!next_vkDestroyCommandPool || !next_vkCreateImage ||
|
||||||
|
!next_vkDestroyImage || !next_vkGetImageMemoryRequirements ||
|
||||||
|
!next_vkBindImageMemory || !next_vkGetMemoryFdKHR ||
|
||||||
|
!next_vkAllocateMemory || !next_vkFreeMemory ||
|
||||||
|
!next_vkCreateSemaphore || !next_vkDestroySemaphore ||
|
||||||
|
!next_vkGetSemaphoreFdKHR || !next_vkGetDeviceQueue ||
|
||||||
|
!next_vkQueueSubmit || !next_vkCmdPipelineBarrier ||
|
||||||
|
!next_vkCmdPipelineBarrier2 || !next_vkCmdCopyImage ||
|
||||||
|
!next_vkAcquireNextImageKHR) {
|
||||||
|
Log::error("lsfg-vk(layer): Failed to get device function pointers");
|
||||||
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer_vkCreateDevice2 = reinterpret_cast<PFN_vkCreateDevice>(
|
||||||
|
Hooks::hooks["vkCreateDevicePost"]);
|
||||||
|
res = layer_vkCreateDevice2(physicalDevice, pCreateInfo, pAllocator, pDevice);
|
||||||
|
if (res != VK_SUCCESS) {
|
||||||
|
Log::error("lsfg-vk(layer): Failed to create Vulkan device: {:x}",
|
||||||
|
static_cast<uint32_t>(res));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug("lsfg-vk(layer): Successfully initialized lsfg-vk device layer");
|
||||||
|
return res;
|
||||||
|
} // NOLINTEND
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, PFN_vkVoidFunction> layerFunctions = {
|
||||||
|
{ "vkCreateInstance",
|
||||||
|
reinterpret_cast<PFN_vkVoidFunction>(&layer_vkCreateInstance) },
|
||||||
|
{ "vkGetInstanceProcAddr",
|
||||||
|
reinterpret_cast<PFN_vkVoidFunction>(&layer_vkGetInstanceProcAddr) },
|
||||||
|
{ "vkGetDeviceProcAddr",
|
||||||
|
reinterpret_cast<PFN_vkVoidFunction>(&layer_vkGetDeviceProcAddr) },
|
||||||
|
{ "vkCreateDevice",
|
||||||
|
reinterpret_cast<PFN_vkVoidFunction>(&layer_vkCreateDevice) },
|
||||||
|
};
|
||||||
|
|
||||||
|
PFN_vkVoidFunction layer_vkGetInstanceProcAddr(VkInstance instance, const char* pName) {
|
||||||
|
std::string name(pName);
|
||||||
|
auto it = layerFunctions.find(name);
|
||||||
|
if (it != layerFunctions.end()) {
|
||||||
|
Log::debug("lsfg-vk(layer): Inserted layer function for {}", name);
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = Hooks::hooks.find(name);
|
||||||
|
if (it != Hooks::hooks.end()) {
|
||||||
|
Log::debug("lsfg-vk(layer): Inserted hook function for {}", name);
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next_vkGetInstanceProcAddr(instance, pName);
|
||||||
|
}
|
||||||
|
|
||||||
|
PFN_vkVoidFunction layer_vkGetDeviceProcAddr(VkDevice device, const char* pName) {
|
||||||
|
std::string name(pName);
|
||||||
|
auto it = layerFunctions.find(name);
|
||||||
|
if (it != layerFunctions.end()) {
|
||||||
|
Log::debug("lsfg-vk(layer): Inserted layer function for {}", name);
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = Hooks::hooks.find(name);
|
||||||
|
if (it != Hooks::hooks.end()) {
|
||||||
|
Log::debug("lsfg-vk(layer): Inserted hook function for {}", name);
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next_vkGetDeviceProcAddr(device, pName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// original functions
|
||||||
|
|
||||||
|
VkResult Layer::ovkCreateInstance(
|
||||||
|
const VkInstanceCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkInstance* pInstance) {
|
||||||
|
return next_vkCreateInstance(pCreateInfo, pAllocator, pInstance);
|
||||||
|
}
|
||||||
|
void Layer::ovkDestroyInstance(
|
||||||
|
VkInstance instance,
|
||||||
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
|
next_vkDestroyInstance(instance, pAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkCreateDevice(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
const VkDeviceCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkDevice* pDevice) {
|
||||||
|
return next_vkCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice);
|
||||||
|
}
|
||||||
|
void Layer::ovkDestroyDevice(
|
||||||
|
VkDevice device,
|
||||||
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
|
next_vkDestroyDevice(device, pAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
PFN_vkVoidFunction Layer::ovkGetInstanceProcAddr(
|
||||||
|
VkInstance instance,
|
||||||
|
const char* pName) {
|
||||||
|
return next_vkGetInstanceProcAddr(instance, pName);
|
||||||
|
}
|
||||||
|
PFN_vkVoidFunction Layer::ovkGetDeviceProcAddr(
|
||||||
|
VkDevice device,
|
||||||
|
const char* pName) {
|
||||||
|
return next_vkGetDeviceProcAddr(device, pName);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkCreateSwapchainKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkSwapchainCreateInfoKHR* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSwapchainKHR* pSwapchain) {
|
||||||
|
return next_vkCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
|
||||||
|
}
|
||||||
|
VkResult Layer::ovkQueuePresentKHR(
|
||||||
|
VkQueue queue,
|
||||||
|
const VkPresentInfoKHR* pPresentInfo) {
|
||||||
|
return next_vkQueuePresentKHR(queue, pPresentInfo);
|
||||||
|
}
|
||||||
|
void Layer::ovkDestroySwapchainKHR(
|
||||||
|
VkDevice device,
|
||||||
|
VkSwapchainKHR swapchain,
|
||||||
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
|
next_vkDestroySwapchainKHR(device, swapchain, pAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkGetSwapchainImagesKHR(
|
||||||
|
VkDevice device,
|
||||||
|
VkSwapchainKHR swapchain,
|
||||||
|
uint32_t* pSwapchainImageCount,
|
||||||
|
VkImage* pSwapchainImages) {
|
||||||
|
return next_vkGetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkAllocateCommandBuffers(
|
||||||
|
VkDevice device,
|
||||||
|
const VkCommandBufferAllocateInfo* pAllocateInfo,
|
||||||
|
VkCommandBuffer* pCommandBuffers) {
|
||||||
|
return next_vkAllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers);
|
||||||
|
}
|
||||||
|
void Layer::ovkFreeCommandBuffers(
|
||||||
|
VkDevice device,
|
||||||
|
VkCommandPool commandPool,
|
||||||
|
uint32_t commandBufferCount,
|
||||||
|
const VkCommandBuffer* pCommandBuffers) {
|
||||||
|
next_vkFreeCommandBuffers(device, commandPool, commandBufferCount, pCommandBuffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkBeginCommandBuffer(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
const VkCommandBufferBeginInfo* pBeginInfo) {
|
||||||
|
return next_vkBeginCommandBuffer(commandBuffer, pBeginInfo);
|
||||||
|
}
|
||||||
|
VkResult Layer::ovkEndCommandBuffer(
|
||||||
|
VkCommandBuffer commandBuffer) {
|
||||||
|
return next_vkEndCommandBuffer(commandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkCreateCommandPool(
|
||||||
|
VkDevice device,
|
||||||
|
const VkCommandPoolCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkCommandPool* pCommandPool) {
|
||||||
|
return next_vkCreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);
|
||||||
|
}
|
||||||
|
void Layer::ovkDestroyCommandPool(
|
||||||
|
VkDevice device,
|
||||||
|
VkCommandPool commandPool,
|
||||||
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
|
next_vkDestroyCommandPool(device, commandPool, pAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkCreateImage(
|
||||||
|
VkDevice device,
|
||||||
|
const VkImageCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkImage* pImage) {
|
||||||
|
return next_vkCreateImage(device, pCreateInfo, pAllocator, pImage);
|
||||||
|
}
|
||||||
|
void Layer::ovkDestroyImage(
|
||||||
|
VkDevice device,
|
||||||
|
VkImage image,
|
||||||
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
|
next_vkDestroyImage(device, image, pAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Layer::ovkGetImageMemoryRequirements(
|
||||||
|
VkDevice device,
|
||||||
|
VkImage image,
|
||||||
|
VkMemoryRequirements* pMemoryRequirements) {
|
||||||
|
next_vkGetImageMemoryRequirements(device, image, pMemoryRequirements);
|
||||||
|
}
|
||||||
|
VkResult Layer::ovkBindImageMemory(
|
||||||
|
VkDevice device,
|
||||||
|
VkImage image,
|
||||||
|
VkDeviceMemory memory,
|
||||||
|
VkDeviceSize memoryOffset) {
|
||||||
|
return next_vkBindImageMemory(device, image, memory, memoryOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkAllocateMemory(
|
||||||
|
VkDevice device,
|
||||||
|
const VkMemoryAllocateInfo* pAllocateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkDeviceMemory* pMemory) {
|
||||||
|
return next_vkAllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
|
||||||
|
}
|
||||||
|
void Layer::ovkFreeMemory(
|
||||||
|
VkDevice device,
|
||||||
|
VkDeviceMemory memory,
|
||||||
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
|
next_vkFreeMemory(device, memory, pAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkCreateSemaphore(
|
||||||
|
VkDevice device,
|
||||||
|
const VkSemaphoreCreateInfo* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkSemaphore* pSemaphore) {
|
||||||
|
return next_vkCreateSemaphore(device, pCreateInfo, pAllocator, pSemaphore);
|
||||||
|
}
|
||||||
|
void Layer::ovkDestroySemaphore(
|
||||||
|
VkDevice device,
|
||||||
|
VkSemaphore semaphore,
|
||||||
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
|
next_vkDestroySemaphore(device, semaphore, pAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkGetMemoryFdKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkMemoryGetFdInfoKHR* pGetFdInfo,
|
||||||
|
int* pFd) {
|
||||||
|
return next_vkGetMemoryFdKHR(device, pGetFdInfo, pFd);
|
||||||
|
}
|
||||||
|
VkResult Layer::ovkGetSemaphoreFdKHR(
|
||||||
|
VkDevice device,
|
||||||
|
const VkSemaphoreGetFdInfoKHR* pGetFdInfo,
|
||||||
|
int* pFd) {
|
||||||
|
return next_vkGetSemaphoreFdKHR(device, pGetFdInfo, pFd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Layer::ovkGetPhysicalDeviceQueueFamilyProperties(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
uint32_t* pQueueFamilyPropertyCount,
|
||||||
|
VkQueueFamilyProperties* pQueueFamilyProperties) {
|
||||||
|
next_vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
|
||||||
|
}
|
||||||
|
void Layer::ovkGetPhysicalDeviceMemoryProperties(
|
||||||
|
VkPhysicalDevice physicalDevice,
|
||||||
|
VkPhysicalDeviceMemoryProperties* pMemoryProperties) {
|
||||||
|
next_vkGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Layer::ovkGetDeviceQueue(
|
||||||
|
VkDevice device,
|
||||||
|
uint32_t queueFamilyIndex,
|
||||||
|
uint32_t queueIndex,
|
||||||
|
VkQueue* pQueue) {
|
||||||
|
next_vkGetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
|
||||||
|
}
|
||||||
|
VkResult Layer::ovkQueueSubmit(
|
||||||
|
VkQueue queue,
|
||||||
|
uint32_t submitCount,
|
||||||
|
const VkSubmitInfo* pSubmits,
|
||||||
|
VkFence fence) {
|
||||||
|
return next_vkQueueSubmit(queue, submitCount, pSubmits, fence);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Layer::ovkCmdPipelineBarrier(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
VkPipelineStageFlags srcStageMask,
|
||||||
|
VkPipelineStageFlags dstStageMask,
|
||||||
|
VkDependencyFlags dependencyFlags,
|
||||||
|
uint32_t memoryBarrierCount,
|
||||||
|
const VkMemoryBarrier* pMemoryBarriers,
|
||||||
|
uint32_t bufferMemoryBarrierCount,
|
||||||
|
const VkBufferMemoryBarrier* pBufferMemoryBarriers,
|
||||||
|
uint32_t imageMemoryBarrierCount,
|
||||||
|
const VkImageMemoryBarrier* pImageMemoryBarriers) {
|
||||||
|
next_vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags,
|
||||||
|
memoryBarrierCount, pMemoryBarriers,
|
||||||
|
bufferMemoryBarrierCount, pBufferMemoryBarriers,
|
||||||
|
imageMemoryBarrierCount, pImageMemoryBarriers);
|
||||||
|
}
|
||||||
|
void Layer::ovkCmdPipelineBarrier2(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
const VkDependencyInfo* pDependencyInfo) {
|
||||||
|
next_vkCmdPipelineBarrier2(commandBuffer, pDependencyInfo);
|
||||||
|
}
|
||||||
|
void Layer::ovkCmdCopyImage(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
VkImage srcImage,
|
||||||
|
VkImageLayout srcImageLayout,
|
||||||
|
VkImage dstImage,
|
||||||
|
VkImageLayout dstImageLayout,
|
||||||
|
uint32_t regionCount,
|
||||||
|
const VkImageCopy* pRegions) {
|
||||||
|
next_vkCmdCopyImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Layer::ovkAcquireNextImageKHR(
|
||||||
|
VkDevice device,
|
||||||
|
VkSwapchainKHR swapchain,
|
||||||
|
uint64_t timeout,
|
||||||
|
VkSemaphore semaphore,
|
||||||
|
VkFence fence,
|
||||||
|
uint32_t* pImageIndex) {
|
||||||
|
return next_vkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex);
|
||||||
|
}
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
#include "loader/dl.hpp"
|
|
||||||
#include "log.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace Loader;
|
|
||||||
|
|
||||||
using dlopen_t = void* (*)(const char*, int);
|
|
||||||
using dlsym_t = void* (*)(void*, const char*);
|
|
||||||
using dlclose_t = int (*)(void*);
|
|
||||||
|
|
||||||
// glibc exclusive function to get versioned symbols
|
|
||||||
extern "C" void* dlvsym(long, const char*, const char*);
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
// original function pointers
|
|
||||||
dlopen_t dlopen_ptr;
|
|
||||||
dlsym_t dlsym_ptr;
|
|
||||||
dlclose_t dlclose_ptr;
|
|
||||||
|
|
||||||
// map of all registered overrides
|
|
||||||
auto& overrides() {
|
|
||||||
// this has to be a function rather than a static variable
|
|
||||||
// because of weird initialization order issues.
|
|
||||||
static std::unordered_map<std::string, DL::File> overrides;
|
|
||||||
return overrides;
|
|
||||||
}
|
|
||||||
|
|
||||||
// vector of loaded handles
|
|
||||||
auto& handles() {
|
|
||||||
static std::vector<void*> handles;
|
|
||||||
return handles;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool enable_hooks{true};
|
|
||||||
}
|
|
||||||
|
|
||||||
void DL::initialize() {
|
|
||||||
if (dlopen_ptr || dlsym_ptr || dlclose_ptr) {
|
|
||||||
Log::warn("lsfg-vk(dl): Dynamic loader already initialized, did you call it twice?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dlopen_ptr = reinterpret_cast<dlopen_t> (dlvsym(-1, "dlopen", "GLIBC_2.2.5"));
|
|
||||||
dlsym_ptr = reinterpret_cast<dlsym_t> (dlvsym(-1, "dlsym", "GLIBC_2.2.5"));
|
|
||||||
dlclose_ptr = reinterpret_cast<dlclose_t>(dlvsym(-1, "dlclose", "GLIBC_2.2.5"));
|
|
||||||
if (!dlopen_ptr || !dlsym_ptr || !dlclose_ptr) {
|
|
||||||
Log::error("lsfg-vk(dl): Failed to initialize dynamic loader, missing symbols");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::debug("lsfg-vk(dl): Initialized dynamic loader with original functions");
|
|
||||||
}
|
|
||||||
|
|
||||||
void DL::registerFile(const File& file) {
|
|
||||||
auto& files = overrides();
|
|
||||||
|
|
||||||
auto it = files.find(file.getFilename());
|
|
||||||
if (it == files.end()) {
|
|
||||||
// simply register if the file hasn't been registered yet
|
|
||||||
files.emplace(file.getFilename(), file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// merge the new file's symbols into the previously registered one
|
|
||||||
auto& existing_file = it->second;
|
|
||||||
for (const auto& [symbol, func] : file.getSymbols())
|
|
||||||
if (existing_file.findSymbol(symbol) == nullptr)
|
|
||||||
existing_file.defineSymbol(symbol, func);
|
|
||||||
else
|
|
||||||
Log::warn("lsfg-vk(dl): Tried registering symbol {}::{}, but it is already defined",
|
|
||||||
existing_file.getFilename(), symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DL::disableHooks() { enable_hooks = false; }
|
|
||||||
void DL::enableHooks() { enable_hooks = true; }
|
|
||||||
|
|
||||||
void* dlopen(const char* filename, int flag) noexcept {
|
|
||||||
auto& files = overrides();
|
|
||||||
auto& loaded = handles();
|
|
||||||
|
|
||||||
// ALWAYS load the library and ensure it's tracked
|
|
||||||
auto* handle = dlopen_ptr(filename, flag);
|
|
||||||
if (handle && std::ranges::find(loaded, handle) == loaded.end())
|
|
||||||
loaded.push_back(handle);
|
|
||||||
|
|
||||||
// no need to check for overrides if hooks are disabled
|
|
||||||
if (!enable_hooks || !filename)
|
|
||||||
return handle;
|
|
||||||
|
|
||||||
// try to find an override for this filename
|
|
||||||
const std::string filename_str(filename);
|
|
||||||
auto it = files.find(filename_str);
|
|
||||||
if (it == files.end())
|
|
||||||
return handle;
|
|
||||||
|
|
||||||
auto& file = it->second;
|
|
||||||
file.setOriginalHandle(handle);
|
|
||||||
file.setHandle(reinterpret_cast<void*>(&file));
|
|
||||||
|
|
||||||
Log::debug("lsfg-vk(dl): Intercepted module load for {}", file.getFilename());
|
|
||||||
return file.getHandle();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* dlsym(void* handle, const char* symbol) noexcept {
|
|
||||||
const auto& files = overrides();
|
|
||||||
|
|
||||||
if (!enable_hooks || !handle || !symbol)
|
|
||||||
return dlsym_ptr(handle, symbol);
|
|
||||||
|
|
||||||
// see if handle is a fake one
|
|
||||||
const auto it = std::ranges::find_if(files, [handle](const auto& pair) {
|
|
||||||
return pair.second.getHandle() == handle;
|
|
||||||
});
|
|
||||||
if (it == files.end())
|
|
||||||
return dlsym_ptr(handle, symbol);
|
|
||||||
const auto& file = it->second;
|
|
||||||
|
|
||||||
// find a symbol override
|
|
||||||
const std::string symbol_str(symbol);
|
|
||||||
auto* func = file.findSymbol(symbol_str);
|
|
||||||
if (func == nullptr)
|
|
||||||
return dlsym_ptr(file.getOriginalHandle(), symbol);
|
|
||||||
|
|
||||||
Log::debug("lsfg-vk(dl): Intercepted symbol {}::{}", file.getFilename(), symbol_str);
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dlclose(void* handle) noexcept {
|
|
||||||
auto& files = overrides();
|
|
||||||
auto& loaded = handles();
|
|
||||||
|
|
||||||
// no handle, let the original dlclose handle it
|
|
||||||
if (!handle)
|
|
||||||
return dlclose_ptr(handle);
|
|
||||||
|
|
||||||
// see if the handle is a fake one
|
|
||||||
auto it = std::ranges::find_if(files, [handle](const auto& pair) {
|
|
||||||
return pair.second.getHandle() == handle;
|
|
||||||
});
|
|
||||||
if (it == files.end()) {
|
|
||||||
// if the handle is not fake, check if it's still loaded.
|
|
||||||
// this is necessary to avoid double closing when
|
|
||||||
// one handle was acquired while hooks were disabled
|
|
||||||
auto l_it = std::ranges::find(loaded, handle);
|
|
||||||
if (l_it == loaded.end())
|
|
||||||
return 0;
|
|
||||||
loaded.erase(l_it);
|
|
||||||
return dlclose_ptr(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& file = it->second;
|
|
||||||
handle = file.getOriginalHandle();
|
|
||||||
file.setHandle(nullptr);
|
|
||||||
file.setOriginalHandle(nullptr);
|
|
||||||
|
|
||||||
// similarly, if it is fake, check if it's still loaded
|
|
||||||
// before unloading it again.
|
|
||||||
auto l_it = std::ranges::find(loaded, handle);
|
|
||||||
if (l_it == loaded.end()) {
|
|
||||||
Log::debug("lsfg-vk(dl): Skipping unload for {} (already unloaded)", file.getFilename());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
loaded.erase(l_it);
|
|
||||||
|
|
||||||
Log::debug("lsfg-vk(dl): Unloaded {}", file.getFilename());
|
|
||||||
return dlclose_ptr(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// original function calls
|
|
||||||
|
|
||||||
void* DL::odlopen(const char* filename, int flag) {
|
|
||||||
return dlopen_ptr(filename, flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* DL::odlsym(void* handle, const char* symbol) {
|
|
||||||
return dlsym_ptr(handle, symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
int DL::odlclose(void* handle) {
|
|
||||||
return dlclose_ptr(handle);
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
#include "loader/vk.hpp"
|
|
||||||
#include "loader/dl.hpp"
|
|
||||||
#include "log.hpp"
|
|
||||||
|
|
||||||
using namespace Loader;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
// original function pointers
|
|
||||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr_ptr{};
|
|
||||||
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr_ptr{};
|
|
||||||
|
|
||||||
// map of all overridden symbols
|
|
||||||
auto& symbols() {
|
|
||||||
static std::unordered_map<std::string, void*> symbols;
|
|
||||||
return symbols;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VK::initialize() {
|
|
||||||
if (vkGetInstanceProcAddr_ptr || vkGetDeviceProcAddr_ptr) {
|
|
||||||
Log::warn("lsfg-vk(vk): Vulkan loader already initialized, did you call it twice?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get original function pointers
|
|
||||||
auto* handle = DL::odlopen("libvulkan.so.1", 0x2);
|
|
||||||
vkGetInstanceProcAddr_ptr =
|
|
||||||
reinterpret_cast<PFN_vkGetInstanceProcAddr>(DL::odlsym(handle, "vkGetInstanceProcAddr"));
|
|
||||||
vkGetDeviceProcAddr_ptr =
|
|
||||||
reinterpret_cast<PFN_vkGetDeviceProcAddr> (DL::odlsym(handle, "vkGetDeviceProcAddr"));
|
|
||||||
if (!vkGetInstanceProcAddr_ptr || !vkGetDeviceProcAddr_ptr) {
|
|
||||||
Log::error("lsfg-vk(vk): Failed to initialize Vulkan loader, missing symbols");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// register dynamic loader overrides
|
|
||||||
DL::File vulkanLib{"libvulkan.so.1"};
|
|
||||||
vulkanLib.defineSymbol("vkGetInstanceProcAddr",
|
|
||||||
reinterpret_cast<void*>(myvkGetInstanceProcAddr));
|
|
||||||
vulkanLib.defineSymbol("vkGetDeviceProcAddr",
|
|
||||||
reinterpret_cast<void*>(myvkGetDeviceProcAddr));
|
|
||||||
DL::registerFile(vulkanLib);
|
|
||||||
|
|
||||||
DL::File vulkanLib2{"libvulkan.so"};
|
|
||||||
vulkanLib2.defineSymbol("vkGetInstanceProcAddr",
|
|
||||||
reinterpret_cast<void*>(myvkGetInstanceProcAddr));
|
|
||||||
vulkanLib2.defineSymbol("vkGetDeviceProcAddr",
|
|
||||||
reinterpret_cast<void*>(myvkGetDeviceProcAddr));
|
|
||||||
DL::registerFile(vulkanLib2);
|
|
||||||
|
|
||||||
// register vulkan loader overrides
|
|
||||||
VK::registerSymbol("vkGetInstanceProcAddr", reinterpret_cast<void*>(myvkGetInstanceProcAddr));
|
|
||||||
VK::registerSymbol("vkGetDeviceProcAddr", reinterpret_cast<void*>(myvkGetDeviceProcAddr));
|
|
||||||
|
|
||||||
Log::debug("lsfg-vk(vk): Initialized Vulkan loader with original functions");
|
|
||||||
}
|
|
||||||
|
|
||||||
void VK::registerSymbol(const std::string& symbol, void* address) {
|
|
||||||
auto& syms = symbols();
|
|
||||||
|
|
||||||
const auto it = syms.find(symbol);
|
|
||||||
if (it != syms.end()) {
|
|
||||||
Log::warn("lsfg-vk(vk): Tried registering symbol {}, but it is already defined", symbol);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
syms.emplace(symbol, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
PFN_vkVoidFunction myvkGetInstanceProcAddr(VkInstance instance, const char* pName) {
|
|
||||||
const auto& syms = symbols();
|
|
||||||
|
|
||||||
if (!pName)
|
|
||||||
return vkGetInstanceProcAddr_ptr(instance, pName);
|
|
||||||
|
|
||||||
// try to find an override
|
|
||||||
const std::string pName_str(pName);
|
|
||||||
const auto it = syms.find(pName_str);
|
|
||||||
if (it == syms.end())
|
|
||||||
return vkGetInstanceProcAddr_ptr(instance, pName);
|
|
||||||
|
|
||||||
Log::debug("lsfg-vk(vk): Intercepted Vulkan symbol {}", pName_str);
|
|
||||||
return reinterpret_cast<PFN_vkVoidFunction>(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
PFN_vkVoidFunction myvkGetDeviceProcAddr(VkDevice device, const char* pName) {
|
|
||||||
const auto& syms = symbols();
|
|
||||||
|
|
||||||
if (!pName)
|
|
||||||
return vkGetDeviceProcAddr_ptr(device, pName);
|
|
||||||
|
|
||||||
const std::string pName_str(pName);
|
|
||||||
auto it = syms.find(pName_str);
|
|
||||||
if (it == syms.end())
|
|
||||||
return vkGetDeviceProcAddr_ptr(device, pName);
|
|
||||||
|
|
||||||
Log::debug("lsfg-vk(vk): Intercepted Vulkan symbol {}", pName_str);
|
|
||||||
return reinterpret_cast<PFN_vkVoidFunction>(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
// original function calls
|
|
||||||
|
|
||||||
PFN_vkVoidFunction VK::ovkGetInstanceProcAddr(VkInstance instance, const char* pName) {
|
|
||||||
return vkGetInstanceProcAddr_ptr(instance, pName);
|
|
||||||
}
|
|
||||||
|
|
||||||
PFN_vkVoidFunction VK::ovkGetDeviceProcAddr(VkDevice device, const char* pName) {
|
|
||||||
return vkGetDeviceProcAddr_ptr(device, pName);
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "mini/commandbuffer.hpp"
|
#include "mini/commandbuffer.hpp"
|
||||||
|
#include "layer.hpp"
|
||||||
|
|
||||||
#include <lsfg.hpp>
|
#include <lsfg.hpp>
|
||||||
|
|
||||||
|
|
@ -13,7 +14,7 @@ CommandBuffer::CommandBuffer(VkDevice device, const CommandPool& pool) {
|
||||||
.commandBufferCount = 1
|
.commandBufferCount = 1
|
||||||
};
|
};
|
||||||
VkCommandBuffer commandBufferHandle{};
|
VkCommandBuffer commandBufferHandle{};
|
||||||
auto res = vkAllocateCommandBuffers(device, &desc, &commandBufferHandle);
|
auto res = Layer::ovkAllocateCommandBuffers(device, &desc, &commandBufferHandle);
|
||||||
if (res != VK_SUCCESS || commandBufferHandle == VK_NULL_HANDLE)
|
if (res != VK_SUCCESS || commandBufferHandle == VK_NULL_HANDLE)
|
||||||
throw LSFG::vulkan_error(res, "Unable to allocate command buffer");
|
throw LSFG::vulkan_error(res, "Unable to allocate command buffer");
|
||||||
|
|
||||||
|
|
@ -22,7 +23,7 @@ CommandBuffer::CommandBuffer(VkDevice device, const CommandPool& pool) {
|
||||||
this->commandBuffer = std::shared_ptr<VkCommandBuffer>(
|
this->commandBuffer = std::shared_ptr<VkCommandBuffer>(
|
||||||
new VkCommandBuffer(commandBufferHandle),
|
new VkCommandBuffer(commandBufferHandle),
|
||||||
[dev = device, pool = pool.handle()](VkCommandBuffer* cmdBuffer) {
|
[dev = device, pool = pool.handle()](VkCommandBuffer* cmdBuffer) {
|
||||||
vkFreeCommandBuffers(dev, pool, 1, cmdBuffer);
|
Layer::ovkFreeCommandBuffers(dev, pool, 1, cmdBuffer);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +36,7 @@ void CommandBuffer::begin() {
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
||||||
};
|
};
|
||||||
auto res = vkBeginCommandBuffer(*this->commandBuffer, &beginInfo);
|
auto res = Layer::ovkBeginCommandBuffer(*this->commandBuffer, &beginInfo);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
throw LSFG::vulkan_error(res, "Unable to begin command buffer");
|
throw LSFG::vulkan_error(res, "Unable to begin command buffer");
|
||||||
|
|
||||||
|
|
@ -46,7 +47,7 @@ void CommandBuffer::end() {
|
||||||
if (*this->state != CommandBufferState::Recording)
|
if (*this->state != CommandBufferState::Recording)
|
||||||
throw std::logic_error("Command buffer is not in Recording state");
|
throw std::logic_error("Command buffer is not in Recording state");
|
||||||
|
|
||||||
auto res = vkEndCommandBuffer(*this->commandBuffer);
|
auto res = Layer::ovkEndCommandBuffer(*this->commandBuffer);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
throw LSFG::vulkan_error(res, "Unable to end command buffer");
|
throw LSFG::vulkan_error(res, "Unable to end command buffer");
|
||||||
|
|
||||||
|
|
@ -72,7 +73,7 @@ void CommandBuffer::submit(VkQueue queue,
|
||||||
.signalSemaphoreCount = static_cast<uint32_t>(signalSemaphores.size()),
|
.signalSemaphoreCount = static_cast<uint32_t>(signalSemaphores.size()),
|
||||||
.pSignalSemaphores = signalSemaphores.data()
|
.pSignalSemaphores = signalSemaphores.data()
|
||||||
};
|
};
|
||||||
auto res = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
auto res = Layer::ovkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
throw LSFG::vulkan_error(res, "Unable to submit command buffer");
|
throw LSFG::vulkan_error(res, "Unable to submit command buffer");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "mini/commandpool.hpp"
|
#include "mini/commandpool.hpp"
|
||||||
|
#include "layer.hpp"
|
||||||
|
|
||||||
#include <lsfg.hpp>
|
#include <lsfg.hpp>
|
||||||
|
|
||||||
|
|
@ -11,7 +12,7 @@ CommandPool::CommandPool(VkDevice device, uint32_t graphicsFamilyIdx) {
|
||||||
.queueFamilyIndex = graphicsFamilyIdx
|
.queueFamilyIndex = graphicsFamilyIdx
|
||||||
};
|
};
|
||||||
VkCommandPool commandPoolHandle{};
|
VkCommandPool commandPoolHandle{};
|
||||||
auto res = vkCreateCommandPool(device, &desc, nullptr, &commandPoolHandle);
|
auto res = Layer::ovkCreateCommandPool(device, &desc, nullptr, &commandPoolHandle);
|
||||||
if (res != VK_SUCCESS || commandPoolHandle == VK_NULL_HANDLE)
|
if (res != VK_SUCCESS || commandPoolHandle == VK_NULL_HANDLE)
|
||||||
throw LSFG::vulkan_error(res, "Unable to create command pool");
|
throw LSFG::vulkan_error(res, "Unable to create command pool");
|
||||||
|
|
||||||
|
|
@ -19,7 +20,7 @@ CommandPool::CommandPool(VkDevice device, uint32_t graphicsFamilyIdx) {
|
||||||
this->commandPool = std::shared_ptr<VkCommandPool>(
|
this->commandPool = std::shared_ptr<VkCommandPool>(
|
||||||
new VkCommandPool(commandPoolHandle),
|
new VkCommandPool(commandPoolHandle),
|
||||||
[dev = device](VkCommandPool* commandPoolHandle) {
|
[dev = device](VkCommandPool* commandPoolHandle) {
|
||||||
vkDestroyCommandPool(dev, *commandPoolHandle, nullptr);
|
Layer::ovkDestroyCommandPool(dev, *commandPoolHandle, nullptr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "mini/image.hpp"
|
#include "mini/image.hpp"
|
||||||
|
#include "layer.hpp"
|
||||||
|
|
||||||
#include <lsfg.hpp>
|
#include <lsfg.hpp>
|
||||||
|
|
||||||
|
|
@ -32,16 +33,16 @@ Image::Image(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
|
||||||
};
|
};
|
||||||
VkImage imageHandle{};
|
VkImage imageHandle{};
|
||||||
auto res = vkCreateImage(device, &desc, nullptr, &imageHandle);
|
auto res = Layer::ovkCreateImage(device, &desc, nullptr, &imageHandle);
|
||||||
if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE)
|
if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE)
|
||||||
throw LSFG::vulkan_error(res, "Failed to create Vulkan image");
|
throw LSFG::vulkan_error(res, "Failed to create Vulkan image");
|
||||||
|
|
||||||
// find memory type
|
// find memory type
|
||||||
VkPhysicalDeviceMemoryProperties memProps;
|
VkPhysicalDeviceMemoryProperties memProps;
|
||||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps);
|
Layer::ovkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps);
|
||||||
|
|
||||||
VkMemoryRequirements memReqs;
|
VkMemoryRequirements memReqs;
|
||||||
vkGetImageMemoryRequirements(device, imageHandle, &memReqs);
|
Layer::ovkGetImageMemoryRequirements(device, imageHandle, &memReqs);
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||||
|
|
@ -74,24 +75,21 @@ Image::Image(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||||
.memoryTypeIndex = memType.value()
|
.memoryTypeIndex = memType.value()
|
||||||
};
|
};
|
||||||
VkDeviceMemory memoryHandle{};
|
VkDeviceMemory memoryHandle{};
|
||||||
res = vkAllocateMemory(device, &allocInfo, nullptr, &memoryHandle);
|
res = Layer::ovkAllocateMemory(device, &allocInfo, nullptr, &memoryHandle);
|
||||||
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
|
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
|
||||||
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan image");
|
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan image");
|
||||||
|
|
||||||
res = vkBindImageMemory(device, imageHandle, memoryHandle, 0);
|
res = Layer::ovkBindImageMemory(device, imageHandle, memoryHandle, 0);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image");
|
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image");
|
||||||
|
|
||||||
// obtain the sharing fd
|
// obtain the sharing fd
|
||||||
auto vkGetMemoryFdKHR =
|
|
||||||
reinterpret_cast<PFN_vkGetMemoryFdKHR>(vkGetDeviceProcAddr(device, "vkGetMemoryFdKHR"));
|
|
||||||
|
|
||||||
const VkMemoryGetFdInfoKHR fdInfo{
|
const VkMemoryGetFdInfoKHR fdInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
|
||||||
.memory = memoryHandle,
|
.memory = memoryHandle,
|
||||||
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
|
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
|
||||||
};
|
};
|
||||||
res = vkGetMemoryFdKHR(device, &fdInfo, fd);
|
res = Layer::ovkGetMemoryFdKHR(device, &fdInfo, fd);
|
||||||
if (res != VK_SUCCESS || *fd < 0)
|
if (res != VK_SUCCESS || *fd < 0)
|
||||||
throw LSFG::vulkan_error(res, "Failed to obtain sharing fd for Vulkan image");
|
throw LSFG::vulkan_error(res, "Failed to obtain sharing fd for Vulkan image");
|
||||||
|
|
||||||
|
|
@ -99,13 +97,13 @@ Image::Image(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||||
this->image = std::shared_ptr<VkImage>(
|
this->image = std::shared_ptr<VkImage>(
|
||||||
new VkImage(imageHandle),
|
new VkImage(imageHandle),
|
||||||
[dev = device](VkImage* img) {
|
[dev = device](VkImage* img) {
|
||||||
vkDestroyImage(dev, *img, nullptr);
|
Layer::ovkDestroyImage(dev, *img, nullptr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this->memory = std::shared_ptr<VkDeviceMemory>(
|
this->memory = std::shared_ptr<VkDeviceMemory>(
|
||||||
new VkDeviceMemory(memoryHandle),
|
new VkDeviceMemory(memoryHandle),
|
||||||
[dev = device](VkDeviceMemory* mem) {
|
[dev = device](VkDeviceMemory* mem) {
|
||||||
vkFreeMemory(dev, *mem, nullptr);
|
Layer::ovkFreeMemory(dev, *mem, nullptr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "mini/semaphore.hpp"
|
#include "mini/semaphore.hpp"
|
||||||
|
#include "layer.hpp"
|
||||||
|
|
||||||
#include <lsfg.hpp>
|
#include <lsfg.hpp>
|
||||||
|
|
||||||
|
|
@ -10,7 +11,7 @@ Semaphore::Semaphore(VkDevice device) {
|
||||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
|
||||||
};
|
};
|
||||||
VkSemaphore semaphoreHandle{};
|
VkSemaphore semaphoreHandle{};
|
||||||
auto res = vkCreateSemaphore(device, &desc, nullptr, &semaphoreHandle);
|
auto res = Layer::ovkCreateSemaphore(device, &desc, nullptr, &semaphoreHandle);
|
||||||
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
|
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
|
||||||
throw LSFG::vulkan_error(res, "Unable to create semaphore");
|
throw LSFG::vulkan_error(res, "Unable to create semaphore");
|
||||||
|
|
||||||
|
|
@ -18,7 +19,7 @@ Semaphore::Semaphore(VkDevice device) {
|
||||||
this->semaphore = std::shared_ptr<VkSemaphore>(
|
this->semaphore = std::shared_ptr<VkSemaphore>(
|
||||||
new VkSemaphore(semaphoreHandle),
|
new VkSemaphore(semaphoreHandle),
|
||||||
[dev = device](VkSemaphore* semaphoreHandle) {
|
[dev = device](VkSemaphore* semaphoreHandle) {
|
||||||
vkDestroySemaphore(dev, *semaphoreHandle, nullptr);
|
Layer::ovkDestroySemaphore(dev, *semaphoreHandle, nullptr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -34,20 +35,17 @@ Semaphore::Semaphore(VkDevice device, int* fd) {
|
||||||
.pNext = &exportInfo
|
.pNext = &exportInfo
|
||||||
};
|
};
|
||||||
VkSemaphore semaphoreHandle{};
|
VkSemaphore semaphoreHandle{};
|
||||||
auto res = vkCreateSemaphore(device, &desc, nullptr, &semaphoreHandle);
|
auto res = Layer::ovkCreateSemaphore(device, &desc, nullptr, &semaphoreHandle);
|
||||||
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
|
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
|
||||||
throw LSFG::vulkan_error(res, "Unable to create semaphore");
|
throw LSFG::vulkan_error(res, "Unable to create semaphore");
|
||||||
|
|
||||||
// export semaphore to fd
|
// export semaphore to fd
|
||||||
auto vkGetSemaphoreFdKHR = reinterpret_cast<PFN_vkGetSemaphoreFdKHR>(
|
|
||||||
vkGetDeviceProcAddr(device, "vkGetSemaphoreFdKHR"));
|
|
||||||
|
|
||||||
const VkSemaphoreGetFdInfoKHR fdInfo{
|
const VkSemaphoreGetFdInfoKHR fdInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
|
||||||
.semaphore = semaphoreHandle,
|
.semaphore = semaphoreHandle,
|
||||||
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT
|
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT
|
||||||
};
|
};
|
||||||
res = vkGetSemaphoreFdKHR(device, &fdInfo, fd);
|
res = Layer::ovkGetSemaphoreFdKHR(device, &fdInfo, fd);
|
||||||
if (res != VK_SUCCESS || *fd < 0)
|
if (res != VK_SUCCESS || *fd < 0)
|
||||||
throw LSFG::vulkan_error(res, "Unable to export semaphore to fd");
|
throw LSFG::vulkan_error(res, "Unable to export semaphore to fd");
|
||||||
|
|
||||||
|
|
@ -55,7 +53,7 @@ Semaphore::Semaphore(VkDevice device, int* fd) {
|
||||||
this->semaphore = std::shared_ptr<VkSemaphore>(
|
this->semaphore = std::shared_ptr<VkSemaphore>(
|
||||||
new VkSemaphore(semaphoreHandle),
|
new VkSemaphore(semaphoreHandle),
|
||||||
[dev = device](VkSemaphore* semaphoreHandle) {
|
[dev = device](VkSemaphore* semaphoreHandle) {
|
||||||
vkDestroySemaphore(dev, *semaphoreHandle, nullptr);
|
Layer::ovkDestroySemaphore(dev, *semaphoreHandle, nullptr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
|
#include "layer.hpp"
|
||||||
|
|
||||||
#include <lsfg.hpp>
|
#include <lsfg.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
|
|
@ -13,9 +15,9 @@ std::pair<uint32_t, VkQueue> Utils::findQueue(VkDevice device, VkPhysicalDevice
|
||||||
std::copy_n(desc->pQueueCreateInfos, enabledQueues.size(), enabledQueues.data());
|
std::copy_n(desc->pQueueCreateInfos, enabledQueues.size(), enabledQueues.data());
|
||||||
|
|
||||||
uint32_t familyCount{};
|
uint32_t familyCount{};
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, nullptr);
|
Layer::ovkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, nullptr);
|
||||||
std::vector<VkQueueFamilyProperties> families(familyCount);
|
std::vector<VkQueueFamilyProperties> families(familyCount);
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount,
|
Layer::ovkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount,
|
||||||
families.data());
|
families.data());
|
||||||
|
|
||||||
std::optional<uint32_t> idx;
|
std::optional<uint32_t> idx;
|
||||||
|
|
@ -30,7 +32,7 @@ std::pair<uint32_t, VkQueue> Utils::findQueue(VkDevice device, VkPhysicalDevice
|
||||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "No suitable queue found");
|
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "No suitable queue found");
|
||||||
|
|
||||||
VkQueue queue{};
|
VkQueue queue{};
|
||||||
vkGetDeviceQueue(device, *idx, 0, &queue);
|
Layer::ovkGetDeviceQueue(device, *idx, 0, &queue);
|
||||||
|
|
||||||
return { *idx, queue };
|
return { *idx, queue };
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +80,7 @@ void Utils::copyImage(VkCommandBuffer buf,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const std::vector<VkImageMemoryBarrier> barriers = { srcBarrier, dstBarrier };
|
const std::vector<VkImageMemoryBarrier> barriers = { srcBarrier, dstBarrier };
|
||||||
vkCmdPipelineBarrier(buf,
|
Layer::ovkCmdPipelineBarrier(buf,
|
||||||
pre, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
pre, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||||
0, nullptr, 0, nullptr,
|
0, nullptr, 0, nullptr,
|
||||||
static_cast<uint32_t>(barriers.size()), barriers.data());
|
static_cast<uint32_t>(barriers.size()), barriers.data());
|
||||||
|
|
@ -98,7 +100,7 @@ void Utils::copyImage(VkCommandBuffer buf,
|
||||||
.depth = 1
|
.depth = 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
vkCmdCopyImage(buf,
|
Layer::ovkCmdCopyImage(buf,
|
||||||
src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
1, &imageCopy);
|
1, &imageCopy);
|
||||||
|
|
@ -115,7 +117,7 @@ void Utils::copyImage(VkCommandBuffer buf,
|
||||||
.layerCount = 1
|
.layerCount = 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
vkCmdPipelineBarrier(buf,
|
Layer::ovkCmdPipelineBarrier(buf,
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT, post, 0,
|
VK_PIPELINE_STAGE_TRANSFER_BIT, post, 0,
|
||||||
0, nullptr, 0, nullptr,
|
0, nullptr, 0, nullptr,
|
||||||
1, &presentBarrier);
|
1, &presentBarrier);
|
||||||
|
|
@ -135,10 +137,29 @@ void Utils::copyImage(VkCommandBuffer buf,
|
||||||
.layerCount = 1
|
.layerCount = 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
vkCmdPipelineBarrier(buf,
|
Layer::ovkCmdPipelineBarrier(buf,
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT, post, 0,
|
VK_PIPELINE_STAGE_TRANSFER_BIT, post, 0,
|
||||||
0, nullptr, 0, nullptr,
|
0, nullptr, 0, nullptr,
|
||||||
1, &presentBarrier);
|
1, &presentBarrier);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::optional<std::string> layersEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::storeLayerEnv() {
|
||||||
|
const char* env = std::getenv("VK_INSTANCE_LAYERS");
|
||||||
|
if (env)
|
||||||
|
layersEnvironment = env;
|
||||||
|
else
|
||||||
|
layersEnvironment.reset();
|
||||||
|
unsetenv("VK_INSTANCE_LAYERS");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::restoreLayerEnv() {
|
||||||
|
if (layersEnvironment.has_value())
|
||||||
|
setenv("VK_INSTANCE_LAYERS", layersEnvironment->c_str(), 1);
|
||||||
|
else
|
||||||
|
unsetenv("VK_INSTANCE_LAYERS");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue