0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-20 13:08:50 +02:00

Merge pull request #2067 from jpark37/srgb-toggle

Linear color math
This commit is contained in:
Jim 2021-01-22 16:03:29 -08:00 committed by GitHub
commit 8333f13587
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 2213 additions and 413 deletions

View File

@ -589,11 +589,13 @@ void OBSBasicFilters::DrawPreview(void *data, uint32_t cx, uint32_t cy)
gs_viewport_push();
gs_projection_push();
const bool previous = gs_set_linear_srgb(true);
gs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f);
gs_set_viewport(x, y, newCX, newCY);
obs_source_video_render(window->source);
gs_set_linear_srgb(previous);
gs_projection_pop();
gs_viewport_pop();
}

View File

@ -148,10 +148,13 @@ void OBSBasicInteraction::DrawPreview(void *data, uint32_t cx, uint32_t cy)
gs_viewport_push();
gs_projection_push();
const bool previous = gs_set_linear_srgb(true);
gs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f);
gs_set_viewport(x, y, newCX, newCY);
obs_source_video_render(window->source);
gs_set_linear_srgb(previous);
gs_projection_pop();
gs_viewport_pop();
}

View File

@ -392,11 +392,13 @@ void OBSBasicProperties::DrawPreview(void *data, uint32_t cx, uint32_t cy)
gs_viewport_push();
gs_projection_push();
const bool previous = gs_set_linear_srgb(true);
gs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f);
gs_set_viewport(x, y, newCX, newCY);
obs_source_video_render(window->source);
gs_set_linear_srgb(previous);
gs_projection_pop();
gs_viewport_pop();
}

View File

@ -77,6 +77,7 @@ Then the uniforms are set through the following functions:
- :c:func:`gs_effect_set_vec3()`
- :c:func:`gs_effect_set_vec4()`
- :c:func:`gs_effect_set_texture()`
- :c:func:`gs_effect_set_texture_srgb()`
There are two "universal" effect parameters that may be expected of
effects: **ViewProj**, and **image**. The **ViewProj** parameter

View File

@ -337,6 +337,15 @@ HLSL format.
---------------------
.. function:: void gs_effect_set_texture_srgb(gs_eparam_t *param, gs_texture_t *val)
Sets a texture parameter using SRGB view if available.
:param param: Effect parameter
:param val: Texture
---------------------
.. function:: void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size)
Sets a parameter with data manually.

View File

@ -42,6 +42,9 @@ Graphics Enumerations
- GS_DXT1 - Compressed DXT1
- GS_DXT3 - Compressed DXT3
- GS_DXT5 - Compressed DXT5
- GS_RGBA_UNORM - RGBA, 8 bits per channel, no SRGB aliasing
- GS_BGRX_UNORM - BGRX, 8 bits per channel, no SRGB aliasing
- GS_BGRA_UNORM - BGRA, 8 bits per channel, no SRGB aliasing
.. type:: enum gs_zstencil_format

View File

@ -33,24 +33,37 @@ void gs_index_buffer::Rebuild(ID3D11Device *dev)
void gs_texture_2d::RebuildSharedTextureFallback()
{
static const gs_color_format format = GS_BGRA;
static const DXGI_FORMAT dxgi_format_resource =
ConvertGSTextureFormatResource(format);
static const DXGI_FORMAT dxgi_format_view =
ConvertGSTextureFormatView(format);
static const DXGI_FORMAT dxgi_format_view_linear =
ConvertGSTextureFormatViewLinear(format);
td = {};
td.Width = 2;
td.Height = 2;
td.MipLevels = 1;
td.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
td.Format = dxgi_format_resource;
td.ArraySize = 1;
td.SampleDesc.Count = 1;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
width = td.Width;
height = td.Height;
dxgiFormat = td.Format;
dxgiFormatResource = dxgi_format_resource;
dxgiFormatView = dxgi_format_view;
dxgiFormatViewLinear = dxgi_format_view_linear;
levels = 1;
resourceDesc = {};
resourceDesc.Format = td.Format;
resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
resourceDesc.Texture2D.MipLevels = 1;
viewDesc = {};
viewDesc.Format = dxgi_format_view;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
viewDesc.Texture2D.MipLevels = 1;
viewDescLinear = viewDesc;
viewDescLinear.Format = dxgi_format_view_linear;
isShared = false;
}
@ -77,9 +90,18 @@ void gs_texture_2d::Rebuild(ID3D11Device *dev)
throw HRError("Failed to create 2D texture", hr);
}
hr = dev->CreateShaderResourceView(texture, &resourceDesc, &shaderRes);
hr = dev->CreateShaderResourceView(texture, &viewDesc, &shaderRes);
if (FAILED(hr))
throw HRError("Failed to create resource view", hr);
throw HRError("Failed to create SRV", hr);
if (viewDesc.Format == viewDescLinear.Format) {
shaderResLinear = shaderRes;
} else {
hr = dev->CreateShaderResourceView(texture, &viewDescLinear,
&shaderResLinear);
if (FAILED(hr))
throw HRError("Failed to create linear SRV", hr);
}
if (isRenderTarget)
InitRenderTargets();
@ -110,9 +132,18 @@ void gs_texture_2d::RebuildNV12_Y(ID3D11Device *dev)
if (FAILED(hr))
throw HRError("Failed to create 2D texture", hr);
hr = dev->CreateShaderResourceView(texture, &resourceDesc, &shaderRes);
hr = dev->CreateShaderResourceView(texture, &viewDesc, &shaderRes);
if (FAILED(hr))
throw HRError("Failed to create resource view", hr);
throw HRError("Failed to create Y SRV", hr);
if (viewDesc.Format == viewDescLinear.Format) {
shaderResLinear = shaderRes;
} else {
hr = dev->CreateShaderResourceView(texture, &viewDescLinear,
&shaderResLinear);
if (FAILED(hr))
throw HRError("Failed to create linear Y SRV", hr);
}
if (isRenderTarget)
InitRenderTargets();
@ -136,9 +167,18 @@ void gs_texture_2d::RebuildNV12_UV(ID3D11Device *dev)
texture = tex_y->texture;
hr = dev->CreateShaderResourceView(texture, &resourceDesc, &shaderRes);
hr = dev->CreateShaderResourceView(texture, &viewDesc, &shaderRes);
if (FAILED(hr))
throw HRError("Failed to create resource view", hr);
throw HRError("Failed to create UV SRV", hr);
if (viewDesc.Format == viewDescLinear.Format) {
shaderResLinear = shaderRes;
} else {
hr = dev->CreateShaderResourceView(texture, &viewDescLinear,
&shaderResLinear);
if (FAILED(hr))
throw HRError("Failed to create linear UV SRV", hr);
}
if (isRenderTarget)
InitRenderTargets();
@ -253,25 +293,38 @@ void gs_timer_range::Rebuild(ID3D11Device *dev)
void gs_texture_3d::RebuildSharedTextureFallback()
{
static const gs_color_format format = GS_BGRA;
static const DXGI_FORMAT dxgi_format_resource =
ConvertGSTextureFormatResource(format);
static const DXGI_FORMAT dxgi_format_view =
ConvertGSTextureFormatView(format);
static const DXGI_FORMAT dxgi_format_view_linear =
ConvertGSTextureFormatViewLinear(format);
td = {};
td.Width = 2;
td.Height = 2;
td.Depth = 2;
td.MipLevels = 1;
td.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
td.Format = dxgi_format_resource;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
width = td.Width;
height = td.Height;
depth = td.Depth;
dxgiFormat = td.Format;
dxgiFormatResource = dxgi_format_resource;
dxgiFormatView = dxgi_format_view;
dxgiFormatViewLinear = dxgi_format_view_linear;
levels = 1;
resourceDesc = {};
resourceDesc.Format = td.Format;
resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
resourceDesc.Texture3D.MostDetailedMip = 0;
resourceDesc.Texture3D.MipLevels = 1;
viewDesc = {};
viewDesc.Format = dxgi_format_view;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
viewDesc.Texture3D.MostDetailedMip = 0;
viewDesc.Texture3D.MipLevels = 1;
viewDescLinear = viewDesc;
viewDescLinear.Format = dxgi_format_view_linear;
isShared = false;
}
@ -298,9 +351,18 @@ void gs_texture_3d::Rebuild(ID3D11Device *dev)
throw HRError("Failed to create 3D texture", hr);
}
hr = dev->CreateShaderResourceView(texture, &resourceDesc, &shaderRes);
hr = dev->CreateShaderResourceView(texture, &viewDesc, &shaderRes);
if (FAILED(hr))
throw HRError("Failed to create resource view", hr);
throw HRError("Failed to create 3D SRV", hr);
if (viewDesc.Format == viewDescLinear.Format) {
shaderResLinear = shaderRes;
} else {
hr = dev->CreateShaderResourceView(texture, &viewDescLinear,
&shaderResLinear);
if (FAILED(hr))
throw HRError("Failed to create linear 3D SRV", hr);
}
acquired = false;

View File

