v1.1.6 — NIS (NVIDIA Image Scaling) scaling filter + HDR highlight whitening slider
This commit is contained in:
@@ -9,5 +9,6 @@ namespace Ryujinx.Common.Configuration
|
||||
Nearest,
|
||||
Fsr,
|
||||
Area,
|
||||
Nis,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Binary file not shown.
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);
|
||||
}
|
||||
Binary file not shown.
@@ -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);
|
||||
}
|
||||
Binary file not shown.
@@ -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))
|
||||
);
|
||||
@@ -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()
|
||||
|
||||
Binary file not shown.
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}"
|
||||
|
||||
Reference in New Issue
Block a user