UnityRenderEvent.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. #include "pch.h"
  2. #include "Context.h"
  3. #include "GpuMemoryBufferPool.h"
  4. #include "GraphicsDevice/GraphicsDevice.h"
  5. #include "GraphicsDevice/GraphicsUtility.h"
  6. #include "ProfilerMarkerFactory.h"
  7. #include "ScopedProfiler.h"
  8. #include "UnityProfilerInterfaceFunctions.h"
  9. #include "UnityVideoTrackSource.h"
  10. #include "VideoFrame.h"
  11. #if defined(SUPPORT_VULKAN)
  12. #include "GraphicsDevice/Vulkan/UnityVulkanInitCallback.h"
  13. #include "UnityVulkanInterfaceFunctions.h"
  14. #endif
  15. enum class VideoStreamRenderEventID
  16. {
  17. Encode = 1,
  18. };
  19. using namespace unity::webrtc;
  20. using namespace ::webrtc;
  21. namespace unity
  22. {
  23. namespace webrtc
  24. {
  25. static IUnityInterfaces* s_UnityInterfaces = nullptr;
  26. static IUnityGraphics* s_Graphics = nullptr;
  27. static Context* s_context = nullptr;
  28. static std::unique_ptr<UnityProfiler> s_UnityProfiler = nullptr;
  29. static std::unique_ptr<ProfilerMarkerFactory> s_ProfilerMarkerFactory = nullptr;
  30. static std::map<const uint32_t, std::shared_ptr<UnityVideoRenderer>> s_mapVideoRenderer;
  31. static std::unique_ptr<Clock> s_clock;
  32. static const UnityProfilerMarkerDesc* s_MarkerEncode = nullptr;
  33. static const UnityProfilerMarkerDesc* s_MarkerDecode = nullptr;
  34. static std::unique_ptr<IGraphicsDevice> s_gfxDevice;
  35. static std::unique_ptr<GpuMemoryBufferPool> s_bufferPool;
  36. IGraphicsDevice* Plugin::GraphicsDevice()
  37. {
  38. RTC_DCHECK(s_gfxDevice.get());
  39. return s_gfxDevice.get();
  40. }
  41. ProfilerMarkerFactory* Plugin::ProfilerMarkerFactory() { return s_ProfilerMarkerFactory.get(); }
  42. static libyuv::FourCC ConvertTextureFormat(UnityRenderingExtTextureFormat type)
  43. {
  44. switch (type)
  45. {
  46. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatB8G8R8A8_SRGB:
  47. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatB8G8R8A8_UNorm:
  48. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatB8G8R8A8_SNorm:
  49. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatB8G8R8A8_UInt:
  50. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatB8G8R8A8_SInt:
  51. return libyuv::FOURCC_ARGB;
  52. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatR8G8B8A8_SRGB:
  53. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatR8G8B8A8_UNorm:
  54. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatR8G8B8A8_SNorm:
  55. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatR8G8B8A8_UInt:
  56. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatR8G8B8A8_SInt:
  57. return libyuv::FOURCC_ABGR;
  58. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatA8R8G8B8_SRGB:
  59. case UnityRenderingExtTextureFormat::kUnityRenderingExtFormatA8R8G8B8_UNorm:
  60. return libyuv::FOURCC_BGRA;
  61. default:
  62. return libyuv::FOURCC_ANY;
  63. }
  64. }
  65. } // end namespace webrtc
  66. } // end namespace unity
  67. static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType)
  68. {
  69. switch (eventType)
  70. {
  71. case kUnityGfxDeviceEventInitialize:
  72. {
  73. /// note::
  74. /// kUnityGfxDeviceEventInitialize event is occurred twice on Unity Editor.
  75. /// First time, s_UnityInterfaces return UnityGfxRenderer as kUnityGfxRendererNull.
  76. /// The actual value of UnityGfxRenderer is returned on second time.
  77. UnityGfxRenderer renderer = s_UnityInterfaces->Get<IUnityGraphics>()->GetRenderer();
  78. if (renderer == kUnityGfxRendererNull)
  79. break;
  80. #if defined(SUPPORT_VULKAN)
  81. if (renderer == kUnityGfxRendererVulkan)
  82. {
  83. std::unique_ptr<UnityGraphicsVulkan> vulkan = UnityGraphicsVulkan::Get(s_UnityInterfaces);
  84. UnityVulkanInstance instance = vulkan->Instance();
  85. // Load vulkan functions dynamically.
  86. if (!LoadVulkanFunctions(instance))
  87. {
  88. RTC_LOG(LS_INFO) << "LoadVulkanFunctions failed";
  89. return;
  90. }
  91. /// note::
  92. /// Configure the event on the rendering thread called from CommandBuffer::IssuePluginEventAndData method in
  93. /// managed code.
  94. UnityVulkanPluginEventConfig encodeEventConfig;
  95. encodeEventConfig.graphicsQueueAccess = kUnityVulkanGraphicsQueueAccess_DontCare;
  96. encodeEventConfig.renderPassPrecondition = kUnityVulkanRenderPass_DontCare;
  97. encodeEventConfig.flags = kUnityVulkanEventConfigFlag_EnsurePreviousFrameSubmission |
  98. kUnityVulkanEventConfigFlag_ModifiesCommandBuffersState;
  99. vulkan->ConfigureEvent(static_cast<int>(VideoStreamRenderEventID::Encode), &encodeEventConfig);
  100. }
  101. #endif
  102. s_gfxDevice.reset(GraphicsDevice::GetInstance().Init(s_UnityInterfaces, s_ProfilerMarkerFactory.get()));
  103. if (s_gfxDevice)
  104. {
  105. s_gfxDevice->InitV();
  106. }
  107. s_bufferPool = std::make_unique<GpuMemoryBufferPool>(s_gfxDevice.get(), s_clock.get());
  108. break;
  109. }
  110. case kUnityGfxDeviceEventShutdown:
  111. {
  112. // Release buffers before graphics device because buffers depends on the device.
  113. s_bufferPool = nullptr;
  114. s_mapVideoRenderer.clear();
  115. if (s_gfxDevice)
  116. {
  117. s_gfxDevice->ShutdownV();
  118. s_gfxDevice = nullptr;
  119. }
  120. // UnityPluginUnload not called normally
  121. s_Graphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent);
  122. s_clock = nullptr;
  123. break;
  124. }
  125. case kUnityGfxDeviceEventBeforeReset:
  126. {
  127. break;
  128. }
  129. case kUnityGfxDeviceEventAfterReset:
  130. {
  131. break;
  132. }
  133. }
  134. }
  135. // forward declaration for plugin load event
  136. void PluginLoad(IUnityInterfaces* unityInterfaces);
  137. void PluginUnload();
  138. // Unity plugin load event
  139. //
  140. // "That is simply registering our UnityPluginLoad and UnityPluginUnload,
  141. // as on iOS we cannot use dynamic libraries (hence we cannot load functions
  142. // from them by name as we usually do on other platforms)."
  143. // https://github.com/Unity-Technologies/iOSNativeCodeSamples/blob/2019-dev/Graphics/MetalNativeRenderingPlugin/README.md
  144. //
  145. #if defined(UNITY_IOS) || defined(UNITY_IOS_SIMULATOR)
  146. extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityWebRTCPluginLoad(IUnityInterfaces* unityInterfaces)
  147. {
  148. PluginLoad(unityInterfaces);
  149. }
  150. extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityWebRTCPluginUnload() { PluginUnload(); }
  151. #else
  152. extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
  153. {
  154. PluginLoad(unityInterfaces);
  155. }
  156. extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload() { PluginUnload(); }
  157. #endif
  158. // Unity plugin load event
  159. void PluginLoad(IUnityInterfaces* unityInterfaces)
  160. {
  161. #if WIN32 && _DEBUG
  162. _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  163. #endif
  164. s_UnityInterfaces = unityInterfaces;
  165. s_Graphics = unityInterfaces->Get<IUnityGraphics>();
  166. s_Graphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
  167. s_clock.reset(Clock::GetRealTimeClock());
  168. #if defined(SUPPORT_VULKAN)
  169. /// note::
  170. /// Intercept Vulkan initialization process to hook vulkan functions because vulkan extensions need adding when
  171. /// initializing vulkan device. This process have to be run before graphics device initialization.
  172. auto vulkan = UnityGraphicsVulkan::Get(s_UnityInterfaces);
  173. if (!vulkan->AddInterceptInitialization(InterceptVulkanInitialization, nullptr, 0))
  174. {
  175. RTC_LOG(LS_INFO) << "AddInterceptInitialization failed.";
  176. }
  177. #endif
  178. s_UnityProfiler = UnityProfiler::Get(unityInterfaces);
  179. if (s_UnityProfiler)
  180. {
  181. s_ProfilerMarkerFactory = ProfilerMarkerFactory::Create(s_UnityProfiler.get());
  182. s_MarkerEncode = s_ProfilerMarkerFactory->CreateMarker(
  183. "UnityVideoTrackSource.OnFrameCaptured", kUnityProfilerCategoryRender, kUnityProfilerMarkerFlagDefault, 0);
  184. s_MarkerDecode = s_ProfilerMarkerFactory->CreateMarker(
  185. "UnityVideoRenderer.ConvertVideoFrameToTextureAndWriteToBuffer",
  186. kUnityProfilerCategoryRender,
  187. kUnityProfilerMarkerFlagDefault,
  188. 0);
  189. }
  190. OnGraphicsDeviceEvent(kUnityGfxDeviceEventInitialize);
  191. }
  192. void PluginUnload() { OnGraphicsDeviceEvent(kUnityGfxDeviceEventShutdown); }
  193. // Data format used by the managed code.
  194. // CommandBuffer.IssuePluginEventAndData method pass data packed by this format.
  195. struct EncodeData
  196. {
  197. void* texture;
  198. UnityVideoTrackSource* source;
  199. int width;
  200. int height;
  201. UnityRenderingExtTextureFormat format;
  202. };
  203. // Notice: When DebugLog is used in a method called from RenderingThread,
  204. // it hangs when attempting to leave PlayMode and re-enter PlayMode.
  205. // So, we comment out `DebugLog`.
  206. static void UNITY_INTERFACE_API OnRenderEvent(int eventID, void* data)
  207. {
  208. if (!s_context)
  209. return;
  210. if (!ContextManager::GetInstance()->Exists(s_context))
  211. return;
  212. std::unique_lock<std::mutex> lock(s_context->mutex, std::try_to_lock);
  213. if (!lock.owns_lock())
  214. return;
  215. EncodeData* encodeData = static_cast<EncodeData*>(data);
  216. RTC_DCHECK(encodeData->texture);
  217. RTC_DCHECK(encodeData->source);
  218. RTC_DCHECK_GT(encodeData->width, 0);
  219. RTC_DCHECK_GT(encodeData->height, 0);
  220. UnityVideoTrackSource* source = encodeData->source;
  221. if (!s_context->ExistsRefPtr(source))
  222. return;
  223. Timestamp timestamp = s_clock->CurrentTime();
  224. IGraphicsDevice* device = Plugin::GraphicsDevice();
  225. UnityGfxRenderer gfxRenderer = device->GetGfxRenderer();
  226. void* ptr = GraphicsUtility::TextureHandleToNativeGraphicsPtr(encodeData->texture, device, gfxRenderer);
  227. unity::webrtc::Size size(encodeData->width, encodeData->height);
  228. {
  229. std::unique_ptr<const ScopedProfiler> profiler;
  230. if (s_ProfilerMarkerFactory)
  231. profiler = s_ProfilerMarkerFactory->CreateScopedProfiler(*s_MarkerEncode);
  232. auto frame = s_bufferPool->CreateFrame(ptr, size, encodeData->format, timestamp);
  233. source->OnFrameCaptured(std::move(frame));
  234. }
  235. s_bufferPool->ReleaseStaleBuffers(timestamp);
  236. }
  237. static void UNITY_INTERFACE_API OnReleaseBuffers(int eventID, void* data)
  238. {
  239. // Release all buffers.
  240. if (s_bufferPool)
  241. s_bufferPool->ReleaseStaleBuffers(Timestamp::PlusInfinity());
  242. }
  243. extern "C" UnityRenderingEventAndData UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API GetRenderEventFunc(Context* context)
  244. {
  245. s_context = context;
  246. return OnRenderEvent;
  247. }
  248. extern "C" UnityRenderingEventAndData UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API GetReleaseBuffersFunc(Context* context)
  249. {
  250. return OnReleaseBuffers;
  251. }
  252. static void UNITY_INTERFACE_API TextureUpdateCallback(int eventID, void* data)
  253. {
  254. if (!s_context)
  255. return;
  256. if (!ContextManager::GetInstance()->Exists(s_context))
  257. return;
  258. std::unique_lock<std::mutex> lock(s_context->mutex, std::try_to_lock);
  259. if (!lock.owns_lock())
  260. return;
  261. auto event = static_cast<UnityRenderingExtEventType>(eventID);
  262. if (event == kUnityRenderingExtEventUpdateTextureBeginV2)
  263. {
  264. auto params = reinterpret_cast<UnityRenderingExtTextureUpdateParamsV2*>(data);
  265. auto renderer = s_context->GetVideoRenderer(params->userData);
  266. if (renderer == nullptr)
  267. return;
  268. s_mapVideoRenderer[params->userData] = renderer;
  269. int width = static_cast<int>(params->width);
  270. int height = static_cast<int>(params->height);
  271. {
  272. std::unique_ptr<const ScopedProfiler> profiler;
  273. if (s_ProfilerMarkerFactory)
  274. profiler = s_ProfilerMarkerFactory->CreateScopedProfiler(*s_MarkerDecode);
  275. params->texData = renderer->ConvertVideoFrameToTextureAndWriteToBuffer(
  276. width, height, ConvertTextureFormat(params->format));
  277. }
  278. }
  279. if (event == kUnityRenderingExtEventUpdateTextureEndV2)
  280. {
  281. auto params = reinterpret_cast<UnityRenderingExtTextureUpdateParamsV2*>(data);
  282. s_mapVideoRenderer.erase(params->userData);
  283. }
  284. }
  285. extern "C" UnityRenderingEventAndData UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API GetUpdateTextureFunc(Context* context)
  286. {
  287. s_context = context;
  288. return TextureUpdateCallback;
  289. }