@ -262,10 +262,15 @@ inline void gs_shader::UpdateParam(vector<uint8_t> &constData,
param.changed = false;
}
} else if (param.curValue.size() == sizeof(gs_texture_t *)) {
gs_texture_t *tex;
memcpy(&tex, param.curValue.data(), sizeof(gs_texture_t *));
device_load_texture(device, tex, param.textureID);
} else if (param.curValue.size() == sizeof(struct gs_shader_texture)) {
struct gs_shader_texture shader_tex;
memcpy(&shader_tex, param.curValue.data(), sizeof(shader_tex));
if (shader_tex.srgb)
device_load_texture_srgb(device, shader_tex.tex,
param.textureID);
else
device_load_texture(device, shader_tex.tex,
param.textureID);
if (param.nextSampler) {
ID3D11SamplerState *state = param.nextSampler->state;

View File

@ -23,7 +23,7 @@ gs_stage_surface::gs_stage_surface(gs_device_t *device, uint32_t width,
width(width),
height(height),
format(colorFormat),
dxgiFormat(ConvertGSTextureFormat(colorFormat))
dxgiFormat(ConvertGSTextureFormatView(colorFormat))
{
HRESULT hr;

View File

@ -78,7 +78,7 @@ static inline void make_swap_desc(DXGI_SWAP_CHAIN_DESC &desc,
{
memset(&desc, 0, sizeof(desc));
desc.BufferCount = data->num_backbuffers;
desc.BufferDesc.Format = ConvertGSTextureFormat(data->format);
desc.BufferDesc.Format = ConvertGSTextureFormatView(data->format);
desc.BufferDesc.Width = data->cx;
desc.BufferDesc.Height = data->cy;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
@ -99,10 +99,24 @@ void gs_swap_chain::InitTarget(uint32_t cx, uint32_t cy)
if (FAILED(hr))
throw HRError("Failed to get swap buffer texture", hr);
D3D11_RENDER_TARGET_VIEW_DESC rtv;
rtv.Format = target.dxgiFormatView;
rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtv.Texture2D.MipSlice = 0;
hr = device->device->CreateRenderTargetView(
target.texture, NULL, target.renderTarget[0].Assign());
target.texture, &rtv, target.renderTarget[0].Assign());
if (FAILED(hr))
throw HRError("Failed to create swap render target view", hr);
throw HRError("Failed to create swap RTV", hr);
if (target.dxgiFormatView == target.dxgiFormatViewLinear) {
target.renderTargetLinear[0] = target.renderTarget[0];
} else {
rtv.Format = target.dxgiFormatViewLinear;
hr = device->device->CreateRenderTargetView(
target.texture, &rtv,
target.renderTargetLinear[0].Assign());
if (FAILED(hr))
throw HRError("Failed to create linear swap RTV", hr);
}
}
void gs_swap_chain::InitZStencilBuffer(uint32_t cx, uint32_t cy)
@ -125,6 +139,7 @@ void gs_swap_chain::Resize(uint32_t cx, uint32_t cy)
target.texture.Clear();
target.renderTarget[0].Clear();
target.renderTargetLinear[0].Clear();
zs.texture.Clear();
zs.view.Clear();
@ -139,7 +154,7 @@ void gs_swap_chain::Resize(uint32_t cx, uint32_t cy)
cy = clientRect.bottom;
}
hr = swap->ResizeBuffers(numBuffers, cx, cy, target.dxgiFormat, 0);
hr = swap->ResizeBuffers(numBuffers, cx, cy, DXGI_FORMAT_UNKNOWN, 0);
if (FAILED(hr))
throw HRError("Failed to resize swap buffers", hr);
@ -152,7 +167,11 @@ void gs_swap_chain::Init()
target.device = device;
target.isRenderTarget = true;
target.format = initData.format;
target.dxgiFormat = ConvertGSTextureFormat(initData.format);
target.dxgiFormatResource =
ConvertGSTextureFormatResource(initData.format);
target.dxgiFormatView = ConvertGSTextureFormatView(initData.format);
target.dxgiFormatViewLinear =
ConvertGSTextureFormatViewLinear(initData.format);
InitTarget(initData.cx, initData.cy);
zs.device = device;
@ -310,6 +329,7 @@ try {
UpdateBlendState();
UpdateRasterState();
UpdateZStencilState();
FlushOutputViews();
context->Draw(4, 0);
device_set_viewport(this, 0, 0, NV12_CX / 2, NV12_CY / 2);
@ -318,6 +338,7 @@ try {
UpdateBlendState();
UpdateRasterState();
UpdateZStencilState();
FlushOutputViews();
context->Draw(4, 0);
device_load_pixelshader(this, nullptr);
@ -727,6 +748,30 @@ void gs_device::UpdateViewProjMatrix()
&curViewProjMatrix);
}
void gs_device::FlushOutputViews()
{
if (curFramebufferInvalidate) {
ID3D11RenderTargetView *rtv = nullptr;
if (curRenderTarget) {
const int i = curRenderSide;
rtv = curFramebufferSrgb
? curRenderTarget->renderTargetLinear[i]
.Get()
: curRenderTarget->renderTarget[i].Get();
if (!rtv) {
blog(LOG_ERROR,
"device_draw (D3D11): texture is not a render target");
return;
}
}
ID3D11DepthStencilView *dsv = nullptr;
if (curZStencilBuffer)
dsv = curZStencilBuffer->view;
context->OMSetRenderTargets(1, &rtv, dsv);
curFramebufferInvalidate = false;
}
}
gs_device::gs_device(uint32_t adapterIdx)
: curToplogy(D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED)
{
@ -1045,18 +1090,9 @@ void device_resize(gs_device_t *device, uint32_t cx, uint32_t cy)
try {
ID3D11RenderTargetView *renderView = NULL;
ID3D11DepthStencilView *depthView = NULL;
int i = device->curRenderSide;
device->context->OMSetRenderTargets(1, &renderView, depthView);
device->context->OMSetRenderTargets(1, &renderView, NULL);
device->curSwapChain->Resize(cx, cy);
if (device->curRenderTarget)
renderView = device->curRenderTarget->renderTarget[i];
if (device->curZStencilBuffer)
depthView = device->curZStencilBuffer->view;
device->context->OMSetRenderTargets(1, &renderView, depthView);
device->curFramebufferInvalidate = true;
} catch (const HRError &error) {
blog(LOG_ERROR, "device_resize (D3D11): %s (%08lX)", error.str,
error.hr);
@ -1419,20 +1455,29 @@ void device_load_indexbuffer(gs_device_t *device, gs_indexbuffer_t *indexbuffer)
device->context->IASetIndexBuffer(buffer, format, 0);
}
void device_load_texture(gs_device_t *device, gs_texture_t *tex, int unit)
static void device_load_texture_internal(gs_device_t *device, gs_texture_t *tex,
int unit,
ID3D11ShaderResourceView *view)
{
ID3D11ShaderResourceView *view = NULL;
if (device->curTextures[unit] == tex)
return;
if (tex)
view = tex->shaderRes;
device->curTextures[unit] = tex;
device->context->PSSetShaderResources(unit, 1, &view);
}
void device_load_texture(gs_device_t *device, gs_texture_t *tex, int unit)
{
ID3D11ShaderResourceView *view = tex ? tex->shaderRes : NULL;
return device_load_texture_internal(device, tex, unit, view);
}
void device_load_texture_srgb(gs_device_t *device, gs_texture_t *tex, int unit)
{
ID3D11ShaderResourceView *view = tex ? tex->shaderResLinear : NULL;
return device_load_texture_internal(device, tex, unit, view);
}
void device_load_samplerstate(gs_device_t *device,
gs_samplerstate_t *samplerstate, int unit)
{
@ -1574,26 +1619,19 @@ void device_set_render_target(gs_device_t *device, gs_texture_t *tex,
return;
if (tex && tex->type != GS_TEXTURE_2D) {
blog(LOG_ERROR, "device_set_render_target (D3D11): "
"texture is not a 2D texture");
blog(LOG_ERROR,
"device_set_render_target (D3D11): texture is not a 2D texture");
return;
}
gs_texture_2d *tex2d = static_cast<gs_texture_2d *>(tex);
if (tex2d && !tex2d->renderTarget[0]) {
blog(LOG_ERROR, "device_set_render_target (D3D11): "
"texture is not a render target");
return;
gs_texture_2d *const tex2d = static_cast<gs_texture_2d *>(tex);
if (device->curRenderTarget != tex2d || device->curRenderSide != 0 ||
device->curZStencilBuffer != zstencil) {
device->curRenderTarget = tex2d;
device->curRenderSide = 0;
device->curZStencilBuffer = zstencil;
device->curFramebufferInvalidate = true;
}
ID3D11RenderTargetView *rt = tex2d ? tex2d->renderTarget[0].Get()
: nullptr;
device->curRenderTarget = tex2d;
device->curRenderSide = 0;
device->curZStencilBuffer = zstencil;
device->context->OMSetRenderTargets(
1, &rt, zstencil ? zstencil->view : nullptr);
}
void device_set_cube_render_target(gs_device_t *device, gs_texture_t *tex,
@ -1619,19 +1657,27 @@ void device_set_cube_render_target(gs_device_t *device, gs_texture_t *tex,
return;
}
gs_texture_2d *tex2d = static_cast<gs_texture_2d *>(tex);
if (!tex2d->renderTarget[side]) {
blog(LOG_ERROR, "device_set_cube_render_target (D3D11): "
"texture is not a render target");
return;
gs_texture_2d *const tex2d = static_cast<gs_texture_2d *>(tex);
if (device->curRenderTarget != tex2d || device->curRenderSide != side ||
device->curZStencilBuffer != zstencil) {
device->curRenderTarget = tex2d;
device->curRenderSide = side;
device->curZStencilBuffer = zstencil;
device->curFramebufferInvalidate = true;
}
}
ID3D11RenderTargetView *rt = tex2d->renderTarget[0];
void device_enable_framebuffer_srgb(gs_device_t *device, bool enable)
{
if (device->curFramebufferSrgb != enable) {
device->curFramebufferSrgb = enable;
device->curFramebufferInvalidate = true;
}
}
device->curRenderTarget = tex2d;
device->curRenderSide = side;
device->curZStencilBuffer = zstencil;
device->context->OMSetRenderTargets(1, &rt, zstencil->view);
bool device_framebuffer_srgb_enabled(gs_device_t *device)
{
return device->curFramebufferSrgb;
}
inline void gs_device::CopyTex(ID3D11Texture2D *dst, uint32_t dst_x,
@ -1780,6 +1826,8 @@ void device_draw(gs_device_t *device, enum gs_draw_mode draw_mode,
if (!device->curSwapChain && !device->curRenderTarget)
throw "No render target or swap chain to render to";
device->FlushOutputViews();
gs_effect_t *effect = gs_get_effect();
if (effect)
gs_effect_update_params(effect);
@ -2589,9 +2637,10 @@ device_texture_create_gdi(gs_device_t *device, uint32_t width, uint32_t height)
{
gs_texture *texture = nullptr;
try {
texture = new gs_texture_2d(device, width, height, GS_BGRA, 1,
nullptr, GS_RENDER_TARGET,
GS_TEXTURE_2D, true);
texture = new gs_texture_2d(device, width, height,
GS_BGRA_UNORM, 1, nullptr,
GS_RENDER_TARGET, GS_TEXTURE_2D,
true);
} catch (const HRError &error) {
blog(LOG_ERROR, "device_texture_create_gdi (D3D11): %s (%08lX)",
error.str, error.hr);

View File

@ -59,7 +59,7 @@ static inline uint32_t GetWinVer()
return (ver.major << 8) | ver.minor;
}
static inline DXGI_FORMAT ConvertGSTextureFormat(gs_color_format format)
static inline DXGI_FORMAT ConvertGSTextureFormatResource(gs_color_format format)
{
switch (format) {
case GS_UNKNOWN:
@ -69,11 +69,11 @@ static inline DXGI_FORMAT ConvertGSTextureFormat(gs_color_format format)
case GS_R8:
return DXGI_FORMAT_R8_UNORM;
case GS_RGBA:
return DXGI_FORMAT_R8G8B8A8_UNORM;
return DXGI_FORMAT_R8G8B8A8_TYPELESS;
case GS_BGRX:
return DXGI_FORMAT_B8G8R8X8_UNORM;
return DXGI_FORMAT_B8G8R8X8_TYPELESS;
case GS_BGRA:
return DXGI_FORMAT_B8G8R8A8_UNORM;
return DXGI_FORMAT_B8G8R8A8_TYPELESS;
case GS_R10G10B10A2:
return DXGI_FORMAT_R10G10B10A2_UNORM;
case GS_RGBA16:
@ -100,11 +100,46 @@ static inline DXGI_FORMAT ConvertGSTextureFormat(gs_color_format format)
return DXGI_FORMAT_BC3_UNORM;
case GS_R8G8:
return DXGI_FORMAT_R8G8_UNORM;
case GS_RGBA_UNORM:
return DXGI_FORMAT_R8G8B8A8_UNORM;
case GS_BGRX_UNORM:
return DXGI_FORMAT_B8G8R8X8_UNORM;
case GS_BGRA_UNORM:
return DXGI_FORMAT_B8G8R8A8_UNORM;
}
return DXGI_FORMAT_UNKNOWN;
}
static inline DXGI_FORMAT ConvertGSTextureFormatView(gs_color_format format)
{
switch (format) {
case GS_RGBA:
return DXGI_FORMAT_R8G8B8A8_UNORM;
case GS_BGRX:
return DXGI_FORMAT_B8G8R8X8_UNORM;
case GS_BGRA:
return DXGI_FORMAT_B8G8R8A8_UNORM;
default:
return ConvertGSTextureFormatResource(format);
}
}
static inline DXGI_FORMAT
ConvertGSTextureFormatViewLinear(gs_color_format format)
{
switch (format) {
case GS_RGBA:
return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
case GS_BGRX:
return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
case GS_BGRA:
return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
default:
return ConvertGSTextureFormatResource(format);
}
}
static inline gs_color_format ConvertDXGITextureFormat(DXGI_FORMAT format)
{
switch ((unsigned long)format) {
@ -115,12 +150,9 @@ static inline gs_color_format ConvertDXGITextureFormat(DXGI_FORMAT format)
case DXGI_FORMAT_R8G8_UNORM:
return GS_R8G8;
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
case DXGI_FORMAT_R8G8B8A8_UNORM:
return GS_RGBA;
case DXGI_FORMAT_B8G8R8X8_UNORM:
case DXGI_FORMAT_B8G8R8X8_TYPELESS:
return GS_BGRX;
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8A8_TYPELESS:
return GS_BGRA;
case DXGI_FORMAT_R10G10B10A2_UNORM:
@ -147,6 +179,12 @@ static inline gs_color_format ConvertDXGITextureFormat(DXGI_FORMAT format)
return GS_DXT3;
case DXGI_FORMAT_BC3_UNORM:
return GS_DXT5;
case DXGI_FORMAT_R8G8B8A8_UNORM:
return GS_RGBA_UNORM;
case DXGI_FORMAT_B8G8R8X8_UNORM:
return GS_BGRX_UNORM;
case DXGI_FORMAT_B8G8R8A8_UNORM:
return GS_BGRA_UNORM;
}
return GS_UNKNOWN;
@ -410,7 +448,9 @@ struct gs_texture : gs_obj {
gs_color_format format;
ComPtr<ID3D11ShaderResourceView> shaderRes;
D3D11_SHADER_RESOURCE_VIEW_DESC resourceDesc = {};
ComPtr<ID3D11ShaderResourceView> shaderResLinear;
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{};
D3D11_SHADER_RESOURCE_VIEW_DESC viewDescLinear{};
void Rebuild(ID3D11Device *dev);
@ -440,11 +480,14 @@ struct gs_texture : gs_obj {
struct gs_texture_2d : gs_texture {
ComPtr<ID3D11Texture2D> texture;
ComPtr<ID3D11RenderTargetView> renderTarget[6];
ComPtr<ID3D11RenderTargetView> renderTargetLinear[6];
ComPtr<IDXGISurface1> gdiSurface;
uint32_t width = 0, height = 0;
uint32_t flags = 0;
DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN;
DXGI_FORMAT dxgiFormatResource = DXGI_FORMAT_UNKNOWN;
DXGI_FORMAT dxgiFormatView = DXGI_FORMAT_UNKNOWN;
DXGI_FORMAT dxgiFormatViewLinear = DXGI_FORMAT_UNKNOWN;
bool isRenderTarget = false;
bool isGDICompatible = false;
bool isDynamic = false;
@ -476,10 +519,13 @@ struct gs_texture_2d : gs_texture {
inline void Release()
{
texture.Release();
for (auto &rt : renderTarget)
for (ComPtr<ID3D11RenderTargetView> &rt : renderTarget)
rt.Release();
for (ComPtr<ID3D11RenderTargetView> &rt : renderTargetLinear)
rt.Release();
gdiSurface.Release();
shaderRes.Release();
shaderResLinear.Release();
}
inline gs_texture_2d() : gs_texture(GS_TEXTURE_2D, 0, GS_UNKNOWN) {}
@ -501,7 +547,9 @@ struct gs_texture_3d : gs_texture {
uint32_t width = 0, height = 0, depth = 0;
uint32_t flags = 0;
DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN;
DXGI_FORMAT dxgiFormatResource = DXGI_FORMAT_UNKNOWN;
DXGI_FORMAT dxgiFormatView = DXGI_FORMAT_UNKNOWN;
DXGI_FORMAT dxgiFormatViewLinear = DXGI_FORMAT_UNKNOWN;
bool isDynamic = false;
bool isShared = false;
bool genMipmaps = false;
@ -912,6 +960,8 @@ struct gs_device {
gs_texture_2d *curRenderTarget = nullptr;
gs_zstencil_buffer *curZStencilBuffer = nullptr;
int curRenderSide = 0;
bool curFramebufferSrgb = false;
bool curFramebufferInvalidate = false;
gs_texture *curTextures[GS_MAX_TEXTURES];
gs_sampler_state *curSamplers[GS_MAX_TEXTURES];
gs_vertex_buffer *curVertexBuffer = nullptr;
@ -972,6 +1022,8 @@ struct gs_device {
void UpdateViewProjMatrix();
void FlushOutputViews();
void RebuildDevice();
bool HasBadNV12Output();

View File

@ -98,7 +98,7 @@ void gs_texture_2d::InitTexture(const uint8_t *const *data)
td.Height = height;
td.MipLevels = genMipmaps ? 0 : levels;
td.ArraySize = type == GS_TEXTURE_CUBE ? 6 : 1;
td.Format = nv12 ? DXGI_FORMAT_NV12 : dxgiFormat;
td.Format = nv12 ? DXGI_FORMAT_NV12 : dxgiFormatResource;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
td.SampleDesc.Count = 1;
td.CPUAccessFlags = isDynamic ? D3D11_CPU_ACCESS_WRITE : 0;
@ -172,23 +172,35 @@ void gs_texture_2d::InitResourceView()
{
HRESULT hr;
memset(&resourceDesc, 0, sizeof(resourceDesc));
resourceDesc.Format = dxgiFormat;
memset(&viewDesc, 0, sizeof(viewDesc));
viewDesc.Format = dxgiFormatView;
if (type == GS_TEXTURE_CUBE) {
resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
resourceDesc.TextureCube.MipLevels =
genMipmaps || !levels ? -1 : levels;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
viewDesc.TextureCube.MipLevels = genMipmaps || !levels ? -1
: levels;
} else {
resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
resourceDesc.Texture2D.MipLevels =
genMipmaps || !levels ? -1 : levels;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
viewDesc.Texture2D.MipLevels = genMipmaps || !levels ? -1
: levels;
}
hr = device->device->CreateShaderResourceView(texture, &resourceDesc,
hr = device->device->CreateShaderResourceView(texture, &viewDesc,
shaderRes.Assign());
if (FAILED(hr))
throw HRError("Failed to create resource view", hr);
throw HRError("Failed to create SRV", hr);
viewDescLinear = viewDesc;
viewDescLinear.Format = dxgiFormatViewLinear;
if (dxgiFormatView == dxgiFormatViewLinear) {
shaderResLinear = shaderRes;
} else {
hr = device->device->CreateShaderResourceView(
texture, &viewDescLinear, shaderResLinear.Assign());
if (FAILED(hr))
throw HRError("Failed to create linear SRV", hr);
}
}
void gs_texture_2d::InitRenderTargets()
@ -196,18 +208,27 @@ void gs_texture_2d::InitRenderTargets()
HRESULT hr;
if (type == GS_TEXTURE_2D) {
D3D11_RENDER_TARGET_VIEW_DESC rtv;
rtv.Format = dxgiFormat;
rtv.Format = dxgiFormatView;
rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtv.Texture2D.MipSlice = 0;
hr = device->device->CreateRenderTargetView(
texture, &rtv, renderTarget[0].Assign());
if (FAILED(hr))
throw HRError("Failed to create render target view",
hr);
throw HRError("Failed to create RTV", hr);
if (dxgiFormatView == dxgiFormatViewLinear) {
renderTargetLinear[0] = renderTarget[0];
} else {
rtv.Format = dxgiFormatViewLinear;
hr = device->device->CreateRenderTargetView(
texture, &rtv, renderTargetLinear[0].Assign());
if (FAILED(hr))
throw HRError("Failed to create linear RTV",
hr);
}
} else {
D3D11_RENDER_TARGET_VIEW_DESC rtv;
rtv.Format = dxgiFormat;
rtv.Format = dxgiFormatView;
rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtv.Texture2DArray.MipSlice = 0;
rtv.Texture2DArray.ArraySize = 1;
@ -217,9 +238,19 @@ void gs_texture_2d::InitRenderTargets()
hr = device->device->CreateRenderTargetView(
texture, &rtv, renderTarget[i].Assign());
if (FAILED(hr))
throw HRError("Failed to create cube render "
"target view",
hr);
throw HRError("Failed to create cube RTV", hr);
if (dxgiFormatView == dxgiFormatViewLinear) {
renderTargetLinear[i] = renderTarget[i];
} else {
rtv.Format = dxgiFormatViewLinear;
hr = device->device->CreateRenderTargetView(
texture, &rtv,
renderTargetLinear[i].Assign());
if (FAILED(hr))
throw HRError(
"Failed to create linear cube RTV",
hr);
}
}
}
}
@ -235,7 +266,9 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width,
width(width),
height(height),
flags(flags_),
dxgiFormat(ConvertGSTextureFormat(format)),
dxgiFormatResource(ConvertGSTextureFormatResource(format)),
dxgiFormatView(ConvertGSTextureFormatView(format)),
dxgiFormatViewLinear(ConvertGSTextureFormatViewLinear(format)),
isRenderTarget((flags_ & GS_RENDER_TARGET) != 0),
isGDICompatible(gdiCompatible),
isDynamic((flags_ & GS_DYNAMIC) != 0),
@ -271,7 +304,9 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12tex,
this->chroma = true;
this->width = td.Width / 2;
this->height = td.Height / 2;
this->dxgiFormat = DXGI_FORMAT_R8G8_UNORM;
this->dxgiFormatResource = DXGI_FORMAT_R8G8_UNORM;
this->dxgiFormatView = DXGI_FORMAT_R8G8_UNORM;
this->dxgiFormatViewLinear = DXGI_FORMAT_R8G8_UNORM;
InitResourceView();
if (isRenderTarget)
@ -292,24 +327,20 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t handle)
texture->GetDesc(&td);
const gs_color_format format = ConvertDXGITextureFormat(td.Format);
this->type = GS_TEXTURE_2D;
this->format = ConvertDXGITextureFormat(td.Format);
this->format = format;
this->levels = 1;
this->device = device;
this->width = td.Width;
this->height = td.Height;
this->dxgiFormat = td.Format;
this->dxgiFormatResource = ConvertGSTextureFormatResource(format);
this->dxgiFormatView = ConvertGSTextureFormatView(format);
this->dxgiFormatViewLinear = ConvertGSTextureFormatViewLinear(format);
memset(&resourceDesc, 0, sizeof(resourceDesc));
resourceDesc.Format = ConvertGSTextureFormat(this->format);
resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
resourceDesc.Texture2D.MipLevels = 1;
hr = device->device->CreateShaderResourceView(texture, &resourceDesc,
shaderRes.Assign());
if (FAILED(hr))
throw HRError("Failed to create shader resource view", hr);
InitResourceView();
}
gs_texture_2d::gs_texture_2d(gs_device_t *device, ID3D11Texture2D *obj)
@ -319,22 +350,18 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, ID3D11Texture2D *obj)
texture->GetDesc(&td);
const gs_color_format format = ConvertDXGITextureFormat(td.Format);
this->type = GS_TEXTURE_2D;
this->format = ConvertDXGITextureFormat(td.Format);
this->format = format;
this->levels = 1;
this->device = device;
this->width = td.Width;
this->height = td.Height;
this->dxgiFormat = td.Format;
this->dxgiFormatResource = ConvertGSTextureFormatResource(format);
this->dxgiFormatView = ConvertGSTextureFormatView(format);
this->dxgiFormatViewLinear = ConvertGSTextureFormatViewLinear(format);
memset(&resourceDesc, 0, sizeof(resourceDesc));
resourceDesc.Format = ConvertGSTextureFormat(this->format);
resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
resourceDesc.Texture2D.MipLevels = 1;
HRESULT hr = device->device->CreateShaderResourceView(
texture, &resourceDesc, shaderRes.Assign());
if (FAILED(hr))
throw HRError("Failed to create shader resource view", hr);
InitResourceView();
}

View File

@ -95,7 +95,7 @@ void gs_texture_3d::InitTexture(const uint8_t *const *data)
td.Height = height;
td.Depth = depth;
td.MipLevels = genMipmaps ? 0 : levels;
td.Format = dxgiFormat;
td.Format = dxgiFormatResource;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
td.CPUAccessFlags = isDynamic ? D3D11_CPU_ACCESS_WRITE : 0;
td.Usage = isDynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
@ -155,17 +155,29 @@ void gs_texture_3d::InitResourceView()
{
HRESULT hr;
memset(&resourceDesc, 0, sizeof(resourceDesc));
resourceDesc.Format = dxgiFormat;
memset(&viewDesc, 0, sizeof(viewDesc));
viewDesc.Format = dxgiFormatView;
resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
resourceDesc.Texture3D.MostDetailedMip = 0;
resourceDesc.Texture3D.MipLevels = genMipmaps || !levels ? -1 : levels;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
viewDesc.Texture3D.MostDetailedMip = 0;
viewDesc.Texture3D.MipLevels = genMipmaps || !levels ? -1 : levels;
hr = device->device->CreateShaderResourceView(texture, &resourceDesc,
hr = device->device->CreateShaderResourceView(texture, &viewDesc,
shaderRes.Assign());
if (FAILED(hr))
throw HRError("Failed to create resource view", hr);
throw HRError("Failed to create 3D SRV", hr);
viewDescLinear = viewDesc;
viewDescLinear.Format = dxgiFormatViewLinear;
if (dxgiFormatView == dxgiFormatViewLinear) {
shaderResLinear = shaderRes;
} else {
hr = device->device->CreateShaderResourceView(
texture, &viewDescLinear, shaderResLinear.Assign());
if (FAILED(hr))
throw HRError("Failed to create linear 3D SRV", hr);
}
}
#define SHARED_FLAGS (GS_SHARED_TEX | GS_SHARED_KM_TEX)
@ -180,7 +192,9 @@ gs_texture_3d::gs_texture_3d(gs_device_t *device, uint32_t width,
height(height),
depth(depth),
flags(flags_),
dxgiFormat(ConvertGSTextureFormat(format)),
dxgiFormatResource(ConvertGSTextureFormatResource(format)),
dxgiFormatView(ConvertGSTextureFormatView(format)),
dxgiFormatViewLinear(ConvertGSTextureFormatViewLinear(format)),
isDynamic((flags_ & GS_DYNAMIC) != 0),
isShared((flags_ & SHARED_FLAGS) != 0),
genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0),
@ -203,24 +217,19 @@ gs_texture_3d::gs_texture_3d(gs_device_t *device, uint32_t handle)
texture->GetDesc(&td);
const gs_color_format format = ConvertDXGITextureFormat(td.Format);
this->type = GS_TEXTURE_3D;
this->format = ConvertDXGITextureFormat(td.Format);
this->format = format;
this->levels = 1;
this->device = device;
this->width = td.Width;
this->height = td.Height;
this->depth = td.Depth;
this->dxgiFormat = td.Format;
this->dxgiFormatResource = ConvertGSTextureFormatResource(format);
this->dxgiFormatView = ConvertGSTextureFormatView(format);
this->dxgiFormatViewLinear = ConvertGSTextureFormatViewLinear(format);
memset(&resourceDesc, 0, sizeof(resourceDesc));
resourceDesc.Format = td.Format;
resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
resourceDesc.Texture3D.MostDetailedMip = 0;
resourceDesc.Texture3D.MipLevels = 1;
hr = device->device->CreateShaderResourceView(texture, &resourceDesc,
shaderRes.Assign());
if (FAILED(hr))
throw HRError("Failed to create shader resource view", hr);
InitResourceView();
}

View File

@ -525,8 +525,13 @@ static void program_set_param_data(struct gs_program *program,
}
glUniform1i(pp->obj, pp->param->texture_id);
device_load_texture(program->device, pp->param->texture,
pp->param->texture_id);
if (pp->param->srgb)
device_load_texture_srgb(program->device,
pp->param->texture,
pp->param->texture_id);
else
device_load_texture(program->device, pp->param->texture,
pp->param->texture_id);
}
}
@ -757,7 +762,7 @@ void gs_shader_set_val(gs_sparam_t *param, const void *val, size_t size)
expected_size = sizeof(float) * 4 * 4;
break;
case GS_SHADER_PARAM_TEXTURE:
expected_size = sizeof(void *);
expected_size = sizeof(struct gs_shader_texture);
break;
default:
expected_size = 0;
@ -773,10 +778,14 @@ void gs_shader_set_val(gs_sparam_t *param, const void *val, size_t size)
return;
}
if (param->type == GS_SHADER_PARAM_TEXTURE)
gs_shader_set_texture(param, *(gs_texture_t **)val);
else
if (param->type == GS_SHADER_PARAM_TEXTURE) {
struct gs_shader_texture shader_tex;
memcpy(&shader_tex, val, sizeof(shader_tex));
gs_shader_set_texture(param, shader_tex.tex);
param->srgb = shader_tex.srgb;
} else {
da_copy_array(param->cur_value, val, size);
}
}
void gs_shader_set_default(gs_sparam_t *param)

View File

@ -416,20 +416,19 @@ static bool gl_write_texture_code(struct gl_shader_parser *glsp,
const char *function_end = ")";
if (cf_token_is(cfp, "Sample"))
if (cf_token_is(cfp, "Sample")) {
written = gl_write_texture_call(glsp, var, "texture", true);
else if (cf_token_is(cfp, "SampleBias"))
} else if (cf_token_is(cfp, "SampleBias")) {
written = gl_write_texture_call(glsp, var, "texture", true);
else if (cf_token_is(cfp, "SampleGrad"))
} else if (cf_token_is(cfp, "SampleGrad")) {
written = gl_write_texture_call(glsp, var, "textureGrad", true);
else if (cf_token_is(cfp, "SampleLevel"))
} else if (cf_token_is(cfp, "SampleLevel")) {
written = gl_write_texture_call(glsp, var, "textureLod", true);
else if (cf_token_is(cfp, "Load")) {
written = gl_write_texture_call(glsp, var, "texelFetch", false);
dstr_cat(&glsp->gl_string, "(");
function_end = (strcmp(var->type, "texture3d") == 0)
? ").xyz, 0)"
: ").xy, 0)";
} else if (cf_token_is(cfp, "Load")) {
const char *const func = (strcmp(var->type, "texture3d") == 0)
? "obs_load_3d"
: "obs_load_2d";
written = gl_write_texture_call(glsp, var, func, false);
}
if (!written)
@ -744,6 +743,26 @@ static bool gl_shader_buildstring(struct gl_shader_parser *glsp)
dstr_copy(&glsp->gl_string, "#version 330\n\n");
dstr_cat(&glsp->gl_string, "const bool obs_glsl_compile = true;\n\n");
dstr_cat(&glsp->gl_string,
"vec4 obs_load_2d(sampler2D s, ivec3 p_lod)\n");
dstr_cat(&glsp->gl_string, "{\n");
dstr_cat(&glsp->gl_string, "\tint lod = p_lod.z;\n");
dstr_cat(&glsp->gl_string, "\tvec2 size = textureSize(s, lod);\n");
dstr_cat(&glsp->gl_string,
"\tvec2 p = (vec2(p_lod.xy) + 0.5) / size;\n");
dstr_cat(&glsp->gl_string, "\tvec4 color = textureLod(s, p, lod);\n");
dstr_cat(&glsp->gl_string, "\treturn color;\n");
dstr_cat(&glsp->gl_string, "}\n\n");
dstr_cat(&glsp->gl_string,
"vec4 obs_load_3d(sampler3D s, ivec4 p_lod)\n");
dstr_cat(&glsp->gl_string, "{\n");
dstr_cat(&glsp->gl_string, "\tint lod = p_lod.w;\n");
dstr_cat(&glsp->gl_string, "\tvec3 size = textureSize(s, lod);\n");
dstr_cat(&glsp->gl_string,
"\tvec3 p = (vec3(p_lod.xyz) + 0.5) / size;\n");
dstr_cat(&glsp->gl_string, "\tvec4 color = textureLod(s, p, lod);\n");
dstr_cat(&glsp->gl_string, "\treturn color;\n");
dstr_cat(&glsp->gl_string, "}\n\n");
gl_write_params(glsp);
gl_write_inputs(glsp, main_func);
gl_write_outputs(glsp, main_func);

View File

@ -138,6 +138,12 @@ static bool gl_init_extensions(struct gs_device *device)
gl_enable_debug();
if (!GLAD_GL_EXT_texture_sRGB_decode) {
blog(LOG_ERROR, "OpenGL extension EXT_texture_sRGB_decode "
"is required.");
return false;
}
gl_enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
if (GLAD_GL_VERSION_4_3 || GLAD_GL_ARB_copy_image)
@ -245,6 +251,16 @@ int device_create(gs_device_t **p_device, uint32_t adapter)
gl_enable(GL_CULL_FACE);
gl_gen_vertex_arrays(1, &device->empty_vao);
struct gs_sampler_info raw_load_info;
raw_load_info.filter = GS_FILTER_POINT;
raw_load_info.address_u = GS_ADDRESS_BORDER;
raw_load_info.address_v = GS_ADDRESS_BORDER;
raw_load_info.address_w = GS_ADDRESS_BORDER;
raw_load_info.max_anisotropy = 1;
raw_load_info.border_color = 0;
device->raw_load_sampler =
device_samplerstate_create(device, &raw_load_info);
gl_clear_context(device);
device->cur_swap = NULL;
@ -273,6 +289,7 @@ void device_destroy(gs_device_t *device)
while (device->first_program)
gs_program_destroy(device->first_program);
samplerstate_release(device->raw_load_sampler);
gl_delete_vertex_arrays(1, &device->empty_vao);
da_free(device->proj_stack);
@ -481,7 +498,8 @@ static inline struct gs_shader_param *get_texture_param(gs_device_t *device,
return NULL;
}
void device_load_texture(gs_device_t *device, gs_texture_t *tex, int unit)
static void device_load_texture_internal(gs_device_t *device, gs_texture_t *tex,
int unit, GLint decode)
{
struct gs_shader_param *param;
struct gs_sampler_state *sampler;
@ -512,14 +530,17 @@ void device_load_texture(gs_device_t *device, gs_texture_t *tex, int unit)
if (!tex)
return;
// texelFetch doesn't need a sampler
if (param->sampler_id != (size_t)-1)
sampler = device->cur_samplers[param->sampler_id];
else
sampler = NULL;
sampler = device->raw_load_sampler;
if (!gl_bind_texture(tex->gl_target, tex->texture))
goto fail;
if (!gl_tex_param_i(tex->gl_target, GL_TEXTURE_SRGB_DECODE_EXT, decode))
goto fail;
if (sampler && !load_texture_sampler(tex, sampler))
goto fail;
@ -529,6 +550,16 @@ fail:
blog(LOG_ERROR, "device_load_texture (GL) failed");
}
void device_load_texture(gs_device_t *device, gs_texture_t *tex, int unit)
{
device_load_texture_internal(device, tex, unit, GL_SKIP_DECODE_EXT);
}
void device_load_texture_srgb(gs_device_t *device, gs_texture_t *tex, int unit)
{
device_load_texture_internal(device, tex, unit, GL_DECODE_EXT);
}
static bool load_sampler_on_textures(gs_device_t *device, gs_samplerstate_t *ss,
int sampler_unit)
{
@ -853,6 +884,21 @@ fail:
blog(LOG_ERROR, "device_set_cube_render_target (GL) failed");
}
void device_enable_framebuffer_srgb(gs_device_t *device, bool enable)
{
if (enable)
gl_enable(GL_FRAMEBUFFER_SRGB);
else
gl_disable(GL_FRAMEBUFFER_SRGB);
}
bool device_framebuffer_srgb_enabled(gs_device_t *device)
{
const GLboolean enabled = glIsEnabled(GL_FRAMEBUFFER_SRGB);
gl_success("glIsEnabled");
return enabled == GL_TRUE;
}
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,

View File

@ -71,6 +71,12 @@ static inline GLenum convert_gs_format(enum gs_color_format format)
return GL_RGBA;
case GS_DXT5:
return GL_RGBA;
case GS_RGBA_UNORM:
return GL_RGBA;
case GS_BGRX_UNORM:
return GL_BGRA;
case GS_BGRA_UNORM:
return GL_BGRA;
case GS_UNKNOWN:
return 0;
}
@ -86,11 +92,11 @@ static inline GLenum convert_gs_internal_format(enum gs_color_format format)
case GS_R8:
return GL_R8;
case GS_RGBA:
return GL_RGBA;
return GL_SRGB8_ALPHA8;
case GS_BGRX:
return GL_RGB;
return GL_SRGB8;
case GS_BGRA:
return GL_RGBA;
return GL_SRGB8_ALPHA8;
case GS_R10G10B10A2:
return GL_RGB10_A2;
case GS_RGBA16:
@ -117,6 +123,12 @@ static inline GLenum convert_gs_internal_format(enum gs_color_format format)
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
case GS_DXT5:
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case GS_RGBA_UNORM:
return GL_RGBA;
case GS_BGRX_UNORM:
return GL_RGB;
case GS_BGRA_UNORM:
return GL_RGBA;
case GS_UNKNOWN:
return 0;
}
@ -163,6 +175,12 @@ static inline GLenum get_gl_format_type(enum gs_color_format format)
return GL_UNSIGNED_BYTE;
case GS_DXT5:
return GL_UNSIGNED_BYTE;
case GS_RGBA_UNORM:
return GL_UNSIGNED_BYTE;
case GS_BGRX_UNORM:
return GL_UNSIGNED_BYTE;
case GS_BGRA_UNORM:
return GL_UNSIGNED_BYTE;
case GS_UNKNOWN:
return 0;
}
@ -411,6 +429,7 @@ struct gs_shader_param {
int array_count;
struct gs_texture *texture;
bool srgb;
DARRAY(uint8_t) cur_value;
DARRAY(uint8_t) def_value;
@ -596,6 +615,7 @@ struct gs_device {
enum copy_type copy_type;
GLuint empty_vao;
gs_samplerstate_t *raw_load_sampler;
gs_texture_t *cur_render_target;
gs_zstencil_t *cur_zstencil_buffer;

View File

@ -186,8 +186,10 @@ struct winrt_capture {
}
if (!texture) {
texture = gs_texture_create_gdi(texture_width,
texture_height);
texture = gs_texture_create(texture_width,
texture_height,
GS_BGRA, 1, NULL,
0);
}
if (client_area) {
@ -491,7 +493,15 @@ static void draw_texture(struct winrt_capture *capture, gs_effect_t *effect)
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
size_t passes;
gs_effect_set_texture(image, texture);
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
if (linear_srgb)
gs_effect_set_texture_srgb(image, texture);
else
gs_effect_set_texture(image, texture);
passes = gs_technique_begin(tech);
for (size_t i = 0; i < passes; i++) {
@ -502,6 +512,8 @@ static void draw_texture(struct winrt_capture *capture, gs_effect_t *effect)
}
}
gs_technique_end(tech);
gs_enable_framebuffer_srgb(previous);
}
extern "C" EXPORT BOOL winrt_capture_active(const struct winrt_capture *capture)

View File

@ -88,6 +88,8 @@ EXPORT void device_load_indexbuffer(gs_device_t *device,
gs_indexbuffer_t *indexbuffer);
EXPORT void device_load_texture(gs_device_t *device, gs_texture_t *tex,
int unit);
EXPORT void device_load_texture_srgb(gs_device_t *device, gs_texture_t *tex,
int unit);
EXPORT void device_load_samplerstate(gs_device_t *device,
gs_samplerstate_t *samplerstate, int unit);
EXPORT void device_load_vertexshader(gs_device_t *device,
@ -105,6 +107,8 @@ EXPORT void device_set_render_target(gs_device_t *device, gs_texture_t *tex,
EXPORT void device_set_cube_render_target(gs_device_t *device,
gs_texture_t *cubetex, int side,
gs_zstencil_t *zstencil);
EXPORT void device_enable_framebuffer_srgb(gs_device_t *device, bool enable);
EXPORT bool device_framebuffer_srgb_enabled(gs_device_t *device);
EXPORT void device_copy_texture(gs_device_t *device, gs_texture_t *dst,
gs_texture_t *src);
EXPORT void device_copy_texture_region(gs_device_t *device, gs_texture_t *dst,

View File

@ -486,7 +486,18 @@ void gs_effect_set_color(gs_eparam_t *param, uint32_t argb)
void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val)
{
effect_setval_inline(param, &val, sizeof(gs_texture_t *));
struct gs_shader_texture shader_tex;
shader_tex.tex = val;
shader_tex.srgb = false;
effect_setval_inline(param, &shader_tex, sizeof(shader_tex));
}
void gs_effect_set_texture_srgb(gs_eparam_t *param, gs_texture_t *val)
{
struct gs_shader_texture shader_tex;
shader_tex.tex = val;
shader_tex.srgb = true;
effect_setval_inline(param, &shader_tex, sizeof(shader_tex));
}
void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size)

View File

@ -82,6 +82,8 @@ bool load_graphics_imports(struct gs_exports *exports, void *module,
GRAPHICS_IMPORT(device_get_zstencil_target);
GRAPHICS_IMPORT(device_set_render_target);
GRAPHICS_IMPORT(device_set_cube_render_target);
GRAPHICS_IMPORT(device_enable_framebuffer_srgb);
GRAPHICS_IMPORT(device_framebuffer_srgb_enabled);
GRAPHICS_IMPORT(device_copy_texture_region);
GRAPHICS_IMPORT(device_copy_texture);
GRAPHICS_IMPORT(device_stage_texture);

View File

@ -106,6 +106,9 @@ struct gs_exports {
void (*device_set_cube_render_target)(gs_device_t *device,
gs_texture_t *cubetex, int side,
gs_zstencil_t *zstencil);
void (*device_enable_framebuffer_srgb)(gs_device_t *device,
bool enable);
bool (*device_framebuffer_srgb_enabled)(gs_device_t *device);
void (*device_copy_texture)(gs_device_t *device, gs_texture_t *dst,
gs_texture_t *src);
void (*device_copy_texture_region)(gs_device_t *device,
@ -362,4 +365,6 @@ struct graphics_subsystem {
struct blend_state cur_blend_state;
DARRAY(struct blend_state) blend_state_stack;
bool linear_srgb;
};

View File

@ -1708,6 +1708,50 @@ void gs_set_cube_render_target(gs_texture_t *cubetex, int side,
graphics->device, cubetex, side, zstencil);
}
void gs_enable_framebuffer_srgb(bool enable)
{
graphics_t *graphics = thread_graphics;
if (!gs_valid("gs_enable_framebuffer_srgb"))
return;
graphics->exports.device_enable_framebuffer_srgb(graphics->device,
enable);
}
bool gs_framebuffer_srgb_enabled(void)
{
graphics_t *graphics = thread_graphics;
if (!gs_valid("gs_framebuffer_srgb_enabled"))
return false;
return graphics->exports.device_framebuffer_srgb_enabled(
graphics->device);
}
bool gs_get_linear_srgb(void)
{
graphics_t *graphics = thread_graphics;
if (!gs_valid("gs_get_linear_srgb"))
return false;
return graphics->linear_srgb;
}
bool gs_set_linear_srgb(bool linear_srgb)
{
graphics_t *graphics = thread_graphics;
if (!gs_valid("gs_set_linear_srgb"))
return false;
const bool previous = graphics->linear_srgb;
graphics->linear_srgb = linear_srgb;
return previous;
}
void gs_copy_texture(gs_texture_t *dst, gs_texture_t *src)
{
graphics_t *graphics = thread_graphics;

View File

@ -73,6 +73,9 @@ enum gs_color_format {
GS_DXT3,
GS_DXT5,
GS_R8G8,
GS_RGBA_UNORM,
GS_BGRX_UNORM,
GS_BGRA_UNORM,
};
enum gs_zstencil_format {
@ -302,6 +305,11 @@ enum gs_shader_param_type {
GS_SHADER_PARAM_TEXTURE,
};
struct gs_shader_texture {
gs_texture_t *tex;
bool srgb;
};
#ifndef SWIG
struct gs_shader_param_info {
enum gs_shader_param_type type;
@ -423,6 +431,7 @@ EXPORT void gs_effect_set_vec2(gs_eparam_t *param, const struct vec2 *val);
EXPORT void gs_effect_set_vec3(gs_eparam_t *param, const struct vec3 *val);
EXPORT void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val);
EXPORT void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val);
EXPORT void gs_effect_set_texture_srgb(gs_eparam_t *param, gs_texture_t *val);
EXPORT void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size);
EXPORT void gs_effect_set_default(gs_eparam_t *param);
EXPORT size_t gs_effect_get_val_size(gs_eparam_t *param);
@ -667,6 +676,12 @@ EXPORT void gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil);
EXPORT void gs_set_cube_render_target(gs_texture_t *cubetex, int side,
gs_zstencil_t *zstencil);
EXPORT void gs_enable_framebuffer_srgb(bool enable);
EXPORT bool gs_framebuffer_srgb_enabled(void);
EXPORT bool gs_get_linear_srgb(void);
EXPORT bool gs_set_linear_srgb(bool linear_srgb);
EXPORT void gs_copy_texture(gs_texture_t *dst, gs_texture_t *src);
EXPORT void gs_copy_texture_region(gs_texture_t *dst, uint32_t dst_x,
uint32_t dst_y, gs_texture_t *src,
@ -939,6 +954,12 @@ static inline uint32_t gs_get_format_bpp(enum gs_color_format format)
return 8;
case GS_R8G8:
return 16;
case GS_RGBA_UNORM:
return 32;
case GS_BGRX_UNORM:
return 32;
case GS_BGRA_UNORM:
return 32;
case GS_UNKNOWN:
return 0;
}
@ -951,6 +972,18 @@ static inline bool gs_is_compressed_format(enum gs_color_format format)
return (format == GS_DXT1 || format == GS_DXT3 || format == GS_DXT5);
}
static inline bool gs_is_srgb_format(enum gs_color_format format)
{
switch (format) {
case GS_RGBA:
case GS_BGRX:
case GS_BGRA:
return true;
default:
return false;
}
}
static inline uint32_t gs_get_total_levels(uint32_t width, uint32_t height,
uint32_t depth)
{

View File

@ -199,43 +199,126 @@ static inline void vec4_ceil(struct vec4 *dst, const struct vec4 *v)
static inline uint32_t vec4_to_rgba(const struct vec4 *src)
{
uint32_t val;
val = (uint32_t)((double)src->x * 255.0);
val |= (uint32_t)((double)src->y * 255.0) << 8;
val |= (uint32_t)((double)src->z * 255.0) << 16;
val |= (uint32_t)((double)src->w * 255.0) << 24;
val = (uint32_t)((src->x * 255.0f) + 0.5f);
val |= (uint32_t)((src->y * 255.0f) + 0.5f) << 8;
val |= (uint32_t)((src->z * 255.0f) + 0.5f) << 16;
val |= (uint32_t)((src->w * 255.0f) + 0.5f) << 24;
return val;
}
static inline uint32_t vec4_to_bgra(const struct vec4 *src)
{
uint32_t val;
val = (uint32_t)((double)src->z * 255.0);
val |= (uint32_t)((double)src->y * 255.0) << 8;
val |= (uint32_t)((double)src->x * 255.0) << 16;
val |= (uint32_t)((double)src->w * 255.0) << 24;
val = (uint32_t)((src->z * 255.0f) + 0.5f);
val |= (uint32_t)((src->y * 255.0f) + 0.5f) << 8;
val |= (uint32_t)((src->x * 255.0f) + 0.5f) << 16;
val |= (uint32_t)((src->w * 255.0f) + 0.5f) << 24;
return val;
}
static inline void vec4_from_rgba(struct vec4 *dst, uint32_t rgba)
{
dst->x = (float)((double)(rgba & 0xFF) * (1.0 / 255.0));
dst->x = (float)(rgba & 0xFF) / 255.0f;
rgba >>= 8;
dst->y = (float)((double)(rgba & 0xFF) * (1.0 / 255.0));
dst->y = (float)(rgba & 0xFF) / 255.0f;
rgba >>= 8;
dst->z = (float)((double)(rgba & 0xFF) * (1.0 / 255.0));
dst->z = (float)(rgba & 0xFF) / 255.0f;
rgba >>= 8;
dst->w = (float)((double)(rgba & 0xFF) * (1.0 / 255.0));
dst->w = (float)rgba / 255.0f;
}
static inline void vec4_from_bgra(struct vec4 *dst, uint32_t bgra)
{
dst->z = (float)((double)(bgra & 0xFF) * (1.0 / 255.0));
dst->z = (float)(bgra & 0xFF) / 255.0f;
bgra >>= 8;
dst->y = (float)((double)(bgra & 0xFF) * (1.0 / 255.0));
dst->y = (float)(bgra & 0xFF) / 255.0f;
bgra >>= 8;
dst->x = (float)((double)(bgra & 0xFF) * (1.0 / 255.0));
dst->x = (float)(bgra & 0xFF) / 255.0f;
bgra >>= 8;
dst->w = (float)((double)(bgra & 0xFF) * (1.0 / 255.0));
dst->w = (float)bgra / 255.0f;
}
static inline float srgb_nonlinear_to_linear(float u)
{
return (u <= 0.04045f) ? (u / 12.92f)
: powf((u + 0.055f) / 1.055f, 2.4f);
}
static inline void vec4_from_rgba_srgb(struct vec4 *dst, uint32_t rgba)
{
dst->x = srgb_nonlinear_to_linear((float)(rgba & 0xFF) / 255.0f);
rgba >>= 8;
dst->y = srgb_nonlinear_to_linear((float)(rgba & 0xFF) / 255.0f);
rgba >>= 8;
dst->z = srgb_nonlinear_to_linear((float)(rgba & 0xFF) / 255.0f);
rgba >>= 8;
dst->w = (float)rgba / 255.0f;
}
static inline void vec4_from_bgra_srgb(struct vec4 *dst, uint32_t bgra)
{
dst->z = srgb_nonlinear_to_linear((float)(bgra & 0xFF) / 255.0f);
bgra >>= 8;
dst->y = srgb_nonlinear_to_linear((float)(bgra & 0xFF) / 255.0f);
bgra >>= 8;
dst->x = srgb_nonlinear_to_linear((float)(bgra & 0xFF) / 255.0f);
bgra >>= 8;
dst->w = (float)bgra / 255.0f;
}
static inline void vec4_from_rgba_srgb_premultiply(struct vec4 *dst,
uint32_t rgba)
{
vec4_from_rgba_srgb(dst, rgba);
dst->x *= dst->w;
dst->y *= dst->w;
dst->z *= dst->w;
}
static inline void vec4_from_bgra_srgb_premultiply(struct vec4 *dst,
uint32_t bgra)
{
vec4_from_bgra_srgb(dst, bgra);
dst->x *= dst->w;
dst->y *= dst->w;
dst->z *= dst->w;
}
static inline float srgb_linear_to_nonlinear(float u)
{
return (u <= 0.0031308f) ? (12.92f * u)
: ((1.055f * powf(u, 1.0f / 2.4f)) - 0.055f);
}
static inline uint32_t vec4_to_rgba_srgb(const struct vec4 *src)
{
uint32_t val;
val = (uint32_t)((srgb_linear_to_nonlinear(src->x) * 255.0f) + 0.5f);
val |= (uint32_t)((srgb_linear_to_nonlinear(src->y) * 255.0f) + 0.5f)
<< 8;
val |= (uint32_t)((srgb_linear_to_nonlinear(src->z) * 255.0f) + 0.5f)
<< 16;
val |= (uint32_t)((src->w * 255.0f) + 0.5f) << 24;
return val;
}
static inline uint32_t vec4_to_bgra_srgb(const struct vec4 *src)
{
uint32_t val;
val = (uint32_t)((srgb_linear_to_nonlinear(src->z) * 255.0f) + 0.5f);
val |= (uint32_t)((srgb_linear_to_nonlinear(src->y) * 255.0f) + 0.5f)
<< 8;
val |= (uint32_t)((srgb_linear_to_nonlinear(src->x) * 255.0f) + 0.5f)
<< 16;
val |= (uint32_t)((src->w * 255.0f) + 0.5f) << 24;
return val;
}
static inline void vec4_srgb_linear_to_nonlinear(struct vec4 *dst)
{
dst->x = srgb_linear_to_nonlinear(dst->x);
dst->y = srgb_linear_to_nonlinear(dst->y);
dst->y = srgb_linear_to_nonlinear(dst->y);
}
EXPORT void vec4_transform(struct vec4 *dst, const struct vec4 *v,

View File

@ -570,6 +570,7 @@ static inline void render_item(struct obs_scene_item *item)
}
}
const bool previous = gs_set_linear_srgb(true);
gs_matrix_push();
gs_matrix_mul(&item->draw_transform);
if (item->item_render) {
@ -578,6 +579,7 @@ static inline void render_item(struct obs_scene_item *item)
obs_source_video_render(item->source);
}
gs_matrix_pop();
gs_set_linear_srgb(previous);
cleanup:
GS_DEBUG_MARKER_END();

View File

@ -346,6 +346,25 @@ static inline gs_effect_t *get_effect(enum obs_deinterlace_mode mode)
return NULL;
}
static bool deinterlace_linear_required(enum obs_deinterlace_mode mode)
{
switch (mode) {
case OBS_DEINTERLACE_MODE_DISABLE:
case OBS_DEINTERLACE_MODE_DISCARD:
case OBS_DEINTERLACE_MODE_RETRO:
return false;
case OBS_DEINTERLACE_MODE_BLEND:
case OBS_DEINTERLACE_MODE_BLEND_2X:
case OBS_DEINTERLACE_MODE_LINEAR:
case OBS_DEINTERLACE_MODE_LINEAR_2X:
case OBS_DEINTERLACE_MODE_YADIF:
case OBS_DEINTERLACE_MODE_YADIF_2X:
return true;
}
return false;
}
void deinterlace_render(obs_source_t *s)
{
gs_effect_t *effect = s->deinterlace_effect;
@ -372,8 +391,21 @@ void deinterlace_render(obs_source_t *s)
if (!cur_tex || !prev_tex || !s->async_width || !s->async_height)
return;
gs_effect_set_texture(image, cur_tex);
gs_effect_set_texture(prev, prev_tex);
const bool linear_srgb =
gs_get_linear_srgb() ||
deinterlace_linear_required(s->deinterlace_mode);
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
if (linear_srgb) {
gs_effect_set_texture_srgb(image, cur_tex);
gs_effect_set_texture_srgb(prev, prev_tex);
} else {
gs_effect_set_texture(image, cur_tex);
gs_effect_set_texture(prev, prev_tex);
}
gs_effect_set_int(field, s->deinterlace_top_first);
gs_effect_set_vec2(dimensions, &size);
@ -385,6 +417,8 @@ void deinterlace_render(obs_source_t *s)
while (gs_effect_loop(effect, "Draw"))
gs_draw_sprite(NULL, s->async_flip ? GS_FLIP_V : 0,
s->async_width, s->async_height);
gs_enable_framebuffer_srgb(previous);
}
static void enable_deinterlacing(obs_source_t *source,

View File

@ -2012,9 +2012,21 @@ static inline void obs_source_draw_texture(struct obs_source *source,
tex = gs_texrender_get_texture(source->async_texrender);
param = gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(param, tex);
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
if (linear_srgb) {
gs_effect_set_texture_srgb(param, tex);
} else {
gs_effect_set_texture(param, tex);
}
gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0);
gs_enable_framebuffer_srgb(previous);
}
static void obs_source_draw_async_texture(struct obs_source *source)
@ -3588,7 +3600,15 @@ static inline void render_filter_tex(gs_texture_t *tex, gs_effect_t *effect,
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
size_t passes, i;
gs_effect_set_texture(image, tex);
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
if (linear_srgb)
gs_effect_set_texture_srgb(image, tex);
else
gs_effect_set_texture(image, tex);
passes = gs_technique_begin(tech);
for (i = 0; i < passes; i++) {
@ -3597,6 +3617,8 @@ static inline void render_filter_tex(gs_texture_t *tex, gs_effect_t *effect,
gs_technique_end_pass(tech);
}
gs_technique_end(tech);
gs_enable_framebuffer_srgb(previous);
}
static inline bool can_bypass(obs_source_t *target, obs_source_t *parent,
@ -4164,21 +4186,27 @@ void obs_source_draw_set_color_matrix(const struct matrix4 *color_matrix,
void obs_source_draw(gs_texture_t *texture, int x, int y, uint32_t cx,
uint32_t cy, bool flip)
{
gs_effect_t *effect = gs_get_effect();
bool change_pos = (x != 0 || y != 0);
gs_eparam_t *image;
if (!obs_ptr_valid(texture, "obs_source_draw"))
return;
gs_effect_t *effect = gs_get_effect();
if (!effect) {
blog(LOG_WARNING, "obs_source_draw: no active effect!");
return;
}
if (!obs_ptr_valid(texture, "obs_source_draw"))
return;
const bool linear_srgb = gs_get_linear_srgb();
image = gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(image, texture);
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
if (linear_srgb)
gs_effect_set_texture_srgb(image, texture);
else
gs_effect_set_texture(image, texture);
const bool change_pos = (x != 0 || y != 0);
if (change_pos) {
gs_matrix_push();
gs_matrix_translate3f((float)x, (float)y, 0.0f);
@ -4188,6 +4216,8 @@ void obs_source_draw(gs_texture_t *texture, int x, int y, uint32_t cx,
if (change_pos)
gs_matrix_pop();
gs_enable_framebuffer_srgb(previous);
}
void obs_source_inc_showing(obs_source_t *source)

View File

@ -256,8 +256,9 @@ static inline gs_texture_t *render_output_texture(struct obs_core_video *video)
gs_effect_set_vec2(bres_i, &base_i);
}
gs_effect_set_texture(image, texture);
gs_effect_set_texture_srgb(image, texture);
gs_enable_framebuffer_srgb(true);
gs_enable_blending(false);
passes = gs_technique_begin(tech);
for (i = 0; i < passes; i++) {
@ -267,6 +268,7 @@ static inline gs_texture_t *render_output_texture(struct obs_core_video *video)
}
gs_technique_end(tech);
gs_enable_blending(true);
gs_enable_framebuffer_srgb(false);
profile_end(render_output_texture_name);

View File

@ -1,7 +1,8 @@
#include <obs-module.h>
struct color_source {
uint32_t color;
struct vec4 color;
struct vec4 color_srgb;
uint32_t width;
uint32_t height;
@ -22,7 +23,8 @@ static void color_source_update(void *data, obs_data_t *settings)
uint32_t width = (uint32_t)obs_data_get_int(settings, "width");
uint32_t height = (uint32_t)obs_data_get_int(settings, "height");
context->color = color;
vec4_from_rgba(&context->color, color);
vec4_from_rgba_srgb(&context->color_srgb, color);
context->width = width;
context->height = height;
}
@ -50,8 +52,8 @@ static obs_properties_t *color_source_properties(void *unused)
obs_properties_t *props = obs_properties_create();
obs_properties_add_color(props, "color",
obs_module_text("ColorSource.Color"));
obs_properties_add_color_alpha(props, "color",
obs_module_text("ColorSource.Color"));
obs_properties_add_int(props, "width",
obs_module_text("ColorSource.Width"), 0, 4096,
@ -64,19 +66,14 @@ static obs_properties_t *color_source_properties(void *unused)
return props;
}
static void color_source_render(void *data, gs_effect_t *effect)
static void color_source_render_helper(struct color_source *context,
struct vec4 *colorVal)
{
UNUSED_PARAMETER(effect);
struct color_source *context = data;
gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
gs_technique_t *tech = gs_effect_get_technique(solid, "Solid");
struct vec4 colorVal;
vec4_from_rgba(&colorVal, context->color);
gs_effect_set_vec4(color, &colorVal);
gs_effect_set_vec4(color, colorVal);
gs_technique_begin(tech);
gs_technique_begin_pass(tech, 0);
@ -87,6 +84,27 @@ static void color_source_render(void *data, gs_effect_t *effect)
gs_technique_end(tech);
}
static void color_source_render(void *data, gs_effect_t *effect)
{
UNUSED_PARAMETER(effect);
struct color_source *context = data;
/* need linear path for correct alpha blending */
const bool linear_srgb = gs_get_linear_srgb() ||
(context->color.w < 1.0f);
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
if (linear_srgb)
color_source_render_helper(context, &context->color_srgb);
else
color_source_render_helper(context, &context->color);
gs_enable_framebuffer_srgb(previous);
}
static uint32_t color_source_getwidth(void *data)
{
struct color_source *context = data;

View File

@ -147,10 +147,21 @@ static void image_source_render(void *data, gs_effect_t *effect)
if (!context->if2.image.texture)
return;
gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"),
context->if2.image.texture);
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
gs_eparam_t *const param = gs_effect_get_param_by_name(effect, "image");
if (linear_srgb)
gs_effect_set_texture_srgb(param, context->if2.image.texture);
else
gs_effect_set_texture(param, context->if2.image.texture);
gs_draw_sprite(context->if2.image.texture, 0, context->if2.image.cx,
context->if2.image.cy);
gs_enable_framebuffer_srgb(previous);
}
static void image_source_tick(void *data, float seconds)

View File

@ -313,11 +313,11 @@ static gs_color_format gs_format_from_tex()
// GS_RGBX format
switch (iformat) {
case GL_RGB:
return GS_BGRX;
return GS_BGRX_UNORM;
case GL_RGBA:
return GS_RGBA;
return GS_RGBA_UNORM;
default:
return GS_RGBA;
return GS_RGBA_UNORM;
}
}
@ -513,7 +513,7 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
XFree(configs);
// Build an OBS texture to bind the pixmap to.
p->gltex = gs_texture_create(p->width, p->height, GS_RGBA, 1, 0,
p->gltex = gs_texture_create(p->width, p->height, GS_RGBA_UNORM, 1, 0,
GS_GL_DUMMYTEX);
GLuint gltex = *(GLuint *)gs_texture_get_obj(p->gltex);
glBindTexture(GL_TEXTURE_2D, gltex);

View File

@ -93,9 +93,17 @@ void xcb_xcursor_render(xcb_xcursor_t *data)
if (!data->tex)
return;
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
gs_effect_t *effect = gs_get_effect();
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(image, data->tex);
if (linear_srgb)
gs_effect_set_texture_srgb(image, data->tex);
else
gs_effect_set_texture(image, data->tex);
gs_blend_state_push();
gs_blend_function(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA);
@ -108,6 +116,8 @@ void xcb_xcursor_render(xcb_xcursor_t *data)
gs_enable_color(true, true, true, true);
gs_blend_state_pop();
gs_enable_framebuffer_srgb(previous);
}
void xcb_xcursor_offset(xcb_xcursor_t *data, const int x_org, const int y_org)

View File

@ -108,9 +108,17 @@ void xcursor_render(xcursor_t *data, int x_offset, int y_offset)
if (!data->tex)
return;
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
gs_effect_t *effect = gs_get_effect();
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(image, data->tex);
if (linear_srgb)
gs_effect_set_texture_srgb(image, data->tex);
else
gs_effect_set_texture(image, data->tex);
gs_blend_state_push();
gs_blend_function(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA);
@ -124,6 +132,8 @@ void xcursor_render(xcursor_t *data, int x_offset, int y_offset)
gs_enable_color(true, true, true, true);
gs_blend_state_pop();
gs_enable_framebuffer_srgb(previous);
}
void xcursor_offset(xcursor_t *data, int_fast32_t x_org, int_fast32_t y_org)

View File

@ -523,13 +523,23 @@ static void xshm_video_render(void *vptr, gs_effect_t *effect)
if (!data->texture)
return;
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(image, data->texture);
if (linear_srgb)
gs_effect_set_texture_srgb(image, data->texture);
else
gs_effect_set_texture(image, data->texture);
while (gs_effect_loop(effect, "Draw")) {
gs_draw_sprite(data->texture, 0, 0, 0);
}
gs_enable_framebuffer_srgb(previous);
if (data->show_cursor) {
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);

View File

@ -399,13 +399,21 @@ static void display_capture_video_render(void *data, gs_effect_t *effect)
if (!dc->tex || (requires_window(dc->crop) && !dc->on_screen))
return;
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
gs_vertexbuffer_flush(dc->vertbuf);
gs_load_vertexbuffer(dc->vertbuf);
gs_load_indexbuffer(NULL);
gs_load_samplerstate(dc->sampler, 0);
gs_technique_t *tech = gs_effect_get_technique(dc->effect, "Draw");
gs_effect_set_texture(gs_effect_get_param_by_name(dc->effect, "image"),
dc->tex);
gs_eparam_t *param = gs_effect_get_param_by_name(dc->effect, "image");
if (linear_srgb)
gs_effect_set_texture_srgb(param, dc->tex);
else
gs_effect_set_texture(param, dc->tex);
gs_technique_begin(tech);
gs_technique_begin_pass(tech, 0);
@ -413,6 +421,8 @@ static void display_capture_video_render(void *data, gs_effect_t *effect)
gs_technique_end_pass(tech);
gs_technique_end(tech);
gs_enable_framebuffer_srgb(previous);
}
static const char *display_capture_getname(void *unused)

View File

@ -54,19 +54,45 @@ struct chroma_key_filter_data {
float spill;
};
struct chroma_key_filter_data_v2 {
obs_source_t *context;
gs_effect_t *effect;
gs_eparam_t *opacity_param;
gs_eparam_t *contrast_param;
gs_eparam_t *brightness_param;
gs_eparam_t *gamma_param;
gs_eparam_t *pixel_size_param;
gs_eparam_t *chroma_param;
gs_eparam_t *similarity_param;
gs_eparam_t *smoothness_param;
gs_eparam_t *spill_param;
float opacity;
float contrast;
float brightness;
float gamma;
struct vec2 chroma;
float similarity;
float smoothness;
float spill;
};
static const char *chroma_key_name(void *unused)
{
UNUSED_PARAMETER(unused);
return obs_module_text("ChromaKeyFilter");
}
static const float yuv_mat[16] = {0.182586f, -0.100644f, 0.439216f, 0.0f,
0.614231f, -0.338572f, -0.398942f, 0.0f,
0.062007f, 0.439216f, -0.040274f, 0.0f,
0.062745f, 0.501961f, 0.501961f, 1.0f};
static const float cb_vec[] = {-0.100644f, -0.338572f, 0.439216f, 0.501961f};
static const float cr_vec[] = {0.439216f, -0.398942f, -0.040274f, 0.501961f};
static inline void color_settings_update(struct chroma_key_filter_data *filter,
obs_data_t *settings)
static inline void
color_settings_update_v1(struct chroma_key_filter_data *filter,
obs_data_t *settings)
{
uint32_t opacity =
(uint32_t)obs_data_get_int(settings, SETTING_OPACITY);
@ -89,8 +115,29 @@ static inline void color_settings_update(struct chroma_key_filter_data *filter,
vec4_from_rgba(&filter->color, color);
}
static inline void chroma_settings_update(struct chroma_key_filter_data *filter,
obs_data_t *settings)
static inline void
color_settings_update_v2(struct chroma_key_filter_data_v2 *filter,
obs_data_t *settings)
{
filter->opacity =
(float)obs_data_get_int(settings, SETTING_OPACITY) * 0.01f;
double contrast = obs_data_get_double(settings, SETTING_CONTRAST);
contrast = (contrast < 0.0) ? (1.0 / (-contrast + 1.0))
: (contrast + 1.0);
filter->contrast = (float)contrast;
filter->brightness =
(float)obs_data_get_double(settings, SETTING_BRIGHTNESS);
double gamma = obs_data_get_double(settings, SETTING_GAMMA);
gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0));
filter->gamma = (float)gamma;
}
static inline void
chroma_settings_update_v1(struct chroma_key_filter_data *filter,
obs_data_t *settings)
{
int64_t similarity = obs_data_get_int(settings, SETTING_SIMILARITY);
int64_t smoothness = obs_data_get_int(settings, SETTING_SMOOTHNESS);
@ -100,8 +147,8 @@ static inline void chroma_settings_update(struct chroma_key_filter_data *filter,
const char *key_type =
obs_data_get_string(settings, SETTING_COLOR_TYPE);
struct vec4 key_rgb;
struct vec4 key_color_v4;
struct matrix4 yuv_mat_m4;
struct vec4 cb_v4;
struct vec4 cr_v4;
if (strcmp(key_type, "green") == 0)
key_color = 0x00FF00;
@ -112,24 +159,67 @@ static inline void chroma_settings_update(struct chroma_key_filter_data *filter,
vec4_from_rgba(&key_rgb, key_color | 0xFF000000);
memcpy(&yuv_mat_m4, yuv_mat, sizeof(yuv_mat));
vec4_transform(&key_color_v4, &key_rgb, &yuv_mat_m4);
vec2_set(&filter->chroma, key_color_v4.y, key_color_v4.z);
memcpy(&cb_v4, cb_vec, sizeof(cb_v4));
memcpy(&cr_v4, cr_vec, sizeof(cr_v4));
filter->chroma.x = vec4_dot(&key_rgb, &cb_v4);
filter->chroma.y = vec4_dot(&key_rgb, &cr_v4);
filter->similarity = (float)similarity / 1000.0f;
filter->smoothness = (float)smoothness / 1000.0f;
filter->spill = (float)spill / 1000.0f;
}
static void chroma_key_update(void *data, obs_data_t *settings)
static inline void
chroma_settings_update_v2(struct chroma_key_filter_data_v2 *filter,
obs_data_t *settings)
{
int64_t similarity = obs_data_get_int(settings, SETTING_SIMILARITY);
int64_t smoothness = obs_data_get_int(settings, SETTING_SMOOTHNESS);
int64_t spill = obs_data_get_int(settings, SETTING_SPILL);
uint32_t key_color =
(uint32_t)obs_data_get_int(settings, SETTING_KEY_COLOR);
const char *key_type =
obs_data_get_string(settings, SETTING_COLOR_TYPE);
struct vec4 key_rgb;
struct vec4 cb_v4;
struct vec4 cr_v4;
if (strcmp(key_type, "green") == 0)
key_color = 0x00FF00;
else if (strcmp(key_type, "blue") == 0)
key_color = 0xFF9900;
else if (strcmp(key_type, "magenta") == 0)
key_color = 0xFF00FF;
vec4_from_rgba_srgb(&key_rgb, key_color | 0xFF000000);
memcpy(&cb_v4, cb_vec, sizeof(cb_v4));
memcpy(&cr_v4, cr_vec, sizeof(cr_v4));
filter->chroma.x = vec4_dot(&key_rgb, &cb_v4);
filter->chroma.y = vec4_dot(&key_rgb, &cr_v4);
filter->similarity = (float)similarity / 1000.0f;
filter->smoothness = (float)smoothness / 1000.0f;
filter->spill = (float)spill / 1000.0f;
}
static void chroma_key_update_v1(void *data, obs_data_t *settings)
{
struct chroma_key_filter_data *filter = data;
color_settings_update(filter, settings);
chroma_settings_update(filter, settings);
color_settings_update_v1(filter, settings);
chroma_settings_update_v1(filter, settings);
}
static void chroma_key_destroy(void *data)
static void chroma_key_update_v2(void *data, obs_data_t *settings)
{
struct chroma_key_filter_data_v2 *filter = data;
color_settings_update_v2(filter, settings);
chroma_settings_update_v2(filter, settings);
}
static void chroma_key_destroy_v1(void *data)
{
struct chroma_key_filter_data *filter = data;
@ -142,7 +232,20 @@ static void chroma_key_destroy(void *data)
bfree(data);
}
static void *chroma_key_create(obs_data_t *settings, obs_source_t *context)
static void chroma_key_destroy_v2(void *data)
{
struct chroma_key_filter_data_v2 *filter = data;
if (filter->effect) {
obs_enter_graphics();
gs_effect_destroy(filter->effect);
obs_leave_graphics();
}
bfree(data);
}
static void *chroma_key_create_v1(obs_data_t *settings, obs_source_t *context)
{
struct chroma_key_filter_data *filter =
bzalloc(sizeof(struct chroma_key_filter_data));
@ -179,15 +282,60 @@ static void *chroma_key_create(obs_data_t *settings, obs_source_t *context)
bfree(effect_path);
if (!filter->effect) {
chroma_key_destroy(filter);
chroma_key_destroy_v1(filter);
return NULL;
}
chroma_key_update(filter, settings);
chroma_key_update_v1(filter, settings);
return filter;
}
static void chroma_key_render(void *data, gs_effect_t *effect)
static void *chroma_key_create_v2(obs_data_t *settings, obs_source_t *context)
{
struct chroma_key_filter_data_v2 *filter =
bzalloc(sizeof(struct chroma_key_filter_data_v2));
char *effect_path = obs_module_file("chroma_key_filter_v2.effect");
filter->context = context;
obs_enter_graphics();
filter->effect = gs_effect_create_from_file(effect_path, NULL);
if (filter->effect) {
filter->opacity_param =
gs_effect_get_param_by_name(filter->effect, "opacity");
filter->contrast_param =
gs_effect_get_param_by_name(filter->effect, "contrast");
filter->brightness_param = gs_effect_get_param_by_name(
filter->effect, "brightness");
filter->gamma_param =
gs_effect_get_param_by_name(filter->effect, "gamma");
filter->chroma_param = gs_effect_get_param_by_name(
filter->effect, "chroma_key");
filter->pixel_size_param = gs_effect_get_param_by_name(
filter->effect, "pixel_size");
filter->similarity_param = gs_effect_get_param_by_name(
filter->effect, "similarity");
filter->smoothness_param = gs_effect_get_param_by_name(
filter->effect, "smoothness");
filter->spill_param =
gs_effect_get_param_by_name(filter->effect, "spill");
}
obs_leave_graphics();
bfree(effect_path);
if (!filter->effect) {
chroma_key_destroy_v2(filter);
return NULL;
}
chroma_key_update_v2(filter, settings);
return filter;
}
static void chroma_key_render_v1(void *data, gs_effect_t *effect)
{
struct chroma_key_filter_data *filter = data;
obs_source_t *target = obs_filter_get_target(filter->context);
@ -216,6 +364,37 @@ static void chroma_key_render(void *data, gs_effect_t *effect)
UNUSED_PARAMETER(effect);
}
static void chroma_key_render_v2(void *data, gs_effect_t *effect)
{
struct chroma_key_filter_data_v2 *filter = data;
obs_source_t *target = obs_filter_get_target(filter->context);
uint32_t width = obs_source_get_base_width(target);
uint32_t height = obs_source_get_base_height(target);
struct vec2 pixel_size;
if (!obs_source_process_filter_begin(filter->context, GS_RGBA,
OBS_ALLOW_DIRECT_RENDERING))
return;
vec2_set(&pixel_size, 1.0f / (float)width, 1.0f / (float)height);
gs_effect_set_float(filter->opacity_param, filter->opacity);
gs_effect_set_float(filter->contrast_param, filter->contrast);
gs_effect_set_float(filter->brightness_param, filter->brightness);
gs_effect_set_float(filter->gamma_param, filter->gamma);
gs_effect_set_vec2(filter->chroma_param, &filter->chroma);
gs_effect_set_vec2(filter->pixel_size_param, &pixel_size);
gs_effect_set_float(filter->similarity_param, filter->similarity);
gs_effect_set_float(filter->smoothness_param, filter->smoothness);
gs_effect_set_float(filter->spill_param, filter->spill);
const bool previous = gs_set_linear_srgb(true);
obs_source_process_filter_end(filter->context, filter->effect, 0, 0);
gs_set_linear_srgb(previous);
UNUSED_PARAMETER(effect);
}
static bool key_type_changed(obs_properties_t *props, obs_property_t *p,
obs_data_t *settings)
{
@ -229,7 +408,7 @@ static bool key_type_changed(obs_properties_t *props, obs_property_t *p,
return true;
}
static obs_properties_t *chroma_key_properties(void *data)
static obs_properties_t *chroma_key_properties_v1(void *data)
{
obs_properties_t *props = obs_properties_create();
@ -265,6 +444,42 @@ static obs_properties_t *chroma_key_properties(void *data)
return props;
}
static obs_properties_t *chroma_key_properties_v2(void *data)
{
obs_properties_t *props = obs_properties_create();
obs_property_t *p = obs_properties_add_list(props, SETTING_COLOR_TYPE,
TEXT_COLOR_TYPE,
OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_STRING);
obs_property_list_add_string(p, obs_module_text("Green"), "green");
obs_property_list_add_string(p, obs_module_text("Blue"), "blue");
obs_property_list_add_string(p, obs_module_text("Magenta"), "magenta");
obs_property_list_add_string(p, obs_module_text("Custom"), "custom");
obs_property_set_modified_callback(p, key_type_changed);
obs_properties_add_color(props, SETTING_KEY_COLOR, TEXT_KEY_COLOR);
obs_properties_add_int_slider(props, SETTING_SIMILARITY,
TEXT_SIMILARITY, 1, 1000, 1);
obs_properties_add_int_slider(props, SETTING_SMOOTHNESS,
TEXT_SMOOTHNESS, 1, 1000, 1);
obs_properties_add_int_slider(props, SETTING_SPILL, TEXT_SPILL, 1, 1000,
1);
obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, 0,
100, 1);
obs_properties_add_float_slider(props, SETTING_CONTRAST, TEXT_CONTRAST,
-4.0, 4.0, 0.01);
obs_properties_add_float_slider(props, SETTING_BRIGHTNESS,
TEXT_BRIGHTNESS, -1.0, 1.0, 0.01);
obs_properties_add_float_slider(props, SETTING_GAMMA, TEXT_GAMMA, -1.0,
1.0, 0.01);
UNUSED_PARAMETER(data);
return props;
}
static void chroma_key_defaults(obs_data_t *settings)
{
obs_data_set_default_int(settings, SETTING_OPACITY, 100);
@ -281,12 +496,26 @@ static void chroma_key_defaults(obs_data_t *settings)
struct obs_source_info chroma_key_filter = {
.id = "chroma_key_filter",
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_OBSOLETE,
.get_name = chroma_key_name,
.create = chroma_key_create,
.destroy = chroma_key_destroy,
.video_render = chroma_key_render,
.update = chroma_key_update,
.get_properties = chroma_key_properties,
.create = chroma_key_create_v1,
.destroy = chroma_key_destroy_v1,
.video_render = chroma_key_render_v1,
.update = chroma_key_update_v1,
.get_properties = chroma_key_properties_v1,
.get_defaults = chroma_key_defaults,
};
struct obs_source_info chroma_key_filter_v2 = {
.id = "chroma_key_filter",
.version = 2,
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.get_name = chroma_key_name,
.create = chroma_key_create_v2,
.destroy = chroma_key_destroy_v2,
.video_render = chroma_key_render_v2,
.update = chroma_key_update_v2,
.get_properties = chroma_key_properties_v2,
.get_defaults = chroma_key_defaults,
};

View File

@ -46,13 +46,7 @@ struct color_correction_filter_data {
gs_eparam_t *gamma_param;
gs_eparam_t *final_matrix_param;
struct vec3 gamma;
float contrast;
float brightness;
float saturation;
float hue_shift;
float opacity;
struct vec4 color;
float gamma;
/* Pre-Computes */
struct matrix4 con_matrix;
@ -62,14 +56,26 @@ struct color_correction_filter_data {
struct matrix4 color_matrix;
struct matrix4 final_matrix;
struct vec3 rot_quaternion;
float rot_quaternion_w;
struct vec3 cross;
struct vec3 square;
struct vec3 wimag;
struct vec3 diag;
struct vec3 a_line;
struct vec3 b_line;
struct vec3 half_unit;
};
struct color_correction_filter_data_v2 {
obs_source_t *context;
gs_effect_t *effect;
gs_eparam_t *gamma_param;
gs_eparam_t *final_matrix_param;
float gamma;
/* Pre-Computes */
struct matrix4 con_matrix;
struct matrix4 bright_matrix;
struct matrix4 sat_matrix;
struct matrix4 hue_op_matrix;
struct matrix4 final_matrix;
struct vec3 half_unit;
};
@ -94,40 +100,29 @@ static const char *color_correction_filter_name(void *unused)
* with a slider this function is called to update the internal settings
* in OBS, and hence the settings being passed to the CPU/GPU.
*/
static void color_correction_filter_update(void *data, obs_data_t *settings)
static void color_correction_filter_update_v1(void *data, obs_data_t *settings)
{
struct color_correction_filter_data *filter = data;
/* Build our Gamma numbers. */
double gamma = obs_data_get_double(settings, SETTING_GAMMA);
gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0));
vec3_set(&filter->gamma, (float)gamma, (float)gamma, (float)gamma);
filter->gamma = (float)gamma;
/* Build our contrast number. */
filter->contrast =
float contrast =
(float)obs_data_get_double(settings, SETTING_CONTRAST) + 1.0f;
float one_minus_con = (1.0f - filter->contrast) / 2.0f;
float one_minus_con = (1.0f - contrast) / 2.0f;
/* Now let's build our Contrast matrix. */
filter->con_matrix = (struct matrix4){filter->contrast,
0.0f,
0.0f,
0.0f,
0.0f,
filter->contrast,
0.0f,
0.0f,
0.0f,
0.0f,
filter->contrast,
0.0f,
one_minus_con,
one_minus_con,
one_minus_con,
1.0f};
filter->con_matrix = (struct matrix4){
contrast, 0.0f, 0.0f, 0.0f,
0.0f, contrast, 0.0f, 0.0f,
0.0f, 0.0f, contrast, 0.0f,
one_minus_con, one_minus_con, one_minus_con, 1.0f};
/* Build our brightness number. */
filter->brightness =
float brightness =
(float)obs_data_get_double(settings, SETTING_BRIGHTNESS);
/*
@ -136,21 +131,21 @@ static void color_correction_filter_update(void *data, obs_data_t *settings)
* this matrix to the identity matrix, so now we only need
* to set the 3 variables that have changed.
*/
filter->bright_matrix.t.x = filter->brightness;
filter->bright_matrix.t.y = filter->brightness;
filter->bright_matrix.t.z = filter->brightness;
filter->bright_matrix.t.x = brightness;
filter->bright_matrix.t.y = brightness;
filter->bright_matrix.t.z = brightness;
/* Build our Saturation number. */
filter->saturation =
float saturation =
(float)obs_data_get_double(settings, SETTING_SATURATION) + 1.0f;
/* Factor in the selected color weights. */
float one_minus_sat_red = (1.0f - filter->saturation) * red_weight;
float one_minus_sat_green = (1.0f - filter->saturation) * green_weight;
float one_minus_sat_blue = (1.0f - filter->saturation) * blue_weight;
float sat_val_red = one_minus_sat_red + filter->saturation;
float sat_val_green = one_minus_sat_green + filter->saturation;
float sat_val_blue = one_minus_sat_blue + filter->saturation;
float one_minus_sat_red = (1.0f - saturation) * red_weight;
float one_minus_sat_green = (1.0f - saturation) * green_weight;
float one_minus_sat_blue = (1.0f - saturation) * blue_weight;
float sat_val_red = one_minus_sat_red + saturation;
float sat_val_green = one_minus_sat_green + saturation;
float sat_val_blue = one_minus_sat_blue + saturation;
/* Now we build our Saturation matrix. */
filter->sat_matrix = (struct matrix4){sat_val_red,
@ -171,57 +166,62 @@ static void color_correction_filter_update(void *data, obs_data_t *settings)
1.0f};
/* Build our Hue number. */
filter->hue_shift =
float hue_shift =
(float)obs_data_get_double(settings, SETTING_HUESHIFT);
/* Build our Transparency number. */
filter->opacity =
float opacity =
(float)obs_data_get_int(settings, SETTING_OPACITY) * 0.01f;
/* Hue is the radian of 0 to 360 degrees. */
float half_angle = 0.5f * (float)(filter->hue_shift / (180.0f / M_PI));
float half_angle = 0.5f * (float)(hue_shift / (180.0f / M_PI));
/* Pseudo-Quaternion To Matrix. */
float rot_quad1 = root3 * (float)sin(half_angle);
vec3_set(&filter->rot_quaternion, rot_quad1, rot_quad1, rot_quad1);
filter->rot_quaternion_w = (float)cos(half_angle);
struct vec3 rot_quaternion;
vec3_set(&rot_quaternion, rot_quad1, rot_quad1, rot_quad1);
float rot_quaternion_w = (float)cos(half_angle);
vec3_mul(&filter->cross, &filter->rot_quaternion,
&filter->rot_quaternion);
vec3_mul(&filter->square, &filter->rot_quaternion,
&filter->rot_quaternion);
vec3_mulf(&filter->wimag, &filter->rot_quaternion,
filter->rot_quaternion_w);
struct vec3 cross;
vec3_mul(&cross, &rot_quaternion, &rot_quaternion);
struct vec3 square;
vec3_mul(&square, &rot_quaternion, &rot_quaternion);
struct vec3 wimag;
vec3_mulf(&wimag, &rot_quaternion, rot_quaternion_w);
vec3_mulf(&filter->square, &filter->square, 2.0f);
vec3_sub(&filter->diag, &filter->half_unit, &filter->square);
vec3_add(&filter->a_line, &filter->cross, &filter->wimag);
vec3_sub(&filter->b_line, &filter->cross, &filter->wimag);
vec3_mulf(&square, &square, 2.0f);
struct vec3 diag;
vec3_sub(&diag, &filter->half_unit, &square);
struct vec3 a_line;
vec3_add(&a_line, &cross, &wimag);
struct vec3 b_line;
vec3_sub(&b_line, &cross, &wimag);
/* Now we build our Hue and Opacity matrix. */
filter->hue_op_matrix = (struct matrix4){filter->diag.x * 2.0f,
filter->b_line.z * 2.0f,
filter->a_line.y * 2.0f,
filter->hue_op_matrix = (struct matrix4){diag.x * 2.0f,
b_line.z * 2.0f,
a_line.y * 2.0f,
0.0f,
filter->a_line.z * 2.0f,
filter->diag.y * 2.0f,
filter->b_line.x * 2.0f,
a_line.z * 2.0f,
diag.y * 2.0f,
b_line.x * 2.0f,
0.0f,
filter->b_line.y * 2.0f,
filter->a_line.x * 2.0f,
filter->diag.z * 2.0f,
b_line.y * 2.0f,
a_line.x * 2.0f,
diag.z * 2.0f,
0.0f,
0.0f,
0.0f,
0.0f,
filter->opacity};
opacity};
/* Now get the overlay color data. */
uint32_t color = (uint32_t)obs_data_get_int(settings, SETTING_COLOR);
vec4_from_rgba(&filter->color, color);
struct vec4 color_v4;
vec4_from_rgba(&color_v4, color);
/*
* Now let's build our Color 'overlay' matrix.
@ -229,13 +229,13 @@ static void color_correction_filter_update(void *data, obs_data_t *settings)
* this matrix to the identity matrix, so now we only need
* to set the 6 variables that have changed.
*/
filter->color_matrix.x.x = filter->color.x;
filter->color_matrix.y.y = filter->color.y;
filter->color_matrix.z.z = filter->color.z;
filter->color_matrix.x.x = color_v4.x;
filter->color_matrix.y.y = color_v4.y;
filter->color_matrix.z.z = color_v4.z;
filter->color_matrix.t.x = filter->color.w * filter->color.x;
filter->color_matrix.t.y = filter->color.w * filter->color.y;
filter->color_matrix.t.z = filter->color.w * filter->color.z;
filter->color_matrix.t.x = color_v4.w * color_v4.x;
filter->color_matrix.t.y = color_v4.w * color_v4.y;
filter->color_matrix.t.z = color_v4.w * color_v4.z;
/* First we apply the Contrast & Brightness matrix. */
matrix4_mul(&filter->final_matrix, &filter->bright_matrix,
@ -251,12 +251,140 @@ static void color_correction_filter_update(void *data, obs_data_t *settings)
&filter->color_matrix);
}
static void color_correction_filter_update_v2(void *data, obs_data_t *settings)
{
struct color_correction_filter_data_v2 *filter = data;
/* Build our Gamma numbers. */
double gamma = obs_data_get_double(settings, SETTING_GAMMA);
gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0));
filter->gamma = (float)gamma;
/* Build our contrast number. */
float contrast = (float)obs_data_get_double(settings, SETTING_CONTRAST);
contrast = (contrast < 0.0f) ? (1.0f / (-contrast + 1.0f))
: (contrast + 1.0f);
/* Now let's build our Contrast matrix. */
filter->con_matrix = (struct matrix4){contrast, 0.0f, 0.0f, 0.0f, 0.0f,
contrast, 0.0f, 0.0f, 0.0f, 0.0f,
contrast, 0.0f, 0.0f, 0.0f, 0.0f,
1.0f};
/* Build our brightness number. */
float brightness =
(float)obs_data_get_double(settings, SETTING_BRIGHTNESS);
/*
* Now let's build our Brightness matrix.
* Earlier (in the function color_correction_filter_create) we set
* this matrix to the identity matrix, so now we only need
* to set the 3 variables that have changed.
*/
filter->bright_matrix.t.x = brightness;
filter->bright_matrix.t.y = brightness;
filter->bright_matrix.t.z = brightness;
/* Build our Saturation number. */
float saturation =
(float)obs_data_get_double(settings, SETTING_SATURATION) + 1.0f;
/* Factor in the selected color weights. */
float one_minus_sat_red = (1.0f - saturation) * red_weight;
float one_minus_sat_green = (1.0f - saturation) * green_weight;
float one_minus_sat_blue = (1.0f - saturation) * blue_weight;
float sat_val_red = one_minus_sat_red + saturation;
float sat_val_green = one_minus_sat_green + saturation;
float sat_val_blue = one_minus_sat_blue + saturation;
/* Now we build our Saturation matrix. */
filter->sat_matrix = (struct matrix4){sat_val_red,
one_minus_sat_red,
one_minus_sat_red,
0.0f,
one_minus_sat_green,
sat_val_green,
one_minus_sat_green,
0.0f,
one_minus_sat_blue,
one_minus_sat_blue,
sat_val_blue,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f};
/* Build our Hue number. */
float hue_shift =
(float)obs_data_get_double(settings, SETTING_HUESHIFT);
/* Build our Transparency number. */
float opacity =
(float)obs_data_get_int(settings, SETTING_OPACITY) * 0.01f;
/* Hue is the radian of 0 to 360 degrees. */
float half_angle = 0.5f * (float)(hue_shift / (180.0f / M_PI));
/* Pseudo-Quaternion To Matrix. */
float rot_quad1 = root3 * (float)sin(half_angle);
struct vec3 rot_quaternion;
vec3_set(&rot_quaternion, rot_quad1, rot_quad1, rot_quad1);
float rot_quaternion_w = (float)cos(half_angle);
struct vec3 cross;
vec3_mul(&cross, &rot_quaternion, &rot_quaternion);
struct vec3 square;
vec3_mul(&square, &rot_quaternion, &rot_quaternion);
struct vec3 wimag;
vec3_mulf(&wimag, &rot_quaternion, rot_quaternion_w);
vec3_mulf(&square, &square, 2.0f);
struct vec3 diag;
vec3_sub(&diag, &filter->half_unit, &square);
struct vec3 a_line;
vec3_add(&a_line, &cross, &wimag);
struct vec3 b_line;
vec3_sub(&b_line, &cross, &wimag);
/* Now we build our Hue and Opacity matrix. */
filter->hue_op_matrix = (struct matrix4){diag.x * 2.0f,
b_line.z * 2.0f,
a_line.y * 2.0f,
0.0f,
a_line.z * 2.0f,
diag.y * 2.0f,
b_line.x * 2.0f,
0.0f,
b_line.y * 2.0f,
a_line.x * 2.0f,
diag.z * 2.0f,
0.0f,
0.0f,
0.0f,
0.0f,
opacity};
/* First we apply the Contrast & Brightness matrix. */
matrix4_mul(&filter->final_matrix, &filter->con_matrix,
&filter->bright_matrix);
/* Now we apply the Saturation matrix. */
matrix4_mul(&filter->final_matrix, &filter->final_matrix,
&filter->sat_matrix);
/* Next we apply the Hue+Opacity matrix. */
matrix4_mul(&filter->final_matrix, &filter->final_matrix,
&filter->hue_op_matrix);
}
/*
* Since this is C we have to be careful when destroying/removing items from
* OBS. Jim has added several useful functions to help keep memory leaks to
* a minimum, and handle the destruction and construction of these filters.
*/
static void color_correction_filter_destroy(void *data)
static void color_correction_filter_destroy_v1(void *data)
{
struct color_correction_filter_data *filter = data;
@ -269,14 +397,27 @@ static void color_correction_filter_destroy(void *data)
bfree(data);
}
static void color_correction_filter_destroy_v2(void *data)
{
struct color_correction_filter_data_v2 *filter = data;
if (filter->effect) {
obs_enter_graphics();
gs_effect_destroy(filter->effect);
obs_leave_graphics();
}
bfree(data);
}
/*
* When you apply a filter OBS creates it, and adds it to the source. OBS also
* starts rendering it immediately. This function doesn't just 'create' the
* filter, it also calls the render function (farther below) that contains the
* actual rendering code.
*/
static void *color_correction_filter_create(obs_data_t *settings,
obs_source_t *context)
static void *color_correction_filter_create_v1(obs_data_t *settings,
obs_source_t *context)
{
/*
* Because of limitations of pre-c99 compilers, you can't create an
@ -324,7 +465,7 @@ static void *color_correction_filter_create(obs_data_t *settings,
* values that don't exist anymore.
*/
if (!filter->effect) {
color_correction_filter_destroy(filter);
color_correction_filter_destroy_v1(filter);
return NULL;
}
@ -333,12 +474,73 @@ static void *color_correction_filter_create(obs_data_t *settings,
* we could end up with the user controlled sliders and values
* updating, but the visuals not updating to match.
*/
color_correction_filter_update(filter, settings);
color_correction_filter_update_v1(filter, settings);
return filter;
}
static void *color_correction_filter_create_v2(obs_data_t *settings,
obs_source_t *context)
{
/*
* Because of limitations of pre-c99 compilers, you can't create an
* array that doesn't have a known size at compile time. The below
* function calculates the size needed and allocates memory to
* handle the source.
*/
struct color_correction_filter_data_v2 *filter =
bzalloc(sizeof(struct color_correction_filter_data_v2));
/*
* By default the effect file is stored in the ./data directory that
* your filter resides in.
*/
char *effect_path = obs_module_file("color_correction_filter.effect");
filter->context = context;
/* Set/clear/assign for all necessary vectors. */
vec3_set(&filter->half_unit, 0.5f, 0.5f, 0.5f);
matrix4_identity(&filter->bright_matrix);
/* Here we enter the GPU drawing/shader portion of our code. */
obs_enter_graphics();
/* Load the shader on the GPU. */
filter->effect = gs_effect_create_from_file(effect_path, NULL);
/* If the filter is active pass the parameters to the filter. */
if (filter->effect) {
filter->gamma_param = gs_effect_get_param_by_name(
filter->effect, SETTING_GAMMA);
filter->final_matrix_param = gs_effect_get_param_by_name(
filter->effect, "color_matrix");
}
obs_leave_graphics();
bfree(effect_path);
/*
* If the filter has been removed/deactivated, destroy the filter
* and exit out so we don't crash OBS by telling it to update
* values that don't exist anymore.
*/
if (!filter->effect) {
color_correction_filter_destroy_v2(filter);
return NULL;
}
/*
* It's important to call the update function here. If we don't
* we could end up with the user controlled sliders and values
* updating, but the visuals not updating to match.
*/
color_correction_filter_update_v2(filter, settings);
return filter;
}
/* This is where the actual rendering of the filter takes place. */
static void color_correction_filter_render(void *data, gs_effect_t *effect)
static void color_correction_filter_render_v1(void *data, gs_effect_t *effect)
{
struct color_correction_filter_data *filter = data;
@ -347,7 +549,7 @@ static void color_correction_filter_render(void *data, gs_effect_t *effect)
return;
/* Now pass the interface variables to the .effect file. */
gs_effect_set_vec3(filter->gamma_param, &filter->gamma);
gs_effect_set_float(filter->gamma_param, filter->gamma);
gs_effect_set_matrix4(filter->final_matrix_param,
&filter->final_matrix);
@ -356,13 +558,33 @@ static void color_correction_filter_render(void *data, gs_effect_t *effect)
UNUSED_PARAMETER(effect);
}
static void color_correction_filter_render_v2(void *data, gs_effect_t *effect)
{
struct color_correction_filter_data_v2 *filter = data;
if (!obs_source_process_filter_begin(filter->context, GS_RGBA,
OBS_ALLOW_DIRECT_RENDERING))
return;
/* Now pass the interface variables to the .effect file. */
gs_effect_set_float(filter->gamma_param, filter->gamma);
gs_effect_set_matrix4(filter->final_matrix_param,
&filter->final_matrix);
const bool previous = gs_set_linear_srgb(true);
obs_source_process_filter_end(filter->context, filter->effect, 0, 0);
gs_set_linear_srgb(previous);
UNUSED_PARAMETER(effect);
}
/*
* This function sets the interface. the types (add_*_Slider), the type of
* data collected (int), the internal name, user-facing name, minimum,
* maximum and step values. While a custom interface can be built, for a
* simple filter like this it's better to use the supplied functions.
*/
static obs_properties_t *color_correction_filter_properties(void *data)
static obs_properties_t *color_correction_filter_properties_v1(void *data)
{
obs_properties_t *props = obs_properties_create();
@ -386,6 +608,28 @@ static obs_properties_t *color_correction_filter_properties(void *data)
return props;
}
static obs_properties_t *color_correction_filter_properties_v2(void *data)
{
obs_properties_t *props = obs_properties_create();
obs_properties_add_float_slider(props, SETTING_GAMMA, TEXT_GAMMA, -3.0,
3.0, 0.01);
obs_properties_add_float_slider(props, SETTING_CONTRAST, TEXT_CONTRAST,
-4.0, 4.0, 0.01);
obs_properties_add_float_slider(props, SETTING_BRIGHTNESS,
TEXT_BRIGHTNESS, -1.0, 1.0, 0.01);
obs_properties_add_float_slider(props, SETTING_SATURATION,
TEXT_SATURATION, -1.0, 5.0, 0.01);
obs_properties_add_float_slider(props, SETTING_HUESHIFT, TEXT_HUESHIFT,
-180.0, 180.0, 0.01);
obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, 0,
100, 1);
UNUSED_PARAMETER(data);
return props;
}
/*
* As the functions' namesake, this provides the default settings for any
* options you wish to provide a default for. Try to select defaults that
@ -393,17 +637,27 @@ static obs_properties_t *color_correction_filter_properties(void *data)
* *NOTE* this function is completely optional, as is providing a default
* for any particular setting.
*/
static void color_correction_filter_defaults(obs_data_t *settings)
static void color_correction_filter_defaults_v1(obs_data_t *settings)
{
obs_data_set_default_double(settings, SETTING_GAMMA, 0.0);
obs_data_set_default_double(settings, SETTING_CONTRAST, 0.0);
obs_data_set_default_double(settings, SETTING_BRIGHTNESS, 0.0);
obs_data_set_default_double(settings, SETTING_SATURATION, 0.0);
obs_data_set_default_double(settings, SETTING_HUESHIFT, 0.0);
obs_data_set_default_double(settings, SETTING_OPACITY, 100.0);
obs_data_set_default_int(settings, SETTING_OPACITY, 100);
obs_data_set_default_int(settings, SETTING_COLOR, 0x00FFFFFF);
}
static void color_correction_filter_defaults_v2(obs_data_t *settings)
{
obs_data_set_default_double(settings, SETTING_GAMMA, 0.0);
obs_data_set_default_double(settings, SETTING_CONTRAST, 0.0);
obs_data_set_default_double(settings, SETTING_BRIGHTNESS, 0.0);
obs_data_set_default_double(settings, SETTING_SATURATION, 0.0);
obs_data_set_default_double(settings, SETTING_HUESHIFT, 0.0);
obs_data_set_default_int(settings, SETTING_OPACITY, 100);
}
/*
* So how does OBS keep track of all these plug-ins/filters? How does OBS know
* which function to call when it needs to update a setting? Or a source? Or
@ -417,12 +671,26 @@ static void color_correction_filter_defaults(obs_data_t *settings)
struct obs_source_info color_filter = {
.id = "color_filter",
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_OBSOLETE,
.get_name = color_correction_filter_name,
.create = color_correction_filter_create_v1,
.destroy = color_correction_filter_destroy_v1,
.video_render = color_correction_filter_render_v1,
.update = color_correction_filter_update_v1,
.get_properties = color_correction_filter_properties_v1,
.get_defaults = color_correction_filter_defaults_v1,
};
struct obs_source_info color_filter_v2 = {
.id = "color_filter",
.version = 2,
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.get_name = color_correction_filter_name,
.create = color_correction_filter_create,
.destroy = color_correction_filter_destroy,
.video_render = color_correction_filter_render,
.update = color_correction_filter_update,
.get_properties = color_correction_filter_properties,
.get_defaults = color_correction_filter_defaults,
.create = color_correction_filter_create_v2,
.destroy = color_correction_filter_destroy_v2,
.video_render = color_correction_filter_render_v2,
.update = color_correction_filter_update_v2,
.get_properties = color_correction_filter_properties_v2,
.get_defaults = color_correction_filter_defaults_v2,
};

View File

@ -452,8 +452,10 @@ static void color_grade_filter_render(void *data, gs_effect_t *effect)
param = gs_effect_get_param_by_name(filter->effect, "cube_width_i");
gs_effect_set_float(param, 1.0f / filter->cube_width);
const bool previous = gs_set_linear_srgb(true);
obs_source_process_filter_tech_end(filter->context, filter->effect, 0,
0, tech_name);
gs_set_linear_srgb(previous);
UNUSED_PARAMETER(effect);
}

View File

@ -49,14 +49,39 @@ struct color_key_filter_data {
float smoothness;
};
struct color_key_filter_data_v2 {
obs_source_t *context;
gs_effect_t *effect;
gs_eparam_t *opacity_param;
gs_eparam_t *contrast_param;
gs_eparam_t *brightness_param;
gs_eparam_t *gamma_param;
gs_eparam_t *key_color_param;
gs_eparam_t *similarity_param;
gs_eparam_t *smoothness_param;
float opacity;
float contrast;
float brightness;
float gamma;
struct vec4 key_color;
float similarity;
float smoothness;
};
static const char *color_key_name(void *unused)
{
UNUSED_PARAMETER(unused);
return obs_module_text("ColorKeyFilter");
}
static inline void color_settings_update(struct color_key_filter_data *filter,
obs_data_t *settings)
static inline void
color_settings_update_v1(struct color_key_filter_data *filter,
obs_data_t *settings)
{
uint32_t opacity =
(uint32_t)obs_data_get_int(settings, SETTING_OPACITY);
@ -79,8 +104,28 @@ static inline void color_settings_update(struct color_key_filter_data *filter,
vec4_from_rgba(&filter->color, color);
}
static inline void key_settings_update(struct color_key_filter_data *filter,
obs_data_t *settings)
static inline void
color_settings_update_v2(struct color_key_filter_data_v2 *filter,
obs_data_t *settings)
{
filter->opacity =
(float)obs_data_get_int(settings, SETTING_OPACITY) / 100.0f;
double contrast = obs_data_get_double(settings, SETTING_CONTRAST);
contrast = (contrast < 0.0) ? (1.0 / (-contrast + 1.0))
: (contrast + 1.0);
filter->contrast = (float)contrast;
filter->brightness =
(float)obs_data_get_double(settings, SETTING_BRIGHTNESS);
double gamma = obs_data_get_double(settings, SETTING_GAMMA);
gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0));
filter->gamma = (float)gamma;
}
static inline void key_settings_update_v1(struct color_key_filter_data *filter,
obs_data_t *settings)
{
int64_t similarity = obs_data_get_int(settings, SETTING_SIMILARITY);
int64_t smoothness = obs_data_get_int(settings, SETTING_SMOOTHNESS);
@ -104,15 +149,49 @@ static inline void key_settings_update(struct color_key_filter_data *filter,
filter->smoothness = (float)smoothness / 1000.0f;
}
static void color_key_update(void *data, obs_data_t *settings)
static inline void
key_settings_update_v2(struct color_key_filter_data_v2 *filter,
obs_data_t *settings)
{
int64_t similarity = obs_data_get_int(settings, SETTING_SIMILARITY);
int64_t smoothness = obs_data_get_int(settings, SETTING_SMOOTHNESS);
uint32_t key_color =
(uint32_t)obs_data_get_int(settings, SETTING_KEY_COLOR);
const char *key_type =
obs_data_get_string(settings, SETTING_COLOR_TYPE);
if (strcmp(key_type, "green") == 0)
key_color = 0x00FF00;
else if (strcmp(key_type, "blue") == 0)
key_color = 0xFF0000;
else if (strcmp(key_type, "red") == 0)
key_color = 0x0000FF;
else if (strcmp(key_type, "magenta") == 0)
key_color = 0xFF00FF;
vec4_from_rgba(&filter->key_color, key_color | 0xFF000000);
filter->similarity = (float)similarity / 1000.0f;
filter->smoothness = (float)smoothness / 1000.0f;
}
static void color_key_update_v1(void *data, obs_data_t *settings)
{
struct color_key_filter_data *filter = data;
color_settings_update(filter, settings);
key_settings_update(filter, settings);
color_settings_update_v1(filter, settings);
key_settings_update_v1(filter, settings);
}
static void color_key_destroy(void *data)
static void color_key_update_v2(void *data, obs_data_t *settings)
{
struct color_key_filter_data_v2 *filter = data;
color_settings_update_v2(filter, settings);
key_settings_update_v2(filter, settings);
}
static void color_key_destroy_v1(void *data)
{
struct color_key_filter_data *filter = data;
@ -125,7 +204,20 @@ static void color_key_destroy(void *data)
bfree(data);
}
static void *color_key_create(obs_data_t *settings, obs_source_t *context)
static void color_key_destroy_v2(void *data)
{
struct color_key_filter_data_v2 *filter = data;
if (filter->effect) {
obs_enter_graphics();
gs_effect_destroy(filter->effect);
obs_leave_graphics();
}
bfree(data);
}
static void *color_key_create_v1(obs_data_t *settings, obs_source_t *context)
{
struct color_key_filter_data *filter =
bzalloc(sizeof(struct color_key_filter_data));
@ -158,15 +250,56 @@ static void *color_key_create(obs_data_t *settings, obs_source_t *context)
bfree(effect_path);
if (!filter->effect) {
color_key_destroy(filter);
color_key_destroy_v1(filter);
return NULL;
}
color_key_update(filter, settings);
color_key_update_v1(filter, settings);
return filter;
}
static void color_key_render(void *data, gs_effect_t *effect)
static void *color_key_create_v2(obs_data_t *settings, obs_source_t *context)
{
struct color_key_filter_data_v2 *filter =
bzalloc(sizeof(struct color_key_filter_data_v2));
char *effect_path = obs_module_file("color_key_filter_v2.effect");
filter->context = context;
obs_enter_graphics();
filter->effect = gs_effect_create_from_file(effect_path, NULL);
if (filter->effect) {
filter->opacity_param =
gs_effect_get_param_by_name(filter->effect, "opacity");
filter->contrast_param =
gs_effect_get_param_by_name(filter->effect, "contrast");
filter->brightness_param = gs_effect_get_param_by_name(
filter->effect, "brightness");
filter->gamma_param =
gs_effect_get_param_by_name(filter->effect, "gamma");
filter->key_color_param = gs_effect_get_param_by_name(
filter->effect, "key_color");
filter->similarity_param = gs_effect_get_param_by_name(
filter->effect, "similarity");
filter->smoothness_param = gs_effect_get_param_by_name(
filter->effect, "smoothness");
}
obs_leave_graphics();
bfree(effect_path);
if (!filter->effect) {
color_key_destroy_v2(filter);
return NULL;
}
color_key_update_v2(filter, settings);
return filter;
}
static void color_key_render_v1(void *data, gs_effect_t *effect)
{
struct color_key_filter_data *filter = data;
@ -187,6 +320,29 @@ static void color_key_render(void *data, gs_effect_t *effect)
UNUSED_PARAMETER(effect);
}
static void color_key_render_v2(void *data, gs_effect_t *effect)
{
struct color_key_filter_data_v2 *filter = data;
if (!obs_source_process_filter_begin(filter->context, GS_RGBA,
OBS_ALLOW_DIRECT_RENDERING))
return;
gs_effect_set_float(filter->opacity_param, filter->opacity);
gs_effect_set_float(filter->contrast_param, filter->contrast);
gs_effect_set_float(filter->brightness_param, filter->brightness);
gs_effect_set_float(filter->gamma_param, filter->gamma);
gs_effect_set_vec4(filter->key_color_param, &filter->key_color);
gs_effect_set_float(filter->similarity_param, filter->similarity);
gs_effect_set_float(filter->smoothness_param, filter->smoothness);
const bool previous = gs_set_linear_srgb(true);
obs_source_process_filter_end(filter->context, filter->effect, 0, 0);
gs_set_linear_srgb(previous);
UNUSED_PARAMETER(effect);
}
static bool key_type_changed(obs_properties_t *props, obs_property_t *p,
obs_data_t *settings)
{
@ -200,7 +356,7 @@ static bool key_type_changed(obs_properties_t *props, obs_property_t *p,
return true;
}
static obs_properties_t *color_key_properties(void *data)
static obs_properties_t *color_key_properties_v1(void *data)
{
obs_properties_t *props = obs_properties_create();
@ -236,6 +392,42 @@ static obs_properties_t *color_key_properties(void *data)
return props;
}
static obs_properties_t *color_key_properties_v2(void *data)
{
obs_properties_t *props = obs_properties_create();
obs_property_t *p = obs_properties_add_list(props, SETTING_COLOR_TYPE,
TEXT_COLOR_TYPE,
OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_STRING);
obs_property_list_add_string(p, obs_module_text("Green"), "green");
obs_property_list_add_string(p, obs_module_text("Blue"), "blue");
obs_property_list_add_string(p, obs_module_text("Red"), "red");
obs_property_list_add_string(p, obs_module_text("Magenta"), "magenta");
obs_property_list_add_string(p, obs_module_text("CustomColor"),
"custom");
obs_property_set_modified_callback(p, key_type_changed);
obs_properties_add_color(props, SETTING_KEY_COLOR, TEXT_KEY_COLOR);
obs_properties_add_int_slider(props, SETTING_SIMILARITY,
TEXT_SIMILARITY, 1, 1000, 1);
obs_properties_add_int_slider(props, SETTING_SMOOTHNESS,
TEXT_SMOOTHNESS, 1, 1000, 1);
obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, 0,
100, 1);
obs_properties_add_float_slider(props, SETTING_CONTRAST, TEXT_CONTRAST,
-4.0, 4.0, 0.01);
obs_properties_add_float_slider(props, SETTING_BRIGHTNESS,
TEXT_BRIGHTNESS, -1.0, 1.0, 0.01);
obs_properties_add_float_slider(props, SETTING_GAMMA, TEXT_GAMMA, -1.0,
1.0, 0.01);
UNUSED_PARAMETER(data);
return props;
}
static void color_key_defaults(obs_data_t *settings)
{
obs_data_set_default_int(settings, SETTING_OPACITY, 100);
@ -251,12 +443,26 @@ static void color_key_defaults(obs_data_t *settings)
struct obs_source_info color_key_filter = {
.id = "color_key_filter",
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_OBSOLETE,
.get_name = color_key_name,
.create = color_key_create,
.destroy = color_key_destroy,
.video_render = color_key_render,
.update = color_key_update,
.get_properties = color_key_properties,
.create = color_key_create_v1,
.destroy = color_key_destroy_v1,
.video_render = color_key_render_v1,
.update = color_key_update_v1,
.get_properties = color_key_properties_v1,
.get_defaults = color_key_defaults,
};
struct obs_source_info color_key_filter_v2 = {
.id = "color_key_filter",
.version = 2,
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.get_name = color_key_name,
.create = color_key_create_v2,
.destroy = color_key_destroy_v2,
.video_render = color_key_render_v2,
.update = color_key_update_v2,
.get_properties = color_key_properties_v2,
.get_defaults = color_key_defaults,
};

View File

@ -0,0 +1,97 @@
uniform float4x4 ViewProj;
uniform texture2d image;
uniform float4 cb_v4 = { -0.100644, -0.338572, 0.439216, 0.501961 };
uniform float4 cr_v4 = { 0.439216, -0.398942, -0.040274, 0.501961 };
uniform float opacity;
uniform float contrast;
uniform float brightness;
uniform float gamma;
uniform float2 chroma_key;
uniform float2 pixel_size;
uniform float similarity;
uniform float smoothness;
uniform float spill;
sampler_state textureSampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
struct VertData {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
VertData VSDefault(VertData v_in)
{
VertData vert_out;
vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
vert_out.uv = v_in.uv;
return vert_out;
}
float4 CalcColor(float4 rgba)
{
return float4(pow(rgba.rgb, gamma) * contrast + brightness, rgba.a);
}
float GetChromaDist(float3 rgb)
{
float cb = dot(rgb.rgb, cb_v4.xyz) + cb_v4.w;
float cr = dot(rgb.rgb, cr_v4.xyz) + cr_v4.w;
return distance(chroma_key, float2(cr, cb));
}
float4 SampleTexture(float2 uv)
{
return image.Sample(textureSampler, uv);
}
float GetBoxFilteredChromaDist(float3 rgb, float2 texCoord)
{
float2 h_pixel_size = pixel_size / 2.0;
float2 point_0 = float2(pixel_size.x, h_pixel_size.y);
float2 point_1 = float2(h_pixel_size.x, -pixel_size.y);
float distVal = GetChromaDist(SampleTexture(texCoord-point_0).rgb);
distVal += GetChromaDist(SampleTexture(texCoord+point_0).rgb);
distVal += GetChromaDist(SampleTexture(texCoord-point_1).rgb);
distVal += GetChromaDist(SampleTexture(texCoord+point_1).rgb);
distVal *= 2.0;
distVal += GetChromaDist(rgb);
return distVal / 9.0;
}
float4 ProcessChromaKey(float4 rgba, VertData v_in)
{
float chromaDist = GetBoxFilteredChromaDist(rgba.rgb, v_in.uv);
float baseMask = chromaDist - similarity;
float fullMask = pow(saturate(baseMask / smoothness), 1.5);
float spillVal = pow(saturate(baseMask / spill), 1.5);
rgba.a *= opacity;
rgba.a *= fullMask;
float desat = dot(rgba.rgb, float3(0.2126, 0.7152, 0.0722));
rgba.rgb = lerp(float3(desat, desat, desat), rgba.rgb, spillVal);
return CalcColor(rgba);
}
float4 PSChromaKeyRGBA(VertData v_in) : TARGET
{
float4 rgba = image.Sample(textureSampler, v_in.uv);
return ProcessChromaKey(rgba, v_in);
}
technique Draw
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSChromaKeyRGBA(v_in);
}
}

View File

@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
uniform float4x4 ViewProj;
uniform texture2d image;
uniform float3 gamma;
uniform float gamma;
/* Pre-Compute variables. */
uniform float4x4 color_matrix;

View File

@ -35,9 +35,29 @@ VertDataOut VSDefault(VertDataIn v_in)
return vert_out;
}
float srgb_linear_to_nonlinear_channel(float u)
{
return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1.0 / 2.4)) - 0.055);
}
float4 srgb_linear_to_nonlinear(float4 v)
{
return float4(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b), v.a);
}
float srgb_nonlinear_to_linear_channel(float u)
{
return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
}
float4 srgb_nonlinear_to_linear(float4 v)
{
return float4(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b), v.a);
}
float4 LUT1D(VertDataOut v_in) : TARGET
{
float4 textureColor = image.Sample(textureSampler, v_in.uv);
float4 textureColor = srgb_linear_to_nonlinear(image.Sample(textureSampler, v_in.uv));
if (textureColor.r >= domain_min.r && textureColor.r <= domain_max.r) {
float u = textureColor.r * clut_scale.r + clut_offset.r;
@ -57,12 +77,12 @@ float4 LUT1D(VertDataOut v_in) : TARGET
textureColor.b = lerp(textureColor.b, channel, clut_amount);
}
return textureColor;
return srgb_nonlinear_to_linear(textureColor);
}
float4 LUT3D(VertDataOut v_in) : TARGET
{
float4 textureColor = image.Sample(textureSampler, v_in.uv);
float4 textureColor = srgb_linear_to_nonlinear(image.Sample(textureSampler, v_in.uv));
float r = textureColor.r;
float g = textureColor.g;
float b = textureColor.b;
@ -145,7 +165,7 @@ float4 LUT3D(VertDataOut v_in) : TARGET
textureColor.rgb = lerp(textureColor.rgb, luttedColor, clut_amount);
}
return textureColor;
return srgb_nonlinear_to_linear(textureColor);
}
technique Draw1D

View File

@ -0,0 +1,64 @@
uniform float4x4 ViewProj;
uniform texture2d image;
uniform float opacity;
uniform float contrast;
uniform float brightness;
uniform float gamma;
uniform float4 key_color;
uniform float similarity;
uniform float smoothness;
sampler_state textureSampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
struct VertData {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
VertData VSDefault(VertData v_in)
{
VertData vert_out;
vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
vert_out.uv = v_in.uv;
return vert_out;
}
float4 CalcColor(float4 rgba)
{
return float4(pow(rgba.rgb, gamma) * contrast + brightness, rgba.a);
}
float GetColorDist(float3 rgb)
{
return distance(key_color.rgb, rgb);
}
float4 ProcessColorKey(float4 rgba, VertData v_in)
{
float colorDist = GetColorDist(rgba.rgb);
rgba.a *= saturate(max(colorDist - similarity, 0.0) / smoothness);
return CalcColor(rgba);
}
float4 PSColorKeyRGBA(VertData v_in) : TARGET
{
float4 rgba = image.Sample(textureSampler, v_in.uv);
rgba.a *= opacity;
return ProcessColorKey(rgba, v_in);
}
technique Draw
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSColorKeyRGBA(v_in);
}
}

