mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-07-04 10:33:30 +00:00
Merge pull request #4950 from jpark37/manual-srgb-filtering
Manual SRGB filtering
This commit is contained in:
commit
4fad39cdc4
|
@ -847,6 +847,14 @@ General Source Functions
|
|||
|
||||
---------------------
|
||||
|
||||
.. function:: bool obs_source_get_texcoords_centered(obs_source_t *source)
|
||||
|
||||
Hints whether or not the source will blend texels.
|
||||
|
||||
:return: Whether or not the source will blend texels
|
||||
|
||||
---------------------
|
||||
|
||||
.. function:: obs_data_t *obs_source_get_settings(const obs_source_t *source)
|
||||
|
||||
:return: The settings string for a source. The reference counter of the
|
||||
|
|
|
@ -218,9 +218,12 @@ static inline void copy_texture(gs_duplicator_t *d, ID3D11Texture2D *tex)
|
|||
d->texture->height != desc.Height) {
|
||||
|
||||
delete d->texture;
|
||||
const gs_color_format format =
|
||||
ConvertDXGITextureFormat(desc.Format);
|
||||
const gs_color_format srgb_format =
|
||||
gs_generalize_format(format);
|
||||
d->texture = (gs_texture_2d *)gs_texture_create(
|
||||
desc.Width, desc.Height,
|
||||
ConvertDXGITextureFormat(desc.Format), 1, nullptr, 0);
|
||||
desc.Width, desc.Height, srgb_format, 1, nullptr, 0);
|
||||
}
|
||||
|
||||
if (!!d->texture)
|
||||
|
|
|
@ -1768,6 +1768,20 @@ inline void gs_device::CopyTex(ID3D11Texture2D *dst, uint32_t dst_x,
|
|||
}
|
||||
}
|
||||
|
||||
static DXGI_FORMAT get_copy_compare_format(gs_color_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case GS_RGBA_UNORM:
|
||||
return DXGI_FORMAT_R8G8B8A8_TYPELESS;
|
||||
case GS_BGRX_UNORM:
|
||||
return DXGI_FORMAT_B8G8R8X8_TYPELESS;
|
||||
case GS_BGRA_UNORM:
|
||||
return DXGI_FORMAT_B8G8R8A8_TYPELESS;
|
||||
default:
|
||||
return ConvertGSTextureFormatResource(format);
|
||||
}
|
||||
}
|
||||
|
||||
void device_copy_texture_region(gs_device_t *device, gs_texture_t *dst,
|
||||
uint32_t dst_x, uint32_t dst_y,
|
||||
gs_texture_t *src, uint32_t src_x,
|
||||
|
@ -1784,7 +1798,8 @@ void device_copy_texture_region(gs_device_t *device, gs_texture_t *dst,
|
|||
if (src->type != GS_TEXTURE_2D || dst->type != GS_TEXTURE_2D)
|
||||
throw "Source and destination textures must be a 2D "
|
||||
"textures";
|
||||
if (dst->format != src->format)
|
||||
if (get_copy_compare_format(dst->format) !=
|
||||
get_copy_compare_format(src->format))
|
||||
throw "Source and destination formats do not match";
|
||||
|
||||
/* apparently casting to the same type that the variable
|
||||
|
|
|
@ -150,7 +150,7 @@ static inline GLenum get_gl_format_type(enum gs_color_format format)
|
|||
case GS_BGRA:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
case GS_R10G10B10A2:
|
||||
return GL_UNSIGNED_INT_10_10_10_2;
|
||||
return GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
case GS_RGBA16:
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case GS_R16:
|
||||
|
|
|
@ -564,24 +564,20 @@ extern "C" EXPORT void winrt_capture_free(struct winrt_capture *capture)
|
|||
}
|
||||
}
|
||||
|
||||
static void draw_texture(struct winrt_capture *capture, gs_effect_t *effect)
|
||||
static void draw_texture(struct winrt_capture *capture)
|
||||
{
|
||||
gs_texture_t *const texture = capture->texture;
|
||||
gs_effect_t *const effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
|
||||
gs_technique_t *tech = gs_effect_get_technique(effect, "Draw");
|
||||
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
|
||||
size_t passes;
|
||||
|
||||
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_enable_blending(false);
|
||||
|
||||
if (linear_srgb)
|
||||
gs_effect_set_texture_srgb(image, texture);
|
||||
else
|
||||
gs_effect_set_texture(image, texture);
|
||||
gs_texture_t *const texture = capture->texture;
|
||||
gs_effect_set_texture_srgb(image, texture);
|
||||
|
||||
passes = gs_technique_begin(tech);
|
||||
const size_t passes = gs_technique_begin(tech);
|
||||
for (size_t i = 0; i < passes; i++) {
|
||||
if (gs_technique_begin_pass(tech, i)) {
|
||||
gs_draw_sprite(texture, 0, 0, 0);
|
||||
|
@ -591,6 +587,7 @@ static void draw_texture(struct winrt_capture *capture, gs_effect_t *effect)
|
|||
}
|
||||
gs_technique_end(tech);
|
||||
|
||||
gs_enable_blending(true);
|
||||
gs_enable_framebuffer_srgb(previous);
|
||||
}
|
||||
|
||||
|
@ -627,11 +624,10 @@ extern "C" EXPORT BOOL winrt_capture_show_cursor(struct winrt_capture *capture,
|
|||
return success;
|
||||
}
|
||||
|
||||
extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture,
|
||||
gs_effect_t *effect)
|
||||
extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture)
|
||||
{
|
||||
if (capture->texture_written)
|
||||
draw_texture(capture, effect);
|
||||
draw_texture(capture);
|
||||
}
|
||||
|
||||
extern "C" EXPORT uint32_t
|
||||
|
|
|
@ -20,8 +20,7 @@ EXPORT void winrt_capture_free(struct winrt_capture *capture);
|
|||
EXPORT BOOL winrt_capture_active(const struct winrt_capture *capture);
|
||||
EXPORT BOOL winrt_capture_show_cursor(struct winrt_capture *capture,
|
||||
BOOL visible);
|
||||
EXPORT void winrt_capture_render(struct winrt_capture *capture,
|
||||
gs_effect_t *effect);
|
||||
EXPORT void winrt_capture_render(struct winrt_capture *capture);
|
||||
EXPORT uint32_t winrt_capture_width(const struct winrt_capture *capture);
|
||||
EXPORT uint32_t winrt_capture_height(const struct winrt_capture *capture);
|
||||
|
||||
|
|
|
@ -62,6 +62,13 @@ float4 PSDrawNonlinearAlpha(VertInOut vert_in) : TARGET
|
|||
return rgba;
|
||||
}
|
||||
|
||||
float4 PSDrawSrgbDecompress(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float4 rgba = image.Sample(def_sampler, vert_in.uv);
|
||||
rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
|
||||
return rgba;
|
||||
}
|
||||
|
||||
float4 PSDrawSrgbDecompressPremultiplied(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float4 rgba = image.Sample(def_sampler, vert_in.uv);
|
||||
|
@ -97,6 +104,15 @@ technique DrawNonlinearAlpha
|
|||
}
|
||||
}
|
||||
|
||||
technique DrawSrgbDecompress
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSDrawSrgbDecompress(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique DrawSrgbDecompressPremultiplied
|
||||
{
|
||||
pass
|
||||
|
|
|
@ -1005,6 +1005,24 @@ static inline bool gs_is_srgb_format(enum gs_color_format format)
|
|||
}
|
||||
}
|
||||
|
||||
static inline enum gs_color_format
|
||||
gs_generalize_format(enum gs_color_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case GS_RGBA_UNORM:
|
||||
format = GS_RGBA;
|
||||
break;
|
||||
case GS_BGRX_UNORM:
|
||||
format = GS_BGRX;
|
||||
break;
|
||||
case GS_BGRA_UNORM:
|
||||
format = GS_BGRA;
|
||||
default:;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
static inline uint32_t gs_get_total_levels(uint32_t width, uint32_t height,
|
||||
uint32_t depth)
|
||||
{
|
||||
|
|
|
@ -630,6 +630,9 @@ struct obs_source {
|
|||
/* used to temporarily disable sources if needed */
|
||||
bool enabled;
|
||||
|
||||
/* hint to allow sources to render more quickly */
|
||||
bool texcoords_centered;
|
||||
|
||||
/* timing (if video is present, is based upon video) */
|
||||
volatile bool timing_set;
|
||||
volatile uint64_t timing_adjust;
|
||||
|
@ -847,6 +850,8 @@ convert_video_format(enum video_format format)
|
|||
}
|
||||
}
|
||||
|
||||
extern void obs_source_set_texcoords_centered(obs_source_t *source,
|
||||
bool centered);
|
||||
extern void obs_source_activate(obs_source_t *source, enum view_type type);
|
||||
extern void obs_source_deactivate(obs_source_t *source, enum view_type type);
|
||||
extern void obs_source_video_tick(obs_source_t *source, float seconds);
|
||||
|
|
|
@ -561,6 +561,20 @@ static void render_item_texture(struct obs_scene_item *item)
|
|||
GS_DEBUG_MARKER_END();
|
||||
}
|
||||
|
||||
static bool are_texcoords_centered(struct matrix4 *m)
|
||||
{
|
||||
static const struct matrix4 identity = {
|
||||
{1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 1.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 1.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f},
|
||||
};
|
||||
struct matrix4 copy = identity;
|
||||
copy.t.x = floorf(m->t.x);
|
||||
copy.t.y = floorf(m->t.y);
|
||||
return memcmp(m, ©, sizeof(*m)) == 0;
|
||||
}
|
||||
|
||||
static inline void render_item(struct obs_scene_item *item)
|
||||
{
|
||||
GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_ITEM, "Item: %s",
|
||||
|
@ -610,7 +624,11 @@ static inline void render_item(struct obs_scene_item *item)
|
|||
cx, cy);
|
||||
obs_source_video_render(item->hide_transition);
|
||||
} else {
|
||||
obs_source_set_texcoords_centered(item->source,
|
||||
true);
|
||||
obs_source_video_render(item->source);
|
||||
obs_source_set_texcoords_centered(item->source,
|
||||
false);
|
||||
}
|
||||
|
||||
gs_texrender_end(item->item_render);
|
||||
|
@ -635,7 +653,11 @@ static inline void render_item(struct obs_scene_item *item)
|
|||
obs_transition_set_size(item->hide_transition, cx, cy);
|
||||
obs_source_video_render(item->hide_transition);
|
||||
} else {
|
||||
const bool centered =
|
||||
are_texcoords_centered(&item->draw_transform);
|
||||
obs_source_set_texcoords_centered(item->source, centered);
|
||||
obs_source_video_render(item->source);
|
||||
obs_source_set_texcoords_centered(item->source, false);
|
||||
}
|
||||
gs_matrix_pop();
|
||||
gs_set_linear_srgb(previous);
|
||||
|
|
|
@ -1030,6 +1030,16 @@ void obs_source_send_key_click(obs_source_t *source,
|
|||
}
|
||||
}
|
||||
|
||||
bool obs_source_get_texcoords_centered(obs_source_t *source)
|
||||
{
|
||||
return source->texcoords_centered;
|
||||
}
|
||||
|
||||
void obs_source_set_texcoords_centered(obs_source_t *source, bool centered)
|
||||
{
|
||||
source->texcoords_centered = centered;
|
||||
}
|
||||
|
||||
static void activate_source(obs_source_t *source)
|
||||
{
|
||||
if (source->context.data && source->info.activate)
|
||||
|
|
|
@ -962,6 +962,9 @@ EXPORT uint32_t obs_source_get_width(obs_source_t *source);
|
|||
/** Gets the height of a source (if it has video) */
|
||||
EXPORT uint32_t obs_source_get_height(obs_source_t *source);
|
||||
|
||||
/** Hints whether or not the source will blend texels */
|
||||
EXPORT bool obs_source_get_texcoords_centered(obs_source_t *source);
|
||||
|
||||
/**
|
||||
* If the source is a filter, returns the parent source of the filter. Only
|
||||
* guaranteed to be valid inside of the video_render, filter_audio,
|
||||
|
|
|
@ -5,14 +5,27 @@
|
|||
|
||||
static inline void init_textures(struct dc_capture *capture)
|
||||
{
|
||||
if (capture->compatibility)
|
||||
if (capture->compatibility) {
|
||||
capture->texture = gs_texture_create(capture->width,
|
||||
capture->height, GS_BGRA,
|
||||
1, NULL, GS_DYNAMIC);
|
||||
else
|
||||
} else {
|
||||
capture->texture =
|
||||
gs_texture_create_gdi(capture->width, capture->height);
|
||||
|
||||
if (capture->texture) {
|
||||
capture->extra_texture = gs_texture_create(
|
||||
capture->width, capture->height, GS_BGRA, 1,
|
||||
NULL, 0);
|
||||
if (!capture->extra_texture) {
|
||||
blog(LOG_WARNING, "[dc_capture_init] Failed to "
|
||||
"create textures");
|
||||
gs_texture_destroy(capture->texture);
|
||||
capture->texture = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!capture->texture) {
|
||||
blog(LOG_WARNING, "[dc_capture_init] Failed to "
|
||||
"create textures");
|
||||
|
@ -73,6 +86,7 @@ void dc_capture_free(struct dc_capture *capture)
|
|||
}
|
||||
|
||||
obs_enter_graphics();
|
||||
gs_texture_destroy(capture->extra_texture);
|
||||
gs_texture_destroy(capture->texture);
|
||||
obs_leave_graphics();
|
||||
|
||||
|
@ -165,41 +179,48 @@ void dc_capture_capture(struct dc_capture *capture, HWND window)
|
|||
capture->texture_written = true;
|
||||
}
|
||||
|
||||
static void draw_texture(struct dc_capture *capture, gs_effect_t *effect)
|
||||
static void draw_texture(struct dc_capture *capture, bool texcoords_centered)
|
||||
{
|
||||
gs_texture_t *texture = capture->texture;
|
||||
gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
|
||||
gs_technique_t *tech = gs_effect_get_technique(effect, "Draw");
|
||||
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
|
||||
size_t passes;
|
||||
|
||||
const bool linear_srgb = gs_get_linear_srgb() && capture->compatibility;
|
||||
gs_texture_t *texture = capture->texture;
|
||||
const bool compatibility = capture->compatibility;
|
||||
bool linear_sample = compatibility;
|
||||
if (!linear_sample && !texcoords_centered) {
|
||||
gs_texture_t *const extra_texture = capture->extra_texture;
|
||||
gs_copy_texture(extra_texture, texture);
|
||||
texture = extra_texture;
|
||||
linear_sample = true;
|
||||
}
|
||||
|
||||
const bool previous = gs_framebuffer_srgb_enabled();
|
||||
gs_enable_framebuffer_srgb(linear_srgb);
|
||||
gs_enable_framebuffer_srgb(linear_sample);
|
||||
gs_enable_blending(false);
|
||||
|
||||
if (linear_srgb)
|
||||
if (linear_sample)
|
||||
gs_effect_set_texture_srgb(image, texture);
|
||||
else
|
||||
gs_effect_set_texture(image, texture);
|
||||
|
||||
passes = gs_technique_begin(tech);
|
||||
const uint32_t flip = compatibility ? GS_FLIP_V : 0;
|
||||
const size_t passes = gs_technique_begin(tech);
|
||||
for (size_t i = 0; i < passes; i++) {
|
||||
if (gs_technique_begin_pass(tech, i)) {
|
||||
if (capture->compatibility)
|
||||
gs_draw_sprite(texture, GS_FLIP_V, 0, 0);
|
||||
else
|
||||
gs_draw_sprite(texture, 0, 0, 0);
|
||||
gs_draw_sprite(texture, flip, 0, 0);
|
||||
|
||||
gs_technique_end_pass(tech);
|
||||
}
|
||||
}
|
||||
gs_technique_end(tech);
|
||||
|
||||
gs_enable_blending(true);
|
||||
gs_enable_framebuffer_srgb(previous);
|
||||
}
|
||||
|
||||
void dc_capture_render(struct dc_capture *capture, gs_effect_t *effect)
|
||||
void dc_capture_render(struct dc_capture *capture, bool texcoords_centered)
|
||||
{
|
||||
if (capture->valid && capture->texture_written)
|
||||
draw_texture(capture, effect);
|
||||
draw_texture(capture, texcoords_centered);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
struct dc_capture {
|
||||
gs_texture_t *texture;
|
||||
gs_texture_t *extra_texture;
|
||||
bool texture_written;
|
||||
int x, y;
|
||||
uint32_t width;
|
||||
|
@ -31,4 +32,5 @@ extern void dc_capture_init(struct dc_capture *capture, int x, int y,
|
|||
extern void dc_capture_free(struct dc_capture *capture);
|
||||
|
||||
extern void dc_capture_capture(struct dc_capture *capture, HWND window);
|
||||
extern void dc_capture_render(struct dc_capture *capture, gs_effect_t *effect);
|
||||
extern void dc_capture_render(struct dc_capture *capture,
|
||||
bool texcoords_centered);
|
||||
|
|
|
@ -39,8 +39,7 @@ typedef struct winrt_capture *(*PFN_winrt_capture_init_monitor)(
|
|||
typedef void (*PFN_winrt_capture_free)(struct winrt_capture *capture);
|
||||
|
||||
typedef BOOL (*PFN_winrt_capture_active)(const struct winrt_capture *capture);
|
||||
typedef void (*PFN_winrt_capture_render)(struct winrt_capture *capture,
|
||||
gs_effect_t *effect);
|
||||
typedef void (*PFN_winrt_capture_render)(struct winrt_capture *capture);
|
||||
typedef uint32_t (*PFN_winrt_capture_width)(const struct winrt_capture *capture);
|
||||
typedef uint32_t (*PFN_winrt_capture_height)(
|
||||
const struct winrt_capture *capture);
|
||||
|
@ -488,18 +487,18 @@ static void draw_cursor(struct duplicator_capture *capture)
|
|||
capture->rot % 180 == 0 ? capture->height : capture->width);
|
||||
}
|
||||
|
||||
static void duplicator_capture_render(void *data, gs_effect_t *effect)
|
||||
static void duplicator_capture_render(void *data, gs_effect_t *unused)
|
||||
{
|
||||
UNUSED_PARAMETER(unused);
|
||||
|
||||
struct duplicator_capture *capture = data;
|
||||
|
||||
if (capture->method == METHOD_WGC) {
|
||||
gs_effect_t *const opaque =
|
||||
obs_get_base_effect(OBS_EFFECT_OPAQUE);
|
||||
if (capture->capture_winrt) {
|
||||
if (capture->exports.winrt_capture_active(
|
||||
capture->capture_winrt)) {
|
||||
capture->exports.winrt_capture_render(
|
||||
capture->capture_winrt, opaque);
|
||||
capture->capture_winrt);
|
||||
} else {
|
||||
capture->exports.winrt_capture_free(
|
||||
capture->capture_winrt);
|
||||
|
@ -507,54 +506,62 @@ static void duplicator_capture_render(void *data, gs_effect_t *effect)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
gs_texture_t *texture;
|
||||
int rot;
|
||||
|
||||
if (!capture->duplicator)
|
||||
return;
|
||||
|
||||
texture = gs_duplicator_get_texture(capture->duplicator);
|
||||
gs_texture_t *const texture =
|
||||
gs_duplicator_get_texture(capture->duplicator);
|
||||
if (!texture)
|
||||
return;
|
||||
|
||||
effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
|
||||
const bool previous = gs_framebuffer_srgb_enabled();
|
||||
gs_enable_framebuffer_srgb(true);
|
||||
gs_enable_blending(false);
|
||||
|
||||
rot = capture->rot;
|
||||
const int rot = capture->rot;
|
||||
if (rot != 0) {
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
|
||||
while (gs_effect_loop(effect, "Draw")) {
|
||||
if (rot != 0) {
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
|
||||
switch (rot) {
|
||||
case 90:
|
||||
x = (float)capture->height;
|
||||
break;
|
||||
case 180:
|
||||
x = (float)capture->width;
|
||||
y = (float)capture->height;
|
||||
break;
|
||||
case 270:
|
||||
y = (float)capture->width;
|
||||
break;
|
||||
}
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(x, y, 0.0f);
|
||||
gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f,
|
||||
RAD((float)rot));
|
||||
switch (rot) {
|
||||
case 90:
|
||||
x = (float)capture->height;
|
||||
break;
|
||||
case 180:
|
||||
x = (float)capture->width;
|
||||
y = (float)capture->height;
|
||||
break;
|
||||
case 270:
|
||||
y = (float)capture->width;
|
||||
break;
|
||||
}
|
||||
|
||||
obs_source_draw(texture, 0, 0, 0, 0, false);
|
||||
|
||||
if (rot != 0)
|
||||
gs_matrix_pop();
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(x, y, 0.0f);
|
||||
gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD((float)rot));
|
||||
}
|
||||
|
||||
if (capture->capture_cursor) {
|
||||
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
|
||||
gs_effect_t *const opaque_effect =
|
||||
obs_get_base_effect(OBS_EFFECT_OPAQUE);
|
||||
while (gs_effect_loop(opaque_effect, "Draw")) {
|
||||
gs_eparam_t *image = gs_effect_get_param_by_name(
|
||||
opaque_effect, "image");
|
||||
gs_effect_set_texture_srgb(image, texture);
|
||||
|
||||
while (gs_effect_loop(effect, "Draw")) {
|
||||
gs_draw_sprite(texture, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (rot != 0)
|
||||
gs_matrix_pop();
|
||||
|
||||
gs_enable_blending(true);
|
||||
gs_enable_framebuffer_srgb(previous);
|
||||
|
||||
if (capture->capture_cursor) {
|
||||
gs_effect_t *const default_effect =
|
||||
obs_get_base_effect(OBS_EFFECT_DEFAULT);
|
||||
|
||||
while (gs_effect_loop(default_effect, "Draw")) {
|
||||
draw_cursor(capture);
|
||||
}
|
||||
}
|
||||
|
@ -670,7 +677,7 @@ struct obs_source_info duplicator_capture_info = {
|
|||
.id = "monitor_capture",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE,
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE | OBS_SOURCE_SRGB,
|
||||
.get_name = duplicator_capture_getname,
|
||||
.create = duplicator_capture_create,
|
||||
.destroy = duplicator_capture_destroy,
|
||||
|
|
|
@ -149,7 +149,9 @@ struct game_capture {
|
|||
|
||||
ipc_pipe_server_t pipe;
|
||||
gs_texture_t *texture;
|
||||
bool supports_srgb;
|
||||
gs_texture_t *extra_texture;
|
||||
gs_texrender_t *extra_texrender;
|
||||
bool linear_sample;
|
||||
struct hook_info *global_hook_info;
|
||||
HANDLE keepalive_mutex;
|
||||
HANDLE hook_init;
|
||||
|
@ -332,12 +334,14 @@ static void stop_capture(struct game_capture *gc)
|
|||
close_handle(&gc->texture_mutexes[0]);
|
||||
close_handle(&gc->texture_mutexes[1]);
|
||||
|
||||
if (gc->texture) {
|
||||
obs_enter_graphics();
|
||||
gs_texture_destroy(gc->texture);
|
||||
obs_leave_graphics();
|
||||
gc->texture = NULL;
|
||||
}
|
||||
obs_enter_graphics();
|
||||
gs_texrender_destroy(gc->extra_texrender);
|
||||
gs_texture_destroy(gc->extra_texture);
|
||||
gs_texture_destroy(gc->texture);
|
||||
obs_leave_graphics();
|
||||
gc->extra_texrender = NULL;
|
||||
gc->extra_texture = NULL;
|
||||
gc->texture = NULL;
|
||||
|
||||
if (gc->active)
|
||||
info("capture stopped");
|
||||
|
@ -1552,49 +1556,104 @@ static inline bool is_16bit_format(uint32_t format)
|
|||
|
||||
static inline bool init_shmem_capture(struct game_capture *gc)
|
||||
{
|
||||
enum gs_color_format format;
|
||||
|
||||
gc->texture_buffers[0] =
|
||||
(uint8_t *)gc->data + gc->shmem_data->tex1_offset;
|
||||
gc->texture_buffers[1] =
|
||||
(uint8_t *)gc->data + gc->shmem_data->tex2_offset;
|
||||
|
||||
gc->convert_16bit = is_16bit_format(gc->global_hook_info->format);
|
||||
format = gc->convert_16bit
|
||||
? GS_BGRA
|
||||
: convert_format(gc->global_hook_info->format);
|
||||
const uint32_t dxgi_format = gc->global_hook_info->format;
|
||||
const bool convert_16bit = is_16bit_format(dxgi_format);
|
||||
const enum gs_color_format format =
|
||||
convert_16bit ? GS_BGRA : convert_format(dxgi_format);
|
||||
|
||||
obs_enter_graphics();
|
||||
gs_texrender_destroy(gc->extra_texrender);
|
||||
gs_texture_destroy(gc->extra_texture);
|
||||
gs_texture_destroy(gc->texture);
|
||||
gc->texture =
|
||||
gs_texture_t *const texture =
|
||||
gs_texture_create(gc->cx, gc->cy, format, 1, NULL, GS_DYNAMIC);
|
||||
obs_leave_graphics();
|
||||
|
||||
if (!gc->texture) {
|
||||
bool success = texture != NULL;
|
||||
if (success) {
|
||||
const bool linear_sample = format != GS_R10G10B10A2;
|
||||
|
||||
gs_texrender_t *extra_texrender = NULL;
|
||||
if (!linear_sample) {
|
||||
extra_texrender =
|
||||
gs_texrender_create(GS_BGRA, GS_ZS_NONE);
|
||||
success = extra_texrender != NULL;
|
||||
if (!success)
|
||||
warn("init_shmem_capture: failed to create extra texrender");
|
||||
}
|
||||
|
||||
if (success) {
|
||||
gc->texture_buffers[0] = (uint8_t *)gc->data +
|
||||
gc->shmem_data->tex1_offset;
|
||||
gc->texture_buffers[1] = (uint8_t *)gc->data +
|
||||
gc->shmem_data->tex2_offset;
|
||||
gc->convert_16bit = convert_16bit;
|
||||
|
||||
gc->texture = texture;
|
||||
gc->extra_texture = NULL;
|
||||
gc->extra_texrender = extra_texrender;
|
||||
gc->linear_sample = linear_sample;
|
||||
gc->copy_texture = copy_shmem_tex;
|
||||
} else {
|
||||
gs_texture_destroy(texture);
|
||||
}
|
||||
} else {
|
||||
warn("init_shmem_capture: failed to create texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
gc->supports_srgb = true;
|
||||
gc->copy_texture = copy_shmem_tex;
|
||||
return true;
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool init_shtex_capture(struct game_capture *gc)
|
||||
{
|
||||
obs_enter_graphics();
|
||||
gs_texrender_destroy(gc->extra_texrender);
|
||||
gs_texture_destroy(gc->extra_texture);
|
||||
gs_texture_destroy(gc->texture);
|
||||
gc->texture = gs_texture_open_shared(gc->shtex_data->tex_handle);
|
||||
enum gs_color_format format = gs_texture_get_color_format(gc->texture);
|
||||
gc->supports_srgb = gs_is_srgb_format(format);
|
||||
gs_texture_t *const texture =
|
||||
gs_texture_open_shared(gc->shtex_data->tex_handle);
|
||||
bool success = texture != NULL;
|
||||
if (success) {
|
||||
enum gs_color_format format =
|
||||
gs_texture_get_color_format(texture);
|
||||
const bool ten_bit_srgb = (format == GS_R10G10B10A2);
|
||||
enum gs_color_format linear_format =
|
||||
ten_bit_srgb ? GS_BGRA : gs_generalize_format(format);
|
||||
const bool linear_sample = (linear_format == format);
|
||||
gs_texture_t *extra_texture = NULL;
|
||||
gs_texrender_t *extra_texrender = NULL;
|
||||
if (!linear_sample) {
|
||||
if (ten_bit_srgb) {
|
||||
extra_texrender = gs_texrender_create(
|
||||
linear_format, GS_ZS_NONE);
|
||||
success = extra_texrender != NULL;
|
||||
if (!success)
|
||||
warn("init_shtex_capture: failed to create extra texrender");
|
||||
} else {
|
||||
extra_texture = gs_texture_create(
|
||||
gs_texture_get_width(texture),
|
||||
gs_texture_get_height(texture),
|
||||
linear_format, 1, NULL, 0);
|
||||
success = extra_texture != NULL;
|
||||
if (!success)
|
||||
warn("init_shtex_capture: failed to create extra texture");
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
gc->texture = texture;
|
||||
gc->linear_sample = linear_sample;
|
||||
gc->extra_texture = extra_texture;
|
||||
gc->extra_texrender = extra_texrender;
|
||||
} else {
|
||||
gs_texture_destroy(texture);
|
||||
}
|
||||
} else {
|
||||
warn("init_shtex_capture: failed to open shared handle");
|
||||
}
|
||||
obs_leave_graphics();
|
||||
|
||||
if (!gc->texture) {
|
||||
warn("init_shtex_capture: failed to open shared handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool start_capture(struct game_capture *gc)
|
||||
|
@ -1802,36 +1861,86 @@ static inline void game_capture_render_cursor(struct game_capture *gc)
|
|||
gc->global_hook_info->cy);
|
||||
}
|
||||
|
||||
static void game_capture_render(void *data, gs_effect_t *effect)
|
||||
static void game_capture_render(void *data, gs_effect_t *unused)
|
||||
{
|
||||
UNUSED_PARAMETER(unused);
|
||||
|
||||
struct game_capture *gc = data;
|
||||
if (!gc->texture || !gc->active)
|
||||
return;
|
||||
|
||||
effect = obs_get_base_effect(gc->config.allow_transparency
|
||||
? OBS_EFFECT_DEFAULT
|
||||
: OBS_EFFECT_OPAQUE);
|
||||
const bool allow_transparency = gc->config.allow_transparency;
|
||||
gs_effect_t *const effect = obs_get_base_effect(
|
||||
allow_transparency ? OBS_EFFECT_DEFAULT : OBS_EFFECT_OPAQUE);
|
||||
|
||||
const bool linear_srgb = gs_get_linear_srgb() && gc->supports_srgb;
|
||||
const bool previous = gs_set_linear_srgb(linear_srgb);
|
||||
bool linear_sample = gc->linear_sample;
|
||||
gs_texture_t *texture = gc->texture;
|
||||
if (!linear_sample && !obs_source_get_texcoords_centered(gc->source)) {
|
||||
gs_texture_t *const extra_texture = gc->extra_texture;
|
||||
if (extra_texture) {
|
||||
gs_copy_texture(extra_texture, texture);
|
||||
texture = extra_texture;
|
||||
} else {
|
||||
gs_texrender_t *const texrender = gc->extra_texrender;
|
||||
gs_texrender_reset(texrender);
|
||||
const uint32_t cx = gs_texture_get_width(texture);
|
||||
const uint32_t cy = gs_texture_get_height(texture);
|
||||
if (gs_texrender_begin(texrender, cx, cy)) {
|
||||
gs_effect_t *const default_effect =
|
||||
obs_get_base_effect(OBS_EFFECT_DEFAULT);
|
||||
const bool previous =
|
||||
gs_framebuffer_srgb_enabled();
|
||||
gs_enable_framebuffer_srgb(false);
|
||||
gs_enable_blending(false);
|
||||
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy,
|
||||
-100.0f, 100.0f);
|
||||
gs_eparam_t *const image =
|
||||
gs_effect_get_param_by_name(
|
||||
default_effect, "image");
|
||||
gs_effect_set_texture(image, texture);
|
||||
while (gs_effect_loop(default_effect, "Draw")) {
|
||||
gs_draw_sprite(texture, 0, 0, 0);
|
||||
}
|
||||
gs_enable_blending(true);
|
||||
gs_enable_framebuffer_srgb(previous);
|
||||
|
||||
while (gs_effect_loop(effect, "Draw")) {
|
||||
obs_source_draw(gc->texture, 0, 0, 0, 0,
|
||||
gc->global_hook_info->flip);
|
||||
gs_texrender_end(texrender);
|
||||
|
||||
if (gc->config.allow_transparency && gc->config.cursor &&
|
||||
texture = gs_texrender_get_texture(texrender);
|
||||
}
|
||||
}
|
||||
|
||||
linear_sample = true;
|
||||
}
|
||||
|
||||
gs_eparam_t *const image = gs_effect_get_param_by_name(effect, "image");
|
||||
const uint32_t flip = gc->global_hook_info->flip ? GS_FLIP_V : 0;
|
||||
const char *tech_name = allow_transparency && !linear_sample
|
||||
? "DrawSrgbDecompress"
|
||||
: "Draw";
|
||||
while (gs_effect_loop(effect, tech_name)) {
|
||||
const bool previous = gs_framebuffer_srgb_enabled();
|
||||
gs_enable_framebuffer_srgb(allow_transparency || linear_sample);
|
||||
gs_enable_blending(allow_transparency);
|
||||
if (linear_sample)
|
||||
gs_effect_set_texture_srgb(image, texture);
|
||||
else
|
||||
gs_effect_set_texture(image, texture);
|
||||
gs_draw_sprite(texture, flip, 0, 0);
|
||||
gs_enable_blending(true);
|
||||
gs_enable_framebuffer_srgb(previous);
|
||||
|
||||
if (allow_transparency && gc->config.cursor &&
|
||||
!gc->cursor_hidden) {
|
||||
game_capture_render_cursor(gc);
|
||||
}
|
||||
}
|
||||
|
||||
gs_set_linear_srgb(previous);
|
||||
if (!allow_transparency && gc->config.cursor && !gc->cursor_hidden) {
|
||||
gs_effect_t *const default_effect =
|
||||
obs_get_base_effect(OBS_EFFECT_DEFAULT);
|
||||
|
||||
if (!gc->config.allow_transparency && gc->config.cursor &&
|
||||
!gc->cursor_hidden) {
|
||||
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
|
||||
|
||||
while (gs_effect_loop(effect, "Draw")) {
|
||||
while (gs_effect_loop(default_effect, "Draw")) {
|
||||
game_capture_render_cursor(gc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ static void monitor_capture_render(void *data, gs_effect_t *effect)
|
|||
{
|
||||
struct monitor_capture *capture = data;
|
||||
dc_capture_render(&capture->data,
|
||||
obs_get_base_effect(OBS_EFFECT_OPAQUE));
|
||||
obs_source_get_texcoords_centered(capture->source));
|
||||
|
||||
UNUSED_PARAMETER(effect);
|
||||
}
|
||||
|
|
|
@ -37,8 +37,7 @@ typedef void (*PFN_winrt_capture_free)(struct winrt_capture *capture);
|
|||
typedef BOOL (*PFN_winrt_capture_active)(const struct winrt_capture *capture);
|
||||
typedef BOOL (*PFN_winrt_capture_show_cursor)(struct winrt_capture *capture,
|
||||
BOOL visible);
|
||||
typedef void (*PFN_winrt_capture_render)(struct winrt_capture *capture,
|
||||
gs_effect_t *effect);
|
||||
typedef void (*PFN_winrt_capture_render)(struct winrt_capture *capture);
|
||||
typedef uint32_t (*PFN_winrt_capture_width)(const struct winrt_capture *capture);
|
||||
typedef uint32_t (*PFN_winrt_capture_height)(
|
||||
const struct winrt_capture *capture);
|
||||
|
@ -574,13 +573,12 @@ static void wc_tick(void *data, float seconds)
|
|||
static void wc_render(void *data, gs_effect_t *effect)
|
||||
{
|
||||
struct window_capture *wc = data;
|
||||
gs_effect_t *const opaque = obs_get_base_effect(OBS_EFFECT_OPAQUE);
|
||||
if (wc->method == METHOD_WGC) {
|
||||
if (wc->capture_winrt) {
|
||||
if (wc->exports.winrt_capture_active(
|
||||
wc->capture_winrt)) {
|
||||
wc->exports.winrt_capture_render(
|
||||
wc->capture_winrt, opaque);
|
||||
wc->capture_winrt);
|
||||
} else {
|
||||
wc->exports.winrt_capture_free(
|
||||
wc->capture_winrt);
|
||||
|
@ -588,7 +586,9 @@ static void wc_render(void *data, gs_effect_t *effect)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
dc_capture_render(&wc->capture, opaque);
|
||||
dc_capture_render(
|
||||
&wc->capture,
|
||||
obs_source_get_texcoords_centered(wc->source));
|
||||
}
|
||||
|
||||
UNUSED_PARAMETER(effect);
|
||||
|
|
Loading…
Reference in a new issue