From 9f9b97fd1b434f807aee3c4048f1aa50b504f6f1 Mon Sep 17 00:00:00 2001 From: Jens Koenen <koenen@vr.rwth-aachen.de> Date: Wed, 9 Nov 2022 15:49:16 +0100 Subject: [PATCH] The Nvidia Encoder now frees all resources when destroyed --- res/dpr/utility/encode_color_conversion.frag | 2 - src/encoder/encoder.hpp | 9 +- src/encoder/nvidia_encoder.cpp | 216 ++++++++++++++----- src/encoder/nvidia_encoder.hpp | 13 +- src/encoder/vulkan_encoder.cpp | 24 +-- src/encoder/vulkan_encoder.hpp | 4 +- src/headset/emulated_headset.cpp | 24 --- src/headset/remote_headset.cpp | 70 +++--- src/vr_application.cpp | 11 +- 9 files changed, 242 insertions(+), 131 deletions(-) diff --git a/res/dpr/utility/encode_color_conversion.frag b/res/dpr/utility/encode_color_conversion.frag index 9125d168..2c707fba 100644 --- a/res/dpr/utility/encode_color_conversion.frag +++ b/res/dpr/utility/encode_color_conversion.frag @@ -19,9 +19,7 @@ void main() float u_max = 0.436; float v_max = 0.615; - int height = textureSize(samplerInput, 0).y; ivec2 coord = ivec2(gl_FragCoord.xy); - coord.y = height - coord.y; vec3 color_rgb_linear = texelFetch(samplerInput, coord, 0).xyz; vec3 color_rgb_gamma = pow(color_rgb_linear, vec3(1.0 / gamma)); //Slight change in brighness when compared to emulated headset. Maybe due to different gamma compression when using sRGB. diff --git a/src/encoder/encoder.hpp b/src/encoder/encoder.hpp index 2968eaad..a97e2b61 100644 --- a/src/encoder/encoder.hpp +++ b/src/encoder/encoder.hpp @@ -59,6 +59,13 @@ enum EncoderSetting ENCODER_SETTING_FRAME_RATE }; +enum EncoderResult +{ + ENCODER_RESULT_SUCCESS, + ENCODER_RESULT_FRAME_DROPPED, + ENCODER_RESULT_ERROR +}; + // When adding or removing an encoder, the functions make_encoder(...), setup_instance_for_encoder(...), setup_device_for_encoder(...) as well as // the function set_encoder(...) of the CommandParser need to be adapted accordingly. enum EncoderType @@ -86,7 +93,7 @@ public: // The following functions should be thread safe. // The callback function is executed only by the worker thread of the encoder. // The format of the image needs to be the same as the format that was specified during the creation of the encoder. - virtual bool encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function) = 0; + virtual EncoderResult encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function) = 0; // The callbacks are executed only by the worker thread of the encoder. // Set the callbacks before calling create and don't do it again after calling create. diff --git a/src/encoder/nvidia_encoder.cpp b/src/encoder/nvidia_encoder.cpp index 5f90d854..adb483df 100644 --- a/src/encoder/nvidia_encoder.cpp +++ b/src/encoder/nvidia_encoder.cpp @@ -29,6 +29,8 @@ NvidiaEncoder::NvidiaEncoder() : worker_pool(1) bool NvidiaEncoder::create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) { + this->device = device; + if (!this->create_context(device)) { return false; @@ -86,13 +88,13 @@ void NvidiaEncoder::destroy() this->destroy_context(); } -bool NvidiaEncoder::encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function) +EncoderResult NvidiaEncoder::encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function) { NvidiaEncoderFrame::Ptr frame; if (!this->aquire_frame(frame)) { - return false; + return ENCODER_RESULT_FRAME_DROPPED; } frame->output_parameter = this->parameter_change; @@ -100,10 +102,10 @@ bool NvidiaEncoder::encode(VkCommandBuffer command_buffer, lava::renderer& rende if (this->config_change) { - /*if (!this->apply_config()) + if (!this->apply_config()) { - return false; - }*/ + return ENCODER_RESULT_ERROR; + } } this->parameter_change = false; @@ -198,7 +200,7 @@ bool NvidiaEncoder::encode(VkCommandBuffer command_buffer, lava::renderer& rende this->submit_frame(frame, renderer); - return false; + return ENCODER_RESULT_SUCCESS; } void NvidiaEncoder::set_on_encode_error(OnEncodeError function) @@ -294,6 +296,7 @@ void NvidiaEncoder::submit_frame(NvidiaEncoderFrame::Ptr frame, lava::renderer& { if (error_code) { + lava::log()->error("Nvidia Encoder: Can't wait for encoding to finished!"); this->on_encode_error(); } @@ -334,6 +337,7 @@ void NvidiaEncoder::read_frame(NvidiaEncoderFrame::Ptr frame) if (nvenc_functions.nvEncGetSequenceParams(this->nvenc_session, ¶meter_info) != NV_ENC_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't get sequence and pixture parameter sets!"); this->on_encode_error(); return; @@ -356,6 +360,7 @@ void NvidiaEncoder::read_frame(NvidiaEncoderFrame::Ptr frame) if (nvenc_functions.nvEncLockBitstream(this->nvenc_session, &lock_stream) != NV_ENC_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't lock bitsteam!"); this->on_encode_error(); return; @@ -365,6 +370,7 @@ void NvidiaEncoder::read_frame(NvidiaEncoderFrame::Ptr frame) if (nvenc_functions.nvEncUnlockBitstream(this->nvenc_session, frame->nvenc_output_buffer) != NV_ENC_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't unlock bistream!"); this->on_encode_error(); return; @@ -372,40 +378,56 @@ void NvidiaEncoder::read_frame(NvidiaEncoderFrame::Ptr frame) if (nvenc_functions.nvEncUnmapInputResource(this->nvenc_session, frame->nvenc_mapped_buffer) != NV_ENC_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't unmap input resource!"); this->on_encode_error(); return; } + + frame->nvenc_mapped_buffer = nullptr; } bool NvidiaEncoder::apply_config() { + NV_ENC_INITIALIZE_PARAMS session_config = this->nvenc_session_config; + NV_ENC_CONFIG encode_config = this->nvenc_encode_config; + session_config.encodeConfig = &encode_config; + if (this->mode == ENCODER_MODE_CONSTANT_QUALITY) { - + encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; + encode_config.rcParams.constQP.qpIntra = (uint32_t)((1.0 - this->quality) * 51); + encode_config.rcParams.constQP.qpInterP = (uint32_t)((1.0 - this->quality) * 51); + encode_config.rcParams.constQP.qpInterB = (uint32_t)((1.0 - this->quality) * 51); } else if (this->mode == ENCODER_MODE_CONSTANT_BITRATE) { - + encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; + encode_config.rcParams.averageBitRate = (uint32_t)(this->bitrate * 1000000.0); + encode_config.rcParams.maxBitRate = this->nvenc_encode_config.rcParams.averageBitRate; + encode_config.rcParams.vbvBufferSize = (uint32_t)(this->nvenc_encode_config.rcParams.averageBitRate * (1.0f / this->frame_rate)) * 5; + encode_config.rcParams.vbvInitialDelay = this->nvenc_encode_config.rcParams.vbvBufferSize; } else { - lava::log()->error("Unsupported encoder mode!"); + lava::log()->error("Nvidia Encoder: Unsupported encoder mode!"); return false; } NV_ENC_RECONFIGURE_PARAMS reconfig; reconfig.version = NV_ENC_RECONFIGURE_PARAMS_VER; - reconfig.reInitEncodeParams = this->nvenc_session_config; + reconfig.reInitEncodeParams = session_config; reconfig.resetEncoder = 1; reconfig.forceIDR = 1; reconfig.reserved = 0; if (nvenc_functions.nvEncReconfigureEncoder(this->nvenc_session, &reconfig) != NV_ENC_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't apply config change!"); + return false; } @@ -416,6 +438,7 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) { if (cuCtxPushCurrent(this->cuda_context) != CUDA_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't push cuda context!"); this->on_encode_error(); return; @@ -428,6 +451,7 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) if (cuWaitExternalSemaphoresAsync(&frame->cuda_external_semaphore, &wait_parameters, 1, 0) != CUDA_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't wait for semaphore to be signaled!"); this->on_encode_error(); return; @@ -445,6 +469,7 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) if (nvenc_functions.nvEncMapInputResource(this->nvenc_session, &map_info) != NV_ENC_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't map input resources!"); this->on_encode_error(); return; @@ -454,6 +479,7 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) if (cuCtxPopCurrent(&this->cuda_context) != CUDA_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't pop cuda context!"); this->on_encode_error(); return; @@ -487,7 +513,7 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) codec_parameters.seiPayloadArrayCnt = 0; codec_parameters.seiPayloadArray = nullptr; codec_parameters.sliceMode = 3; - codec_parameters.sliceModeData = 4; + codec_parameters.sliceModeData = 10; codec_parameters.ltrMarkFrameIdx = 0; codec_parameters.ltrUseFrameBitmap = 0; codec_parameters.ltrUsageMode = 0; @@ -527,6 +553,7 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) if (nvenc_functions.nvEncEncodePicture(this->nvenc_session, &encode_parameters) != NV_ENC_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't encoder image!"); this->on_encode_error(); return; @@ -549,7 +576,7 @@ bool NvidiaEncoder::create_context(lava::device_ptr device) if (cuDeviceGetCount(&device_count) != CUDA_SUCCESS) { - lava::log()->error("Can't get cuda device count!"); + lava::log()->error("Nvidia Encoder: Can't get cuda device count!"); return false; } @@ -561,14 +588,14 @@ bool NvidiaEncoder::create_context(lava::device_ptr device) if (cuDeviceGet(&device_handle, index) != CUDA_SUCCESS) { - lava::log()->error("Can't get cuda device handle!"); + lava::log()->error("Nvidia Encoder: 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!"); + lava::log()->error("Nvidia Encoder: Can't get cuda device identifier!"); return false; } @@ -582,7 +609,7 @@ bool NvidiaEncoder::create_context(lava::device_ptr device) if(cuCtxCreate(&this->cuda_context, 0, this->cuda_device) != CUDA_SUCCESS) { - lava::log()->error("Can't create cuda context!"); + lava::log()->error("Nvidia Encoder: Can't create cuda context!"); return false; } @@ -590,14 +617,19 @@ bool NvidiaEncoder::create_context(lava::device_ptr device) return true; } - lava::log()->error("Can't find matching cuda device!"); + lava::log()->error("Nvidia Encoder: Can't find matching cuda device!"); return false; } void NvidiaEncoder::destroy_context() { - + if (this->cuda_context != nullptr) + { + cuCtxDestroy(this->cuda_context); + this->cuda_device = 0; + this->cuda_context = nullptr; + } } bool NvidiaEncoder::create_session(const glm::uvec2& size) @@ -613,35 +645,35 @@ bool NvidiaEncoder::create_session(const glm::uvec2& size) if (nvenc_functions.nvEncOpenEncodeSessionEx(&session_parameters, &this->nvenc_session) != NV_ENC_SUCCESS) { - lava::log()->error("Can't create nvenc session!"); + lava::log()->error("Nvidia Encoder: Can't create nvenc session!"); return false; } if (!this->check_encode_support(NV_ENC_CODEC_H264_GUID)) { - lava::log()->error("Codec not supported!"); + lava::log()->error("Nvidia Encoder: Codec not supported!"); return false; } if (!this->check_profile_support(NV_ENC_CODEC_H264_GUID, NV_ENC_H264_PROFILE_HIGH_GUID)) { - lava::log()->error("Profile not supported!"); + lava::log()->error("Nvidia Encoder: Profile not supported!"); return false; } if (!this->check_preset_support(NV_ENC_CODEC_H264_GUID, NV_ENC_PRESET_P1_GUID)) { - lava::log()->error("Preset not supported!"); + lava::log()->error("Nvidia Encoder: Preset not supported!"); return false; } if (!this->check_format_support(NV_ENC_CODEC_H264_GUID, NV_ENC_BUFFER_FORMAT_ABGR)) { - lava::log()->error("Input format not supported!"); + lava::log()->error("Nvidia Encoder: Input format not supported!"); return false; } @@ -656,17 +688,22 @@ bool NvidiaEncoder::create_session(const glm::uvec2& size) if (nvenc_functions.nvEncGetEncodePresetConfigEx(this->nvenc_session, NV_ENC_CODEC_H264_GUID, NV_ENC_PRESET_P1_GUID, NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY, &preset_config) != NV_ENC_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't get preset for config!"); + return false; } this->nvenc_encode_config = preset_config.presetCfg; this->nvenc_encode_config.profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID; this->nvenc_encode_config.rcParams.version = NV_ENC_RC_PARAMS_VER; - this->nvenc_encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; - this->nvenc_encode_config.rcParams.averageBitRate = (uint32_t)(this->bitrate * 1000000.0); - this->nvenc_encode_config.rcParams.maxBitRate = this->nvenc_encode_config.rcParams.averageBitRate; - this->nvenc_encode_config.rcParams.vbvBufferSize = (uint32_t)(this->nvenc_encode_config.rcParams.averageBitRate * (1.0f / this->frame_rate)) * 5; - this->nvenc_encode_config.rcParams.vbvInitialDelay = this->nvenc_encode_config.rcParams.vbvBufferSize; + this->nvenc_encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; + + this->nvenc_encode_config.encodeCodecConfig.h264Config.disableSPSPPS = 1; + this->nvenc_encode_config.encodeCodecConfig.h264Config.enableIntraRefresh = 1; + this->nvenc_encode_config.encodeCodecConfig.h264Config.intraRefreshPeriod = this->frame_rate * 2; + this->nvenc_encode_config.encodeCodecConfig.h264Config.intraRefreshCnt = 10; + this->nvenc_encode_config.rcParams.enableAQ = 1; + this->nvenc_encode_config.rcParams.aqStrength = 0; this->nvenc_session_config.version = NV_ENC_INITIALIZE_PARAMS_VER; this->nvenc_session_config.encodeGUID = NV_ENC_CODEC_H264_GUID; @@ -699,7 +736,7 @@ bool NvidiaEncoder::create_session(const glm::uvec2& size) if (nvenc_functions.nvEncInitializeEncoder(this->nvenc_session, &this->nvenc_session_config) != NV_ENC_SUCCESS) { - lava::log()->error("Can't init nvenc session!"); + lava::log()->error("Nvidia Encoder: Can't init nvenc session!"); return false; } @@ -709,7 +746,11 @@ bool NvidiaEncoder::create_session(const glm::uvec2& size) void NvidiaEncoder::destroy_session() { - + if (this->nvenc_session != nullptr) + { + nvenc_functions.nvEncDestroyEncoder(this->nvenc_session); + this->nvenc_session = nullptr; + } } bool NvidiaEncoder::create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::device_ptr device, const glm::uvec2& size, EncoderFormat format) @@ -740,7 +781,7 @@ bool NvidiaEncoder::create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::dev if (vkCreateImage(device->get(), &image_info, lava::memory::alloc(), &frame->image) != VK_SUCCESS) { - lava::log()->error("Can't create input image!"); + lava::log()->error("Nvidia Encoder: Can't create input image!"); return false; } @@ -775,6 +816,8 @@ bool NvidiaEncoder::create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::dev if (!memory_found) { + lava::log()->error("Nvidia Encoder: Can't find matching memory heap for input image!"); + return false; } @@ -791,11 +834,15 @@ bool NvidiaEncoder::create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::dev if (vkAllocateMemory(device->get(), &allocation_info, lava::memory::alloc(), &frame->device_memory) != VK_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't allocate memory for input image!"); + return false; } if (vkBindImageMemory(device->get(), frame->image, frame->device_memory, 0) != VK_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't bind memory to input image!"); + return false; } @@ -807,6 +854,8 @@ bool NvidiaEncoder::create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::dev if (vkGetMemoryWin32HandleKHR_Func(device->get(), &memory_info, (HANDLE*)&frame->memory_handle) != VK_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't get handle to input image!"); + return false; } @@ -820,6 +869,8 @@ bool NvidiaEncoder::create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::dev if (cuImportExternalMemory(&frame->cuda_external_memory, &external_memory_description) != CUDA_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't convert memory of input image to cuda external memory object!"); + return false; } @@ -838,6 +889,8 @@ bool NvidiaEncoder::create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::dev if (cuExternalMemoryGetMappedBuffer(&frame->cuda_buffer, frame->cuda_external_memory, &buffer_description) != CUDA_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't create buffer from external memory of input image!"); + return false; } @@ -852,7 +905,7 @@ bool NvidiaEncoder::create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::dev buffer_format = NV_ENC_BUFFER_FORMAT_ARGB; break; default: - lava::log()->error("Unkown format!"); + lava::log()->error("Nvidia Encoder: Unkown input image format!"); return false; } @@ -873,6 +926,8 @@ bool NvidiaEncoder::create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::dev if(nvenc_functions.nvEncRegisterResource(this->nvenc_session, ®ister_info) != NV_ENC_SUCCESS) { + lava::log()->error("Nvidia Encoder: Can't register input image!"); + return false; } @@ -896,7 +951,7 @@ bool NvidiaEncoder::create_output_buffer(NvidiaEncoderFrame::Ptr frame) if (nvenc_functions.nvEncCreateBitstreamBuffer(this->nvenc_session, &create_info) != NV_ENC_SUCCESS) { - lava::log()->error("Can't create output bistream buffer!"); + lava::log()->error("Nvidia Encoder: Can't create output bistream buffer!"); return false; } @@ -924,7 +979,7 @@ bool NvidiaEncoder::create_async_event(NvidiaEncoderFrame::Ptr frame) if (nvenc_functions.nvEncRegisterAsyncEvent(this->nvenc_session, &event_info) != NV_ENC_SUCCESS) { - lava::log()->error("Can't register async event!"); + lava::log()->error("Nvidia Encoder: Can't register async event!"); return false; } @@ -946,7 +1001,7 @@ bool NvidiaEncoder::create_semaphore(NvidiaEncoderFrame::Ptr frame, lava::device if (vkCreateSemaphore(device->get(), &semaphore_info, lava::memory::alloc(), &frame->semaphore) != VK_SUCCESS) { - lava::log()->error("Can't create semaphore!"); + lava::log()->error("Nvidia Encoder: Can't create semaphore!"); return false; } @@ -959,7 +1014,7 @@ bool NvidiaEncoder::create_semaphore(NvidiaEncoderFrame::Ptr frame, lava::device if (vkGetSemaphoreWin32HandleKHR_Func(device->get(), &export_info, (HANDLE*)&frame->semaphore_handle) != VK_SUCCESS) { - lava::log()->error("Can't get handle of semaphore!"); + lava::log()->error("Nvidia Encoder: Can't get handle of semaphore!"); return false; } @@ -971,7 +1026,7 @@ bool NvidiaEncoder::create_semaphore(NvidiaEncoderFrame::Ptr frame, lava::device if (cuImportExternalSemaphore(&frame->cuda_external_semaphore, &semaphore_description) != CUDA_SUCCESS) { - lava::log()->error("Can't import semaphore!"); + lava::log()->error("Nvidia Encoder: Can't import semaphore!"); return false; } @@ -981,22 +1036,85 @@ bool NvidiaEncoder::create_semaphore(NvidiaEncoderFrame::Ptr frame, lava::device void NvidiaEncoder::destroy_frame(NvidiaEncoderFrame::Ptr frame) { + if (frame->nvenc_mapped_buffer != nullptr) + { + nvenc_functions.nvEncUnmapInputResource(this->nvenc_session, frame->nvenc_mapped_buffer); + frame->nvenc_mapped_buffer = nullptr; + } + if (frame->nvenc_input_buffer != nullptr) + { + nvenc_functions.nvEncUnregisterResource(this->nvenc_session, frame->nvenc_input_buffer); + frame->nvenc_input_buffer = nullptr; + } + if (frame->nvenc_output_buffer != nullptr) + { + nvenc_functions.nvEncDestroyBitstreamBuffer(this->nvenc_session, frame->nvenc_output_buffer); + frame->nvenc_output_buffer = nullptr; + } + if (frame->event_handle != nullptr) + { + NV_ENC_EVENT_PARAMS event_info; + event_info.version = NV_ENC_EVENT_PARAMS_VER; + event_info.reserved = 0; + event_info.completionEvent = frame->event_handle; + memset(event_info.reserved1, 0, sizeof(event_info.reserved1)); + memset(event_info.reserved2, 0, sizeof(event_info.reserved2)); + nvenc_functions.nvEncUnregisterAsyncEvent(this->nvenc_session, &event_info); + frame->event_handle = nullptr; + frame->async_event.reset(); + } + if (frame->cuda_buffer != 0) + { + cuMemFree(frame->cuda_buffer); + frame->cuda_buffer = 0; + } + if (frame->cuda_external_memory != nullptr) + { + cuDestroyExternalMemory(frame->cuda_external_memory); + frame->cuda_external_memory = nullptr; + } + if (frame->cuda_external_semaphore != nullptr) + { + cuDestroyExternalSemaphore(frame->cuda_external_semaphore); + frame->cuda_external_semaphore = nullptr; + } + if (frame->memory_handle != nullptr) + { + CloseHandle(frame->memory_handle); + frame->memory_handle = nullptr; + } + if (frame->semaphore_handle != nullptr) + { + CloseHandle(frame->semaphore_handle); + frame->semaphore_handle = nullptr; + } + if (frame->image != VK_NULL_HANDLE) + { + vkDestroyImage(this->device->get(), frame->image, lava::memory::alloc()); + frame->image = VK_NULL_HANDLE; + } + if (frame->device_memory != VK_NULL_HANDLE) + { + vkFreeMemory(this->device->get(), frame->device_memory, lava::memory::alloc()); + frame->device_memory = VK_NULL_HANDLE; + } - - - - + if (frame->semaphore != VK_NULL_HANDLE) + { + vkDestroySemaphore(this->device->get(), frame->semaphore, lava::memory::alloc()); + frame->semaphore = VK_NULL_HANDLE; + } } bool NvidiaEncoder::check_encode_support(GUID required_guid) const @@ -1117,7 +1235,7 @@ bool load_functions(lava::instance& instance) if (vkGetMemoryWin32HandleKHR_Func == nullptr) { - lava::log()->error("Can't get function pointer for 'vkGetMemoryWin32HandleKHR'"); + lava::log()->error("Nvidia Encoder: Can't get function pointer for 'vkGetMemoryWin32HandleKHR'"); return false; } @@ -1126,7 +1244,7 @@ bool load_functions(lava::instance& instance) if (vkGetSemaphoreWin32HandleKHR_Func == nullptr) { - lava::log()->error("Can't get function pointer for 'vkGetSemaphoreWin32HandleKHR'"); + lava::log()->error("Nvidia Encoder: Can't get function pointer for 'vkGetSemaphoreWin32HandleKHR'"); return false; } @@ -1150,7 +1268,7 @@ bool load_library() if (nvenc_library == nullptr) { - lava::log()->error("Can't load library!"); + lava::log()->error("Nvidia Encoder: Can't load library!"); return false; } @@ -1167,14 +1285,14 @@ bool load_library() if (NvEncodeAPIGetMaxSupportedVersion_Func == nullptr) { - lava::log()->error("Can't get function pointer for 'NvEncodeAPIGetMaxSupportedVersion'"); + lava::log()->error("Nvidia Encoder: Can't get function pointer for 'NvEncodeAPIGetMaxSupportedVersion'"); return false; } if (NvEncodeAPICreateInstance_Func == nullptr) { - lava::log()->error("Can't get function pointer for 'NvEncodeAPICreateInstance' !"); + lava::log()->error("Nvidia Encoder: Can't get function pointer for 'NvEncodeAPICreateInstance' !"); return false; } @@ -1184,14 +1302,14 @@ bool load_library() if (NvEncodeAPIGetMaxSupportedVersion_Func(&max_version) != NV_ENC_SUCCESS) { - lava::log()->error("Can't get max API version!"); + lava::log()->error("Nvidia Encoder: Can't get max API version!"); return false; } if (max_version < current_version) { - lava::log()->error("Mismatch between header version and driver version!"); + lava::log()->error("Nvidia Encoder: Mismatch between header version and driver version!"); return false; } @@ -1200,7 +1318,7 @@ bool load_library() if (NvEncodeAPICreateInstance_Func(&nvenc_functions) != NV_ENC_SUCCESS) { - lava::log()->error("Can't create function list!"); + lava::log()->error("Nvidia Encoder: Can't create function list!"); return false; } @@ -1228,7 +1346,7 @@ bool setup_instance_for_nvidia_encoder(lava::frame_config& config) { if(cuInit(0) != CUDA_SUCCESS) { - std::cout << "Can't init cuda!" << std::endl; + std::cout << "Nvidia Encoder: Can't init cuda!" << std::endl; return false; } diff --git a/src/encoder/nvidia_encoder.hpp b/src/encoder/nvidia_encoder.hpp index 796c6e07..78060a72 100644 --- a/src/encoder/nvidia_encoder.hpp +++ b/src/encoder/nvidia_encoder.hpp @@ -36,9 +36,9 @@ public: void* event_handle = nullptr; std::optional<EventType> async_event; - CUexternalMemory cuda_external_memory; - CUexternalSemaphore cuda_external_semaphore; - CUdeviceptr cuda_buffer; + CUexternalMemory cuda_external_memory = nullptr; + CUexternalSemaphore cuda_external_semaphore = nullptr; + CUdeviceptr cuda_buffer = 0; void* nvenc_input_buffer = nullptr; void* nvenc_output_buffer = nullptr; @@ -60,7 +60,7 @@ public: bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format); void destroy(); - bool encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function); + EncoderResult encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function); void set_on_encode_error(OnEncodeError function); @@ -116,8 +116,9 @@ private: private: asio::thread_pool worker_pool; - CUdevice cuda_device; - CUcontext cuda_context; + lava::device_ptr device; + CUdevice cuda_device = 0; + CUcontext cuda_context = nullptr; void* nvenc_session = nullptr; NV_ENC_INITIALIZE_PARAMS nvenc_session_config; diff --git a/src/encoder/vulkan_encoder.cpp b/src/encoder/vulkan_encoder.cpp index 578b2a31..48a17447 100644 --- a/src/encoder/vulkan_encoder.cpp +++ b/src/encoder/vulkan_encoder.cpp @@ -9,6 +9,9 @@ VulkanEncoder::VulkanEncoder() : worker_pool(1) bool VulkanEncoder::create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) { + this->frame_count = VULKAN_ENCODER_FRAMES; + this->frame_index = 0; + //Get the default graphics queue of lava for querey ownership transiations this->default_queue = renderer.get_queue(); @@ -88,7 +91,7 @@ bool VulkanEncoder::create(lava::device_ptr device, const lava::renderer& render } //Create pools such as the command buffer pool for the encode command buffers and the queue pool for the queues that are needed in order to get the number of bytes written to the output buffer. - if (!this->create_pools(device, frame_count)) + if (!this->create_pools(device, this->frame_count)) { return false; } @@ -100,7 +103,7 @@ bool VulkanEncoder::create(lava::device_ptr device, const lava::renderer& render } //Create a pool of frames. Each frame is used for a single input image that needs to be encoded. - for (uint32_t index = 0; index < frame_count; index++) + for (uint32_t index = 0; index < this->frame_count; index++) { VulkanEncoderFrame::Ptr frame = std::make_shared<VulkanEncoderFrame>(); frame->output_query_index = index; @@ -160,9 +163,6 @@ bool VulkanEncoder::create(lava::device_ptr device, const lava::renderer& render this->setup_slots(); - this->frame_count = frame_count; - this->frame_index = 0; - return true; } @@ -182,16 +182,16 @@ void VulkanEncoder::destroy() this->destroy_session(device); } -bool VulkanEncoder::encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete callback) +EncoderResult VulkanEncoder::encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete callback) { if (!this->encode_control(renderer, callback)) { - return false; + return ENCODER_RESULT_ERROR; } if (!this->encode_config(renderer, callback)) { - return false; + return ENCODER_RESULT_ERROR; } lava::device_ptr device = image->get_device(); @@ -200,7 +200,7 @@ bool VulkanEncoder::encode(VkCommandBuffer command_buffer, lava::renderer& rende //Get an unused image form the frame pool if (!this->aquire_frame(frame)) { - return false; + return ENCODER_RESULT_FRAME_DROPPED; } frame->frame_index = this->frame_index; @@ -284,18 +284,18 @@ bool VulkanEncoder::encode(VkCommandBuffer command_buffer, lava::renderer& rende //Create the encode command buffer. if (!this->record_encoding(device, frame)) { - return false; + return ENCODER_RESULT_ERROR; } //Submit the encode command buffer at the end of the frame and async wait for the completion of the frame. if (!this->submit_frame(device, frame, renderer)) { - return false; + return ENCODER_RESULT_ERROR; } this->frame_index++; - return true; + return ENCODER_RESULT_SUCCESS; } void VulkanEncoder::set_on_encode_error(OnEncodeError function) diff --git a/src/encoder/vulkan_encoder.hpp b/src/encoder/vulkan_encoder.hpp index 1684571b..12e9fb10 100644 --- a/src/encoder/vulkan_encoder.hpp +++ b/src/encoder/vulkan_encoder.hpp @@ -12,7 +12,7 @@ #include "encoder.hpp" #include "utility/extern_fence.hpp" -#define VULKAN_ENCODER_FRAMES 10 +#define VULKAN_ENCODER_FRAMES 4 enum VulkanEncoderFrameType { @@ -82,7 +82,7 @@ public: bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format); void destroy(); - bool encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function); + EncoderResult encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function); void set_on_encode_error(OnEncodeError function); diff --git a/src/headset/emulated_headset.cpp b/src/headset/emulated_headset.cpp index 0df35a1d..3bd74ecd 100644 --- a/src/headset/emulated_headset.cpp +++ b/src/headset/emulated_headset.cpp @@ -4,12 +4,6 @@ #include <imgui.h> #include <glm/gtx/matrix_operation.hpp> -//DEBUG!!! -#include <fstream> -#include "encoder/encoder.hpp" -Encoder::Ptr encoder = nullptr; -std::fstream file; - bool EmulatedHeadset::on_create() { lava::camera& camera = this->get_application()->get_camera(); @@ -30,21 +24,12 @@ bool EmulatedHeadset::on_create() return false; } - //DEBUG!! - encoder = make_encoder(ENCODER_TYPE_NVIDIA); - encoder->create(this->get_application()->get_device(), this->get_application()->get_renderer(), this->resolution, (EncoderFormat)this->get_format()); - - file.open("video.h264", std::ios::out | std::ios::binary); - return true; } void EmulatedHeadset::on_destroy() { this->destroy_framebuffer(); - - //DEBUG: - file.close(); } bool EmulatedHeadset::on_interface() @@ -89,15 +74,6 @@ void EmulatedHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout lava::image::ptr window_image = this->get_application()->get_target()->get_backbuffer(this->get_application()->get_frame_index()); lava::image::ptr frame_image = this->get_framebuffer(frame_id); - //DEBUG!! - if (frame_id == 0) - { - encoder->encode(command_buffer, this->get_application()->get_renderer(), frame_image, frame_layout, [this](const std::span<uint8_t>& content, bool is_config) - { - file.write((const char*)content.data(), content.size()); - }); - } - std::vector<VkImageMemoryBarrier> image_barriers; VkImageMemoryBarrier window_barrier = lava::image_memory_barrier(window_image->get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); diff --git a/src/headset/remote_headset.cpp b/src/headset/remote_headset.cpp index f7614bd5..80c78e7b 100644 --- a/src/headset/remote_headset.cpp +++ b/src/headset/remote_headset.cpp @@ -113,54 +113,71 @@ bool RemoteHeadset::on_interface() ImGui::Separator(); - std::vector<const char*> encoder_mode_names; - encoder_mode_names.push_back("Constant Bitrate"); - encoder_mode_names.push_back("Constant Quality"); + Encoder::Ptr encoder = this->encoders.front(); - if (ImGui::Combo("Encoder Mode", (int32_t*)&this->encoder_mode, encoder_mode_names.data(), encoder_mode_names.size())) + if (encoder->is_supported(ENCODER_SETTING_MODE_CONSTANT_BITRATE) && encoder->is_supported(ENCODER_SETTING_MODE_CONSTANT_QUALITY)) { - for (Encoder::Ptr encoder : this->encoders) + std::vector<const char*> encoder_mode_names; + encoder_mode_names.push_back("Constant Bitrate"); + encoder_mode_names.push_back("Constant Quality"); + + if (ImGui::Combo("Encoder Mode", (int32_t*)&this->encoder_mode, encoder_mode_names.data(), encoder_mode_names.size())) { - encoder->set_mode((EncoderMode)this->encoder_mode); + for (Encoder::Ptr encoder : this->encoders) + { + encoder->set_mode((EncoderMode)this->encoder_mode); + } } } ImGui::SliderInt("Input-Rate (Fps)", (int32_t*) &this->encoder_input_rate, 1, 180); - if (ImGui::SliderInt("Key-Rate (Frames)", (int32_t*)&this->encoder_key_rate, 1, 180)) + if (encoder->is_supported(ENCODER_SETTING_KEY_RATE)) { - for (Encoder::Ptr encoder : this->encoders) + if (ImGui::SliderInt("Key-Rate (Frames)", (int32_t*) &this->encoder_key_rate, 1, 180)) { - encoder->set_key_rate(this->encoder_key_rate); + for (Encoder::Ptr encoder : this->encoders) + { + encoder->set_key_rate(this->encoder_key_rate); + } } } if (this->encoder_mode == ENCODER_MODE_CONSTANT_BITRATE) { - if (ImGui::SliderFloat("Bit-Rate (Mbps)", &this->encoder_bit_rate, 0.1, 10.0)) + if (encoder->is_supported(ENCODER_SETTING_BITRATE)) { - for (Encoder::Ptr encoder : this->encoders) + if (ImGui::SliderFloat("Bit-Rate (Mbps)", &this->encoder_bit_rate, 0.1, 10.0)) { - encoder->set_bitrate(this->encoder_bit_rate * 1000); - } + for (Encoder::Ptr encoder : this->encoders) + { + encoder->set_bitrate(this->encoder_bit_rate); + } + } } - - if (ImGui::SliderInt("Frame-Rate (Fps)", (int32_t*)&this->encoder_frame_rate, 1, 180)) + + if (encoder->is_supported(ENCODER_SETTING_FRAME_RATE)) { - for (Encoder::Ptr encoder : this->encoders) + if (ImGui::SliderInt("Frame-Rate (Fps)", (int32_t*) &this->encoder_frame_rate, 1, 180)) { - encoder->set_frame_rate(this->encoder_frame_rate); - } + for (Encoder::Ptr encoder : this->encoders) + { + encoder->set_frame_rate(this->encoder_frame_rate); + } + } } } else if (this->encoder_mode == ENCODER_MODE_CONSTANT_QUALITY) { - if (ImGui::SliderFloat("Quality", &this->encoder_quality, 0.0, 1.0)) + if (encoder->is_supported(ENCODER_SETTING_QUALITY)) { - for (Encoder::Ptr encoder : this->encoders) + if (ImGui::SliderFloat("Quality", &this->encoder_quality, 0.0, 1.0)) { - encoder->set_quality(this->encoder_quality); + for (Encoder::Ptr encoder : this->encoders) + { + encoder->set_quality(this->encoder_quality); + } } } } @@ -244,7 +261,7 @@ void RemoteHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f uint32_t transform_id = this->transform_id; pass_timer->begin_pass(command_buffer, "Encode Setup " + std::to_string(frame_id)); - bool result = encoder->encode(command_buffer, renderer, framebuffer, frame_layout, [this, frame_number, frame_id, transform_id](const std::span<uint8_t>& content, bool is_config) + EncoderResult result = encoder->encode(command_buffer, renderer, framebuffer, frame_layout, [this, frame_number, frame_id, transform_id](const std::span<uint8_t>& content, bool is_config) { if (is_config) { @@ -258,7 +275,7 @@ void RemoteHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f }); pass_timer->end_pass(command_buffer); - if (!result) + if (result == ENCODER_RESULT_ERROR) { lava::log()->error("Remote Headset: Error during encode submission!"); } @@ -425,7 +442,7 @@ void RemoteHeadset::destroy_framebuffers() bool RemoteHeadset::create_encoders() { - this->encoders.resize(this->frame_id_count); + this->encoders.resize(this->frame_id_count, nullptr); for (uint32_t index = 0; index < this->encoders.size(); index++) { @@ -460,7 +477,10 @@ void RemoteHeadset::destroy_encoders() { for (uint32_t index = 0; index < this->encoders.size(); index++) { - this->encoders[index]->destroy(); + if (this->encoders[index] != nullptr) + { + this->encoders[index]->destroy(); + } } this->encoders.clear(); diff --git a/src/vr_application.cpp b/src/vr_application.cpp index f7658b98..e8390490 100644 --- a/src/vr_application.cpp +++ b/src/vr_application.cpp @@ -3,9 +3,6 @@ #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)) @@ -26,9 +23,6 @@ bool VRApplication::setup(lava::name name, argh::parser cmd_line) lava::frame_config config(name, std::forward<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)) { @@ -52,9 +46,6 @@ 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, this->get_instance(), parameters); - if (!this->headset->on_setup_device(parameters)) { lava::log()->error("Error during headset setup device!"); @@ -692,7 +683,7 @@ bool VRApplication::on_render(VkCommandBuffer command_buffer, lava::index frame) this->stereo_transform->write(frame); - if(!this->scene->stage(command_buffer, frame)) + if (!this->scene->stage(command_buffer, frame)) { lava::log()->error("Error during scene stage!"); -- GitLab