View File

@ -0,0 +1,51 @@
uniform float4x4 ViewProj;
uniform texture2d image;
uniform float lumaMax;
uniform float lumaMin;
uniform float lumaMaxSmooth;
uniform float lumaMinSmooth;
sampler_state textureSampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
struct VertData {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
VertData VSDefault(VertData v_in)
{
VertData vert_out;
vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
vert_out.uv = v_in.uv;
return vert_out;
}
float4 PSALumaKeyRGBA(VertData v_in) : TARGET
{
float4 rgba = image.Sample(textureSampler, v_in.uv);
float3 lumaCoef = float3(0.2126, 0.7152, 0.0722);
float luminance = dot(rgba.rgb, lumaCoef);
float clo = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luminance);
float chi = 1. - smoothstep(lumaMax - lumaMaxSmooth, lumaMax, luminance);
float amask = clo * chi;
return float4(rgba.rgb, rgba.a * amask);
}
technique Draw
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSALumaKeyRGBA(v_in);
}
}

View File

@ -4,8 +4,6 @@
uniform float4x4 ViewProj;
uniform texture2d image;
uniform texture2d target;
uniform float sharpness;
uniform float texture_width;
uniform float texture_height;

View File

@ -195,12 +195,22 @@ static void draw_frame(struct gpu_delay_filter_data *f)
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_eparam_t *image =
gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(image, tex);
if (linear_srgb)
gs_effect_set_texture_srgb(image, tex);
else
gs_effect_set_texture(image, tex);
while (gs_effect_loop(effect, "Draw"))
gs_draw_sprite(tex, 0, f->cx, f->cy);
gs_enable_framebuffer_srgb(previous);
}
}

