commit
8333f13587
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
97
plugins/obs-filters/data/chroma_key_filter_v2.effect
Normal file
97
plugins/obs-filters/data/chroma_key_filter_v2.effect
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
|
64
plugins/obs-filters/data/color_key_filter_v2.effect
Normal file
64
plugins/obs-filters/data/color_key_filter_v2.effect
Normal 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);
|
||||
}
|
||||
}
|
51
plugins/obs-filters/data/luma_key_filter_v2.effect
Normal file
51
plugins/obs-filters/data/luma_key_filter_v2.effect
Normal 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);
|
||||
}
|
||||
}
|
@ -4,8 +4,6 @@
|
||||
uniform float4x4 ViewProj;
|
||||
uniform texture2d image;
|
||||
|
||||
uniform texture2d target;
|
||||
|
||||
uniform float sharpness;
|
||||
uniform float texture_width;
|
||||
uniform float texture_height;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user