libobs: Add dormant SRGB format support

GS_RGBA, GS_BGRX, and GS_BGRA now use TYPELESS DXGI formats, so we can
alias them between UNORM and UNORM_SRGB as necessary. GS_RGBA_UNORM,
GS_BGRX_UNORM, and GS_BGRA_UNORM have been added to support straight
UNORM types, which Windows requires for sharing textures from D3D9 and
OpenGL. The D3D path aliases via views, and GL aliases via
GL_EXT_texture_sRGB_decode/GL_FRAMEBUFFER_SRGB.

A significant amount of code has changed in the D3D/GL backends, but the
concepts are simple. On the D3D side, we need separate SRVs and RTVs to
support nonlinear/linear reads and writes. On the GL side, we need to
set the proper GL parameters to emulate the same.

Add gs_enable_framebuffer_srgb/gs_framebuffer_srgb_enabled to set/get
the framebuffer as SRGB or not.

Add gs_linear_srgb_active/gs_set_linear_srgb to instruct sources that
they should render as SRGB. Legacy sources can ignore this setting
without regression.

Update obs_source_draw to use linear SRGB as needed.

Update render_filter_tex to use linear SRGB as needed.

Add gs_effect_set_texture_srgb next to gs_effect_set_texture to set
texture with SRGB view instead.

Add SRGB helpers for vec4 struct.

Create GDI-compatible textures without SRGB support. Doesn't seem to
work with SRGB formats.
This commit is contained in:
jpark37 2021-01-19 15:00:51 -08:00
parent 7427272062
commit 66259560e0
18 changed files with 667 additions and 187 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
@ -492,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;
@ -530,6 +537,10 @@ void device_load_texture(gs_device_t *device, gs_texture_t *tex, int unit)
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;
@ -539,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)
{
@ -863,6 +884,21 @@ fail:
blog(LOG_ERROR, "device_set_cube_render_target (GL) failed");
}
void device_enable_framebuffer_srgb(gs_device_t *device, bool enable)
{
if (enable)
gl_enable(GL_FRAMEBUFFER_SRGB);
else
gl_disable(GL_FRAMEBUFFER_SRGB);
}
bool device_framebuffer_srgb_enabled(gs_device_t *device)
{
const GLboolean enabled = glIsEnabled(GL_FRAMEBUFFER_SRGB);
gl_success("glIsEnabled");
return enabled == GL_TRUE;
}
void device_copy_texture_region(gs_device_t *device, gs_texture_t *dst,
uint32_t dst_x, uint32_t dst_y,
gs_texture_t *src, uint32_t src_x,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)