View File

@ -66,11 +66,13 @@ static void luma_key_destroy(void *data)
bfree(data);
}
static void *luma_key_create(obs_data_t *settings, obs_source_t *context)
static void *luma_key_create_internal(obs_data_t *settings,
obs_source_t *context,
const char *effect_name)
{
struct luma_key_filter_data *filter =
bzalloc(sizeof(struct luma_key_filter_data));
char *effect_path = obs_module_file("luma_key_filter.effect");
char *effect_path = obs_module_file(effect_name);
filter->context = context;
@ -101,7 +103,19 @@ static void *luma_key_create(obs_data_t *settings, obs_source_t *context)
return filter;
}
static void luma_key_render(void *data, gs_effect_t *effect)
static void *luma_key_create_v1(obs_data_t *settings, obs_source_t *context)
{
return luma_key_create_internal(settings, context,
"luma_key_filter.effect");
}
static void *luma_key_create_v2(obs_data_t *settings, obs_source_t *context)
{
return luma_key_create_internal(settings, context,
"luma_key_filter_v2.effect");
}
static void luma_key_render_internal(void *data, bool srgb)
{
struct luma_key_filter_data *filter = data;
@ -116,9 +130,19 @@ static void luma_key_render(void *data, gs_effect_t *effect)
gs_effect_set_float(filter->luma_min_smooth_param,
filter->luma_min_smooth);
const bool previous = gs_set_linear_srgb(srgb);
obs_source_process_filter_end(filter->context, filter->effect, 0, 0);
gs_set_linear_srgb(previous);
}
UNUSED_PARAMETER(effect);
static void luma_key_render_v1(void *data, gs_effect_t *effect)
{
luma_key_render_internal(data, false);
}
static void luma_key_render_v2(void *data, gs_effect_t *effect)
{
luma_key_render_internal(data, true);
}
static obs_properties_t *luma_key_properties(void *data)
@ -149,11 +173,25 @@ static void luma_key_defaults(obs_data_t *settings)
struct obs_source_info luma_key_filter = {
.id = "luma_key_filter",
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_OBSOLETE,
.get_name = luma_key_name,
.create = luma_key_create,
.create = luma_key_create_v1,
.destroy = luma_key_destroy,
.video_render = luma_key_render,
.video_render = luma_key_render_v1,
.update = luma_key_update,
.get_properties = luma_key_properties,
.get_defaults = luma_key_defaults,
};
struct obs_source_info luma_key_filter_v2 = {
.id = "luma_key_filter",
.version = 2,
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.get_name = luma_key_name,
.create = luma_key_create_v2,
.destroy = luma_key_destroy,
.video_render = luma_key_render_v2,
.update = luma_key_update,
.get_properties = luma_key_properties,
.get_defaults = luma_key_defaults,

View File

@ -75,12 +75,13 @@ static void mask_filter_image_load(struct mask_filter_data *filter)
obs_enter_graphics();
gs_image_file_init_texture(&filter->image);
obs_leave_graphics();
filter->target = filter->image.texture;
}
filter->target = filter->image.texture;
}
static void mask_filter_update(void *data, obs_data_t *settings)
static void mask_filter_update_internal(void *data, obs_data_t *settings,
bool srgb)
{
struct mask_filter_data *filter = data;
@ -97,7 +98,10 @@ static void mask_filter_update(void *data, obs_data_t *settings)
color &= 0xFFFFFF;
color |= (uint32_t)(((double)opacity) * 2.55) << 24;
vec4_from_rgba(&filter->color, color);
if (srgb)
vec4_from_rgba_srgb(&filter->color, color);
else
vec4_from_rgba(&filter->color, color);
mask_filter_image_load(filter);
filter->lock_aspect = !obs_data_get_bool(settings, SETTING_STRETCH);
@ -111,6 +115,16 @@ static void mask_filter_update(void *data, obs_data_t *settings)
obs_leave_graphics();
}
static void mask_filter_update_v1(void *data, obs_data_t *settings)
{
mask_filter_update_internal(data, settings, false);
}
static void mask_filter_update_v2(void *data, obs_data_t *settings)
{
mask_filter_update_internal(data, settings, true);
}
static void mask_filter_defaults(obs_data_t *settings)
{
obs_data_set_default_string(settings, SETTING_TYPE,
@ -220,7 +234,7 @@ static void mask_filter_tick(void *data, float seconds)
}
}
static void mask_filter_render(void *data, gs_effect_t *effect)
static void mask_filter_render_internal(void *data, bool srgb)
{
struct mask_filter_data *filter = data;
obs_source_t *target = obs_filter_get_target(filter->context);
@ -279,7 +293,21 @@ static void mask_filter_render(void *data, gs_effect_t *effect)
param = gs_effect_get_param_by_name(filter->effect, "add_val");
gs_effect_set_vec2(param, &add_val);
const bool previous = gs_set_linear_srgb(srgb);
obs_source_process_filter_end(filter->context, filter->effect, 0, 0);
gs_set_linear_srgb(previous);
}
static void mask_filter_render_v1(void *data, gs_effect_t *effect)
{
mask_filter_render_internal(data, false);
UNUSED_PARAMETER(effect);
}
static void mask_filter_render_v2(void *data, gs_effect_t *effect)
{
mask_filter_render_internal(data, true);
UNUSED_PARAMETER(effect);
}
@ -287,13 +315,28 @@ static void mask_filter_render(void *data, gs_effect_t *effect)
struct obs_source_info mask_filter = {
.id = "mask_filter",
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_OBSOLETE,
.get_name = mask_filter_get_name,
.create = mask_filter_create,
.destroy = mask_filter_destroy,
.update = mask_filter_update_v1,
.get_defaults = mask_filter_defaults,
.get_properties = mask_filter_properties,
.video_tick = mask_filter_tick,
.video_render = mask_filter_render_v1,
};
struct obs_source_info mask_filter_v2 = {
.id = "mask_filter",
.version = 2,
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.get_name = mask_filter_get_name,
.create = mask_filter_create,
.destroy = mask_filter_destroy,
.update = mask_filter_update,
.update = mask_filter_update_v2,
.get_defaults = mask_filter_defaults,
.get_properties = mask_filter_properties,
.video_tick = mask_filter_tick,
.video_render = mask_filter_render,
.video_render = mask_filter_render_v2,
};

