gpu-delay.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. #include <obs-module.h>
  2. #include <util/circlebuf.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. uint64_t ts;
  9. };
  10. struct gpu_delay_filter_data {
  11. obs_source_t *context;
  12. struct circlebuf frames;
  13. uint64_t delay_ns;
  14. uint64_t interval_ns;
  15. uint32_t cx;
  16. uint32_t cy;
  17. bool target_valid;
  18. bool processed_frame;
  19. };
  20. static const char *gpu_delay_filter_get_name(void *unused)
  21. {
  22. UNUSED_PARAMETER(unused);
  23. return obs_module_text("GPUDelayFilter");
  24. }
  25. static void free_textures(struct gpu_delay_filter_data *f)
  26. {
  27. obs_enter_graphics();
  28. while (f->frames.size) {
  29. struct frame frame;
  30. circlebuf_pop_front(&f->frames, &frame, sizeof(frame));
  31. gs_texrender_destroy(frame.render);
  32. }
  33. circlebuf_free(&f->frames);
  34. obs_leave_graphics();
  35. }
  36. static size_t num_frames(struct circlebuf *buf)
  37. {
  38. return buf->size / sizeof(struct frame);
  39. }
  40. static void update_interval(struct gpu_delay_filter_data *f,
  41. 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. circlebuf_upsize(&f->frames, num * sizeof(struct frame));
  53. for (size_t i = prev_num; i < num; i++) {
  54. struct frame *frame =
  55. circlebuf_data(&f->frames, i * sizeof(*frame));
  56. frame->render =
  57. gs_texrender_create(GS_RGBA, GS_ZS_NONE);
  58. }
  59. obs_leave_graphics();
  60. } else if (num < num_frames(&f->frames)) {
  61. obs_enter_graphics();
  62. while (num_frames(&f->frames) > num) {
  63. struct frame frame;
  64. circlebuf_pop_front(&f->frames, &frame, sizeof(frame));
  65. gs_texrender_destroy(frame.render);
  66. }
  67. obs_leave_graphics();
  68. }
  69. }
  70. static inline void check_interval(struct gpu_delay_filter_data *f)
  71. {
  72. struct obs_video_info ovi = {0};
  73. uint64_t interval_ns;
  74. obs_get_video_info(&ovi);
  75. interval_ns = util_mul_div64(ovi.fps_den, 1000000000ULL, ovi.fps_num);
  76. if (interval_ns != f->interval_ns)
  77. update_interval(f, interval_ns);
  78. }
  79. static inline void reset_textures(struct gpu_delay_filter_data *f)
  80. {
  81. f->interval_ns = 0;
  82. free_textures(f);
  83. check_interval(f);
  84. }
  85. static inline bool check_size(struct gpu_delay_filter_data *f)
  86. {
  87. obs_source_t *target = obs_filter_get_target(f->context);
  88. uint32_t cx;
  89. uint32_t cy;
  90. f->target_valid = !!target;
  91. if (!f->target_valid)
  92. return true;
  93. cx = obs_source_get_base_width(target);
  94. cy = obs_source_get_base_height(target);
  95. f->target_valid = !!cx && !!cy;
  96. if (!f->target_valid)
  97. return true;
  98. if (cx != f->cx || cy != f->cy) {
  99. f->cx = cx;
  100. f->cy = cy;
  101. reset_textures(f);
  102. return true;
  103. }
  104. return false;
  105. }
  106. static void gpu_delay_filter_update(void *data, obs_data_t *s)
  107. {
  108. struct gpu_delay_filter_data *f = data;
  109. f->delay_ns = (uint64_t)obs_data_get_int(s, S_DELAY_MS) * 1000000ULL;
  110. /* full reset */
  111. f->cx = 0;
  112. f->cy = 0;
  113. f->interval_ns = 0;
  114. free_textures(f);
  115. }
  116. static obs_properties_t *gpu_delay_filter_properties(void *data)
  117. {
  118. obs_properties_t *props = obs_properties_create();
  119. obs_property_t *p = obs_properties_add_int(props, S_DELAY_MS,
  120. T_DELAY_MS, 0, 500, 1);
  121. obs_property_int_set_suffix(p, " ms");
  122. UNUSED_PARAMETER(data);
  123. return props;
  124. }
  125. static void *gpu_delay_filter_create(obs_data_t *settings,
  126. obs_source_t *context)
  127. {
  128. struct gpu_delay_filter_data *f = bzalloc(sizeof(*f));
  129. f->context = context;
  130. obs_source_update(context, settings);
  131. return f;
  132. }
  133. static void gpu_delay_filter_destroy(void *data)
  134. {
  135. struct gpu_delay_filter_data *f = data;
  136. free_textures(f);
  137. bfree(f);
  138. }
  139. static void gpu_delay_filter_tick(void *data, float t)
  140. {
  141. UNUSED_PARAMETER(t);
  142. struct gpu_delay_filter_data *f = data;
  143. f->processed_frame = false;
  144. if (check_size(f))
  145. return;
  146. check_interval(f);
  147. }
  148. static void draw_frame(struct gpu_delay_filter_data *f)
  149. {
  150. struct frame frame;
  151. circlebuf_peek_front(&f->frames, &frame, sizeof(frame));
  152. gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
  153. gs_texture_t *tex = gs_texrender_get_texture(frame.render);
  154. if (tex) {
  155. const bool linear_srgb = gs_get_linear_srgb();
  156. const bool previous = gs_framebuffer_srgb_enabled();
  157. gs_enable_framebuffer_srgb(linear_srgb);
  158. gs_eparam_t *image =
  159. gs_effect_get_param_by_name(effect, "image");
  160. if (linear_srgb)
  161. gs_effect_set_texture_srgb(image, tex);
  162. else
  163. gs_effect_set_texture(image, tex);
  164. while (gs_effect_loop(effect, "Draw"))
  165. gs_draw_sprite(tex, 0, f->cx, f->cy);
  166. gs_enable_framebuffer_srgb(previous);
  167. }
  168. }
  169. static void gpu_delay_filter_render(void *data, gs_effect_t *effect)
  170. {
  171. struct gpu_delay_filter_data *f = data;
  172. obs_source_t *target = obs_filter_get_target(f->context);
  173. obs_source_t *parent = obs_filter_get_parent(f->context);
  174. if (!f->target_valid || !target || !parent || !f->frames.size) {
  175. obs_source_skip_video_filter(f->context);
  176. return;
  177. }
  178. if (f->processed_frame) {
  179. draw_frame(f);
  180. return;
  181. }
  182. struct frame frame;
  183. circlebuf_pop_front(&f->frames, &frame, sizeof(frame));
  184. gs_texrender_reset(frame.render);
  185. gs_blend_state_push();
  186. gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
  187. if (gs_texrender_begin(frame.render, f->cx, f->cy)) {
  188. uint32_t parent_flags = obs_source_get_output_flags(target);
  189. bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
  190. bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0;
  191. struct vec4 clear_color;
  192. vec4_zero(&clear_color);
  193. gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);
  194. gs_ortho(0.0f, (float)f->cx, 0.0f, (float)f->cy, -100.0f,
  195. 100.0f);
  196. if (target == parent && !custom_draw && !async)
  197. obs_source_default_render(target);
  198. else
  199. obs_source_video_render(target);
  200. gs_texrender_end(frame.render);
  201. }
  202. gs_blend_state_pop();
  203. circlebuf_push_back(&f->frames, &frame, sizeof(frame));
  204. draw_frame(f);
  205. f->processed_frame = true;
  206. UNUSED_PARAMETER(effect);
  207. }
  208. struct obs_source_info gpu_delay_filter = {
  209. .id = "gpu_delay",
  210. .type = OBS_SOURCE_TYPE_FILTER,
  211. .output_flags = OBS_SOURCE_VIDEO,
  212. .get_name = gpu_delay_filter_get_name,
  213. .create = gpu_delay_filter_create,
  214. .destroy = gpu_delay_filter_destroy,
  215. .update = gpu_delay_filter_update,
  216. .get_properties = gpu_delay_filter_properties,
  217. .video_tick = gpu_delay_filter_tick,
  218. .video_render = gpu_delay_filter_render,
  219. };