|
|
@@ -7,6 +7,7 @@
|
|
|
|
|
|
struct frame {
|
|
|
gs_texrender_t *render;
|
|
|
+ enum gs_color_space space;
|
|
|
uint64_t ts;
|
|
|
};
|
|
|
|
|
|
@@ -187,27 +188,73 @@ static void gpu_delay_filter_tick(void *data, float t)
|
|
|
check_interval(f);
|
|
|
}
|
|
|
|
|
|
+static const char *
|
|
|
+get_tech_name_and_multiplier(enum gs_color_space current_space,
|
|
|
+ enum gs_color_space source_space,
|
|
|
+ float *multiplier)
|
|
|
+{
|
|
|
+ const char *tech_name = "Draw";
|
|
|
+ *multiplier = 1.f;
|
|
|
+
|
|
|
+ switch (source_space) {
|
|
|
+ case GS_CS_SRGB:
|
|
|
+ case GS_CS_SRGB_16F:
|
|
|
+ switch (current_space) {
|
|
|
+ case GS_CS_709_SCRGB:
|
|
|
+ tech_name = "DrawMultiply";
|
|
|
+ *multiplier = obs_get_video_sdr_white_level() / 80.0f;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GS_CS_709_EXTENDED:
|
|
|
+ switch (current_space) {
|
|
|
+ case GS_CS_SRGB:
|
|
|
+ case GS_CS_SRGB_16F:
|
|
|
+ tech_name = "DrawTonemap";
|
|
|
+ break;
|
|
|
+ case GS_CS_709_SCRGB:
|
|
|
+ tech_name = "DrawMultiply";
|
|
|
+ *multiplier = obs_get_video_sdr_white_level() / 80.0f;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GS_CS_709_SCRGB:
|
|
|
+ switch (current_space) {
|
|
|
+ case GS_CS_SRGB:
|
|
|
+ case GS_CS_SRGB_16F:
|
|
|
+ tech_name = "DrawMultiplyTonemap";
|
|
|
+ *multiplier = 80.0f / obs_get_video_sdr_white_level();
|
|
|
+ break;
|
|
|
+ case GS_CS_709_EXTENDED:
|
|
|
+ tech_name = "DrawMultiply";
|
|
|
+ *multiplier = 80.0f / obs_get_video_sdr_white_level();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return tech_name;
|
|
|
+}
|
|
|
+
|
|
|
static void draw_frame(struct gpu_delay_filter_data *f)
|
|
|
{
|
|
|
struct frame frame;
|
|
|
circlebuf_peek_front(&f->frames, &frame, sizeof(frame));
|
|
|
|
|
|
+ const enum gs_color_space current_space = gs_get_color_space();
|
|
|
+ float multiplier;
|
|
|
+ const char *technique = get_tech_name_and_multiplier(
|
|
|
+ current_space, frame.space, &multiplier);
|
|
|
+
|
|
|
gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
|
|
|
gs_texture_t *tex = gs_texrender_get_texture(frame.render);
|
|
|
if (tex) {
|
|
|
- const bool linear_srgb = gs_get_linear_srgb();
|
|
|
-
|
|
|
const bool previous = gs_framebuffer_srgb_enabled();
|
|
|
- gs_enable_framebuffer_srgb(linear_srgb);
|
|
|
+ gs_enable_framebuffer_srgb(true);
|
|
|
|
|
|
- gs_eparam_t *image =
|
|
|
- gs_effect_get_param_by_name(effect, "image");
|
|
|
- if (linear_srgb)
|
|
|
- gs_effect_set_texture_srgb(image, tex);
|
|
|
- else
|
|
|
- gs_effect_set_texture(image, tex);
|
|
|
+ gs_effect_set_texture_srgb(
|
|
|
+ gs_effect_get_param_by_name(effect, "image"), tex);
|
|
|
+ gs_effect_set_float(gs_effect_get_param_by_name(effect,
|
|
|
+ "multiplier"),
|
|
|
+ multiplier);
|
|
|
|
|
|
- while (gs_effect_loop(effect, "Draw"))
|
|
|
+ while (gs_effect_loop(effect, technique))
|
|
|
gs_draw_sprite(tex, 0, f->cx, f->cy);
|
|
|
|
|
|
gs_enable_framebuffer_srgb(previous);
|
|
|
@@ -233,12 +280,26 @@ static void gpu_delay_filter_render(void *data, gs_effect_t *effect)
|
|
|
struct frame frame;
|
|
|
circlebuf_pop_front(&f->frames, &frame, sizeof(frame));
|
|
|
|
|
|
+ const enum gs_color_space preferred_spaces[] = {
|
|
|
+ GS_CS_SRGB,
|
|
|
+ GS_CS_SRGB_16F,
|
|
|
+ GS_CS_709_EXTENDED,
|
|
|
+ };
|
|
|
+ const enum gs_color_space space = obs_source_get_color_space(
|
|
|
+ parent, OBS_COUNTOF(preferred_spaces), preferred_spaces);
|
|
|
+ const enum gs_color_format format = gs_get_format_from_space(space);
|
|
|
+ if (gs_texrender_get_format(frame.render) != format) {
|
|
|
+ gs_texrender_destroy(frame.render);
|
|
|
+ frame.render = gs_texrender_create(format, GS_ZS_NONE);
|
|
|
+ }
|
|
|
+
|
|
|
gs_texrender_reset(frame.render);
|
|
|
|
|
|
gs_blend_state_push();
|
|
|
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
|
|
|
|
|
- if (gs_texrender_begin(frame.render, f->cx, f->cy)) {
|
|
|
+ if (gs_texrender_begin_with_color_space(frame.render, f->cx, f->cy,
|
|
|
+ space)) {
|
|
|
uint32_t parent_flags = obs_source_get_output_flags(target);
|
|
|
bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
|
|
|
bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0;
|
|
|
@@ -255,6 +316,8 @@ static void gpu_delay_filter_render(void *data, gs_effect_t *effect)
|
|
|
obs_source_video_render(target);
|
|
|
|
|
|
gs_texrender_end(frame.render);
|
|
|
+
|
|
|
+ frame.space = space;
|
|
|
}
|
|
|
|
|
|
gs_blend_state_pop();
|
|
|
@@ -266,6 +329,30 @@ static void gpu_delay_filter_render(void *data, gs_effect_t *effect)
|
|
|
UNUSED_PARAMETER(effect);
|
|
|
}
|
|
|
|
|
|
+static enum gs_color_space
|
|
|
+gpu_delay_filter_get_color_space(void *data, size_t count,
|
|
|
+ const enum gs_color_space *preferred_spaces)
|
|
|
+{
|
|
|
+ struct gpu_delay_filter_data *const f = data;
|
|
|
+ obs_source_t *target = obs_filter_get_target(f->context);
|
|
|
+ obs_source_t *parent = obs_filter_get_parent(f->context);
|
|
|
+
|
|
|
+ if (!f->target_valid || !target || !parent || !f->frames.size) {
|
|
|
+ return (count > 0) ? preferred_spaces[0] : GS_CS_SRGB;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct frame frame;
|
|
|
+ circlebuf_peek_front(&f->frames, &frame, sizeof(frame));
|
|
|
+ enum gs_color_space space = frame.space;
|
|
|
+ for (size_t i = 0; i < count; ++i) {
|
|
|
+ space = preferred_spaces[i];
|
|
|
+ if (space == frame.space)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return space;
|
|
|
+}
|
|
|
+
|
|
|
struct obs_source_info gpu_delay_filter = {
|
|
|
.id = "gpu_delay",
|
|
|
.type = OBS_SOURCE_TYPE_FILTER,
|
|
|
@@ -277,4 +364,5 @@ struct obs_source_info gpu_delay_filter = {
|
|
|
.get_properties = gpu_delay_filter_properties,
|
|
|
.video_tick = gpu_delay_filter_tick,
|
|
|
.video_render = gpu_delay_filter_render,
|
|
|
+ .video_get_color_space = gpu_delay_filter_get_color_space,
|
|
|
};
|