View File

@ -9,16 +9,21 @@ MODULE_EXPORT const char *obs_module_description(void)
}
extern struct obs_source_info mask_filter;
extern struct obs_source_info mask_filter_v2;
extern struct obs_source_info crop_filter;
extern struct obs_source_info gain_filter;
extern struct obs_source_info color_filter;
extern struct obs_source_info color_filter_v2;
extern struct obs_source_info scale_filter;
extern struct obs_source_info scroll_filter;
extern struct obs_source_info gpu_delay_filter;
extern struct obs_source_info color_key_filter;
extern struct obs_source_info color_key_filter_v2;
extern struct obs_source_info color_grade_filter;
extern struct obs_source_info sharpness_filter;
extern struct obs_source_info sharpness_filter_v2;
extern struct obs_source_info chroma_key_filter;
extern struct obs_source_info chroma_key_filter_v2;
extern struct obs_source_info async_delay_filter;
#if NOISEREDUCTION_ENABLED
extern struct obs_source_info noise_suppress_filter;
@ -30,20 +35,26 @@ extern struct obs_source_info compressor_filter;
extern struct obs_source_info limiter_filter;
extern struct obs_source_info expander_filter;
extern struct obs_source_info luma_key_filter;
extern struct obs_source_info luma_key_filter_v2;
bool obs_module_load(void)
{
obs_register_source(&mask_filter);
obs_register_source(&mask_filter_v2);
obs_register_source(&crop_filter);
obs_register_source(&gain_filter);
obs_register_source(&color_filter);
obs_register_source(&color_filter_v2);
obs_register_source(&scale_filter);
obs_register_source(&scroll_filter);
obs_register_source(&gpu_delay_filter);
obs_register_source(&color_key_filter);
obs_register_source(&color_key_filter_v2);
obs_register_source(&color_grade_filter);
obs_register_source(&sharpness_filter);
obs_register_source(&sharpness_filter_v2);
obs_register_source(&chroma_key_filter);
obs_register_source(&chroma_key_filter_v2);
obs_register_source(&async_delay_filter);
#if NOISEREDUCTION_ENABLED
obs_register_source(&noise_suppress_filter);
@ -55,5 +66,6 @@ bool obs_module_load(void)
obs_register_source(&limiter_filter);
obs_register_source(&expander_filter);
obs_register_source(&luma_key_filter);
obs_register_source(&luma_key_filter_v2);
return true;
}

