gpu-delay.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. #include <obs-module.h>
  2. #include <util/deque.h>
  3. #include <util/util_uint64.h>
  4. #define S_DELAY_MS "delay_ms"
  5. #define T_DELAY_MS obs_module_text("DelayMs")
  6. struct frame {
  7. gs_texrender_t *render;
  8. enum gs_color_space space;
  9. uint64_t ts;
  10. };
  11. struct gpu_delay_filter_data {
  12. obs_source_t *context;
  13. struct deque frames;
  14. uint64_t delay_ns;
  15. uint64_t interval_ns;
  16. uint32_t cx;
  17. uint32_t cy;
  18. bool target_valid;
  19. bool processed_frame;
  20. };
  21. static const char *gpu_delay_filter_get_name(void *unused)
  22. {
  23. UNUSED_PARAMETER(unused);
  24. return obs_module_text("GPUDelayFilter");
  25. }
  26. static void free_textures(struct gpu_delay_filter_data *f)
  27. {
  28. obs_enter_graphics();
  29. while (f->frames.size) {
  30. struct frame frame;
  31. deque_pop_front(&f->frames, &frame, sizeof(frame));
  32. gs_texrender_destroy(frame.render);
  33. }
  34. deque_free(&f->frames);
  35. obs_leave_graphics();
  36. }
  37. static size_t num_frames(struct deque *buf)
  38. {
  39. return buf->size / sizeof(struct frame);
  40. }
  41. static void update_interval(struct gpu_delay_filter_data *f, uint64_t new_interval_ns)
  42. {
  43. if (!f->target_valid) {
  44. free_textures(f);
  45. return;
  46. }
  47. f->interval_ns = new_interval_ns;
  48. size_t num = (size_t)(f->delay_ns / new_interval_ns);
  49. if (num > num_frames(&f->frames)) {
  50. size_t prev_num = num_frames(&f->frames);
  51. obs_enter_graphics();
  52. deque_upsize(&f->frames, num * sizeof(struct frame));
  53. for (size_t i = prev_num; i < num; i++) {
  54. struct frame *frame = deque_data(&f->frames, i * sizeof(*frame));
  55. frame->render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
  56. }
  57. obs_leave_graphics();
  58. } else if (num < num_frames(&f->frames)) {
  59. obs_enter_graphics();
  60. while (num_frames(&f->frames) > num) {
  61. struct frame frame;
  62. deque_pop_front(&f->frames, &frame, sizeof(frame));
  63. gs_texrender_destroy(frame.render);
  64. }
  65. obs_leave_graphics();
  66. }
  67. }
  68. static inline void check_interval(struct gpu_delay_filter_data *f)
  69. {
  70. struct obs_video_info ovi = {0};
  71. uint64_t interval_ns;
  72. obs_get_video_info(&ovi);
  73. interval_ns = util_mul_div64(ovi.fps_den, 1000000000ULL, ovi.fps_num);
  74. if (interval_ns != f->interval_ns)
  75. update_interval(f, interval_ns);
  76. }
  77. static inline void reset_textures(struct gpu_delay_filter_data *f)
  78. {
  79. f->interval_ns = 0;
  80. free_textures(f);
  81. check_interval(f);
  82. }
  83. static inline bool check_size(struct gpu_delay_filter_data *f)
  84. {
  85. obs_source_t *target = obs_filter_get_target(f->context);
  86. uint32_t cx;
  87. uint32_t cy;
  88. f->target_valid = !!target;
  89. if (!f->target_valid)
  90. return true;
  91. cx = obs_source_get_base_width(target);
  92. cy = obs_source_get_base_height(target);
  93. f->target_valid = !!cx && !!cy;
  94. if (!f->target_valid)
  95. return true;
  96. if (cx != f->cx || cy != f->cy) {
  97. f->cx = cx;
  98. f->cy = cy;
  99. reset_textures(f);
  100. return true;
  101. }
  102. return false;
  103. }
  104. static void gpu_delay_filter_update(void *data, obs_data_t *s)
  105. {
  106. struct gpu_delay_filter_data *f = data;
  107. f->delay_ns = (uint64_t)obs_data_get_int(s, S_DELAY_MS) * 1000000ULL;
  108. /* full reset */
  109. f->cx = 0;
  110. f->cy = 0;
  111. f->interval_ns = 0;
  112. free_textures(f);
  113. }
  114. static obs_properties_t *gpu_delay_filter_properties(void *data)
  115. {
  116. obs_properties_t *props = obs_properties_create();
  117. obs_property_t *p = obs_properties_add_int(props, S_DELAY_MS, T_DELAY_MS, 0, 500, 1);
  118. obs_property_int_set_suffix(p, " ms");
  119. UNUSED_PARAMETER(data);
  120. return props;
  121. }
  122. static void *gpu_delay_filter_create(obs_data_t *settings, obs_source_t *context)
  123. {
  124. struct gpu_delay_filter_data *f = bzalloc(sizeof(*f));
  125. f->context = context;
  126. obs_source_update(context, settings);
  127. return f;
  128. }
  129. static void gpu_delay_filter_destroy(void *data)
  130. {
  131. struct gpu_delay_filter_data *f = data;
  132. free_textures(f);
  133. bfree(f);
  134. }
  135. static void gpu_delay_filter_tick(void *data, float t)
  136. {
  137. UNUSED_PARAMETER(t);
  138. struct gpu_delay_filter_data *f = data;
  139. f->processed_frame = false;
  140. if (check_size(f))
  141. return;
  142. check_interval(f);
  143. }
  144. static const char *get_tech_name_and_multiplier(enum gs_color_space current_space, enum gs_color_space source_space,
  145. float *multiplier)
  146. {
  147. const char *tech_name = "Draw";
  148. *multiplier = 1.f;
  149. switch (source_space) {
  150. case GS_CS_SRGB:
  151. case GS_CS_SRGB_16F:
  152. if (current_space == GS_CS_709_SCRGB) {
  153. tech_name = "DrawMultiply";
  154. *multiplier = obs_get_video_sdr_white_level() / 80.0f;
  155. }
  156. break;
  157. case GS_CS_709_EXTENDED:
  158. switch (current_space) {
  159. case GS_CS_SRGB:
  160. case GS_CS_SRGB_16F:
  161. tech_name = "DrawTonemap";
  162. break;
  163. case GS_CS_709_SCRGB:
  164. tech_name = "DrawMultiply";
  165. *multiplier = obs_get_video_sdr_white_level() / 80.0f;
  166. break;
  167. default:
  168. break;
  169. }
  170. break;
  171. case GS_CS_709_SCRGB:
  172. switch (current_space) {
  173. case GS_CS_SRGB:
  174. case GS_CS_SRGB_16F:
  175. tech_name = "DrawMultiplyTonemap";
  176. *multiplier = 80.0f / obs_get_video_sdr_white_level();
  177. break;
  178. case GS_CS_709_EXTENDED:
  179. tech_name = "DrawMultiply";
  180. *multiplier = 80.0f / obs_get_video_sdr_white_level();
  181. break;
  182. default:
  183. break;
  184. }
  185. }
  186. return tech_name;
  187. }
  188. static void draw_frame(struct gpu_delay_filter_data *f)
  189. {
  190. struct frame frame;
  191. deque_peek_front(&f->frames, &frame, sizeof(frame));
  192. const enum gs_color_space current_space = gs_get_color_space();
  193. float multiplier;
  194. const char *technique = get_tech_name_and_multiplier(current_space, frame.space, &multiplier);
  195. gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
  196. gs_texture_t *tex = gs_texrender_get_texture(frame.render);
  197. if (tex) {
  198. const bool previous = gs_framebuffer_srgb_enabled();
  199. gs_enable_framebuffer_srgb(true);
  200. gs_effect_set_texture_srgb(gs_effect_get_param_by_name(effect, "image"), tex);
  201. gs_effect_set_float(gs_effect_get_param_by_name(effect, "multiplier"), multiplier);
  202. while (gs_effect_loop(effect, technique))
  203. gs_draw_sprite(tex, 0, f->cx, f->cy);
  204. gs_enable_framebuffer_srgb(previous);
  205. }
  206. }
  207. static void gpu_delay_filter_render(void *data, gs_effect_t *effect)
  208. {
  209. struct gpu_delay_filter_data *f = data;
  210. obs_source_t *target = obs_filter_get_target(f->context);
  211. obs_source_t *parent = obs_filter_get_parent(f->context);
  212. if (!f->target_valid || !target || !parent || !f->frames.size) {
  213. obs_source_skip_video_filter(f->context);
  214. return;
  215. }
  216. if (f->processed_frame) {
  217. draw_frame(f);
  218. return;
  219. }
  220. struct frame frame;
  221. deque_pop_front(&f->frames, &frame, sizeof(frame));
  222. const enum gs_color_space preferred_spaces[] = {
  223. GS_CS_SRGB,
  224. GS_CS_SRGB_16F,
  225. GS_CS_709_EXTENDED,
  226. };
  227. const enum gs_color_space space =
  228. obs_source_get_color_space(target, OBS_COUNTOF(preferred_spaces), preferred_spaces);
  229. const enum gs_color_format format = gs_get_format_from_space(space);
  230. if (gs_texrender_get_format(frame.render) != format) {
  231. gs_texrender_destroy(frame.render);
  232. frame.render = gs_texrender_create(format, GS_ZS_NONE);
  233. }
  234. gs_texrender_reset(frame.render);
  235. gs_blend_state_push();
  236. gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
  237. if (gs_texrender_begin_with_color_space(frame.render, f->cx, f->cy, space)) {
  238. uint32_t parent_flags = obs_source_get_output_flags(target);
  239. bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
  240. bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0;
  241. struct vec4 clear_color;
  242. vec4_zero(&clear_color);
  243. gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);
  244. gs_ortho(0.0f, (float)f->cx, 0.0f, (float)f->cy, -100.0f, 100.0f);
  245. if (target == parent && !custom_draw && !async)
  246. obs_source_default_render(target);
  247. else
  248. obs_source_video_render(target);
  249. gs_texrender_end(frame.render);
  250. frame.space = space;
  251. }
  252. gs_blend_state_pop();
  253. deque_push_back(&f->frames, &frame, sizeof(frame));
  254. draw_frame(f);
  255. f->processed_frame = true;
  256. UNUSED_PARAMETER(effect);
  257. }
  258. static enum gs_color_space gpu_delay_filter_get_color_space(void *data, size_t count,
  259. const enum gs_color_space *preferred_spaces)
  260. {
  261. struct gpu_delay_filter_data *const f = data;
  262. obs_source_t *target = obs_filter_get_target(f->context);
  263. obs_source_t *parent = obs_filter_get_parent(f->context);
  264. if (!f->target_valid || !target || !parent || !f->frames.size) {
  265. return (count > 0) ? preferred_spaces[0] : GS_CS_SRGB;
  266. }
  267. struct frame frame;
  268. deque_peek_front(&f->frames, &frame, sizeof(frame));
  269. enum gs_color_space space = frame.space;
  270. for (size_t i = 0; i < count; ++i) {
  271. space = preferred_spaces[i];
  272. if (space == frame.space)
  273. break;
  274. }
  275. return space;
  276. }
  277. struct obs_source_info gpu_delay_filter = {
  278. .id = "gpu_delay",
  279. .type = OBS_SOURCE_TYPE_FILTER,
  280. .output_flags = OBS_SOURCE_VIDEO,
  281. .get_name = gpu_delay_filter_get_name,
  282. .create = gpu_delay_filter_create,
  283. .destroy = gpu_delay_filter_destroy,
  284. .update = gpu_delay_filter_update,
  285. .get_properties = gpu_delay_filter_properties,
  286. .video_tick = gpu_delay_filter_tick,
  287. .video_render = gpu_delay_filter_render,
  288. .video_get_color_space = gpu_delay_filter_get_color_space,
  289. };