From c710287143734ab6f708dbdf54fe9a46134ae402 Mon Sep 17 00:00:00 2001
From: Jens Koenen <koenen@vr.rwth-aachen.de>
Date: Thu, 27 Oct 2022 17:34:53 +0200
Subject: [PATCH] Started allocating the input images for the NvEnc encoder.

---
 src/encoder/encoder.hpp          |   4 +-
 src/encoder/nvidia_encoder.cpp   | 406 ++++++++++++++++++++++++++++++-
 src/encoder/nvidia_encoder.hpp   |  27 +-
 src/encoder/vulkan_encoder.cpp   |   2 +-
 src/encoder/vulkan_encoder.hpp   |   2 +-
 src/headset/emulated_headset.cpp |   2 +-
 src/headset/remote_headset.cpp   |   2 +-
 src/vr_application.cpp           |   9 +
 8 files changed, 432 insertions(+), 22 deletions(-)

diff --git a/src/encoder/encoder.hpp b/src/encoder/encoder.hpp
index a766aed1..91b8db81 100644
--- a/src/encoder/encoder.hpp
+++ b/src/encoder/encoder.hpp
@@ -13,7 +13,7 @@
    setup_device_for_encoder(...);
 
    //During creation and submission of a frame
-   Encoder::Ptr encoder = make_encoder();
+   Encoder::Ptr encoder = make_encoder(...);
 
    encoder->create(...);
 
@@ -59,7 +59,7 @@ public:
     virtual ~Encoder() = default;
 
     //NOTE: The parameter input_buffers defines how many input images the encoder should provide.
-    virtual bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t input_buffers) = 0;
+    virtual bool create(lava::instance& instance, lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t input_buffers) = 0;
     virtual void destroy() = 0;
 
     //NOTE: The following functions should be thread safe.
diff --git a/src/encoder/nvidia_encoder.cpp b/src/encoder/nvidia_encoder.cpp
index 45abd910..99e13714 100644
--- a/src/encoder/nvidia_encoder.cpp
+++ b/src/encoder/nvidia_encoder.cpp
@@ -1,22 +1,38 @@
 #include "nvidia_encoder.hpp"
 
-#include <cuda.h>
-
-#if defined(__unix__)
+#if defined(_WIN32)
+    #include <windows.h>
+    #include <vulkan/vulkan_win32.h>
+#elif defined(__unix__)
     #include <dlfcn.h>
+#else
+    #error "Not implemented for this platform!"
 #endif
 
 typedef NVENCSTATUS(NVENCAPI* NvEncodeAPICreateInstance_Type)(NV_ENCODE_API_FUNCTION_LIST*);
 
-bool NvidiaEncoder::create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t input_buffers)
+bool NvidiaEncoder::create(lava::instance& instance, lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t input_buffers)
 {
-    cuInit(0);
+    if (!this->create_context(device))
+    {
+        return false;
+    }
 
     if (!this->load_library())
     {
         return false;
     }
 
+    if (!this->create_session())
+    {
+        return false;
+    }
+
+    if (!this->create_input_buffers(instance, device, size, input_buffers))
+    {
+        return false;
+    }
+
     return false;
 }
 
@@ -85,21 +101,88 @@ uint32_t NvidiaEncoder::get_frame_rate() const
     return this->frame_rate;
 }
 