View File

@ -297,9 +297,11 @@ static void scale_filter_render(void *data, gs_effect_t *effect)
gs_effect_set_next_sampler(filter->image_param,
filter->point_sampler);
const bool previous = gs_set_linear_srgb(true);
obs_source_process_filter_tech_end(filter->context, filter->effect,
filter->cx_out, filter->cy_out,
technique);
gs_set_linear_srgb(previous);
UNUSED_PARAMETER(effect);
}

View File

@ -73,7 +73,7 @@ static void *sharpness_create(obs_data_t *settings, obs_source_t *context)
return filter;
}
static void sharpness_render(void *data, gs_effect_t *effect)
static void sharpness_render_internal(void *data, bool srgb)
{
struct sharpness_data *filter = data;
@ -90,7 +90,21 @@ static void sharpness_render(void *data, gs_effect_t *effect)
gs_effect_set_float(filter->texture_width, filter->texwidth);
gs_effect_set_float(filter->texture_height, filter->texheight);
const bool previous = gs_set_linear_srgb(srgb);
obs_source_process_filter_end(filter->context, filter->effect, 0, 0);
gs_set_linear_srgb(previous);
}
static void sharpness_render_v1(void *data, gs_effect_t *effect)
{
sharpness_render_internal(data, false);
UNUSED_PARAMETER(effect);
}
static void sharpness_render_v2(void *data, gs_effect_t *effect)
{
sharpness_render_internal(data, true);
UNUSED_PARAMETER(effect);
}
@ -115,12 +129,26 @@ static void sharpness_defaults(obs_data_t *settings)
struct obs_source_info sharpness_filter = {
.id = "sharpness_filter",
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_OBSOLETE,
.get_name = sharpness_getname,
.create = sharpness_create,
.destroy = sharpness_destroy,
.update = sharpness_update,
.video_render = sharpness_render_v1,
.get_properties = sharpness_properties,
.get_defaults = sharpness_defaults,
};
struct obs_source_info sharpness_filter_v2 = {
.id = "sharpness_filter",
.version = 2,
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_VIDEO,
.get_name = sharpness_getname,
.create = sharpness_create,
.destroy = sharpness_destroy,
.update = sharpness_update,
.video_render = sharpness_render,
.video_render = sharpness_render_v2,
.get_properties = sharpness_properties,
.get_defaults = sharpness_defaults,
};

