diff --git a/res/gamedata/shaders/r3/yuv2rgb.ps b/res/gamedata/shaders/r3/yuv2rgb.ps new file mode 100644 index 00000000000..cb91ddeb339 Binary files /dev/null and b/res/gamedata/shaders/r3/yuv2rgb.ps differ diff --git a/res/gamedata/shaders/r5/yuv2rgb.ps b/res/gamedata/shaders/r5/yuv2rgb.ps index 6ddcca40a40..cb91ddeb339 100644 Binary files a/res/gamedata/shaders/r5/yuv2rgb.ps and b/res/gamedata/shaders/r5/yuv2rgb.ps differ diff --git a/src/Layers/xrRender/dxRainRender.cpp b/src/Layers/xrRender/dxRainRender.cpp index 30f9a70d5dc..ee97aec1e61 100644 --- a/src/Layers/xrRender/dxRainRender.cpp +++ b/src/Layers/xrRender/dxRainRender.cpp @@ -22,6 +22,18 @@ const int max_particles = 1000; const int particles_cache = 400; const float particles_time = .3f; +namespace +{ + +float srgbToLinear(float c) { return std::pow(c, 2.2f); } + +Fvector3 srgbToLinear(const Fvector3 c) +{ + return Fvector3{srgbToLinear(c.x), srgbToLinear(c.y), srgbToLinear(c.z)}; +} + +} // namespace + dxRainRender::dxRainRender() { IReader* F = FS.r_open("$game_meshes$", "dm" DELIMITER "rain.dm"); @@ -84,7 +96,7 @@ void dxRainRender::Render(CEffect_Rain& owner) // visual const float factor_visual = factor / 2.f + .5f; - const Fvector3 f_rain_color = g_pGamePersistent->Environment().CurrentEnv.rain_color; + const Fvector3 f_rain_color = srgbToLinear(g_pGamePersistent->Environment().CurrentEnv.rain_color); const u32 u_rain_color = color_rgba_f(f_rain_color.x, f_rain_color.y, f_rain_color.z, factor_visual); const float b_radius_wrap_sqr = _sqr((source_radius + .5f)); diff --git a/src/Layers/xrRender/xrRender_console.cpp b/src/Layers/xrRender/xrRender_console.cpp index 614e020d196..099ecf94862 100644 --- a/src/Layers/xrRender/xrRender_console.cpp +++ b/src/Layers/xrRender/xrRender_console.cpp @@ -115,6 +115,10 @@ const xr_token qmsaa__atest_token[] = { u32 ps_r3_minmax_sm = 3; // = 0; const xr_token qminmax_sm_token[] = {{"off", 0}, {"on", 1}, {"auto", 2}, {"autodetect", 3}, {nullptr, 0}}; +u32 ps_r3_rendering_space = 0; // = 0; +const xr_token rendering__space_token[] = { + {"gamma", 0}, {"linear", 1}, {nullptr, 0}}; + // “Off” // “DX10.0 style [Standard]” // “DX10.1 style [Higher quality]” @@ -1100,6 +1104,7 @@ void xrRender_initconsole() //CMD3(CCC_Mask, "r3_msaa_alphatest", &ps_r2_ls_flags, (u32)R3FLAG_MSAA_ALPHATEST); CMD3(CCC_Token, "r3_msaa_alphatest", &ps_r3_msaa_atest, qmsaa__atest_token); CMD3(CCC_Token, "r3_minmax_sm", &ps_r3_minmax_sm, qminmax_sm_token); + CMD3(CCC_Token, "r3_rendering_space", &ps_r3_rendering_space, rendering__space_token); // Allow real-time fog config reload #if (RENDER == R_R3) || (RENDER == R_R4) diff --git a/src/Layers/xrRender/xrRender_console.h b/src/Layers/xrRender/xrRender_console.h index d8008a948f3..3410deb83df 100644 --- a/src/Layers/xrRender/xrRender_console.h +++ b/src/Layers/xrRender/xrRender_console.h @@ -29,6 +29,9 @@ extern ECORE_API const xr_token qmsaa__atest_token[]; extern ECORE_API u32 ps_r3_minmax_sm; // = 0; extern ECORE_API const xr_token qminmax_sm_token[]; +extern ECORE_API u32 ps_r3_rendering_space; // = 0; +extern ECORE_API const xr_token rendering__space_token[]; + extern ENGINE_API int ps_r__Supersample; extern ECORE_API int ps_r__LightSleepFrames; diff --git a/src/Layers/xrRenderDX11/dx11HW.cpp b/src/Layers/xrRenderDX11/dx11HW.cpp index 00c16c2e639..d5b3266738a 100644 --- a/src/Layers/xrRenderDX11/dx11HW.cpp +++ b/src/Layers/xrRenderDX11/dx11HW.cpp @@ -271,15 +271,9 @@ bool CHW::CreateSwapChain(HWND hwnd) sd.BufferDesc.Height = Device.dwHeight; // TODO: DX11: implement dynamic format selection - constexpr DXGI_FORMAT formats[] = - { - //DXGI_FORMAT_R16G16B16A16_FLOAT, // Do we even need this? - //DXGI_FORMAT_R10G10B10A2_UNORM, // D3DX11SaveTextureToMemory fails on this format - DXGI_FORMAT_R8G8B8A8_UNORM, - }; + sd.BufferDesc.Format = selectBackBufferFormat(); // Select back-buffer format - sd.BufferDesc.Format = SelectFormat(D3D_FORMAT_SUPPORT_DISPLAY, formats); Caps.fTarget = dx11TextureUtils::ConvertTextureFormat(sd.BufferDesc.Format); // Buffering @@ -329,15 +323,8 @@ bool CHW::CreateSwapChain2(HWND hwnd) desc.Width = Device.dwWidth; desc.Height = Device.dwHeight; - constexpr DXGI_FORMAT formats[] = - { - //DXGI_FORMAT_R16G16B16A16_FLOAT, - //DXGI_FORMAT_R10G10B10A2_UNORM, - DXGI_FORMAT_R8G8B8A8_UNORM, - }; + desc.Format = selectBackBufferFormat(); - // Select back-buffer format - desc.Format = SelectFormat(D3D11_FORMAT_SUPPORT_DISPLAY, formats); Caps.fTarget = dx11TextureUtils::ConvertTextureFormat(desc.Format); // Buffering @@ -388,6 +375,31 @@ bool CHW::CreateSwapChain2(HWND hwnd) return false; } +DXGI_FORMAT CHW::selectBackBufferFormat() const +{ + if (ps_r3_rendering_space == 1) + { + constexpr DXGI_FORMAT formats[] = { + // DXGI_FORMAT_R16G16B16A16_FLOAT, + // DXGI_FORMAT_R10G10B10A2_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + // DXGI_FORMAT_R8G8B8A8_UNORM, + }; + + // Select back-buffer format + return SelectFormat(D3D11_FORMAT_SUPPORT_DISPLAY, formats); + } else { + constexpr DXGI_FORMAT formats[] = { + // DXGI_FORMAT_R16G16B16A16_FLOAT, + // DXGI_FORMAT_R10G10B10A2_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + }; + + // Select back-buffer format + return SelectFormat(D3D11_FORMAT_SUPPORT_DISPLAY, formats); + } +} + bool CHW::ThisInstanceIsGlobal() const { return this == &HW; @@ -438,6 +450,7 @@ void CHW::Reset() DXGI_MODE_DESC& desc = m_ChainDesc.BufferDesc; desc.Width = Device.dwWidth; desc.Height = Device.dwHeight; + desc.Format = selectBackBufferFormat(); CHK_DX(m_pSwapChain->ResizeTarget(&desc)); CHK_DX(m_pSwapChain->ResizeBuffers( diff --git a/src/Layers/xrRenderDX11/dx11HW.h b/src/Layers/xrRenderDX11/dx11HW.h index 116149d492b..306411a05c0 100644 --- a/src/Layers/xrRenderDX11/dx11HW.h +++ b/src/Layers/xrRenderDX11/dx11HW.h @@ -103,6 +103,7 @@ class CHW XRay::Module hD3DCompiler; XRay::Module hDXGI; XRay::Module hD3D; + DXGI_FORMAT selectBackBufferFormat() const; }; extern ECORE_API CHW HW; diff --git a/src/Layers/xrRenderDX11/dx11Texture.cpp b/src/Layers/xrRenderDX11/dx11Texture.cpp index cafed2f3e8d..d72faf503cc 100644 --- a/src/Layers/xrRenderDX11/dx11Texture.cpp +++ b/src/Layers/xrRenderDX11/dx11Texture.cpp @@ -290,6 +290,21 @@ ID3DBaseTexture* CRender::texture_load(LPCSTR fRName, u32& ret_msize) string_path fname; xr_strcpy(fname, fRName); //. andy if (strext(fname)) *strext(fname)=0; fix_texture_name(fname); + + bool force_srgb = + o.linear_space_rendering + && !strstr(fname, "_bump") + && !strstr(fname, "_mask") + && !strstr(fname, "_dudv") + && !strstr(fname, "water_normal") + && !strstr(fname, "internal_") + + && !strstr(fname, "_lm.") // terrain lightmaps (level/*/terrain/) + && !strstr(fname, "level_lods_nm") // level lods normal map (level/*/) + && !strstr(fname, "lmap#") // level light maps (level/*/) + + && !strstr(fname, "ui_magnifier2"); + IReader* S = NULL; if (!FS.exist(fn, "$game_textures$", fname, ".dds") && strstr(fname, "_bump")) goto _BUMP_from_base; @@ -346,9 +361,13 @@ ID3DBaseTexture* CRender::texture_load(LPCSTR fRName, u32& ret_msize) } R_CHK2(CreateTextureEx(HW.pDevice, texture.GetImages() + mip_lod, texture.GetImageCount(), IMG, - D3D_USAGE_IMMUTABLE, D3D_BIND_SHADER_RESOURCE, 0, IMG.miscFlags, DirectX::CREATETEX_DEFAULT, + D3D_USAGE_IMMUTABLE, D3D_BIND_SHADER_RESOURCE, 0, IMG.miscFlags, force_srgb ? DirectX::CREATETEX_FORCE_SRGB : DirectX::CREATETEX_DEFAULT, &pTexture2D), fn ); + /*R_CHK2(DirectX::CreateDDSTextureFromMemoryEx(HW.pDevice, reinterpret_cast(S->pointer()), S->length(), 0, D3D_USAGE_IMMUTABLE, D3D_BIND_SHADER_RESOURCE, 0, 0, + force_srgb ? DirectX::DDS_LOADER_FORCE_SRGB : DirectX::DDS_LOADER_DEFAULT, &pTexture2D, nullptr), + fn);*/ + FS.r_close(S); // OK diff --git a/src/Layers/xrRenderDX11/dx11TextureUtils.cpp b/src/Layers/xrRenderDX11/dx11TextureUtils.cpp index 41fba87b5f2..55e1d395661 100644 --- a/src/Layers/xrRenderDX11/dx11TextureUtils.cpp +++ b/src/Layers/xrRenderDX11/dx11TextureUtils.cpp @@ -26,6 +26,7 @@ TextureFormatPairs TextureFormatList[] = { // D3DFMT_X4R4G4B4 Not available {D3DFMT_A2B10G10R10, DXGI_FORMAT_R10G10B10A2_UNORM}, {D3DFMT_A8B8G8R8, DXGI_FORMAT_R8G8B8A8_UNORM}, // & DXGI_FORMAT_R8G8B8A8_UNORM_SRGB + {D3DFMT_HACK_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB}, // D3DFMT_X8B8G8R8 Not available {D3DFMT_G16R16, DXGI_FORMAT_R16G16_UNORM}, // D3DFMT_A2R10G10B10 Not available diff --git a/src/Layers/xrRenderPC_R4/r4_rendertarget.h b/src/Layers/xrRenderPC_R4/r4_rendertarget.h index a0cde0c26fc..5a2f39c417e 100644 --- a/src/Layers/xrRenderPC_R4/r4_rendertarget.h +++ b/src/Layers/xrRenderPC_R4/r4_rendertarget.h @@ -359,6 +359,8 @@ class CRenderTarget : public IRender_Target void DoAsyncScreenshot(); + + #ifdef DEBUG void dbg_addline(const Fvector& P0, const Fvector& P1, u32 c) { @@ -407,4 +409,13 @@ class CRenderTarget : public IRender_Target void dbg_addline(Fvector& /*P0*/, Fvector& /*P1*/, u32 /*c*/) {} void dbg_addplane(Fplane& /*P0*/, u32 /*c*/) {} #endif +private: + float toLinearSpace(float c) { + return RImplementation.o.linear_space_rendering ? std::pow(c, 2.2f) : c; + } + + Fvector4 toLinearSpace(const Fvector4& c) + { + return Fvector4{toLinearSpace(c.x), toLinearSpace(c.y), toLinearSpace(c.z), c.w}; + } }; diff --git a/src/Layers/xrRenderPC_R4/r4_rendertarget_phase_combine.cpp b/src/Layers/xrRenderPC_R4/r4_rendertarget_phase_combine.cpp index f7163af4143..68b75080421 100644 --- a/src/Layers/xrRenderPC_R4/r4_rendertarget_phase_combine.cpp +++ b/src/Layers/xrRenderPC_R4/r4_rendertarget_phase_combine.cpp @@ -216,8 +216,8 @@ void CRenderTarget::phase_combine() RCache.set_c("Ldynamic_color", sunclr); RCache.set_c("Ldynamic_dir", sundir); - RCache.set_c("env_color", envclr); - RCache.set_c("fog_color", fogclr); + RCache.set_c("env_color", toLinearSpace(envclr)); + RCache.set_c("fog_color", toLinearSpace(fogclr)); RCache.set_c("ssao_noise_tile_factor", fSSAONoise); RCache.set_c("ssao_kernel_size", fSSAOKernelSize); diff --git a/src/Layers/xrRenderPC_R4/r4_shaders.cpp b/src/Layers/xrRenderPC_R4/r4_shaders.cpp index b5681053453..50498323af0 100644 --- a/src/Layers/xrRenderPC_R4/r4_shaders.cpp +++ b/src/Layers/xrRenderPC_R4/r4_shaders.cpp @@ -418,6 +418,10 @@ HRESULT CRender::shader_compile(pcstr name, IReader* fs, pcstr pFunctionName, // Minmax SM appendShaderOption(o.minmax_sm, "USE_MINMAX_SM", "1"); + // Linear space rendering if 1, or gamma space if 0 + appendShaderOption(o.linear_space_rendering, "LINEAR_SPACE_RENDERING", "1"); + + // Ascii's Screen Space Shaders - SSS preprocessor stuff if (ps_ssfx_rain_1.w > 0) { diff --git a/src/Layers/xrRender_R2/r2.cpp b/src/Layers/xrRender_R2/r2.cpp index d1433aecefc..6b9429f43a5 100644 --- a/src/Layers/xrRender_R2/r2.cpp +++ b/src/Layers/xrRender_R2/r2.cpp @@ -559,6 +559,7 @@ void CRender::create() o.tessellation = HW.FeatureLevel >= D3D_FEATURE_LEVEL_11_0 && ps_r2_ls_flags_ext.test(R2FLAGEXT_ENABLE_TESSELLATION); o.support_rt_arrays = true; + o.linear_space_rendering = (ps_r3_rendering_space == 1); #else o.support_rt_arrays = false; #endif diff --git a/src/Layers/xrRender_R2/r2.h b/src/Layers/xrRender_R2/r2.h index 52c4a6be11d..cb626dcf5f9 100644 --- a/src/Layers/xrRender_R2/r2.h +++ b/src/Layers/xrRender_R2/r2.h @@ -275,6 +275,8 @@ class CRender final : public D3DXRenderBase // Yohji - New shader support u32 new_shader_support : 1; + + u32 linear_space_rendering : 1; } o; struct RenderR2Statistics diff --git a/src/Layers/xrRender_R2/r2_rendertarget.cpp b/src/Layers/xrRender_R2/r2_rendertarget.cpp index 7490a391dfa..aa370a2ef1d 100644 --- a/src/Layers/xrRender_R2/r2_rendertarget.cpp +++ b/src/Layers/xrRender_R2/r2_rendertarget.cpp @@ -261,6 +261,8 @@ CRenderTarget::CRenderTarget() const u32 SampleCount = options.msaa ? options.msaa_samples : 1u; const u32 BoundSamples = options.msaa_opt ? 1u : options.msaa_samples; + const D3DFORMAT mainColorFormat = options.linear_space_rendering ? D3DFMT_HACK_R8G8B8A8_UNORM_SRGB : D3DFMT_A8R8G8B8; + #ifdef DEBUG Msg("MSAA samples = %d", SampleCount); if (options.msaa_opt) @@ -327,7 +329,7 @@ CRenderTarget::CRenderTarget() if (options.mrtmixdepth) { // NV50 - rt_Color.create(r2_RT_albedo, w, h, D3DFMT_A8R8G8B8, SampleCount); + rt_Color.create(r2_RT_albedo, w, h, mainColorFormat, SampleCount); rt_Accumulator.create(r2_RT_accum, w, h, D3DFMT_A16B16G16R16F, SampleCount); } else @@ -358,10 +360,10 @@ CRenderTarget::CRenderTarget() } // generic(LDR) RTs - rt_Generic_0.create(r2_RT_generic0, w, h, D3DFMT_A8R8G8B8, 1); - rt_Generic_1.create(r2_RT_generic1, w, h, D3DFMT_A8R8G8B8, 1); + rt_Generic_0.create(r2_RT_generic0, w, h, mainColorFormat, 1); + rt_Generic_1.create(r2_RT_generic1, w, h, mainColorFormat, 1); #if defined(USE_DX11) || defined(USE_OGL) - rt_Generic.create(r2_RT_generic, w, h, D3DFMT_A8R8G8B8, 1); + rt_Generic.create(r2_RT_generic, w, h, mainColorFormat, 1); #endif if (!options.msaa) { @@ -370,8 +372,8 @@ CRenderTarget::CRenderTarget() } else { - rt_Generic_0_r.create(r2_RT_generic0_r, w, h, D3DFMT_A8R8G8B8, SampleCount); - rt_Generic_1_r.create(r2_RT_generic1_r, w, h, D3DFMT_A8R8G8B8, SampleCount); + rt_Generic_0_r.create(r2_RT_generic0_r, w, h, mainColorFormat, SampleCount); + rt_Generic_1_r.create(r2_RT_generic1_r, w, h, mainColorFormat, SampleCount); } // Igor: for volumetric lights // rt_Generic_2.create (r2_RT_generic2,w,h,D3DFMT_A8R8G8B8 ); @@ -631,7 +633,7 @@ CRenderTarget::CRenderTarget() // BLOOM { - D3DFORMAT fmt = D3DFMT_A8R8G8B8; // D3DFMT_X8R8G8B8; + D3DFORMAT fmt = mainColorFormat; u32 w = BLOOM_size_X, h = BLOOM_size_Y; constexpr u32 fvf_build = D3DFVF_XYZRHW | D3DFVF_TEX4 | D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE2(1) | D3DFVF_TEXCOORDSIZE2(2) | D3DFVF_TEXCOORDSIZE2(3); diff --git a/src/Layers/xrRender_R2/r2_types.h b/src/Layers/xrRender_R2/r2_types.h index 78262aabf25..5386a68aa78 100644 --- a/src/Layers/xrRender_R2/r2_types.h +++ b/src/Layers/xrRender_R2/r2_types.h @@ -151,3 +151,5 @@ IC float u_diffuse2s(Fvector3& c) { return u_diffuse2s(c.x, c.y, c.z); } + +#define D3DFMT_HACK_R8G8B8A8_UNORM_SRGB ((D3DFORMAT)666)