+bool NvidiaEncoder::create_context(lava::device_ptr device)
+{
+    VkPhysicalDeviceIDProperties physical_device_id;
+    physical_device_id.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
+    physical_device_id.pNext = nullptr;
+
+    VkPhysicalDeviceProperties2 physical_device_properties;
+    physical_device_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+    physical_device_properties.pNext = &physical_device_id;
+
+    vkGetPhysicalDeviceProperties2(device->get_physical_device()->get(), &physical_device_properties);
+
+    int32_t device_count = 0;
+
+    if (cuDeviceGetCount(&device_count) != CUDA_SUCCESS)
+    {
+        lava::log()->error("Can't get cuda device count!");
+
+        return false;
+    }
+
+    for (uint32_t index = 0; index < device_count; index++)
+    {
+        CUdevice device_handle;
+        CUuuid device_id;
+
+        if (cuDeviceGet(&device_handle, index) != CUDA_SUCCESS)
+        {
+            lava::log()->error("Can't get cuda device handle!");
+
+            return false;
+        }
+
+        if (cuDeviceGetUuid(&device_id, device_handle) != CUDA_SUCCESS)
+        {
+            lava::log()->error("Can't get cuda device identifier!");
+
+            return false;
+        }
+
+        if (memcmp(device_id.bytes, physical_device_id.deviceUUID, sizeof(device_id.bytes)) != 0)
+        {
+            continue;
+        }
+
+        this->cuda_device = device_handle;
+
+        if(cuCtxCreate(&this->cuda_context, 0, this->cuda_device) != CUDA_SUCCESS)
+        {
+            lava::log()->error("Can't create cuda context!");
+
+            return false;
+        }
+
+        return true;
+    }
+
+    lava::log()->error("Can't find matching cuda device!");
+
+    return false;
+}
+
+void NvidiaEncoder::destroy_context()
+{
+
+}
+
 bool NvidiaEncoder::load_library()
 {
 #if defined(_WIN32)
 #if defined(_WIN64)
-    this->library_handle = LoadLibrary("nvEncodeAPI64.dll");
+    this->nvenc_library = LoadLibrary("nvEncodeAPI64.dll");
 #else
-    this->library_handle = LoadLibrary("nvEncodeAPI.dll");
+    this->nvenc_library = LoadLibrary("nvEncodeAPI.dll");
 #endif
 #elif defined(__unix__)
-    this->library_handle = dlopen("libnvidia-encode.so.1", RTLD_LAZY);
+    this->nvenc_library = dlopen("libnvidia-encode.so.1", RTLD_LAZY);
 #else 
     #error "Not implemented for this platform!"
 #endif
 
-    if (this->library_handle == nullptr)
+    if (this->nvenc_library == nullptr)
     {
         lava::log()->error("Can't load library!");
 
@@ -109,9 +192,9 @@ bool NvidiaEncoder::load_library()
     NvEncodeAPICreateInstance_Type NvEncodeAPICreateInstance = nullptr;
 
 #if defined(_WIN32)
-    NvEncodeAPICreateInstance = (NvEncodeAPICreateInstance_Type)GetProcAddress((HMODULE) this->library_handle, "NvEncodeAPICreateInstance");
+    NvEncodeAPICreateInstance = (NvEncodeAPICreateInstance_Type)GetProcAddress((HMODULE)this->nvenc_library, "NvEncodeAPICreateInstance");
 #elif defined(__unix__)
-    NvEncodeAPICreateInstance = (NvEncodeAPICreateInstance_Type)dlsym((HMODULE) this->library_handle, "NvEncodeAPICreateInstance");
+    NvEncodeAPICreateInstance = (NvEncodeAPICreateInstance_Type)dlsym((HMODULE)this->nvenc_library, "NvEncodeAPICreateInstance");
 #else
     #error "Not implemented for this platform!"
 #endif
@@ -123,9 +206,9 @@ bool NvidiaEncoder::load_library()
         return false;
     }
 
-    this->function_list.version = NV_ENCODE_API_FUNCTION_LIST_VER;
+    this->nvenc_functions.version = NV_ENCODE_API_FUNCTION_LIST_VER;
 
-    if (NvEncodeAPICreateInstance(&this->function_list) != NV_ENC_SUCCESS)
+    if (NvEncodeAPICreateInstance(&this->nvenc_functions) != NV_ENC_SUCCESS)
     {
         lava::log()->error("Can't create function list!");
 
@@ -140,12 +223,309 @@ void NvidiaEncoder::unload_library()
 
 }
 
+bool NvidiaEncoder::create_session()
+{
+    NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_parameters;
+    session_parameters.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
+    session_parameters.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
+    session_parameters.device = (void*)this->cuda_context;
+    session_parameters.reserved = 0;
+    session_parameters.apiVersion = NVENCAPI_VERSION;
+    memset(session_parameters.reserved1, 0, sizeof(session_parameters.reserved1));
+    memset(session_parameters.reserved2, 0, sizeof(session_parameters.reserved2));
+
+    if (this->nvenc_functions.nvEncOpenEncodeSessionEx(&session_parameters, &this->nvenc_session) != NV_ENC_SUCCESS)
+    {
+        lava::log()->error("Can't create nvenc session!");
+
+        return false;
+    }
+
+    if (!this->check_encode_support(NV_ENC_CODEC_H264_GUID))
+    {
+        lava::log()->error("Codec not supported!");
+
+        return false;
+    }
+
+
+    return true;
+
+
+    if (!this->check_profile_support(NV_ENC_H264_PROFILE_HIGH_GUID))
+    {
+        lava::log()->error("Profile not supported!");
+
+        return false;
+    }
+
+    if (!this->check_preset_support(NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID))
+    {
+        lava::log()->error("Preset not supported!");
+
+        return false;
+    }
+
+    NV_ENC_INITIALIZE_PARAMS init_parameters;
+    /*init_parameters.version = NV_ENC_INITIALIZE_PARAMS_VER; /**< [in]: Struct version. Must be set to ::NV_ENC_INITIALIZE_PARAMS_VER. */
+    /*init_parameters.encodeGUID; /**< [in]: Specifies the Encode GUID for which the encoder is being created. ::NvEncInitializeEncoder() API will fail if this is not set, or set to unsupported value. */
+    /*init_parameters.presetGUID; /**< [in]: Specifies the preset for encoding. If the preset GUID is set then , the preset configuration will be applied before any other parameter. */
+    /*init_parameters.encodeWidth; /**< [in]: Specifies the encode width. If not set ::NvEncInitializeEncoder() API will fail. */
+    /*init_parameters.encodeHeight; /**< [in]: Specifies the encode height. If not set ::NvEncInitializeEncoder() API will fail. */
+    /*init_parameters.darWidth; /**< [in]: Specifies the display aspect ratio Width. */
+    /*init_parameters.darHeight; /**< [in]: Specifies the display aspect ratio height. */
+    /*init_parameters.frameRateNum; /**< [in]: Specifies the numerator for frame rate used for encoding in frames per second ( Frame rate = frameRateNum / frameRateDen ). */
+    /*init_parameters.frameRateDen; /**< [in]: Specifies the denominator for frame rate used for encoding in frames per second ( Frame rate = frameRateNum / frameRateDen ). */
+    /*init_parameters.enableEncodeAsync; /**< [in]: Set this to 1 to enable asynchronous mode and is expected to use events to get picture completion notification. */
+    /*init_parameters.enablePTD; /**< [in]: Set this to 1 to enable the Picture Type Decision is be taken by the NvEncodeAPI interface. */
+    /*init_parameters.reportSliceOffsets : 1; /**< [in]: Set this to 1 to enable reporting slice offsets in ::_NV_ENC_LOCK_BITSTREAM. NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync must be set to 0 to use this feature. Client must set this to 0 if NV_ENC_CONFIG_H264::sliceMode is 1 on Kepler GPUs */
+    /*init_parameters.enableSubFrameWrite : 1; /**< [in]: Set this to 1 to write out available bitstream to memory at subframe intervals.
+                                                                                           If enableSubFrameWrite = 1, then the hardware encoder returns data as soon as a slice has completed encoding.
+                                                                                           This results in better encoding latency, but the downside is that the application has to keep polling via a call to nvEncLockBitstream API continuously to see if any encoded slice data is available.
+                                                                                           Use this mode if you feel that the marginal reduction in latency from sub-frame encoding is worth the increase in complexity due to CPU-based polling. */
+    /*init_parameters.enableExternalMEHints : 1; /**< [in]: Set to 1 to enable external ME hints for the current frame. For NV_ENC_INITIALIZE_PARAMS::enablePTD=1 with B frames, programming L1 hints is optional for B frames since Client doesn't know internal GOP structure.
+                                                                                           NV_ENC_PIC_PARAMS::meHintRefPicDist should preferably be set with enablePTD=1. */
+    /*init_parameters.enableMEOnlyMode : 1; /**< [in]: Set to 1 to enable ME Only Mode .*/
+    /*init_parameters.enableWeightedPrediction : 1; /**< [in]: Set this to 1 to enable weighted prediction. Not supported if encode session is configured for B-Frames (i.e. NV_ENC_CONFIG::frameIntervalP > 1 or preset >=P3 when tuningInfo = ::NV_ENC_TUNING_INFO_HIGH_QUALITY or
+                                                                                           tuningInfo = ::NV_ENC_TUNING_INFO_LOSSLESS. This is because preset >=p3 internally enables B frames when tuningInfo = ::NV_ENC_TUNING_INFO_HIGH_QUALITY or ::NV_ENC_TUNING_INFO_LOSSLESS). */
+    /*init_parameters.enableOutputInVidmem : 1; /**< [in]: Set this to 1 to enable output of NVENC in video memory buffer created by application. This feature is not supported for HEVC ME only mode. */
+    /*init_parameters.reservedBitFields : 26; /**< [in]: Reserved bitfields and must be set to 0 */
+    /*init_parameters.privDataSize; /**< [in]: Reserved private data buffer size and must be set to 0 */
+    /*init_parameters.privData; /**< [in]: Reserved private data buffer and must be set to NULL */
+    /*init_parameters.encodeConfig; /**< [in]: Specifies the advanced codec specific structure. If client has sent a valid codec config structure, it will override parameters set by the NV_ENC_INITIALIZE_PARAMS::presetGUID parameter. If set to NULL the NvEncodeAPI interface will use the NV_ENC_INITIALIZE_PARAMS::presetGUID to set the codec specific parameters.
+                                                                                           Client can also optionally query the NvEncodeAPI interface to get codec specific parameters for a presetGUID using ::NvEncGetEncodePresetConfig() API. It can then modify (if required) some of the codec config parameters and send down a custom config structure as part of ::_NV_ENC_INITIALIZE_PARAMS.
+                                                                                           Even in this case client is recommended to pass the same preset guid it has used in ::NvEncGetEncodePresetConfig() API to query the config structure; as NV_ENC_INITIALIZE_PARAMS::presetGUID. This will not override the custom config structure but will be used to determine other Encoder HW specific parameters not exposed in the API. */
+    /*init_parameters.maxEncodeWidth; /**< [in]: Maximum encode width to be used for current Encode session.
+                                                                                           Client should allocate output buffers according to this dimension for dynamic resolution change. If set to 0, Encoder will not allow dynamic resolution change. */
+    /*init_parameters.maxEncodeHeight; /**< [in]: Maximum encode height to be allowed for current Encode session.
+                                                                                           Client should allocate output buffers according to this dimension for dynamic resolution change. If set to 0, Encode will not allow dynamic resolution change. */
+    /*init_parameters.maxMEHintCountsPerBlock[2]; /**< [in]: If Client wants to pass external motion vectors in NV_ENC_PIC_PARAMS::meExternalHints buffer it must specify the maximum number of hint candidates per block per direction for the encode session.
+                                                                                           The NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[0] is for L0 predictors and NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[1] is for L1 predictors.
+                                                                                           This client must also set NV_ENC_INITIALIZE_PARAMS::enableExternalMEHints to 1. */
+    /*init_parameters.tuningInfo; /**< [in]: Tuning Info of NVENC encoding(TuningInfo is not applicable to H264 and HEVC meonly mode). */
+    /*init_parameters.bufferFormat; /**< [in]: Specifies input buffer format. Client should set input buffer format only when D3D12 interface type is used. */
+    /*init_parameters.reserved[287]; /**< [in]: Reserved and must be set to 0 */
+    /*init_parameters.reserved2[64]; /**< [in]: Reserved and must be set to NULL */
+
+    if (this->nvenc_functions.nvEncInitializeEncoder(&this->nvenc_session, &init_parameters) != NV_ENC_SUCCESS)
+    {
+        lava::log()->error("Can't init nvenc session!");
+
+        return false;
+    }
+
+    return true;
+}
+
+void NvidiaEncoder::destroy_session()
+{
+
+}
+
+bool NvidiaEncoder::create_input_buffers(lava::instance& instance, lava::device_ptr device, const glm::uvec2& size, uint32_t input_buffers)
+{
+    PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR_Func = (PFN_vkGetMemoryWin32HandleKHR) vkGetInstanceProcAddr(instance.get(), "vkGetMemoryWin32HandleKHR");
+
+    if (vkGetMemoryWin32HandleKHR_Func == nullptr)
+    {
+        return false;
+    }
+
+    for (uint32_t index = 0; index < input_buffers; index++)
+    {
+        VkExternalMemoryImageCreateInfo export_image_info;
+        export_image_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
+        export_image_info.pNext = nullptr;
+        export_image_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
+
+        VkImageCreateInfo image_info;
+        image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+        image_info.pNext = &export_image_info;
+        image_info.flags = 0;
+        image_info.imageType = VK_IMAGE_TYPE_2D;
+        image_info.format = VK_FORMAT_R8G8B8A8_SRGB;
+        image_info.extent.width = size.x;
+        image_info.extent.height = size.y;
+        image_info.extent.depth = 1;
+        image_info.mipLevels = 1;
+        image_info.arrayLayers = 1;
+        image_info.samples = VK_SAMPLE_COUNT_1_BIT;
+        image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+        image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+        image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+        image_info.queueFamilyIndexCount = 0;
+        image_info.pQueueFamilyIndices = nullptr;
+        image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+        VkImage image = VK_NULL_HANDLE;
+
+        if (vkCreateImage(device->get(), &image_info, nullptr, &image) != VK_SUCCESS)
+        {
+            lava::log()->error("Can't create input image!");
+
+            return false;
+        }
+
+        VkMemoryRequirements memory_requirements;
+        vkGetImageMemoryRequirements(device->get(), image, &memory_requirements);
+
+        VkPhysicalDeviceMemoryProperties memory_properties;
+        vkGetPhysicalDeviceMemoryProperties(device->get_physical_device()->get(), &memory_properties);
+
+        bool memory_found = false;
+        uint32_t memory_index = 0;
+        VkMemoryPropertyFlags memory_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+
+        for (uint32_t index = 0; index < memory_properties.memoryTypeCount; index++)
+        {
+            if ((memory_requirements.memoryTypeBits & (1 << index)) == 0)
+            {
+                continue;   
+            }
+
+            if ((memory_properties.memoryTypes[index].propertyFlags & memory_flags) == 0)
+            {
+                continue;
+            }
+
+            memory_found = true;
+            memory_index = index;
+
+            break;
+        }
+
+        if (!memory_found)
+        {
+            return false;
+        }
+
+        VkExportMemoryAllocateInfo export_info;
+        export_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO;
+        export_info.pNext = nullptr;
+        export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
+
+        VkMemoryAllocateInfo allocation_info;
+        allocation_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+        allocation_info.pNext = &export_info;
+        allocation_info.allocationSize = memory_requirements.size;
+        allocation_info.memoryTypeIndex = memory_index;
+
+        VkDeviceMemory device_memory = VK_NULL_HANDLE;
+
+        if (vkAllocateMemory(device->get(), &allocation_info, nullptr, &device_memory) != VK_SUCCESS)
+        {
+            return false;
+        }
+
+        if (vkBindImageMemory(device->get(), image, device_memory, 0) != VK_SUCCESS)
+        {
+            return false;
+        }
+
+        VkMemoryGetWin32HandleInfoKHR memory_info;
+        memory_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
+        memory_info.pNext = nullptr;
+        memory_info.memory = device_memory;
+        memory_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
+
+        HANDLE memory_handle;
+
+        if (vkGetMemoryWin32HandleKHR_Func(device->get(), &memory_info, &memory_handle) != VK_SUCCESS)
+        {
+            return false; 
+        }
+
+        CUDA_EXTERNAL_MEMORY_HANDLE_DESC external_memory_description;
+        external_memory_description.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32;
+        external_memory_description.handle.win32.handle = memory_handle;
+        external_memory_description.handle.win32.name = nullptr;
+        external_memory_description.size = memory_requirements.size;
+        external_memory_description.flags = 0;
+        memset(external_memory_description.reserved, 0, sizeof(external_memory_description.reserved));
+
+        CUexternalMemory external_memory;
+
+        if (cuImportExternalMemory(&external_memory, &external_memory_description) != CUDA_SUCCESS)
+        {
+            return false;
+        }
+
+        //cuExternalMemoryGetMappedMipmappedArray();
+
+        this->nvenc_functions.nvEncRegisterResource()
+
+        //TODO:!!!!!!!
+    }
+
+    return true;
+}
+
+void NvidiaEncoder::destroy_input_buffers()
+{
+
+}
+
+bool NvidiaEncoder::check_encode_support(GUID identifier) const
+{
+    uint32_t guid_count = 0;
+
+    if (this->nvenc_functions.nvEncGetEncodeGUIDCount(this->nvenc_session, &guid_count) != NV_ENC_SUCCESS)
+    {
+        return false;
+    }
+
+    std::vector<GUID> guid_list;
+    guid_list.resize(guid_count);
+
+    if(this->nvenc_functions.nvEncGetEncodeGUIDs(this->nvenc_session, guid_list.data(), guid_count, &guid_count) != NV_ENC_SUCCESS)
+    {
+        return false;
+    }
+
+    for (const GUID& guid : guid_list)
+    {
+        if (memcmp(&guid, &identifier, sizeof(guid)) == 0)
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool NvidiaEncoder::check_profile_support(GUID identifier) const
+{
+
+    return false;
+}
+
+bool NvidiaEncoder::check_preset_support(GUID identifier) const
+{
+
+    return false;
+}
+
+bool NvidiaEncoder::check_format_support(NV_ENC_BUFFER_FORMAT format) const
+{
+
+    return false;
+}
+
 bool setup_instance_for_nvidia_encoder(lava::frame_config& config)
 {
+    if(cuInit(0) != CUDA_SUCCESS)
+    {
+        lava::log()->error("Can't init cuda!");
+
+        return false;
+    }
+
     return true;
 }
 
 bool setup_device_for_nvidia_encoder(lava::device::create_param& parameters)
 {
+    parameters.extensions.push_back(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
+
     return true;
 }
\ No newline at end of file
diff --git a/src/encoder/nvidia_encoder.hpp b/src/encoder/nvidia_encoder.hpp
index 278a52cc..a6fd078b 100644
--- a/src/encoder/nvidia_encoder.hpp
+++ b/src/encoder/nvidia_encoder.hpp
@@ -6,6 +6,7 @@
 #include <memory>
 #include <cstdint>
 
+#include <cuda.h>
 #include <nvEncodeAPI.h>
 
 #include "encoder.hpp"
@@ -18,7 +19,7 @@ public:
 public:
     NvidiaEncoder() = default;
 
-    bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t input_buffers);
+    bool create(lava::instance& instance, lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t input_buffers);
     void destroy();
 
     bool encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function);
@@ -38,9 +39,23 @@ public:
     uint32_t get_frame_rate() const;
 
 private:
+    bool create_context(lava::device_ptr device);
+    void destroy_context();
+
     bool load_library();
     void unload_library();
 
+    bool create_session();
+    void destroy_session();
+
+    bool create_input_buffers(lava::instance& instance, lava::device_ptr device, const glm::uvec2& size, uint32_t input_buffers);
+    void destroy_input_buffers();
+
+    bool check_encode_support(GUID identifier) const;
+    bool check_profile_support(GUID identifier) const;
+    bool check_preset_support(GUID identifier) const;
+    bool check_format_support(NV_ENC_BUFFER_FORMAT format) const;
+
 private:
     OnEncodeError on_encoder_error;
 
@@ -51,8 +66,14 @@ private:
     uint32_t frame_rate = 90;
 
 private:
-    void* library_handle = nullptr;
-    NV_ENCODE_API_FUNCTION_LIST function_list;
+    CUdevice cuda_device;
+    CUcontext cuda_context;
+
+    void* nvenc_library = nullptr;
+    void* nvenc_session = nullptr;
+    NV_ENCODE_API_FUNCTION_LIST nvenc_functions;
+
+    std::vector<VkImage> input_buffers;
 };
 
 bool setup_instance_for_nvidia_encoder(lava::frame_config& config);
diff --git a/src/encoder/vulkan_encoder.cpp b/src/encoder/vulkan_encoder.cpp
index b11d8b5e..cde326d0 100644
--- a/src/encoder/vulkan_encoder.cpp
+++ b/src/encoder/vulkan_encoder.cpp
@@ -2,7 +2,7 @@
 #include <array>
 #include <vk_video/vulkan_video_codecs_common.h>
 
-bool VulkanEncoder::create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t frame_count)
+bool VulkanEncoder::create(lava::instance& instance, lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t frame_count)
 {
     //Get the default graphics queue of lava for querey ownership transiations
     this->default_queue = renderer.get_queue();
diff --git a/src/encoder/vulkan_encoder.hpp b/src/encoder/vulkan_encoder.hpp
index 78a18994..8311ee3c 100644
--- a/src/encoder/vulkan_encoder.hpp
+++ b/src/encoder/vulkan_encoder.hpp
@@ -77,7 +77,7 @@ public:
 public:
     VulkanEncoder() = default;
 
-    bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t input_buffers);
+    bool create(lava::instance& instance, lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t input_buffers);
     void destroy();
 
     bool encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function);