View File

@ -871,15 +871,20 @@ inline void TextSource::Render()
gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
gs_technique_t *tech = gs_effect_get_technique(effect, "Draw");
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(true);
gs_technique_begin(tech);
gs_technique_begin_pass(tech, 0);
gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"),
tex);
gs_effect_set_texture_srgb(gs_effect_get_param_by_name(effect, "image"),
tex);
gs_draw_sprite(tex, 0, cx, cy);
gs_technique_end_pass(tech);
gs_technique_end(tech);
gs_enable_framebuffer_srgb(previous);
}
/* ------------------------------------------------------------------------- */

View File

@ -24,8 +24,7 @@ VertData VSDefault(VertData v_in)
float4 PSFadeToColor(VertData v_in) : TARGET
{
float4 premultiplied = float4(color.rgb * color.a, color.a);
return lerp(tex.Sample(textureSampler, v_in.uv), premultiplied, swp);
return lerp(tex.Sample(textureSampler, v_in.uv), color, swp);
}
technique FadeToColor

View File

@ -52,7 +52,7 @@ static void fade_to_color_update(void *data, obs_data_t *settings)
color |= 0xFF000000;
vec4_from_rgba(&fade_to_color->color, color);
vec4_from_rgba_srgb_premultiply(&fade_to_color->color, color);
fade_to_color->switch_point = (float)swp / 100.0f;
}
@ -105,13 +105,19 @@ static void fade_to_color_callback(void *data, gs_texture_t *a, gs_texture_t *b,
float swp = t < fade_to_color->switch_point ? sa : 1.0f - sb;
gs_effect_set_texture(fade_to_color->ep_tex,
t < fade_to_color->switch_point ? a : b);
gs_effect_set_float(fade_to_color->ep_swp, swp);
gs_texture_t *const tex = (t < fade_to_color->switch_point) ? a : b;
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(true);
gs_effect_set_texture_srgb(fade_to_color->ep_tex, tex);
gs_effect_set_vec4(fade_to_color->ep_color, &fade_to_color->color);
gs_effect_set_float(fade_to_color->ep_swp, swp);
while (gs_effect_loop(fade_to_color->effect, "FadeToColor"))
gs_draw_sprite(NULL, 0, cx, cy);
gs_enable_framebuffer_srgb(previous);
}
static void fade_to_color_video_render(void *data, gs_effect_t *effect)

