diff --git a/res/dpr/utility/encode_color_conversion.frag b/res/dpr/utility/encode_color_conversion.frag index 9125d168879650d312f8b721cf9e1cf868322bc4..2c707fbab99f66173d28389cb4136fd1559cb9c2 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 2968eaadfd59879c40393a94843364f4f759e796..a97e2b616e3b44681f8645b2e6773a3fa2d1460e 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 5f90d8543ccf0be43f6a610b56fd9536fded4a36..adb483dff3cc98ddb081de24502c17d067f46783 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 796c6e0776683eaa6cdc9e4e0bfae197dbe0141e..78060a72cb043ad1bbe4b1a873b36123f710380c 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 578b2a31ba5bead31d3c47c612afa5ccc449b868..48a17447effad710a54f7f0e9856513ee2a5a0d3 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 1684571b3e05c4e1ae47cc3d5f58077b95f880f2..12e9fb10e5e75f74e4093eaeb6ef09984cafcb77 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 0df35a1da791a7a60ecde9d37de5ddb4e41e01a0..3bd74ecdad9d65e96c617d1c9c4a0d113dc0546c 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 f7614bd59abc47627cfcc2bd2791d850d0c40a10..80c78e7b56441ef56d7978dcd2095d1fbabf7365 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 f7658b98360b27d7e95294f36140c2c1ab4a65a0..e8390490848dfdca4ed66889b22dc5c530e938fc 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!");