diff --git a/src/headset/emulated_headset.cpp b/src/headset/emulated_headset.cpp
index 61fd3c7e..ff651832 100644
--- a/src/headset/emulated_headset.cpp
+++ b/src/headset/emulated_headset.cpp
@@ -11,7 +11,7 @@ bool EmulatedHeadset::on_create()
 {
     //DEBUG!!
     Encoder::Ptr encoder = make_encoder(ENCODER_TYPE_NVIDIA);
-    encoder->create(this->get_application()->get_device(), this->get_application()->get_renderer(), glm::uvec2(1920, 1080), 4);
+    encoder->create(this->get_application()->get_instance(), this->get_application()->get_device(), this->get_application()->get_renderer(), glm::uvec2(1920, 1080), 4);
 
 
     lava::camera& camera = this->get_application()->get_camera();
diff --git a/src/headset/remote_headset.cpp b/src/headset/remote_headset.cpp
index 22bda66a..69d56eec 100644
--- a/src/headset/remote_headset.cpp
+++ b/src/headset/remote_headset.cpp
@@ -456,7 +456,7 @@ bool RemoteHeadset::create_encoders()
         lava::renderer& renderer = this->get_application()->get_renderer();
         uint32_t frame_count = this->get_application()->get_frame_count();
 
-        if (!encoder->create(device, renderer, this->resolution, frame_count))
+        if (!encoder->create(this->get_application()->get_instance(), device, renderer, this->resolution, frame_count))
         {
             return false;
         }
diff --git a/src/vr_application.cpp b/src/vr_application.cpp
index e4f7dec5..6d69a72d 100644
--- a/src/vr_application.cpp
+++ b/src/vr_application.cpp
@@ -3,6 +3,9 @@
 #include <iostream>
 #include <imgui.h>
 
+//DEBUG!!!!!!!!!!!!!!!!
+#include "encoder/encoder.hpp"
+
 bool VRApplication::setup(lava::name name, argh::parser cmd_line)
 {
     if (!this->command_parser.parse_command(cmd_line))
@@ -24,6 +27,9 @@ bool VRApplication::setup(lava::name name, argh::parser cmd_line)
     config.log.debug = true;
     config.debug.validation = false;
     
+    //DEBUG!!!!!!!!!!!!!!!!
+    setup_instance_for_encoder(ENCODER_TYPE_NVIDIA, config);
+
     if (!this->headset->on_setup_instance(config))
     {
         std::cout << "Error during headset setup instance!" << std::endl;
@@ -46,6 +52,9 @@ bool VRApplication::setup(lava::name name, argh::parser cmd_line)
    
 	this->app->manager.on_create_param = [this](lava::device::create_param& parameters)
 	{
+        //DEBUG!!!!!!!!!!!!!!!!
+        setup_device_for_encoder(ENCODER_TYPE_NVIDIA, parameters);
+
         if (!this->headset->on_setup_device(parameters))
         {
             lava::log()->error("Error during headset setup device!");
-- 
GitLab