View File

@ -53,12 +53,17 @@ static void fade_callback(void *data, gs_texture_t *a, gs_texture_t *b, float t,
{
struct fade_info *fade = data;
gs_effect_set_texture(fade->a_param, a);
gs_effect_set_texture(fade->b_param, b);
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(true);
gs_effect_set_texture_srgb(fade->a_param, a);
gs_effect_set_texture_srgb(fade->b_param, b);
gs_effect_set_float(fade->fade_param, t);
while (gs_effect_loop(fade->effect, "Fade"))
gs_draw_sprite(NULL, 0, cx, cy);
gs_enable_framebuffer_srgb(previous);
}
static void fade_video_render(void *data, gs_effect_t *effect)

View File

@ -165,8 +165,11 @@ static void luma_wipe_callback(void *data, gs_texture_t *a, gs_texture_t *b,
{
struct luma_wipe_info *lwipe = data;
gs_effect_set_texture(lwipe->ep_a_tex, a);
gs_effect_set_texture(lwipe->ep_b_tex, b);
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(true);
gs_effect_set_texture_srgb(lwipe->ep_a_tex, a);
gs_effect_set_texture_srgb(lwipe->ep_b_tex, b);
gs_effect_set_texture(lwipe->ep_l_tex, lwipe->luma_image.texture);
gs_effect_set_float(lwipe->ep_progress, t);
@ -175,6 +178,8 @@ static void luma_wipe_callback(void *data, gs_texture_t *a, gs_texture_t *b,
while (gs_effect_loop(lwipe->effect, "LumaWipe"))
gs_draw_sprite(NULL, 0, cx, cy);
gs_enable_framebuffer_srgb(previous);
}
void luma_wipe_video_render(void *data, gs_effect_t *effect)

View File

@ -93,14 +93,26 @@ static void slide_callback(void *data, gs_texture_t *a, gs_texture_t *b,
vec2_mulf(&tex_a_dir, &tex_a_dir, t);
vec2_mulf(&tex_b_dir, &tex_b_dir, 1.0f - t);
gs_effect_set_texture(slide->a_param, a);
gs_effect_set_texture(slide->b_param, b);
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
if (linear_srgb) {
gs_effect_set_texture_srgb(slide->a_param, a);
gs_effect_set_texture_srgb(slide->b_param, b);
} else {
gs_effect_set_texture(slide->a_param, a);
gs_effect_set_texture(slide->b_param, b);
}
gs_effect_set_vec2(slide->tex_a_dir_param, &tex_a_dir);
gs_effect_set_vec2(slide->tex_b_dir_param, &tex_b_dir);
while (gs_effect_loop(slide->effect, "Slide"))
gs_draw_sprite(NULL, 0, cx, cy);
gs_enable_framebuffer_srgb(previous);
}
void slide_video_render(void *data, gs_effect_t *effect)

View File

@ -88,12 +88,26 @@ static void swipe_callback(void *data, gs_texture_t *a, gs_texture_t *b,
vec2_mulf(&swipe_val, &swipe_val, swipe->swipe_in ? 1.0f - t : t);
gs_effect_set_texture(swipe->a_param, swipe->swipe_in ? b : a);
gs_effect_set_texture(swipe->b_param, swipe->swipe_in ? a : b);
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
gs_texture_t *t0 = swipe->swipe_in ? b : a;
gs_texture_t *t1 = swipe->swipe_in ? a : b;
if (linear_srgb) {
gs_effect_set_texture_srgb(swipe->a_param, t0);
gs_effect_set_texture_srgb(swipe->b_param, t1);
} else {
gs_effect_set_texture(swipe->a_param, t0);
gs_effect_set_texture(swipe->b_param, t1);
}
gs_effect_set_vec2(swipe->swipe_param, &swipe_val);
while (gs_effect_loop(swipe->effect, "Swipe"))
gs_draw_sprite(NULL, 0, cx, cy);
gs_enable_framebuffer_srgb(previous);
}
static void swipe_video_render(void *data, gs_effect_t *effect)

View File

@ -67,6 +67,11 @@ void draw_uv_vbuffer(gs_vertbuffer_t *vbuf, gs_texture_t *tex,
if (vbuf == NULL || tex == NULL)
return;
const bool linear_srgb = gs_get_linear_srgb();
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
gs_vertexbuffer_flush(vbuf);
gs_load_vertexbuffer(vbuf);
gs_load_indexbuffer(NULL);
@ -75,7 +80,10 @@ void draw_uv_vbuffer(gs_vertbuffer_t *vbuf, gs_texture_t *tex,
for (size_t i = 0; i < passes; i++) {
if (gs_technique_begin_pass(tech, i)) {
gs_effect_set_texture(image, texture);
if (linear_srgb)
gs_effect_set_texture_srgb(image, texture);
else
gs_effect_set_texture(image, texture);
gs_draw(GS_TRIS, 0, num_verts);
@ -84,4 +92,6 @@ void draw_uv_vbuffer(gs_vertbuffer_t *vbuf, gs_texture_t *tex,
}
gs_technique_end(tech);
gs_enable_framebuffer_srgb(previous);
}

View File

@ -247,6 +247,8 @@ static void ft2_source_render(void *data, gs_effect_t *effect)
if (srcdata->text == NULL || *srcdata->text == 0)
return;
const bool previous = gs_set_linear_srgb(true);
gs_reset_blend_state();
if (srcdata->outline_text)
draw_outlines(srcdata);
@ -256,6 +258,8 @@ static void ft2_source_render(void *data, gs_effect_t *effect)
draw_uv_vbuffer(srcdata->vbuf, srcdata->tex, srcdata->draw_effect,
(uint32_t)wcslen(srcdata->text) * 6);
gs_set_linear_srgb(previous);
UNUSED_PARAMETER(effect);
}

View File

@ -172,7 +172,15 @@ static void draw_texture(struct dc_capture *capture, gs_effect_t *effect)
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
size_t passes;
gs_effect_set_texture(image, texture);
const bool linear_srgb = gs_get_linear_srgb() && capture->compatibility;
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(linear_srgb);
if (linear_srgb)
gs_effect_set_texture_srgb(image, texture);
else
gs_effect_set_texture(image, texture);
passes = gs_technique_begin(tech);
for (size_t i = 0; i < passes; i++) {
@ -186,6 +194,8 @@ static void draw_texture(struct dc_capture *capture, gs_effect_t *effect)
}
}
gs_technique_end(tech);
gs_enable_framebuffer_srgb(previous);
}
void dc_capture_render(struct dc_capture *capture, gs_effect_t *effect)

View File

@ -236,6 +236,8 @@ static void duplicator_capture_render(void *data, gs_effect_t *effect)
rot = capture->rot;
const bool previous = gs_set_linear_srgb(false);
while (gs_effect_loop(effect, "Draw")) {
if (rot != 0) {
float x = 0.0f;
@ -265,6 +267,8 @@ static void duplicator_capture_render(void *data, gs_effect_t *effect)
gs_matrix_pop();
}
gs_set_linear_srgb(previous);
if (capture->capture_cursor) {
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);

View File

@ -149,6 +149,7 @@ struct game_capture {
ipc_pipe_server_t pipe;
gs_texture_t *texture;
bool supports_srgb;
struct hook_info *global_hook_info;
HANDLE keepalive_mutex;
HANDLE hook_init;
@ -1574,6 +1575,7 @@ static inline bool init_shmem_capture(struct game_capture *gc)
return false;
}
gc->supports_srgb = true;
gc->copy_texture = copy_shmem_tex;
return true;
}
@ -1583,6 +1585,8 @@ static inline bool init_shtex_capture(struct game_capture *gc)
obs_enter_graphics();
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);
obs_leave_graphics();
if (!gc->texture) {
@ -1808,6 +1812,9 @@ static void game_capture_render(void *data, gs_effect_t *effect)
? 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);
while (gs_effect_loop(effect, "Draw")) {
obs_source_draw(gc->texture, 0, 0, 0, 0,
gc->global_hook_info->flip);
@ -1818,6 +1825,8 @@ static void game_capture_render(void *data, gs_effect_t *effect)
}
}
gs_set_linear_srgb(previous);
if (!gc->config.allow_transparency && gc->config.cursor &&
!gc->cursor_hidden) {
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);