diff --git a/src/encoder/encoder.hpp b/src/encoder/encoder.hpp index a97e2b616e3b44681f8645b2e6773a3fa2d1460e..ac89a55f9299c2ea74f05c2a76b2946a46485f95 100644 --- a/src/encoder/encoder.hpp +++ b/src/encoder/encoder.hpp @@ -90,16 +90,17 @@ public: virtual bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) = 0; virtual void destroy() = 0; - // 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 EncoderResult 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, uint32_t timestamp, OnEncodeComplete function) = 0; + + // Can be used to invalidate the reference frame that was encodided using the specified timestamp. + virtual bool invalidate(uint32_t timestamp) = 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. virtual void set_on_encode_error(OnEncodeError function) = 0; - // The following functions should be thread safe. // The following settings are only suggestions and can be ignored if for example the feature is not supported. virtual void set_mode(EncoderMode mode); // Defines if the encoder should use constant quality or constant bitrate. Default: constant quality virtual void set_quality(double quality); // The quality ranges from 0.0 (low quality) to 1.0 (high quality). Default: 0.0 @@ -107,7 +108,6 @@ public: virtual void set_key_rate(uint32_t key_rate); // The keyrate is defined as the number of frames between two i-frames. Default: 90 virtual void set_frame_rate(uint32_t frame_rate); // The frame rate is given in frames per second. Default: 90 - // The following functions should be thread safe. virtual EncoderMode get_mode() const; virtual double get_quality() const; virtual double get_bitrate() const; diff --git a/src/encoder/nvidia_encoder.cpp b/src/encoder/nvidia_encoder.cpp index 0823eaf8c958f3ea4ed00da107162e7d1b9217d8..e9f8b5c54ce696e8ffd0742261336a4b5560bd96 100644 --- a/src/encoder/nvidia_encoder.cpp +++ b/src/encoder/nvidia_encoder.cpp @@ -88,7 +88,7 @@ void NvidiaEncoder::destroy() this->destroy_context(); } -EncoderResult 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, uint32_t timestamp, OnEncodeComplete function) { NvidiaEncoderFrame::Ptr frame; @@ -97,6 +97,7 @@ EncoderResult NvidiaEncoder::encode(VkCommandBuffer command_buffer, lava::render return ENCODER_RESULT_FRAME_DROPPED; } + frame->timestamp = timestamp; frame->output_parameter = this->parameter_change; frame->on_encode_complete = std::move(function); @@ -203,6 +204,18 @@ EncoderResult NvidiaEncoder::encode(VkCommandBuffer command_buffer, lava::render return ENCODER_RESULT_SUCCESS; } +bool NvidiaEncoder::invalidate(uint32_t timestamp) +{ + if (nvenc_functions.nvEncInvalidateRefFrames(this->nvenc_session, timestamp) != NV_ENC_SUCCESS) + { + lava::log()->error("Nvidia Encoder: Can't invalidate frame!"); + + return false; + } + + return true; +} + void NvidiaEncoder::set_on_encode_error(OnEncodeError function) { this->on_encode_error = std::move(function); @@ -449,6 +462,9 @@ bool NvidiaEncoder::apply_config() return true; } +uint32_t temp_index = 0; +uint32_t write_index = 0; + void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) { if (cuCtxPushCurrent(this->cuda_context) != CUDA_SUCCESS) @@ -512,6 +528,22 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) codec_extention_parameters.mvcPicParams = motion_vector_parameters; memset(codec_extention_parameters.reserved1, 0, sizeof(codec_extention_parameters.reserved1)); + uint32_t mark = 0; + uint32_t mark_index = 0; + uint32_t read_mask = 0; + + if (temp_index > 0) + { + read_mask = 0x3FF & ~(1 << write_index); + } + + if (temp_index % (90) == 0) + { + mark = 1; + mark_index = write_index; + write_index = (write_index + 1) % 10; + } + NV_ENC_PIC_PARAMS_H264 codec_parameters; codec_parameters.displayPOCSyntax = 0; codec_parameters.reserved3 = 0; @@ -520,7 +552,7 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) codec_parameters.forceIntraRefreshWithFrameCnt = 0; codec_parameters.constrainedFrame = 1; codec_parameters.sliceModeDataUpdate = 1; - codec_parameters.ltrMarkFrame = 0; + codec_parameters.ltrMarkFrame = mark; codec_parameters.ltrUseFrames = 0; codec_parameters.reservedBitFields = 0; codec_parameters.sliceTypeData = 0; @@ -529,8 +561,8 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) codec_parameters.seiPayloadArray = nullptr; codec_parameters.sliceMode = 3; codec_parameters.sliceModeData = 10; - codec_parameters.ltrMarkFrameIdx = 0; - codec_parameters.ltrUseFrameBitmap = 0; + codec_parameters.ltrMarkFrameIdx = mark_index; + codec_parameters.ltrUseFrameBitmap = read_mask; codec_parameters.ltrUsageMode = 0; codec_parameters.forceIntraSliceCount = 0; codec_parameters.forceIntraSliceIdx = 0; @@ -545,7 +577,7 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) encode_parameters.inputPitch = frame->image_layout.rowPitch; encode_parameters.encodePicFlags = 0; encode_parameters.frameIdx = 0; - encode_parameters.inputTimeStamp = 0; + encode_parameters.inputTimeStamp = frame->timestamp; encode_parameters.inputDuration = 0; encode_parameters.inputBuffer = frame->nvenc_mapped_buffer; encode_parameters.outputBitstream = frame->nvenc_output_buffer; @@ -573,6 +605,8 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) return; } + + temp_index++; } bool NvidiaEncoder::create_context(lava::device_ptr device) @@ -717,6 +751,8 @@ bool NvidiaEncoder::create_session(const glm::uvec2& size) this->nvenc_encode_config.encodeCodecConfig.h264Config.enableIntraRefresh = 1; //NOTE: Can create problems when replayed in VLC this->nvenc_encode_config.encodeCodecConfig.h264Config.intraRefreshPeriod = this->frame_rate * 2; this->nvenc_encode_config.encodeCodecConfig.h264Config.intraRefreshCnt = 10; + this->nvenc_encode_config.encodeCodecConfig.h264Config.enableLTR = 1; + this->nvenc_encode_config.encodeCodecConfig.h264Config.ltrNumFrames = 10; this->nvenc_encode_config.rcParams.enableAQ = 1; this->nvenc_encode_config.rcParams.aqStrength = 0; diff --git a/src/encoder/nvidia_encoder.hpp b/src/encoder/nvidia_encoder.hpp index 4a364f1e940ec323903ab2d1ae0a5f53fb860048..ad2f9a3199c0ccadf38e3f7deb0ab60ac6f73a23 100644 --- a/src/encoder/nvidia_encoder.hpp +++ b/src/encoder/nvidia_encoder.hpp @@ -39,6 +39,7 @@ public: bool output_parameter = false; + uint32_t timestamp = 0; Encoder::OnEncodeComplete on_encode_complete; }; @@ -53,7 +54,8 @@ public: bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) override; void destroy() override; - EncoderResult encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function) override; + EncoderResult encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, uint32_t timestamp, OnEncodeComplete function) override; + bool invalidate(uint32_t timestamp) override; void set_on_encode_error(OnEncodeError function) override; diff --git a/src/encoder/vulkan_encoder.cpp b/src/encoder/vulkan_encoder.cpp index 48a17447effad710a54f7f0e9856513ee2a5a0d3..ddbefcce5365c80df1d7218387d96af62d4502ce 100644 --- a/src/encoder/vulkan_encoder.cpp +++ b/src/encoder/vulkan_encoder.cpp @@ -182,7 +182,7 @@ void VulkanEncoder::destroy() this->destroy_session(device); } -EncoderResult 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, uint32_t timestamp, OnEncodeComplete callback) { if (!this->encode_control(renderer, callback)) { @@ -204,6 +204,7 @@ EncoderResult VulkanEncoder::encode(VkCommandBuffer command_buffer, lava::render } frame->frame_index = this->frame_index; + frame->timestamp = timestamp; frame->type = VULKAN_ENCODER_FRAME_TYPE_FRAME; frame->callback = std::move(callback); @@ -298,6 +299,24 @@ EncoderResult VulkanEncoder::encode(VkCommandBuffer command_buffer, lava::render return ENCODER_RESULT_SUCCESS; } +bool VulkanEncoder::invalidate(uint32_t timestamp) +{ + for (VulkanEncoderFrame::Ptr& slot : this->frame_slots) + { + if (slot != nullptr) + { + if (slot->timestamp == timestamp) + { + slot = nullptr; + + break; + } + } + } + + return true; +} + void VulkanEncoder::set_on_encode_error(OnEncodeError function) { this->on_encode_error = std::move(function); @@ -2171,6 +2190,13 @@ void VulkanEncoder::encode_pass_frame_setup(VkCommandBuffer command_buffer, Vulk void VulkanEncoder::encode_pass_frame_command(VkCommandBuffer command_buffer, VulkanEncoderFrame::Ptr frame, const std::vector<VulkanEncoderFrame::Ptr>& reference_slots) { + bool key_frame = this->is_key_frame(); + + if (reference_slots.empty()) + { + key_frame = true; + } + VkOffset2D encode_offset; encode_offset.x = 0; encode_offset.y = 0; diff --git a/src/encoder/vulkan_encoder.hpp b/src/encoder/vulkan_encoder.hpp index cea0f938b75bbcab1369a64da352ca22a50444ab..c935d876ab92795d7f5c3a8fd28d472231b7d5fa 100644 --- a/src/encoder/vulkan_encoder.hpp +++ b/src/encoder/vulkan_encoder.hpp @@ -65,6 +65,7 @@ public: ExternFence::Ptr encode_fence; uint32_t frame_index = 0; + uint32_t timestamp = 0; VulkanEncoderFrameType type; Encoder::OnEncodeComplete callback; }; @@ -80,8 +81,9 @@ public: bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) override; void destroy() override; - EncoderResult encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function) override; - + EncoderResult encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, uint32_t timestamp, OnEncodeComplete function) override; + bool invalidate(uint32_t timestamp) override; + void set_on_encode_error(OnEncodeError function) override; void set_mode(EncoderMode mode) override; diff --git a/src/headset/remote_headset.cpp b/src/headset/remote_headset.cpp index f7e4b94c158ac5c15a00fc8dcfbb22ec3ca93ec1..ae4d6f8287e9befb6b50251cd7c3ccac47b3574b 100644 --- a/src/headset/remote_headset.cpp +++ b/src/headset/remote_headset.cpp @@ -120,6 +120,7 @@ bool RemoteHeadset::on_interface() ImGui::Separator(); + std::unique_lock<std::mutex> encoder_lock(this->encoder_mutex); Encoder::Ptr encoder = this->encoders.front(); if (encoder->is_supported(ENCODER_SETTING_MODE_CONSTANT_BITRATE) && encoder->is_supported(ENCODER_SETTING_MODE_CONSTANT_QUALITY)) @@ -195,6 +196,7 @@ bool RemoteHeadset::on_interface() return false; } + encoder_lock.unlock(); if (ImGui::SliderInt("Send-Queue Limit (KBytes)", (int32_t*)&this->send_queue_size_limit, 1, 1000)) { @@ -211,7 +213,7 @@ bool RemoteHeadset::on_interface() std::vector<std::string> graph_names; std::vector<const char*> graph_name_pointers; - std::unique_lock<std::mutex> lock(this->transport_mutex); + std::unique_lock<std::mutex> transport_lock(this->transport_mutex); for (const Statistic::Ptr& statistic : this->statistic_list) { if (!statistic->is_log_only()) @@ -219,7 +221,7 @@ bool RemoteHeadset::on_interface() graph_names.push_back(statistic->get_display_name()); } } - lock.unlock(); + transport_lock.unlock(); for (const std::string& graph_name : graph_names) { @@ -230,7 +232,7 @@ bool RemoteHeadset::on_interface() ImGui::Separator(); - lock.lock(); + transport_lock.lock(); for (Statistic::Ptr statistic : this->statistic_list) { if (!statistic->is_log_only()) @@ -238,7 +240,7 @@ bool RemoteHeadset::on_interface() statistic->interface(128); } } - lock.unlock(); + transport_lock.unlock(); return true; } @@ -285,13 +287,16 @@ void RemoteHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f lava::renderer& renderer = this->get_application()->get_renderer(); lava::image::ptr framebuffer = this->framebuffers[frame_id]; - PassTimer::Ptr pass_timer = this->get_application()->get_pass_timer(); - Encoder::Ptr encoder = this->encoders[frame_id]; + PassTimer::Ptr pass_timer = this->get_application()->get_pass_timer(); uint32_t frame_number = this->frame_number; uint32_t transform_id = this->transform_id; pass_timer->begin_pass(command_buffer, "encode_setup_" + std::to_string(frame_id)); - 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) + std::unique_lock<std::mutex> lock(this->encoder_mutex); + Encoder::Ptr encoder = this->encoders[frame_id]; + uint32_t last_invalidate = this->encoder_last_invalidate[frame_id]; + + EncoderResult result = encoder->encode(command_buffer, renderer, framebuffer, frame_layout, frame_number, [this, frame_number, frame_id, transform_id, last_invalidate](const std::span<uint8_t>& content, bool is_config) { if (is_config) { @@ -300,7 +305,7 @@ void RemoteHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f else { - this->transport->send_frame_nal(frame_number, frame_id, transform_id, content); + this->transport->send_frame_nal(frame_number, frame_id, transform_id, last_invalidate, content); } if (!this->encoder_files.empty()) @@ -308,6 +313,7 @@ void RemoteHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f this->encoder_files[frame_id].write((const char*)content.data(), content.size()); } }); + lock.unlock(); pass_timer->end_pass(command_buffer); if (result == ENCODER_RESULT_ERROR) @@ -408,6 +414,10 @@ bool RemoteHeadset::create_transport() { this->on_controller_thumbstick(controller, thumbstick); }); + this->transport->set_on_frame_invalidate([this](FrameNumber frame_number, FrameId frame_id) + { + this->on_frame_invalidate(frame_number, frame_id); + }); this->transport->set_on_performance_sample([this](std::optional<FrameNumber> frame_number, std::optional<FrameId> frame_id, std::optional<TransformId> transform_id, Unit unit, bool log_only, double sample, const std::string& name) { this->on_performance_sample(frame_number, frame_id, transform_id, unit, log_only, sample, name); @@ -477,6 +487,7 @@ void RemoteHeadset::destroy_framebuffers() bool RemoteHeadset::create_encoders() { this->encoders.resize(this->frame_id_count, nullptr); + this->encoder_last_invalidate.resize(this->frame_id_count, 0); for (uint32_t index = 0; index < this->encoders.size(); index++) { @@ -711,6 +722,18 @@ void RemoteHeadset::on_controller_thumbstick(Controller controller, const glm::v lock.unlock(); } +void RemoteHeadset::on_frame_invalidate(FrameNumber frame_number, FrameId frame_id) +{ + std::unique_lock<std::mutex> lock(this->encoder_mutex); + Encoder::Ptr encoder = this->encoders[frame_id]; + encoder->invalidate(frame_number); + + this->encoder_last_invalidate[frame_id] = frame_number; + lock.unlock(); + + lava::log()->debug("Invalidate Frame Number: {} Frame Id: {}", frame_number, frame_id); +} + void RemoteHeadset::on_performance_sample(std::optional<FrameNumber> frame_number, std::optional<FrameId> frame_id, std::optional<TransformId> transform_id, Unit unit, bool log_only, double sample, const std::string& name) { std::unique_lock<std::mutex> lock(this->transport_mutex); diff --git a/src/headset/remote_headset.hpp b/src/headset/remote_headset.hpp index 02d97c42b110b0ee58fde8040cadbd19e96ced23..f52aa38001de36985da92fabe88e094555cd464d 100644 --- a/src/headset/remote_headset.hpp +++ b/src/headset/remote_headset.hpp @@ -68,6 +68,7 @@ private: void on_controller_event(Controller controller, ControllerState controller_state); void on_controller_button(Controller controller, Button button, ButtonState button_state); void on_controller_thumbstick(Controller controller, const glm::vec2& thumbstick); + void on_frame_invalidate(FrameNumber frame_number, FrameId frame_id); void on_performance_sample(std::optional<FrameNumber> frame_number, std::optional<FrameId> frame_id, std::optional<TransformId> transform_id, Unit unit, bool log_only, double sample, const std::string& name); void on_transport_error(); void on_encode_error(); @@ -111,7 +112,9 @@ private: lava::image::ptr framebuffer_array; std::vector<lava::image::ptr> framebuffers; - std::vector<Encoder::Ptr> encoders; + std::mutex encoder_mutex; + std::vector<Encoder::Ptr> encoders; //NOTE: Protected by encoder_mutex + std::vector<uint32_t> encoder_last_invalidate; //NOTE: Protected by encoder_mutex std::vector<std::fstream> encoder_files; uint32_t encoder_mode = ENCODER_MODE_CONSTANT_QUALITY; uint32_t encoder_input_rate = 90; diff --git a/src/transport/transport.hpp b/src/transport/transport.hpp index eedfcb859ec198cf77306934588bbcd7b108ef3e..7f93ad6644fe47d72521e7ecd3be06e9f4ce8e61 100644 --- a/src/transport/transport.hpp +++ b/src/transport/transport.hpp @@ -88,6 +88,11 @@ public: // thumbstick: Position of the thumbstick on the specified controller typedef std::function<void(Controller controller, const glm::vec2& thumbstick)> OnControllerThumbstick; + // OnFrameInvalidate: Used to indicate that a frame was not correctly received. + // frame_number: The the number of the frame. The frame number of the first rendered frame would be zero + // frame_id: The frame id to which the information in this packet belong (e.g. left or right eye) + typedef std::function<void(FrameNumber frame_number, FrameId frame_id)> OnFrameInvalidate; + // OnPerformanceSample: Used to transmit performance related samples. // frame_number: Identifies the frame to which the information in this packet belongs // frame_id: Identifies the frame id (e.g. left or right eye) to which the information in this packet belongs @@ -133,11 +138,12 @@ public: // send_frame_nal: Use to send encoded images // This could be for example an h264 encoded image // This function should be threadsafe and callable by main and worker thread - // frame_number: The the number of the frame. The frame number of the first rendered frame would be zero - // frame_id: The frame id to which the information in this packet belong (e.g. left or right eye) - // transform_id: The id of the head transformation that was used to render the image in this packet - // content: Encoded image - virtual bool send_frame_nal(FrameNumber frame_number, FrameId frame_id, TransformId transform_id, const std::span<const uint8_t>& content) = 0; + // frame_number: The the number of the frame. The frame number of the first rendered frame would be zero + // frame_id: The frame id to which the information in this packet belong (e.g. left or right eye) + // transform_id: The id of the head transformation that was used to render the image in this packet + // last_invalidate: The frame number of the last frame that was invalidated + // content: Encoded image + virtual bool send_frame_nal(FrameNumber frame_number, FrameId frame_id, TransformId transform_id, FrameNumber last_invalidate, const std::span<const uint8_t>& content) = 0; // send_frame_metadata: Used to send additional information that could be needed by the post processing step that was selected in the setup-complete packet // This function should be threadsafe and callable by main and worker thread @@ -175,6 +181,7 @@ public: virtual void set_on_controller_event(OnControllerEvent function) = 0; virtual void set_on_controller_button(OnControllerButton function) = 0; virtual void set_on_controller_thumbstick(OnControllerThumbstick function) = 0; + virtual void set_on_frame_invalidate(OnFrameInvalidate function) = 0; virtual void set_on_performance_sample(OnPerformanceSample function) = 0; virtual void set_on_transport_error(OnTransportError function) = 0; diff --git a/src/transport/udp_packet.hpp b/src/transport/udp_packet.hpp index ab19362951827620eb646bb9897709cece83cf62..45be97073d8373f6b30de8dde9bc32bcc74a6b23 100644 --- a/src/transport/udp_packet.hpp +++ b/src/transport/udp_packet.hpp @@ -27,25 +27,26 @@ enum UDPPacketId : uint32_t UDP_PACKET_ID_CONTROLLER_THUMBSTICK = 0x08, UDP_PACKET_ID_FRAME_CONFIG_NAL = 0x09, UDP_PACKET_ID_FRAME_NAL = 0x0A, - UDP_PACKET_ID_FRAME_METADATA = 0x0B, - UDP_PACKET_ID_PERFORMANCE_SAMPLE = 0x0C, - UDP_PACKET_ID_OVERLAY_CONFIG = 0x0D, - UDP_PACKET_ID_OVERLAY_TEXT = 0x0E, - UDP_PACKET_ID_OVERLAY_GRAPH = 0x0F, + UDP_PACKET_ID_FRAME_INVALIDATE = 0x0B, + UDP_PACKET_ID_FRAME_METADATA = 0x0C, + UDP_PACKET_ID_PERFORMANCE_SAMPLE = 0x0D, + UDP_PACKET_ID_OVERLAY_CONFIG = 0x0E, + UDP_PACKET_ID_OVERLAY_TEXT = 0x0F, + UDP_PACKET_ID_OVERLAY_GRAPH = 0x10, UDP_PACKET_ID_MAX_COUNT }; enum UDPStereoStrategyId : uint32_t { - UDP_STEREO_STRATEGY_ID_DEBUG = 0x00, + UDP_STEREO_STRATEGY_ID_DEBUG = 0x00, UDP_STEREO_STRATEGY_ID_NATIVE_STEREO = 0x01, UDP_STEREO_STRATEGY_ID_MAX_COUNT }; enum UDPControllerId : uint32_t { - UDP_CONTROLLER_ID_LEFT = 0x00, - UDP_CONTROLLER_ID_RIGHT = 0x01, + UDP_CONTROLLER_ID_LEFT = 0x00, + UDP_CONTROLLER_ID_RIGHT = 0x01, UDP_CONTROLLER_ID_MAX_COUNT }; @@ -57,11 +58,11 @@ enum UDPControllerState : uint32_t enum UDPButtonId : uint32_t { - UDP_BUTTON_ID_A = 0x00, - UDP_BUTTON_ID_B = 0x01, - UDP_BUTTON_ID_X = 0x02, - UDP_BUTTON_ID_Y = 0x03, - UDP_BUTTON_ID_TRIGGER = 0x04, + UDP_BUTTON_ID_A = 0x00, + UDP_BUTTON_ID_B = 0x01, + UDP_BUTTON_ID_X = 0x02, + UDP_BUTTON_ID_Y = 0x03, + UDP_BUTTON_ID_TRIGGER = 0x04, UDP_BUTTON_ID_MAX_COUNT }; @@ -225,6 +226,7 @@ struct UDPPacketFrameNal UDPFrameNumber frame_number; //In case of fragmentation, frame number and frame id are used together as identifier UDPFrameId frame_id; UDPTransformId transform_id; + UDPFrameNumber last_invalidate; uint32_t payload_offset; uint32_t payload_size; @@ -232,6 +234,15 @@ struct UDPPacketFrameNal //Followed by: Nal }; +struct UDPPacketFrameInvalidate +{ + UDPPacketId id = UDP_PACKET_ID_FRAME_INVALIDATE; + uint32_t size = 0; + + UDPFrameNumber frame_number; + UDPFrameId frame_id; +}; + //NOTE: Assume frame metadata of a particular frame number is always send before any frame nals of the same frame number //NOTE: The packet needs to be fragmented if the payload is too big struct UDPPacketFrameMetadata diff --git a/src/transport/udp_transport.cpp b/src/transport/udp_transport.cpp index 311101a55ee025b2f0960709ac85fe491ab34938..c73891c5467ce9de2d35108fbe74f7c0e7c12fda 100644 --- a/src/transport/udp_transport.cpp +++ b/src/transport/udp_transport.cpp @@ -189,7 +189,7 @@ bool UDPTransport::send_frame_config_nal(FrameId frame_id, const std::span<const return true; } -bool UDPTransport::send_frame_nal(FrameNumber frame_number, FrameId frame_id, TransformId transform_id, const std::span<const uint8_t>& content) +bool UDPTransport::send_frame_nal(FrameNumber frame_number, FrameId frame_id, TransformId transform_id, FrameNumber last_invalidate, const std::span<const uint8_t>& content) { if (this->get_state() != TRANSPORT_STATE_CONNECTED) { @@ -212,6 +212,7 @@ bool UDPTransport::send_frame_nal(FrameNumber frame_number, FrameId frame_id, Tr packet->frame_number = frame_number; packet->frame_id = frame_id; packet->transform_id = transform_id; + packet->last_invalidate = last_invalidate; packet->payload_offset = offset; packet->payload_size = content.size(); @@ -374,6 +375,11 @@ void UDPTransport::set_on_controller_thumbstick(OnControllerThumbstick function) this->on_controller_thumbstick = std::move(function); } +void UDPTransport::set_on_frame_invalidate(OnFrameInvalidate function) +{ + this->on_frame_invalidate = std::move(function); +} + void UDPTransport::set_on_performance_sample(OnPerformanceSample function) { this->on_performance_sample = std::move(function); @@ -602,6 +608,7 @@ void UDPTransport::receive_datagram() case UDP_PACKET_ID_CONTROLLER_EVENT: case UDP_PACKET_ID_CONTROLLER_BUTTON: case UDP_PACKET_ID_CONTROLLER_THUMBSTICK: + case UDP_PACKET_ID_FRAME_INVALIDATE: case UDP_PACKET_ID_PERFORMANCE_SAMPLE: this->receive_queue.push_back(datagram); break; @@ -757,6 +764,9 @@ void UDPTransport::process_receive_queue() case UDP_PACKET_ID_CONTROLLER_THUMBSTICK: this->parse_controller_thumbstick(datagram); break; + case UDP_PACKET_ID_FRAME_INVALIDATE: + this->parse_frame_invalidate(datagram); + break; case UDP_PACKET_ID_PERFORMANCE_SAMPLE: this->parse_performance_sample(datagram); break; @@ -955,6 +965,22 @@ void UDPTransport::parse_controller_thumbstick(const Datagram& datagram) this->on_controller_thumbstick(controller, packet->thumbstick); } +void UDPTransport::parse_frame_invalidate(const Datagram& datagram) +{ + UDPPacketFrameInvalidate* packet = (UDPPacketFrameInvalidate*)datagram.buffer; + + if (packet->id != UDP_PACKET_ID_FRAME_INVALIDATE) + { + lava::log()->error("UDP-Transport: Wrong packet id for frame invalidate packet!"); + this->on_transport_error(); + this->set_state(TRANSPORT_STATE_ERROR); + + return; + } + + this->on_frame_invalidate(packet->frame_number, packet->frame_id); +} + void UDPTransport::parse_performance_sample(const Datagram& datagram) { UDPPacketPerformanceSample* packet = (UDPPacketPerformanceSample*)datagram.buffer; diff --git a/src/transport/udp_transport.hpp b/src/transport/udp_transport.hpp index 4a80198c49d7c6cdf3b49307be3a4fd1797ad75a..3bd1dc2c4fc320956863b16638d6f35dc0ee953b 100644 --- a/src/transport/udp_transport.hpp +++ b/src/transport/udp_transport.hpp @@ -26,7 +26,7 @@ public: bool send_setup_complete(RemoteStrategy remote_strategy, uint32_t max_frame_ids, float near_plane, float far_plane) override; bool send_stage_transform(const glm::mat4& stage_transform) override; bool send_frame_config_nal(FrameId frame_id, const std::span<const uint8_t>& content) override; - bool send_frame_nal(FrameNumber frame_number, FrameId frame_id, TransformId transform_id, const std::span<const uint8_t>& content) override; + bool send_frame_nal(FrameNumber frame_number, FrameId frame_id, TransformId transform_id, FrameNumber last_invalidate, const std::span<const uint8_t>& content) override; bool send_frame_metadata(FrameNumber frame_number, const std::span<const uint8_t>& content) override; bool send_overlay_config(bool overlay_enable) override; bool send_overlay_text(FrameNumber frame_number, uint32_t overlay_index, const std::string& label, const std::string& text) override; @@ -39,6 +39,7 @@ public: void set_on_controller_event(OnControllerEvent function) override; void set_on_controller_button(OnControllerButton function) override; void set_on_controller_thumbstick(OnControllerThumbstick function) override; + void set_on_frame_invalidate(OnFrameInvalidate function) override; void set_on_performance_sample(OnPerformanceSample function) override; void set_on_transport_error(OnTransportError function) override; @@ -68,6 +69,7 @@ private: void parse_controller_event(const Datagram& datagram); void parse_controller_button(const Datagram& datagram); void parse_controller_thumbstick(const Datagram& datagram); + void parse_frame_invalidate(const Datagram& datagram); void parse_performance_sample(const Datagram& datagram); private: @@ -104,5 +106,6 @@ private: OnControllerButton on_controller_button; OnControllerThumbstick on_controller_thumbstick; OnPerformanceSample on_performance_sample; + OnFrameInvalidate on_frame_invalidate; OnTransportError on_transport_error; }; \ No newline at end of file