diff --git a/Cargo.lock b/Cargo.lock index 6cb75c0cf4..d596423f58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -489,28 +489,6 @@ dependencies = [ "settings-schema", ] -[[package]] -name = "alvr_vrcompositor_wrapper" -version = "21.0.0-dev10" -dependencies = [ - "alvr_common", - "alvr_filesystem", - "exec", - "xshell", -] - -[[package]] -name = "alvr_vulkan_layer" -version = "21.0.0-dev10" -dependencies = [ - "alvr_common", - "alvr_filesystem", - "bindgen 0.71.1", - "cc", - "pkg-config", - "walkdir", -] - [[package]] name = "alvr_xtask" version = "21.0.0-dev10" @@ -1984,17 +1962,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - [[package]] name = "errno" version = "0.3.10" @@ -2005,16 +1972,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "error-code" version = "3.3.1" @@ -2042,16 +1999,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "exec" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "886b70328cba8871bfc025858e1de4be16b1d5088f2ba50b57816f4210672615" -dependencies = [ - "errno 0.2.8", - "libc", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -4837,7 +4784,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.8.0", - "errno 0.3.10", + "errno", "libc", "linux-raw-sys", "windows-sys 0.59.0", diff --git a/alvr/dashboard/src/steamvr_launcher/linux_steamvr.rs b/alvr/dashboard/src/steamvr_launcher/linux_steamvr.rs index d4c8f7a7a6..62045aee20 100644 --- a/alvr/dashboard/src/steamvr_launcher/linux_steamvr.rs +++ b/alvr/dashboard/src/steamvr_launcher/linux_steamvr.rs @@ -17,43 +17,6 @@ pub fn terminate_process(process: &Process) { process.kill_with(sysinfo::Signal::Term); } -pub fn maybe_wrap_vrcompositor_launcher() -> alvr_common::anyhow::Result<()> { - let steamvr_bin_dir = alvr_server_io::steamvr_root_dir()? - .join("bin") - .join("linux64"); - let steamvr_vrserver_path = steamvr_bin_dir.join("vrserver"); - debug!( - "File path used to check for linux files: {}", - steamvr_vrserver_path.display().to_string() - ); - match steamvr_vrserver_path.try_exists() { - Ok(exists) => { - if !exists { - bail!( - "SteamVR Linux files missing, aborting startup, please re-check compatibility tools for SteamVR, verify integrity of files for SteamVR and make sure you're not using Flatpak Steam with non-Flatpak ALVR." - ); - } - } - Err(e) => { - return Err(e.into()); - } - }; - - let launcher_path = steamvr_bin_dir.join("vrcompositor"); - // In case of SteamVR update, vrcompositor will be restored - if fs::read_link(&launcher_path).is_ok() { - fs::remove_file(&launcher_path)?; // recreate the link - } else { - fs::rename(&launcher_path, steamvr_bin_dir.join("vrcompositor.real"))?; - } - - std::os::unix::fs::symlink( - crate::get_filesystem_layout().vrcompositor_wrapper(), - &launcher_path, - )?; - - Ok(()) -} #[derive(PartialEq)] enum DeviceInfo { Nvidia, diff --git a/alvr/dashboard/src/steamvr_launcher/mod.rs b/alvr/dashboard/src/steamvr_launcher/mod.rs index 012e7d1aee..b2ee224e94 100644 --- a/alvr/dashboard/src/steamvr_launcher/mod.rs +++ b/alvr/dashboard/src/steamvr_launcher/mod.rs @@ -144,15 +144,6 @@ impl Launcher { warn!("Failed to unblock ALVR driver: {:?}", err); } - #[cfg(target_os = "linux")] - { - let vrcompositor_wrap_result = linux_steamvr::maybe_wrap_vrcompositor_launcher(); - alvr_common::show_err(linux_steamvr::maybe_wrap_vrcompositor_launcher()); - if vrcompositor_wrap_result.is_err() { - return; - } - } - if !is_steamvr_running() { debug!("SteamVR is dead. Launching..."); diff --git a/alvr/filesystem/src/lib.rs b/alvr/filesystem/src/lib.rs index d647aa94b2..4ed82b2929 100644 --- a/alvr/filesystem/src/lib.rs +++ b/alvr/filesystem/src/lib.rs @@ -85,8 +85,6 @@ pub fn dashboard_fname() -> &'static str { pub struct Layout { // directory containing the dashboard executable pub executables_dir: PathBuf, - // (linux only) directory where libalvr_vulkan_layer.so is saved - pub libraries_dir: PathBuf, // parent directory of resources like the dashboard and presets folders pub static_resources_dir: PathBuf, // directory for storing configuration files (session.json) @@ -95,16 +93,12 @@ pub struct Layout { pub log_dir: PathBuf, // directory to register in openVR driver path pub openvr_driver_root_dir: PathBuf, - // (linux only) parent directory of the executable to wrap vrcompositor - pub vrcompositor_wrapper_dir: PathBuf, // (linux only) parent directory of the firewall script pub firewall_script_dir: PathBuf, // (linux only) parent directory of the firewalld config pub firewalld_config_dir: PathBuf, // (linux only) parent directory of the ufw config pub ufw_config_dir: PathBuf, - // (linux only) directory where the vulkan layer manifest is saved - pub vulkan_layer_manifest_dir: PathBuf, pub launcher_root: Option, } @@ -117,20 +111,13 @@ impl Layout { // Get paths from environment or use FHS compliant paths let executables_dir = or_path(option_env!("ALVR_EXECUTABLES_DIR"), "bin"); - let libraries_dir = or_path(option_env!("ALVR_LIBRARIES_DIR"), "lib64"); let static_resources_dir = or_path(option_env!("ALVR_STATIC_RESOURCES_DIR"), "share/alvr"); let openvr_driver_root_dir = or_path(option_env!("ALVR_OPENVR_DRIVER_ROOT_DIR"), "lib64/alvr"); - let vrcompositor_wrapper_dir = - or_path(option_env!("ALVR_VRCOMPOSITOR_WRAPPER_DIR"), "libexec/alvr"); let firewall_script_dir = or_path(option_env!("FIREWALL_SCRIPT_DIR"), "libexec/alvr"); let firewalld_config_dir = or_path(option_env!("FIREWALLD_CONFIG_DIR"), "libexec/alvr"); let ufw_config_dir = or_path(option_env!("UFW_CONFIG_DIR"), "libexec/alvr"); - let vulkan_layer_manifest_dir = or_path( - option_env!("ALVR_VULKAN_LAYER_MANIFEST_DIR"), - "share/vulkan/explicit_layer.d", - ); let config_dir = option_env!("ALVR_CONFIG_DIR") .map_or_else(|| dirs::config_dir().unwrap().join("alvr"), PathBuf::from); @@ -139,16 +126,13 @@ impl Layout { Self { executables_dir, - libraries_dir, static_resources_dir, config_dir, log_dir, openvr_driver_root_dir, - vrcompositor_wrapper_dir, firewall_script_dir, firewalld_config_dir, ufw_config_dir, - vulkan_layer_manifest_dir, launcher_root: root .parent() .and_then(|p| p.parent()) @@ -164,11 +148,9 @@ impl Layout { config_dir: root.to_owned(), log_dir: root.to_owned(), openvr_driver_root_dir: root.to_owned(), - vrcompositor_wrapper_dir: root.to_owned(), firewall_script_dir: root.to_owned(), firewalld_config_dir: root.to_owned(), ufw_config_dir: root.to_owned(), - vulkan_layer_manifest_dir: root.to_owned(), launcher_root: root.parent().and_then(|p| p.parent()).map(|p| p.to_owned()), } } @@ -252,18 +234,6 @@ impl Layout { self.openvr_driver_root_dir.join("driver.vrdrivermanifest") } - pub fn vrcompositor_wrapper(&self) -> PathBuf { - self.vrcompositor_wrapper_dir.join("vrcompositor-wrapper") - } - - pub fn drm_lease_shim(&self) -> PathBuf { - self.vrcompositor_wrapper_dir.join("alvr_drm_lease_shim.so") - } - - pub fn vulkan_layer(&self) -> PathBuf { - self.libraries_dir.join(dynlib_fname("alvr_vulkan_layer")) - } - pub fn firewall_script(&self) -> PathBuf { self.firewall_script_dir.join("alvr_fw_config.sh") } @@ -276,10 +246,6 @@ impl Layout { self.ufw_config_dir.join("ufw-alvr") } - pub fn vulkan_layer_manifest(&self) -> PathBuf { - self.vulkan_layer_manifest_dir.join("alvr_x86_64.json") - } - pub fn launcher_exe(&self) -> Option { self.launcher_root .as_ref() diff --git a/alvr/server_openvr/build.rs b/alvr/server_openvr/build.rs index 11e5a5a222..cbefc78852 100644 --- a/alvr/server_openvr/build.rs +++ b/alvr/server_openvr/build.rs @@ -61,7 +61,8 @@ fn main() { let mut build = cc::Build::new(); build .cpp(true) - .std("c++17") + .std("c++20") + .flag_if_supported("-fdiagnostics-color=always") .files(source_files_paths) .include(alvr_filesystem::workspace_dir().join("openvr/headers")) .include("cpp"); diff --git a/alvr/server_openvr/cpp/alvr_server/HMD.cpp b/alvr/server_openvr/cpp/alvr_server/HMD.cpp index 27c41db105..9c1298915c 100644 --- a/alvr/server_openvr/cpp/alvr_server/HMD.cpp +++ b/alvr/server_openvr/cpp/alvr_server/HMD.cpp @@ -8,13 +8,12 @@ #include "Utils.h" #include "ViveTrackerProxy.h" #include "bindings.h" +#include #ifdef _WIN32 #include "platform/win32/CEncoder.h" #elif __APPLE__ #include "platform/macos/CEncoder.h" -#else -#include "platform/linux/CEncoder.h" #endif Hmd::Hmd() @@ -49,15 +48,16 @@ Hmd::Hmd() } Hmd::~Hmd() { + ShutdownRuntime(); Debug("Hmd::destructor"); +#ifdef _WIN32 if (m_encoder) { Debug("Hmd::~Hmd(): Stopping encoder...\n"); m_encoder->Stop(); m_encoder.reset(); } -#ifdef _WIN32 if (m_D3DRender) { m_D3DRender->Shutdown(); m_D3DRender.reset(); @@ -80,31 +80,15 @@ bool Hmd::activate() { vr::VRDriverInput()->CreateBooleanComponent(this->prop_container, "/proximity", &m_proximity); -#ifdef _WIN32 + float originalIPD = vr::VRSettings()->GetFloat(vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_IPD_Float); vr::VRSettings()->SetFloat(vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_IPD_Float, 0.063); -#endif + HmdMatrix_SetIdentity(&m_eyeToHeadLeft); HmdMatrix_SetIdentity(&m_eyeToHeadRight); -// Disable async reprojection on Linux. Windows interface uses IVRDriverDirectModeComponent -// which never applies reprojection -// Also Disable async reprojection on vulkan -#ifndef _WIN32 - vr::VRSettings()->SetBool( - vr::k_pch_SteamVR_Section, - vr::k_pch_SteamVR_EnableLinuxVulkanAsync_Bool, - Settings::Instance().m_enableLinuxVulkanAsyncCompute - ); - vr::VRSettings()->SetBool( - vr::k_pch_SteamVR_Section, - vr::k_pch_SteamVR_DisableAsyncReprojection_Bool, - !Settings::Instance().m_enableLinuxAsyncReprojection - ); -#endif - if (!m_baseComponentsInitialized) { m_baseComponentsInitialized = true; @@ -139,6 +123,8 @@ bool Hmd::activate() { m_directModeComponent = std::make_shared(m_D3DRender, m_poseHistory); +#elif __linux__ + m_directModeComponent = std::make_shared(m_poseHistory); #endif } @@ -166,11 +152,9 @@ void* Hmd::get_component(const char* component_name_and_version) { return (vr::IVRDisplayComponent*)this; } -#ifdef _WIN32 if (name_and_vers == vr::IVRDriverDirectModeComponent_Version) { return m_directModeComponent.get(); } -#endif return nullptr; } @@ -210,7 +194,7 @@ void Hmd::OnPoseUpdated(uint64_t targetTimestampNs, FfiDeviceMotion motion) { #if !defined(_WIN32) && !defined(__APPLE__) // This has to be set after initialization is done, because something in vrcompositor is // setting it to 90Hz in the meantime - if (!m_refreshRateSet && m_encoder && m_encoder->IsConnected()) { + if (!m_refreshRateSet /* && m_encoder && m_encoder->IsConnected() */) { m_refreshRateSet = true; vr::VRProperties()->SetFloatProperty( this->prop_container, @@ -248,14 +232,13 @@ void Hmd::StartStreaming() { m_encoder->Start(); m_directModeComponent->SetEncoder(m_encoder); + m_encoder->OnStreamStart(); #elif __APPLE__ m_encoder = std::make_shared(); -#else - m_encoder = std::make_shared(m_poseHistory); - m_encoder->Start(); -#endif + m_encoder->OnStreamStart(); +#endif } m_streamComponentsInitialized = true; @@ -312,11 +295,7 @@ void Hmd::GetWindowBounds(int32_t* pnX, int32_t* pnY, uint32_t* pnWidth, uint32_ } bool Hmd::IsDisplayRealDisplay() { -#ifdef _WIN32 return false; -#else - return true; -#endif } void Hmd::GetRecommendedRenderTargetSize(uint32_t* pnWidth, uint32_t* pnHeight) { @@ -354,3 +333,9 @@ void Hmd::GetProjectionRaw(vr::EVREye eye, float* left, float* right, float* top vr::DistortionCoordinates_t Hmd::ComputeDistortion(vr::EVREye, float u, float v) { return { { u, v }, { u, v }, { u, v } }; } + +bool Hmd::ComputeInverseDistortion( + vr::HmdVector2_t* pResult, vr::EVREye eEye, uint32_t unChannel, float fU, float fV +) { + return false; +} diff --git a/alvr/server_openvr/cpp/alvr_server/HMD.h b/alvr/server_openvr/cpp/alvr_server/HMD.h index 91077767bb..b84725178b 100644 --- a/alvr/server_openvr/cpp/alvr_server/HMD.h +++ b/alvr/server_openvr/cpp/alvr_server/HMD.h @@ -6,6 +6,8 @@ #include #ifdef _WIN32 #include "platform/win32/OvrDirectModeComponent.h" +#else +#include "platform/linux/OvrDirectModeComponent.h" #endif class Controller; @@ -15,13 +17,15 @@ class ViveTrackerProxy; class CEncoder; #ifdef _WIN32 class CD3DRender; +#elif __linux__ +class Renderer; #endif class PoseHistory; class Hmd : public TrackedDevice, vr::IVRDisplayComponent { public: std::shared_ptr m_poseHistory; - std::shared_ptr m_encoder; + // std::shared_ptr m_encoder; Hmd(); virtual ~Hmd(); @@ -49,8 +53,12 @@ class Hmd : public TrackedDevice, vr::IVRDisplayComponent { std::shared_ptr m_D3DRender; #endif -#ifdef _WIN32 +#ifdef __linux__ +public: +#endif std::shared_ptr m_directModeComponent; +#ifdef __linux__ +private: #endif std::shared_ptr m_viveTrackerProxy; @@ -74,4 +82,7 @@ class Hmd : public TrackedDevice, vr::IVRDisplayComponent { virtual void GetProjectionRaw(vr::EVREye eEye, float* pfLeft, float* pfRight, float* pfTop, float* pfBottom); virtual vr::DistortionCoordinates_t ComputeDistortion(vr::EVREye eEye, float fU, float fV); + virtual bool ComputeInverseDistortion( + vr::HmdVector2_t* pResult, vr::EVREye eEye, uint32_t unChannel, float fU, float fV + ); }; diff --git a/alvr/server_openvr/cpp/alvr_server/alvr_server.cpp b/alvr/server_openvr/cpp/alvr_server/alvr_server.cpp index 5ad783168b..e5155692a9 100644 --- a/alvr/server_openvr/cpp/alvr_server/alvr_server.cpp +++ b/alvr/server_openvr/cpp/alvr_server/alvr_server.cpp @@ -3,8 +3,6 @@ #include #elif __APPLE__ #include "platform/macos/CEncoder.h" -#else -#include "platform/linux/CEncoder.h" #endif #include "Controller.h" #include "FakeViveTracker.h" @@ -380,9 +378,15 @@ void DeinitializeStreaming() { void SendVSync() { vr::VRServerDriverHost()->VsyncEvent(0.0); } void RequestIDR() { +#ifdef _WIN32 if (g_driver_provider.hmd && g_driver_provider.hmd->m_encoder) { g_driver_provider.hmd->m_encoder->InsertIDR(); } +#elif __linux__ + if (g_driver_provider.hmd && g_driver_provider.hmd->m_directModeComponent) { + g_driver_provider.hmd->m_directModeComponent->RequestIdr(); + } +#endif } void SetTracking( @@ -517,9 +521,9 @@ void SetChaperoneArea(float areaWidth, float areaHeight) { } void CaptureFrame() { -#ifndef __APPLE__ - if (g_driver_provider.hmd && g_driver_provider.hmd->m_encoder) { - g_driver_provider.hmd->m_encoder->CaptureFrame(); - } +#if _WIN32 + // if (g_driver_provider.hmd && g_driver_provider.hmd->m_encoder) { + // g_driver_provider.hmd->m_encoder->CaptureFrame(); + // } #endif } diff --git a/alvr/server_openvr/cpp/platform/linux/CEncoder.cpp b/alvr/server_openvr/cpp/platform/linux/CEncoder.cpp deleted file mode 100644 index e7f71037d7..0000000000 --- a/alvr/server_openvr/cpp/platform/linux/CEncoder.cpp +++ /dev/null @@ -1,317 +0,0 @@ -#include "CEncoder.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ALVR-common/packet_types.h" -#include "EncodePipeline.h" -#include "FrameRender.h" -#include "alvr_server/Logger.h" -#include "alvr_server/PoseHistory.h" -#include "alvr_server/Settings.h" -#include "ffmpeg_helper.h" -#include "protocol.h" - -extern "C" { -#include -} - -CEncoder::CEncoder(std::shared_ptr poseHistory) - : m_poseHistory(poseHistory) { } - -CEncoder::~CEncoder() { Stop(); } - -namespace { -void read_exactly(pollfd pollfds, char* out, size_t size, std::atomic_bool& exiting) { - while (not exiting and size != 0) { - int timeout = 1; // poll api doesn't fit perfectly(100 mircoseconds) poll uses milliseconds - // we do the best we can(1000 mircoseconds) - pollfds.events = POLLIN; - int count = poll(&pollfds, 1, timeout); - if (count < 0) { - throw MakeException("poll failed: %s", strerror(errno)); - } else if (count == 1) { - int s = read(pollfds.fd, out, size); - if (s == -1) { - throw MakeException("read failed: %s", strerror(errno)); - } - out += s; - size -= s; - } - } -} - -void read_latest(pollfd pollfds, char* out, size_t size, std::atomic_bool& exiting) { - read_exactly(pollfds, out, size, exiting); - while (not exiting) { - int timeout = 0; // poll api fixes the original perfectly(0 microseconds) - pollfds.events = POLLIN; - int count = poll(&pollfds, 1, timeout); - if (count == 0) - return; - read_exactly(pollfds, out, size, exiting); - } -} - -int accept_timeout(pollfd socket, std::atomic_bool& exiting) { - while (not exiting) { - int timeout = 15; // poll api also fits the original perfectly(15000 microseconds) - socket.events = POLLIN; - int count = poll(&socket, 1, timeout); - if (count < 0) { - throw MakeException("poll failed: %s", strerror(errno)); - } else if (count == 1) { - return accept4(socket.fd, NULL, NULL, SOCK_CLOEXEC); - } - } - return -1; -} - -void av_logfn(void*, int level, const char* data, va_list va) { - if (level > -#ifdef DEBUG - AV_LOG_DEBUG) -#else - AV_LOG_INFO) -#endif - return; - - char buf[256]; - vsnprintf(buf, sizeof(buf), data, va); - - if (level <= AV_LOG_ERROR) - Error("Encoder: %s", buf); - else - Info("Encoder: %s", buf); -} - -} // namespace - -void CEncoder::GetFds(int client, int (*received_fds)[6]) { - struct msghdr msg; - struct cmsghdr* cmsg; - union { - struct cmsghdr cm; - u_int8_t pktinfo_sizer[sizeof(struct cmsghdr) + 1024]; - } control_un; - struct iovec iov[1]; - char data[1]; - int ret; - - msg.msg_control = &control_un; - msg.msg_controllen = sizeof(control_un); - msg.msg_flags = 0; - msg.msg_name = NULL; - msg.msg_namelen = 0; - iov[0].iov_base = data; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - ret = recvmsg(client, &msg, 0); - if (ret == -1) { - throw MakeException("recvmsg failed: %s", strerror(errno)); - } - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - memcpy(received_fds, CMSG_DATA(cmsg), sizeof(*received_fds)); - break; - } - } - - if (cmsg == NULL) { - throw MakeException("cmsg is NULL"); - } -} - -void CEncoder::Run() { - Info("CEncoder::Run\n"); - m_socketPath = getenv("XDG_RUNTIME_DIR"); - m_socketPath += "/alvr-ipc"; - - int ret; - // we don't really care about what happends with unlink, it's just incase we crashed before this - // run - ret = unlink(m_socketPath.c_str()); - - m_socket.fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - struct sockaddr_un name; - if (m_socket.fd == -1) { - perror("socket"); - exit(1); - } - - memset(&name, 0, sizeof(name)); - name.sun_family = AF_UNIX; - strncpy(name.sun_path, m_socketPath.c_str(), sizeof(name.sun_path) - 1); - - ret = bind(m_socket.fd, (const struct sockaddr*)&name, sizeof(name)); - if (ret == -1) { - perror("bind"); - exit(1); - } - - ret = listen(m_socket.fd, 1024); - if (ret == -1) { - perror("listen"); - exit(1); - } - - Info("CEncoder Listening\n"); - struct pollfd client; - client.fd = accept_timeout(m_socket, m_exiting); - if (m_exiting) - return; - init_packet init; - client.events = POLLIN; - read_exactly(client, (char*)&init, sizeof(init), m_exiting); - if (m_exiting) - return; - - // check that pointer types are null, other values would not make sense over a socket - assert(init.image_create_info.queueFamilyIndexCount == 0); - assert(init.image_create_info.pNext == NULL); - - char ifbuf[256]; - char ifbuf2[256]; - sprintf(ifbuf, "/proc/%d/cmdline", (int)init.source_pid); - std::ifstream ifscmdl(ifbuf); - ifscmdl >> ifbuf2; - Info("CEncoder client connected, pid %d, cmdline %s\n", (int)init.source_pid, ifbuf2); - - try { - GetFds(client.fd, &m_fds); - - m_connected = true; - - fprintf(stderr, "\n\nWe are initalizing Vulkan in CEncoder thread\n\n\n"); - - av_log_set_callback(av_logfn); - - alvr::VkContext vk_ctx(init.device_uuid.data(), {}); - - FrameRender render(vk_ctx, init, m_fds); - auto output = render.CreateOutput(); - - alvr::VkFrame frame( - vk_ctx, output.image, output.imageInfo, output.size, output.memory, output.drm - ); - auto encode_pipeline = alvr::EncodePipeline::Create( - &render, - vk_ctx, - frame, - output.imageInfo, - render.GetEncodingWidth(), - render.GetEncodingHeight() - ); - - bool valid_timestamps = true; - - fprintf(stderr, "CEncoder starting to read present packets"); - present_packet frame_info; - while (not m_exiting) { - read_latest(client, (char*)&frame_info, sizeof(frame_info), m_exiting); - - encode_pipeline->SetParams(GetDynamicEncoderParams()); - - auto pose = m_poseHistory->GetBestPoseMatch((const vr::HmdMatrix34_t&)frame_info.pose); - if (!pose) { - continue; - } - - if (m_captureFrame) { - m_captureFrame = false; - render.CaptureInputFrame( - Settings::Instance().m_captureFrameDir + "/alvr_frame_input.ppm" - ); - render.CaptureOutputFrame( - Settings::Instance().m_captureFrameDir + "/alvr_frame_output.ppm" - ); - } - - render.Render(frame_info.image, frame_info.semaphore_value); - - if (!valid_timestamps) { - ReportPresent(pose->targetTimestampNs, 0); - ReportComposed(pose->targetTimestampNs, 0); - } - - encode_pipeline->PushFrame(pose->targetTimestampNs, m_scheduler.CheckIDRInsertion()); - - static_assert(sizeof(frame_info.pose) == sizeof(vr::HmdMatrix34_t&)); - - alvr::FramePacket packet; - if (!encode_pipeline->GetEncoded(packet)) { - Error("Failed to get encoded data!"); - continue; - } - - if (valid_timestamps) { - auto render_timestamps = render.GetTimestamps(); - auto encode_timestamp = encode_pipeline->GetTimestamp(); - - uint64_t present_offset = render_timestamps.now - render_timestamps.renderBegin; - uint64_t composed_offset = 0; - - valid_timestamps = render_timestamps.now != 0; - - if (encode_timestamp.gpu) { - composed_offset = render_timestamps.now - encode_timestamp.gpu; - } else if (encode_timestamp.cpu) { - auto now = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch() - ) - .count(); - composed_offset = now - encode_timestamp.cpu; - } else { - composed_offset = render_timestamps.now - render_timestamps.renderComplete; - } - - if (present_offset < composed_offset) { - present_offset = composed_offset; - } - - ReportPresent(pose->targetTimestampNs, present_offset); - ReportComposed(pose->targetTimestampNs, composed_offset); - } - - ParseFrameNals( - encode_pipeline->GetCodec(), packet.data, packet.size, packet.pts, packet.isIDR - ); - } - } catch (std::exception& e) { - std::stringstream err; - err << "error in encoder thread: " << e.what(); - Error(err.str().c_str()); - } - - client.events = POLLHUP; - close(client.fd); -} - -void CEncoder::Stop() { - m_exiting = true; - m_socket.events = POLLHUP; - close(m_socket.fd); - unlink(m_socketPath.c_str()); -} - -void CEncoder::OnStreamStart() { m_scheduler.OnStreamStart(); } - -void CEncoder::InsertIDR() { m_scheduler.InsertIDR(); } - -void CEncoder::CaptureFrame() { m_captureFrame = true; } diff --git a/alvr/server_openvr/cpp/platform/linux/CEncoder.h b/alvr/server_openvr/cpp/platform/linux/CEncoder.h deleted file mode 100644 index 28728773c4..0000000000 --- a/alvr/server_openvr/cpp/platform/linux/CEncoder.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "alvr_server/IDRScheduler.h" -#include "shared/threadtools.h" -#include -#include -#include -#include - -class PoseHistory; - -class CEncoder : public CThread { -public: - CEncoder(std::shared_ptr poseHistory); - ~CEncoder(); - bool Init() override { return true; } - void Run() override; - - void Stop(); - void OnStreamStart(); - void InsertIDR(); - bool IsConnected() { return m_connected; } - void CaptureFrame(); - -private: - void GetFds(int client, int (*fds)[6]); - std::shared_ptr m_poseHistory; - std::atomic_bool m_exiting { false }; - IDRScheduler m_scheduler; - pollfd m_socket; - std::string m_socketPath; - int m_fds[6]; - bool m_connected = false; - std::atomic_bool m_captureFrame = false; -}; diff --git a/alvr/server_openvr/cpp/platform/linux/EncodePipeline.cpp b/alvr/server_openvr/cpp/platform/linux/EncodePipeline.cpp index 52ac82aedc..21fea99151 100644 --- a/alvr/server_openvr/cpp/platform/linux/EncodePipeline.cpp +++ b/alvr/server_openvr/cpp/platform/linux/EncodePipeline.cpp @@ -1,7 +1,7 @@ #include "EncodePipeline.h" #include "EncodePipelineNvEnc.h" -#include "EncodePipelineSW.h" +// #include "EncodePipelineSW.h" #include "EncodePipelineVAAPI.h" #include "alvr_server/Logger.h" #include "alvr_server/Settings.h" @@ -11,6 +11,7 @@ extern "C" { #include } +// TODO: Uninheritance this void alvr::EncodePipeline::SetParams(FfiDynamicEncoderParams params) { if (params.updated) { encoder_ctx->bit_rate = params.bitrate_bps / params.framerate * 60.0; @@ -22,32 +23,32 @@ void alvr::EncodePipeline::SetParams(FfiDynamicEncoderParams params) { } std::unique_ptr alvr::EncodePipeline::Create( - Renderer* render, - VkContext& vk_ctx, + alvr::VkContext& vk_ctx, + std::string devicePath, VkFrame& input_frame, - VkImageCreateInfo& image_create_info, uint32_t width, uint32_t height ) { + using alvr::Vendor; if (Settings::Instance().m_force_sw_encoding == false) { - if (vk_ctx.nvidia) { - try { - auto nvenc = std::make_unique( - render, vk_ctx, input_frame, image_create_info, width, height - ); - Info("Using NvEnc encoder"); - return nvenc; - } catch (std::exception& e) { - Error( - "Failed to create NvEnc encoder: %s\nPlease make sure you have installed CUDA " - "runtime.", - e.what() - ); - } + alvr::HWContext hwCtx(vk_ctx); + if (vk_ctx.meta.vendor == Vendor::Nvidia) { + // try { + // auto nvenc = std::make_unique( + // render, vk_ctx, input_frame, image_create_info, width, height + // ); + // Info("Using NvEnc encoder"); + // return nvenc; + // } catch (std::exception& e) { + // Error( + // "Failed to create NvEnc encoder: %s\nPlease make sure you have installed CUDA + // " "runtime.", e.what() + // ); + // } } else { try { auto vaapi = std::make_unique( - render, vk_ctx, input_frame, width, height + hwCtx, devicePath, vk_ctx.meta.vendor, input_frame, width, height ); Info("Using VAAPI encoder"); return vaapi; @@ -60,9 +61,10 @@ std::unique_ptr alvr::EncodePipeline::Create( } } } - auto sw = std::make_unique(render, width, height); - Info("Using SW encoder"); - return sw; + // auto sw = std::make_unique(render, width, height); + // Info("Using SW encoder"); + // return sw; + return nullptr; } alvr::EncodePipeline::~EncodePipeline() { avcodec_free_context(&encoder_ctx); } diff --git a/alvr/server_openvr/cpp/platform/linux/EncodePipeline.h b/alvr/server_openvr/cpp/platform/linux/EncodePipeline.h index 2347940872..1db6379eed 100644 --- a/alvr/server_openvr/cpp/platform/linux/EncodePipeline.h +++ b/alvr/server_openvr/cpp/platform/linux/EncodePipeline.h @@ -2,9 +2,10 @@ #include "alvr_server/bindings.h" #include #include -#include #include +#include "VkContext.hpp" + extern "C" struct AVCodecContext; extern "C" struct AVPacket; @@ -39,10 +40,9 @@ class EncodePipeline { virtual void SetParams(FfiDynamicEncoderParams params); static std::unique_ptr Create( - Renderer* render, - VkContext& vk_ctx, + alvr::VkContext& vk_ctx, + std::string devicePath, VkFrame& input_frame, - VkImageCreateInfo& image_create_info, uint32_t width, uint32_t height ); diff --git a/alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.cpp b/alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.cpp index 58475fe298..f1510badb3 100644 --- a/alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.cpp +++ b/alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.cpp @@ -5,6 +5,7 @@ #include "ffmpeg_helper.h" #include #include +#include extern "C" { #include @@ -61,15 +62,15 @@ void set_hwframe_ctx(AVCodecContext* ctx, AVBufferRef* hw_device_ctx) { } // namespace alvr::EncodePipelineNvEnc::EncodePipelineNvEnc( - Renderer* render, - VkContext& vk_ctx, + Renderer* render, + HWContext& vk_ctx, + VkContext& v_ctx, VkFrame& input_frame, - VkImageCreateInfo& image_create_info, uint32_t width, uint32_t height -) { - r = render; - vk_frame_ctx = std::make_unique(vk_ctx, image_create_info); +) + : v_ctx(v_ctx) { + // vk_frame_ctx = std::make_unique(v_ctx, nullptr); auto input_frame_ctx = (AVHWFramesContext*)vk_frame_ctx->ctx->data; assert(input_frame_ctx->sw_format == AV_PIX_FMT_BGRA); @@ -77,7 +78,7 @@ alvr::EncodePipelineNvEnc::EncodePipelineNvEnc( int err; vk_frame = input_frame.make_av_frame(*vk_frame_ctx); - err = av_hwdevice_ctx_create_derived(&hw_ctx, AV_HWDEVICE_TYPE_CUDA, vk_ctx.ctx, 0); + err = av_hwdevice_ctx_create_derived(&hw_ctx, AV_HWDEVICE_TYPE_CUDA, vk_ctx.avCtx, 0); if (err < 0) { throw alvr::AvException("Failed to create a CUDA device:", err); } @@ -199,17 +200,19 @@ void alvr::EncodePipelineNvEnc::PushFrame(uint64_t targetTimestampNs, bool idr) timelineInfo.signalSemaphoreValueCount = 1; timelineInfo.pSignalSemaphoreValues = &vkf->sem_value[0]; - VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + vk::PipelineStageFlags waitStage = vk::PipelineStageFlagBits::eBottomOfPipe; - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + vk::SubmitInfo submitInfo = {}; + // submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = &timelineInfo; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &r->GetOutput().semaphore; + // submitInfo.waitSemaphoreCount = 1; + // submitInfo.pWaitSemaphores = &r->GetOutput().semaphore; submitInfo.pWaitDstStageMask = &waitStage; submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &vkf->sem[0]; - VK_CHECK(vkQueueSubmit(r->m_queue, 1, &submitInfo, nullptr)); + // submitInfo.pSignalSemaphores = &vkf->sem[0]; + submitInfo.pSignalSemaphores = reinterpret_cast(&vkf->sem[0]); + // VK_CHECK(vkQueueSubmit(r->m_queue, 1, &submitInfo, nullptr)); + v_ctx.useQueue([&](vk::Queue& queue) { queue.submit(submitInfo); }); int err = av_hwframe_get_buffer(encoder_ctx->hw_frames_ctx, hw_frame, 0); if (err < 0) { diff --git a/alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.h b/alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.h index 94a381fd34..d79aae43ec 100644 --- a/alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.h +++ b/alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.h @@ -1,6 +1,8 @@ #pragma once #include "EncodePipeline.h" +#include "VkContext.hpp" +#include "ffmpeg_helper.h" #include #include @@ -17,9 +19,9 @@ class EncodePipelineNvEnc : public EncodePipeline { ~EncodePipelineNvEnc(); EncodePipelineNvEnc( Renderer* render, - VkContext& vk_ctx, + HWContext& vk_ctx, + VkContext& v_ctx, VkFrame& input_frame, - VkImageCreateInfo& image_create_info, uint32_t width, uint32_t height ); @@ -27,7 +29,7 @@ class EncodePipelineNvEnc : public EncodePipeline { void PushFrame(uint64_t targetTimestampNs, bool idr) override; private: - Renderer* r = nullptr; + VkContext& v_ctx; std::unique_ptr vk_frame_ctx; AVBufferRef* hw_ctx = nullptr; std::unique_ptr> vk_frame; diff --git a/alvr/server_openvr/cpp/platform/linux/EncodePipelineSW.cpp b/alvr/server_openvr/cpp/platform/linux/EncodePipelineSW.cpp deleted file mode 100644 index 646a2db2d7..0000000000 --- a/alvr/server_openvr/cpp/platform/linux/EncodePipelineSW.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "EncodePipelineSW.h" - -#include - -#include "FormatConverter.h" -#include "alvr_server/Logger.h" -#include "alvr_server/Settings.h" - -namespace { - -void x264_log(void*, int level, const char* fmt, va_list args) { - char buf[256]; - vsnprintf(buf, sizeof(buf), fmt, args); - switch (level) { - case X264_LOG_ERROR: - Error("x264: %s", buf); - break; - case X264_LOG_WARNING: - Warn("x264: %s", buf); - break; - case X264_LOG_INFO: - Info("x264: %s", buf); - break; - case X264_LOG_DEBUG: - Debug("x264: %s", buf); - break; - default: - break; - } -} - -} - -alvr::EncodePipelineSW::EncodePipelineSW(Renderer* render, uint32_t width, uint32_t height) { - const auto& settings = Settings::Instance(); - - x264_param_default_preset(¶m, "ultrafast", "zerolatency"); - - param.pf_log = x264_log; - param.i_log_level = X264_LOG_INFO; - - param.b_aud = 0; - param.b_cabac = settings.m_entropyCoding == ALVR_CABAC; - param.b_sliced_threads = true; - param.i_threads = settings.m_swThreadCount; - param.i_width = width; - param.i_height = height; - param.rc.i_rc_method = X264_RC_ABR; - - switch (settings.m_h264Profile) { - case ALVR_H264_PROFILE_BASELINE: - x264_param_apply_profile(¶m, "baseline"); - break; - case ALVR_H264_PROFILE_MAIN: - x264_param_apply_profile(¶m, "main"); - break; - default: - case ALVR_H264_PROFILE_HIGH: - x264_param_apply_profile(¶m, "high"); - break; - } - - auto params = FfiDynamicEncoderParams {}; - params.updated = true; - params.bitrate_bps = 30'000'000; - params.framerate = Settings::Instance().m_refreshRate; - SetParams(params); - - enc = x264_encoder_open(¶m); - if (!enc) { - throw std::runtime_error("Failed to open encoder"); - } - - x264_picture_init(&picture); - picture.img.i_csp = X264_CSP_I420; - picture.img.i_plane = 3; - - x264_picture_init(&picture_out); - - rgbtoyuv = new RgbToYuv420( - render, - render->GetOutput().image, - render->GetOutput().imageInfo, - render->GetOutput().semaphore - ); -} - -alvr::EncodePipelineSW::~EncodePipelineSW() { - if (rgbtoyuv) { - delete rgbtoyuv; - } - if (enc) { - x264_encoder_close(enc); - } -} - -void alvr::EncodePipelineSW::PushFrame(uint64_t targetTimestampNs, bool idr) { - rgbtoyuv->Convert(picture.img.plane, picture.img.i_stride); - rgbtoyuv->Sync(); - timestamp.cpu = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch() - ) - .count(); - - picture.i_type = idr ? X264_TYPE_IDR : X264_TYPE_AUTO; - pts = picture.i_pts = targetTimestampNs; - is_idr = idr; - - int nnal = 0; - nal_size = x264_encoder_encode(enc, &nal, &nnal, &picture, &picture_out); - if (nal_size < 0) { - throw std::runtime_error("x264 encoder_encode failed"); - } -} - -bool alvr::EncodePipelineSW::GetEncoded(FramePacket& packet) { - if (!nal) { - return false; - } - packet.size = nal_size; - packet.data = nal[0].p_payload; - packet.pts = pts; - packet.isIDR = is_idr; - return packet.size > 0; -} - -void alvr::EncodePipelineSW::SetParams(FfiDynamicEncoderParams params) { - if (!params.updated) { - return; - } - // x264 doesn't work well with adaptive bitrate/fps - param.i_fps_num = Settings::Instance().m_refreshRate; - param.i_fps_den = 1; - param.rc.i_bitrate - = params.bitrate_bps / 1'000 * 1.4; // needs higher value to hit target bitrate - param.rc.i_vbv_buffer_size = param.rc.i_bitrate / param.i_fps_num * 1.1; - param.rc.i_vbv_max_bitrate = param.rc.i_bitrate; - param.rc.f_vbv_buffer_init = 0.75; - if (enc) { - x264_encoder_reconfig(enc, ¶m); - } -} - -int alvr::EncodePipelineSW::GetCodec() { return ALVR_CODEC_H264; } diff --git a/alvr/server_openvr/cpp/platform/linux/EncodePipelineSW.h b/alvr/server_openvr/cpp/platform/linux/EncodePipelineSW.h deleted file mode 100644 index 5bfb93afd4..0000000000 --- a/alvr/server_openvr/cpp/platform/linux/EncodePipelineSW.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "EncodePipeline.h" - -#include - -class FormatConverter; - -namespace alvr { - -class EncodePipelineSW : public EncodePipeline { -public: - ~EncodePipelineSW(); - EncodePipelineSW(Renderer* render, uint32_t width, uint32_t height); - - void PushFrame(uint64_t targetTimestampNs, bool idr) override; - bool GetEncoded(FramePacket& packet) override; - void SetParams(FfiDynamicEncoderParams params) override; - int GetCodec() override; - -private: - x264_t* enc = nullptr; - x264_param_t param; - x264_picture_t picture; - x264_picture_t picture_out; - x264_nal_t* nal = nullptr; - int nal_size = 0; - int64_t pts = 0; - bool is_idr = false; - FormatConverter* rgbtoyuv = nullptr; -}; -} diff --git a/alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.cpp b/alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.cpp index 194d28b923..91670bbcdc 100644 --- a/alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.cpp +++ b/alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.cpp @@ -1,5 +1,6 @@ #include "EncodePipelineVAAPI.h" #include "ALVR-common/packet_types.h" +#include "VkContext.hpp" #include "alvr_server/Logger.h" #include "alvr_server/Settings.h" #include "ffmpeg_helper.h" @@ -14,6 +15,8 @@ extern "C" { #include } +using alvr::Vendor; + namespace { const char* encoder(ALVR_CODEC codec) { @@ -98,7 +101,7 @@ map_frame(AVBufferRef* hw_frames_ref, AVBufferRef* drm_device_ctx, alvr::VkFrame } // Import VA surface -AVFrame* import_frame(AVBufferRef* hw_frames_ref, DrmImage& drm) { +AVFrame* import_frame(AVBufferRef* hw_frames_ref, alvr::DrmImage& drm) { AVFrame* va_frame = av_frame_alloc(); int err = av_hwframe_get_buffer(hw_frames_ref, va_frame, 0); if (err < 0) { @@ -128,9 +131,13 @@ AVFrame* import_frame(AVBufferRef* hw_frames_ref, DrmImage& drm) { } alvr::EncodePipelineVAAPI::EncodePipelineVAAPI( - Renderer* render, VkContext& vk_ctx, VkFrame& input_frame, uint32_t width, uint32_t height -) - : r(render) { + alvr::HWContext& vk_ctx, + std::string devicePath, + alvr::Vendor vendor, + VkFrame& input_frame, + uint32_t width, + uint32_t height +) { /* VAAPI Encoding pipeline * The encoding pipeline has 3 frame types: * - input vulkan frames, only used to initialize the mapped frames @@ -142,7 +149,7 @@ alvr::EncodePipelineVAAPI::EncodePipelineVAAPI( * and the encoder that takes the converted frame and produces packets. */ int err = av_hwdevice_ctx_create( - &hw_ctx, AV_HWDEVICE_TYPE_VAAPI, vk_ctx.devicePath.c_str(), NULL, 0 + &hw_ctx, AV_HWDEVICE_TYPE_VAAPI, devicePath.c_str(), NULL, 0 ); if (err < 0) { throw alvr::AvException("Failed to create a VAAPI device:", err); @@ -241,30 +248,30 @@ alvr::EncodePipelineVAAPI::EncodePipelineVAAPI( // quality by allocating more bits to smooth areas switch (settings.m_encoderQualityPreset) { case ALVR_QUALITY: - if (vk_ctx.amd) { + if (vendor == Vendor::Amd) { quality.preset_mode = PRESET_MODE_QUALITY; encoder_ctx->compression_level = quality.quality; // (QUALITY preset, no pre-encoding, vbaq) - } else if (vk_ctx.intel) { + } else if (vendor == Vendor::Intel) { encoder_ctx->compression_level = 1; } break; case ALVR_BALANCED: - if (vk_ctx.amd) { + if (vendor == Vendor::Amd) { quality.preset_mode = PRESET_MODE_BALANCE; encoder_ctx->compression_level = quality.quality; // (BALANCE preset, no pre-encoding, vbaq) - } else if (vk_ctx.intel) { + } else if (vendor == Vendor::Intel) { encoder_ctx->compression_level = 4; } break; case ALVR_SPEED: default: - if (vk_ctx.amd) { + if (vendor == Vendor::Amd) { quality.preset_mode = PRESET_MODE_SPEED; encoder_ctx->compression_level = quality.quality; // (speed preset, no pre-encoding, vbaq) - } else if (vk_ctx.intel) { + } else if (vendor == Vendor::Intel) { encoder_ctx->compression_level = 7; } break; @@ -295,13 +302,14 @@ alvr::EncodePipelineVAAPI::EncodePipelineVAAPI( } encoder_frame = av_frame_alloc(); - if (vk_ctx.intel || getenv("ALVR_VAAPI_IMPORT_SURFACE")) { + if (vendor == Vendor::Intel || getenv("ALVR_VAAPI_IMPORT_SURFACE")) { Info("Importing VA surface"); DrmImage drm; mapped_frame = import_frame(hw_frames_ref, drm); - r->ImportOutput(drm); + // r->ImportOutput(drm); } else { mapped_frame = map_frame(hw_frames_ref, drm_ctx, input_frame); + // std::cout << "mapped frame" << std::endl; } filter_graph = avfilter_graph_alloc(); @@ -309,6 +317,7 @@ alvr::EncodePipelineVAAPI::EncodePipelineVAAPI( AVFilterInOut* outputs = avfilter_inout_alloc(); AVFilterInOut* inputs = avfilter_inout_alloc(); + // TODO: Respect colorspace std::stringstream buffer_filter_args; buffer_filter_args << "video_size=" << mapped_frame->width << "x" << mapped_frame->height; buffer_filter_args << ":pix_fmt=" << mapped_frame->format; @@ -383,7 +392,6 @@ alvr::EncodePipelineVAAPI::~EncodePipelineVAAPI() { } void alvr::EncodePipelineVAAPI::PushFrame(uint64_t targetTimestampNs, bool idr) { - r->Sync(); timestamp.cpu = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch() ) diff --git a/alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.h b/alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.h index a27706eb0d..1977ffdb34 100644 --- a/alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.h +++ b/alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.h @@ -1,6 +1,7 @@ #pragma once #include "EncodePipeline.h" +#include "ffmpeg_helper.h" extern "C" struct AVBufferRef; extern "C" struct AVCodecContext; @@ -22,14 +23,20 @@ class EncodePipelineVAAPI : public EncodePipeline { public: ~EncodePipelineVAAPI(); EncodePipelineVAAPI( - Renderer* render, VkContext& vk_ctx, VkFrame& input_frame, uint32_t width, uint32_t height + HWContext& vk_ctx, + std::string devicePath, + alvr::Vendor vendor, + VkFrame& input_frame, + uint32_t width, + uint32_t height ); - + + // TODO: Don't pass the timestamp here we're literally just passing it through (or does it help + // with dumped videos?) void PushFrame(uint64_t targetTimestampNs, bool idr) override; void SetParams(FfiDynamicEncoderParams params) override; private: - Renderer* r = nullptr; AVBufferRef* hw_ctx = nullptr; AVBufferRef* drm_ctx = nullptr; AVFrame* mapped_frame = nullptr; diff --git a/alvr/server_openvr/cpp/platform/linux/Encoder.hpp b/alvr/server_openvr/cpp/platform/linux/Encoder.hpp new file mode 100644 index 0000000000..636b1fb79d --- /dev/null +++ b/alvr/server_openvr/cpp/platform/linux/Encoder.hpp @@ -0,0 +1,275 @@ +#pragma once + +#include +#include +#include + +#include "EncodePipeline.h" +#include "Renderer.hpp" + +#include "alvr_server/IDRScheduler.h" +#include "alvr_server/Settings.h" +#include "alvr_server/bindings.h" +#include "alvr_server/Logger.h" + +namespace alvr { + +// TODO: Move to source file +template +concept floats = (std::is_same_v && ...); + +template +auto makeSpecs(T... args) + requires floats +{ + render::PipelineCreateInfo info {}; + + constexpr auto Size = sizeof(float); + u32 index = 0; + + // NOTE: This only works with little endian (but what pcvr target is big endian anyway) + auto put = [&](float data) { + std::array arr; + memcpy(arr.data(), reinterpret_cast(&data), arr.size()); + + info.specData.insert(info.specData.end(), arr.begin(), arr.end()); + info.specs.push_back({ + .constantID = index, + .offset = static_cast(index * Size), + .size = Size, + }); + }; + + (put(args), ...); + + return info; +} + +inline auto makeColorCorrection(Settings const& settings, vk::Extent2D extent) { + // The order of this needs to be kept in sync with the shader! + // clang-format off + auto info = makeSpecs( + (float)extent.width, + (float)extent.height, + settings.m_brightness, + settings.m_contrast + 1.f, + settings.m_saturation + 1.f, + settings.m_gamma, + settings.m_sharpening); + // clang-format on + + info.shaderData = std::vector( + COLOR_SHADER_COMP_SPV_PTR, COLOR_SHADER_COMP_SPV_PTR + COLOR_SHADER_COMP_SPV_LEN + ); + + return info; +} + +inline auto makeFoveation(Settings const& settings, vk::Extent2D extent) { + float targetEyeWidth = (float)extent.width / 2; + float targetEyeHeight = extent.height; + + float centerSizeX = settings.m_foveationCenterSizeX; + float centerSizeY = settings.m_foveationCenterSizeY; + float centerShiftX = settings.m_foveationCenterShiftX; + float centerShiftY = settings.m_foveationCenterShiftY; + float edgeRatioX = settings.m_foveationEdgeRatioX; + float edgeRatioY = settings.m_foveationEdgeRatioY; + + float edgeSizeX = targetEyeWidth - centerSizeX * targetEyeWidth; + float edgeSizeY = targetEyeHeight - centerSizeY * targetEyeHeight; + + float centerSizeXAligned + = 1. - ceil(edgeSizeX / (edgeRatioX * 2.)) * (edgeRatioX * 2.) / targetEyeWidth; + float centerSizeYAligned + = 1. - ceil(edgeSizeY / (edgeRatioY * 2.)) * (edgeRatioY * 2.) / targetEyeHeight; + + float edgeSizeXAligned = targetEyeWidth - centerSizeXAligned * targetEyeWidth; + float edgeSizeYAligned = targetEyeHeight - centerSizeYAligned * targetEyeHeight; + + float centerShiftXAligned = ceil(centerShiftX * edgeSizeXAligned / (edgeRatioX * 2.)) + * (edgeRatioX * 2.) / edgeSizeXAligned; + float centerShiftYAligned = ceil(centerShiftY * edgeSizeYAligned / (edgeRatioY * 2.)) + * (edgeRatioY * 2.) / edgeSizeYAligned; + + float foveationScaleX = (centerSizeXAligned + (1. - centerSizeXAligned) / edgeRatioX); + float foveationScaleY = (centerSizeYAligned + (1. - centerSizeYAligned) / edgeRatioY); + + float optimizedEyeWidth = foveationScaleX * targetEyeWidth; + float optimizedEyeHeight = foveationScaleY * targetEyeHeight; + + // round the frame dimensions to a number of pixel multiple of 32 for the encoder + auto optimizedEyeWidthAligned = (uint32_t)ceil(optimizedEyeWidth / 32.f) * 32; + auto optimizedEyeHeightAligned = (uint32_t)ceil(optimizedEyeHeight / 32.f) * 32; + + float eyeWidthRatioAligned = optimizedEyeWidth / optimizedEyeWidthAligned; + float eyeHeightRatioAligned = optimizedEyeHeight / optimizedEyeHeightAligned; + + vk::Extent2D outSize { + .width = optimizedEyeWidthAligned * 2, + .height = optimizedEyeHeightAligned, + }; + + // The order of this needs to be kept in sync with the shader! + // clang-format off + auto info = makeSpecs( + eyeWidthRatioAligned, + eyeHeightRatioAligned, + centerSizeXAligned, + centerSizeYAligned, + centerShiftXAligned, + centerShiftYAligned, + edgeRatioX, + edgeRatioY); + // clang-format on + + info.shaderData + = std::vector(FFR_SHADER_COMP_SPV_PTR, FFR_SHADER_COMP_SPV_PTR + FFR_SHADER_COMP_SPV_LEN); + + return std::tuple(info, outSize); +} + +class Encoder { + vk::Extent2D outExtent; + + VkContext vkCtx; + + Optional renderer; + + std::unique_ptr encoder; + IDRScheduler idrScheduler; + +public: + // TODO: How are we supposed to match the physical device with direct mode? + Encoder() + : vkCtx(std::vector {}) { } + + void createImages(render::RendererCreateInfo rendererCI) { + if (renderer.hasValue()) { + renderer.get().destroy(vkCtx); + } + + auto const& settings = Settings::Instance(); + + vk::Extent2D inExtent { + .width = rendererCI.inputEyeExtent.width * 2, + .height = rendererCI.inputEyeExtent.height, + }; + + std::vector pipeCIs; + + if (settings.m_enableColorCorrection) + pipeCIs.push_back(makeColorCorrection(settings, inExtent)); + + // NOTE: This needs to be last as it needs to render into the output image + if (settings.m_enableFoveatedEncoding) { + auto [info, newExtent] = makeFoveation(settings, rendererCI.outputExtent); + rendererCI.outputExtent = newExtent; + + pipeCIs.push_back(info); + } + + if (pipeCIs.empty()) { + pipeCIs.push_back({ + .shaderData = std::vector( + QUAD_SHADER_COMP_SPV_PTR, QUAD_SHADER_COMP_SPV_PTR + QUAD_SHADER_COMP_SPV_LEN + ), + }); + } + + outExtent = rendererCI.outputExtent; + renderer.emplace(vkCtx, rendererCI, pipeCIs); + } + + void initEncoding() { + auto const& settings = Settings::Instance(); + + VkPhysicalDeviceDrmPropertiesEXT drmProps = {}; + drmProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT; + + VkPhysicalDeviceProperties2 deviceProps = {}; + deviceProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + deviceProps.pNext = &drmProps; + vkGetPhysicalDeviceProperties2(vkCtx.physDev, &deviceProps); + + std::string devicePath; + for (int i = 128; i < 136; ++i) { + auto path = "/dev/dri/renderD" + std::to_string(i); + int fd = open(path.c_str(), O_RDONLY); + if (fd == -1) { + continue; + } + struct stat s = {}; + int ret = fstat(fd, &s); + close(fd); + if (ret != 0) { + continue; + } + dev_t primaryDev = makedev(drmProps.primaryMajor, drmProps.primaryMinor); + dev_t renderDev = makedev(drmProps.renderMajor, drmProps.renderMinor); + if (primaryDev == s.st_rdev || renderDev == s.st_rdev) { + devicePath = path; + break; + } + } + if (devicePath.empty()) { + devicePath = "/dev/dri/renderD128"; + } + Info("Using device path %s", devicePath.c_str()); + + // av_log_set_level(AV_LOG_DEBUG); + + // TODO: Fix this memory leakage + + // TODO: The EncodePipeline should store this on it's own + auto& avHwCtx = *new alvr::HWContext(vkCtx); + + auto out = renderer.get().getOutput(); + + // TODO: Fix Nvidia + + // auto framCtx = new alvr::VkFrameCtx(aCtx, *(vk::ImageCreateInfo*)&out.imageCI); + + auto frame = new alvr::VkFrame( + vkCtx, out.image.image, out.imageCI, out.size, out.image.memory, out.drm + ); + + encoder + = EncodePipeline::Create(vkCtx, devicePath, *frame, outExtent.width, outExtent.height); + + idrScheduler.OnStreamStart(); + } + + void present(u32 leftIdx, u32 rightIdx, u64 targetTimestampNs) { + ReportPresent(targetTimestampNs, 0); + renderer.get().render(vkCtx, leftIdx, rightIdx); + ReportComposed(targetTimestampNs, 0); + + encoder->PushFrame(0, idrScheduler.CheckIDRInsertion()); + + alvr::FramePacket framePacket; + if (!encoder->GetEncoded(framePacket)) { + assert(false); + } + + ParseFrameNals( + encoder->GetCodec(), + framePacket.data, + framePacket.size, + targetTimestampNs, + framePacket.isIDR + ); + } + + void requestIdr() { idrScheduler.InsertIDR(); } + + ~Encoder() { + if (renderer.hasValue()) { + renderer.get().destroy(vkCtx); + } + + vkCtx.destroy(); + } +}; + +} diff --git a/alvr/server_openvr/cpp/platform/linux/FormatConverter.cpp b/alvr/server_openvr/cpp/platform/linux/FormatConverter.cpp deleted file mode 100644 index d8b447fe93..0000000000 --- a/alvr/server_openvr/cpp/platform/linux/FormatConverter.cpp +++ /dev/null @@ -1,323 +0,0 @@ -#include "FormatConverter.h" -#include "alvr_server/bindings.h" - -FormatConverter::FormatConverter(Renderer* render) - : r(render) { } - -FormatConverter::~FormatConverter() { - for (const OutputImage& image : m_images) { - vkUnmapMemory(r->m_dev, image.memory); - vkDestroyImageView(r->m_dev, image.view, nullptr); - vkDestroyImage(r->m_dev, image.image, nullptr); - vkFreeMemory(r->m_dev, image.memory, nullptr); - } - - vkDestroySemaphore(r->m_dev, m_output.semaphore, nullptr); - - vkDestroyQueryPool(r->m_dev, m_queryPool, nullptr); - vkDestroyDescriptorSetLayout(r->m_dev, m_descriptorLayout, nullptr); - vkDestroyImageView(r->m_dev, m_view, nullptr); - vkDestroyShaderModule(r->m_dev, m_shader, nullptr); - vkDestroyPipeline(r->m_dev, m_pipeline, nullptr); - vkDestroyPipelineLayout(r->m_dev, m_pipelineLayout, nullptr); -} - -void FormatConverter::init( - VkImage image, - VkImageCreateInfo imageCreateInfo, - VkSemaphore semaphore, - int count, - const unsigned char* shaderData, - unsigned shaderLen -) { - m_images.resize(count); - m_semaphore = semaphore; - - // Timestamp query - VkQueryPoolCreateInfo queryPoolInfo = {}; - queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; - queryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP; - queryPoolInfo.queryCount = 1; - VK_CHECK(vkCreateQueryPool(r->m_dev, &queryPoolInfo, nullptr, &m_queryPool)); - - // Command buffer - VkCommandBufferAllocateInfo commandBufferInfo = {}; - commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - commandBufferInfo.commandPool = r->m_commandPool; - commandBufferInfo.commandBufferCount = 1; - VK_CHECK(vkAllocateCommandBuffers(r->m_dev, &commandBufferInfo, &m_commandBuffer)); - - // Descriptors - VkDescriptorSetLayoutBinding descriptorBindings[2]; - descriptorBindings[0] = {}; - descriptorBindings[0].binding = 0; - descriptorBindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - descriptorBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - descriptorBindings[0].descriptorCount = 1; - descriptorBindings[1] = {}; - descriptorBindings[1].binding = 1; - descriptorBindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - descriptorBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - descriptorBindings[1].descriptorCount = count; - - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {}; - descriptorSetLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - descriptorSetLayoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR; - descriptorSetLayoutInfo.bindingCount = 2; - descriptorSetLayoutInfo.pBindings = descriptorBindings; - VK_CHECK(vkCreateDescriptorSetLayout( - r->m_dev, &descriptorSetLayoutInfo, nullptr, &m_descriptorLayout - )); - - // Input image - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = imageCreateInfo.format; - viewInfo.image = image; - viewInfo.subresourceRange = {}; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - VK_CHECK(vkCreateImageView(r->m_dev, &viewInfo, nullptr, &m_view)); - - // Output images - for (int i = 0; i < count; ++i) { - VkImageCreateInfo imageInfo = {}; - imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.format = VK_FORMAT_R8_UNORM; - imageInfo.extent.width = imageCreateInfo.extent.width; - imageInfo.extent.height = imageCreateInfo.extent.height; - imageInfo.extent.depth = 1; - imageInfo.arrayLayers = 1; - imageInfo.mipLevels = 1; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.tiling = VK_IMAGE_TILING_LINEAR; - imageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT; - VK_CHECK(vkCreateImage(r->m_dev, &imageInfo, nullptr, &m_images[i].image)); - - VkMemoryRequirements memReqs; - VkMemoryAllocateInfo memAllocInfo {}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - vkGetImageMemoryRequirements(r->m_dev, m_images[i].image, &memReqs); - memAllocInfo.allocationSize = memReqs.size; - - VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT - | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - memAllocInfo.memoryTypeIndex = r->memoryTypeIndex(memType, memReqs.memoryTypeBits); - VK_CHECK(vkAllocateMemory(r->m_dev, &memAllocInfo, nullptr, &m_images[i].memory)); - VK_CHECK(vkBindImageMemory(r->m_dev, m_images[i].image, m_images[i].memory, 0)); - - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = imageInfo.format; - viewInfo.image = m_images[i].image; - viewInfo.subresourceRange = {}; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - VK_CHECK(vkCreateImageView(r->m_dev, &viewInfo, nullptr, &m_images[i].view)); - - VkImageMemoryBarrier imageBarrier = {}; - imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - imageBarrier.image = m_images[i].image; - imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBarrier.subresourceRange.layerCount = 1; - imageBarrier.subresourceRange.levelCount = 1; - imageBarrier.srcAccessMask = 0; - imageBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - - r->commandBufferBegin(); - vkCmdPipelineBarrier( - r->m_commandBuffer, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, - 0, - nullptr, - 0, - nullptr, - 1, - &imageBarrier - ); - r->commandBufferSubmit(); - - VkImageSubresource subresource = {}; - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - VkSubresourceLayout layout; - vkGetImageSubresourceLayout(r->m_dev, m_images[i].image, &subresource, &layout); - - m_images[i].linesize = layout.rowPitch; - VK_CHECK(vkMapMemory( - r->m_dev, - m_images[i].memory, - 0, - VK_WHOLE_SIZE, - 0, - reinterpret_cast(&m_images[i].mapped) - )); - } - - VkSemaphoreCreateInfo semInfo = {}; - semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - VK_CHECK(vkCreateSemaphore(r->m_dev, &semInfo, nullptr, &m_output.semaphore)); - - // Shader - VkShaderModuleCreateInfo moduleInfo = {}; - moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - moduleInfo.codeSize = shaderLen; - moduleInfo.pCode = (uint32_t*)shaderData; - VK_CHECK(vkCreateShaderModule(r->m_dev, &moduleInfo, nullptr, &m_shader)); - - // Pipeline - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = &m_descriptorLayout; - VK_CHECK(vkCreatePipelineLayout(r->m_dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout)); - - VkPipelineShaderStageCreateInfo stageInfo = {}; - stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; - stageInfo.pName = "main"; - stageInfo.module = m_shader; - - VkComputePipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - pipelineInfo.layout = m_pipelineLayout; - pipelineInfo.stage = stageInfo; - VK_CHECK(vkCreateComputePipelines(r->m_dev, nullptr, 1, &pipelineInfo, nullptr, &m_pipeline)); - - m_groupCountX = (imageCreateInfo.extent.width + 7) / 8; - m_groupCountY = (imageCreateInfo.extent.height + 7) / 8; -} - -void FormatConverter::Convert(uint8_t** data, int* linesize) { - VkCommandBufferBeginInfo commandBufferBegin = {}; - commandBufferBegin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VK_CHECK(vkBeginCommandBuffer(m_commandBuffer, &commandBufferBegin)); - - vkCmdResetQueryPool(m_commandBuffer, m_queryPool, 0, 1); - - vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline); - - std::vector descriptorWriteSets; - - VkDescriptorImageInfo descriptorImageInfoIn = {}; - descriptorImageInfoIn.imageView = m_view; - descriptorImageInfoIn.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - - VkWriteDescriptorSet descriptorWriteSet = {}; - descriptorWriteSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWriteSet.descriptorCount = 1; - descriptorWriteSet.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - descriptorWriteSet.pImageInfo = &descriptorImageInfoIn; - descriptorWriteSet.dstBinding = 0; - descriptorWriteSets.push_back(descriptorWriteSet); - - VkDescriptorImageInfo descriptorImageInfoOuts[3] = {}; - for (size_t i = 0; i < m_images.size(); ++i) { - descriptorImageInfoOuts[i].imageView = m_images[i].view; - descriptorImageInfoOuts[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - - descriptorWriteSet.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - descriptorWriteSet.pImageInfo = &descriptorImageInfoOuts[i]; - descriptorWriteSet.dstBinding = 1; - descriptorWriteSet.dstArrayElement = i; - descriptorWriteSets.push_back(descriptorWriteSet); - } - - r->d.vkCmdPushDescriptorSetKHR( - m_commandBuffer, - VK_PIPELINE_BIND_POINT_COMPUTE, - m_pipelineLayout, - 0, - descriptorWriteSets.size(), - descriptorWriteSets.data() - ); - - vkCmdDispatch(m_commandBuffer, m_groupCountX, m_groupCountY, 1); - - vkCmdWriteTimestamp(m_commandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, m_queryPool, 0); - - vkEndCommandBuffer(m_commandBuffer); - - VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &m_semaphore; - submitInfo.pWaitDstStageMask = &waitStage; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &m_output.semaphore; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &m_commandBuffer; - VK_CHECK(vkQueueSubmit(r->m_queue, 1, &submitInfo, nullptr)); - - for (size_t i = 0; i < m_images.size(); ++i) { - data[i] = m_images[i].mapped; - linesize[i] = m_images[i].linesize; - } -} - -void FormatConverter::Sync() { - VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &m_output.semaphore; - submitInfo.pWaitDstStageMask = &waitStage; - VK_CHECK(vkQueueSubmit(r->m_queue, 1, &submitInfo, r->m_fence)); - - VK_CHECK(vkWaitForFences(r->m_dev, 1, &r->m_fence, VK_TRUE, UINT64_MAX)); - VK_CHECK(vkResetFences(r->m_dev, 1, &r->m_fence)); -} - -uint64_t FormatConverter::GetTimestamp() { - uint64_t query; - VK_CHECK(vkGetQueryPoolResults( - r->m_dev, - m_queryPool, - 0, - 1, - sizeof(uint64_t), - &query, - sizeof(uint64_t), - VK_QUERY_RESULT_64_BIT - )); - return query * r->m_timestampPeriod; -} - -RgbToYuv420::RgbToYuv420( - Renderer* render, VkImage image, VkImageCreateInfo imageInfo, VkSemaphore semaphore -) - : FormatConverter(render) { - init( - image, - imageInfo, - semaphore, - 3, - RGBTOYUV420_SHADER_COMP_SPV_PTR, - RGBTOYUV420_SHADER_COMP_SPV_LEN - ); -} diff --git a/alvr/server_openvr/cpp/platform/linux/FormatConverter.h b/alvr/server_openvr/cpp/platform/linux/FormatConverter.h deleted file mode 100644 index 539ba8fc26..0000000000 --- a/alvr/server_openvr/cpp/platform/linux/FormatConverter.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "Renderer.h" - -class FormatConverter { -public: - struct Output { - VkSemaphore semaphore = VK_NULL_HANDLE; - }; - - virtual ~FormatConverter(); - - Output GetOutput(); - - void Convert(uint8_t** data, int* linesize); - - void Sync(); - - uint64_t GetTimestamp(); - -protected: - struct OutputImage { - VkImage image = VK_NULL_HANDLE; - VkDeviceMemory memory = VK_NULL_HANDLE; - VkImageView view = VK_NULL_HANDLE; - VkSemaphore semaphore = VK_NULL_HANDLE; - VkDeviceSize linesize = 0; - uint8_t* mapped = nullptr; - }; - - explicit FormatConverter(Renderer* render); - void init( - VkImage image, - VkImageCreateInfo imageCreateInfo, - VkSemaphore semaphore, - int count, - const unsigned char* shaderData, - unsigned shaderLen - ); - - Renderer* r; - VkQueryPool m_queryPool = VK_NULL_HANDLE; - VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE; - VkDescriptorSetLayout m_descriptorLayout = VK_NULL_HANDLE; - VkImageView m_view = VK_NULL_HANDLE; - VkSemaphore m_semaphore = VK_NULL_HANDLE; - VkShaderModule m_shader = VK_NULL_HANDLE; - VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; - VkPipeline m_pipeline = VK_NULL_HANDLE; - uint32_t m_groupCountX = 0; - uint32_t m_groupCountY = 0; - std::vector m_images; - Output m_output; -}; - -class RgbToYuv420 : public FormatConverter { -public: - explicit RgbToYuv420( - Renderer* render, VkImage image, VkImageCreateInfo imageInfo, VkSemaphore semaphore - ); -}; diff --git a/alvr/server_openvr/cpp/platform/linux/FrameRender.cpp b/alvr/server_openvr/cpp/platform/linux/FrameRender.cpp deleted file mode 100644 index 045a1e9f87..0000000000 --- a/alvr/server_openvr/cpp/platform/linux/FrameRender.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include "FrameRender.h" -#include "alvr_server/Logger.h" -#include "alvr_server/Settings.h" -#include "alvr_server/bindings.h" - -#include -#include - -FrameRender::FrameRender(alvr::VkContext& ctx, init_packet& init, int fds[]) - : Renderer( - ctx.get_vk_instance(), - ctx.get_vk_device(), - ctx.get_vk_phys_device(), - ctx.get_vk_queue_family_index(), - ctx.get_vk_device_extensions() - ) { - m_quadShaderSize = QUAD_SHADER_COMP_SPV_LEN; - m_quadShaderCode = reinterpret_cast(QUAD_SHADER_COMP_SPV_PTR); - - Startup( - init.image_create_info.extent.width, - init.image_create_info.extent.height, - init.image_create_info.format - ); - - for (size_t i = 0; i < 3; ++i) { - AddImage(init.image_create_info, init.mem_index, fds[2 * i], fds[2 * i + 1]); - } - - m_width = Settings::Instance().m_renderWidth; - m_height = Settings::Instance().m_renderHeight; - - Info("FrameRender: Input size %ux%u", m_width, m_height); - - if (Settings::Instance().m_force_sw_encoding) { - m_handle = ExternalHandle::None; - } else if (ctx.amd || ctx.intel) { - m_handle = ExternalHandle::DmaBuf; - } else if (ctx.nvidia) { - m_handle = ExternalHandle::OpaqueFd; - } - - setupCustomShaders("pre"); - - if (Settings::Instance().m_enableColorCorrection) { - setupColorCorrection(); - } - - if (Settings::Instance().m_enableFoveatedEncoding) { - setupFoveatedRendering(); - } - - setupCustomShaders("post"); - - if (m_pipelines.empty()) { - RenderPipeline* pipeline = new RenderPipeline(this); - pipeline->SetShader(QUAD_SHADER_COMP_SPV_PTR, QUAD_SHADER_COMP_SPV_LEN); - m_pipelines.push_back(pipeline); - AddPipeline(pipeline); - } - - Info("FrameRender: Output size %ux%u", m_width, m_height); -} - -FrameRender::~FrameRender() { - for (RenderPipeline* pipeline : m_pipelines) { - delete pipeline; - } -} - -FrameRender::Output FrameRender::CreateOutput() { - Renderer::CreateOutput(m_width, m_height, m_handle); - return GetOutput(); -} - -uint32_t FrameRender::GetEncodingWidth() const { return m_width; } - -uint32_t FrameRender::GetEncodingHeight() const { return m_height; } - -void FrameRender::setupColorCorrection() { - std::vector entries; - -#define ENTRY(x, v) \ - m_colorCorrectionConstants.x = v; \ - entries.push_back( \ - { (uint32_t)entries.size(), offsetof(ColorCorrection, x), sizeof(ColorCorrection::x) } \ - ); - - ENTRY(renderWidth, m_width); - ENTRY(renderHeight, m_height); - ENTRY(brightness, Settings::Instance().m_brightness); - ENTRY(contrast, Settings::Instance().m_contrast + 1.f); - ENTRY(saturation, Settings::Instance().m_saturation + 1.f); - ENTRY(gamma, Settings::Instance().m_gamma); - ENTRY(sharpening, Settings::Instance().m_sharpening); -#undef ENTRY - - RenderPipeline* pipeline = new RenderPipeline(this); - pipeline->SetShader(COLOR_SHADER_COMP_SPV_PTR, COLOR_SHADER_COMP_SPV_LEN); - pipeline->SetConstants(&m_colorCorrectionConstants, std::move(entries)); - m_pipelines.push_back(pipeline); - AddPipeline(pipeline); -} - -void FrameRender::setupFoveatedRendering() { - float targetEyeWidth = (float)m_width / 2; - float targetEyeHeight = (float)m_height; - - float centerSizeX = (float)Settings::Instance().m_foveationCenterSizeX; - float centerSizeY = (float)Settings::Instance().m_foveationCenterSizeY; - float centerShiftX = (float)Settings::Instance().m_foveationCenterShiftX; - float centerShiftY = (float)Settings::Instance().m_foveationCenterShiftY; - float edgeRatioX = (float)Settings::Instance().m_foveationEdgeRatioX; - float edgeRatioY = (float)Settings::Instance().m_foveationEdgeRatioY; - - float edgeSizeX = targetEyeWidth - centerSizeX * targetEyeWidth; - float edgeSizeY = targetEyeHeight - centerSizeY * targetEyeHeight; - - float centerSizeXAligned - = 1. - ceil(edgeSizeX / (edgeRatioX * 2.)) * (edgeRatioX * 2.) / targetEyeWidth; - float centerSizeYAligned - = 1. - ceil(edgeSizeY / (edgeRatioY * 2.)) * (edgeRatioY * 2.) / targetEyeHeight; - - float edgeSizeXAligned = targetEyeWidth - centerSizeXAligned * targetEyeWidth; - float edgeSizeYAligned = targetEyeHeight - centerSizeYAligned * targetEyeHeight; - - float centerShiftXAligned = ceil(centerShiftX * edgeSizeXAligned / (edgeRatioX * 2.)) - * (edgeRatioX * 2.) / edgeSizeXAligned; - float centerShiftYAligned = ceil(centerShiftY * edgeSizeYAligned / (edgeRatioY * 2.)) - * (edgeRatioY * 2.) / edgeSizeYAligned; - - float foveationScaleX = (centerSizeXAligned + (1. - centerSizeXAligned) / edgeRatioX); - float foveationScaleY = (centerSizeYAligned + (1. - centerSizeYAligned) / edgeRatioY); - - float optimizedEyeWidth = foveationScaleX * targetEyeWidth; - float optimizedEyeHeight = foveationScaleY * targetEyeHeight; - - // round the frame dimensions to a number of pixel multiple of 32 for the encoder - auto optimizedEyeWidthAligned = (uint32_t)ceil(optimizedEyeWidth / 32.f) * 32; - auto optimizedEyeHeightAligned = (uint32_t)ceil(optimizedEyeHeight / 32.f) * 32; - - float eyeWidthRatioAligned = optimizedEyeWidth / optimizedEyeWidthAligned; - float eyeHeightRatioAligned = optimizedEyeHeight / optimizedEyeHeightAligned; - - m_width = optimizedEyeWidthAligned * 2; - m_height = optimizedEyeHeightAligned; - - std::vector entries; - -#define ENTRY(x, v) \ - m_foveatedRenderingConstants.x = v; \ - entries.push_back( \ - { (uint32_t)entries.size(), offsetof(FoveationVars, x), sizeof(FoveationVars::x) } \ - ); - - ENTRY(eyeWidthRatio, eyeWidthRatioAligned); - ENTRY(eyeHeightRatio, eyeHeightRatioAligned); - ENTRY(centerSizeX, centerSizeXAligned); - ENTRY(centerSizeY, centerSizeYAligned); - ENTRY(centerShiftX, centerShiftXAligned); - ENTRY(centerShiftY, centerShiftYAligned); - ENTRY(edgeRatioX, edgeRatioX); - ENTRY(edgeRatioY, edgeRatioY); -#undef ENTRY - - RenderPipeline* pipeline = new RenderPipeline(this); - pipeline->SetShader(FFR_SHADER_COMP_SPV_PTR, FFR_SHADER_COMP_SPV_LEN); - pipeline->SetConstants(&m_foveatedRenderingConstants, std::move(entries)); - m_pipelines.push_back(pipeline); - AddPipeline(pipeline); -} - -void FrameRender::setupCustomShaders(const std::string& stage) { - try { - const std::filesystem::path shadersDir - = std::filesystem::path(g_sessionPath).replace_filename("shaders"); - for (const auto& entry : - std::filesystem::directory_iterator(shadersDir / std::filesystem::path(stage))) { - std::ifstream fs(entry.path(), std::ios::binary | std::ios::in); - uint32_t magic = 0; - fs.read((char*)&magic, sizeof(uint32_t)); - if (magic != 0x07230203) { - Warn("FrameRender: Shader file %s is not a SPIR-V file", entry.path().c_str()); - continue; - } - Info( - "FrameRender: Adding [%s] shader %s", stage.c_str(), entry.path().filename().c_str() - ); - RenderPipeline* pipeline = new RenderPipeline(this); - pipeline->SetShader(entry.path().c_str()); - m_pipelines.push_back(pipeline); - AddPipeline(pipeline); - } - } catch (...) { } -} diff --git a/alvr/server_openvr/cpp/platform/linux/FrameRender.h b/alvr/server_openvr/cpp/platform/linux/FrameRender.h deleted file mode 100644 index 9c0acc5afe..0000000000 --- a/alvr/server_openvr/cpp/platform/linux/FrameRender.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "Renderer.h" -#include "ffmpeg_helper.h" -#include "protocol.h" - -class FrameRender : public Renderer { -public: - explicit FrameRender(alvr::VkContext& ctx, init_packet& init, int fds[]); - ~FrameRender(); - - Output CreateOutput(); - uint32_t GetEncodingWidth() const; - uint32_t GetEncodingHeight() const; - -private: - struct ColorCorrection { - float renderWidth; - float renderHeight; - float brightness; - float contrast; - float saturation; - float gamma; - float sharpening; - }; - - struct FoveationVars { - float eyeWidthRatio; - float eyeHeightRatio; - float centerSizeX; - float centerSizeY; - float centerShiftX; - float centerShiftY; - float edgeRatioX; - float edgeRatioY; - }; - - void setupColorCorrection(); - void setupFoveatedRendering(); - void setupCustomShaders(const std::string& stage); - - uint32_t m_width; - uint32_t m_height; - ExternalHandle m_handle = ExternalHandle::None; - ColorCorrection m_colorCorrectionConstants; - FoveationVars m_foveatedRenderingConstants; - std::vector m_pipelines; -}; diff --git a/alvr/server_openvr/cpp/platform/linux/OvrDirectModeComponent.cpp b/alvr/server_openvr/cpp/platform/linux/OvrDirectModeComponent.cpp new file mode 100644 index 0000000000..fd34e83a91 --- /dev/null +++ b/alvr/server_openvr/cpp/platform/linux/OvrDirectModeComponent.cpp @@ -0,0 +1,248 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include +#include + +#include "OvrDirectModeComponent.h" + +#include "alvr_server/Logger.h" + +OvrDirectModeComponent::OvrDirectModeComponent(std::shared_ptr poseHistory) + : m_poseHistory(poseHistory) + , m_submitLayer(0) { } + +void OvrDirectModeComponent::CreateSwapTextureSet( + uint32_t unPid, + const SwapTextureSetDesc_t* pSwapTextureSetDesc, + SwapTextureSet_t* pOutSwapTextureSet +) { + Info( + "CreateSwapTextureSet pid=%d Format=%d %dx%d SampleCount=%d\n", + unPid, + pSwapTextureSetDesc->nFormat, + pSwapTextureSetDesc->nWidth, + pSwapTextureSetDesc->nHeight, + pSwapTextureSetDesc->nSampleCount + ); + + ProcessResource* processResource = new ProcessResource(); + processResource->textDesc = *pSwapTextureSetDesc; + processResource->pid = unPid; + + { + auto pid = getpid(); + Info("VrServer PID %d\n", pid); + } + + for (int i = 0; i < 3; i++) { + vr::SharedTextureHandle_t myHandle = 0; + bool success = vr::VRIPCResourceManager()->NewSharedVulkanImage( + pSwapTextureSetDesc->nFormat, + pSwapTextureSetDesc->nWidth, + pSwapTextureSetDesc->nHeight, + true, + false, + true, + 1, + 1, + &myHandle + ); + + uint64_t ipcHandle = 0; + vr::VRIPCResourceManager()->RefResource(myHandle, &ipcHandle); + + if (!success) { + Error("VRCIPCResourceManager: Failed to create shared texture\n"); + for (int j = 0; j < i; j++) { + vr::VRIPCResourceManager()->UnrefResource(processResource->sharedHandles[j]); + m_handleMap.erase(processResource->sharedHandles[j]); + } + delete processResource; + break; + } + + int fd = 0; + auto ret = vr::VRIPCResourceManager()->ReceiveSharedFd(ipcHandle, &fd); + if (ret == false) { + Error("Failed to get fd for texture\n"); + break; + } + + processResource->fds[i] = fd; + processResource->sharedHandles[i] = myHandle; + + m_handleMap.insert( + std::make_pair(processResource->sharedHandles[i], std::make_pair(processResource, i)) + ); + pOutSwapTextureSet->rSharedTextureHandles[i] = myHandle; + Info("Created Texture %d %p\n", i, processResource->sharedHandles[i]); + } +} + +/** Used to textures created using CreateSwapTextureSet. Only one of the set's handles needs to be + * used to destroy the entire set. */ +void OvrDirectModeComponent::DestroySwapTextureSet(vr::SharedTextureHandle_t sharedTextureHandle) { + Info("DestroySwapTextureSet %p\n", sharedTextureHandle); + + auto id = m_handleMap.find(sharedTextureHandle); + if (id != m_handleMap.end()) { + ProcessResource* p = id->second.first; + + vr::VRIPCResourceManager()->UnrefResource(p->sharedHandles[0]); + vr::VRIPCResourceManager()->UnrefResource(p->sharedHandles[1]); + vr::VRIPCResourceManager()->UnrefResource(p->sharedHandles[2]); + + m_handleMap.erase(p->sharedHandles[0]); + m_handleMap.erase(p->sharedHandles[1]); + m_handleMap.erase(p->sharedHandles[2]); + delete p; + } else { + Debug("Requested to destroy not managing texture. handle:%p\n", sharedTextureHandle); + } +} + +/** Used to purge all texture sets for a given process. */ +void OvrDirectModeComponent::DestroyAllSwapTextureSets(uint32_t unPid) { + Info("DestroyAllSwapTextureSets pid=%d\n", unPid); + for (auto it = m_handleMap.begin(); it != m_handleMap.end();) { + if (it->second.first->pid == unPid) { + if (it->second.second == 0) { + ProcessResource* p = it->second.first; + vr::VRIPCResourceManager()->UnrefResource(p->sharedHandles[0]); + vr::VRIPCResourceManager()->UnrefResource(p->sharedHandles[1]); + vr::VRIPCResourceManager()->UnrefResource(p->sharedHandles[2]); + delete p; + } + m_handleMap.erase(it++); + } else { + ++it; + } + } +} + +/** After Present returns, calls this to get the next index to use for rendering. */ +void OvrDirectModeComponent::GetNextSwapTextureSetIndex( + vr::SharedTextureHandle_t sharedTextureHandles[2], uint32_t (*pIndices)[2] +) { + (*pIndices)[0]++; + (*pIndices)[0] %= 3; + (*pIndices)[1]++; + (*pIndices)[1] %= 3; +} + +/** Call once per layer to draw for this frame. One shared texture handle per eye. Textures must + * be created using CreateSwapTextureSet and should be alternated per frame. Call Present once all + * layers have been submitted. */ +void OvrDirectModeComponent::SubmitLayer(const SubmitLayerPerEye_t (&perEye)[2]) { + m_presentMutex.lock(); + + auto pPose = &perEye[0].mHmdPose; // TODO: are both poses the same? Name HMD suggests yes. + + if (m_submitLayer == 0) { + // Detect FrameIndex of submitted frame by pPose. + // This is important part to achieve smooth headtracking. + // We search for history of TrackingInfo and find the TrackingInfo which have nearest matrix + // value. + + auto pose = m_poseHistory->GetBestPoseMatch(*pPose); + if (pose) { + // found the frameIndex + m_prevTargetTimestampNs = m_targetTimestampNs; + m_targetTimestampNs = pose->targetTimestampNs; + + m_prevFramePoseRotation = m_framePoseRotation; + m_framePoseRotation.x = pose->motion.pose.orientation.x; + m_framePoseRotation.y = pose->motion.pose.orientation.y; + m_framePoseRotation.z = pose->motion.pose.orientation.z; + m_framePoseRotation.w = pose->motion.pose.orientation.w; + } else { + m_targetTimestampNs = 0; + m_framePoseRotation = HmdQuaternion_Init(0.0, 0.0, 0.0, 0.0); + } + } + if (m_submitLayer < MAX_LAYERS) { + m_submitLayers[m_submitLayer][0] = perEye[0]; + m_submitLayers[m_submitLayer][1] = perEye[1]; + m_submitLayer++; + } else { + Warn("Too many layers submitted!\n"); + } + + m_presentMutex.unlock(); +} + +/** Submits queued layers for display. */ +void OvrDirectModeComponent::Present(vr::SharedTextureHandle_t syncTexture) { + m_submitLayer = 0; + + std::optional leftIdx; + std::optional rightIdx; + + for (u32 i = 0; i < layer0Texts.size(); ++i) { + if (layer0Texts[i] == m_submitLayers[0][0].hTexture) + leftIdx = i; + + if (layer0Texts[i] == m_submitLayers[0][1].hTexture) + rightIdx = i; + } + + if (!leftIdx.has_value() || !rightIdx.has_value()) { + auto leftIt = m_handleMap.find(m_submitLayers[0][0].hTexture); + auto rightIt = m_handleMap.find(m_submitLayers[0][1].hTexture); + + if (leftIt == m_handleMap.end() || rightIt == m_handleMap.end()) { + Error( + "Textures not found in handle map %llu, %llu\n", + m_submitLayers[0][0].hTexture, + m_submitLayers[0][1].hTexture + ); + return; + } + + std::array fds; + + for (u32 i = 0; i < 3; ++i) { + layer0Texts[i] = leftIt->second.first->sharedHandles[i]; + fds[i] = leftIt->second.first->fds[i]; + } + for (u32 i = 0; i < 3; ++i) { + layer0Texts[i + 3] = rightIt->second.first->sharedHandles[i]; + fds[i + 3] = rightIt->second.first->fds[i]; + } + + auto const& settings = Settings::Instance(); + + // Hopefully it's the same for both eyes in a layer + auto& desc = leftIt->second.first->textDesc; + + alvr::render::RendererCreateInfo rendererCI { + .format = (vk::Format)(VkFormat)desc.nFormat, + .inputEyeExtent { + .width = desc.nWidth, + .height = desc.nHeight, + }, + .outputExtent { + .width = settings.m_recommendedTargetWidth, + .height = settings.m_recommendedTargetHeight, + }, + .inputImgFds = fds, + }; + + enc.createImages(rendererCI); + enc.initEncoding(); + + // We'll get em next time + return; + } + + // TODO: Merge layers or something + + enc.present(leftIdx.value(), rightIdx.value(), m_targetTimestampNs); +} + +void OvrDirectModeComponent::PostPresent(const Throttling_t* pThrottling) { /* WaitForVSync(); */ } diff --git a/alvr/server_openvr/cpp/platform/linux/OvrDirectModeComponent.h b/alvr/server_openvr/cpp/platform/linux/OvrDirectModeComponent.h new file mode 100644 index 0000000000..07d3a4cf91 --- /dev/null +++ b/alvr/server_openvr/cpp/platform/linux/OvrDirectModeComponent.h @@ -0,0 +1,78 @@ +#pragma once +#include "alvr_server/PoseHistory.h" +#include "alvr_server/Utils.h" +#include "openvr_driver.h" + +#include "alvr_server/Settings.h" + +#include +#include + +#include "Encoder.hpp" + +class OvrDirectModeComponent : public vr::IVRDriverDirectModeComponent { +public: + OvrDirectModeComponent( + /* std::shared_ptr pVKRender, */ std::shared_ptr poseHistory + ); + + void RequestIdr() { enc.requestIdr(); } + + /** Specific to Oculus compositor support, textures supplied must be created using this method. + */ + virtual void CreateSwapTextureSet( + uint32_t unPid, + const SwapTextureSetDesc_t* pSwapTextureSetDesc, + SwapTextureSet_t* pOutSwapTextureSet + ); + + /** Used to textures created using CreateSwapTextureSet. Only one of the set's handles needs to + * be used to destroy the entire set. */ + virtual void DestroySwapTextureSet(vr::SharedTextureHandle_t sharedTextureHandle); + + /** Used to purge all texture sets for a given process. */ + virtual void DestroyAllSwapTextureSets(uint32_t unPid); + + /** After Present returns, calls this to get the next index to use for rendering. */ + virtual void GetNextSwapTextureSetIndex( + vr::SharedTextureHandle_t sharedTextureHandles[2], uint32_t (*pIndices)[2] + ); + + /** Call once per layer to draw for this frame. One shared texture handle per eye. Textures + * must be created using CreateSwapTextureSet and should be alternated per frame. Call Present + * once all layers have been submitted. */ + virtual void SubmitLayer(const SubmitLayerPerEye_t (&perEye)[2]); + + /** Submits queued layers for display. */ + virtual void Present(vr::SharedTextureHandle_t syncTexture); + + /** Called after Present to allow driver to take more time until vsync after they've + * successfully acquired the sync texture in Present.*/ + virtual void PostPresent(const Throttling_t* pThrottling); + +private: + std::shared_ptr m_poseHistory; + + // Resource for each process + struct ProcessResource { + vr::SharedTextureHandle_t sharedHandles[3]; + int fds[3]; + SwapTextureSetDesc_t textDesc; + uint32_t pid; + }; + std::map> m_handleMap; + + static const int MAX_LAYERS = 10; + int m_submitLayer; + SubmitLayerPerEye_t m_submitLayers[MAX_LAYERS][2]; + vr::HmdQuaternion_t m_prevFramePoseRotation; + vr::HmdQuaternion_t m_framePoseRotation; + uint64_t m_targetTimestampNs; + uint64_t m_prevTargetTimestampNs; + + std::array layer0Texts; + + alvr::Encoder enc; + + std::mutex m_presentMutex; +}; diff --git a/alvr/server_openvr/cpp/platform/linux/Renderer.cpp b/alvr/server_openvr/cpp/platform/linux/Renderer.cpp index 8552e137a0..b40e0ab603 100644 --- a/alvr/server_openvr/cpp/platform/linux/Renderer.cpp +++ b/alvr/server_openvr/cpp/platform/linux/Renderer.cpp @@ -1,11 +1,92 @@ -#include "Renderer.h" +#include "Renderer.hpp" +#include +#include -#include -#include -#include -#include -#include -#include +namespace alvr::render::detail { + +RenderPipeline::RenderPipeline( + VkContext const& ctx, vk::DescriptorSetLayout& layout, PipelineCreateInfo& pipelineCI +) { + vk::ShaderModuleCreateInfo shaderCI { + .codeSize = pipelineCI.shaderData.size(), + .pCode = reinterpret_cast(pipelineCI.shaderData.data()), + }; + shader = ctx.dev.createShaderModule(shaderCI); + + vk::PipelineLayoutCreateInfo pipeLayoutCI { + .setLayoutCount = 1, + .pSetLayouts = &layout, + }; + pipeLayout = ctx.dev.createPipelineLayout(pipeLayoutCI); + + vk::SpecializationInfo specInfo { + .mapEntryCount = static_cast(pipelineCI.specs.size()), + .pMapEntries = pipelineCI.specs.data(), + .dataSize = pipelineCI.specData.size(), + .pData = pipelineCI.specData.data(), + }; + + vk::PipelineShaderStageCreateInfo stageCI { + .stage = vk::ShaderStageFlagBits::eCompute, + .module = shader, + .pName = "main", + .pSpecializationInfo = not pipelineCI.specs.empty() ? &specInfo : nullptr, + }; + vk::ComputePipelineCreateInfo pipeCI { + .stage = stageCI, + .layout = pipeLayout, + }; + auto [result, pipes] = ctx.dev.createComputePipelines({}, pipeCI); + + assert(result == vk::Result::eSuccess); + pipe = pipes[0]; +}; + +void RenderPipeline::render( + VkContext const& ctx, + vk::CommandBuffer cmdBuf, + vk::ImageView in, + vk::ImageView out, + vk::Extent2D outSize +) { + cmdBuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipe); + + vk::DescriptorImageInfo descImgInfoIn { + .imageView = in, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }; + vk::DescriptorImageInfo descImgInfoOut { + .imageView = out, + .imageLayout = vk::ImageLayout::eGeneral, + }; + + vk::WriteDescriptorSet descWriteSets[2]; + descWriteSets[0] = { + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &descImgInfoIn, + }; + descWriteSets[1] = { + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageImage, + .pImageInfo = &descImgInfoOut, + }; + cmdBuf.pushDescriptorSetKHR( + vk::PipelineBindPoint::eCompute, pipeLayout, 0, descWriteSets, ctx.dispatch + ); + + cmdBuf.dispatch((outSize.width + 7) / 8, (outSize.height + 7) / 8, 1); +} + +void RenderPipeline::destroy(VkContext const& ctx) { + ctx.dev.destroy(pipe); + ctx.dev.destroy(pipeLayout); + ctx.dev.destroy(shader); +} + +} #ifndef DRM_FORMAT_INVALID #define DRM_FORMAT_INVALID 0 @@ -24,22 +105,6 @@ (((value) >> AMD_FMT_MOD_##field##_SHIFT) & AMD_FMT_MOD_##field##_MASK) #endif -struct Vertex { - float position[2]; -}; - -static uint32_t to_drm_format(VkFormat format) { - switch (format) { - case VK_FORMAT_B8G8R8A8_UNORM: - return DRM_FORMAT_ARGB8888; - case VK_FORMAT_R8G8B8A8_UNORM: - return DRM_FORMAT_ABGR8888; - default: - std::cerr << "Unsupported format " << format << std::endl; - return DRM_FORMAT_INVALID; - } -} - static bool filter_modifier(uint64_t modifier) { if (IS_AMD_FMT_MOD(modifier)) { // DCC not supported as encode input @@ -50,1149 +115,613 @@ static bool filter_modifier(uint64_t modifier) { return true; } -Renderer::Renderer( - const VkInstance& inst, - const VkDevice& dev, - const VkPhysicalDevice& physDev, - uint32_t queueIdx, - const std::vector& devExtensions -) - : m_inst(inst) - , m_dev(dev) - , m_physDev(physDev) - , m_queueFamilyIndex(queueIdx) { - auto checkExtension = [devExtensions](const char* name) { - return std::find_if( - devExtensions.begin(), - devExtensions.end(), - [name](const char* ext) { return strcmp(ext, name) == 0; } - ) - != devExtensions.end(); - }; - d.haveDmaBuf = checkExtension(VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME); - d.haveDrmModifiers = checkExtension(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME); - d.haveCalibratedTimestamps = checkExtension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME); - - if (!checkExtension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)) { - throw std::runtime_error("Vulkan: Required extension " VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME - " not available"); +static u32 to_drm_format(vk::Format format) { + switch (format) { + case vk::Format::eB8G8R8A8Unorm: + return DRM_FORMAT_ARGB8888; + case vk::Format::eR8G8B8A8Unorm: + return DRM_FORMAT_ABGR8888; + default: + // assert(false); + return DRM_FORMAT_INVALID; } - -#define VK_LOAD_PFN(name) d.name = (PFN_##name)vkGetInstanceProcAddr(m_inst, #name) - VK_LOAD_PFN(vkImportSemaphoreFdKHR); - VK_LOAD_PFN(vkGetMemoryFdKHR); - VK_LOAD_PFN(vkGetMemoryFdPropertiesKHR); - VK_LOAD_PFN(vkGetImageDrmFormatModifierPropertiesEXT); - VK_LOAD_PFN(vkGetCalibratedTimestampsEXT); - VK_LOAD_PFN(vkCmdPushDescriptorSetKHR); -#undef VK_LOAD_PFN - - VkPhysicalDeviceProperties props = {}; - vkGetPhysicalDeviceProperties(m_physDev, &props); - m_timestampPeriod = props.limits.timestampPeriod; } -Renderer::~Renderer() { - vkDeviceWaitIdle(m_dev); +using namespace alvr; +using namespace alvr::render; - for (const InputImage& image : m_images) { - vkDestroyImageView(m_dev, image.view, nullptr); - vkDestroyImage(m_dev, image.image, nullptr); - vkFreeMemory(m_dev, image.memory, nullptr); - vkDestroySemaphore(m_dev, image.semaphore, nullptr); +u32 memoryTypeIndex(VkContext const& ctx, vk::MemoryPropertyFlags properties, u32 typeBits) { + auto prop = ctx.physDev.getMemoryProperties(); + for (u32 i = 0; i < prop.memoryTypeCount; ++i) { + if ((prop.memoryTypes[i].propertyFlags & properties) == properties && typeBits & (1 << i)) { + return i; + } } + throw std::runtime_error("No matching memoryTypeIndex found"); +} - for (const StagingImage& image : m_stagingImages) { - vkDestroyImageView(m_dev, image.view, nullptr); - vkDestroyImage(m_dev, image.image, nullptr); - vkFreeMemory(m_dev, image.memory, nullptr); +// Create or import Image, depending on whether an fd is passed +inline Image createImage( + VkContext const& ctx, vk::ImageCreateInfo imageCI, std::optional fd = std::nullopt +) { + vk::ExternalMemoryImageCreateInfo extMemImgCI { + .handleTypes = vk::ExternalMemoryHandleTypeFlagBits::eOpaqueFd, + }; + if (fd.has_value()) { + extMemImgCI.pNext = imageCI.pNext; + imageCI.pNext = &extMemImgCI; } - vkDestroyImageView(m_dev, m_output.view, nullptr); - vkDestroyImage(m_dev, m_output.image, nullptr); - vkFreeMemory(m_dev, m_output.memory, nullptr); - vkDestroySemaphore(m_dev, m_output.semaphore, nullptr); + Image img { + .image = ctx.dev.createImage(imageCI), + .layout = imageCI.initialLayout, + }; - vkDestroyQueryPool(m_dev, m_queryPool, nullptr); - vkDestroyCommandPool(m_dev, m_commandPool, nullptr); - vkDestroySampler(m_dev, m_sampler, nullptr); - vkDestroyDescriptorSetLayout(m_dev, m_descriptorLayout, nullptr); - vkDestroyFence(m_dev, m_fence, nullptr); -} + vk::MemoryDedicatedRequirements dedicatedReqs {}; + vk::MemoryRequirements2 memReqs { + .pNext = &dedicatedReqs, + }; + vk::ImageMemoryRequirementsInfo2 memReqInfo { + .image = img.image, + }; + ctx.dev.getImageMemoryRequirements2(&memReqInfo, &memReqs); -void Renderer::Startup(uint32_t width, uint32_t height, VkFormat format) { - m_format = format; - m_imageSize.width = width; - m_imageSize.height = height; - - vkGetDeviceQueue(m_dev, m_queueFamilyIndex, 0, &m_queue); - - // Timestamp query - VkQueryPoolCreateInfo queryPoolInfo = {}; - queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; - queryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP; - queryPoolInfo.queryCount = 2; - VK_CHECK(vkCreateQueryPool(m_dev, &queryPoolInfo, nullptr, &m_queryPool)); - - // Command buffer - VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = m_queueFamilyIndex; - cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VK_CHECK(vkCreateCommandPool(m_dev, &cmdPoolInfo, nullptr, &m_commandPool)); - - VkCommandBufferAllocateInfo commandBufferInfo = {}; - commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - commandBufferInfo.commandPool = m_commandPool; - commandBufferInfo.commandBufferCount = 1; - VK_CHECK(vkAllocateCommandBuffers(m_dev, &commandBufferInfo, &m_commandBuffer)); - - // Sampler - VkSamplerCreateInfo samplerInfo = {}; - samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerInfo.magFilter = VK_FILTER_LINEAR; - samplerInfo.minFilter = VK_FILTER_LINEAR; - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.anisotropyEnable = VK_TRUE; - samplerInfo.maxAnisotropy = 16.0f; - samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; - VK_CHECK(vkCreateSampler(m_dev, &samplerInfo, nullptr, &m_sampler)); - - // Descriptors - VkDescriptorSetLayoutBinding descriptorBindings[2] = {}; - descriptorBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorBindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - descriptorBindings[0].descriptorCount = 1; - descriptorBindings[0].pImmutableSamplers = &m_sampler; - descriptorBindings[0].binding = 0; - descriptorBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - descriptorBindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - descriptorBindings[1].descriptorCount = 1; - descriptorBindings[1].binding = 1; - - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {}; - descriptorSetLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - descriptorSetLayoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR; - descriptorSetLayoutInfo.bindingCount = 2; - descriptorSetLayoutInfo.pBindings = descriptorBindings; - VK_CHECK( - vkCreateDescriptorSetLayout(m_dev, &descriptorSetLayoutInfo, nullptr, &m_descriptorLayout) - ); + vk::ImportMemoryFdInfoKHR importMemInfo { + .handleType = vk::ExternalMemoryHandleTypeFlagBits::eOpaqueFd, + .fd = fd.value_or(-1), + }; + vk::MemoryDedicatedAllocateInfo memDedicatedInfo { + .pNext = fd.has_value() ? &importMemInfo : nullptr, + .image = img.image, + }; + vk::MemoryAllocateInfo memAllocInfo { + .pNext = &memDedicatedInfo, + .allocationSize = memReqs.memoryRequirements.size, + .memoryTypeIndex = memoryTypeIndex( + ctx, vk::MemoryPropertyFlagBits::eDeviceLocal, memReqs.memoryRequirements.memoryTypeBits + ), + }; + img.memory = ctx.dev.allocateMemory(memAllocInfo); - // Fence - VkFenceCreateInfo fenceInfo = {}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - VK_CHECK(vkCreateFence(m_dev, &fenceInfo, nullptr, &m_fence)); + vk::BindImageMemoryInfo bindImgInfo { + .image = img.image, + .memory = img.memory, + .memoryOffset = 0, + }; + ctx.dev.bindImageMemory2(bindImgInfo); + + vk::ImageViewCreateInfo imgViewCI { + .image = img.image, + .viewType = vk::ImageViewType::e2D, + .format = imageCI.format, + .components { + .r = vk::ComponentSwizzle::eIdentity, + .g = vk::ComponentSwizzle::eIdentity, + .b = vk::ComponentSwizzle::eIdentity, + .a = vk::ComponentSwizzle::eIdentity, + }, + .subresourceRange { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = imageCI.mipLevels, + .baseArrayLayer = 0, + .layerCount = imageCI.arrayLayers, + }, + }; + img.view = ctx.dev.createImageView(imgViewCI); + + return img; } -void Renderer::AddImage( - VkImageCreateInfo imageInfo, size_t memoryIndex, int imageFd, int semaphoreFd +Output createOutputImage( + VkContext const& ctx, vk::Extent2D extent, vk::Format format, HandleType handleType ) { - VkExternalMemoryImageCreateInfo extMemImageInfo = {}; - extMemImageInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; - extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; - imageInfo.pNext = &extMemImageInfo; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VkImage image; - VK_CHECK(vkCreateImage(m_dev, &imageInfo, nullptr, &image)); - - VkMemoryRequirements req; - vkGetImageMemoryRequirements(m_dev, image, &req); - - VkMemoryDedicatedAllocateInfo dedicatedMemInfo = {}; - dedicatedMemInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; - dedicatedMemInfo.image = image; - - VkImportMemoryFdInfoKHR importMemInfo = {}; - importMemInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; - importMemInfo.pNext = &dedicatedMemInfo; - importMemInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; - importMemInfo.fd = imageFd; - - VkMemoryAllocateInfo memAllocInfo = {}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllocInfo.pNext = &importMemInfo; - memAllocInfo.allocationSize = req.size; - memAllocInfo.memoryTypeIndex = memoryIndex; - - VkDeviceMemory mem; - VK_CHECK(vkAllocateMemory(m_dev, &memAllocInfo, nullptr, &mem)); - VK_CHECK(vkBindImageMemory(m_dev, image, mem, 0)); - - VkSemaphoreTypeCreateInfo timelineInfo = {}; - timelineInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; - timelineInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; - - VkSemaphoreCreateInfo semInfo = {}; - semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - semInfo.pNext = &timelineInfo; - VkSemaphore semaphore; - VK_CHECK(vkCreateSemaphore(m_dev, &semInfo, nullptr, &semaphore)); - - VkImportSemaphoreFdInfoKHR impSemInfo = {}; - impSemInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; - impSemInfo.semaphore = semaphore; - impSemInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; - impSemInfo.fd = semaphoreFd; - VK_CHECK(d.vkImportSemaphoreFdKHR(m_dev, &impSemInfo)); - - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = imageInfo.format; - viewInfo.image = image; - viewInfo.subresourceRange = {}; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - VkImageView view; - VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &view)); - - m_images.push_back({ image, VK_IMAGE_LAYOUT_UNDEFINED, mem, semaphore, view }); -} + Image img; + Output out; + + vk::ImageCreateInfo imgCI { + .imageType = vk::ImageType::e2D, + .format = format, + .extent = { + .width = extent.width, + .height = extent.height, + .depth = 1, + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = vk::SampleCountFlagBits::e1, + .usage = vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eSampled, + .sharingMode = vk::SharingMode::eExclusive, + .initialLayout = vk::ImageLayout::eUndefined, + }; -void Renderer::AddPipeline(RenderPipeline* pipeline) { - pipeline->Build(); - m_pipelines.push_back(pipeline); + img.layout = imgCI.initialLayout; - if (m_pipelines.size() > 1 && m_stagingImages.size() < 2) { - addStagingImage(m_imageSize.width, m_imageSize.height); - } -} + vk::ExternalMemoryImageCreateInfo extImgCI {}; + + // Lifetime + std::vector imageModifiers; + + // TODO: Actually import this + bool haveDmaBuf = true; + bool haveDrmModifiers = true; + std::vector modProps; + + if (haveDrmModifiers && handleType == HandleType::DmaBuf) { + imgCI.tiling = vk::ImageTiling::eDrmFormatModifierEXT; + + vk::DrmFormatModifierPropertiesListEXT modPropList {}; + vk::FormatProperties2 formatProps { + .pNext = &modPropList, + }; + ctx.physDev.getFormatProperties2(imgCI.format, &formatProps); + + modProps.resize(modPropList.drmFormatModifierCount); + modPropList.pDrmFormatModifierProperties = modProps.data(); + ctx.physDev.getFormatProperties2(imgCI.format, &formatProps); -void Renderer::CreateOutput(uint32_t width, uint32_t height, ExternalHandle handle) { - m_output.imageInfo = {}; - m_output.imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - m_output.imageInfo.imageType = VK_IMAGE_TYPE_2D; - m_output.imageInfo.format = m_format; - m_output.imageInfo.extent.width = width; - m_output.imageInfo.extent.height = height; - m_output.imageInfo.extent.depth = 1; - m_output.imageInfo.mipLevels = 1; - m_output.imageInfo.arrayLayers = 1; - m_output.imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - m_output.imageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - m_output.imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - m_output.imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - std::vector modifierProps; - - VkExternalMemoryImageCreateInfo extMemImageInfo = {}; - extMemImageInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; - - if (d.haveDrmModifiers && handle == ExternalHandle::DmaBuf) { - VkImageDrmFormatModifierListCreateInfoEXT modifierListInfo = {}; - modifierListInfo.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT; - - m_output.imageInfo.pNext = &modifierListInfo; - m_output.imageInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; - - VkDrmFormatModifierPropertiesListEXT modifierPropsList = {}; - modifierPropsList.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT; - - VkFormatProperties2 formatProps = {}; - formatProps.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; - formatProps.pNext = &modifierPropsList; - vkGetPhysicalDeviceFormatProperties2(m_physDev, m_output.imageInfo.format, &formatProps); - - modifierProps.resize(modifierPropsList.drmFormatModifierCount); - modifierPropsList.pDrmFormatModifierProperties = modifierProps.data(); - vkGetPhysicalDeviceFormatProperties2(m_physDev, m_output.imageInfo.format, &formatProps); - - std::vector imageModifiers; - std::cout << "Available modifiers:" << std::endl; - for (const VkDrmFormatModifierPropertiesEXT& prop : modifierProps) { - std::cout << "modifier: " << prop.drmFormatModifier - << " planes: " << prop.drmFormatModifierPlaneCount << std::endl; + for (auto& prop : modProps) { if (!filter_modifier(prop.drmFormatModifier)) { - std::cout << " filtered" << std::endl; continue; } - VkPhysicalDeviceImageDrmFormatModifierInfoEXT modInfo = {}; - modInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT; - modInfo.drmFormatModifier = prop.drmFormatModifier; - modInfo.sharingMode = m_output.imageInfo.sharingMode; - modInfo.queueFamilyIndexCount = m_output.imageInfo.queueFamilyIndexCount; - modInfo.pQueueFamilyIndices = m_output.imageInfo.pQueueFamilyIndices; - - VkPhysicalDeviceImageFormatInfo2 formatInfo = {}; - formatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2; - formatInfo.pNext = &modInfo; - formatInfo.format = m_output.imageInfo.format; - formatInfo.type = m_output.imageInfo.imageType; - formatInfo.tiling = m_output.imageInfo.tiling; - formatInfo.usage = m_output.imageInfo.usage; - formatInfo.flags = m_output.imageInfo.flags; - - VkImageFormatProperties2 imageFormatProps = {}; - imageFormatProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; - imageFormatProps.pNext = NULL; - - VkResult r = vkGetPhysicalDeviceImageFormatProperties2( - m_physDev, &formatInfo, &imageFormatProps - ); - if (r == VK_SUCCESS) { + vk::PhysicalDeviceImageDrmFormatModifierInfoEXT modInfo { + .drmFormatModifier = prop.drmFormatModifier, + .sharingMode = imgCI.sharingMode, + .queueFamilyIndexCount = imgCI.queueFamilyIndexCount, + .pQueueFamilyIndices = imgCI.pQueueFamilyIndices, + }; + vk::PhysicalDeviceImageFormatInfo2 formatInfo { + .pNext = &modInfo, + .format = imgCI.format, + .type = imgCI.imageType, + .tiling = imgCI.tiling, + .usage = imgCI.usage, + .flags = imgCI.flags, + }; + vk::ImageFormatProperties2 imgFormatProps { + .pNext = nullptr, + }; + auto result = ctx.physDev.getImageFormatProperties2(&formatInfo, &imgFormatProps); + if (result == vk::Result::eSuccess) imageModifiers.push_back(prop.drmFormatModifier); - } } - modifierListInfo.drmFormatModifierCount = imageModifiers.size(); - modifierListInfo.pDrmFormatModifiers = imageModifiers.data(); - - extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; - modifierListInfo.pNext = &extMemImageInfo; - - VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, nullptr, &m_output.image)); - } else if (d.haveDmaBuf && handle == ExternalHandle::DmaBuf) { - extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; - m_output.imageInfo.pNext = &extMemImageInfo; - - m_output.imageInfo.tiling = VK_IMAGE_TILING_LINEAR; - VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, nullptr, &m_output.image)); - } else if (handle == ExternalHandle::OpaqueFd) { - extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; - m_output.imageInfo.pNext = &extMemImageInfo; - - m_output.imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, nullptr, &m_output.image)); - } else { - m_output.imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, nullptr, &m_output.image)); - } - VkMemoryDedicatedRequirements mdr = {}; - mdr.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS; + extImgCI.handleTypes = vk::ExternalMemoryHandleTypeFlagBits::eDmaBufEXT; + + vk::ImageDrmFormatModifierListCreateInfoEXT modListCI { + .pNext = &extImgCI, + .drmFormatModifierCount = static_cast(imageModifiers.size()), + .pDrmFormatModifiers = imageModifiers.data(), + }; + + imgCI.pNext = &modListCI; - VkMemoryRequirements2 memoryReqs = {}; - memoryReqs.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; - memoryReqs.pNext = &mdr; + img.image = ctx.dev.createImage(imgCI); + } else if (haveDmaBuf && handleType == HandleType::DmaBuf) { + extImgCI.handleTypes = vk::ExternalMemoryHandleTypeFlagBits::eDmaBufEXT; + imgCI.pNext = &extImgCI; + imgCI.tiling = vk::ImageTiling::eLinear; - VkImageMemoryRequirementsInfo2 memoryReqsInfo = {}; - memoryReqsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2; - memoryReqsInfo.image = m_output.image; - vkGetImageMemoryRequirements2(m_dev, &memoryReqsInfo, &memoryReqs); - m_output.size = memoryReqs.memoryRequirements.size; + img.image = ctx.dev.createImage(imgCI); + } else if (handleType == HandleType::OpaqueFd) { + extImgCI.handleTypes = vk::ExternalMemoryHandleTypeFlagBits::eOpaqueFd; + imgCI.pNext = &extImgCI; + imgCI.tiling = vk::ImageTiling::eOptimal; - VkExportMemoryAllocateInfo memory_export_info = {}; - memory_export_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO; - memory_export_info.handleTypes = extMemImageInfo.handleTypes; + img.image = ctx.dev.createImage(imgCI); + } else if (handleType == HandleType::None) { + imgCI.tiling = vk::ImageTiling::eOptimal; - VkMemoryDedicatedAllocateInfo memory_dedicated_info = {}; - memory_dedicated_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; - memory_dedicated_info.image = m_output.image; - if (handle != ExternalHandle::None) { - memory_dedicated_info.pNext = &memory_export_info; + img.image = ctx.dev.createImage(imgCI); } - VkMemoryAllocateInfo memi = {}; - memi.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memi.pNext = &memory_dedicated_info; - memi.allocationSize = memoryReqs.memoryRequirements.size; - memi.memoryTypeIndex = memoryTypeIndex( - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memoryReqs.memoryRequirements.memoryTypeBits - ); - VK_CHECK(vkAllocateMemory(m_dev, &memi, nullptr, &m_output.memory)); - - VkBindImageMemoryInfo bimi = {}; - bimi.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; - bimi.image = m_output.image; - bimi.memory = m_output.memory; - bimi.memoryOffset = 0; - VK_CHECK(vkBindImageMemory2(m_dev, 1, &bimi)); - - // DRM export - if (d.haveDmaBuf) { - VkMemoryGetFdInfoKHR memoryGetFdInfo = {}; - memoryGetFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; - memoryGetFdInfo.memory = m_output.memory; - memoryGetFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; - VkResult res = d.vkGetMemoryFdKHR(m_dev, &memoryGetFdInfo, &m_output.drm.fd); - if (res != VK_SUCCESS) { - std::cout << "vkGetMemoryFdKHR " << result_to_str(res) << std::endl; - } else { - if (d.haveDrmModifiers) { - VkImageDrmFormatModifierPropertiesEXT imageDrmProps = {}; - imageDrmProps.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT; - d.vkGetImageDrmFormatModifierPropertiesEXT(m_dev, m_output.image, &imageDrmProps); - if (res != VK_SUCCESS) { - std::cout << "vkGetImageDrmFormatModifierPropertiesEXT " << result_to_str(res) - << std::endl; - } else { - m_output.drm.modifier = imageDrmProps.drmFormatModifier; - for (VkDrmFormatModifierPropertiesEXT prop : modifierProps) { - if (prop.drmFormatModifier == m_output.drm.modifier) { - m_output.drm.planes = prop.drmFormatModifierPlaneCount; - } - } - } - } else { - m_output.drm.modifier = DRM_FORMAT_MOD_INVALID; - m_output.drm.planes = 1; - } + vk::MemoryDedicatedRequirements dedicatedReqs {}; + vk::MemoryRequirements2 memReqs { + .pNext = &dedicatedReqs, + }; - for (uint32_t i = 0; i < m_output.drm.planes; i++) { - VkImageSubresource subresource = {}; - if (d.haveDrmModifiers) { - subresource.aspectMask = VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT << i; - } else { - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - } - VkSubresourceLayout layout; - vkGetImageSubresourceLayout(m_dev, m_output.image, &subresource, &layout); - m_output.drm.strides[i] = layout.rowPitch; - m_output.drm.offsets[i] = layout.offset; + vk::ImageMemoryRequirementsInfo2 memReqInfo { + .image = img.image, + }; + ctx.dev.getImageMemoryRequirements2(&memReqInfo, &memReqs); + + vk::MemoryDedicatedAllocateInfo memDedicatedInfo { + .image = img.image, + }; + vk::ExportMemoryAllocateInfo memExportInfo { + .handleTypes = extImgCI.handleTypes, + }; + if (handleType != HandleType::None) + memDedicatedInfo.pNext = &memExportInfo; + + vk::MemoryAllocateInfo memAllocInfo { + .pNext = &memDedicatedInfo, + .allocationSize = memReqs.memoryRequirements.size, + .memoryTypeIndex = memoryTypeIndex( + ctx, vk::MemoryPropertyFlagBits::eDeviceLocal, memReqs.memoryRequirements.memoryTypeBits + ), + }; + img.memory = ctx.dev.allocateMemory(memAllocInfo); + + vk::BindImageMemoryInfo bindImgInfo { + .image = img.image, + .memory = img.memory, + .memoryOffset = 0, + }; + ctx.dev.bindImageMemory2(bindImgInfo); + + if (haveDmaBuf) { + vk::MemoryGetFdInfoKHR memFdInfo { + .memory = img.memory, + .handleType = vk::ExternalMemoryHandleTypeFlagBits::eDmaBufEXT, + }; + out.drm.fd = ctx.dev.getMemoryFdKHR(memFdInfo, ctx.dispatch); + + if (haveDrmModifiers) { + auto imgDrmProps + = ctx.dev.getImageDrmFormatModifierPropertiesEXT(img.image, ctx.dispatch); + + out.drm.modifier = imgDrmProps.drmFormatModifier; + for (auto prop : modProps) { + if (prop.drmFormatModifier == out.drm.modifier) + out.drm.planes = prop.drmFormatModifierPlaneCount; } + } else { + out.drm.modifier = DRM_FORMAT_MOD_INVALID; + out.drm.planes = 1; } - m_output.drm.format = to_drm_format(m_output.imageInfo.format); - } - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = m_output.imageInfo.format; - viewInfo.image = m_output.image; - viewInfo.subresourceRange = {}; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &m_output.view)); - - VkSemaphoreCreateInfo semInfo = {}; - semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - VK_CHECK(vkCreateSemaphore(m_dev, &semInfo, nullptr, &m_output.semaphore)); -} + for (u32 i = 0; i < out.drm.planes; ++i) { + vk::ImageSubresource subresource { + .aspectMask = static_cast( + haveDrmModifiers ? VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT << i + : VK_IMAGE_ASPECT_COLOR_BIT + ), + }; + auto layout = ctx.dev.getImageSubresourceLayout(img.image, subresource); + out.drm.strides[i] = layout.rowPitch; + out.drm.offsets[i] = layout.offset; + } -void Renderer::ImportOutput(const DrmImage& drm) { - vkDestroyImageView(m_dev, m_output.view, nullptr); - vkDestroyImage(m_dev, m_output.image, nullptr); - vkFreeMemory(m_dev, m_output.memory, nullptr); + out.drm.format = to_drm_format(imgCI.format); + } - m_output.drm = drm; - m_output.imageInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + vk::ImageViewCreateInfo imgViewCI { + .image = img.image, + .viewType = vk::ImageViewType::e2D, + .format = imgCI.format, + .components { + .r = vk::ComponentSwizzle::eIdentity, + .g = vk::ComponentSwizzle::eIdentity, + .b = vk::ComponentSwizzle::eIdentity, + .a = vk::ComponentSwizzle::eIdentity, + }, + .subresourceRange { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = imgCI.mipLevels, + .baseArrayLayer = 0, + .layerCount = imgCI.arrayLayers, + }, + }; + img.view = ctx.dev.createImageView(imgViewCI); - VkExternalMemoryImageCreateInfo extMemImageInfo = {}; - extMemImageInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; - extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; - m_output.imageInfo.pNext = &extMemImageInfo; + // out.semaphore = ctx.dev.createSemaphore({}); - VkSubresourceLayout layouts[4] = {}; - for (uint32_t i = 0; i < drm.planes; ++i) { - layouts[i].offset = drm.offsets[i]; - layouts[i].rowPitch = drm.strides[i]; - } - VkImageDrmFormatModifierExplicitCreateInfoEXT modifierInfo = {}; - modifierInfo.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT; - modifierInfo.drmFormatModifier = drm.modifier; - modifierInfo.drmFormatModifierPlaneCount = drm.planes; - modifierInfo.pPlaneLayouts = layouts; - extMemImageInfo.pNext = &modifierInfo; - - VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, NULL, &m_output.image)); - - VkMemoryFdPropertiesKHR fdProps = {}; - fdProps.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR; - VK_CHECK(d.vkGetMemoryFdPropertiesKHR( - m_dev, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, drm.fd, &fdProps - )); - - VkImageMemoryRequirementsInfo2 memoryReqsInfo = {}; - memoryReqsInfo.image = m_output.image; - memoryReqsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2; - - VkMemoryRequirements2 memoryReqs = {}; - memoryReqs.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; - vkGetImageMemoryRequirements2(m_dev, &memoryReqsInfo, &memoryReqs); - - VkMemoryAllocateInfo memoryAllocInfo = {}; - memoryAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memoryAllocInfo.allocationSize = memoryReqs.memoryRequirements.size; - memoryAllocInfo.memoryTypeIndex = memoryTypeIndex( - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memoryReqs.memoryRequirements.memoryTypeBits - ); + out.imageCI = imgCI; + out.size = memReqs.memoryRequirements.size; + out.image = img; - VkImportMemoryFdInfoKHR importMemInfo = {}; - importMemInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; - importMemInfo.fd = drm.fd; - importMemInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; - memoryAllocInfo.pNext = &importMemInfo; - - VkMemoryDedicatedAllocateInfo dedicatedMemInfo = {}; - dedicatedMemInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; - dedicatedMemInfo.image = m_output.image; - importMemInfo.pNext = &dedicatedMemInfo; - - VK_CHECK(vkAllocateMemory(m_dev, &memoryAllocInfo, NULL, &m_output.memory)); - - VkBindImageMemoryInfo bindInfo = {}; - bindInfo.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; - bindInfo.image = m_output.image; - bindInfo.memory = m_output.memory; - bindInfo.memoryOffset = 0; - VK_CHECK(vkBindImageMemory2(m_dev, 1, &bindInfo)); - - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = m_output.imageInfo.format; - viewInfo.image = m_output.image; - viewInfo.subresourceRange = {}; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &m_output.view)); + return out; } -void Renderer::Render(uint32_t index, uint64_t waitValue) { - if (!m_inputImageCapture.empty()) { - VkSemaphoreWaitInfo waitInfo = {}; - waitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO; - waitInfo.semaphoreCount = 1; - waitInfo.pSemaphores = &m_images[index].semaphore; - waitInfo.pValues = &waitValue; - VK_CHECK(vkWaitSemaphores(m_dev, &waitInfo, UINT64_MAX)); - - dumpImage( - m_images[index].image, - m_images[index].view, - m_images[index].layout, - m_imageSize.width, - m_imageSize.height, - m_inputImageCapture - ); - m_inputImageCapture.clear(); - } +namespace alvr::render { - VkCommandBufferBeginInfo commandBufferBegin = {}; - commandBufferBegin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VK_CHECK(vkBeginCommandBuffer(m_commandBuffer, &commandBufferBegin)); - - vkCmdResetQueryPool(m_commandBuffer, m_queryPool, 0, 2); - vkCmdWriteTimestamp(m_commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, m_queryPool, 0); - - for (size_t i = 0; i < m_pipelines.size(); ++i) { - VkRect2D rect = {}; - VkImage in = VK_NULL_HANDLE; - VkImageView inView = VK_NULL_HANDLE; - VkImageLayout* inLayout = nullptr; - VkImage out = VK_NULL_HANDLE; - VkImageView outView = VK_NULL_HANDLE; - VkImageLayout* outLayout = nullptr; - if (i == 0) { - auto& img = m_images[index]; - in = img.image; - inView = img.view; - inLayout = &img.layout; - } else { - auto& img = m_stagingImages[(i - 1) % m_stagingImages.size()]; - in = img.image; - inView = img.view; - inLayout = &img.layout; - } - if (i == m_pipelines.size() - 1) { - out = m_output.image; - outView = m_output.view; - outLayout = &m_output.layout; - rect.extent.width = m_output.imageInfo.extent.width; - rect.extent.height = m_output.imageInfo.extent.height; - } else { - auto& img = m_stagingImages[i % m_stagingImages.size()]; - out = img.image; - outView = img.view; - outLayout = &img.layout; - rect.extent = m_imageSize; - } - VkImageMemoryBarrier imageBarrier = {}; - imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBarrier.subresourceRange.layerCount = 1; - imageBarrier.subresourceRange.levelCount = 1; - std::vector imageBarriers; - if (*inLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - imageBarrier.image = in; - imageBarrier.oldLayout = *inLayout; - *inLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageBarrier.newLayout = *inLayout; - imageBarrier.srcAccessMask = 0; - imageBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - imageBarriers.push_back(imageBarrier); - } - if (*outLayout != VK_IMAGE_LAYOUT_GENERAL) { - imageBarrier.image = out; - imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - *outLayout = VK_IMAGE_LAYOUT_GENERAL; - imageBarrier.newLayout = *outLayout; - imageBarrier.srcAccessMask = 0; - imageBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - imageBarriers.push_back(imageBarrier); - } - if (imageBarriers.size()) { - vkCmdPipelineBarrier( - m_commandBuffer, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, - 0, - nullptr, - 0, - nullptr, - imageBarriers.size(), - imageBarriers.data() - ); - } - m_pipelines[i]->Render(inView, outView, rect); +Renderer::Renderer( + VkContext const& vkCtx, RendererCreateInfo& createInfo, std::vector pipeCIs +) { + eyeExtent = createInfo.inputEyeExtent; + outExtent = createInfo.outputExtent; + + // TODO: This is not standard compliant, but it respects the binary format so only the colors + // should be messed up + vk::Format inputFormat = createInfo.format; + if (inputFormat == vk::Format::eR8G8B8A8Srgb) + inputFormat = vk::Format::eR8G8B8A8Unorm; + + if (inputFormat == vk::Format::eB8G8R8A8Srgb) + inputFormat = vk::Format::eB8G8R8A8Unorm; + + // TODO: Only put the usage flags for the images that need to have them + vk::ImageCreateInfo inputImgCI { + .imageType = vk::ImageType::e2D, + .format = inputFormat, + + .extent = { + .width = eyeExtent.width, + .height = eyeExtent.height, + .depth = 1, + }, + + .mipLevels = 1, + .arrayLayers = 1, + .samples = vk::SampleCountFlagBits::e1, + .usage = vk::ImageUsageFlagBits::eTransferSrc + | vk::ImageUsageFlagBits::eInputAttachment | vk::ImageUsageFlagBits::eSampled, + .sharingMode = vk::SharingMode::eExclusive, + .initialLayout = vk::ImageLayout::eUndefined, + }; + + for (int i = 0; i < ImageCount; ++i) { + inputImages[i] = createImage(vkCtx, inputImgCI, createInfo.inputImgFds[i]); } - vkCmdWriteTimestamp(m_commandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, m_queryPool, 1); + auto stagingImgCI = inputImgCI; + stagingImgCI.extent.width = eyeExtent.width * 2; + stagingImgCI.usage = inputImgCI.usage | vk::ImageUsageFlagBits::eTransferDst; - VK_CHECK(vkEndCommandBuffer(m_commandBuffer)); + for (auto& img : stagingImgs) { + img = createImage(vkCtx, stagingImgCI); + } - VkTimelineSemaphoreSubmitInfo timelineInfo = {}; - timelineInfo.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; - timelineInfo.waitSemaphoreValueCount = 1; - timelineInfo.pWaitSemaphoreValues = &waitValue; + output = createOutputImage( + vkCtx, createInfo.outputExtent, stagingImgCI.format, HandleType::DmaBuf + ); - VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + vk::QueryPoolCreateInfo poolCI { + .queryType = vk::QueryType::eTimestamp, + .queryCount = 2, + }; + timestampPool = vkCtx.dev.createQueryPool(poolCI); - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.pNext = &timelineInfo; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &m_images[index].semaphore; - submitInfo.pWaitDstStageMask = &waitStage; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &m_output.semaphore; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &m_commandBuffer; - VK_CHECK(vkQueueSubmit(m_queue, 1, &submitInfo, nullptr)); -} + vk::CommandPoolCreateInfo cmdPoolCI { + .flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer, + .queueFamilyIndex = vkCtx.meta.queueFamily, + }; + cmdPool = vkCtx.dev.createCommandPool(cmdPoolCI); -void Renderer::Sync() { - VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + vk::CommandBufferAllocateInfo cmdBufAllocInfo { + .commandPool = cmdPool, + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = 1, + }; + cmdBuf = vkCtx.dev.allocateCommandBuffers(cmdBufAllocInfo)[0]; - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &m_output.semaphore; - submitInfo.pWaitDstStageMask = &waitStage; - VK_CHECK(vkQueueSubmit(m_queue, 1, &submitInfo, m_fence)); + vk::SamplerCreateInfo samplerCI { + .magFilter = vk::Filter::eLinear, + .minFilter = vk::Filter::eLinear, + .mipmapMode = vk::SamplerMipmapMode::eLinear, + .addressModeU = vk::SamplerAddressMode::eRepeat, + .addressModeV = vk::SamplerAddressMode::eRepeat, + .addressModeW = vk::SamplerAddressMode::eRepeat, - VK_CHECK(vkWaitForFences(m_dev, 1, &m_fence, VK_TRUE, UINT64_MAX)); - VK_CHECK(vkResetFences(m_dev, 1, &m_fence)); -} + .anisotropyEnable = true, + .maxAnisotropy = 16.f, -Renderer::Output& Renderer::GetOutput() { return m_output; } + .borderColor = vk::BorderColor::eFloatTransparentBlack, + }; + sampler = vkCtx.dev.createSampler(samplerCI); + + vk::DescriptorSetLayoutBinding descBindings[2]; + descBindings[0] = { + .binding = 0, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute, + .pImmutableSamplers = &sampler, + }; + descBindings[1] = { + .binding = 1, + .descriptorType = vk::DescriptorType::eStorageImage, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute, + }; -Renderer::Timestamps Renderer::GetTimestamps() { - if (!d.haveCalibratedTimestamps) { - return { 0, 0, 0 }; - } + vk::DescriptorSetLayoutCreateInfo descLayoutCI { + .flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR, + .bindingCount = 2, + .pBindings = descBindings, + }; + descLayout = vkCtx.dev.createDescriptorSetLayout(descLayoutCI); - uint64_t queries[2]; - VK_CHECK(vkGetQueryPoolResults( - m_dev, - m_queryPool, - 0, - 2, - 2 * sizeof(uint64_t), - queries, - sizeof(uint64_t), - VK_QUERY_RESULT_64_BIT - )); - queries[0] *= m_timestampPeriod; - queries[1] *= m_timestampPeriod; - - VkCalibratedTimestampInfoEXT timestampInfo = {}; - timestampInfo.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT; - timestampInfo.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT; - uint64_t deviation; - uint64_t timestamp; - VK_CHECK(d.vkGetCalibratedTimestampsEXT(m_dev, 1, ×tampInfo, ×tamp, &deviation)); - timestamp *= m_timestampPeriod; - - if (!m_outputImageCapture.empty()) { - dumpImage( - m_output.image, - m_output.view, - m_output.layout, - m_output.imageInfo.extent.width, - m_output.imageInfo.extent.height, - m_outputImageCapture - ); - m_outputImageCapture.clear(); + pipes.reserve(pipeCIs.size()); + for (auto& pipeCI : pipeCIs) { + pipes.emplace_back(vkCtx, descLayout, pipeCI); } - return { timestamp, queries[0], queries[1] }; -} + vk::SemaphoreTypeCreateInfo timelineCI { + .semaphoreType = vk::SemaphoreType::eTimeline, + .initialValue = 0, + }; + vk::SemaphoreCreateInfo semCI { + .pNext = &timelineCI, + }; + // renderFinishedSem = vkCtx.dev.createSemaphore(semCI); -void Renderer::CaptureInputFrame(const std::string& filename) { m_inputImageCapture = filename; } - -void Renderer::CaptureOutputFrame(const std::string& filename) { m_outputImageCapture = filename; } - -std::string Renderer::result_to_str(VkResult result) { - switch (result) { -#define VAL(x) \ - case x: \ - return #x - VAL(VK_SUCCESS); - VAL(VK_NOT_READY); - VAL(VK_TIMEOUT); - VAL(VK_EVENT_SET); - VAL(VK_EVENT_RESET); - VAL(VK_INCOMPLETE); - VAL(VK_ERROR_OUT_OF_HOST_MEMORY); - VAL(VK_ERROR_OUT_OF_DEVICE_MEMORY); - VAL(VK_ERROR_INITIALIZATION_FAILED); - VAL(VK_ERROR_DEVICE_LOST); - VAL(VK_ERROR_MEMORY_MAP_FAILED); - VAL(VK_ERROR_LAYER_NOT_PRESENT); - VAL(VK_ERROR_EXTENSION_NOT_PRESENT); - VAL(VK_ERROR_FEATURE_NOT_PRESENT); - VAL(VK_ERROR_INCOMPATIBLE_DRIVER); - VAL(VK_ERROR_TOO_MANY_OBJECTS); - VAL(VK_ERROR_FORMAT_NOT_SUPPORTED); - VAL(VK_ERROR_FRAGMENTED_POOL); - VAL(VK_ERROR_OUT_OF_POOL_MEMORY); - VAL(VK_ERROR_INVALID_EXTERNAL_HANDLE); - VAL(VK_ERROR_SURFACE_LOST_KHR); - VAL(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); - VAL(VK_SUBOPTIMAL_KHR); - VAL(VK_ERROR_OUT_OF_DATE_KHR); - VAL(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); - VAL(VK_ERROR_VALIDATION_FAILED_EXT); - VAL(VK_ERROR_INVALID_SHADER_NV); - VAL(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); - VAL(VK_ERROR_NOT_PERMITTED_EXT); - VAL(VK_RESULT_MAX_ENUM); -#undef VAL - default: - return "Unknown VkResult"; - } + fence = vkCtx.dev.createFence({}); } -void Renderer::commandBufferBegin() { - VkCommandBufferBeginInfo commandBufferBegin = {}; - commandBufferBegin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VK_CHECK(vkBeginCommandBuffer(m_commandBuffer, &commandBufferBegin)); -} +void Renderer::render(VkContext& vkCtx, u32 leftIdx, u32 rightIdx) { + vk::CommandBufferBeginInfo beginInfo {}; + cmdBuf.begin(beginInfo); -void Renderer::commandBufferSubmit() { - VK_CHECK(vkEndCommandBuffer(m_commandBuffer)); - - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &m_commandBuffer; - VkFenceCreateInfo fenceInfo = {}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - VkFence fence; - VK_CHECK(vkCreateFence(m_dev, &fenceInfo, nullptr, &fence)); - VK_CHECK(vkQueueSubmit(m_queue, 1, &submitInfo, fence)); - VK_CHECK(vkWaitForFences(m_dev, 1, &fence, VK_TRUE, UINT64_MAX)); - vkDestroyFence(m_dev, fence, nullptr); -} + cmdBuf.resetQueryPool(timestampPool, 0, 2); + cmdBuf.writeTimestamp(vk::PipelineStageFlagBits::eTopOfPipe, timestampPool, 0); -void Renderer::addStagingImage(uint32_t width, uint32_t height) { - VkImageCreateInfo imageInfo = {}; - imageInfo = {}; - imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.format = m_format; - imageInfo.extent.width = width; - imageInfo.extent.height = height; - imageInfo.extent.depth = 1; - imageInfo.mipLevels = 1; - imageInfo.arrayLayers = 1; - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VkImage image; - VK_CHECK(vkCreateImage(m_dev, &imageInfo, nullptr, &image)); - - VkMemoryRequirements memoryReqs; - vkGetImageMemoryRequirements(m_dev, image, &memoryReqs); - VkMemoryAllocateInfo memoryAllocInfo = {}; - memoryAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memoryAllocInfo.allocationSize = memoryReqs.size; - memoryAllocInfo.memoryTypeIndex - = memoryTypeIndex(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memoryReqs.memoryTypeBits); - VkDeviceMemory memory; - VK_CHECK(vkAllocateMemory(m_dev, &memoryAllocInfo, nullptr, &memory)); - VK_CHECK(vkBindImageMemory(m_dev, image, memory, 0)); - - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = imageInfo.format; - viewInfo.image = image; - viewInfo.subresourceRange = {}; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - VkImageView view; - VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &view)); - - m_stagingImages.push_back({ image, VK_IMAGE_LAYOUT_UNDEFINED, memory, view }); -} + // TODO: This loop is really scuffed, improve it + Image* prev = nullptr; + for (usize i = 0; i < pipes.size() + 1; ++i) { + Image* out = nullptr; + vk::Extent2D targetExtent; -void Renderer::dumpImage( - VkImage image, - VkImageView imageView, - VkImageLayout imageLayout, - uint32_t width, - uint32_t height, - const std::string& filename -) { - VkImageCreateInfo imageInfo = {}; - imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; - imageInfo.extent.width = width; - imageInfo.extent.height = height; - imageInfo.extent.depth = 1; - imageInfo.arrayLayers = 1; - imageInfo.mipLevels = 1; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.tiling = VK_IMAGE_TILING_LINEAR; - imageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT; - VkImage dstImage; - VK_CHECK(vkCreateImage(m_dev, &imageInfo, nullptr, &dstImage)); - - VkMemoryRequirements memReqs; - VkMemoryAllocateInfo memAllocInfo {}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - vkGetImageMemoryRequirements(m_dev, dstImage, &memReqs); - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = memoryTypeIndex( - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT - | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - memReqs.memoryTypeBits - ); - VkDeviceMemory dstMemory; - VK_CHECK(vkAllocateMemory(m_dev, &memAllocInfo, nullptr, &dstMemory)); - VK_CHECK(vkBindImageMemory(m_dev, dstImage, dstMemory, 0)); - - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = imageInfo.format; - viewInfo.image = dstImage; - viewInfo.subresourceRange = {}; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - VkImageView dstView; - VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &dstView)); - - std::array imageBarrierIn; - imageBarrierIn[0] = {}; - imageBarrierIn[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageBarrierIn[0].oldLayout = imageLayout; - imageBarrierIn[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageBarrierIn[0].image = image; - imageBarrierIn[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBarrierIn[0].subresourceRange.layerCount = 1; - imageBarrierIn[0].subresourceRange.levelCount = 1; - imageBarrierIn[0].srcAccessMask = 0; - imageBarrierIn[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - imageBarrierIn[1] = {}; - imageBarrierIn[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageBarrierIn[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageBarrierIn[1].newLayout = VK_IMAGE_LAYOUT_GENERAL; - imageBarrierIn[1].image = dstImage; - imageBarrierIn[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBarrierIn[1].subresourceRange.layerCount = 1; - imageBarrierIn[1].subresourceRange.levelCount = 1; - imageBarrierIn[1].srcAccessMask = 0; - imageBarrierIn[1].dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - - // Shader - VkShaderModuleCreateInfo moduleInfo = {}; - moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - moduleInfo.codeSize = m_quadShaderSize; - moduleInfo.pCode = m_quadShaderCode; - VkShaderModule shader; - VK_CHECK(vkCreateShaderModule(m_dev, &moduleInfo, nullptr, &shader)); - - // Pipeline - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = &m_descriptorLayout; - VkPipelineLayout pipelineLayout; - VK_CHECK(vkCreatePipelineLayout(m_dev, &pipelineLayoutInfo, nullptr, &pipelineLayout)); - - VkPipelineShaderStageCreateInfo stageInfo = {}; - stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; - stageInfo.pName = "main"; - stageInfo.module = shader; - - VkComputePipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - pipelineInfo.layout = pipelineLayout; - pipelineInfo.stage = stageInfo; - VkPipeline pipeline; - VK_CHECK(vkCreateComputePipelines(m_dev, nullptr, 1, &pipelineInfo, nullptr, &pipeline)); - - std::array imageBarrierOut; - imageBarrierOut[0] = {}; - imageBarrierOut[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageBarrierOut[0].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageBarrierOut[0].newLayout = imageLayout; - imageBarrierOut[0].image = image; - imageBarrierOut[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBarrierOut[0].subresourceRange.layerCount = 1; - imageBarrierOut[0].subresourceRange.levelCount = 1; - imageBarrierOut[0].srcAccessMask = 0; - imageBarrierOut[0].dstAccessMask = 0; - imageBarrierOut[1] = {}; - imageBarrierOut[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageBarrierOut[1].oldLayout = VK_IMAGE_LAYOUT_GENERAL; - imageBarrierOut[1].newLayout = VK_IMAGE_LAYOUT_GENERAL; - imageBarrierOut[1].image = dstImage; - imageBarrierOut[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBarrierOut[1].subresourceRange.layerCount = 1; - imageBarrierOut[1].subresourceRange.levelCount = 1; - imageBarrierOut[1].srcAccessMask = 0; - imageBarrierOut[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - - std::vector descriptorWriteSets; - - VkDescriptorImageInfo descriptorImageInfoIn = {}; - descriptorImageInfoIn.imageView = imageView; - descriptorImageInfoIn.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - VkDescriptorImageInfo descriptorImageInfoOut = {}; - descriptorImageInfoOut.imageView = dstView; - descriptorImageInfoOut.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - - VkWriteDescriptorSet descriptorWriteSet = {}; - descriptorWriteSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWriteSet.descriptorCount = 1; - descriptorWriteSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWriteSet.pImageInfo = &descriptorImageInfoIn; - descriptorWriteSet.dstBinding = 0; - descriptorWriteSets.push_back(descriptorWriteSet); - - descriptorWriteSet.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - descriptorWriteSet.pImageInfo = &descriptorImageInfoOut; - descriptorWriteSet.dstBinding = 1; - descriptorWriteSets.push_back(descriptorWriteSet); - - commandBufferBegin(); - vkCmdPipelineBarrier( - m_commandBuffer, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, - 0, - nullptr, - 0, - nullptr, - imageBarrierIn.size(), - imageBarrierIn.data() - ); - vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); - d.vkCmdPushDescriptorSetKHR( - m_commandBuffer, - VK_PIPELINE_BIND_POINT_COMPUTE, - pipelineLayout, - 0, - descriptorWriteSets.size(), - descriptorWriteSets.data() - ); - vkCmdDispatch( - m_commandBuffer, (imageInfo.extent.width + 7) / 8, (imageInfo.extent.height + 7) / 8, 1 - ); - vkCmdPipelineBarrier( - m_commandBuffer, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, - 0, - nullptr, - 0, - nullptr, - imageBarrierOut.size(), - imageBarrierOut.data() - ); - commandBufferSubmit(); + if (i == pipes.size()) { + out = &output.image; + targetExtent = outExtent; + } else { + out = &stagingImgs[i % StagingImgCount]; + targetExtent = vk::Extent2D { + .width = eyeExtent.width * 2, + .height = eyeExtent.height, + }; + } - VkImageSubresource subresource = {}; - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - VkSubresourceLayout layout; - vkGetImageSubresourceLayout(m_dev, dstImage, &subresource, &layout); + vk::ImageMemoryBarrier imgBarrier { .subresourceRange { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .levelCount = 1, + .layerCount = 1, + } }; - const char* imageData; - VK_CHECK(vkMapMemory(m_dev, dstMemory, 0, VK_WHOLE_SIZE, 0, (void**)&imageData)); - imageData += layout.offset; + std::vector barriers; - std::ofstream file(filename, std::ios::out | std::ios::binary); + if (i == 0) { + prev = &inputImages[leftIdx]; + + for (int i = 0; i < 2; ++i) { + if (prev->layout != vk::ImageLayout::eGeneral) { + imgBarrier.image = prev->image; + imgBarrier.oldLayout = prev->layout; + prev->layout = vk::ImageLayout::eGeneral; + imgBarrier.newLayout = vk::ImageLayout::eGeneral; + imgBarrier.srcAccessMask = vk::AccessFlagBits::eNone; + imgBarrier.dstAccessMask = vk::AccessFlagBits::eTransferRead, + barriers.push_back(imgBarrier); + barriers.push_back(imgBarrier); + } + prev = &inputImages[rightIdx]; + } + } else { + if (prev->layout != vk::ImageLayout::eShaderReadOnlyOptimal) { + imgBarrier.image = prev->image; + imgBarrier.oldLayout = prev->layout; + prev->layout = vk::ImageLayout::eShaderReadOnlyOptimal; + imgBarrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; + imgBarrier.srcAccessMask = vk::AccessFlagBits::eNone; + imgBarrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; + barriers.push_back(imgBarrier); + } + } - // PPM header - file << "P6\n" << width << "\n" << height << "\n" << 255 << "\n"; + // if (out->layout != vk::ImageLayout::eGeneral) { + imgBarrier.image = out->image; + imgBarrier.oldLayout = vk::ImageLayout::eUndefined; + out->layout = vk::ImageLayout::eGeneral; + imgBarrier.newLayout = vk::ImageLayout::eGeneral; + imgBarrier.srcAccessMask = vk::AccessFlagBits::eNone; + imgBarrier.dstAccessMask + = i == 0 ? vk::AccessFlagBits::eTransferWrite : vk::AccessFlagBits::eShaderWrite; + barriers.push_back(imgBarrier); + // } + + if (barriers.size()) { + cmdBuf.pipelineBarrier( + vk::PipelineStageFlagBits::eBottomOfPipe, + i == 0 ? vk::PipelineStageFlagBits::eTransfer + : vk::PipelineStageFlagBits::eComputeShader, + {}, + {}, + {}, + barriers + ); + } - // PPM binary pixel data - for (uint32_t y = 0; y < height; y++) { - uint32_t* row = (uint32_t*)imageData; - for (uint32_t x = 0; x < width; x++) { - file.write((char*)row++, 3); + if (i == 0) { + vk::ImageSubresourceLayers subresLayout { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + vk::ImageCopy2KHR leftCopy { + .srcSubresource = subresLayout, + .srcOffset = {}, + .dstSubresource = subresLayout, + .dstOffset = {}, + .extent = { + .width = eyeExtent.width, + .height = eyeExtent.height, + .depth = 1, + }, + }; + vk::CopyImageInfo2KHR leftCopyInfo { + .srcImage = inputImages[leftIdx].image, + .srcImageLayout = inputImages[leftIdx].layout, + .dstImage = out->image, + .dstImageLayout = out->layout, + .regionCount = 1, + .pRegions = &leftCopy, + }; + cmdBuf.copyImage2KHR(leftCopyInfo, vkCtx.dispatch); + + auto rightCopy = leftCopy; + rightCopy.dstOffset = vk::Offset3D { + .x = static_cast(eyeExtent.width), + .y = 0, + .z = 0, + }; + vk::CopyImageInfo2KHR rightCopyInfo { + .srcImage = inputImages[rightIdx].image, + .srcImageLayout = inputImages[rightIdx].layout, + .dstImage = out->image, + .dstImageLayout = out->layout, + .regionCount = 1, + .pRegions = &rightCopy, + }; + cmdBuf.copyImage2KHR(rightCopyInfo, vkCtx.dispatch); + } else { + pipes[i - 1].render(vkCtx, cmdBuf, prev->view, out->view, targetExtent); } - imageData += layout.rowPitch; + + prev = out; } - file.close(); - std::cout << "Image saved to \"" << filename << "\"" << std::endl; + cmdBuf.writeTimestamp(vk::PipelineStageFlagBits::eBottomOfPipe, timestampPool, 1); + + cmdBuf.end(); + + // vk::TimelineSemaphoreSubmitInfo timelineInfo { + // .waitSemaphoreValueCount = 1, + // .pWaitSemaphoreValues = &waitValue, + // }; + vk::PipelineStageFlags waitStage = vk::PipelineStageFlagBits::eBottomOfPipe; + vk::SubmitInfo submitInfo { + // .pNext = &timelineInfo, + // .waitSemaphoreCount = 1, + // .pWaitSemaphores = &renderFinishedSem, + .pWaitDstStageMask = &waitStage, + .commandBufferCount = 1, + .pCommandBuffers = &cmdBuf, + }; - vkUnmapMemory(m_dev, dstMemory); - vkFreeMemory(m_dev, dstMemory, nullptr); - vkDestroyImage(m_dev, dstImage, nullptr); - vkDestroyImageView(m_dev, dstView, nullptr); - vkDestroyShaderModule(m_dev, shader, nullptr); - vkDestroyPipeline(m_dev, pipeline, nullptr); - vkDestroyPipelineLayout(m_dev, pipelineLayout, nullptr); + vkCtx.useQueue([&](auto& queue) { queue.submit(submitInfo, fence); }); + assert(vkCtx.dev.waitForFences(fence, true, UINT64_MAX) == vk::Result::eSuccess); + vkCtx.dev.resetFences(fence); } -uint32_t Renderer::memoryTypeIndex(VkMemoryPropertyFlags properties, uint32_t typeBits) const { - VkPhysicalDeviceMemoryProperties prop; - vkGetPhysicalDeviceMemoryProperties(m_physDev, &prop); - for (uint32_t i = 0; i < prop.memoryTypeCount; i++) { - if ((prop.memoryTypes[i].propertyFlags & properties) == properties && typeBits & (1 << i)) { - return i; - } - } - return 0xFFFFFFFF; -} +void Renderer::destroy(VkContext const& ctx) { + // ctx.dev.destroy(renderFinishedSem); -// RenderPipeline -RenderPipeline::RenderPipeline(Renderer* render) - : r(render) { } + for (auto& pipe : pipes) { + pipe.destroy(ctx); + } -RenderPipeline::~RenderPipeline() { - vkDestroyShaderModule(r->m_dev, m_shader, nullptr); - vkDestroyPipeline(r->m_dev, m_pipeline, nullptr); - vkDestroyPipelineLayout(r->m_dev, m_pipelineLayout, nullptr); -} + ctx.dev.destroy(fence); + ctx.dev.destroy(descLayout); + ctx.dev.destroy(sampler); -void RenderPipeline::SetShader(const char* filename) { - std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate); - if (!is.is_open()) { - std::cerr << "Failed to open shader file: " << filename << std::endl; - return; - } - size_t size = is.tellg(); - is.seekg(0, std::ios::beg); - std::vector data(size); - is.read(data.data(), size); - SetShader((unsigned char*)data.data(), size); -} + ctx.dev.destroy(cmdPool); + ctx.dev.destroy(timestampPool); -void RenderPipeline::SetShader(const unsigned char* data, unsigned len) { - VkShaderModuleCreateInfo moduleInfo = {}; - moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - moduleInfo.codeSize = len; - moduleInfo.pCode = (uint32_t*)data; - VK_CHECK(vkCreateShaderModule(r->m_dev, &moduleInfo, nullptr, &m_shader)); -} + output.image.destroy(ctx); -void RenderPipeline::Build() { - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = &r->m_descriptorLayout; - VK_CHECK(vkCreatePipelineLayout(r->m_dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout)); - - VkSpecializationInfo specInfo = {}; - specInfo.mapEntryCount = m_constantEntries.size(); - specInfo.pMapEntries = m_constantEntries.data(); - specInfo.dataSize = m_constantSize; - specInfo.pData = m_constant; - - VkPipelineShaderStageCreateInfo stageInfo = {}; - stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; - stageInfo.pName = "main"; - stageInfo.module = m_shader; - if (m_constant) { - stageInfo.pSpecializationInfo = &specInfo; + for (auto& img : stagingImgs) { + img.destroy(ctx); } - VkComputePipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - pipelineInfo.layout = m_pipelineLayout; - pipelineInfo.stage = stageInfo; - VK_CHECK(vkCreateComputePipelines(r->m_dev, nullptr, 1, &pipelineInfo, nullptr, &m_pipeline)); + for (auto& img : inputImages) { + img.destroy(ctx); + } } -void RenderPipeline::Render(VkImageView in, VkImageView out, VkRect2D outSize) { - vkCmdBindPipeline(r->m_commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline); - - VkDescriptorImageInfo descriptorImageInfoIn = {}; - descriptorImageInfoIn.imageView = in; - descriptorImageInfoIn.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - VkDescriptorImageInfo descriptorImageInfoOut = {}; - descriptorImageInfoOut.imageView = out; - descriptorImageInfoOut.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - - VkWriteDescriptorSet descriptorWriteSets[2] = {}; - descriptorWriteSets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWriteSets[0].descriptorCount = 1; - descriptorWriteSets[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWriteSets[0].pImageInfo = &descriptorImageInfoIn; - descriptorWriteSets[0].dstBinding = 0; - descriptorWriteSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWriteSets[1].descriptorCount = 1; - descriptorWriteSets[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - descriptorWriteSets[1].pImageInfo = &descriptorImageInfoOut; - descriptorWriteSets[1].dstBinding = 1; - r->d.vkCmdPushDescriptorSetKHR( - r->m_commandBuffer, - VK_PIPELINE_BIND_POINT_COMPUTE, - m_pipelineLayout, - 0, - 2, - descriptorWriteSets - ); - - vkCmdDispatch( - r->m_commandBuffer, (outSize.extent.width + 7) / 8, (outSize.extent.height + 7) / 8, 1 - ); } diff --git a/alvr/server_openvr/cpp/platform/linux/Renderer.h b/alvr/server_openvr/cpp/platform/linux/Renderer.h deleted file mode 100644 index 724f3bd8c7..0000000000 --- a/alvr/server_openvr/cpp/platform/linux/Renderer.h +++ /dev/null @@ -1,183 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#define VK_CHECK(f) \ - { \ - VkResult res = (f); \ - if (res != VK_SUCCESS) { \ - std::cerr << Renderer::result_to_str(res) << "at" << __FILE__ << ":" << __LINE__ \ - << std::endl; \ - throw std::runtime_error( \ - "Vulkan: " + Renderer::result_to_str(res) + "at " __FILE__ ":" \ - + std::to_string(__LINE__) \ - ); \ - } \ - } - -struct DrmImage { - int fd = -1; - uint32_t format = 0; - uint64_t modifier = 0; - uint32_t planes = 0; - std::array strides; - std::array offsets; -}; - -class RenderPipeline; - -class Renderer { -public: - enum class ExternalHandle { None, DmaBuf, OpaqueFd }; - - struct Output { - VkImage image = VK_NULL_HANDLE; - VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; - VkImageCreateInfo imageInfo; - VkDeviceSize size = 0; - VkDeviceMemory memory = VK_NULL_HANDLE; - VkSemaphore semaphore = VK_NULL_HANDLE; - // --- - VkImageView view = VK_NULL_HANDLE; - // --- - DrmImage drm; - }; - - struct Timestamps { - uint64_t now; - uint64_t renderBegin; - uint64_t renderComplete; - }; - - explicit Renderer( - const VkInstance& inst, - const VkDevice& dev, - const VkPhysicalDevice& physDev, - uint32_t queueIdx, - const std::vector& devExtensions - ); - virtual ~Renderer(); - - void Startup(uint32_t width, uint32_t height, VkFormat format); - - void AddImage(VkImageCreateInfo imageInfo, size_t memoryIndex, int imageFd, int semaphoreFd); - - void AddPipeline(RenderPipeline* pipeline); - - void CreateOutput(uint32_t width, uint32_t height, ExternalHandle handle); - void ImportOutput(const DrmImage& drm); - - void Render(uint32_t index, uint64_t waitValue); - - void Sync(); - - Output& GetOutput(); - Timestamps GetTimestamps(); - - void CaptureInputFrame(const std::string& filename); - void CaptureOutputFrame(const std::string& filename); - - static std::string result_to_str(VkResult result); - - // private: - struct InputImage { - VkImage image = VK_NULL_HANDLE; - VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; - VkDeviceMemory memory = VK_NULL_HANDLE; - VkSemaphore semaphore = VK_NULL_HANDLE; - VkImageView view = VK_NULL_HANDLE; - }; - - struct StagingImage { - VkImage image = VK_NULL_HANDLE; - VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; - VkDeviceMemory memory = VK_NULL_HANDLE; - VkImageView view = VK_NULL_HANDLE; - }; - - void commandBufferBegin(); - void commandBufferSubmit(); - void addStagingImage(uint32_t width, uint32_t height); - void dumpImage( - VkImage image, - VkImageView imageView, - VkImageLayout imageLayout, - uint32_t width, - uint32_t height, - const std::string& filename - ); - uint32_t memoryTypeIndex(VkMemoryPropertyFlags properties, uint32_t typeBits) const; - - struct { - PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr; - PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR = nullptr; - PFN_vkGetMemoryFdPropertiesKHR vkGetMemoryFdPropertiesKHR = nullptr; - PFN_vkGetImageDrmFormatModifierPropertiesEXT vkGetImageDrmFormatModifierPropertiesEXT - = nullptr; - PFN_vkGetCalibratedTimestampsEXT vkGetCalibratedTimestampsEXT = nullptr; - PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSetKHR = nullptr; - bool haveDmaBuf = false; - bool haveDrmModifiers = false; - bool haveCalibratedTimestamps = false; - } d; - - Output m_output; - std::vector m_images; - std::vector m_stagingImages; - std::vector m_pipelines; - - VkInstance m_inst = VK_NULL_HANDLE; - VkDevice m_dev = VK_NULL_HANDLE; - VkPhysicalDevice m_physDev = VK_NULL_HANDLE; - VkQueue m_queue = VK_NULL_HANDLE; - uint32_t m_queueFamilyIndex = 0; - VkFormat m_format = VK_FORMAT_UNDEFINED; - VkExtent2D m_imageSize = { 0, 0 }; - VkQueryPool m_queryPool = VK_NULL_HANDLE; - VkCommandPool m_commandPool = VK_NULL_HANDLE; - VkSampler m_sampler = VK_NULL_HANDLE; - VkDescriptorSetLayout m_descriptorLayout = VK_NULL_HANDLE; - VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE; - VkFence m_fence = VK_NULL_HANDLE; - double m_timestampPeriod = 0; - - size_t m_quadShaderSize = 0; - const uint32_t* m_quadShaderCode = nullptr; - - std::string m_inputImageCapture; - std::string m_outputImageCapture; -}; - -class RenderPipeline { -public: - explicit RenderPipeline(Renderer* render); - virtual ~RenderPipeline(); - - void SetShader(const char* filename); - void SetShader(const unsigned char* data, unsigned len); - - template - void SetConstants(const T* data, std::vector&& entries) { - m_constant = static_cast(data); - m_constantSize = sizeof(T); - m_constantEntries = std::move(entries); - } - -private: - void Build(); - void Render(VkImageView in, VkImageView out, VkRect2D outSize); - - Renderer* r; - VkShaderModule m_shader = VK_NULL_HANDLE; - const void* m_constant = nullptr; - uint32_t m_constantSize = 0; - std::vector m_constantEntries; - VkPipeline m_pipeline = VK_NULL_HANDLE; - VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; - - friend class Renderer; -}; diff --git a/alvr/server_openvr/cpp/platform/linux/Renderer.hpp b/alvr/server_openvr/cpp/platform/linux/Renderer.hpp new file mode 100644 index 0000000000..b14b019794 --- /dev/null +++ b/alvr/server_openvr/cpp/platform/linux/Renderer.hpp @@ -0,0 +1,123 @@ +#pragma once + +#define VULKAN_HPP_NO_CONSTRUCTORS +#include +#include +#include +#include +#include +#include + +#include "VkContext.hpp" +#include "utils.hpp" + +#include "ffmpeg_helper.h" + +namespace alvr::render { + +constexpr u32 ImageCount = 6; +constexpr usize StagingImgCount = 2; + +enum class HandleType { + None, + DmaBuf, + OpaqueFd, +}; + +struct Image { + vk::Image image = VK_NULL_HANDLE; + vk::ImageLayout layout = vk::ImageLayout::eUndefined; + vk::DeviceMemory memory = VK_NULL_HANDLE; + vk::ImageView view = VK_NULL_HANDLE; + + void destroy(VkContext const& ctx) { + ctx.dev.destroy(view); + ctx.dev.free(memory); + ctx.dev.destroy(image); + } +}; + +struct Output { + Image image; + DrmImage drm; + // VkSemaphore semaphore; + VkImageCreateInfo imageCI; + VkDeviceSize size; +}; + +struct PipelineCreateInfo { + std::vector shaderData; + std::vector specs; + std::vector specData; +}; + +struct RendererCreateInfo { + vk::Format format; + vk::Extent2D inputEyeExtent; + vk::Extent2D outputExtent; + std::array inputImgFds; +}; + +namespace detail { + + class RenderPipeline { + vk::ShaderModule shader; + vk::PipelineLayout pipeLayout; + vk::Pipeline pipe; + + public: + RenderPipeline( + VkContext const& ctx, vk::DescriptorSetLayout& layout, PipelineCreateInfo& pipelineCI + ); + + void render( + VkContext const& ctx, + vk::CommandBuffer cmdBuf, + vk::ImageView in, + vk::ImageView out, + vk::Extent2D outSize + ); + + void destroy(VkContext const& ctx); + }; + +} + +class Renderer { + vk::Extent2D eyeExtent; + vk::Extent2D outExtent; + + Image inputImages[ImageCount]; + Image stagingImgs[StagingImgCount]; + Output output; + + vk::QueryPool timestampPool; + vk::CommandPool cmdPool; + vk::CommandBuffer cmdBuf; + vk::Sampler sampler; + vk::DescriptorSetLayout descLayout; + vk::Fence fence; + + std::vector pipes; + + // vk::Semaphore renderFinishedSem; + +public: + Renderer( + VkContext const& vkCtx, + RendererCreateInfo& createInfo, + std::vector pipeCIs + ); + + // TODO: Import output (somehow?) + + // NOTE: Use the output immediately afterwards, as this synchronizes to the end of gpu + // operations + void render(VkContext& vkCtx, u32 leftIdx, u32 rightIdx); + + Output getOutput() { return output; } + + void destroy(VkContext const& ctx); +}; + +} diff --git a/alvr/server_openvr/cpp/platform/linux/VkContext.hpp b/alvr/server_openvr/cpp/platform/linux/VkContext.hpp new file mode 100644 index 0000000000..ae776dcc89 --- /dev/null +++ b/alvr/server_openvr/cpp/platform/linux/VkContext.hpp @@ -0,0 +1,210 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define VULKAN_HPP_NO_CONSTRUCTORS +#include +#include +#include +#include +#include +#include +#include + +#include "utils.hpp" + +extern "C" { +#include +} + +namespace alvr { + +enum class Vendor { Amd, Intel, Nvidia }; + +class VkContext { +public: + vk::Instance instance; + vk::PhysicalDevice physDev; + vk::Device dev; + + vk::detail::DispatchLoaderDynamic dispatch; + + struct Meta { + Vendor vendor; + + std::vector instExtensions; + std::vector devExtensions; + + vk::PhysicalDeviceVulkan12Features feats12; + vk::PhysicalDeviceFeatures2 feats; + + u32 queueFamily; + u32 queueIndex; + } meta; + +private: + vk::Queue queue; + + void sharedInit() { + auto devProps = physDev.getProperties2(); + + if (devProps.properties.vendorID == 0x1002) + meta.vendor = Vendor::Amd; + else if (devProps.properties.vendorID == 0x8086) + meta.vendor = Vendor::Intel; + else if (devProps.properties.vendorID == 0x10de) + meta.vendor = Vendor::Nvidia; + + dispatch = { instance, vkGetInstanceProcAddr }; + } + +public: + VkContext(std::vector deviceUUID) { + std::vector wantedInstExts = { + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, + }; + + auto availInstExts = vk::enumerateInstanceExtensionProperties(); + std::vector acquiredInstExts; + for (auto wantedName : wantedInstExts) { + auto it = std::find_if(availInstExts.begin(), availInstExts.end(), [&](auto& ext) { + return wantedName == ext.extensionName; + }); + if (it != availInstExts.end()) + acquiredInstExts.push_back(wantedName.data()); + else + assert(false); + } + + vk::ApplicationInfo appInfo { + .pApplicationName = "ALVR", + .apiVersion = VK_API_VERSION_1_2, + }; + vk::InstanceCreateInfo instanceCI { + .pApplicationInfo = &appInfo, + .enabledExtensionCount = static_cast(acquiredInstExts.size()), + .ppEnabledExtensionNames = acquiredInstExts.data(), + }; + instance = vk::createInstance(instanceCI); + auto physDevs = instance.enumeratePhysicalDevices(); + + physDev = physDevs[0]; + // for (auto dev : physDevs) { + // vk::PhysicalDeviceVulkan11Properties props11{}; + // vk::PhysicalDeviceProperties2 props { + // .pNext = &props11, + // }; + // dev.getProperties2(&props); + + // assert(deviceUUID.size() == VK_UUID_SIZE); + // if (memcmp(props11.deviceUUID, deviceUUID.data(), VK_UUID_SIZE) == 0) { + // physDev = dev; + // break; + // } + // } + // if (!physDev && !physDevs.empty()) { + // Warn("Falling back to first physical device"); + // physDev = physDevs[0]; + // } + // if (!physDev) { + // throw std::runtime_error("Failed to find vulkan device"); + // } + + auto queueFamilyProps = physDev.getQueueFamilyProperties(); + + std::optional wantedQueueFamily; + + for (u32 i = 0; i < queueFamilyProps.size(); ++i) { + auto& props = queueFamilyProps[i]; + bool isGraphics = static_cast(props.queueFlags & vk::QueueFlagBits::eGraphics); + bool isCompute = static_cast(props.queueFlags & vk::QueueFlagBits::eCompute); + + if (isCompute && (!wantedQueueFamily.has_value() || !isGraphics)) { + wantedQueueFamily = i; + } + } + meta.queueFamily = wantedQueueFamily.value(); + meta.queueIndex = 0; + + std::vector wantedExts = { + VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, + VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, + VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, + VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, + VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, + VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, + VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME, + VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, + VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME, + }; + + auto availExts = physDev.enumerateDeviceExtensionProperties(); + + std::vector acquiredExts; + for (auto name : wantedExts) { + auto it = std::find_if(availExts.begin(), availExts.end(), [&](auto& other) { + return name == (const char*)other.extensionName; + }); + if (it != availExts.end()) + acquiredExts.push_back(name.data()); + } + + f32 queuePrio = 1.f; + vk::DeviceQueueCreateInfo queueCI { + .queueFamilyIndex = meta.queueFamily, + .queueCount = 1, + .pQueuePriorities = &queuePrio, + }; + + meta.feats12 = vk::PhysicalDeviceVulkan12Features { + .timelineSemaphore = 1, + }; + + meta.feats = vk::PhysicalDeviceFeatures2 { + .pNext = &meta.feats12, + .features = { + .robustBufferAccess = true, + .samplerAnisotropy = true, + }, + }; + vk::DeviceCreateInfo devCI { + .pNext = &meta.feats, + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queueCI, + .enabledExtensionCount = static_cast(acquiredExts.size()), + .ppEnabledExtensionNames = acquiredExts.data(), + }; + dev = physDev.createDevice(devCI); + + meta.devExtensions = acquiredExts; + + queue = dev.getQueue(wantedQueueFamily.value(), meta.queueIndex); + + sharedInit(); + } + + void useQueue(std::function fn) { fn(queue); } + + void destroy() { + dev.destroy(); + instance.destroy(); + } +}; + +} diff --git a/alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.cpp b/alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.cpp index 5ada8c6965..5514d4242e 100644 --- a/alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.cpp +++ b/alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.cpp @@ -1,4 +1,5 @@ #include "ffmpeg_helper.h" +#include "VkContext.hpp" #include #include @@ -16,13 +17,16 @@ extern "C" { } namespace { -// it seems that ffmpeg does not provide this mapping +// TODO: I don't think this selects the optimal format actually +// Seeing as we don't want any re-encoding here yet +// We just want it to make the drm image 1:1 AVPixelFormat vk_format_to_av_format(vk::Format vk_fmt) { for (int f = AV_PIX_FMT_NONE; f < AV_PIX_FMT_NB; ++f) { auto current_fmt = av_vkfmt_from_pixfmt(AVPixelFormat(f)); if (current_fmt and *current_fmt == (VkFormat)vk_fmt) return AVPixelFormat(f); } + throw std::runtime_error("unsupported vulkan pixel format " + std::to_string((VkFormat)vk_fmt)); } } @@ -33,251 +37,26 @@ std::string alvr::AvException::makemsg(const std::string& msg, int averror) { return msg + " " + av_msg; } -alvr::VkContext::VkContext( - const uint8_t* deviceUUID, const std::vector& requiredDeviceExtensions -) { - std::vector instance_extensions = { - VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, - VK_KHR_SURFACE_EXTENSION_NAME, - }; - - std::vector device_extensions = { - VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, - VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, - VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, - VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, - VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, - VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, - VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, - VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, - VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME, - VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, - }; - device_extensions.insert( - device_extensions.end(), requiredDeviceExtensions.begin(), requiredDeviceExtensions.end() - ); - - uint32_t instanceExtensionCount = 0; - vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, nullptr); - std::vector instanceExts(instanceExtensionCount); - vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, instanceExts.data()); - for (const char* name : instance_extensions) { - auto it = std::find_if( - instanceExts.begin(), - instanceExts.end(), - [name](VkExtensionProperties e) { return strcmp(e.extensionName, name) == 0; } - ); - if (it != instanceExts.end()) { - instanceExtensions.push_back(name); - } - } - - VkApplicationInfo appInfo = {}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = "ALVR"; - appInfo.apiVersion = VK_API_VERSION_1_2; - - VkInstanceCreateInfo instanceInfo = {}; - instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instanceInfo.pApplicationInfo = &appInfo; - -#ifdef DEBUG - const char* validationLayers[] = { "VK_LAYER_KHRONOS_validation" }; - instanceInfo.ppEnabledLayerNames = validationLayers; - instanceInfo.enabledLayerCount = 1; -#endif - - instanceInfo.enabledExtensionCount = instanceExtensions.size(); - instanceInfo.ppEnabledExtensionNames = instanceExtensions.data(); - VK_CHECK(vkCreateInstance(&instanceInfo, nullptr, &instance)); - - uint32_t deviceCount = 0; - VK_CHECK(vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr)); - std::vector physicalDevices(deviceCount); - VK_CHECK(vkEnumeratePhysicalDevices(instance, &deviceCount, physicalDevices.data())); - for (VkPhysicalDevice dev : physicalDevices) { - VkPhysicalDeviceVulkan11Properties props11 = {}; - props11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; - - VkPhysicalDeviceProperties2 props = {}; - props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - props.pNext = &props11; - vkGetPhysicalDeviceProperties2(dev, &props); - if (memcmp(props11.deviceUUID, deviceUUID, VK_UUID_SIZE) == 0) { - physicalDevice = dev; - break; - } - } - if (!physicalDevice && !physicalDevices.empty()) { - Warn("Falling back to first device"); - physicalDevice = physicalDevices[0]; - } - if (!physicalDevice) { - throw std::runtime_error("Failed to find vulkan device."); - } - - VkPhysicalDeviceDrmPropertiesEXT drmProps = {}; - drmProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT; - - VkPhysicalDeviceProperties2 deviceProps = {}; - deviceProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - deviceProps.pNext = &drmProps; - vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProps); - - amd = deviceProps.properties.vendorID == 0x1002; - intel = deviceProps.properties.vendorID == 0x8086; - nvidia = deviceProps.properties.vendorID == 0x10de; - Info("Using Vulkan device %s", deviceProps.properties.deviceName); - - uint32_t deviceExtensionCount = 0; - VK_CHECK(vkEnumerateDeviceExtensionProperties( - physicalDevice, nullptr, &deviceExtensionCount, nullptr - )); - std::vector deviceExts(deviceExtensionCount); - VK_CHECK(vkEnumerateDeviceExtensionProperties( - physicalDevice, nullptr, &deviceExtensionCount, deviceExts.data() - )); - for (const char* name : device_extensions) { - auto it - = std::find_if(deviceExts.begin(), deviceExts.end(), [name](VkExtensionProperties e) { - return strcmp(e.extensionName, name) == 0; - }); - if (it != deviceExts.end()) { - deviceExtensions.push_back(name); - } - } - - float queuePriority = 1.0; - std::vector queueInfos; - - uint32_t queueFamilyCount; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); - std::vector queueFamilyProperties(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties( - physicalDevice, &queueFamilyCount, queueFamilyProperties.data() - ); - for (uint32_t i = 0; i < queueFamilyProperties.size(); ++i) { - const bool graphics = queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT; - const bool compute = queueFamilyProperties[i].queueFlags & VK_QUEUE_COMPUTE_BIT; - if (compute && (queueFamilyIndex == VK_QUEUE_FAMILY_IGNORED || !graphics)) { - queueFamilyIndex = i; - } - VkDeviceQueueCreateInfo queueInfo = {}; - queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo.queueFamilyIndex = i; - queueInfo.queueCount = 1; - queueInfo.pQueuePriorities = &queuePriority; - queueInfos.push_back(queueInfo); - } - - VkPhysicalDeviceVulkan12Features features12 = {}; - features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; - features12.timelineSemaphore = true; - - VkPhysicalDeviceFeatures2 features = {}; - features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - features.pNext = &features12; - features.features.samplerAnisotropy = VK_TRUE; - - VkDeviceCreateInfo deviceInfo = {}; - deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceInfo.pNext = &features; - deviceInfo.queueCreateInfoCount = queueInfos.size(); - deviceInfo.pQueueCreateInfos = queueInfos.data(); - deviceInfo.enabledExtensionCount = deviceExtensions.size(); - deviceInfo.ppEnabledExtensionNames = deviceExtensions.data(); - VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device)); - - for (int i = 128; i < 136; ++i) { - auto path = "/dev/dri/renderD" + std::to_string(i); - int fd = open(path.c_str(), O_RDONLY); - if (fd == -1) { - continue; - } - struct stat s = {}; - int ret = fstat(fd, &s); - close(fd); - if (ret != 0) { - continue; - } - dev_t primaryDev = makedev(drmProps.primaryMajor, drmProps.primaryMinor); - dev_t renderDev = makedev(drmProps.renderMajor, drmProps.renderMinor); - if (primaryDev == s.st_rdev || renderDev == s.st_rdev) { - devicePath = path; - break; - } - } - if (devicePath.empty()) { - devicePath = "/dev/dri/renderD128"; - } - Info("Using device path %s", devicePath.c_str()); - - ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VULKAN); - AVHWDeviceContext* hwctx = (AVHWDeviceContext*)ctx->data; - AVVulkanDeviceContext* vkctx = (AVVulkanDeviceContext*)hwctx->hwctx; - - vkctx->alloc = nullptr; - vkctx->inst = instance; - vkctx->phys_dev = physicalDevice; - vkctx->act_dev = device; - vkctx->device_features = features; - vkctx->queue_family_index = queueFamilyIndex; - vkctx->nb_graphics_queues = 1; - vkctx->queue_family_tx_index = queueFamilyIndex; - vkctx->nb_tx_queues = 1; - vkctx->queue_family_comp_index = queueFamilyIndex; - vkctx->nb_comp_queues = 1; - vkctx->get_proc_addr = vkGetInstanceProcAddr; - vkctx->queue_family_encode_index = -1; - vkctx->nb_encode_queues = 0; - vkctx->queue_family_decode_index = -1; - vkctx->nb_decode_queues = 0; - - char** inst_extensions = (char**)malloc(sizeof(char*) * instanceExtensions.size()); - for (uint32_t i = 0; i < instanceExtensions.size(); ++i) { - inst_extensions[i] = strdup(instanceExtensions[i]); - } - vkctx->enabled_inst_extensions = inst_extensions; - vkctx->nb_enabled_inst_extensions = instanceExtensions.size(); - - char** dev_extensions = (char**)malloc(sizeof(char*) * deviceExtensions.size()); - for (uint32_t i = 0; i < deviceExtensions.size(); ++i) { - dev_extensions[i] = strdup(deviceExtensions[i]); - } - vkctx->enabled_dev_extensions = dev_extensions; - vkctx->nb_enabled_dev_extensions = deviceExtensions.size(); - - int ret = av_hwdevice_ctx_init(ctx); - if (ret) - throw AvException("failed to initialize ffmpeg", ret); -} - -alvr::VkContext::~VkContext() { - av_buffer_unref(&ctx); - vkDestroyDevice(device, nullptr); - vkDestroyInstance(instance, nullptr); -} - -alvr::VkFrameCtx::VkFrameCtx(VkContext& vkContext, vk::ImageCreateInfo image_create_info) { - AVHWFramesContext* frames_ctx = NULL; - int err = 0; - - if (!(ctx = av_hwframe_ctx_alloc(vkContext.ctx))) { - throw std::runtime_error("Failed to create vulkan frame context."); - } - frames_ctx = (AVHWFramesContext*)(ctx->data); - frames_ctx->format = AV_PIX_FMT_VULKAN; - frames_ctx->sw_format = vk_format_to_av_format(image_create_info.format); - frames_ctx->width = image_create_info.extent.width; - frames_ctx->height = image_create_info.extent.height; - frames_ctx->initial_pool_size = 0; - if ((err = av_hwframe_ctx_init(ctx)) < 0) { - av_buffer_unref(&ctx); - throw alvr::AvException("Failed to initialize vulkan frame context:", err); - } -} - -alvr::VkFrameCtx::~VkFrameCtx() { av_buffer_unref(&ctx); } +// alvr::VkFrameCtx::VkFrameCtx(VkContext& vkContext, vk::ImageCreateInfo image_create_info) { +// AVHWFramesContext* frames_ctx = NULL; +// int err = 0; + +// if (!(ctx = av_hwframe_ctx_alloc(vkContext.ctx))) { +// throw std::runtime_error("Failed to create vulkan frame context."); +// } +// frames_ctx = (AVHWFramesContext*)(ctx->data); +// frames_ctx->format = AV_PIX_FMT_VULKAN; +// frames_ctx->sw_format = vk_format_to_av_format(image_create_info.format); +// frames_ctx->width = image_create_info.extent.width; +// frames_ctx->height = image_create_info.extent.height; +// frames_ctx->initial_pool_size = 0; +// if ((err = av_hwframe_ctx_init(ctx)) < 0) { +// av_buffer_unref(&ctx); +// throw alvr::AvException("Failed to initialize vulkan frame context:", err); +// } +// } + +// alvr::VkFrameCtx::~VkFrameCtx() { av_buffer_unref(&ctx); } alvr::VkFrame::VkFrame( const VkContext& vk_ctx, @@ -289,7 +68,7 @@ alvr::VkFrame::VkFrame( ) : vkimage(image) , vkimageinfo(image_info) { - device = vk_ctx.get_vk_device(); + device = vk_ctx.dev; avformat = vk_format_to_av_format(vk::Format(image_info.format)); av_drmframe = (AVDRMFrameDescriptor*)malloc(sizeof(AVDRMFrameDescriptor)); diff --git a/alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.h b/alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.h index fef9923b8e..85fde9463b 100644 --- a/alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.h +++ b/alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.h @@ -2,11 +2,8 @@ #include #include -#include extern "C" { -#include - #include #include @@ -14,6 +11,7 @@ extern "C" { #include #include +#include #include #include #include @@ -21,7 +19,7 @@ extern "C" { #include } -#include "Renderer.h" +#include "VkContext.hpp" namespace alvr { @@ -36,28 +34,62 @@ class AvException : public std::runtime_error { static std::string makemsg(const std::string& msg, int averror); }; -class VkContext { +struct DrmImage { + int fd = -1; + uint32_t format = 0; + uint64_t modifier = 0; + uint32_t planes = 0; + std::array strides; + std::array offsets; +}; + +class HWContext { public: - VkContext(const uint8_t* deviceUUID, const std::vector& requiredDeviceExtensions); - ~VkContext(); - VkDevice get_vk_device() const { return device; } - VkInstance get_vk_instance() const { return instance; } - VkPhysicalDevice get_vk_phys_device() const { return physicalDevice; } - uint32_t get_vk_queue_family_index() const { return queueFamilyIndex; } - std::vector get_vk_instance_extensions() const { return instanceExtensions; } - std::vector get_vk_device_extensions() const { return deviceExtensions; } - - AVBufferRef* ctx = nullptr; - VkInstance instance = VK_NULL_HANDLE; - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VkDevice device = VK_NULL_HANDLE; - uint32_t queueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - std::vector instanceExtensions; - std::vector deviceExtensions; - bool amd = false; - bool intel = false; - bool nvidia = false; - std::string devicePath; + AVBufferRef* avCtx; + + HWContext(VkContext const& vkCtx) { + avCtx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VULKAN); + auto hwCtx = (AVHWDeviceContext*)avCtx->data; + auto avVk = (AVVulkanDeviceContext*)hwCtx->hwctx; + + avVk->alloc = nullptr; + avVk->get_proc_addr = vkGetInstanceProcAddr; + + avVk->inst = vkCtx.instance; + avVk->phys_dev = vkCtx.physDev; + avVk->act_dev = vkCtx.dev; + + avVk->device_features = vkCtx.meta.feats; + + auto queueFam = vkCtx.meta.queueFamily; + + avVk->nb_graphics_queues = 1; + avVk->queue_family_index = queueFam; + + avVk->nb_tx_queues = 1; + avVk->queue_family_tx_index = queueFam; + + avVk->nb_comp_queues = 1; + avVk->queue_family_comp_index = queueFam; + + avVk->nb_encode_queues = 0; + avVk->queue_family_encode_index = -1; + + avVk->nb_decode_queues = 0; + avVk->queue_family_decode_index = -1; + + avVk->nb_enabled_inst_extensions = vkCtx.meta.instExtensions.size(); + avVk->enabled_inst_extensions = vkCtx.meta.instExtensions.data(); + + avVk->nb_enabled_dev_extensions = vkCtx.meta.devExtensions.size(); + avVk->enabled_dev_extensions = vkCtx.meta.devExtensions.data(); + + int ret = av_hwdevice_ctx_init(avCtx); + if (ret) + throw AvException("failed to initialize ffmpeg", ret); + } + + ~HWContext() { av_buffer_unref(&avCtx); } }; class VkFrameCtx { diff --git a/alvr/server_openvr/cpp/platform/linux/utils.hpp b/alvr/server_openvr/cpp/platform/linux/utils.hpp new file mode 100644 index 0000000000..00bf515bac --- /dev/null +++ b/alvr/server_openvr/cpp/platform/linux/utils.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include + +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; +using u64 = uint64_t; +using usize = size_t; + +using i8 = int8_t; +using i16 = int16_t; +using i32 = int32_t; +using i64 = int64_t; + +using f32 = float; +using f64 = double; + +// This actually has memory stability guarantees +template class Optional { + union { + T value; + char dummy; + }; + + bool hasValue_ = false; + +public: + Optional() { } + + template void emplace(CTs&&... cvals) { + if (hasValue_) + value.~T(); + + new (&value) T { std::forward(cvals)... }; + hasValue_ = true; + } + + T& get() { + if (hasValue_) + return value; + + assert(false); + } + + bool hasValue() { return hasValue_; } + + ~Optional() { + if (hasValue_) + value.~T(); + + hasValue_ = false; + } +}; diff --git a/alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.cpp b/alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.cpp index 7bb7a30547..2e7cd41d9d 100644 --- a/alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.cpp +++ b/alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.cpp @@ -259,7 +259,7 @@ void OvrDirectModeComponent::Present(vr::SharedTextureHandle_t syncTexture) { m_presentMutex.unlock(); } -void OvrDirectModeComponent::PostPresent() { +void OvrDirectModeComponent::PostPresent(const Throttling_t* pThrottling) { Debug("OvrDirectModeComponent::PostPresent"); WaitForVSync(); diff --git a/alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.h b/alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.h index adef8973ea..cbe492d1b6 100644 --- a/alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.h +++ b/alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.h @@ -46,7 +46,7 @@ class OvrDirectModeComponent : public vr::IVRDriverDirectModeComponent { /** Called after Present to allow driver to take more time until vsync after they've * successfully acquired the sync texture in Present.*/ - virtual void PostPresent(); + virtual void PostPresent(const Throttling_t* pThrottling); void CopyTexture(uint32_t layerCount); diff --git a/alvr/vrcompositor_wrapper/Cargo.toml b/alvr/vrcompositor_wrapper/Cargo.toml deleted file mode 100644 index c76bd2ac0c..0000000000 --- a/alvr/vrcompositor_wrapper/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "alvr_vrcompositor_wrapper" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -authors.workspace = true -license.workspace = true - -[dependencies] -alvr_common.workspace = true -alvr_filesystem.workspace = true - -[build-dependencies] -xshell = "0.2" - -[target.'cfg(target_os = "linux")'.dependencies] -exec = "0.3.1" diff --git a/alvr/vrcompositor_wrapper/build.rs b/alvr/vrcompositor_wrapper/build.rs deleted file mode 100644 index 6db0bab17b..0000000000 --- a/alvr/vrcompositor_wrapper/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -#[cfg(target_os = "linux")] -fn main() { - use std::{env, path::PathBuf}; - use xshell::{Shell, cmd}; - - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let target_dir = out_dir.join("../../.."); - - let sh = Shell::new().unwrap(); - let command = format!( - "g++ -shared -fPIC $(pkg-config --cflags libdrm) drm-lease-shim.cpp -o {}/alvr_drm_lease_shim.so", - target_dir.display() - ); - cmd!(sh, "bash -c {command}").run().unwrap(); -} - -#[cfg(not(target_os = "linux"))] -fn main() {} diff --git a/alvr/vrcompositor_wrapper/drm-lease-shim.cpp b/alvr/vrcompositor_wrapper/drm-lease-shim.cpp deleted file mode 100644 index 6d2a49c43b..0000000000 --- a/alvr/vrcompositor_wrapper/drm-lease-shim.cpp +++ /dev/null @@ -1,233 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define PICOJSON_USE_INT64 -#include "../server_openvr/cpp/alvr_server/include/picojson.h" - -#define LOAD_FN(f) \ - if (!real_##f) { \ - real_##f = reinterpret_cast(dlsym(RTLD_NEXT, #f)); \ - if (!real_##f) { \ - ERR("Failed to load %s", #f); \ - abort(); \ - } \ - } \ - -#define LOG(f, ...) printf(f "\n" __VA_OPT__(,) __VA_ARGS__) -#define ERR(f, ...) fprintf(stderr, f "\n" __VA_OPT__(,) __VA_ARGS__) - -template -static constexpr bool compare_ptr(X x, Y y) -{ - return reinterpret_cast(x) == reinterpret_cast(y); -} - -struct wl_registry_listener { - void (*global)(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version); - void (*global_remove)(void *data, struct wl_registry *wl_registry, uint32_t name); -}; - -struct wp_drm_lease_device_v1_listener { - void (*drm_fd)(void *data, struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1, int32_t fd); - void (*connector)(void *data, struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1, struct wp_drm_lease_connector_v1 *id); - void (*done)(void *data, struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1); - void (*released)(void *data, struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1); -}; - -struct wp_drm_lease_connector_v1_listener { - void (*name)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, const char *name); - void (*description)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, const char *description); - void (*connector_id)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, uint32_t connector_id); - void (*done)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1); - void (*withdrawn)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1); -}; - -struct wp_drm_lease_v1_listener { - void (*lease_fd)(void *data, struct wp_drm_lease_v1 *wp_drm_lease_v1, int32_t leased_fd); - void (*finished)(void *data, struct wp_drm_lease_v1 *wp_drm_lease_v1); -}; - -static struct wp_drm_lease_device_v1 {} fake_device_id; -static struct wp_drm_lease_connector_v1 {} fake_connector_id; -static struct wp_drm_lease_request_v1 {} fake_lease_request_id; -static struct wp_drm_lease_v1 {} fake_lease_id; - -static int drm_fd = -1; -static int drm_connector_id = -1; - -static void open_drm_fd() -{ - static drmModeResPtr (*real_drmModeGetResources)(int fd) = nullptr; - LOAD_FN(drmModeGetResources); - for(auto cardCandidate : std::filesystem::directory_iterator("/dev/dri")) { - if(cardCandidate.path().filename().string().rfind("card", 0) == 0) { - LOG("cardCandidateFound: file=%s", cardCandidate.path().c_str()); - drm_fd = open(cardCandidate.path().c_str(), O_RDONLY); - auto res = real_drmModeGetResources(drm_fd); - if (res && res->count_connectors) { - drm_connector_id = res->connectors[0]; - break; - } - } - } - LOG("DRM: fd=%d, connector_id=%d", drm_fd, drm_connector_id); -} - -static int (*real_wl_proxy_add_listener)(struct wl_proxy *proxy, void (**implementation)(void), void *data); -static int hooked_wl_proxy_add_listener(struct wl_proxy *proxy, void (**implementation)(void), void *data) -{ - // wp_drm_lease_connector_v1 - if (compare_ptr(proxy, &fake_connector_id)) { - LOG("LISTENER wp_drm_lease_connector_v1"); - auto listener = reinterpret_cast(implementation); - listener->name(data, &fake_connector_id, "ALVR_name"); - listener->description(data, &fake_connector_id, "ALVR_description"); - listener->connector_id(data, &fake_connector_id, drm_connector_id); - listener->done(data, &fake_connector_id); - LOG("LISTENER done"); - return 0; - } - - // wp_drm_lease_v1 - if (compare_ptr(proxy, &fake_lease_id)) { - LOG("LISTENER wp_drm_lease_v1"); - auto listener = reinterpret_cast(implementation); - listener->lease_fd(data, &fake_lease_id, drm_fd); - LOG("LISTENER done"); - return 0; - } - - // wp_drm_lease_device_v1 - if (compare_ptr(proxy, &fake_device_id)) { - LOG("LISTENER wp_drm_lease_device_v1"); - auto listener = reinterpret_cast(implementation); - open_drm_fd(); - listener->drm_fd(data, &fake_device_id, drm_fd); - if (drm_connector_id != -1) { - listener->connector(data, &fake_device_id, &fake_connector_id); - } - listener->done(data, &fake_device_id); - LOG("LISTENER done"); - return 0; - } - - const char *name = *(*reinterpret_cast(proxy)); - - if (strcmp(name, "wl_registry") == 0) { - LOG("LISTENER wl_registry"); - auto listener = reinterpret_cast(implementation); - listener->global(data, reinterpret_cast(proxy), 0, "wp_drm_lease_device_v1", 1); - LOG("LISTENER done"); - return 0; - } - - return real_wl_proxy_add_listener(proxy, implementation, data); -} - -static struct wl_proxy *(*real_wl_proxy_marshal_flags)(struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, ...); -static struct wl_proxy *hooked_wl_proxy_marshal_flags(struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, ...) -{ - // wp_drm_lease_connector_v1 - if (compare_ptr(proxy, &fake_connector_id)) { - if (opcode == 0) { - LOG("CALL wp_drm_lease_connector_v1_destroy"); - } else { - ERR("Unknown wp_drm_lease_connector_v1 opcode=%u", opcode); - } - return nullptr; - } - - // wp_drm_lease_request_v1 - if (compare_ptr(proxy, &fake_lease_request_id)) { - if (opcode == 0) { - LOG("CALL wp_drm_lease_request_v1_request_connector"); - } else if (opcode == 1) { - LOG("CALL wp_drm_lease_request_v1_submit"); - return reinterpret_cast(&fake_lease_id); - } else { - ERR("Unknown wp_drm_lease_request_v1 opcode=%u", opcode); - } - return nullptr; - } - - // wp_drm_lease_device_v1 - if (compare_ptr(proxy, &fake_device_id)) { - if (opcode == 0) { - LOG("CALL wp_drm_lease_device_v1_create_lease_request"); - return reinterpret_cast(&fake_lease_request_id); - } else if (opcode == 1) { - LOG("CALL wp_drm_lease_device_v1_release"); - } else { - ERR("Unknown wp_drm_lease_device_v1 opcode=%u", opcode); - } - return nullptr; - } - - const char *name = **reinterpret_cast(proxy); - const char *iname = *reinterpret_cast(const_cast(interface)); - - if (strcmp(name, "wl_registry") == 0 && strcmp(iname, "wp_drm_lease_device_v1") == 0 && opcode == 0) { - LOG("CALL wl_registry_bind - wp_drm_lease_device_v1"); - return reinterpret_cast(&fake_device_id); - } - - __builtin_return(__builtin_apply(reinterpret_cast(real_wl_proxy_marshal_flags), __builtin_apply_args(), 1024)); -} - -extern "C" void *SDL_LoadFunction(void *handle, const char *name) -{ - static void *(*real_SDL_LoadFunction)(void *handle, const char *name) = nullptr; - LOAD_FN(SDL_LoadFunction); - -#define HOOK(f) \ - if (strcmp(name, #f) == 0) { \ - LOG("HOOK %s", #f); \ - real_##f = reinterpret_cast(real_SDL_LoadFunction(handle, #f)); \ - return reinterpret_cast(hooked_##f); \ - } \ - - HOOK(wl_proxy_add_listener); - HOOK(wl_proxy_marshal_flags); - -#undef HOOK - - return real_SDL_LoadFunction(handle, name); -} - -extern "C" drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connectorId) -{ - LOG("CALL drmModeGetConnector(%d, %u)", fd, connectorId); - - static drmModeConnectorPtr (*real_drmModeGetConnector)(int fd, uint32_t connectorId) = nullptr; - LOAD_FN(drmModeGetConnector); - - auto con = real_drmModeGetConnector(fd, connectorId); - if (con) { - auto sessionFile = std::ifstream(getenv("ALVR_SESSION_JSON")); - auto json = std::string(std::istreambuf_iterator(sessionFile), std::istreambuf_iterator()); - picojson::value v; - picojson::parse(v, json); - auto config = v.get("openvr_config"); - - con->count_modes = 1; - con->modes = (drmModeModeInfo*)calloc(1, sizeof(drmModeModeInfo)); - con->modes->hdisplay = config.get("eye_resolution_width").get() * 2; - con->modes->vdisplay = config.get("eye_resolution_height").get(); - } - return con; -} - -__attribute__((constructor)) static void lib_init() -{ - LOG("ALVR: drm-lease shim loaded"); - - unsetenv("LD_PRELOAD"); -} diff --git a/alvr/vrcompositor_wrapper/src/main.rs b/alvr/vrcompositor_wrapper/src/main.rs deleted file mode 100644 index 369f9a2300..0000000000 --- a/alvr/vrcompositor_wrapper/src/main.rs +++ /dev/null @@ -1,49 +0,0 @@ -#[cfg(target_os = "linux")] -fn main() { - let argv0 = std::env::args().next().unwrap(); - // location of the ALVR vulkan layer manifest - let layer_path = match std::fs::read_link(&argv0) { - Ok(path) => path - .parent() - .unwrap() - .join("../../share/vulkan/explicit_layer.d"), - Err(err) => panic!("Failed to read vrcompositor symlink: {err}"), - }; - unsafe { - std::env::set_var("VK_LAYER_PATH", layer_path); - // Vulkan < 1.3.234 - std::env::set_var("VK_INSTANCE_LAYERS", "VK_LAYER_ALVR_capture"); - std::env::set_var("DISABLE_VK_LAYER_VALVE_steam_fossilize_1", "1"); - std::env::set_var("DISABLE_MANGOHUD", "1"); - std::env::set_var("DISABLE_VKBASALT", "1"); - std::env::set_var("DISABLE_OBS_VKCAPTURE", "1"); - // Vulkan >= 1.3.234 - std::env::set_var( - "VK_LOADER_LAYERS_ENABLE", - "VK_LAYER_ALVR_capture,VK_LAYER_MESA_device_select", - ); - std::env::set_var("VK_LOADER_LAYERS_DISABLE", "*"); - } - if std::env::var("WAYLAND_DISPLAY").is_ok() { - let drm_lease_shim_path = match std::fs::read_link(&argv0) { - Ok(path) => path.parent().unwrap().join("alvr_drm_lease_shim.so"), - Err(err) => panic!("Failed to read vrcompositor symlink: {err}"), - }; - unsafe { - std::env::set_var("LD_PRELOAD", drm_lease_shim_path); - std::env::set_var( - "ALVR_SESSION_JSON", - alvr_filesystem::filesystem_layout_invalid() - .session() - .to_string_lossy() - .to_string(), - ); - } - } - - let err = exec::execvp(argv0 + ".real", std::env::args()); - println!("Failed to run vrcompositor {err}"); -} - -#[cfg(not(target_os = "linux"))] -fn main() {} diff --git a/alvr/vulkan_layer/.gitignore b/alvr/vulkan_layer/.gitignore deleted file mode 100644 index ce5b7ea647..0000000000 --- a/alvr/vulkan_layer/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -CMakeLists.txt.user -CMakeCache.txt -CMakeFiles -CMakeScripts -Testing -Makefile -cmake_install.cmake -install_manifest.txt -compile_commands.json -CTestTestfile.cmake -_deps -build diff --git a/alvr/vulkan_layer/Cargo.toml b/alvr/vulkan_layer/Cargo.toml deleted file mode 100644 index a3007f6819..0000000000 --- a/alvr/vulkan_layer/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "alvr_vulkan_layer" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -authors.workspace = true -license.workspace = true - -[lib] -crate-type = ["cdylib"] - -[dependencies] -alvr_common.workspace = true -alvr_filesystem.workspace = true - -[build-dependencies] -bindgen = "0.71" -cc = { version = "1", features = ["parallel"] } -pkg-config = "0.3" -walkdir = "2" diff --git a/alvr/vulkan_layer/LICENSE b/alvr/vulkan_layer/LICENSE deleted file mode 100644 index 3866bd8cb6..0000000000 --- a/alvr/vulkan_layer/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2019 Arm Limited. -Copyright (c) 2021-2024 alvr-org - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/alvr/vulkan_layer/README.md b/alvr/vulkan_layer/README.md deleted file mode 100644 index 0ecbd4022c..0000000000 --- a/alvr/vulkan_layer/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# ALVR capture vulkan layer - -## Introduction - -The ALVR capture vulkan layer is intended to overcome a limitation of SteamVR runtime on Linux: it does'nt allow software based output devices. -The layer is based on [vulkan wsi layer](https://gitlab.freedesktop.org/mesa/vulkan-wsi-layer), which is meant to implement window system integration as layers. - -The ALVR layer adds a display to the vkGetPhysicalDeviceDisplayPropertiesKHR call, and implements all functions related to that device. It then allows images of the swapchain to be shared to an other process (the alvr server process), and communicates present calls. - -There are unfortunately a few hacks that make it heavily dependent to SteamVR: requested extentions manipulation to enable the required ones, searching through the stack to find the headset position, and not fully implementing the advertised features. diff --git a/alvr/vulkan_layer/build.rs b/alvr/vulkan_layer/build.rs deleted file mode 100644 index 905aa01980..0000000000 --- a/alvr/vulkan_layer/build.rs +++ /dev/null @@ -1,61 +0,0 @@ -#[cfg(target_os = "linux")] -fn main() { - use std::{env, path::PathBuf}; - - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let cpp_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - let server_cpp_dir = - PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("../server_openvr/cpp"); - - let vulkan = pkg_config::Config::new().probe("vulkan").unwrap(); - let libunwind = pkg_config::Config::new().probe("libunwind").unwrap(); - - let cpp_paths = walkdir::WalkDir::new(".") - .into_iter() - .filter_map(|maybe_entry| maybe_entry.ok()) - .map(|entry| entry.into_path()) - .collect::>(); - - let source_files_paths = cpp_paths.iter().filter(|path| { - path.extension() - .filter(|ext| ext.to_string_lossy() == "cpp") - .is_some() - }); - - let mut build = cc::Build::new(); - build - .cpp(true) - .files(source_files_paths) - .flag("-std=c++17") - .flag_if_supported("-Wno-unused-parameter") - .define("VK_USE_PLATFORM_XLIB_XRANDR_EXT", None) - .include(cpp_dir) - .include(server_cpp_dir) - .includes(vulkan.include_paths) - .includes(libunwind.include_paths); - - build.compile("VkLayer_ALVR"); - - bindgen::builder() - .clang_arg("-xc++") - .header("layer/layer.h") - .derive_default(true) - .generate() - .expect("layer bindings") - .write_to_file(out_dir.join("layer_bindings.rs")) - .expect("layer_bindings.rs"); - - for lib in libunwind.libs { - println!("cargo:rustc-link-lib={lib}"); - } - - // fail build if there are undefined symbols in final library - println!("cargo:rustc-cdylib-link-arg=-Wl,--no-undefined"); - - for path in cpp_paths { - println!("cargo:rerun-if-changed={}", path.to_string_lossy()); - } -} - -#[cfg(not(target_os = "linux"))] -fn main() {} diff --git a/alvr/vulkan_layer/layer/alvr_x86_64.json b/alvr/vulkan_layer/layer/alvr_x86_64.json deleted file mode 100644 index c9d502fcd6..0000000000 --- a/alvr/vulkan_layer/layer/alvr_x86_64.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "file_format_version" : "1.1.2", - "layer" : { - "name": "VK_LAYER_ALVR_capture", - "type": "GLOBAL", - "library_path": "../../../lib64/libalvr_vulkan_layer.so", - "api_version": "1.0.68", - "implementation_version": "1", - "description": "ALVR display intercept layer", - "instance_extensions": [ - { - "name" : "VK_EXT_headless_surface", - "spec_version" : "1" - }, - { - "name" : "VK_KHR_surface", - "spec_version" : "1" - }, - { - "name" : "VK_EXT_acquire_xlib_display", - "spec_version" : "1" - }, - { - "name" : "VK_KHR_display", - "spec_version" : "1" - } - ], - "device_extensions": [ - { - "name" : "VK_KHR_swapchain", - "spec_version" : "1" - } - ], - "functions": { - "vkNegotiateLoaderLayerInterfaceVersion" : "ALVR_Negotiate" - }, - "disable_environment": { - "DISABLE_ALVR_DISPLAY": "1" - } - } -} diff --git a/alvr/vulkan_layer/layer/device_api.cpp b/alvr/vulkan_layer/layer/device_api.cpp deleted file mode 100644 index ba2d2c7d9f..0000000000 --- a/alvr/vulkan_layer/layer/device_api.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "device_api.hpp" -#include "private_data.hpp" -#include "wsi/display.hpp" -#include "settings.h" - -#include - -static const char *alvr_display_name = "ALVR display"; - -const struct { -} alvr_display; -const VkDisplayKHR alvr_display_handle = (VkDisplayKHR_T *)&alvr_display; - -const struct { -} alvr_display_mode; -const VkDisplayModeKHR alvr_display_mode_handle = (VkDisplayModeKHR_T *)&alvr_display_mode; - -extern "C" { - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetPhysicalDeviceDisplayPropertiesKHR( - VkPhysicalDevice device, uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties) { - if (!pProperties) { - *pPropertyCount = 1; - return VK_SUCCESS; - } - if (*pPropertyCount < 1) { - return VK_INCOMPLETE; - } - pProperties[0].display = alvr_display_handle; - pProperties[0].displayName = alvr_display_name; - pProperties[0].physicalDimensions = VkExtent2D{20, 20}; - pProperties[0].physicalResolution = VkExtent2D{Settings::Instance().m_renderWidth, Settings::Instance().m_renderHeight}; - pProperties[0].supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - pProperties[0].planeReorderPossible = VK_FALSE; - pProperties[0].persistentContent = VK_TRUE; - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDisplayModePropertiesKHR( - VkPhysicalDevice device, VkDisplayKHR display, uint32_t *pPropertyCount, - VkDisplayModePropertiesKHR *pProperties) { - if (display != alvr_display_handle) { - *pPropertyCount = 0; - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - if (!pProperties) { - *pPropertyCount = 1; - return VK_SUCCESS; - } - if (*pPropertyCount < 1) { - return VK_INCOMPLETE; - } - pProperties[0].displayMode = alvr_display_mode_handle; - pProperties[0].parameters.visibleRegion = VkExtent2D{Settings::Instance().m_renderWidth, Settings::Instance().m_renderHeight}; - pProperties[0].parameters.refreshRate = Settings::Instance().m_refreshRate * 1000; - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetPhysicalDeviceDisplayPlanePropertiesKHR( - VkPhysicalDevice device, uint32_t *pPropertyCount, VkDisplayPlanePropertiesKHR *pProperties) { - if (!pProperties) { - *pPropertyCount = 1; - return VK_SUCCESS; - } - if (*pPropertyCount < 1) { - return VK_INCOMPLETE; - } - pProperties[0].currentDisplay = alvr_display_handle; - pProperties[0].currentStackIndex = 0; - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkAcquireXlibDisplayEXT(VkPhysicalDevice device, - Display *dpy, - VkDisplayKHR display) { - if (display != alvr_display_handle) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDrmDisplayEXT(VkPhysicalDevice physicalDevice, - int32_t drmFd, - uint32_t connectorId, - VkDisplayKHR *display) { - *display = alvr_display_handle; - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkAcquireDrmDisplayEXT(VkPhysicalDevice physicalDevice, - int32_t drmFd, - VkDisplayKHR display) { - if (display != alvr_display_handle) { - return VK_ERROR_INITIALIZATION_FAILED; - } - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDisplayPlaneSupportedDisplaysKHR( - VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t *pDisplayCount, - VkDisplayKHR *pDisplays) { - if (planeIndex != 0) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - if (!pDisplays) { - *pDisplayCount = 1; - return VK_SUCCESS; - } - pDisplays[0] = alvr_display_handle; - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkCreateDisplayPlaneSurfaceKHR( - VkInstance vkinstance, const VkDisplaySurfaceCreateInfoKHR * /*pCreateInfo*/, - const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - auto &instance = layer::instance_private_data::get(vkinstance); - VkHeadlessSurfaceCreateInfoEXT createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT; - auto res = - instance.disp.CreateHeadlessSurfaceEXT(vkinstance, &createInfo, pAllocator, pSurface); - if (*pSurface == NULL) - std::abort(); - instance.add_surface(*pSurface); - return res; -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkReleaseDisplayEXT(VkPhysicalDevice physicalDevice, - VkDisplayKHR display) { - return VK_SUCCESS; -} - -VKAPI_ATTR void VKAPI_CALL wsi_layer_vkDestroySurfaceKHR(VkInstance vkinstance, - VkSurfaceKHR surface, - const VkAllocationCallbacks *pAllocator) { - auto &instance = layer::instance_private_data::get(vkinstance); - if (instance.should_layer_handle_surface(surface)) { - return; - } - return instance.disp.DestroySurfaceKHR(vkinstance, surface, pAllocator); -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkRegisterDisplayEventEXT( - VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT *pDisplayEventInfo, - const VkAllocationCallbacks *pAllocator, VkFence *pFence) { - if (display != alvr_display_handle) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - auto &instance = layer::device_private_data::get(device); - *pFence = instance.display->get_vsync_fence(); - - return VK_SUCCESS; -} - -VKAPI_ATTR void VKAPI_CALL wsi_layer_vkDestroyFence(VkDevice device, VkFence fence, - const VkAllocationCallbacks *pAllocator) { - auto &instance = layer::device_private_data::get(device); - auto alvr_fence = instance.display->peek_vsync_fence(); - if (fence == alvr_fence) { - return; - } - instance.disp.DestroyFence(device, fence, pAllocator); -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkWaitForFences(VkDevice device, uint32_t fenceCount, - const VkFence *pFences, VkBool32 waitAll, - uint64_t timeout) { - auto &instance = layer::device_private_data::get(device); - auto alvr_fence = instance.display->peek_vsync_fence(); - for (uint32_t i = 0; i < fenceCount; ++i) { - if (pFences[i] == alvr_fence) { - assert(fenceCount == 1); // only our fence - return instance.display->wait_for_vsync(timeout) ? VK_SUCCESS : VK_TIMEOUT; - } - } - return instance.disp.WaitForFences(device, fenceCount, pFences, waitAll, timeout); -} - -VKAPI_ATTR VkResult wsi_layer_vkGetFenceStatus(VkDevice device, VkFence fence) -{ - auto &instance = layer::device_private_data::get(device); - auto alvr_fence = instance.display->peek_vsync_fence(); - if (fence == alvr_fence) { - return instance.display->is_signaled() ? VK_SUCCESS : VK_NOT_READY; - } - return instance.disp.GetFenceStatus(device, fence); -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkCreateDisplayModeKHR( - VkPhysicalDevice physicalDevice, - VkDisplayKHR display, - const VkDisplayModeCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDisplayModeKHR* pMode) -{ - return VK_ERROR_INITIALIZATION_FAILED; -} - -} diff --git a/alvr/vulkan_layer/layer/device_api.hpp b/alvr/vulkan_layer/layer/device_api.hpp deleted file mode 100644 index 34641aeefa..0000000000 --- a/alvr/vulkan_layer/layer/device_api.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include - -extern "C" { - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetPhysicalDeviceDisplayPropertiesKHR( - VkPhysicalDevice device, uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDisplayModePropertiesKHR( - VkPhysicalDevice device, VkDisplayKHR display, uint32_t *pPropertyCount, - VkDisplayModePropertiesKHR *pProperties); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetPhysicalDeviceDisplayPlanePropertiesKHR( - VkPhysicalDevice device, uint32_t *pPropertyCount, VkDisplayPlanePropertiesKHR *pProperties); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkAcquireXlibDisplayEXT(VkPhysicalDevice device, - Display *dpy, - VkDisplayKHR display); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDrmDisplayEXT(VkPhysicalDevice physicalDevice, - int32_t drmFd, - uint32_t connectorId, - VkDisplayKHR *display); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkAcquireDrmDisplayEXT(VkPhysicalDevice physicalDevice, - int32_t drmFd, - VkDisplayKHR display); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDisplayPlaneSupportedDisplaysKHR( - VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t *pDisplayCount, - VkDisplayKHR *pDisplays); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkCreateDisplayPlaneSurfaceKHR( - VkInstance instance, const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, - const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkReleaseDisplayEXT(VkPhysicalDevice physicalDevice, - VkDisplayKHR display); - -VKAPI_ATTR void VKAPI_CALL wsi_layer_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, - const VkAllocationCallbacks *pAllocator); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkRegisterDisplayEventEXT( - VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT *pDisplayEventInfo, - const VkAllocationCallbacks *pAllocator, VkFence *pFence); - -VKAPI_ATTR void VKAPI_CALL wsi_layer_vkDestroyFence(VkDevice device, VkFence fence, - const VkAllocationCallbacks *pAllocator); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkWaitForFences(VkDevice device, uint32_t fenceCount, - const VkFence *pFences, VkBool32 waitAll, - uint64_t timeout); - -VKAPI_ATTR VkResult wsi_layer_vkGetFenceStatus(VkDevice device, VkFence fence); - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkCreateDisplayModeKHR( - VkPhysicalDevice physicalDevice, - VkDisplayKHR display, - const VkDisplayModeCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDisplayModeKHR* pMode); -} diff --git a/alvr/vulkan_layer/layer/layer.cpp b/alvr/vulkan_layer/layer/layer.cpp deleted file mode 100644 index 8bb35a7597..0000000000 --- a/alvr/vulkan_layer/layer/layer.cpp +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright (c) 2016-2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#include - -#include "settings.h" -#include "device_api.hpp" -#include "private_data.hpp" -#include "surface_api.hpp" -#include "swapchain_api.hpp" -#include "util/custom_allocator.hpp" -#include "util/extension_list.hpp" -#include "wsi/wsi_factory.hpp" -#include "layer.h" - -#define VK_LAYER_API_VERSION VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION) - -namespace layer { - -static const VkLayerProperties global_layer = { - "VK_LAYER_ALVR_capture", - VK_LAYER_API_VERSION, - 1, - "ALVR capture layer", -}; -static const VkExtensionProperties device_extension[] = { - {VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_SPEC_VERSION}}; -static const VkExtensionProperties instance_extension[] = { - {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION}}; - -VKAPI_ATTR VkResult extension_properties(const uint32_t count, - const VkExtensionProperties *layer_ext, uint32_t *pCount, - VkExtensionProperties *pProp) { - uint32_t size; - - if (pProp == NULL || layer_ext == NULL) { - *pCount = count; - return VK_SUCCESS; - } - - size = *pCount < count ? *pCount : count; - memcpy(pProp, layer_ext, size * sizeof(VkExtensionProperties)); - *pCount = size; - if (size < count) { - return VK_INCOMPLETE; - } - - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult layer_properties(const uint32_t count, const VkLayerProperties *layer_prop, - uint32_t *pCount, VkLayerProperties *pProp) { - uint32_t size; - - if (pProp == NULL || layer_prop == NULL) { - *pCount = count; - return VK_SUCCESS; - } - - size = *pCount < count ? *pCount : count; - memcpy(pProp, layer_prop, size * sizeof(VkLayerProperties)); - *pCount = size; - if (size < count) { - return VK_INCOMPLETE; - } - - return VK_SUCCESS; -} - -VKAPI_ATTR VkLayerInstanceCreateInfo *get_chain_info(const VkInstanceCreateInfo *pCreateInfo, - VkLayerFunction func) { - VkLayerInstanceCreateInfo *chain_info = (VkLayerInstanceCreateInfo *)pCreateInfo->pNext; - while (chain_info && !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && - chain_info->function == func)) { - chain_info = (VkLayerInstanceCreateInfo *)chain_info->pNext; - } - - return chain_info; -} - -VKAPI_ATTR VkLayerDeviceCreateInfo *get_chain_info(const VkDeviceCreateInfo *pCreateInfo, - VkLayerFunction func) { - VkLayerDeviceCreateInfo *chain_info = (VkLayerDeviceCreateInfo *)pCreateInfo->pNext; - while (chain_info && !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO && - chain_info->function == func)) { - chain_info = (VkLayerDeviceCreateInfo *)chain_info->pNext; - } - - return chain_info; -} - -/* This is where the layer is initialised and the instance dispatch table is constructed. */ -VKAPI_ATTR VkResult create_instance(const VkInstanceCreateInfo *pCreateInfo, - const VkAllocationCallbacks *pAllocator, - VkInstance *pInstance) { - // Make sure settings are loaded before we access them - Settings::Instance().Load(); - - VkLayerInstanceCreateInfo *layerCreateInfo = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); - PFN_vkSetInstanceLoaderData loader_callback = - get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK)->u.pfnSetInstanceLoaderData; - - if (nullptr == layerCreateInfo || nullptr == layerCreateInfo->u.pLayerInfo) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = - layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; - - PFN_vkCreateInstance fpCreateInstance = - (PFN_vkCreateInstance)fpGetInstanceProcAddr(nullptr, "vkCreateInstance"); - if (nullptr == fpCreateInstance) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - /* Advance the link info for the next element on the chain. */ - layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext; - - /* The layer needs some Vulkan 1.2 functionality in order to operate correctly. - * We thus change the application info to require this API version, if necessary. - * This may have consequences for ICDs whose behaviour depends on apiVersion. - */ - const uint32_t minimum_required_vulkan_version = VK_API_VERSION_1_2; - VkApplicationInfo modified_app_info{}; - if (nullptr != pCreateInfo->pApplicationInfo) { - modified_app_info = *pCreateInfo->pApplicationInfo; - if (modified_app_info.apiVersion < minimum_required_vulkan_version) { - modified_app_info.apiVersion = minimum_required_vulkan_version; - } - } else { - modified_app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - modified_app_info.apiVersion = minimum_required_vulkan_version; - } - - // Hijack one extension name - // the headless extension can't be added as a new parameter, because the loader performs a copy before - // calling the createInstance functions. The loader must know we activated this function because it - // will enable bits in the wsi part, so we switch to vulkan 1.1, and replace one of the extentions - // that has been promoted, with a const_cast. - for (uint32_t i = 0 ; i < pCreateInfo->enabledExtensionCount ; ++i) { - if (strcmp("VK_KHR_external_memory_capabilities", pCreateInfo->ppEnabledExtensionNames[i]) == 0) - { - const char** ext = const_cast(pCreateInfo->ppEnabledExtensionNames + i); - *ext = VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME; - } - } - - auto createInfo = *pCreateInfo; - createInfo.pApplicationInfo = &modified_app_info; - - /* Now call create instance on the chain further down the list. - * Note that we do not remove the extensions that the layer supports from - * modified_info.ppEnabledExtensionNames. Layers have to abide the rule that vkCreateInstance - * must not generate an error for unrecognized extension names. Also, the loader filters the - * extension list to ensure that ICDs do not see extensions that they do not support. - */ - VkResult result; - result = fpCreateInstance(&createInfo, pAllocator, pInstance); - if (result != VK_SUCCESS) { - return result; - } - - instance_dispatch_table table; - result = table.populate(*pInstance, fpGetInstanceProcAddr); - if (result != VK_SUCCESS) { - return result; - } - - /* Find all the platforms that the layer can handle based on - * pCreateInfo->ppEnabledExtensionNames. */ - auto layer_platforms_to_enable = wsi::find_enabled_layer_platforms(pCreateInfo); - - std::unique_ptr inst_data{ - new instance_private_data{table, loader_callback, layer_platforms_to_enable}}; - instance_private_data::set(*pInstance, std::move(inst_data)); - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult create_device(VkPhysicalDevice physicalDevice, - const VkDeviceCreateInfo *pCreateInfo, - const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) { - VkLayerDeviceCreateInfo *layerCreateInfo = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); - PFN_vkSetDeviceLoaderData loader_callback = - get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK)->u.pfnSetDeviceLoaderData; - - if (nullptr == layerCreateInfo || nullptr == layerCreateInfo->u.pLayerInfo) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - /* Retrieve the vkGetDeviceProcAddr and the vkCreateDevice function pointers for the next layer - * in the chain. */ - PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = - layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; - PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = - layerCreateInfo->u.pLayerInfo->pfnNextGetDeviceProcAddr; - PFN_vkCreateDevice fpCreateDevice = - (PFN_vkCreateDevice)fpGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDevice"); - if (nullptr == fpCreateDevice) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - /* Advance the link info for the next element on the chain. */ - layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext; - - /* Copy the extension to a util::extension_list. */ - util::allocator allocator{pAllocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND}; - util::extension_list enabled_extensions{allocator}; - VkResult result; - result = enabled_extensions.add(pCreateInfo->ppEnabledExtensionNames, - pCreateInfo->enabledExtensionCount); - if (result != VK_SUCCESS) { - return result; - } - - /* Add the extensions required by the platforms that are being enabled in the layer. */ - auto &inst_data = instance_private_data::get(physicalDevice); - const util::wsi_platform_set &enabled_platforms = inst_data.get_enabled_platforms(); - result = wsi::add_extensions_required_by_layer(physicalDevice, enabled_platforms, - enabled_extensions); - if (result != VK_SUCCESS) { - return result; - } - - util::vector modified_enabled_extensions{allocator}; - if (!enabled_extensions.get_extension_strings(modified_enabled_extensions)) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - /* Now call create device on the chain further down the list. */ - VkDeviceCreateInfo modified_info = *pCreateInfo; - modified_info.ppEnabledExtensionNames = modified_enabled_extensions.data(); - modified_info.enabledExtensionCount = modified_enabled_extensions.size(); - - // Enable timeline semaphores - VkPhysicalDeviceFeatures2 features = {}; - features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - - VkPhysicalDeviceVulkan12Features features12 = {}; - features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; - - VkPhysicalDeviceFeatures2 *features_ptr = nullptr; - VkPhysicalDeviceVulkan12Features *features12_ptr = nullptr; - - VkDeviceCreateInfo *next = &modified_info; - while (next->pNext) { - if (next->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) { - features_ptr = (VkPhysicalDeviceFeatures2*)next; - } else if (next->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES) { - features12_ptr = (VkPhysicalDeviceVulkan12Features*)next; - } - next = (VkDeviceCreateInfo*)next->pNext; - } - if (!features_ptr) { - features_ptr = &features; - next->pNext = features_ptr; - next = (VkDeviceCreateInfo*)features_ptr; - } - if (!features12_ptr) { - features12_ptr = &features12; - next->pNext = features12_ptr; - next = (VkDeviceCreateInfo*)features12_ptr; - } - features12_ptr->timelineSemaphore = true; - - if (modified_info.pEnabledFeatures) { - features_ptr->features = *modified_info.pEnabledFeatures; - modified_info.pEnabledFeatures = nullptr; - } - - result = fpCreateDevice(physicalDevice, &modified_info, pAllocator, pDevice); - if (result != VK_SUCCESS) { - return result; - } - - device_dispatch_table table; - result = table.populate(*pDevice, fpGetDeviceProcAddr); - if (result != VK_SUCCESS) { - return result; - } - - std::unique_ptr device{ - new device_private_data{inst_data, physicalDevice, *pDevice, table, loader_callback}}; - device->display = std::make_unique(); - device_private_data::set(*pDevice, std::move(device)); - return VK_SUCCESS; -} - -/* Clean up the dispatch table for this instance. */ -VKAPI_ATTR void VKAPI_CALL -wsi_layer_vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator) { - assert(instance); - layer::instance_private_data::get(instance).disp.DestroyInstance(instance, pAllocator); - layer::instance_private_data::destroy(instance); -} - -VKAPI_ATTR void VKAPI_CALL -wsi_layer_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) { - layer::device_private_data::destroy(device); -} - -VKAPI_ATTR VkResult VKAPI_CALL -wsi_layer_vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, - const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) { - return layer::create_instance(pCreateInfo, pAllocator, pInstance); -} - -VKAPI_ATTR VkResult VKAPI_CALL -wsi_layer_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, - const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) { - return layer::create_device(physicalDevice, pCreateInfo, pAllocator, pDevice); -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkEnumerateDeviceExtensionProperties( - VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pCount, - VkExtensionProperties *pProperties) { - if (pLayerName && !strcmp(pLayerName, layer::global_layer.layerName)) - return layer::extension_properties(1, layer::device_extension, pCount, pProperties); - - assert(physicalDevice); - return layer::instance_private_data::get(physicalDevice) - .disp.EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pCount, pProperties); -} - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkEnumerateInstanceExtensionProperties( - const char *pLayerName, uint32_t *pCount, VkExtensionProperties *pProperties) { - if (pLayerName && !strcmp(pLayerName, layer::global_layer.layerName)) - return layer::extension_properties(1, layer::instance_extension, pCount, pProperties); - - return VK_ERROR_LAYER_NOT_PRESENT; -} - -VKAPI_ATTR VkResult VKAPI_CALL -wsi_layer_vkEnumerateInstanceLayerProperties(uint32_t *pCount, VkLayerProperties *pProperties) { - return layer::layer_properties(1, &layer::global_layer, pCount, pProperties); -} - -#define GET_PROC_ADDR(func) \ - if (!strcmp(funcName, #func)) \ - return (PFN_vkVoidFunction)&wsi_layer_##func; - - -PFN_vkVoidFunction VKAPI_CALL wsi_layer_vkGetDeviceProcAddr(VkDevice device, - const char *funcName) { - GET_PROC_ADDR(vkCreateSwapchainKHR); - GET_PROC_ADDR(vkDestroySwapchainKHR); - GET_PROC_ADDR(vkGetSwapchainImagesKHR); - GET_PROC_ADDR(vkAcquireNextImageKHR); - GET_PROC_ADDR(vkQueuePresentKHR); - GET_PROC_ADDR(vkGetSwapchainCounterEXT); - GET_PROC_ADDR(vkRegisterDisplayEventEXT); - GET_PROC_ADDR(vkDestroyFence); - GET_PROC_ADDR(vkWaitForFences); - GET_PROC_ADDR(vkGetFenceStatus); - - return layer::device_private_data::get(device).disp.GetDeviceProcAddr(device, funcName); -} - -VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL -wsi_layer_vkGetInstanceProcAddr(VkInstance instance, const char *funcName) { - GET_PROC_ADDR(vkGetDeviceProcAddr); - GET_PROC_ADDR(vkGetInstanceProcAddr); - GET_PROC_ADDR(vkCreateInstance); - GET_PROC_ADDR(vkDestroyInstance); - GET_PROC_ADDR(vkCreateDevice); - GET_PROC_ADDR(vkDestroyDevice); - GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceSupportKHR); - GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); - GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceFormatsKHR); - GET_PROC_ADDR(vkGetPhysicalDeviceSurfacePresentModesKHR); - GET_PROC_ADDR(vkEnumerateDeviceExtensionProperties); - GET_PROC_ADDR(vkEnumerateInstanceExtensionProperties); - GET_PROC_ADDR(vkEnumerateInstanceLayerProperties); - - GET_PROC_ADDR(vkGetPhysicalDeviceDisplayPropertiesKHR); - GET_PROC_ADDR(vkGetDisplayModePropertiesKHR); - GET_PROC_ADDR(vkGetPhysicalDeviceDisplayPlanePropertiesKHR); - GET_PROC_ADDR(vkAcquireXlibDisplayEXT); - GET_PROC_ADDR(vkGetDrmDisplayEXT); - GET_PROC_ADDR(vkAcquireDrmDisplayEXT); - GET_PROC_ADDR(vkGetDisplayPlaneSupportedDisplaysKHR); - GET_PROC_ADDR(vkCreateDisplayPlaneSurfaceKHR); - GET_PROC_ADDR(vkCreateDisplayModeKHR); - GET_PROC_ADDR(vkReleaseDisplayEXT); - - return layer::instance_private_data::get(instance).disp.GetInstanceProcAddr(instance, funcName); -} - -} /* namespace layer */ - -const char *g_sessionPath; - -VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_Negotiate(VkNegotiateLayerInterface *nli) -{ - if (nli->loaderLayerInterfaceVersion < 2) - return VK_ERROR_INITIALIZATION_FAILED; - - nli->loaderLayerInterfaceVersion = 2; - nli->pfnGetInstanceProcAddr = layer::wsi_layer_vkGetInstanceProcAddr; - nli->pfnGetDeviceProcAddr = layer::wsi_layer_vkGetDeviceProcAddr; - - return VK_SUCCESS; -} diff --git a/alvr/vulkan_layer/layer/layer.h b/alvr/vulkan_layer/layer/layer.h deleted file mode 100644 index bacb30a0d3..0000000000 --- a/alvr/vulkan_layer/layer/layer.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -extern "C" const char *g_sessionPath; - -extern "C" VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_Negotiate(VkNegotiateLayerInterface *nli); diff --git a/alvr/vulkan_layer/layer/private_data.cpp b/alvr/vulkan_layer/layer/private_data.cpp deleted file mode 100644 index c856bac5a7..0000000000 --- a/alvr/vulkan_layer/layer/private_data.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2018-2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "private_data.hpp" - -#include "wsi/wsi_factory.hpp" - -#include - -namespace layer { - -static std::mutex g_data_lock; -static std::unordered_map> g_instance_data; -static std::unordered_map> g_device_data; - -template -static PFN_vkVoidFunction get_proc_helper(object_type obj, get_proc_type get_proc, - const char *proc_name, bool required, bool &ok) { - PFN_vkVoidFunction ret = get_proc(obj, proc_name); - if (nullptr == ret && required) { - ok = false; - } - return ret; -} - -VkResult instance_dispatch_table::populate(VkInstance instance, - PFN_vkGetInstanceProcAddr get_proc) { - bool ok = true; -#define REQUIRED(x) \ - x = reinterpret_cast(get_proc_helper(instance, get_proc, "vk" #x, true, ok)); -#define OPTIONAL(x) \ - x = reinterpret_cast(get_proc_helper(instance, get_proc, "vk" #x, false, ok)); - INSTANCE_ENTRYPOINTS_LIST(REQUIRED, OPTIONAL); -#undef REQUIRED -#undef OPTIONAL - return ok ? VK_SUCCESS : VK_ERROR_INITIALIZATION_FAILED; -} - -VkResult device_dispatch_table::populate(VkDevice device, PFN_vkGetDeviceProcAddr get_proc) { - bool ok = true; -#define REQUIRED(x) \ - x = reinterpret_cast(get_proc_helper(device, get_proc, "vk" #x, true, ok)); -#define OPTIONAL(x) \ - x = reinterpret_cast(get_proc_helper(device, get_proc, "vk" #x, false, ok)); - DEVICE_ENTRYPOINTS_LIST(REQUIRED, OPTIONAL); -#undef REQUIRED -#undef OPTIONAL - return ok ? VK_SUCCESS : VK_ERROR_INITIALIZATION_FAILED; -} - -instance_private_data::instance_private_data(const instance_dispatch_table &table, - PFN_vkSetInstanceLoaderData set_loader_data, - util::wsi_platform_set enabled_layer_platforms) - : disp(table), SetInstanceLoaderData(set_loader_data), - enabled_layer_platforms(enabled_layer_platforms) {} - -template -static inline void *get_key(dispatchable_type dispatchable_object) { - return *reinterpret_cast(dispatchable_object); -} - -void instance_private_data::set(VkInstance inst, std::unique_ptr inst_data) { - scoped_mutex lock(g_data_lock); - g_instance_data[get_key(inst)] = std::move(inst_data); -} - -template -static instance_private_data &get_instance_private_data(dispatchable_type dispatchable_object) { - scoped_mutex lock(g_data_lock); - return *g_instance_data[get_key(dispatchable_object)]; -} - -instance_private_data &instance_private_data::get(VkInstance instance) { - return get_instance_private_data(instance); -} - -instance_private_data &instance_private_data::get(VkPhysicalDevice phys_dev) { - return get_instance_private_data(phys_dev); -} - -static VkIcdWsiPlatform get_platform_of_surface(VkSurfaceKHR surface) { - VkIcdSurfaceBase *surface_base = reinterpret_cast(surface); - return surface_base->platform; -} - -bool instance_private_data::does_layer_support_surface(VkSurfaceKHR surface) { - return enabled_layer_platforms.contains(get_platform_of_surface(surface)); -} - -bool instance_private_data::do_icds_support_surface(VkPhysicalDevice, VkSurfaceKHR) { - /* For now assume ICDs do not support VK_KHR_surface. This means that the layer will handle all - * the surfaces it can handle (even if the ICDs can handle the surface) and only call down for - * surfaces it cannot handle. In the future we may allow system integrators to configure which - * ICDs have precedence handling which platforms. - */ - return false; -} - -bool instance_private_data::should_layer_handle_surface(VkSurfaceKHR surface) { - return surfaces.find(surface) != surfaces.end(); -} - -void instance_private_data::destroy(VkInstance inst) { - scoped_mutex lock(g_data_lock); - g_instance_data.erase(get_key(inst)); -} - -void instance_private_data::add_surface(VkSurfaceKHR surface) { - scoped_mutex lock(g_data_lock); - surfaces.insert(surface); -} - -device_private_data::device_private_data(instance_private_data &inst_data, - VkPhysicalDevice phys_dev, VkDevice dev, - const device_dispatch_table &table, - PFN_vkSetDeviceLoaderData set_loader_data) - : disp{table}, instance_data{inst_data}, SetDeviceLoaderData{set_loader_data}, - physical_device{phys_dev}, device{dev} {} - -void device_private_data::set(VkDevice dev, std::unique_ptr dev_data) { - scoped_mutex lock(g_data_lock); - g_device_data[get_key(dev)] = std::move(dev_data); -} - -template -static device_private_data &get_device_private_data(dispatchable_type dispatchable_object) { - scoped_mutex lock(g_data_lock); - return *g_device_data[get_key(dispatchable_object)]; -} - -device_private_data &device_private_data::get(VkDevice device) { - return get_device_private_data(device); -} - -device_private_data &device_private_data::get(VkQueue queue) { - return get_device_private_data(queue); -} - -void device_private_data::add_layer_swapchain(VkSwapchainKHR swapchain) { - scoped_mutex lock(swapchains_lock); - swapchains.insert(swapchain); -} - -bool device_private_data::layer_owns_all_swapchains(const VkSwapchainKHR *swapchain, - uint32_t swapchain_count) const { - scoped_mutex lock(swapchains_lock); - for (uint32_t i = 0; i < swapchain_count; i++) { - if (swapchains.find(swapchain[i]) == swapchains.end()) { - return false; - } - } - return true; -} - -bool device_private_data::should_layer_create_swapchain(VkSurfaceKHR vk_surface) { - return instance_data.should_layer_handle_surface(vk_surface); -} - -bool device_private_data::can_icds_create_swapchain(VkSurfaceKHR vk_surface) { - return disp.CreateSwapchainKHR != nullptr; -} - -void device_private_data::destroy(VkDevice dev) { - scoped_mutex lock(g_data_lock); - g_device_data.erase(get_key(dev)); -} -} /* namespace layer */ diff --git a/alvr/vulkan_layer/layer/private_data.hpp b/alvr/vulkan_layer/layer/private_data.hpp deleted file mode 100644 index 3345548ee0..0000000000 --- a/alvr/vulkan_layer/layer/private_data.hpp +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2018-2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "util/platform_set.hpp" -#include "wsi/display.hpp" - -#include -#include -#include - -#include -#include -#include -#include - -using scoped_mutex = std::lock_guard; - -namespace layer { - -/* List of device entrypoints in the layer's instance dispatch table. - * Note that the Vulkan loader implements some of these entrypoints so the fact that these are - * non-null doesn't guarantee than we can safely call them. We still mark the entrypoints with - * REQUIRED() and OPTIONAL(). The layer fails if vkGetInstanceProcAddr returns null for entrypoints - * that are REQUIRED(). - */ -#define INSTANCE_ENTRYPOINTS_LIST(REQUIRED, OPTIONAL) \ - REQUIRED(GetInstanceProcAddr) \ - REQUIRED(DestroyInstance) \ - REQUIRED(GetPhysicalDeviceProperties) \ - REQUIRED(GetPhysicalDeviceProperties2) \ - REQUIRED(GetPhysicalDeviceMemoryProperties) \ - REQUIRED(GetPhysicalDeviceImageFormatProperties) \ - REQUIRED(EnumerateDeviceExtensionProperties) \ - OPTIONAL(GetPhysicalDeviceSurfaceCapabilitiesKHR) \ - OPTIONAL(GetPhysicalDeviceSurfaceFormatsKHR) \ - OPTIONAL(GetPhysicalDeviceSurfacePresentModesKHR) \ - OPTIONAL(GetPhysicalDeviceSurfaceSupportKHR) \ - OPTIONAL(GetPhysicalDeviceDisplayPropertiesKHR) \ - OPTIONAL(GetDisplayModePropertiesKHR) \ - OPTIONAL(GetPhysicalDeviceDisplayPlanePropertiesKHR) \ - OPTIONAL(AcquireXlibDisplayEXT) \ - OPTIONAL(GetDisplayPlaneSupportedDisplaysKHR) \ - OPTIONAL(CreateDisplayPlaneSurfaceKHR) \ - OPTIONAL(ReleaseDisplayEXT) \ - OPTIONAL(DestroySurfaceKHR) \ - OPTIONAL(CreateHeadlessSurfaceEXT) \ - OPTIONAL(GetPhysicalDeviceQueueFamilyProperties) \ - OPTIONAL(CreateDisplayModeKHR) \ - -struct instance_dispatch_table { - VkResult populate(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc); - -#define DISPATCH_TABLE_ENTRY(x) PFN_vk##x x{}; - INSTANCE_ENTRYPOINTS_LIST(DISPATCH_TABLE_ENTRY, DISPATCH_TABLE_ENTRY) -#undef DISPATCH_TABLE_ENTRY -}; - -/* List of device entrypoints in the layer's device dispatch table. - * The layer fails initializing a device instance when entrypoints marked with REQUIRED() are - * retrieved as null. The layer will instead tolerate retrieving a null for entrypoints marked as - * OPTIONAL(). Code in the layer needs to check these entrypoints are non-null before calling them. - * - * Note that we cannot rely on checking whether the physical device supports a particular extension - * as the Vulkan loader currently aggregates all extensions advertised by all implicit layers (in - * their JSON manifests) and adds them automatically to the output of - * vkEnumeratePhysicalDeviceProperties. - */ -#define DEVICE_ENTRYPOINTS_LIST(REQUIRED, OPTIONAL) \ - REQUIRED(GetDeviceProcAddr) \ - REQUIRED(GetDeviceQueue) \ - REQUIRED(QueueSubmit) \ - REQUIRED(QueueWaitIdle) \ - REQUIRED(CreateCommandPool) \ - REQUIRED(DestroyCommandPool) \ - REQUIRED(AllocateCommandBuffers) \ - REQUIRED(FreeCommandBuffers) \ - REQUIRED(ResetCommandBuffer) \ - REQUIRED(BeginCommandBuffer) \ - REQUIRED(EndCommandBuffer) \ - REQUIRED(CreateImage) \ - REQUIRED(DestroyImage) \ - REQUIRED(GetImageMemoryRequirements) \ - REQUIRED(BindImageMemory) \ - REQUIRED(AllocateMemory) \ - REQUIRED(FreeMemory) \ - REQUIRED(CreateFence) \ - REQUIRED(DestroyFence) \ - REQUIRED(ResetFences) \ - REQUIRED(WaitForFences) \ - OPTIONAL(CreateSwapchainKHR) \ - OPTIONAL(DestroySwapchainKHR) \ - OPTIONAL(GetSwapchainImagesKHR) \ - OPTIONAL(AcquireNextImageKHR) \ - OPTIONAL(QueuePresentKHR) \ - OPTIONAL(GetSwapchainCounterEXT) \ - OPTIONAL(RegisterDisplayEventEXT) \ - OPTIONAL(GetFenceStatus) \ - OPTIONAL(GetMemoryFdKHR) \ - OPTIONAL(CreateSemaphore) \ - OPTIONAL(GetSemaphoreFdKHR) - -struct device_dispatch_table { - VkResult populate(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc); - -#define DISPATCH_TABLE_ENTRY(x) PFN_vk##x x{}; - DEVICE_ENTRYPOINTS_LIST(DISPATCH_TABLE_ENTRY, DISPATCH_TABLE_ENTRY) -#undef DISPATCH_TABLE_ENTRY -}; - -/** - * @brief Layer "mirror object" for VkInstance. - */ -class instance_private_data { - public: - instance_private_data() = delete; - instance_private_data(const instance_private_data &) = delete; - instance_private_data &operator=(const instance_private_data &) = delete; - - instance_private_data(const instance_dispatch_table &table, - PFN_vkSetInstanceLoaderData set_loader_data, - util::wsi_platform_set enabled_layer_platforms); - static void set(VkInstance inst, std::unique_ptr inst_data); - - /** - * @brief Get the mirror object that the layer associates to a given Vulkan instance. - */ - static instance_private_data &get(VkInstance instance); - - /** - * @brief Get the layer instance object associated to the VkInstance owning the specified - * VkPhysicalDevice. - */ - static instance_private_data &get(VkPhysicalDevice phys_dev); - - /** - * @brief Get the set of enabled platforms that are also supported by the layer. - */ - const util::wsi_platform_set &get_enabled_platforms() { return enabled_layer_platforms; } - - /** - * @brief Check whether a surface command should be handled by the WSI layer. - * - * @param phys_dev Physical device involved in the Vulkan command. - * @param surface The surface involved in the Vulkan command. - * - * @retval @c true if the layer should handle commands for the specified surface, which may mean - * returning an error if the layer does not support @p surface 's platform. - * - * @retval @c false if the layer should call down to the layers and ICDs below to handle the - * surface commands. - */ - bool should_layer_handle_surface(VkSurfaceKHR surface); - - /** - * @brief Check whether the given surface is supported for presentation via the layer. - * - * @param surface A VK_KHR_surface surface. - * - * @return Whether the WSI layer supports this surface. - */ - bool does_layer_support_surface(VkSurfaceKHR surface); - - void add_surface(VkSurfaceKHR); - - static void destroy(VkInstance inst); - - const instance_dispatch_table disp; - - private: - /** - * @brief Check whether the given surface is already supported for presentation without the - * layer. - */ - bool do_icds_support_surface(VkPhysicalDevice phys_dev, VkSurfaceKHR surface); - - const PFN_vkSetInstanceLoaderData SetInstanceLoaderData; - const util::wsi_platform_set enabled_layer_platforms; - - std::unordered_set surfaces; -}; - -class device_private_data { - public: - device_private_data() = delete; - device_private_data(const device_private_data &) = delete; - device_private_data &operator=(const device_private_data &) = delete; - - device_private_data(instance_private_data &inst_data, VkPhysicalDevice phys_dev, VkDevice dev, - const device_dispatch_table &table, - PFN_vkSetDeviceLoaderData set_loader_data); - static void set(VkDevice dev, std::unique_ptr dev_data); - - /** - * @brief Get the mirror object that the layer associates to a given Vulkan device. - */ - static device_private_data &get(VkDevice device); - - /** - * @brief Get the layer device object associated to the VkDevice owning the specified VkQueue. - */ - static device_private_data &get(VkQueue queue); - - void add_layer_swapchain(VkSwapchainKHR swapchain); - - /** - * @brief Return whether all the provided swapchains are owned by us (the WSI Layer). - */ - bool layer_owns_all_swapchains(const VkSwapchainKHR *swapchain, uint32_t swapchain_count) const; - - /** - * @brief Check whether the given swapchain is owned by us (the WSI Layer). - */ - bool layer_owns_swapchain(VkSwapchainKHR swapchain) const { - return layer_owns_all_swapchains(&swapchain, 1); - } - - /** - * @brief Check whether the layer can create a swapchain for the given surface. - */ - bool should_layer_create_swapchain(VkSurfaceKHR vk_surface); - - /** - * @brief Check whether the ICDs or layers below support VK_KHR_swapchain. - */ - bool can_icds_create_swapchain(VkSurfaceKHR vk_surface); - - static void destroy(VkDevice dev); - - const device_dispatch_table disp; - instance_private_data &instance_data; - const PFN_vkSetDeviceLoaderData SetDeviceLoaderData; - const VkPhysicalDevice physical_device; - const VkDevice device; - - std::unique_ptr display; - private: - std::unordered_set swapchains; - mutable std::mutex swapchains_lock; -}; - -} /* namespace layer */ diff --git a/alvr/vulkan_layer/layer/settings.cpp b/alvr/vulkan_layer/layer/settings.cpp deleted file mode 100644 index ab93c8bc8d..0000000000 --- a/alvr/vulkan_layer/layer/settings.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "settings.h" -#define PICOJSON_USE_INT64 -#include "alvr_server/include/picojson.h" -#include -#include -#include -#include -#include -#include "layer.h" -#include "util/logger.h" - -using namespace std; - -extern uint64_t g_DriverTestMode; - -Settings Settings::m_Instance; - -Settings::Settings() - : m_loaded(false) -{ -} - -Settings::~Settings() -{ -} - -void Settings::Load() -{ - try - { - auto sessionFile = std::ifstream(g_sessionPath); - - auto json = std::string( - std::istreambuf_iterator(sessionFile), - std::istreambuf_iterator()); - - picojson::value v; - std::string err = picojson::parse(v, json); - if (!err.empty()) - { - Error("Error on parsing session config (%s): %hs\n", g_sessionPath, err.c_str()); - return; - } - - auto config = v.get("openvr_config"); - - m_renderWidth = config.get("eye_resolution_width").get() * 2; - m_renderHeight = config.get("eye_resolution_height").get(); - - m_refreshRate = (int)config.get("refresh_rate").get(); - - Debug("Config JSON: %hs\n", json.c_str()); - Info("Render Target: %d %d\n", m_renderWidth, m_renderHeight); - Info("Refresh Rate: %d\n", m_refreshRate); - m_loaded = true; - } - catch (std::exception &e) - { - Error("Exception on parsing session config (%s): %hs\n", g_sessionPath, e.what()); - } -} diff --git a/alvr/vulkan_layer/layer/settings.h b/alvr/vulkan_layer/layer/settings.h deleted file mode 100644 index 233fc268b4..0000000000 --- a/alvr/vulkan_layer/layer/settings.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -class Settings -{ - static Settings m_Instance; - bool m_loaded; - - Settings(); - virtual ~Settings(); - -public: - void Load(); - static Settings &Instance() { - return m_Instance; - } - - bool IsLoaded() { - return m_loaded; - } - - int m_refreshRate; - uint32_t m_renderWidth; - uint32_t m_renderHeight; -}; diff --git a/alvr/vulkan_layer/layer/surface_api.cpp b/alvr/vulkan_layer/layer/surface_api.cpp deleted file mode 100644 index a096ee450a..0000000000 --- a/alvr/vulkan_layer/layer/surface_api.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2016-2017, 2019, 2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "surface_api.hpp" -#include "private_data.hpp" -#include -#include - -extern "C" { - -/** - * @brief Implements vkGetPhysicalDeviceSurfaceCapabilitiesKHR Vulkan entrypoint. - */ -VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, - VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) { - auto &instance = layer::instance_private_data::get(physicalDevice); - if (instance.should_layer_handle_surface(surface)) { - wsi::surface_properties *props = wsi::get_surface_properties(surface); - assert(props != nullptr); - return props->get_surface_capabilities(physicalDevice, surface, pSurfaceCapabilities); - } - - /* If the layer cannot handle this surface, then necessarily the surface must have been created - * by the ICDs (or a layer below us.) So it is safe to assume that the ICDs (or layers below us) - * support VK_KHR_surface and therefore it is safe to can call down. This holds for other - * entrypoints below. - */ - return instance.disp.GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, - pSurfaceCapabilities); -} - -/** - * @brief Implements vkGetPhysicalDeviceSurfaceFormatsKHR Vulkan entrypoint. - */ -VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceFormatsKHR( - VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pSurfaceFormatCount, - VkSurfaceFormatKHR *pSurfaceFormats) { - auto &instance = layer::instance_private_data::get(physicalDevice); - if (instance.should_layer_handle_surface(surface)) { - wsi::surface_properties *props = wsi::get_surface_properties(surface); - assert(props != nullptr); - return props->get_surface_formats(physicalDevice, surface, pSurfaceFormatCount, - pSurfaceFormats); - } - - return instance.disp.GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, - pSurfaceFormatCount, pSurfaceFormats); -} - -/** - * @brief Implements vkGetPhysicalDeviceSurfacePresentModesKHR Vulkan entrypoint. - */ -VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfacePresentModesKHR( - VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pPresentModeCount, - VkPresentModeKHR *pPresentModes) { - auto &instance = layer::instance_private_data::get(physicalDevice); - if (instance.should_layer_handle_surface(surface)) { - wsi::surface_properties *props = wsi::get_surface_properties(surface); - assert(props != nullptr); - return props->get_surface_present_modes(physicalDevice, surface, pPresentModeCount, - pPresentModes); - } - - return instance.disp.GetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, - pPresentModeCount, pPresentModes); -} - -/** - * @brief Implements vkGetPhysicalDeviceSurfaceSupportKHR Vulkan entrypoint. - */ -VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, - uint32_t queueFamilyIndex, - VkSurfaceKHR surface, - VkBool32 *pSupported) { - auto &instance = layer::instance_private_data::get(physicalDevice); - if (instance.should_layer_handle_surface(surface)) { - *pSupported = VK_TRUE; - return VK_SUCCESS; - } - - return instance.disp.GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, - surface, pSupported); -} - -} /* extern "C" */ diff --git a/alvr/vulkan_layer/layer/surface_api.hpp b/alvr/vulkan_layer/layer/surface_api.hpp deleted file mode 100644 index 8842f04d8a..0000000000 --- a/alvr/vulkan_layer/layer/surface_api.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2019, 2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file surface_api.hpp - * - * @brief Contains the Vulkan entrypoints for the VkSurfaceKHR. - */ -#pragma once - -#include - -extern "C" { - -/** - * @brief Implements vkGetPhysicalDeviceSurfaceCapabilitiesKHR Vulkan entrypoint. - */ -VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, - VkSurfaceCapabilitiesKHR *pSurfaceCapabilities); - -/** - * @brief Implements vkGetPhysicalDeviceSurfaceFormatsKHR Vulkan entrypoint. - */ -VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceFormatsKHR( - VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pSurfaceFormatCount, - VkSurfaceFormatKHR *pSurfaceFormats); - -/** - * @brief Implements vkGetPhysicalDeviceSurfacePresentModesKHR Vulkan entrypoint. - */ -VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfacePresentModesKHR( - VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pPresentModeCount, - VkPresentModeKHR *pPresentModes); - -/** - * @brief Implements vkGetPhysicalDeviceSurfaceSupportKHR Vulkan entrypoint. - */ -VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, - uint32_t queueFamilyIndex, - VkSurfaceKHR surface, - VkBool32 *pSupported); -} diff --git a/alvr/vulkan_layer/layer/swapchain_api.cpp b/alvr/vulkan_layer/layer/swapchain_api.cpp deleted file mode 100644 index 0bc744d293..0000000000 --- a/alvr/vulkan_layer/layer/swapchain_api.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2017, 2019, 2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file swapchain_api.cpp - * - * @brief Contains the Vulkan entrypoints for the swapchain. - */ - -#include -#include -#include - -#include - -#include "private_data.hpp" -#include "swapchain_api.hpp" - -extern "C" { - -VKAPI_ATTR VkResult wsi_layer_vkCreateSwapchainKHR( - VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo, - const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) { - assert(pSwapchain != nullptr); - layer::device_private_data &device_data = layer::device_private_data::get(device); - VkSurfaceKHR surface = pSwapchainCreateInfo->surface; - - if (!device_data.should_layer_create_swapchain(surface)) { - if (!device_data.can_icds_create_swapchain(surface)) { - return VK_ERROR_INITIALIZATION_FAILED; - } - return device_data.disp.CreateSwapchainKHR(device_data.device, pSwapchainCreateInfo, - pAllocator, pSwapchain); - } - - wsi::swapchain_base *sc = wsi::allocate_surface_swapchain(surface, device_data, pAllocator); - if (sc == nullptr) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - VkResult result = sc->init(device, pSwapchainCreateInfo); - if (result != VK_SUCCESS) { - /* Error occured during initialization, need to free allocated memory. */ - wsi::destroy_surface_swapchain(sc, pAllocator); - return result; - } - - *pSwapchain = reinterpret_cast(sc); - device_data.add_layer_swapchain(*pSwapchain); - return result; -} - -VKAPI_ATTR void wsi_layer_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapc, - const VkAllocationCallbacks *pAllocator) { - layer::device_private_data &device_data = layer::device_private_data::get(device); - - if (!device_data.layer_owns_swapchain(swapc)) { - return device_data.disp.DestroySwapchainKHR(device_data.device, swapc, pAllocator); - } - - assert(swapc != VK_NULL_HANDLE); - wsi::swapchain_base *sc = reinterpret_cast(swapc); - wsi::destroy_surface_swapchain(sc, pAllocator); -} - -VKAPI_ATTR VkResult wsi_layer_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapc, - uint32_t *pSwapchainImageCount, - VkImage *pSwapchainImages) { - layer::device_private_data &device_data = layer::device_private_data::get(device); - - if (!device_data.layer_owns_swapchain(swapc)) { - return device_data.disp.GetSwapchainImagesKHR(device_data.device, swapc, - pSwapchainImageCount, pSwapchainImages); - } - - assert(pSwapchainImageCount != nullptr); - assert(swapc != VK_NULL_HANDLE); - wsi::swapchain_base *sc = reinterpret_cast(swapc); - return sc->get_swapchain_images(pSwapchainImageCount, pSwapchainImages); -} - -VKAPI_ATTR VkResult wsi_layer_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapc, - uint64_t timeout, VkSemaphore semaphore, - VkFence fence, uint32_t *pImageIndex) { - layer::device_private_data &device_data = layer::device_private_data::get(device); - - if (!device_data.layer_owns_swapchain(swapc)) { - return device_data.disp.AcquireNextImageKHR(device_data.device, swapc, timeout, semaphore, - fence, pImageIndex); - } - - assert(swapc != VK_NULL_HANDLE); - assert(semaphore != VK_NULL_HANDLE || fence != VK_NULL_HANDLE); - assert(pImageIndex != nullptr); - wsi::swapchain_base *sc = reinterpret_cast(swapc); - return sc->acquire_next_image(timeout, semaphore, fence, pImageIndex); -} - -VKAPI_ATTR VkResult wsi_layer_vkQueuePresentKHR(VkQueue queue, - const VkPresentInfoKHR *pPresentInfo) { - assert(queue != VK_NULL_HANDLE); - assert(pPresentInfo != nullptr); - - layer::device_private_data &device_data = layer::device_private_data::get(queue); - - if (!device_data.layer_owns_all_swapchains(pPresentInfo->pSwapchains, - pPresentInfo->swapchainCount)) { - return device_data.disp.QueuePresentKHR(queue, pPresentInfo); - } - - VkResult ret = VK_SUCCESS; - for (uint32_t i = 0; i < pPresentInfo->swapchainCount; ++i) { - VkSwapchainKHR swapc = pPresentInfo->pSwapchains[i]; - - wsi::swapchain_base *sc = reinterpret_cast(swapc); - assert(sc != nullptr); - - VkResult res = sc->queue_present(queue, pPresentInfo, pPresentInfo->pImageIndices[i]); - - if (pPresentInfo->pResults != nullptr) { - pPresentInfo->pResults[i] = res; - } - - if (res != VK_SUCCESS && ret == VK_SUCCESS) { - ret = res; - } - } - - return ret; -} - -VKAPI_ATTR VkResult wsi_layer_vkGetSwapchainCounterEXT(VkDevice device, VkSwapchainKHR swapchain, - VkSurfaceCounterFlagBitsEXT counter, - uint64_t *pCounterValue) { - layer::device_private_data &device_data = layer::device_private_data::get(device); - if (!device_data.layer_owns_swapchain(swapchain)) { - return device_data.disp.GetSwapchainCounterEXT(device, swapchain, counter, pCounterValue); - } - if (VK_SURFACE_COUNTER_VBLANK_EXT == counter) { - *pCounterValue = device_data.display->m_vsync_count; - } - return VK_SUCCESS; -} - -} /* extern "C" */ diff --git a/alvr/vulkan_layer/layer/swapchain_api.hpp b/alvr/vulkan_layer/layer/swapchain_api.hpp deleted file mode 100644 index d5ec946ad9..0000000000 --- a/alvr/vulkan_layer/layer/swapchain_api.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-2019 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file swapchain_api.hpp - * - * @brief Contains the Vulkan entrypoints for the swapchain. - */ - -#pragma once - -#include - -extern "C" { - -VKAPI_ATTR VkResult wsi_layer_vkCreateSwapchainKHR( - VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo, - const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain); - -VKAPI_ATTR void wsi_layer_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapc, - const VkAllocationCallbacks *pAllocator); - -VKAPI_ATTR VkResult wsi_layer_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapc, - uint32_t *pSwapchainImageCount, - VkImage *pSwapchainImages); - -VKAPI_ATTR VkResult wsi_layer_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapc, - uint64_t timeout, VkSemaphore semaphore, - VkFence fence, uint32_t *pImageIndex); - -VKAPI_ATTR VkResult wsi_layer_vkQueuePresentKHR(VkQueue queue, - const VkPresentInfoKHR *pPresentInfo); - -VKAPI_ATTR VkResult wsi_layer_vkGetSwapchainCounterEXT(VkDevice device, VkSwapchainKHR swapchain, - VkSurfaceCounterFlagBitsEXT counter, - uint64_t *pCounterValue); -} diff --git a/alvr/vulkan_layer/src/lib.rs b/alvr/vulkan_layer/src/lib.rs deleted file mode 100644 index 74bc932339..0000000000 --- a/alvr/vulkan_layer/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -#![cfg(target_os = "linux")] -#![allow( - dead_code, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unsafe_op_in_unsafe_fn, - unused_imports, - clippy::missing_safety_doc, - clippy::ptr_offset_with_cast, - clippy::too_many_arguments, - clippy::useless_transmute, - clippy::pedantic, - clippy::nursery -)] - -use std::ffi::CString; - -mod bindings { - include!(concat!(env!("OUT_DIR"), "/layer_bindings.rs")); -} -use bindings::*; - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn ALVR_Negotiate(nli: *mut VkNegotiateLayerInterface) -> VkResult { - unsafe { - g_sessionPath = CString::new( - alvr_filesystem::filesystem_layout_invalid() - .session() - .to_string_lossy() - .to_string(), - ) - .unwrap() - .into_raw(); - - bindings::wsi_layer_Negotiate(nli) - } -} diff --git a/alvr/vulkan_layer/util/custom_allocator.cpp b/alvr/vulkan_layer/util/custom_allocator.cpp deleted file mode 100644 index 55cdea3a78..0000000000 --- a/alvr/vulkan_layer/util/custom_allocator.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020-2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "custom_allocator.hpp" - -extern "C" { - -static void *default_allocation(void *, size_t size, size_t, VkSystemAllocationScope) { - return malloc(size); -} - -static void *default_reallocation(void *, void *pOriginal, size_t size, size_t, - VkSystemAllocationScope) { - return realloc(pOriginal, size); -} - -static void default_free(void *, void *pMemory) { free(pMemory); } -} - -namespace util { - -const allocator &allocator::get_generic() { - static allocator generic{nullptr, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND}; - return generic; -} - -allocator::allocator(const allocator &other, VkSystemAllocationScope new_scope) - : allocator{other.get_original_callbacks(), new_scope} {} - -/* If callbacks is already populated by vulkan then use those specified as default. */ -allocator::allocator(const VkAllocationCallbacks *callbacks, VkSystemAllocationScope scope) { - m_scope = scope; - if (callbacks != nullptr) { - m_callbacks = *callbacks; - } else { - m_callbacks = {}; - m_callbacks.pfnAllocation = default_allocation; - m_callbacks.pfnReallocation = default_reallocation; - m_callbacks.pfnFree = default_free; - } -} - -const VkAllocationCallbacks *allocator::get_original_callbacks() const { - return m_callbacks.pfnAllocation == default_allocation ? nullptr : &m_callbacks; -} - -} /* namespace util */ diff --git a/alvr/vulkan_layer/util/custom_allocator.hpp b/alvr/vulkan_layer/util/custom_allocator.hpp deleted file mode 100644 index 47bb689cd8..0000000000 --- a/alvr/vulkan_layer/util/custom_allocator.hpp +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2020-2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -#include - -#pragma once - -namespace util { - -/** - * @brief Minimalistic wrapper of VkAllocationCallbacks. - */ -class allocator { - public: - /** - * @brief Get an allocator that can be used if VkAllocationCallbacks are not provided. - */ - static const allocator &get_generic(); - - /** - * @brief Construct a new wrapper for the given VK callbacks and scope. - * @param callbacks Pointer to allocation callbacks. If this is @c nullptr, then default - * allocation callbacks are used. These can be accessed through #m_callbacks. - * @param scope The scope to use for this allocator. - */ - allocator(const VkAllocationCallbacks *callbacks, VkSystemAllocationScope scope); - - /** - * @brief Copy the given allocator, but change the allocation scope. - */ - allocator(const allocator &other, VkSystemAllocationScope new_scope); - - /** - * @brief Get a pointer to the allocation callbacks provided while constructing this object. - * @return a copy of the #VkAllocationCallback argument provided in the allocator constructor - * or @c nullptr if this argument was provided as @c nullptr. - * @note The #m_callbacks member is always populated with callable pointers for pfnAllocation, - * pfnReallocation and pfnFree. - */ - const VkAllocationCallbacks *get_original_callbacks() const; - - /** - * @brief Helper method to allocate and construct objects with a custom allocator. - * @param num_objects Number of objects to create. - * @return Pointer to the newly created objects or @c nullptr if allocation failed. - */ - template - T *create(size_t num_objects, arg_types &&...args) const noexcept; - - /** - * @brief Helper method to destroy and deallocate objects constructed with allocator::create(). - * @param num_objects Number of objects to destroy. - */ - template void destroy(size_t num_objects, T *obj) const noexcept; - - VkAllocationCallbacks m_callbacks; - VkSystemAllocationScope m_scope; -}; - -/** - * @brief Implementation of an allocator that can be used with STL containers. - */ -template class custom_allocator { - public: - using value_type = T; - using pointer = T *; - - custom_allocator(const allocator &alloc) : m_alloc(alloc) {} - - template - custom_allocator(const custom_allocator &other) : m_alloc(other.get_data()) {} - - const allocator &get_data() const { return m_alloc; } - - pointer allocate(size_t n) const { - size_t size = n * sizeof(T); - auto &cb = m_alloc.m_callbacks; - void *ret = cb.pfnAllocation(cb.pUserData, size, alignof(T), m_alloc.m_scope); - if (ret == nullptr) - throw std::bad_alloc(); - return reinterpret_cast(ret); - } - - pointer allocate(size_t n, void *ptr) const { - size_t size = n * sizeof(T); - auto &cb = m_alloc.m_callbacks; - void *ret = cb.pfnReallocation(cb.pUserData, ptr, size, alignof(T), m_alloc.m_scope); - if (ret == nullptr) - throw std::bad_alloc(); - return reinterpret_cast(ret); - } - - void deallocate(void *ptr, size_t) const noexcept { - m_alloc.m_callbacks.pfnFree(m_alloc.m_callbacks.pUserData, ptr); - } - - private: - const allocator m_alloc; -}; - -template -bool operator==(const custom_allocator &, const custom_allocator &) { - return true; -} - -template -bool operator!=(const custom_allocator &, const custom_allocator &) { - return false; -} - -template -T *allocator::create(size_t num_objects, arg_types &&...args) const noexcept { - if (num_objects < 1) { - return nullptr; - } - - custom_allocator allocator(*this); - T *ptr; - try { - ptr = allocator.allocate(num_objects); - } catch (...) { - return nullptr; - } - - size_t objects_constructed = 0; - try { - while (objects_constructed < num_objects) { - T *next_object = &ptr[objects_constructed]; - new (next_object) T(std::forward(args)...); - objects_constructed++; - } - } catch (...) { - /* We catch all exceptions thrown while constructing the object, not just - * std::bad_alloc. - */ - while (objects_constructed > 0) { - objects_constructed--; - ptr[objects_constructed].~T(); - } - allocator.deallocate(ptr, num_objects); - return nullptr; - } - return ptr; -} - -template void allocator::destroy(size_t num_objects, T *objects) const noexcept { - assert((objects == nullptr) == (num_objects == 0)); - if (num_objects == 0) { - return; - } - - custom_allocator allocator(*this); - for (size_t i = 0; i < num_objects; i++) { - objects[i].~T(); - } - allocator.deallocate(objects, num_objects); -} - -template void destroy_custom(T *obj) { T::destroy(obj); } - -/** - * @brief Vector using a Vulkan custom allocator to allocate its elements. - * @note The vector must be passed a custom_allocator during construction and it takes a copy - * of it, meaning that the user is free to destroy the custom_allocator after constructing the - * vector. - */ -template class vector : public std::vector> { - public: - using base = std::vector>; - using base::base; - - /* Delete all methods that can cause allocation failure, i.e. can throw std::bad_alloc. - * - * Rationale: we want to force users to use our corresponding try_... method instead: - * this makes the API slightly more annoying to use, but hopefully safer as it encourages - * users to check for allocation failures, which is important for Vulkan. - * - * Note: deleting each of these methods (below) deletes all its overloads from the base class, - * to be precise: the deleted method covers the methods (all overloads) in the base class. - * Note: clear() is already noexcept since C++11. - */ - void insert() = delete; - void emplace() = delete; - void emplace_back() = delete; - void push_back() = delete; - void resize() = delete; - void reserve() = delete; - - /* Note pop_back(), erase(), clear() do not throw std::bad_alloc exceptions. */ - - /* @brief Like std::vector::push_back, but non throwing. - * @return @c false iff the operation could not be performed due to an allocation failure. - */ - template bool try_push_back(arg_types &&...args) noexcept { - try { - base::push_back(std::forward(args)...); - return true; - } catch (const std::bad_alloc &e) { - return false; - } - } - - /* @brief push back multiple elements at once - * @return @c false iff the operation could not be performed due to an allocation failure. - */ - bool try_push_back_many(const T *begin, const T *end) noexcept { - for (const T *it = begin; it != end; ++it) { - if (!try_push_back(*it)) { - return false; - } - } - return true; - } - - /* @brief Like std::vector::resize, but non throwing. - * @return @c false iff the operation could not be performed due to an allocation failure. - */ - template bool try_resize(arg_types &&...args) noexcept { - try { - base::resize(std::forward(args)...); - return true; - } catch (const std::bad_alloc &e) { - return false; - } - } -}; - -} /* namespace util */ diff --git a/alvr/vulkan_layer/util/extension_list.cpp b/alvr/vulkan_layer/util/extension_list.cpp deleted file mode 100644 index d6c46a656b..0000000000 --- a/alvr/vulkan_layer/util/extension_list.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2019, 2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "extension_list.hpp" -#include -#include -#include - -namespace util { - -extension_list::extension_list(const util::allocator &allocator) - : m_alloc{allocator}, m_ext_props(allocator) {} - -VkResult extension_list::add(const struct VkEnumerateInstanceExtensionPropertiesChain *chain) { - uint32_t count; - VkResult m_error = chain->CallDown(nullptr, &count, nullptr); - if (m_error == VK_SUCCESS) { - if (!m_ext_props.try_resize(count)) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - m_error = chain->CallDown(nullptr, &count, m_ext_props.data()); - } - return m_error; -} - -VkResult extension_list::add(VkPhysicalDevice dev) { - layer::instance_private_data &inst_data = layer::instance_private_data::get(dev); - uint32_t count; - VkResult m_error = - inst_data.disp.EnumerateDeviceExtensionProperties(dev, nullptr, &count, nullptr); - - if (m_error == VK_SUCCESS) { - if (!m_ext_props.try_resize(count)) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - m_error = inst_data.disp.EnumerateDeviceExtensionProperties(dev, nullptr, &count, - m_ext_props.data()); - } - return m_error; -} - -VkResult extension_list::add( - PFN_vkEnumerateInstanceExtensionProperties fpEnumerateInstanceExtensionProperties) { - uint32_t count = 0; - VkResult m_error = fpEnumerateInstanceExtensionProperties(nullptr, &count, nullptr); - - if (m_error == VK_SUCCESS) { - if (!m_ext_props.try_resize(count)) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - m_error = fpEnumerateInstanceExtensionProperties(nullptr, &count, m_ext_props.data()); - } - return m_error; -} - -VkResult extension_list::add(const char *const *extensions, uint32_t count) { - for (uint32_t i = 0; i < count; i++) { - VkExtensionProperties props = {}; - strncpy(props.extensionName, extensions[i], sizeof(props.extensionName) - 1); - props.extensionName[sizeof(props.extensionName) - 1] = '\0'; - if (!m_ext_props.try_push_back(props)) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - return VK_SUCCESS; -} - -VkResult extension_list::add(const VkExtensionProperties *props, uint32_t count) { - if (!m_ext_props.try_push_back_many(props, props + count)) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - return VK_SUCCESS; -} - -VkResult extension_list::add(const char *ext) { - if (!contains(ext)) { - VkExtensionProperties props = {}; - strncpy(props.extensionName, ext, sizeof(props.extensionName) - 1); - props.extensionName[sizeof(props.extensionName) - 1] = '\0'; - if (!m_ext_props.try_push_back(props)) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - return VK_SUCCESS; -} - -VkResult extension_list::add(VkExtensionProperties ext_prop) { - if (!contains(ext_prop.extensionName)) { - if (!m_ext_props.try_push_back(ext_prop)) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - return VK_SUCCESS; -} - -VkResult extension_list::add(const char **ext_list, uint32_t count) { - for (uint32_t i = 0; i < count; i++) { - if (add(ext_list[i]) != VK_SUCCESS) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - return VK_SUCCESS; -} - -VkResult extension_list::add(const extension_list &ext_list) { - util::vector ext_vect = ext_list.get_extension_props(); - for (auto &ext : ext_vect) { - if (add(ext) != VK_SUCCESS) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - return VK_SUCCESS; -} - -bool extension_list::get_extension_strings(util::vector &out) const { - size_t old_size = out.size(); - size_t new_size = old_size + m_ext_props.size(); - if (!out.try_resize(new_size)) { - return false; - } - - for (size_t i = old_size; i < new_size; i++) { - out[i] = m_ext_props[i - old_size].extensionName; - } - return true; -} - -bool extension_list::contains(const extension_list &req) const { - for (const auto &req_ext : req.m_ext_props) { - if (!contains(req_ext.extensionName)) { - return false; - } - } - return true; -} - -bool extension_list::contains(const char *extension_name) const { - for (const auto &p : m_ext_props) { - if (strcmp(p.extensionName, extension_name) == 0) { - return true; - } - } - return false; -} - -void extension_list::remove(const char *ext) { - m_ext_props.erase(std::remove_if(m_ext_props.begin(), m_ext_props.end(), - [&ext](VkExtensionProperties ext_prop) { - return (strcmp(ext_prop.extensionName, ext) == 0); - })); -} -} // namespace util diff --git a/alvr/vulkan_layer/util/extension_list.hpp b/alvr/vulkan_layer/util/extension_list.hpp deleted file mode 100644 index 80d3826185..0000000000 --- a/alvr/vulkan_layer/util/extension_list.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2019, 2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#pragma once - -#include "util/custom_allocator.hpp" - -#include -#include - -#include -#include - -namespace util { - -class extension_list { - public: - extension_list(const util::allocator &allocator); - - extension_list(const extension_list &rhs) = delete; - const extension_list &operator=(const extension_list &rhs) = delete; - - /** - * @brief Obtain a vector of #VkExtensionProperties equivalent to this extension_list object. - */ - const util::vector &get_extension_props() const { return m_ext_props; } - - /** - * @brief Get the allocator used to manage the memory of this object. - */ - const util::allocator get_allocator() const { return m_alloc; } - - /** - * @brief Append pointers to extension strings to the given vector. - * - * @warning Pointers in the vector are referring to string allocated in this extension_list and - * will become invalid if the extension_list is modified (e.g. by adding/removing elements.) - * - * @param[out] out A vector of C strings to which all extension are appended. - * - * @return A boolean indicating whether the operation was successful. If this is @c false, then - * @p out is unmodified. - */ - bool get_extension_strings(util::vector &out) const; - - bool contains(const extension_list &req) const; - bool contains(const char *ext) const; - void remove(const char *ext); - VkResult add(const char *ext); - VkResult add(VkExtensionProperties ext_prop); - VkResult add(const char **ext_list, uint32_t count); - VkResult add(const extension_list &ext_list); - VkResult add(const struct VkEnumerateInstanceExtensionPropertiesChain *chain); - VkResult add(PFN_vkEnumerateInstanceExtensionProperties fpEnumerateInstanceExtensionProperties); - VkResult add(VkPhysicalDevice dev); - VkResult add(const char *const *extensions, uint32_t count); - VkResult add(const VkExtensionProperties *props, uint32_t count); - - private: - util::allocator m_alloc; - util::vector m_ext_props; -}; -} // namespace util diff --git a/alvr/vulkan_layer/util/logger.cpp b/alvr/vulkan_layer/util/logger.cpp deleted file mode 100644 index cbd4539f3a..0000000000 --- a/alvr/vulkan_layer/util/logger.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include - -void _log(const char *format, va_list args, bool err) { - vfprintf(err ? stderr : stdout, format, args); -} - -void Error(const char *format, ...) { - va_list args; - va_start(args, format); - _log(format, args, true); - va_end(args); -} - -void Warn(const char *format, ...) { - va_list args; - va_start(args, format); - _log(format, args, true); - va_end(args); -} - -void Info(const char *format, ...) { - va_list args; - va_start(args, format); - _log(format, args, false); - va_end(args); -} - -void Debug(const char *format, ...) { - if (getenv("ALVR_LOG_DEBUG") == NULL) return; - va_list args; - va_start(args, format); - _log(format, args, true); - va_end(args); -} diff --git a/alvr/vulkan_layer/util/logger.h b/alvr/vulkan_layer/util/logger.h deleted file mode 100644 index 7269331990..0000000000 --- a/alvr/vulkan_layer/util/logger.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -void Error(const char *format, ...); -void Warn(const char *format, ...); -void Info(const char *format, ...); -void Debug(const char *format, ...); diff --git a/alvr/vulkan_layer/util/platform_set.hpp b/alvr/vulkan_layer/util/platform_set.hpp deleted file mode 100644 index 0261a7ff1b..0000000000 --- a/alvr/vulkan_layer/util/platform_set.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include -#include - -#include - -namespace util { - -/** - * @brief Set of WSI platforms. - * @note This could be implemented via std::unordered_set, but would require handling allocation - * callbacks and would therefore be less convenient to use. Instead, we can store all info in the - * bits of uint64_t. - */ -class wsi_platform_set { - public: - void add(VkIcdWsiPlatform p) { m_platforms |= (static_cast(1) << to_int(p)); } - - bool contains(VkIcdWsiPlatform p) const { - return (m_platforms & (static_cast(1) << to_int(p))) != 0; - } - - private: - /** - * @brief Convert a VkIcdWsiPlatform to an integer between 0-63. - */ - static int to_int(VkIcdWsiPlatform p) { - assert(static_cast(p) >= 0 && static_cast(p) < 64); - return static_cast(p); - } - - uint64_t m_platforms = 0; -}; - -} /* namespace util */ diff --git a/alvr/vulkan_layer/util/pose.cpp b/alvr/vulkan_layer/util/pose.cpp deleted file mode 100644 index 3d83d21bcc..0000000000 --- a/alvr/vulkan_layer/util/pose.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "pose.hpp" - -#include -#include - -#define UNW_LOCAL_ONLY -#include - -namespace { - -inline HmdMatrix34_t transposeMul33(const HmdMatrix34_t& a) { - HmdMatrix34_t result; - for (unsigned i = 0; i < 3; i++) { - for (unsigned k = 0; k < 3; k++) { - result.m[i][k] = a.m[k][i]; - } - } - result.m[0][3] = a.m[0][3]; - result.m[1][3] = a.m[1][3]; - result.m[2][3] = a.m[2][3]; - return result; -} - - -inline HmdMatrix34_t matMul33(const HmdMatrix34_t& a, const HmdMatrix34_t& b) { - HmdMatrix34_t result; - for (unsigned i = 0; i < 3; i++) { - for (unsigned j = 0; j < 3; j++) { - result.m[i][j] = 0.0f; - for (unsigned k = 0; k < 3; k++) { - result.m[i][j] += a.m[i][k] * b.m[k][j]; - } - } - } - return result; -} - - -bool check_pose(const TrackedDevicePose_t & p) -{ - if (p.bPoseIsValid != 1 or p.bDeviceIsConnected != 1) - return false; - - if (p.eTrackingResult != 200) - return false; - - auto m = matMul33(p.mDeviceToAbsoluteTracking, transposeMul33(p.mDeviceToAbsoluteTracking)); - for (int i = 0 ; i < 3; ++i ) - { - for (int j = 0 ; j < 3 ; ++j) - { - if (std::abs(m.m[i][j] - (i == j)) > 0.1) - return false; - } - } - return true; -} - -} - -// For a smooth experience, the correct pose for a frame must be known. -// Of course this is not part of vulkan parameters, so we must inspect -// the stack. -// First we look for the correct function (CRenderThread::UpdateAsync), -// then we scan all the local variables, and check for a suitable one. -// Such a variable is a TrackedDevicePose_t, with both booleans to true, -// which we compare to 1 to avoid false positives, a tracking result of -// 200, and a rotation matrix (A*transpose(A)) close to identity. -const TrackedDevicePose_t & find_pose_in_call_stack() -{ - static TrackedDevicePose_t * res; - if (res != nullptr) - return *res; - static TrackedDevicePose_t notfound; - unw_context_t ctx; - unw_getcontext(&ctx); - unw_cursor_t cursor; - unw_init_local(&cursor, &ctx); - while (unw_step(&cursor) > 0) - { - char name[1024]; - unw_word_t off; - unw_get_proc_name(&cursor, name, sizeof(name), &off); - if ((strcmp("_ZN13CRenderThread11UpdateAsyncEv", name) == 0) || (strcmp("_ZN13CRenderThread6UpdateEv", name) == 0)) - { - unw_word_t sp, sp_end; - unw_get_reg(&cursor, UNW_REG_SP, &sp); - unw_step(&cursor); - unw_get_reg(&cursor, UNW_REG_SP, &sp_end); - for (uintptr_t addr = sp ; addr < sp_end; addr += 4) - { - TrackedDevicePose_t * p = (TrackedDevicePose_t *) addr; - if (check_pose(*p)) - { - res = p; - return *p; - } - } - return notfound; - } - } - return notfound; -} diff --git a/alvr/vulkan_layer/util/pose.hpp b/alvr/vulkan_layer/util/pose.hpp deleted file mode 100644 index 1dcd1def8e..0000000000 --- a/alvr/vulkan_layer/util/pose.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -struct HmdMatrix34_t -{ - float m[3][4]; -}; - -struct HmdVector3_t -{ - float v[3]; -}; - -struct TrackedDevicePose_t -{ - HmdMatrix34_t mDeviceToAbsoluteTracking; - HmdVector3_t vVelocity; // velocity in tracker space in m/s - HmdVector3_t vAngularVelocity; // angular velocity in radians/s (?) - int eTrackingResult; - char bPoseIsValid; - - // This indicates that there is a device connected for this spot in the pose array. - // It could go from true to false if the user unplugs the device. - char bDeviceIsConnected; -}; - -const TrackedDevicePose_t & find_pose_in_call_stack(); diff --git a/alvr/vulkan_layer/util/timed_semaphore.cpp b/alvr/vulkan_layer/util/timed_semaphore.cpp deleted file mode 100644 index 25807fd86f..0000000000 --- a/alvr/vulkan_layer/util/timed_semaphore.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2017, 2019 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "timed_semaphore.hpp" - -namespace util { - -VkResult timed_semaphore::init(unsigned count) { - int res; - - m_count = count; - - pthread_condattr_t attr; - res = pthread_condattr_init(&attr); - /* the only failure that can occur is ENOMEM */ - assert(res == 0 || res == ENOMEM); - if (res != 0) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - res = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); - /* only programming error can cause _setclock to fail */ - assert(res == 0); - - res = pthread_cond_init(&m_cond, &attr); - /* the only failure that can occur that is not programming error is ENOMEM */ - assert(res == 0 || res == ENOMEM); - if (res != 0) { - pthread_condattr_destroy(&attr); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - res = pthread_condattr_destroy(&attr); - /* only programming error can cause _destroy to fail */ - assert(res == 0); - - res = pthread_mutex_init(&m_mutex, NULL); - /* only programming errors can result in failure */ - assert(res == 0); - - initialized = true; - - return VK_SUCCESS; -} - -timed_semaphore::~timed_semaphore() { - int res; - (void)res; /* unused when NDEBUG */ - - if (initialized) { - res = pthread_cond_destroy(&m_cond); - assert(res == 0); /* only programming error (EBUSY, EINVAL) */ - - res = pthread_mutex_destroy(&m_mutex); - assert(res == 0); /* only programming error (EBUSY, EINVAL) */ - } -} - -VkResult timed_semaphore::wait(uint64_t timeout) { - VkResult retval = VK_SUCCESS; - int res; - - assert(initialized); - - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); /* only fails with programming error (EINVAL) */ - - if (m_count == 0) { - switch (timeout) { - case 0: - retval = VK_NOT_READY; - break; - case UINT64_MAX: - res = pthread_cond_wait(&m_cond, &m_mutex); - assert(res == 0); /* only fails with programming error (EINVAL) */ - - break; - default: - struct timespec diff = {/* narrowing casts */ - static_cast(timeout / (1000 * 1000 * 1000)), - static_cast(timeout % (1000 * 1000 * 1000))}; - - struct timespec now; - res = clock_gettime(CLOCK_MONOTONIC, &now); - assert(res == 0); /* only fails with programming error (EINVAL, EFAULT, EPERM) */ - - /* add diff to now, handling overflow */ - struct timespec end = {now.tv_sec + diff.tv_sec, now.tv_nsec + diff.tv_nsec}; - - if (end.tv_nsec >= 1000 * 1000 * 1000) { - end.tv_nsec -= 1000 * 1000 * 1000; - end.tv_sec++; - } - - res = pthread_cond_timedwait(&m_cond, &m_mutex, &end); - /* only fails with programming error, other than timeout */ - assert(res == 0 || res == ETIMEDOUT); - if (res != 0) { - retval = VK_TIMEOUT; - } - } - } - if (retval == VK_SUCCESS) { - assert(m_count > 0); - m_count--; - } - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); /* only fails with programming error (EPERM) */ - - return retval; -} - -void timed_semaphore::post() { - int res; - (void)res; /* unused when NDEBUG */ - - assert(initialized); - - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); /* only fails with programming error (EINVAL) */ - - m_count++; - - res = pthread_cond_signal(&m_cond); - assert(res == 0); /* only fails with programming error (EINVAL) */ - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); /* only fails with programming error (EPERM) */ -} - -} /* namespace util */ diff --git a/alvr/vulkan_layer/util/timed_semaphore.hpp b/alvr/vulkan_layer/util/timed_semaphore.hpp deleted file mode 100644 index 5221230d3a..0000000000 --- a/alvr/vulkan_layer/util/timed_semaphore.hpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2017, 2019 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file timed_semaphore.hpp - * - * @brief Contains the class definition for a semaphore with a relative timedwait - * - * sem_timedwait takes an absolute time, based on CLOCK_REALTIME. Simply - * taking the current time and adding on a relative timeout is not correct, - * as the system time may change, resulting in an incorrect timeout period - * (potentially by a significant amount). - * - * We therefore have to re-engineer semaphores using condition variables. - * - * This code does not use the C++ standard library to avoid exceptions. - */ - -#pragma once - -extern "C" { -#include -} - -#include - -namespace util { - -/** - * brief semaphore with a safe relative timed wait - * - * sem_timedwait takes an absolute time, based on CLOCK_REALTIME. Simply - * taking the current time and adding on a relative timeout is not correct, - * as the system time may change, resulting in an incorrect timeout period - * (potentially by a significant amount). - * - * We therefore have to re-engineer semaphores using condition variables. - * - * This code does not use the C++ standard library to avoid exceptions. - */ -class timed_semaphore { - public: - /* copying not implemented */ - timed_semaphore &operator=(const timed_semaphore &) = delete; - timed_semaphore(const timed_semaphore &) = delete; - - ~timed_semaphore(); - timed_semaphore() : initialized(false){}; - - /** - * @brief initializes the semaphore - * - * @param count initial value of the semaphore - * @retval VK_ERROR_OUT_OF_HOST_MEMORY out of memory condition from pthread calls - * @retval VK_SUCCESS on success - */ - VkResult init(unsigned count); - - /** - * @brief decrement semaphore, waiting (with timeout) if the value is 0 - * - * @param timeout time to wait (ns). 0 doesn't block, UINT64_MAX waits indefinately. - * @retval VK_TIMEOUT timeout was non-zero and reached the timeout - * @retval VK_NOT_READY timeout was zero and count is 0 - * @retval VK_SUCCESS on success - */ - VkResult wait(uint64_t timeout); - - /** - * @brief increment semaphore, potentially unblocking a waiting thread - */ - void post(); - - private: - /** - * @brief true if the semaphore has been initialized - * - * Determines if the destructor should cleanup the mutex and cond. - */ - bool initialized; - /** - * @brief semaphore value - */ - unsigned m_count; - - pthread_mutex_t m_mutex; - pthread_cond_t m_cond; -}; - -} /* namespace util */ diff --git a/alvr/vulkan_layer/wsi/display.cpp b/alvr/vulkan_layer/wsi/display.cpp deleted file mode 100644 index d2b875d29e..0000000000 --- a/alvr/vulkan_layer/wsi/display.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "display.hpp" - -#include"layer/settings.h" - -#include - -wsi::display::display() -{ -} - -VkFence wsi::display::get_vsync_fence() -{ - if (not std::atomic_exchange(&m_thread_running, true)) - { - vsync_fence = reinterpret_cast(this); - m_vsync_thread = std::thread([this]() - { - auto refresh = Settings::Instance().m_refreshRate; - auto next_frame = std::chrono::steady_clock::now(); - auto frame_time = std::chrono::duration_cast(std::chrono::duration(1. / refresh)); - while (not m_exiting) { - std::this_thread::sleep_until(next_frame); - m_signaled = true; - m_cond.notify_all(); - m_vsync_count += 1; - next_frame += frame_time; - } - }); - } - m_signaled = false; - return vsync_fence; -} - -wsi::display::~display() { - std::unique_lock lock(m_mutex); - m_exiting = true; - if (m_vsync_thread.joinable()) - m_vsync_thread.join(); -} - -bool wsi::display::wait_for_vsync(uint64_t timeoutNs) -{ - if (!m_signaled) { - std::unique_lock lock(m_mutex); - return m_cond.wait_for(lock, std::chrono::nanoseconds(timeoutNs)) == std::cv_status::no_timeout; - } - return true; -} diff --git a/alvr/vulkan_layer/wsi/display.hpp b/alvr/vulkan_layer/wsi/display.hpp deleted file mode 100644 index e37b932bea..0000000000 --- a/alvr/vulkan_layer/wsi/display.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace wsi { - -class display { - public: - display(); - ~display(); - - VkFence get_vsync_fence(); - VkFence peek_vsync_fence() { return vsync_fence;}; - - bool is_signaled() const { return m_signaled; } - bool wait_for_vsync(uint64_t timeoutNs); - - std::atomic m_vsync_count{0}; - - private: - std::atomic_bool m_thread_running{false}; - std::atomic_bool m_exiting{false}; - std::thread m_vsync_thread; - VkFence vsync_fence = VK_NULL_HANDLE; - std::mutex m_mutex; - std::condition_variable m_cond; - std::atomic_bool m_signaled = false; -}; - -} // namespace wsi diff --git a/alvr/vulkan_layer/wsi/headless/surface_properties.cpp b/alvr/vulkan_layer/wsi/headless/surface_properties.cpp deleted file mode 100644 index e16c4f949b..0000000000 --- a/alvr/vulkan_layer/wsi/headless/surface_properties.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2017-2019, 2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include "layer/settings.h" - -#include "surface_properties.hpp" - -#define UNUSED(x) ((void)(x)) - -namespace wsi { -namespace headless { - -surface_properties &surface_properties::get_instance() { - static surface_properties instance; - return instance; -} - -VkResult -surface_properties::get_surface_capabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface, - VkSurfaceCapabilitiesKHR *surface_capabilities) { - UNUSED(surface); - /* Image count limits */ - surface_capabilities->minImageCount = 1; - /* There is no maximum theoretically speaking */ - surface_capabilities->maxImageCount = UINT32_MAX; - - /* Surface extents */ - surface_capabilities->currentExtent = surface_capabilities->maxImageExtent = - surface_capabilities->minImageExtent = {Settings::Instance().m_renderWidth, - Settings::Instance().m_renderHeight}; - /* Ask the device for max */ - VkPhysicalDeviceProperties dev_props; - layer::instance_private_data::get(physical_device) - .disp.GetPhysicalDeviceProperties(physical_device, &dev_props); - - surface_capabilities->maxImageArrayLayers = 1; - - /* Surface transforms */ - surface_capabilities->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - surface_capabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - - /* Composite alpha */ - surface_capabilities->supportedCompositeAlpha = static_cast( - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR | VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | - VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR | VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR); - - /* Image usage flags */ - surface_capabilities->supportedUsageFlags = - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; - - return VK_SUCCESS; -} - -VkResult surface_properties::get_surface_formats(VkPhysicalDevice physical_device, - VkSurfaceKHR surface, - uint32_t *surface_format_count, - VkSurfaceFormatKHR *surface_formats) { - UNUSED(surface); - - VkResult res = VK_SUCCESS; - /* Construct a list of all formats supported by the driver - for color attachment */ - VkFormat formats[] = { - VK_FORMAT_R8_UNORM, - VK_FORMAT_R16_UNORM, - VK_FORMAT_R8G8_UNORM, - VK_FORMAT_R16G16_UNORM, - VK_FORMAT_B8G8R8A8_UNORM, - VK_FORMAT_R8G8B8A8_UNORM}; - uint32_t format_count = 0; - - for (size_t id = 0; id < std::size(formats); id++) { - VkImageFormatProperties image_format_props; - - res = layer::instance_private_data::get(physical_device) - .disp.GetPhysicalDeviceImageFormatProperties( - physical_device, formats[id], VK_IMAGE_TYPE_2D, - VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, &image_format_props); - - if (res != VK_ERROR_FORMAT_NOT_SUPPORTED) { - formats[format_count] = formats[id]; - format_count++; - } - } - assert(format_count > 0); - assert(surface_format_count != nullptr); - res = VK_SUCCESS; - if (nullptr == surface_formats) { - *surface_format_count = format_count; - } else { - if (format_count > *surface_format_count) { - res = VK_INCOMPLETE; - } - - *surface_format_count = std::min(*surface_format_count, format_count); - for (uint32_t i = 0; i < *surface_format_count; ++i) { - surface_formats[i].format = formats[i]; - surface_formats[i].colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - } - } - - return res; -} - -VkResult surface_properties::get_surface_present_modes(VkPhysicalDevice physical_device, - VkSurfaceKHR surface, - uint32_t *present_mode_count, - VkPresentModeKHR *present_modes) { - UNUSED(physical_device); - UNUSED(surface); - - VkResult res = VK_SUCCESS; - static const std::array modes = {VK_PRESENT_MODE_FIFO_KHR, - VK_PRESENT_MODE_FIFO_RELAXED_KHR}; - - assert(present_mode_count != nullptr); - - if (nullptr == present_modes) { - *present_mode_count = modes.size(); - } else { - if (modes.size() > *present_mode_count) { - res = VK_INCOMPLETE; - } - *present_mode_count = std::min(*present_mode_count, static_cast(modes.size())); - for (uint32_t i = 0; i < *present_mode_count; ++i) { - present_modes[i] = modes[i]; - } - } - - return res; -} - -} /* namespace headless */ -} /* namespace wsi */ diff --git a/alvr/vulkan_layer/wsi/headless/surface_properties.hpp b/alvr/vulkan_layer/wsi/headless/surface_properties.hpp deleted file mode 100644 index c0a6cc7586..0000000000 --- a/alvr/vulkan_layer/wsi/headless/surface_properties.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2017-2019 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include -#include -#include - -namespace wsi { -namespace headless { - -class surface_properties : public wsi::surface_properties { - public: - VkResult get_surface_capabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface, - VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) override; - - VkResult get_surface_formats(VkPhysicalDevice physical_device, VkSurfaceKHR surface, - uint32_t *surfaceFormatCount, - VkSurfaceFormatKHR *surfaceFormats) override; - - VkResult get_surface_present_modes(VkPhysicalDevice physical_device, VkSurfaceKHR surface, - uint32_t *pPresentModeCount, - VkPresentModeKHR *pPresentModes) override; - - static surface_properties &get_instance(); -}; - -} /* namespace headless */ -} /* namespace wsi */ diff --git a/alvr/vulkan_layer/wsi/headless/swapchain.cpp b/alvr/vulkan_layer/wsi/headless/swapchain.cpp deleted file mode 100644 index f5975442bc..0000000000 --- a/alvr/vulkan_layer/wsi/headless/swapchain.cpp +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (c) 2017-2020 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file swapchain.cpp - * - * @brief Contains the implementation for a headless swapchain. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "util/logger.h" -#include "platform/linux/protocol.h" -#include "swapchain.hpp" -#include "wsi/display.hpp" - -namespace wsi { -namespace headless { - -struct image_data { - /* Device memory backing the image. */ - VkDeviceMemory memory; -}; - -swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator) - : wsi::swapchain_base(dev_data, pAllocator), m_display(*dev_data.display) {} - -swapchain::~swapchain() { - /* Call the base's teardown */ - close(m_socket); - teardown(); -} - -VkResult swapchain::create_image(const VkImageCreateInfo &image_create, - wsi::swapchain_image &image) { - VkResult res = VK_SUCCESS; - m_create_info = image_create; - m_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT - | VK_IMAGE_USAGE_TRANSFER_DST_BIT - | VK_IMAGE_USAGE_SAMPLED_BIT - | VK_IMAGE_USAGE_STORAGE_BIT; - res = m_device_data.disp.CreateImage(m_device, &m_create_info, nullptr, &image.image); - if (res != VK_SUCCESS) { - return res; - } - m_create_info.pNext = nullptr; - m_create_info.pQueueFamilyIndices = nullptr; - - VkMemoryRequirements memory_requirements; - m_device_data.disp.GetImageMemoryRequirements(m_device, image.image, &memory_requirements); - - /* Find a memory type */ - size_t mem_type_idx = 0; - VkMemoryPropertyFlags memFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - VkPhysicalDeviceMemoryProperties prop; - m_device_data.instance_data.disp.GetPhysicalDeviceMemoryProperties(m_device_data.physical_device, &prop); - for (; mem_type_idx < prop.memoryTypeCount; ++mem_type_idx) { - if ((prop.memoryTypes[mem_type_idx].propertyFlags & memFlags) == memFlags && memory_requirements.memoryTypeBits & (1 << mem_type_idx)) { - break; - } - } - assert(mem_type_idx < prop.memoryTypeCount); - - VkExportMemoryAllocateInfo export_info = {}; - export_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO; - export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; - - VkMemoryDedicatedAllocateInfo ded_info = {}; - ded_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; - ded_info.image = image.image; - ded_info.pNext = &export_info; - - VkMemoryAllocateInfo mem_info = {}; - mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - mem_info.allocationSize = memory_requirements.size; - mem_info.memoryTypeIndex = mem_type_idx; - mem_info.pNext = &ded_info; - m_mem_index = mem_type_idx; - image_data *data = nullptr; - - /* Create image_data */ - data = m_allocator.create(1); - if (data == nullptr) { - m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks()); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - image.data = reinterpret_cast(data); - image.status = wsi::swapchain_image::FREE; - - res = m_device_data.disp.AllocateMemory(m_device, &mem_info, nullptr, &data->memory); - assert(VK_SUCCESS == res); - if (res != VK_SUCCESS) { - destroy_image(image); - return res; - } - - res = m_device_data.disp.BindImageMemory(m_device, image.image, data->memory, 0); - assert(VK_SUCCESS == res); - if (res != VK_SUCCESS) { - destroy_image(image); - return res; - } - - /* Initialize presentation fence. */ - VkFenceCreateInfo fence_info = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0}; - res = m_device_data.disp.CreateFence(m_device, &fence_info, nullptr, &image.present_fence); - if (res != VK_SUCCESS) { - destroy_image(image); - return res; - } - - // Export into a FD to send later - VkMemoryGetFdInfoKHR fd_info = {}; - fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; - fd_info.pNext = NULL; - fd_info.memory = data->memory; - fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; - - int fd; - res = m_device_data.disp.GetMemoryFdKHR(m_device, &fd_info, &fd); - if (res != VK_SUCCESS) { - Error("GetMemoryFdKHR failed\n"); - destroy_image(image); - return res; - } - m_fds.push_back(fd); - Debug("GetMemoryFdKHR returned fd=%d\n", fd); - - VkExportSemaphoreCreateInfo exp_info = {}; - exp_info.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; - exp_info.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; - - VkSemaphoreTypeCreateInfo tim_info = {}; - tim_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; - tim_info.pNext = &exp_info; - tim_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; - - VkSemaphoreCreateInfo sem_info = {}; - sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - sem_info.pNext = &tim_info; - - res = m_device_data.disp.CreateSemaphore(m_device, &sem_info, nullptr, &image.semaphore); - if (res != VK_SUCCESS) { - Error("CreateSemaphore failed\n"); - destroy_image(image); - return res; - } - - VkSemaphoreGetFdInfoKHR sem_fd_info = {}; - sem_fd_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; - sem_fd_info.semaphore = image.semaphore; - sem_fd_info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; - - res = m_device_data.disp.GetSemaphoreFdKHR(m_device, &sem_fd_info, &fd); - if (res != VK_SUCCESS) { - Error("GetSemaphoreFdKHR failed\n"); - destroy_image(image); - return res; - } - m_fds.push_back(fd); - Debug("GetSemaphoreFdKHR returned fd=%d\n", fd); - - return res; -} - -int swapchain::send_fds() { - // This function does the arcane magic for sending - // file descriptors over unix domain sockets - // Stolen from https://gist.github.com/kokjo/75cec0f466fc34fa2922 - // - // There will always be 6 fds (for the 3 images and sempahores created in the swapchain) so we can avoid - // dynamic length. Initially, I tried to send the length in the normal data field (msg.msg_iov / - // data) but for some reason it was emptied on arrival, no matter what I did. - // - struct msghdr msg; - struct iovec iov[1]; - struct cmsghdr *cmsg = NULL; - assert(m_fds.size() == 6); - int fds[6]; - char ctrl_buf[CMSG_SPACE(sizeof(fds))]; - char data[1]; - - std::copy(m_fds.begin(), m_fds.end(), fds); - - memset(&msg, 0, sizeof(struct msghdr)); - memset(ctrl_buf, 0, CMSG_SPACE(sizeof(fds))); - - iov[0].iov_base = data; - iov[0].iov_len = sizeof(data); - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_controllen = CMSG_SPACE(sizeof(fds)); - msg.msg_control = ctrl_buf; - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(fds)); - - memcpy(CMSG_DATA(cmsg), fds, sizeof(fds)); - - int ret = sendmsg(m_socket, &msg, 0); - - for (auto fd: m_fds) - close(fd); - - return ret; -} - -bool swapchain::try_connect() { - Debug("swapchain::try_connect\n"); - m_socketPath = getenv("XDG_RUNTIME_DIR"); - m_socketPath += "/alvr-ipc"; - - int ret; - if (m_socket == -1) { - m_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (m_socket == -1) { - perror("socket"); - exit(1); - } - } - - struct sockaddr_un name; - memset(&name, 0, sizeof(name)); - name.sun_family = AF_UNIX; - strncpy(name.sun_path, m_socketPath.c_str(), sizeof(name.sun_path) - 1); - - ret = connect(m_socket, (const struct sockaddr *)&name, sizeof(name)); - if (ret == -1) { - return false; // we will try again next frame - } - - VkPhysicalDeviceVulkan11Properties props11 = {}; - props11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; - - VkPhysicalDeviceProperties2 props = {}; - props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - props.pNext = &props11; - m_device_data.instance_data.disp.GetPhysicalDeviceProperties2(m_device_data.physical_device, - &props); - - init_packet init{.num_images = uint32_t(m_swapchain_images.size()), - .device_uuid = {}, - .image_create_info = m_create_info, - .mem_index = m_mem_index, - .source_pid = getpid()}; - memcpy(init.device_uuid.data(), props11.deviceUUID, VK_UUID_SIZE); - ret = write(m_socket, &init, sizeof(init)); - if (ret == -1) { - perror("write"); - exit(1); - } - - ret = send_fds(); - if (ret == -1) { - perror("sendmsg"); - exit(1); - } - Debug("swapchain sent fds\n"); - - return true; -} - -void swapchain::submit_image(uint32_t pending_index) { - const auto & pose = m_swapchain_images[pending_index].pose.mDeviceToAbsoluteTracking.m; - if (!m_connected) { - m_connected = try_connect(); - } - if (m_connected) { - int ret; - present_packet packet; - packet.image = pending_index; - packet.frame = m_display.m_vsync_count; - packet.semaphore_value = m_swapchain_images[pending_index].semaphore_value; - memcpy(&packet.pose, pose, sizeof(packet.pose)); - ret = write(m_socket, &packet, sizeof(packet)); - if (ret == -1) { - //FIXME: try to reconnect? - } - } -} - -void swapchain::present_image(uint32_t pending_index) { - if (in_flight_index != UINT32_MAX) - unpresent_image(in_flight_index); - in_flight_index = pending_index; -} - -void swapchain::destroy_image(wsi::swapchain_image &image) { - if (image.status != wsi::swapchain_image::INVALID) { - if (image.present_fence != VK_NULL_HANDLE) { - m_device_data.disp.DestroyFence(m_device, image.present_fence, nullptr); - image.present_fence = VK_NULL_HANDLE; - } - - if (image.image != VK_NULL_HANDLE) { - m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks()); - image.image = VK_NULL_HANDLE; - } - } - - if (image.data != nullptr) { - auto *data = reinterpret_cast(image.data); - if (data->memory != VK_NULL_HANDLE) { - m_device_data.disp.FreeMemory(m_device, data->memory, nullptr); - data->memory = VK_NULL_HANDLE; - } - m_allocator.destroy(1, data); - image.data = nullptr; - } - - image.status = wsi::swapchain_image::INVALID; -} - -} /* namespace headless */ -} /* namespace wsi */ diff --git a/alvr/vulkan_layer/wsi/headless/swapchain.hpp b/alvr/vulkan_layer/wsi/headless/swapchain.hpp deleted file mode 100644 index 8870dc301d..0000000000 --- a/alvr/vulkan_layer/wsi/headless/swapchain.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2017-2019 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file swapchain.hpp - * - * @brief Contains the class definition for a headless swapchain. - */ - -#pragma once - -#include - -#include -#include -#include - -#include "platform/linux/protocol.h" - -namespace wsi { -namespace headless { - -/** - * @brief Headless swapchain class. - * - * This class is mostly empty, because all the swapchain stuff is handled by the swapchain class, - * which we inherit. This class only provides a way to create an image and page-flip ops. - */ -class swapchain : public wsi::swapchain_base { - public: - explicit swapchain(layer::device_private_data &dev_data, - const VkAllocationCallbacks *pAllocator); - - ~swapchain(); - - protected: - /** - * @brief Platform specific init - */ - VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo) { - return VK_SUCCESS; - }; - - /** - * @brief Creates a new swapchain image. - * - * @param image_create_info Data to be used to create the image. - * - * @param image Handle to the image. - * - * @return If image creation is successful returns VK_SUCCESS, otherwise - * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED - * depending on the error that occured. - */ - VkResult create_image(const VkImageCreateInfo &image_create_info, wsi::swapchain_image &image); - - void submit_image(uint32_t pendingIndex); - - /** - * @brief Method to perform a present - just calls unpresent_image on headless - * - * @param pendingIndex Index of the pending image to be presented. - * - */ - void present_image(uint32_t pendingIndex); - - /** - * @brief Method to release a swapchain image - * - * @param image Handle to the image about to be released. - */ - void destroy_image(wsi::swapchain_image &image); - - private: - bool try_connect(); - int send_fds(); - int m_socket = -1; - std::string m_socketPath; - bool m_connected = false; - std::vector m_fds; - VkImageCreateInfo m_create_info; - size_t m_mem_index; - display &m_display; - uint32_t in_flight_index = UINT32_MAX; -}; - -} /* namespace headless */ -} /* namespace wsi */ diff --git a/alvr/vulkan_layer/wsi/surface_properties.hpp b/alvr/vulkan_layer/wsi/surface_properties.hpp deleted file mode 100644 index 07376ee66c..0000000000 --- a/alvr/vulkan_layer/wsi/surface_properties.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2017-2019, 2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file surface_properties.hpp - * - * @brief Vulkan WSI surface query interfaces. - */ - -#pragma once - -#include -#include - -namespace wsi { - -/** - * @brief The base surface property query interface. - */ -class surface_properties { - public: - /** - * @brief Implementation of vkGetPhysicalDeviceSurfaceCapabilitiesKHR for the specific VkSurface - * type. - */ - virtual VkResult get_surface_capabilities(VkPhysicalDevice physical_device, - VkSurfaceKHR surface, - VkSurfaceCapabilitiesKHR *surface_capabilities) = 0; - - /** - * @brief Implementation of vkGetPhysicalDeviceSurfaceFormatsKHR for the specific VkSurface - * type. - */ - virtual VkResult get_surface_formats(VkPhysicalDevice physical_device, VkSurfaceKHR surface, - uint32_t *surface_format_count, - VkSurfaceFormatKHR *surface_formats) = 0; - - /** - * @brief Implementation of vkGetPhysicalDeviceSurfacePresentModesKHR for the specific VkSurface - * type. - */ - virtual VkResult get_surface_present_modes(VkPhysicalDevice physical_device, - VkSurfaceKHR surface, uint32_t *present_mode_count, - VkPresentModeKHR *present_modes) = 0; - - /** - * @brief Return the device extensions that this surface_properties implementation needs. - */ - virtual const util::extension_list &get_required_device_extensions() { - static const util::extension_list empty{util::allocator::get_generic()}; - return empty; - } -}; - -} /* namespace wsi */ diff --git a/alvr/vulkan_layer/wsi/swapchain_base.cpp b/alvr/vulkan_layer/wsi/swapchain_base.cpp deleted file mode 100644 index 66f8652483..0000000000 --- a/alvr/vulkan_layer/wsi/swapchain_base.cpp +++ /dev/null @@ -1,549 +0,0 @@ -/* - * Copyright (c) 2017-2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file swapchain_base.cpp - * - * @brief Contains the implementation for the swapchain. - * - * This file contains much of the swapchain implementation, - * that is not specific to how images are created or presented. - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include "display.hpp" -#include "swapchain_base.hpp" - -#if VULKAN_WSI_DEBUG > 0 -#define WSI_PRINT_ERROR(...) fprintf(stderr, ##__VA_ARGS__) -#else -#define WSI_PRINT_ERROR(...) (void)0 -#endif - -namespace wsi { - -void swapchain_base::page_flip_thread() { - auto &sc_images = m_swapchain_images; - VkResult vk_res = VK_SUCCESS; - uint64_t timeout = UINT64_MAX; - constexpr uint64_t SEMAPHORE_TIMEOUT = 250000000; /* 250 ms. */ - - /* No mutex is needed for the accesses to m_page_flip_thread_run variable as after the variable - * is initialized it is only ever changed to false. The while loop will make the thread read the - * value repeatedly, and the combination of semaphores and thread joins will force any changes - * to the variable to be visible to this thread. - */ - while (m_page_flip_thread_run) { - /* Waiting for the page_flip_semaphore which will be signalled once there is an - * image to display.*/ - if ((vk_res = m_page_flip_semaphore.wait(SEMAPHORE_TIMEOUT)) == VK_TIMEOUT) { - /* Image is not ready yet. */ - continue; - } - assert(vk_res == VK_SUCCESS); - - /* We want to present the oldest queued for present image from our present queue, - * which we can find at the sc->pending_buffer_pool.head index. */ - uint32_t pending_index = m_pending_buffer_pool.ring[m_pending_buffer_pool.head]; - m_pending_buffer_pool.head = (m_pending_buffer_pool.head + 1) % m_pending_buffer_pool.size; - - submit_image(pending_index); - - /* We wait for the fence of the oldest pending image to be signalled. */ - vk_res = m_device_data.disp.WaitForFences( - m_device, 1, &sc_images[pending_index].present_fence, VK_TRUE, timeout); - if (vk_res != VK_SUCCESS) { - m_is_valid = false; - m_free_image_semaphore.post(); - continue; - } - - /* If the descendant has started presenting the queue_present operation has marked the image - * as FREE so we simply release it and continue. */ - if (sc_images[pending_index].status == swapchain_image::FREE) { - destroy_image(sc_images[pending_index]); - m_free_image_semaphore.post(); - continue; - } - - /* First present of the swapchain. If it has an ancestor, wait until all the pending buffers - * from the ancestor have finished page flipping before we set mode. */ - if (m_first_present) { - if (m_ancestor != VK_NULL_HANDLE) { - auto *ancestor = reinterpret_cast(m_ancestor); - ancestor->wait_for_pending_buffers(); - } - - sem_post(&m_start_present_semaphore); - - present_image(pending_index); - - m_first_present = false; - } - /* The swapchain has already started presenting. */ - else { - present_image(pending_index); - } - } -} - -void swapchain_base::unpresent_image(uint32_t presented_index) { - m_swapchain_images[presented_index].status = swapchain_image::FREE; - - if (m_descendant != VK_NULL_HANDLE) { - destroy_image(m_swapchain_images[presented_index]); - } - - m_free_image_semaphore.post(); -} - -swapchain_base::swapchain_base(layer::device_private_data &dev_data, - const VkAllocationCallbacks *callbacks) - : m_device_data(dev_data), m_page_flip_thread_run(true), m_thread_sem_defined(false), - m_first_present(true), m_pending_buffer_pool{nullptr, 0, 0, 0}, - m_allocator(callbacks, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT), m_swapchain_images(m_allocator), - m_surface(VK_NULL_HANDLE), m_present_mode(VK_PRESENT_MODE_IMMEDIATE_KHR), - m_descendant(VK_NULL_HANDLE), m_ancestor(VK_NULL_HANDLE), m_device(VK_NULL_HANDLE), - m_queue(VK_NULL_HANDLE) {} - -VkResult swapchain_base::init(VkDevice device, - const VkSwapchainCreateInfoKHR *swapchain_create_info) { - assert(device != VK_NULL_HANDLE); - assert(swapchain_create_info != nullptr); - assert(swapchain_create_info->surface != VK_NULL_HANDLE); - - int res; - VkResult result; - - m_device = device; - m_surface = swapchain_create_info->surface; - - /* Check presentMode has a compatible value with swapchain - everything else should be taken - * care at image creation.*/ - static const std::array present_modes = {VK_PRESENT_MODE_FIFO_KHR, - VK_PRESENT_MODE_FIFO_RELAXED_KHR}; - bool present_mode_found = false; - for (uint32_t i = 0; i < present_modes.size() && !present_mode_found; i++) { - if (swapchain_create_info->presentMode == present_modes[i]) { - present_mode_found = true; - } - } - - if (!present_mode_found) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - /* Init image to invalid values. */ - if (!m_swapchain_images.try_resize(swapchain_create_info->minImageCount)) - return VK_ERROR_OUT_OF_HOST_MEMORY; - - /* Initialize ring buffer. */ - m_pending_buffer_pool.ring = m_allocator.create(m_swapchain_images.size(), 0); - if (m_pending_buffer_pool.ring == nullptr) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - m_pending_buffer_pool.head = 0; - m_pending_buffer_pool.tail = 0; - m_pending_buffer_pool.size = m_swapchain_images.size(); - - /* We have allocated images, we can call the platform init function if something needs to be - * done. */ - result = init_platform(device, swapchain_create_info); - if (result != VK_SUCCESS) { - return result; - } - - VkExternalMemoryImageCreateInfo ext_info = {}; - ext_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; - ext_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; - - VkImageCreateInfo image_create_info = {}; - image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_create_info.pNext = &ext_info; - image_create_info.imageType = VK_IMAGE_TYPE_2D; - image_create_info.format = swapchain_create_info->imageFormat; - image_create_info.extent = {swapchain_create_info->imageExtent.width, - swapchain_create_info->imageExtent.height, 1}; - image_create_info.mipLevels = 1; - image_create_info.arrayLayers = swapchain_create_info->imageArrayLayers; - image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_create_info.usage = swapchain_create_info->imageUsage; - image_create_info.flags = VK_IMAGE_CREATE_ALIAS_BIT; - image_create_info.sharingMode = swapchain_create_info->imageSharingMode; - image_create_info.queueFamilyIndexCount = swapchain_create_info->queueFamilyIndexCount; - image_create_info.pQueueFamilyIndices = swapchain_create_info->pQueueFamilyIndices; - image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - result = m_free_image_semaphore.init(m_swapchain_images.size()); - if (result != VK_SUCCESS) { - assert(result == VK_ERROR_OUT_OF_HOST_MEMORY); - return result; - } - - m_device_data.disp.GetDeviceQueue(m_device, 0, 0, &m_queue); - result = m_device_data.SetDeviceLoaderData(m_device, m_queue); - if (VK_SUCCESS != result) { - return result; - } - - for (auto &img : m_swapchain_images) { - result = create_image(image_create_info, img); - if (result != VK_SUCCESS) { - return result; - } - } - - /* Setup semaphore for signaling pageflip thread */ - result = m_page_flip_semaphore.init(0); - if (result != VK_SUCCESS) { - return result; - } - - res = sem_init(&m_start_present_semaphore, 0, 0); - /* Only programming error can cause this to fail. */ - assert(res == 0); - if (res != 0) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - m_thread_sem_defined = true; - - /* Launch page flipping thread */ - m_page_flip_thread = std::thread(&swapchain_base::page_flip_thread, this); - - /* Release the swapchain images of the old swapchain in order - * to free up memory for new swapchain. This is necessary especially - * on platform with limited display memory size. - * - * NB: This must be done last in initialization, when the rest of - * the swapchain is valid. - */ - if (swapchain_create_info->oldSwapchain != VK_NULL_HANDLE) { - /* Set ancestor. */ - m_ancestor = swapchain_create_info->oldSwapchain; - - auto *ancestor = reinterpret_cast(m_ancestor); - ancestor->deprecate(reinterpret_cast(this)); - } - - m_is_valid = true; - - return VK_SUCCESS; -} - -void swapchain_base::teardown() { - /* This method will block until all resources associated with this swapchain - * are released. Images in the ACQUIRED or FREE state can be freed - * immediately. For images in the PRESENTED state, we will block until the - * presentation engine is finished with them. */ - - int res; - bool descendent_started_presenting = false; - - if (m_descendant != VK_NULL_HANDLE) { - auto *desc = reinterpret_cast(m_descendant); - for (auto &img : desc->m_swapchain_images) { - if (img.status == swapchain_image::PRESENTED || - img.status == swapchain_image::PENDING) { - /* Here we wait for the start_present_semaphore, once this semaphore is up, - * the descendant has finished waiting, we don't want to delete vkImages and - * vkFences and semaphores before the waiting is done. */ - sem_wait(&desc->m_start_present_semaphore); - - descendent_started_presenting = true; - break; - } - } - } - - /* If descendant started presenting, there is no pending buffer in the swapchain. */ - if (m_is_valid && descendent_started_presenting == false) { - wait_for_pending_buffers(); - } - - if (m_queue != VK_NULL_HANDLE) { - /* Make sure the vkFences are done signaling. */ - m_device_data.disp.QueueWaitIdle(m_queue); - } - - /* We are safe to destroy everything. */ - if (m_thread_sem_defined) { - /* Tell flip thread to end. */ - m_page_flip_thread_run = false; - - if (m_page_flip_thread.joinable()) { - m_page_flip_thread.join(); - } else { - WSI_PRINT_ERROR("m_page_flip_thread is not joinable"); - } - - res = sem_destroy(&m_start_present_semaphore); - if (res != 0) { - WSI_PRINT_ERROR("sem_destroy failed for start_present_semaphore with %d\n", errno); - } - } - - if (m_descendant != VK_NULL_HANDLE) { - auto *sc = reinterpret_cast(m_descendant); - sc->clear_ancestor(); - } - - if (m_ancestor != VK_NULL_HANDLE) { - auto *sc = reinterpret_cast(m_ancestor); - sc->clear_descendant(); - } - /* Release the images array. */ - for (auto &img : m_swapchain_images) { - /* Call implementation specific release */ - destroy_image(img); - } - - m_allocator.destroy(m_swapchain_images.size(), m_pending_buffer_pool.ring); -} - -VkResult swapchain_base::acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence, - uint32_t *image_index) { - VkResult retval = wait_for_free_buffer(timeout); - if (retval != VK_SUCCESS) { - return retval; - } - - if (!m_is_valid) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - uint32_t i = m_last_acquired_image + 1; - for (uint32_t j = 0; j < m_swapchain_images.size(); ++j) { - i = (i + 1) % m_pending_buffer_pool.size; - if (m_swapchain_images[i].status == swapchain_image::FREE) { - m_swapchain_images[i].status = swapchain_image::ACQUIRED; - *image_index = i; - m_last_acquired_image = i; - break; - } - } - - assert(i < m_swapchain_images.size()); - - if (VK_NULL_HANDLE != semaphore || VK_NULL_HANDLE != fence) { - VkSubmitInfo submit = {}; - submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - if (VK_NULL_HANDLE != semaphore) { - submit.signalSemaphoreCount = 1; - submit.pSignalSemaphores = &semaphore; - } - - submit.commandBufferCount = 0; - submit.pCommandBuffers = nullptr; - retval = m_device_data.disp.QueueSubmit(m_queue, 1, &submit, fence); - assert(retval == VK_SUCCESS); - } - - return retval; -} - -VkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count, - VkImage *swapchain_images) { - if (swapchain_images == nullptr) { - /* Return the number of swapchain images. */ - *swapchain_image_count = m_swapchain_images.size(); - - return VK_SUCCESS; - } else { - assert(m_swapchain_images.size() > 0); - assert(*swapchain_image_count > 0); - - /* Populate array, write actual number of images returned. */ - uint32_t current_image = 0; - - do { - swapchain_images[current_image] = m_swapchain_images[current_image].image; - - current_image++; - - if (current_image == m_swapchain_images.size()) { - *swapchain_image_count = current_image; - - return VK_SUCCESS; - } - - } while (current_image < *swapchain_image_count); - - /* If swapchain_image_count is smaller than the number of presentable images - * in the swapchain, VK_INCOMPLETE must be returned instead of VK_SUCCESS. */ - *swapchain_image_count = current_image; - - return VK_INCOMPLETE; - } -} - -VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, - const uint32_t image_index) { - VkResult result; - bool descendent_started_presenting = false; - - const auto & pose = find_pose_in_call_stack(); - - if (m_descendant != VK_NULL_HANDLE) { - auto *desc = reinterpret_cast(m_descendant); - for (auto &img : desc->m_swapchain_images) { - if (img.status == swapchain_image::PRESENTED || - img.status == swapchain_image::PENDING) { - descendent_started_presenting = true; - break; - } - } - } - - /* When the semaphore that comes in is signalled, we know that all work is done. So, we do not - * want to block any future Vulkan queue work on it. So, we pass in BOTTOM_OF_PIPE bit as the - * wait flag. - */ - VkPipelineStageFlags pipeline_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - - uint64_t signal_value = ++m_swapchain_images[image_index].semaphore_value; - - VkTimelineSemaphoreSubmitInfo timeline_info = {}; - timeline_info.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; - timeline_info.signalSemaphoreValueCount = 1; - timeline_info.pSignalSemaphoreValues = &signal_value; - - VkSubmitInfo submit_info = {VK_STRUCTURE_TYPE_SUBMIT_INFO, - &timeline_info, - present_info->waitSemaphoreCount, - present_info->pWaitSemaphores, - &pipeline_stage_flags, - 0, - NULL, - 1, - &m_swapchain_images[image_index].semaphore}; - - assert(m_swapchain_images[image_index].status == swapchain_image::ACQUIRED); - result = - m_device_data.disp.ResetFences(m_device, 1, &m_swapchain_images[image_index].present_fence); - if (result != VK_SUCCESS) { - return result; - } - - result = m_device_data.disp.QueueSubmit(queue, 1, &submit_info, - m_swapchain_images[image_index].present_fence); - if (result != VK_SUCCESS) { - return result; - } - - /* If the descendant has started presenting, we should release the image - * however we do not want to block inside the main thread so we mark it - * as free and let the page flip thread take care of it. */ - if (descendent_started_presenting) { - m_swapchain_images[image_index].status = swapchain_image::FREE; - - m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index; - m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size; - - m_page_flip_semaphore.post(); - - return VK_ERROR_OUT_OF_DATE_KHR; - } - - m_swapchain_images[image_index].status = swapchain_image::PENDING; - m_swapchain_images[image_index].pose = pose; - - m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index; - m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size; - - m_page_flip_semaphore.post(); - return VK_SUCCESS; -} - -void swapchain_base::deprecate(VkSwapchainKHR descendant) { - for (auto &img : m_swapchain_images) { - if (img.status == swapchain_image::FREE) { - destroy_image(img); - } - } - - /* Set its descendant. */ - m_descendant = descendant; -} - -void swapchain_base::wait_for_pending_buffers() { - int num_acquired_images = 0; - int wait; - - for (auto &img : m_swapchain_images) { - if (img.status == swapchain_image::ACQUIRED) { - ++num_acquired_images; - } - } - - /* Once all the pending buffers are flipped, the swapchain should have images - * in ACQUIRED (application fails to queue them back for presentation), FREE - * and one and only one in PRESENTED. */ - wait = m_swapchain_images.size() - num_acquired_images - 1; - - while (wait > 0) { - /* Take down one free image semaphore. */ - wait_for_free_buffer(UINT64_MAX); - --wait; - } -} - -void swapchain_base::clear_ancestor() { m_ancestor = VK_NULL_HANDLE; } - -void swapchain_base::clear_descendant() { m_descendant = VK_NULL_HANDLE; } - -VkResult swapchain_base::wait_for_free_buffer(uint64_t timeout) { - VkResult retval; - /* first see if a buffer is already marked as free */ - retval = m_free_image_semaphore.wait(0); - if (retval == VK_NOT_READY) { - /* if not, we still have work to do even if timeout==0 - - * the swapchain implementation may be able to get a buffer without - * waiting */ - - retval = get_free_buffer(&timeout); - if (retval == VK_SUCCESS) { - /* the sub-implementation has done it's thing, so re-check the - * semaphore */ - retval = m_free_image_semaphore.wait(timeout); - } - } - - return retval; -} - -#undef WSI_PRINT_ERROR - -} /* namespace wsi */ diff --git a/alvr/vulkan_layer/wsi/swapchain_base.hpp b/alvr/vulkan_layer/wsi/swapchain_base.hpp deleted file mode 100644 index 040b35fb78..0000000000 --- a/alvr/vulkan_layer/wsi/swapchain_base.hpp +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (c) 2017-2020 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file swapchain_base.hpp - * - * @brief Contains the class definition for a base swapchain. - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace wsi { -struct swapchain_image { - enum status { - INVALID, - ACQUIRED, - PENDING, - PRESENTED, - FREE, - }; - - /* Implementation specific data */ - void *data{nullptr}; - - VkImage image{VK_NULL_HANDLE}; - status status{swapchain_image::INVALID}; - - VkFence present_fence{VK_NULL_HANDLE}; - VkSemaphore semaphore{VK_NULL_HANDLE}; - uint64_t semaphore_value = 0; - - TrackedDevicePose_t pose; -}; - -/** - * @brief Base swapchain class - * - * - the swapchain implementations inherit from this class. - * - the VkSwapchain will hold a pointer to this class. - * - much of the swapchain implementation is done by this class, as the only things needed - * in the implementation are how to create a presentable image and how to present an image. - */ -class swapchain_base { - public: - swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *allocator); - - virtual ~swapchain_base() { /* nop */ - } - - /** - * @brief Create swapchain. - * - * Perform all swapchain initialization, create presentable images etc. - */ - VkResult init(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info); - - /** - * @brief Acquires a free image. - * - * Current implementation blocks until a free image is available. - * - * @param timeout Unused since we block until a free image is available. - * - * @param semaphore A semaphore signaled once an image is acquired. - * - * @param fence A fence signaled once an image is acquired. - * - * @param pImageIndex The index of the acquired image. - * - * @return VK_SUCCESS on completion. - */ - VkResult acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence, - uint32_t *image_index); - - /** - * @brief Gets the number of swapchain images or a number of at most - * m_num_swapchain_images images. - * - * @param pSwapchainImageCount Used to return number of images in - * the swapchain if second parameter is nullptr or represents the - * number of images to be returned in second parameter. - * - * @param pSwapchainImage Array of VkImage handles. - * - * @return If number of requested images is less than the number of available - * images in the swapchain returns VK_INCOMPLETE otherwise VK_SUCCESS. - */ - VkResult get_swapchain_images(uint32_t *swapchain_image_count, VkImage *swapchain_image); - - /** - * @brief Submits a present request for the supplied image. - * - * @param queue The queue to which the submission will be made to. - * - * @param pPresentInfo Information about the swapchain and image to be presented. - * - * @param imageIndex The index of the image to be presented. - * - * @return If queue submission fails returns error of vkQueueSubmit, if the - * swapchain has a descendant who started presenting returns VK_ERROR_OUT_OF_DATE_KHR, - * otherwise returns VK_SUCCESS. - */ - VkResult queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, - const uint32_t image_index); - - protected: - layer::device_private_data &m_device_data; - - /** - * @brief Handle to the page flip thread. - */ - std::thread m_page_flip_thread; - - /** - * @brief Whether the page flip thread has to continue running or terminate. - */ - bool m_page_flip_thread_run; - - /** - * @brief In case we encounter threading or drm errors we need a way to - * notify the user of the failure. When this flag is false, acquire_next_image - * will return an error code. - */ - bool m_is_valid; - - struct ring_buffer { - /* Ring buffer to hold the image indexes. */ - uint32_t *ring; - /* Head of the ring. */ - uint32_t head; - /* End of the ring. */ - uint32_t tail; - /* Size of the ring. */ - uint32_t size; - }; - /** - * @brief A semaphore to be signalled once a page flip event occurs. - */ - util::timed_semaphore m_page_flip_semaphore; - - /** - * @brief A semaphore to be signalled once the swapchain has one frame on screen. - */ - sem_t m_start_present_semaphore; - - /** - * @brief Defines if the pthread_t and sem_t members of the class are defined. - * - * As they are opaque types theer's no known invalid value that we ca initialize to, - * and therefore determine if we need to cleanup. - */ - bool m_thread_sem_defined; - - /** - * @brief A flag to track if it is the first present for the chain. - */ - bool m_first_present; - - /** - * @brief In order to present the images in a FIFO order we implement - * a ring buffer to hold the images queued for presentation. Since the - * two pointers (head and tail) are used by different - * threads and we do not allow the application to acquire more images - * than we have we eliminate race conditions. - */ - ring_buffer m_pending_buffer_pool; - - /** - * @brief User provided memory allocation callbacks. - */ - const util::allocator m_allocator; - - /** - * @brief Vector of images in the swapchain. - */ - util::vector m_swapchain_images; - - /** - * @brief Handle to the surface object this swapchain will present images to. - */ - VkSurfaceKHR m_surface; - - /** - * @brief present mode to use for this swapchain - */ - VkPresentModeKHR m_present_mode; - - /** - * @brief Descendant of this swapchain. - * Used to check whether or not a descendant of this swapchain has started - * presenting images to the surface already. If it has, any calls to queuePresent - * for this swapchain will return VK_ERROR_OUT_OF_DATE_KHR. - */ - VkSwapchainKHR m_descendant; - - /** - * @brief Ancestor of this swapchain. - * Used to check whether the ancestor swapchain has completed all of its - * pending page flips (this is required before this swapchain presents for the - * first time. - */ - VkSwapchainKHR m_ancestor; - - /** - * @brief Handle to the logical device the swapchain is created for. - */ - VkDevice m_device; - - /** - * @brief Handle to the queue used for signalling submissions - */ - VkQueue m_queue; - - /** - * @brief Return the VkAllocationCallbacks passed in this object constructor. - */ - const VkAllocationCallbacks *get_allocation_callbacks() { - return m_allocator.get_original_callbacks(); - } - - /** - * @brief Method to wait on all pending buffers to be displayed. - */ - void wait_for_pending_buffers(); - - /** - * @brief Remove cached ancestor. - */ - void clear_ancestor(); - - /** - * @brief Remove cached descendant. - */ - void clear_descendant(); - - /** - * @brief Deprecate this swapchain. - * - * If an application replaces an old swapchain with a new one, the older swapchain - * needs to be deprecated. This method releases all the FREE images and sets the - * descendant of the swapchain. We do not need to care about images in other states - * at this point since they will be released by the page flip thread. - * - * @param descendant Handle to the descendant swapchain. - */ - void deprecate(VkSwapchainKHR descendant); - - /** - * @brief Platform specific initialization - */ - virtual VkResult init_platform(VkDevice device, - const VkSwapchainCreateInfoKHR *swapchain_create_info) = 0; - - /** - * @brief Base swapchain teardown. - * - * Even though the inheritance gives us a nice way to defer display specific allocation - * and presentation outside of the base class, it however robs the children classes - which - * also happen to do some of their state setting - the oppurtunity to do the last clean up - * call, as the base class' destructor is called at the end. This method provides a way to do - * it. The destructor is a virtual function and much of the swapchain teardown happens in this - * method which gets called from the child's destructor. - */ - void teardown(); - - /** - * @brief Creates a new swapchain image. - * - * @param image_create_info Data to be used to create the image. - * - * @param image Handle to the image. - * - * @return If image creation is successful returns VK_SUCCESS, otherwise - * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED - * depending on the error that occured. - */ - virtual VkResult create_image(const VkImageCreateInfo &image_create_info, - swapchain_image &image) = 0; - - - virtual void submit_image(uint32_t pending_index) = 0; - - /** - * @brief Method to present and image - * - * @param pending_index Index of the pending image to be presented. - * - */ - virtual void present_image(uint32_t pending_index) = 0; - - /** - * @brief Transition a presented image to free. - * - * Called by swapchain implementation when a new image has been presented. - * - * @param presented_index Index of the image to be marked as free. - */ - void unpresent_image(uint32_t presented_index); - - /** - * @brief Method to release a swapchain image - * - * @param image Handle to the image about to be released. - */ - virtual void destroy_image(swapchain_image &image){}; - - /** - * @brief Hook for any actions to free up a buffer for acquire - * - * @param[in,out] timeout time to wait, in nanoseconds. 0 doesn't block, - * UINT64_MAX waits indefinately. The timeout should - * be updated if a sleep is required - this can - * be set to 0 if the semaphore is now not expected - * block. - */ - virtual VkResult get_free_buffer(uint64_t *timeout) { return VK_SUCCESS; } - - private: - /** - * @brief Wait for a buffer to become free. - */ - VkResult wait_for_free_buffer(uint64_t timeout); - - /** - * @brief A semaphore to be signalled once a free image becomes available. - * - * Uses a custom semaphore implementation that uses a condition variable. - * it is slower, but has a safe timedwait implementation. - * - * This is kept private as waiting should be done via wait_for_free_buffer(). - */ - util::timed_semaphore m_free_image_semaphore; - - /** - * @brief Per swapchain thread function that handles page flipping. - * - * This thread should be running for the lifetime of the swapchain. - * The thread simply calls the implementation's present_image() method. - * There are 3 main cases we cover here: - * - * 1. On the first present of the swapchain if the swapchain has - * an ancestor we must wait for it to finish presenting. - * 2. The normal use case where we do page flipping, in this - * case change the currently PRESENTED image with the oldest - * PENDING image. - * 3. If the enqueued image is marked as FREE it means the - * descendant of the swapchain has started presenting so we - * should release the image and continue. - * - * The function always waits on the page_flip_semaphore of the - * swapchain. Once it passes that we must wait for the fence of the - * oldest pending image to be signalled, this means that the gpu has - * finished rendering to it and we can present it. From there on the - * logic splits into the above 3 cases and if an image has been - * presented then the old one is marked as FREE and the free_image - * semaphore of the swapchain will be posted. - **/ - void page_flip_thread(); - - uint32_t m_last_acquired_image = 0; - - std::vector m_fences; -}; - -} /* namespace wsi */ diff --git a/alvr/vulkan_layer/wsi/wsi_factory.cpp b/alvr/vulkan_layer/wsi/wsi_factory.cpp deleted file mode 100644 index bc7f04869a..0000000000 --- a/alvr/vulkan_layer/wsi/wsi_factory.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2019-2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file - * @brief Implements factory methods for obtaining the specific surface and swapchain - * implementations. - */ - -#include "wsi_factory.hpp" -#include "headless/surface_properties.hpp" -#include "headless/swapchain.hpp" - -#include -#include -#include -#include -#include -#include - -namespace wsi { - -static struct wsi_extension { - VkExtensionProperties extension; - VkIcdWsiPlatform platform; -} const supported_wsi_extensions[] = { - {{VK_KHR_DISPLAY_EXTENSION_NAME, VK_KHR_DISPLAY_SPEC_VERSION}, VK_ICD_WSI_PLATFORM_HEADLESS}}; - -static surface_properties *get_surface_properties(VkIcdWsiPlatform platform) { - switch (platform) { - case VK_ICD_WSI_PLATFORM_HEADLESS: - return &headless::surface_properties::get_instance(); - default: - return nullptr; - } -} - -surface_properties *get_surface_properties(VkSurfaceKHR) { - return get_surface_properties(VK_ICD_WSI_PLATFORM_HEADLESS); -} - -template -static swapchain_base *allocate_swapchain(layer::device_private_data &dev_data, - const VkAllocationCallbacks *pAllocator) { - if (!pAllocator) { - return new swapchain_type(dev_data, pAllocator); - } - void *memory = - pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(swapchain_type), - alignof(swapchain_type), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); - return new (memory) swapchain_type(dev_data, pAllocator); -} - -swapchain_base *allocate_surface_swapchain(VkSurfaceKHR, - layer::device_private_data &dev_data, - const VkAllocationCallbacks *pAllocator) { - return allocate_swapchain(dev_data, pAllocator); -} - -util::wsi_platform_set find_enabled_layer_platforms(const VkInstanceCreateInfo *pCreateInfo) { - util::wsi_platform_set ret; - for (const auto &ext_provided_by_layer : supported_wsi_extensions) { - for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { - const char *ext_requested_by_user = pCreateInfo->ppEnabledExtensionNames[i]; - if (strcmp(ext_requested_by_user, ext_provided_by_layer.extension.extensionName) == 0) { - ret.add(ext_provided_by_layer.platform); - } - } - } - return ret; -} - -VkResult add_extensions_required_by_layer(VkPhysicalDevice phys_dev, - const util::wsi_platform_set enabled_platforms, - util::extension_list &extensions_to_enable) { - util::allocator allocator{extensions_to_enable.get_allocator(), - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND}; - util::extension_list device_extensions{allocator}; - VkResult res = device_extensions.add(phys_dev); - if (res != VK_SUCCESS) { - return res; - } - - for (const auto &wsi_ext : supported_wsi_extensions) { - /* Skip iterating over platforms not enabled in the instance. */ - if (!enabled_platforms.contains(wsi_ext.platform)) { - continue; - } - - surface_properties *props = get_surface_properties(wsi_ext.platform); - const auto &extensions_required_by_layer = props->get_required_device_extensions(); - bool supported = device_extensions.contains(extensions_required_by_layer); - if (!supported) { - /* Can we accept failure? The layer unconditionally advertises support for this platform - * and the loader uses this information to enable its own support of the - * vkCreate*SurfaceKHR entrypoints. The rest of the Vulkan stack may not support this - * extension so we cannot blindly fall back to it. For now treat this as an error. - */ - return VK_ERROR_INITIALIZATION_FAILED; - } - - res = extensions_to_enable.add(extensions_required_by_layer); - if (res != VK_SUCCESS) { - return res; - } - } - return VK_SUCCESS; -} - -void destroy_surface_swapchain(swapchain_base *swapchain, const VkAllocationCallbacks *pAllocator) { - assert(swapchain); - - if (!pAllocator) { - delete swapchain; - } else { - swapchain->~swapchain_base(); - pAllocator->pfnFree(pAllocator->pUserData, reinterpret_cast(swapchain)); - } -} - -} // namespace wsi diff --git a/alvr/vulkan_layer/wsi/wsi_factory.hpp b/alvr/vulkan_layer/wsi/wsi_factory.hpp deleted file mode 100644 index 943bb25b56..0000000000 --- a/alvr/vulkan_layer/wsi/wsi_factory.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2019, 2021 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @file - * @brief Contains the factory methods for obtaining the specific surface and swapchain - * implementations. - */ - -#pragma once - -#include "surface_properties.hpp" -#include "swapchain_base.hpp" -#include "util/platform_set.hpp" - -#include - -namespace wsi { - -/** - * @brief Obtains the surface properties for the specific surface type. - * - * @param surface The surface for which to get the properties. - * - * @return nullptr if surface type is unsupported. - */ -surface_properties *get_surface_properties(VkSurfaceKHR surface); - -/** - * @brief Allocates a surface specific swapchain. - * - * @param surface The surface for which a swapchain is allocated. - * @param dev_data The device specific data. - * @param pAllocator The allocator from which to allocate any memory. - * - * @return nullptr on failure. - */ -swapchain_base *allocate_surface_swapchain(VkSurfaceKHR surface, - layer::device_private_data &dev_data, - const VkAllocationCallbacks *pAllocator); - -/** - * @brief Destroys a swapchain and frees memory. Used with @ref allocate_surface_swapchain. - * - * @param swapchain Pointer to the swapchain to destroy. - * @param pAllocator The allocator to use for freeing memory. - */ -void destroy_surface_swapchain(swapchain_base *swapchain, const VkAllocationCallbacks *pAllocator); - -/** - * @brief Return which platforms the layer can handle for an instance constructed in the specified - * way. - * - * @details This function looks at the extensions specified in @p pCreateInfo and based on this - * returns a list of platforms that the layer can support. For example, if the @c - * pCreateInfo.ppEnabledExtensionNames contains the string "VK_EXT_headless_surface" then the - * returned platform set will contain @c VK_ICD_WSI_PLATFORM_HEADLESS. - * - * @param pCreateInfo Structure used when creating the instance in vkCreateInstance(). - * - * @return A list of WS platforms supported by the layer. - */ -util::wsi_platform_set find_enabled_layer_platforms(const VkInstanceCreateInfo *pCreateInfo); - -/** - * @brief Add extra extensions that the layer requires to support the specified list of enabled - * platforms. - * - * @details Check whether @p phys_dev has support for the extensions required by the layer in order - * to support the platforms it implements. The extensions that the layer requires to operate are - * added to @p extensions_to_enable. - * - * @param[in] phys_dev The physical device to check. - * @param[in] enabled_platforms All the platforms that the layer must enable for @p phys_dev. - * @param[in,out] extensions_to_enable All the extensions required by the layer are added to this - * list. - * - * @retval @c VK_SUCCESS if the operation was successful. - */ -VkResult add_extensions_required_by_layer(VkPhysicalDevice phys_dev, - const util::wsi_platform_set enabled_platforms, - util::extension_list &extensions_to_enable); - -} // namespace wsi diff --git a/alvr/xtask/src/build.rs b/alvr/xtask/src/build.rs index 8b2ef9e147..5f2f3665f7 100644 --- a/alvr/xtask/src/build.rs +++ b/alvr/xtask/src/build.rs @@ -209,41 +209,6 @@ pub fn build_streamer( } } } else if cfg!(target_os = "linux") { - // build compositor wrapper - let _push_guard = sh.push_dir(afs::crate_dir("vrcompositor_wrapper")); - cmd!(sh, "cargo build {common_flags_ref...}").run().unwrap(); - sh.create_dir(&build_layout.vrcompositor_wrapper_dir) - .unwrap(); - sh.copy_file( - artifacts_dir.join("alvr_vrcompositor_wrapper"), - build_layout.vrcompositor_wrapper(), - ) - .unwrap(); - sh.copy_file( - artifacts_dir.join("alvr_drm_lease_shim.so"), - build_layout.drm_lease_shim(), - ) - .unwrap(); - - // build vulkan layer - let _push_guard = sh.push_dir(afs::crate_dir("vulkan_layer")); - cmd!(sh, "cargo build {common_flags_ref...}").run().unwrap(); - sh.create_dir(&build_layout.libraries_dir).unwrap(); - sh.copy_file( - artifacts_dir.join(afs::dynlib_fname("alvr_vulkan_layer")), - build_layout.vulkan_layer(), - ) - .unwrap(); - - // copy vulkan layer manifest - sh.create_dir(&build_layout.vulkan_layer_manifest_dir) - .unwrap(); - sh.copy_file( - afs::crate_dir("vulkan_layer").join("layer/alvr_x86_64.json"), - build_layout.vulkan_layer_manifest(), - ) - .unwrap(); - sh.copy_file( afs::workspace_dir().join("openvr/bin/linux64/libopenvr_api.so"), build_layout.openvr_driver_lib_dir(), @@ -255,6 +220,7 @@ pub fn build_streamer( let ufw = afs::crate_dir("xtask").join("firewall/ufw-alvr"); // copy linux specific firewalls + sh.create_dir(build_layout.firewall_script()).unwrap(); sh.copy_file(firewall_script, build_layout.firewall_script()) .unwrap(); sh.copy_file(firewalld, build_layout.firewalld_config()) diff --git a/openvr b/openvr index 4c85abcb7f..ebd4253312 160000 --- a/openvr +++ b/openvr @@ -1 +1 @@ -Subproject commit 4c85abcb7f7f1f02adaf3812018c99fc593bc341 +Subproject commit ebd425331229365dc3ec42d1bb8b2cc3c2332f81