v1.1.6 — NIS (NVIDIA Image Scaling) scaling filter + HDR highlight whitening slider

This commit is contained in:
2026-06-26 18:25:48 -04:00
parent c08dec9579
commit 6c248f6174
29 changed files with 1749 additions and 22 deletions
@@ -9,5 +9,6 @@ namespace Ryujinx.Common.Configuration
Nearest,
Fsr,
Area,
Nis,
}
}
+1 -1
View File
@@ -15,6 +15,6 @@ namespace Ryujinx.Graphics.GAL
void SetScalingFilter(ScalingFilter type);
void SetScalingFilterLevel(float level);
void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled);
void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend);
void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend, float whiten);
}
}
@@ -42,6 +42,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
public void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled) { }
public void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend) { }
public void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend, float whiten) { }
}
}
+1 -1
View File
@@ -424,7 +424,7 @@ namespace Ryujinx.Graphics.OpenGL
_updateScalingFilter = true;
}
public void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend)
public void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend, float whiten)
{
// HDR output (scRGB inverse tone mapping) is only implemented on the Vulkan backend.
}
@@ -55,6 +55,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
], scalingResourceLayout);
}
public bool IsResolutionSupported(int srcWidth, int srcHeight, int dstWidth, int dstHeight) => true;
public void Run(
TextureView view,
CommandBufferScoped cbs,
@@ -69,7 +71,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
float peak = 12.5f,
float curve = 4.0f,
float gamma = 2.2f,
float blend = 0.5f)
float blend = 0.5f,
float whiten = 0.0f)
{
_pipeline.SetCommandBuffer(cbs);
_pipeline.SetProgram(_scalingProgram);
@@ -87,6 +87,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
], sharpeningResourceLayout);
}
public bool IsResolutionSupported(int srcWidth, int srcHeight, int dstWidth, int dstHeight) => true;
public void Run(
TextureView view,
CommandBufferScoped cbs,
@@ -101,7 +103,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
float peak = 12.5f,
float curve = 4.0f,
float gamma = 2.2f,
float blend = 0.5f)
float blend = 0.5f,
float whiten = 0.0f)
{
if (_intermediaryTexture == null
|| _intermediaryTexture.Info.Width != width
@@ -159,14 +162,15 @@ namespace Ryujinx.Graphics.Vulkan.Effects
// Non-HDR: [sharpening]. HDR: [sharpening, paperWhite, peak, curve, gamma, blend] (the HDR sharpening
// shader reads the extra floats from the same uniform block to tone-map to scRGB).
int sharpeningCount = hdr ? 6 : 1;
Span<float> sharpeningBufferData = stackalloc float[6];
int sharpeningCount = hdr ? 7 : 1;
Span<float> sharpeningBufferData = stackalloc float[7];
sharpeningBufferData[0] = 1.5f - (Level * 0.01f * 1.5f);
sharpeningBufferData[1] = paperWhite;
sharpeningBufferData[2] = peak;
sharpeningBufferData[3] = curve;
sharpeningBufferData[4] = gamma;
sharpeningBufferData[5] = blend;
sharpeningBufferData[6] = whiten;
using ScopedTemporaryBuffer sharpeningBuffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, sharpeningCount * sizeof(float));
sharpeningBuffer.Holder.SetDataUnchecked(sharpeningBuffer.Offset, sharpeningBufferData[..sharpeningCount]);
@@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
internal interface IScalingFilter : IDisposable
{
float Level { get; set; }
bool IsResolutionSupported(int srcWidth, int srcHeight, int dstWidth, int dstHeight);
void Run(
TextureView view,
CommandBufferScoped cbs,
@@ -21,6 +22,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
float peak = 12.5f,
float curve = 4.0f,
float gamma = 2.2f,
float blend = 0.5f);
float blend = 0.5f,
float whiten = 0.0f);
}
}
@@ -0,0 +1,231 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
using Silk.NET.Vulkan;
using System;
using Extent2D = Ryujinx.Graphics.GAL.Extents2D;
using Format = Silk.NET.Vulkan.Format;
using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
namespace Ryujinx.Graphics.Vulkan.Effects
{
/// <summary>
/// NVIDIA Image Scaling (NIS) upscaler. Wraps the MIT-licensed NIS NVScaler compute
/// shader (see Effects/Shaders/NisScaler.h, nis_coef.glsl, NisScaling.glsl).
/// NVScaler is only defined for upscale ratios in [1x, 2x]; outside that the input
/// is simply stretched without the NIS quality benefit. SDR (rgba8) path for now;
/// an scRGB/HDR variant is a follow-up.
/// </summary>
internal class NisScalingFilter : IScalingFilter
{
// NIS shader block dimensions (must match NisScaling.glsl: NIS_BLOCK_WIDTH/HEIGHT).
private const int BlockWidth = 32;
private const int BlockHeight = 24;
private const int ConfigFloats = 30; // NISConfig: 28 named values + 2 reserved.
private readonly VulkanRenderer _renderer;
private PipelineHelperShader _pipeline;
private ISampler _sampler;
private ShaderCollection _scalingProgram;
private ShaderCollection _scalingProgramHdr;
private Device _device;
// Slider value (0..100). Mapped to NIS sharpness (0..1) in Run. 50 = neutral.
public float Level { get; set; } = 50f;
public NisScalingFilter(VulkanRenderer renderer, Device device)
{
_device = device;
_renderer = renderer;
Initialize();
}
public void Dispose()
{
_pipeline.Dispose();
_scalingProgram.Dispose();
_scalingProgramHdr.Dispose();
_sampler.Dispose();
}
public void Initialize()
{
_pipeline = new PipelineHelperShader(_renderer, _device);
_pipeline.Initialize();
byte[] scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/NisScaling.spv");
// Same layout as the other scaling filters: config UBO (2), input+sampler (1), output image (0/set 3).
ResourceLayout scalingResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
_scalingProgram = _renderer.CreateProgramWithMinimalLayout([
new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv)
], scalingResourceLayout);
byte[] scalingShaderHdr = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/NisScalingHdr.spv");
// HDR variant adds the tone-map params UBO at binding 3 and outputs to rgba16f (scRGB).
ResourceLayout scalingResourceLayoutHdr = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_scalingProgramHdr = _renderer.CreateProgramWithMinimalLayout([
new ShaderSource(scalingShaderHdr, ShaderStage.Compute, TargetLanguage.Spirv)
], scalingResourceLayoutHdr);
}
// NIS NVScaler is only defined for upscale ratios in [1x, 2x] (kScale in [0.5, 1.0]).
// Outside that range - notably when the internal resolution is at/above the output, as
// with high-res mods or texture packs - its block tile cache reads out of bounds and
// corrupts the image. Report unsupported so Window.Present falls back to a normal blit.
public bool IsResolutionSupported(int srcWidth, int srcHeight, int dstWidth, int dstHeight)
{
float scaleX = (float)srcWidth / dstWidth;
float scaleY = (float)srcHeight / dstHeight;
return scaleX >= 0.5f && scaleX <= 1.0f && scaleY >= 0.5f && scaleY <= 1.0f;
}
public void Run(
TextureView view,
CommandBufferScoped cbs,
Auto<DisposableImageView> destinationTexture,
Format format,
int width,
int height,
Extent2D source,
Extent2D destination,
bool hdr = false,
float paperWhite = 2.5f,
float peak = 12.5f,
float curve = 4.0f,
float gamma = 2.2f,
float blend = 0.5f,
float whiten = 0.0f)
{
_pipeline.SetCommandBuffer(cbs);
_pipeline.SetProgram(hdr ? _scalingProgramHdr : _scalingProgram);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _sampler);
// v1: stretch the full source frame to the full output (swapchain) surface.
// Letterboxing/flip via source/destination extents is a follow-up.
uint inW = (uint)view.Width;
uint inH = (uint)view.Height;
uint outW = (uint)width;
uint outH = (uint)height;
float sharpness = Math.Clamp(Level * 0.01f, 0f, 1f);
Span<float> cb = stackalloc float[ConfigFloats];
BuildConfig(cb, sharpness, inW, inH, outW, outH);
int rangeSize = ConfigFloats * sizeof(float);
using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, rangeSize);
buffer.Holder.SetDataUnchecked(buffer.Offset, cb);
// HDR tone-map params (paperWhite, peak, curve, gamma, blend, whiten), matching NisScalingHdr.glsl.
Span<float> hdrData = stackalloc float[] { paperWhite, peak, curve, gamma, blend, whiten };
using ScopedTemporaryBuffer hdrBuffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, hdrData.Length * sizeof(float));
hdrBuffer.Holder.SetDataUnchecked(hdrBuffer.Offset, hdrData);
if (hdr)
{
_pipeline.SetUniformBuffers([new BufferAssignment(2, buffer.Range), new BufferAssignment(3, hdrBuffer.Range)]);
}
else
{
_pipeline.SetUniformBuffers([new BufferAssignment(2, buffer.Range)]);
}
_pipeline.SetImage(0, destinationTexture);
int dispatchX = ((int)outW + (BlockWidth - 1)) / BlockWidth;
int dispatchY = ((int)outH + (BlockHeight - 1)) / BlockHeight;
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier();
_pipeline.Finish();
}
// Port of NVScalerUpdateConfig (NIS_Config.h, MIT) for the SDR path. Fills the
// NISConfig constant buffer in the exact field order expected by NisScaling.glsl.
private static void BuildConfig(Span<float> cb, float sharpness, uint inW, uint inH, uint outW, uint outH)
{
sharpness = Math.Clamp(sharpness, 0f, 1f);
float sharpenSlider = sharpness - 0.5f; // map 0..1 to -0.5..+0.5
float maxScale = sharpenSlider >= 0.0f ? 1.25f : 1.75f;
float minScale = sharpenSlider >= 0.0f ? 1.25f : 1.0f;
float limitScale = sharpenSlider >= 0.0f ? 1.25f : 1.0f;
float kDetectRatio = 2f * 1127f / 1024f;
// SDR params (HDR variant handled separately later).
float kDetectThres = 64f / 1024f;
float kMinContrastRatio = 2.0f;
float kMaxContrastRatio = 10.0f;
float kSharpStartY = 0.45f;
float kSharpEndY = 0.9f;
float kSharpStrengthMin = MathF.Max(0.0f, 0.4f + sharpenSlider * minScale * 1.2f);
float kSharpStrengthMax = 1.6f + sharpenSlider * maxScale * 1.8f;
float kSharpLimitMin = MathF.Max(0.1f, 0.14f + sharpenSlider * limitScale * 0.32f);
float kSharpLimitMax = 0.5f + sharpenSlider * limitScale * 0.6f;
float kRatioNorm = 1.0f / (kMaxContrastRatio - kMinContrastRatio);
float kSharpScaleY = 1.0f / (kSharpEndY - kSharpStartY);
float kSharpStrengthScale = kSharpStrengthMax - kSharpStrengthMin;
float kSharpLimitScale = kSharpLimitMax - kSharpLimitMin;
float kSrcNormX = 1f / inW;
float kSrcNormY = 1f / inH;
float kDstNormX = 1f / outW;
float kDstNormY = 1f / outH;
float kScaleX = inW / (float)outW;
float kScaleY = inH / (float)outH;
int i = 0;
cb[i++] = kDetectRatio;
cb[i++] = kDetectThres;
cb[i++] = kMinContrastRatio;
cb[i++] = kRatioNorm;
cb[i++] = 1.0f; // kContrastBoost
cb[i++] = 1.0f / 255.0f; // kEps
cb[i++] = kSharpStartY;
cb[i++] = kSharpScaleY;
cb[i++] = kSharpStrengthMin;
cb[i++] = kSharpStrengthScale;
cb[i++] = kSharpLimitMin;
cb[i++] = kSharpLimitScale;
cb[i++] = kScaleX;
cb[i++] = kScaleY;
cb[i++] = kDstNormX;
cb[i++] = kDstNormY;
cb[i++] = kSrcNormX;
cb[i++] = kSrcNormY;
cb[i++] = AsFloat(0); // kInputViewportOriginX
cb[i++] = AsFloat(0); // kInputViewportOriginY
cb[i++] = AsFloat(inW); // kInputViewportWidth
cb[i++] = AsFloat(inH); // kInputViewportHeight
cb[i++] = AsFloat(0); // kOutputViewportOriginX
cb[i++] = AsFloat(0); // kOutputViewportOriginY
cb[i++] = AsFloat(outW); // kOutputViewportWidth
cb[i++] = AsFloat(outH); // kOutputViewportHeight
cb[i++] = 0f; // reserved0
cb[i++] = 0f; // reserved1
}
// Reinterpret a uint's bits as a float so the UBO's uint members get the right bytes.
private static float AsFloat(uint value) => BitConverter.Int32BitsToSingle((int)value);
}
}
@@ -24,6 +24,7 @@ layout( binding = 4 ) uniform sharpening
float curve_data;
float gamma_data;
float blend_data;
float whiten_data;
#endif
};
@@ -3916,8 +3917,14 @@ vec3 sdrToHdr(vec3 srgb)
float luma = dot(lin, vec3(0.2126, 0.7152, 0.0722));
float maxc = max(max(lin.r, lin.g), lin.b);
float hl = mix(luma, maxc, blend_data);
float boost = mix(1.0, peak / paperWhite, pow(clamp(hl, 0.0, 1.0), curve));
return lin * paperWhite * boost;
float hlAmount = pow(clamp(hl, 0.0, 1.0), curve);
float boost = mix(1.0, peak / paperWhite, hlAmount);
vec3 outc = lin * paperWhite * boost;
// Desaturate the most extreme highlights toward white (same change as the plain blit path):
// a blinding light reads as white, not a saturated colour. Luminance-preserving.
float t = whiten_data * hlAmount;
float lumaOut = dot(outc, vec3(0.2126, 0.7152, 0.0722));
return mix(outc, vec3(lumaOut), t);
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,88 @@
// The MIT License (MIT)
//
// Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// NVIDIA Image Scaling (NIS) v1.0.3 - MIT License, (c) 2022 NVIDIA CORPORATION.
// Vulkan compute wrapper adapted for Ryujinx: combined samplers + inline coefficient
// tables (see NisScaler.h macro edits and nis_coef.glsl). Output path: SDR (rgba8).
#version 450 core
#extension GL_GOOGLE_include_directive : require
#define NIS_GLSL 1
#define NIS_SCALER 1
#define NIS_THREAD_GROUP_SIZE 256
#define NIS_BLOCK_WIDTH 32
#define NIS_BLOCK_HEIGHT 24
layout(local_size_x = NIS_THREAD_GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
// NISConfig constant buffer (exact field order; filled host-side by NisScalingFilter.cs).
layout(binding = 2) uniform cb
{
float kDetectRatio;
float kDetectThres;
float kMinContrastRatio;
float kRatioNorm;
float kContrastBoost;
float kEps;
float kSharpStartY;
float kSharpScaleY;
float kSharpStrengthMin;
float kSharpStrengthScale;
float kSharpLimitMin;
float kSharpLimitScale;
float kScaleX;
float kScaleY;
float kDstNormX;
float kDstNormY;
float kSrcNormX;
float kSrcNormY;
uint kInputViewportOriginX;
uint kInputViewportOriginY;
uint kInputViewportWidth;
uint kInputViewportHeight;
uint kOutputViewportOriginX;
uint kOutputViewportOriginY;
uint kOutputViewportWidth;
uint kOutputViewportHeight;
float reserved0;
float reserved1;
};
layout(binding = 1, set = 2) uniform sampler2D in_texture;
layout(rgba8, binding = 0, set = 3) uniform image2D out_texture;
// coef_scaler[64][2] and coef_usm[64][2] (vec4 = taps0-3 / taps4-7), MIT, verbatim values.
#include "nis_coef.glsl"
#include "NisScaler.h"
void main()
{
NVScaler(uvec2(gl_WorkGroupID.xy), gl_LocalInvocationID.x);
}
@@ -0,0 +1,125 @@
// The MIT License (MIT)
//
// Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// NVIDIA Image Scaling (NIS) v1.0.3 - HDR variant for Ryujinx. Same as NisScaling.glsl
// but outputs to an scRGB (rgba16f) swapchain: NIS runs in SDR space, then the result is
// inverse-tone-mapped to scRGB via NIS_OUTPUT_TRANSFORM. The tonemap below matches the
// fork's existing HDR path (FsrSharpeningHdr) exactly.
#version 450 core
#extension GL_GOOGLE_include_directive : require
#define NIS_GLSL 1
#define NIS_SCALER 1
#define NIS_THREAD_GROUP_SIZE 256
#define NIS_BLOCK_WIDTH 32
#define NIS_BLOCK_HEIGHT 24
layout(local_size_x = NIS_THREAD_GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
layout(binding = 2) uniform cb
{
float kDetectRatio;
float kDetectThres;
float kMinContrastRatio;
float kRatioNorm;
float kContrastBoost;
float kEps;
float kSharpStartY;
float kSharpScaleY;
float kSharpStrengthMin;
float kSharpStrengthScale;
float kSharpLimitMin;
float kSharpLimitScale;
float kScaleX;
float kScaleY;
float kDstNormX;
float kDstNormY;
float kSrcNormX;
float kSrcNormY;
uint kInputViewportOriginX;
uint kInputViewportOriginY;
uint kInputViewportWidth;
uint kInputViewportHeight;
uint kOutputViewportOriginX;
uint kOutputViewportOriginY;
uint kOutputViewportWidth;
uint kOutputViewportHeight;
float reserved0;
float reserved1;
};
layout(binding = 1, set = 2) uniform sampler2D in_texture;
layout(rgba16f, binding = 0, set = 3) uniform image2D out_texture;
// HDR tone-map parameters (filled host-side; same values the other filters receive).
layout(binding = 3, std140) uniform hdrParams
{
float hdrPaperWhite;
float hdrPeak;
float hdrCurve;
float hdrGamma;
float hdrBlend;
float hdrWhiten;
};
#include "nis_coef.glsl"
// SDR -> scRGB inverse tone map. Ported verbatim from the fork's FsrSharpeningHdr path.
vec3 sdrToLinear(vec3 c, float g)
{
return pow(max(c, vec3(0.0)), vec3(g));
}
vec3 sdrToHdr(vec3 srgb)
{
vec3 lin = sdrToLinear(srgb, hdrGamma);
float luma = dot(lin, vec3(0.2126, 0.7152, 0.0722));
float maxc = max(max(lin.x, lin.y), lin.z);
float hl = mix(luma, maxc, hdrBlend);
float hlAmount = pow(clamp(hl, 0.0, 1.0), hdrCurve);
float boost = mix(1.0, hdrPeak / hdrPaperWhite, hlAmount);
vec3 outc = (lin * hdrPaperWhite) * boost;
float t = hdrWhiten * hlAmount;
float lumaOut = dot(outc, vec3(0.2126, 0.7152, 0.0722));
return mix(outc, vec3(lumaOut), vec3(t));
}
vec4 NisStore(vec4 c)
{
return vec4(sdrToHdr(c.rgb), c.a);
}
#define NIS_OUTPUT_TRANSFORM(c) NisStore(c)
#include "NisScaler.h"
void main()
{
NVScaler(uvec2(gl_WorkGroupID.xy), gl_LocalInvocationID.x);
}
@@ -0,0 +1,157 @@
// The MIT License (MIT)
//
// Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// NIS coefficient tables, extracted verbatim from NVIDIA NIS_Config.h (v1.0.3, MIT).
// Layout: [phase 0..63][col 0=taps0-3, 1=taps4-7] matching texelFetch(coef, ivec2(vIdx,phase)).
const vec4 coef_scaler[64][2] = vec4[64][2](
vec4[2](vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 0.0)),
vec4[2](vec4(0.0029, -0.0127, 1.0, 0.0132), vec4(-0.0034, 0.0, 0.0, 0.0)),
vec4[2](vec4(0.0063, -0.0249, 0.9985, 0.0269), vec4(-0.0068, 0.0, 0.0, 0.0)),
vec4[2](vec4(0.0088, -0.0361, 0.9956, 0.0415), vec4(-0.0103, 0.0005, 0.0, 0.0)),
vec4[2](vec4(0.0117, -0.0474, 0.9932, 0.0562), vec4(-0.0142, 0.0005, 0.0, 0.0)),
vec4[2](vec4(0.0142, -0.0576, 0.9897, 0.0713), vec4(-0.0181, 0.0005, 0.0, 0.0)),
vec4[2](vec4(0.0166, -0.0674, 0.9844, 0.0874), vec4(-0.022, 0.001, 0.0, 0.0)),
vec4[2](vec4(0.0186, -0.0762, 0.9785, 0.104), vec4(-0.0264, 0.0015, 0.0, 0.0)),
vec4[2](vec4(0.0205, -0.085, 0.9727, 0.1206), vec4(-0.0308, 0.002, 0.0, 0.0)),
vec4[2](vec4(0.0225, -0.0928, 0.9648, 0.1382), vec4(-0.0352, 0.0024, 0.0, 0.0)),
vec4[2](vec4(0.0239, -0.1006, 0.9575, 0.1558), vec4(-0.0396, 0.0029, 0.0, 0.0)),
vec4[2](vec4(0.0254, -0.1074, 0.9487, 0.1738), vec4(-0.0439, 0.0034, 0.0, 0.0)),
vec4[2](vec4(0.0264, -0.1138, 0.939, 0.1929), vec4(-0.0488, 0.0044, 0.0, 0.0)),
vec4[2](vec4(0.0278, -0.1191, 0.9282, 0.2119), vec4(-0.0537, 0.0049, 0.0, 0.0)),
vec4[2](vec4(0.0288, -0.1245, 0.917, 0.231), vec4(-0.0581, 0.0059, 0.0, 0.0)),
vec4[2](vec4(0.0293, -0.1294, 0.9058, 0.251), vec4(-0.063, 0.0063, 0.0, 0.0)),
vec4[2](vec4(0.0303, -0.1333, 0.8926, 0.271), vec4(-0.0679, 0.0073, 0.0, 0.0)),
vec4[2](vec4(0.0308, -0.1367, 0.8789, 0.2915), vec4(-0.0728, 0.0083, 0.0, 0.0)),
vec4[2](vec4(0.0308, -0.1401, 0.8657, 0.312), vec4(-0.0776, 0.0093, 0.0, 0.0)),
vec4[2](vec4(0.0313, -0.1426, 0.8506, 0.333), vec4(-0.0825, 0.0103, 0.0, 0.0)),
vec4[2](vec4(0.0313, -0.1445, 0.8354, 0.354), vec4(-0.0874, 0.0112, 0.0, 0.0)),
vec4[2](vec4(0.0313, -0.146, 0.8193, 0.3755), vec4(-0.0923, 0.0122, 0.0, 0.0)),
vec4[2](vec4(0.0313, -0.147, 0.8022, 0.3965), vec4(-0.0967, 0.0137, 0.0, 0.0)),
vec4[2](vec4(0.0308, -0.1479, 0.7856, 0.4185), vec4(-0.1016, 0.0146, 0.0, 0.0)),
vec4[2](vec4(0.0303, -0.1479, 0.7681, 0.4399), vec4(-0.106, 0.0156, 0.0, 0.0)),
vec4[2](vec4(0.0298, -0.1479, 0.7505, 0.4614), vec4(-0.1104, 0.0166, 0.0, 0.0)),
vec4[2](vec4(0.0293, -0.147, 0.7314, 0.4829), vec4(-0.1147, 0.0181, 0.0, 0.0)),
vec4[2](vec4(0.0288, -0.146, 0.7119, 0.5049), vec4(-0.1187, 0.019, 0.0, 0.0)),
vec4[2](vec4(0.0278, -0.1445, 0.6929, 0.5264), vec4(-0.1226, 0.02, 0.0, 0.0)),
vec4[2](vec4(0.0273, -0.1431, 0.6724, 0.5479), vec4(-0.126, 0.0215, 0.0, 0.0)),
vec4[2](vec4(0.0264, -0.1411, 0.6528, 0.5693), vec4(-0.1299, 0.0225, 0.0, 0.0)),
vec4[2](vec4(0.0254, -0.1387, 0.6323, 0.5903), vec4(-0.1328, 0.0234, 0.0, 0.0)),
vec4[2](vec4(0.0244, -0.1357, 0.6113, 0.6113), vec4(-0.1357, 0.0244, 0.0, 0.0)),
vec4[2](vec4(0.0234, -0.1328, 0.5903, 0.6323), vec4(-0.1387, 0.0254, 0.0, 0.0)),
vec4[2](vec4(0.0225, -0.1299, 0.5693, 0.6528), vec4(-0.1411, 0.0264, 0.0, 0.0)),
vec4[2](vec4(0.0215, -0.126, 0.5479, 0.6724), vec4(-0.1431, 0.0273, 0.0, 0.0)),
vec4[2](vec4(0.02, -0.1226, 0.5264, 0.6929), vec4(-0.1445, 0.0278, 0.0, 0.0)),
vec4[2](vec4(0.019, -0.1187, 0.5049, 0.7119), vec4(-0.146, 0.0288, 0.0, 0.0)),
vec4[2](vec4(0.0181, -0.1147, 0.4829, 0.7314), vec4(-0.147, 0.0293, 0.0, 0.0)),
vec4[2](vec4(0.0166, -0.1104, 0.4614, 0.7505), vec4(-0.1479, 0.0298, 0.0, 0.0)),
vec4[2](vec4(0.0156, -0.106, 0.4399, 0.7681), vec4(-0.1479, 0.0303, 0.0, 0.0)),
vec4[2](vec4(0.0146, -0.1016, 0.4185, 0.7856), vec4(-0.1479, 0.0308, 0.0, 0.0)),
vec4[2](vec4(0.0137, -0.0967, 0.3965, 0.8022), vec4(-0.147, 0.0313, 0.0, 0.0)),
vec4[2](vec4(0.0122, -0.0923, 0.3755, 0.8193), vec4(-0.146, 0.0313, 0.0, 0.0)),
vec4[2](vec4(0.0112, -0.0874, 0.354, 0.8354), vec4(-0.1445, 0.0313, 0.0, 0.0)),
vec4[2](vec4(0.0103, -0.0825, 0.333, 0.8506), vec4(-0.1426, 0.0313, 0.0, 0.0)),
vec4[2](vec4(0.0093, -0.0776, 0.312, 0.8657), vec4(-0.1401, 0.0308, 0.0, 0.0)),
vec4[2](vec4(0.0083, -0.0728, 0.2915, 0.8789), vec4(-0.1367, 0.0308, 0.0, 0.0)),
vec4[2](vec4(0.0073, -0.0679, 0.271, 0.8926), vec4(-0.1333, 0.0303, 0.0, 0.0)),
vec4[2](vec4(0.0063, -0.063, 0.251, 0.9058), vec4(-0.1294, 0.0293, 0.0, 0.0)),
vec4[2](vec4(0.0059, -0.0581, 0.231, 0.917), vec4(-0.1245, 0.0288, 0.0, 0.0)),
vec4[2](vec4(0.0049, -0.0537, 0.2119, 0.9282), vec4(-0.1191, 0.0278, 0.0, 0.0)),
vec4[2](vec4(0.0044, -0.0488, 0.1929, 0.939), vec4(-0.1138, 0.0264, 0.0, 0.0)),
vec4[2](vec4(0.0034, -0.0439, 0.1738, 0.9487), vec4(-0.1074, 0.0254, 0.0, 0.0)),
vec4[2](vec4(0.0029, -0.0396, 0.1558, 0.9575), vec4(-0.1006, 0.0239, 0.0, 0.0)),
vec4[2](vec4(0.0024, -0.0352, 0.1382, 0.9648), vec4(-0.0928, 0.0225, 0.0, 0.0)),
vec4[2](vec4(0.002, -0.0308, 0.1206, 0.9727), vec4(-0.085, 0.0205, 0.0, 0.0)),
vec4[2](vec4(0.0015, -0.0264, 0.104, 0.9785), vec4(-0.0762, 0.0186, 0.0, 0.0)),
vec4[2](vec4(0.001, -0.022, 0.0874, 0.9844), vec4(-0.0674, 0.0166, 0.0, 0.0)),
vec4[2](vec4(0.0005, -0.0181, 0.0713, 0.9897), vec4(-0.0576, 0.0142, 0.0, 0.0)),
vec4[2](vec4(0.0005, -0.0142, 0.0562, 0.9932), vec4(-0.0474, 0.0117, 0.0, 0.0)),
vec4[2](vec4(0.0005, -0.0103, 0.0415, 0.9956), vec4(-0.0361, 0.0088, 0.0, 0.0)),
vec4[2](vec4(0.0, -0.0068, 0.0269, 0.9985), vec4(-0.0249, 0.0063, 0.0, 0.0)),
vec4[2](vec4(0.0, -0.0034, 0.0132, 1.0), vec4(-0.0127, 0.0029, 0.0, 0.0))
);
const vec4 coef_usm[64][2] = vec4[64][2](
vec4[2](vec4(0.0, -0.6001, 1.2002, -0.6001), vec4(0.0, 0.0, 0.0, 0.0)),
vec4[2](vec4(0.0029, -0.6084, 1.1987, -0.5903), vec4(-0.0029, 0.0, 0.0, 0.0)),
vec4[2](vec4(0.0049, -0.6147, 1.1958, -0.5791), vec4(-0.0068, 0.0005, 0.0, 0.0)),
vec4[2](vec4(0.0073, -0.6196, 1.189, -0.5659), vec4(-0.0103, 0.0, 0.0, 0.0)),
vec4[2](vec4(0.0093, -0.6235, 1.1802, -0.5513), vec4(-0.0151, 0.0, 0.0, 0.0)),
vec4[2](vec4(0.0112, -0.6265, 1.1699, -0.5352), vec4(-0.0195, 0.0005, 0.0, 0.0)),
vec4[2](vec4(0.0122, -0.627, 1.1582, -0.5181), vec4(-0.0259, 0.0005, 0.0, 0.0)),
vec4[2](vec4(0.0142, -0.6284, 1.1455, -0.5005), vec4(-0.0317, 0.0005, 0.0, 0.0)),
vec4[2](vec4(0.0156, -0.6265, 1.1274, -0.479), vec4(-0.0386, 0.0005, 0.0, 0.0)),
vec4[2](vec4(0.0166, -0.6235, 1.1089, -0.457), vec4(-0.0454, 0.001, 0.0, 0.0)),
vec4[2](vec4(0.0176, -0.6187, 1.0879, -0.4346), vec4(-0.0532, 0.001, 0.0, 0.0)),
vec4[2](vec4(0.0181, -0.6138, 1.0659, -0.4102), vec4(-0.0615, 0.0015, 0.0, 0.0)),
vec4[2](vec4(0.019, -0.6069, 1.0405, -0.3843), vec4(-0.0698, 0.0015, 0.0, 0.0)),
vec4[2](vec4(0.0195, -0.6006, 1.0161, -0.3574), vec4(-0.0796, 0.002, 0.0, 0.0)),
vec4[2](vec4(0.02, -0.5928, 0.9893, -0.3286), vec4(-0.0898, 0.0024, 0.0, 0.0)),
vec4[2](vec4(0.02, -0.582, 0.958, -0.2988), vec4(-0.1001, 0.0029, 0.0, 0.0)),
vec4[2](vec4(0.02, -0.5728, 0.9292, -0.269), vec4(-0.1104, 0.0034, 0.0, 0.0)),
vec4[2](vec4(0.02, -0.562, 0.8975, -0.2368), vec4(-0.1226, 0.0039, 0.0, 0.0)),
vec4[2](vec4(0.0205, -0.5498, 0.8643, -0.2046), vec4(-0.1343, 0.0044, 0.0, 0.0)),
vec4[2](vec4(0.02, -0.5371, 0.8301, -0.1709), vec4(-0.1465, 0.0049, 0.0, 0.0)),
vec4[2](vec4(0.0195, -0.5239, 0.7944, -0.1367), vec4(-0.1587, 0.0054, 0.0, 0.0)),
vec4[2](vec4(0.0195, -0.5107, 0.7598, -0.1021), vec4(-0.1724, 0.0059, 0.0, 0.0)),
vec4[2](vec4(0.019, -0.4966, 0.7231, -0.0649), vec4(-0.1865, 0.0063, 0.0, 0.0)),
vec4[2](vec4(0.0186, -0.4819, 0.6846, -0.0288), vec4(-0.1997, 0.0068, 0.0, 0.0)),
vec4[2](vec4(0.0186, -0.4668, 0.646, 0.0093), vec4(-0.2144, 0.0073, 0.0, 0.0)),
vec4[2](vec4(0.0176, -0.4507, 0.6055, 0.0479), vec4(-0.229, 0.0083, 0.0, 0.0)),
vec4[2](vec4(0.0171, -0.437, 0.5693, 0.0859), vec4(-0.2446, 0.0088, 0.0, 0.0)),
vec4[2](vec4(0.0161, -0.4199, 0.5283, 0.1255), vec4(-0.2598, 0.0098, 0.0, 0.0)),
vec4[2](vec4(0.0161, -0.4048, 0.4883, 0.1655), vec4(-0.2754, 0.0103, 0.0, 0.0)),
vec4[2](vec4(0.0151, -0.3887, 0.4497, 0.2041), vec4(-0.291, 0.0107, 0.0, 0.0)),
vec4[2](vec4(0.0142, -0.3711, 0.4072, 0.2446), vec4(-0.3066, 0.0117, 0.0, 0.0)),
vec4[2](vec4(0.0137, -0.3555, 0.3672, 0.2852), vec4(-0.3228, 0.0122, 0.0, 0.0)),
vec4[2](vec4(0.0132, -0.3394, 0.3262, 0.3262), vec4(-0.3394, 0.0132, 0.0, 0.0)),
vec4[2](vec4(0.0122, -0.3228, 0.2852, 0.3672), vec4(-0.3555, 0.0137, 0.0, 0.0)),
vec4[2](vec4(0.0117, -0.3066, 0.2446, 0.4072), vec4(-0.3711, 0.0142, 0.0, 0.0)),
vec4[2](vec4(0.0107, -0.291, 0.2041, 0.4497), vec4(-0.3887, 0.0151, 0.0, 0.0)),
vec4[2](vec4(0.0103, -0.2754, 0.1655, 0.4883), vec4(-0.4048, 0.0161, 0.0, 0.0)),
vec4[2](vec4(0.0098, -0.2598, 0.1255, 0.5283), vec4(-0.4199, 0.0161, 0.0, 0.0)),
vec4[2](vec4(0.0088, -0.2446, 0.0859, 0.5693), vec4(-0.437, 0.0171, 0.0, 0.0)),
vec4[2](vec4(0.0083, -0.229, 0.0479, 0.6055), vec4(-0.4507, 0.0176, 0.0, 0.0)),
vec4[2](vec4(0.0073, -0.2144, 0.0093, 0.646), vec4(-0.4668, 0.0186, 0.0, 0.0)),
vec4[2](vec4(0.0068, -0.1997, -0.0288, 0.6846), vec4(-0.4819, 0.0186, 0.0, 0.0)),
vec4[2](vec4(0.0063, -0.1865, -0.0649, 0.7231), vec4(-0.4966, 0.019, 0.0, 0.0)),
vec4[2](vec4(0.0059, -0.1724, -0.1021, 0.7598), vec4(-0.5107, 0.0195, 0.0, 0.0)),
vec4[2](vec4(0.0054, -0.1587, -0.1367, 0.7944), vec4(-0.5239, 0.0195, 0.0, 0.0)),
vec4[2](vec4(0.0049, -0.1465, -0.1709, 0.8301), vec4(-0.5371, 0.02, 0.0, 0.0)),
vec4[2](vec4(0.0044, -0.1343, -0.2046, 0.8643), vec4(-0.5498, 0.0205, 0.0, 0.0)),
vec4[2](vec4(0.0039, -0.1226, -0.2368, 0.8975), vec4(-0.562, 0.02, 0.0, 0.0)),
vec4[2](vec4(0.0034, -0.1104, -0.269, 0.9292), vec4(-0.5728, 0.02, 0.0, 0.0)),
vec4[2](vec4(0.0029, -0.1001, -0.2988, 0.958), vec4(-0.582, 0.02, 0.0, 0.0)),
vec4[2](vec4(0.0024, -0.0898, -0.3286, 0.9893), vec4(-0.5928, 0.02, 0.0, 0.0)),
vec4[2](vec4(0.002, -0.0796, -0.3574, 1.0161), vec4(-0.6006, 0.0195, 0.0, 0.0)),
vec4[2](vec4(0.0015, -0.0698, -0.3843, 1.0405), vec4(-0.6069, 0.019, 0.0, 0.0)),
vec4[2](vec4(0.0015, -0.0615, -0.4102, 1.0659), vec4(-0.6138, 0.0181, 0.0, 0.0)),
vec4[2](vec4(0.001, -0.0532, -0.4346, 1.0879), vec4(-0.6187, 0.0176, 0.0, 0.0)),
vec4[2](vec4(0.001, -0.0454, -0.457, 1.1089), vec4(-0.6235, 0.0166, 0.0, 0.0)),
vec4[2](vec4(0.0005, -0.0386, -0.479, 1.1274), vec4(-0.6265, 0.0156, 0.0, 0.0)),
vec4[2](vec4(0.0005, -0.0317, -0.5005, 1.1455), vec4(-0.6284, 0.0142, 0.0, 0.0)),
vec4[2](vec4(0.0005, -0.0259, -0.5181, 1.1582), vec4(-0.627, 0.0122, 0.0, 0.0)),
vec4[2](vec4(0.0005, -0.0195, -0.5352, 1.1699), vec4(-0.6265, 0.0112, 0.0, 0.0)),
vec4[2](vec4(0.0, -0.0151, -0.5513, 1.1802), vec4(-0.6235, 0.0093, 0.0, 0.0)),
vec4[2](vec4(0.0, -0.0103, -0.5659, 1.189), vec4(-0.6196, 0.0073, 0.0, 0.0)),
vec4[2](vec4(0.0005, -0.0068, -0.5791, 1.1958), vec4(-0.6147, 0.0049, 0.0, 0.0)),
vec4[2](vec4(0.0, -0.0029, -0.5903, 1.1987), vec4(-0.6084, 0.0029, 0.0, 0.0))
);
+3 -1
View File
@@ -369,7 +369,8 @@ namespace Ryujinx.Graphics.Vulkan
float peak = 12.5f,
float curve = 4.0f,
float gamma = 2.2f,
float blend = 0.5f)
float blend = 0.5f,
float whiten = 0.0f)
{
_pipeline.SetCommandBuffer(cbs);
@@ -412,6 +413,7 @@ namespace Ryujinx.Graphics.Vulkan
hdrParams[2] = curve;
hdrParams[3] = gamma;
hdrParams[4] = blend;
hdrParams[5] = whiten;
hdrBuffer.Holder.SetDataUnchecked<float>(hdrBuffer.Offset, hdrParams);
_pipeline.SetUniformBuffers([new BufferAssignment(1, buffer.Range), new BufferAssignment(2, hdrBuffer.Range)]);
@@ -16,6 +16,8 @@
<EmbeddedResource Include="Effects\Textures\SmaaAreaTexture.bin" />
<EmbeddedResource Include="Effects\Textures\SmaaSearchTexture.bin" />
<EmbeddedResource Include="Effects\Shaders\AreaScaling.spv" />
<EmbeddedResource Include="Effects\Shaders\NisScaling.spv" />
<EmbeddedResource Include="Effects\Shaders\NisScalingHdr.spv" />
<EmbeddedResource Include="Effects\Shaders\FsrScaling.spv" />
<EmbeddedResource Include="Effects\Shaders\FsrSharpening.spv" />
<EmbeddedResource Include="Effects\Shaders\FsrSharpeningHdr.spv" />
@@ -8,6 +8,8 @@ layout (std140, binding = 2) uniform hdr_params
vec4 hdr_params_data;
// highlight colour mix: 0 = luma (calm/white highlights), 1 = max channel (coloured lights pop)
float hdr_blend_data;
// highlight whitening: 0 = keep colour (old look), 1 = fully desaturate extreme highlights to white
float hdr_whiten_data;
};
layout (location = 0) in vec2 tex_coord;
@@ -41,8 +43,15 @@ vec3 sdrToHdr(vec3 srgb)
float luma = dot(lin, vec3(0.2126, 0.7152, 0.0722));
float maxc = max(max(lin.r, lin.g), lin.b);
float hl = mix(luma, maxc, hdr_blend_data);
float boost = mix(1.0, peak / paperWhite, pow(clamp(hl, 0.0, 1.0), curve));
return lin * paperWhite * boost;
float hlAmount = pow(clamp(hl, 0.0, 1.0), curve);
float boost = mix(1.0, peak / paperWhite, hlAmount);
vec3 outc = lin * paperWhite * boost;
// Desaturate the most extreme highlights toward white: a blinding light (sun, fire, a bright lamp)
// physically reads as white, not a saturated colour. Luminance-preserving (mix toward a grey of
// equal brightness) so only the harsh hue at the very top is removed, not the brightness itself.
float t = hdr_whiten_data * hlAmount;
float lumaOut = dot(outc, vec3(0.2126, 0.7152, 0.0722));
return mix(outc, vec3(lumaOut), t);
}
void main()
+17 -4
View File
@@ -48,6 +48,7 @@ namespace Ryujinx.Graphics.Vulkan
private float _hdrCurve = 2.8f;
private float _hdrGamma = 2.2f;
private float _hdrBlend = 0.5f;
private float _hdrWhiten = 0.4f;
public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device)
{
@@ -450,7 +451,7 @@ namespace Ryujinx.Graphics.Vulkan
int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
if (_scalingFilter != null)
if (_scalingFilter != null && _scalingFilter.IsResolutionSupported(view.Width, view.Height, _width, _height))
{
_scalingFilter.Run(
view,
@@ -466,7 +467,8 @@ namespace Ryujinx.Graphics.Vulkan
_hdrPeak / 80f,
_hdrCurve,
_hdrGamma,
_hdrBlend
_hdrBlend,
_hdrWhiten
);
}
else
@@ -485,7 +487,8 @@ namespace Ryujinx.Graphics.Vulkan
_hdrPeak / 80f,
_hdrCurve,
_hdrGamma,
_hdrBlend);
_hdrBlend,
_hdrWhiten);
}
Transition(
@@ -620,6 +623,15 @@ namespace Ryujinx.Graphics.Vulkan
_scalingFilter = new AreaScalingFilter(_gd, _device);
}
break;
case ScalingFilter.Nis:
if (_scalingFilter is not NisScalingFilter)
{
_scalingFilter?.Dispose();
_scalingFilter = new NisScalingFilter(_gd, _device);
}
_scalingFilter.Level = _scalingFilterLevel;
break;
}
}
@@ -687,13 +699,14 @@ namespace Ryujinx.Graphics.Vulkan
_swapchainIsDirty = true;
}
public override void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend)
public override void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend, float whiten)
{
_hdrPaperWhite = paperWhite;
_hdrPeak = peak;
_hdrCurve = curve;
_hdrGamma = gamma;
_hdrBlend = blend;
_hdrWhiten = whiten;
if (_hdrEnabled != enabled)
{
+1 -1
View File
@@ -16,6 +16,6 @@ namespace Ryujinx.Graphics.Vulkan
public abstract void SetScalingFilter(ScalingFilter scalerType);
public abstract void SetScalingFilterLevel(float scale);
public abstract void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled);
public abstract void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend);
public abstract void SetHdrMode(bool enabled, float paperWhite, float peak, float curve, float gamma, float blend, float whiten);
}
}
+6 -2
View File
@@ -212,6 +212,7 @@ namespace Ryujinx.Ava.Systems
ConfigurationState.Instance.Graphics.HdrIntensity.Event += UpdateHdrLevel;
ConfigurationState.Instance.Graphics.HdrGamma.Event += UpdateHdrLevel;
ConfigurationState.Instance.Graphics.HdrHighlightMix.Event += UpdateHdrLevel;
ConfigurationState.Instance.Graphics.HdrHighlightWhiten.Event += UpdateHdrLevel;
ConfigurationState.Instance.Graphics.VSyncMode.Event += UpdateVSyncMode;
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Event += UpdateCustomVSyncIntervalValue;
ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Event += UpdateCustomVSyncIntervalEnabled;
@@ -323,7 +324,8 @@ namespace Ryujinx.Ava.Systems
ConfigurationState.Instance.Graphics.HdrPeakBrightness,
HdrIntensityToCurve(ConfigurationState.Instance.Graphics.HdrIntensity),
ConfigurationState.Instance.Graphics.HdrGamma / 100f,
ConfigurationState.Instance.Graphics.HdrHighlightMix / 100f);
ConfigurationState.Instance.Graphics.HdrHighlightMix / 100f,
ConfigurationState.Instance.Graphics.HdrHighlightWhiten / 100f);
}
// Maps the user-facing HDR intensity (0-100, higher = more pop) to the highlight curve
@@ -695,6 +697,7 @@ namespace Ryujinx.Ava.Systems
ConfigurationState.Instance.Graphics.HdrIntensity.Event -= UpdateHdrLevel;
ConfigurationState.Instance.Graphics.HdrGamma.Event -= UpdateHdrLevel;
ConfigurationState.Instance.Graphics.HdrHighlightMix.Event -= UpdateHdrLevel;
ConfigurationState.Instance.Graphics.HdrHighlightWhiten.Event -= UpdateHdrLevel;
_topLevel.PointerMoved -= TopLevel_PointerEnteredOrMoved;
_topLevel.PointerEntered -= TopLevel_PointerEnteredOrMoved;
@@ -1156,7 +1159,8 @@ namespace Ryujinx.Ava.Systems
ConfigurationState.Instance.Graphics.HdrPeakBrightness,
HdrIntensityToCurve(ConfigurationState.Instance.Graphics.HdrIntensity),
ConfigurationState.Instance.Graphics.HdrGamma / 100f,
ConfigurationState.Instance.Graphics.HdrHighlightMix / 100f);
ConfigurationState.Instance.Graphics.HdrHighlightMix / 100f,
ConfigurationState.Instance.Graphics.HdrHighlightWhiten / 100f);
Width = (int)RendererHost.Bounds.Width;
Height = (int)RendererHost.Bounds.Height;
@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Systems.Configuration
/// <summary>
/// The current version of the file format
/// </summary>
public const int CurrentVersion = 77;
public const int CurrentVersion = 78;
/// <summary>
/// Version of the configuration file format
@@ -99,6 +99,11 @@ namespace Ryujinx.Ava.Systems.Configuration
/// </summary>
public int HdrHighlightMix { get; set; }
/// <summary>
/// HDR highlight whitening (0-100): desaturate the brightest highlights toward white. 0 = old look (keep colour).
/// </summary>
public int HdrHighlightWhiten { get; set; }
/// <summary>
/// Dumps shaders in this local directory
/// </summary>
@@ -91,6 +91,7 @@ namespace Ryujinx.Ava.Systems.Configuration
Graphics.HdrIntensity.Value = cff.HdrIntensity;
Graphics.HdrGamma.Value = cff.HdrGamma;
Graphics.HdrHighlightMix.Value = cff.HdrHighlightMix;
Graphics.HdrHighlightWhiten.Value = cff.HdrHighlightWhiten;
Graphics.VSyncMode.Value = cff.VSyncMode;
Graphics.EnableCustomVSyncInterval.Value = cff.EnableCustomVSyncInterval;
Graphics.CustomVSyncInterval.Value = cff.CustomVSyncInterval;
@@ -554,7 +555,8 @@ namespace Ryujinx.Ava.Systems.Configuration
}),
(75, static cff => cff.HdrIntensity = 80),
(76, static cff => cff.HdrGamma = 220),
(77, static cff => cff.HdrHighlightMix = 50)
(77, static cff => cff.HdrHighlightMix = 50),
(78, static cff => cff.HdrHighlightWhiten = 0)
);
}
}
@@ -652,6 +652,12 @@ namespace Ryujinx.Ava.Systems.Configuration
/// </summary>
public ReactiveObject<int> HdrHighlightMix { get; private set; }
/// <summary>
/// HDR highlight whitening (0-100): desaturate the brightest highlights toward white.
/// 0 = keep full colour (old look), 100 = strong whitening of extreme highlights.
/// </summary>
public ReactiveObject<int> HdrHighlightWhiten { get; private set; }
/// <summary>
/// Preferred GPU
/// </summary>
@@ -706,6 +712,8 @@ namespace Ryujinx.Ava.Systems.Configuration
HdrGamma.LogChangesToValue(nameof(HdrGamma));
HdrHighlightMix = new ReactiveObject<int>();
HdrHighlightMix.LogChangesToValue(nameof(HdrHighlightMix));
HdrHighlightWhiten = new ReactiveObject<int>();
HdrHighlightWhiten.LogChangesToValue(nameof(HdrHighlightWhiten));
}
}
@@ -46,6 +46,7 @@ namespace Ryujinx.Ava.Systems.Configuration
HdrIntensity = Graphics.HdrIntensity,
HdrGamma = Graphics.HdrGamma,
HdrHighlightMix = Graphics.HdrHighlightMix,
HdrHighlightWhiten = Graphics.HdrHighlightWhiten,
GraphicsShadersDumpPath = Graphics.ShadersDumpPath,
LoggingEnableDebug = Logger.EnableDebug,
LoggingEnableStub = Logger.EnableStub,
@@ -220,6 +221,7 @@ namespace Ryujinx.Ava.Systems.Configuration
Graphics.HdrIntensity.Value = 80;
Graphics.HdrGamma.Value = 220;
Graphics.HdrHighlightMix.Value = 50;
Graphics.HdrHighlightWhiten.Value = 0;
System.EnablePtc.Value = true;
System.EnableInternetAccess.Value = false;
System.EnableFsIntegrityChecks.Value = true;
@@ -446,6 +446,19 @@ namespace Ryujinx.Ava.UI.ViewModels
public string HdrHighlightMixText => $"{HdrHighlightMix}";
public string HdrHighlightWhitenText => $"{HdrHighlightWhiten}";
public int HdrHighlightWhiten
{
get;
set
{
field = value;
OnPropertyChanged();
OnPropertyChanged(nameof(HdrHighlightWhitenText));
}
}
public int HdrHighlightMix
{
get;
@@ -850,6 +863,7 @@ namespace Ryujinx.Ava.UI.ViewModels
HdrIntensity = config.Graphics.HdrIntensity.Value;
HdrGamma = config.Graphics.HdrGamma.Value;
HdrHighlightMix = config.Graphics.HdrHighlightMix.Value;
HdrHighlightWhiten = config.Graphics.HdrHighlightWhiten.Value;
EnableMacroHLE = config.Graphics.EnableMacroHLE;
EnableColorSpacePassthrough = config.Graphics.EnableColorSpacePassthrough;
ResolutionScale = config.Graphics.ResScale == -1 ? 4 : config.Graphics.ResScale - 1;
@@ -986,6 +1000,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Graphics.HdrIntensity.Value = HdrIntensity;
config.Graphics.HdrGamma.Value = HdrGamma;
config.Graphics.HdrHighlightMix.Value = HdrHighlightMix;
config.Graphics.HdrHighlightWhiten.Value = HdrHighlightWhiten;
config.Graphics.EnableMacroHLE.Value = EnableMacroHLE;
config.Graphics.EnableColorSpacePassthrough.Value = EnableColorSpacePassthrough;
config.Graphics.ResScale.Value = ResolutionScale == 4 ? -1 : ResolutionScale + 1;
@@ -209,6 +209,28 @@
VerticalAlignment="Center"
Text="{Binding HdrHighlightMixText}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="24,4,0,0" IsVisible="{Binding EnableHdr}">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="Whitens the very brightest highlights (sun, fire, neon) so they read as light rather than a harsh saturated colour. 0 = old look (keep colour); higher = more whitening at the top."
Text="HDR highlight whitening"
Width="226" />
<controls:SliderScroll Value="{Binding HdrHighlightWhiten}"
MinWidth="150"
Margin="10,-3,0,0"
Height="32"
Padding="0,-5"
TickFrequency="1"
IsSnapToTickEnabled="True"
LargeChange="10"
SmallChange="1"
VerticalAlignment="Center"
Minimum="0"
Maximum="100" />
<TextBlock Margin="5,0"
Width="70"
VerticalAlignment="Center"
Text="{Binding HdrHighlightWhitenText}"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
@@ -292,6 +314,8 @@
Content="{ext:Locale GraphicsScalingFilterFsr}" />
<ComboBoxItem
Content="{ext:Locale GraphicsScalingFilterArea}" />
<ComboBoxItem
Content="NIS" />
</ComboBox>
<controls:SliderScroll Value="{Binding ScalingFilterLevel}"
ToolTip.Tip="{ext:Locale GraphicsScalingFilterLevelTooltip}"