radeonsi/vpe: enhance scaling quality
add support for lanczos coefficients which enhaces the quality of scaling down Signed-off-by: Peyton Lee <peytolee@amd.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35361>
This commit is contained in:
@@ -0,0 +1,566 @@
|
||||
/* Copyright 2025 Advanced Micro Devices, Inc.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Authors: AMD
|
||||
*
|
||||
*/
|
||||
#include "lanczosFilterGenerator.h"
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
const double LanczosFilterGenerator::Epsilon = 0.00000000000000000005;
|
||||
|
||||
const float LanczosFilterGenerator::UpdBFuzzy = -6.0206f;
|
||||
const float LanczosFilterGenerator::UpdBFlat = 0.0000f;
|
||||
const float LanczosFilterGenerator::UpdBSharp = +6.0206f;
|
||||
|
||||
const float LanczosFilterGenerator::DowndBFuzzy = -12.0412f;
|
||||
const float LanczosFilterGenerator::DowndBFlat = -6.02060f;
|
||||
const float LanczosFilterGenerator::DowndBSharp = -1.00000f;
|
||||
|
||||
const float LanczosFilterGenerator::ThresholdRatioLow = 0.8f;
|
||||
const float LanczosFilterGenerator::ThresholdRatioUp = 1.0f;
|
||||
|
||||
const float LanczosFilterGenerator::PCoef0 = -0.73420f;
|
||||
const float LanczosFilterGenerator::PCoef1 = 11.5964f;
|
||||
const float LanczosFilterGenerator::PCoef2 = -20.3973f;
|
||||
const float LanczosFilterGenerator::PCoef3 = 15.9062f;
|
||||
|
||||
const float LanczosFilterGenerator::LancDownScaledBTable[DowndBScales+1][DowndBPoints] =
|
||||
{
|
||||
{6.021f, 4.000f, 2.000f, 0.000f, -1.000f, -2.000f, -4.000f, -6.021f, -8.000f, -10.000f, -12.041f },
|
||||
{1.430900f, 1.430900f, 1.430900f, 1.000000f, 0.010000f, 0.010000f, 0.010000f, 0.010000f, 0.010000f, 0.010000f, 0.010000f},
|
||||
{1.430900f, 1.430900f, 1.430900f, 1.000000f, 0.631104f, 0.010000f, 0.010000f, 0.010000f, 0.010000f, 0.010000f, 0.010000f},
|
||||
{1.430900f, 1.430900f, 1.430900f, 1.000000f, 0.852667f, 0.683285f, 0.010000f, 0.010000f, 0.010000f, 0.010000f, 0.010000f},
|
||||
{1.430900f, 1.430900f, 1.211063f, 1.000000f, 0.911794f, 0.823094f, 0.632013f, 0.371977f, 0.010000f, 0.010000f, 0.010000f},
|
||||
{1.430900f, 1.430900f, 1.147498f, 1.000000f, 0.937014f, 0.877198f, 0.760127f, 0.644078f, 0.525000f, 0.388752f, 0.203904f},
|
||||
{1.430900f, 1.308486f, 1.117958f, 1.000000f, 0.949518f, 0.901692f, 0.813452f, 0.731170f, 0.656033f, 0.584572f, 0.515552f},
|
||||
{1.430900f, 1.257660f, 1.104867f, 1.000000f, 0.955050f, 0.913236f, 0.836873f, 0.767940f, 0.707312f, 0.652090f, 0.601553f},
|
||||
{1.430900f, 1.244853f, 1.100741f, 1.000000f, 0.956680f, 0.916528f, 0.843580f, 0.778528f, 0.721578f, 0.670147f, 0.624064f}
|
||||
};
|
||||
|
||||
const float LanczosFilterGenerator::LancUpScaledBTable[UpdBScales+1][UpdBPoints] =
|
||||
{
|
||||
{6.021f, 4.000f, 2.000f, 0.000f, -2.000f, -4.000f, -6.021f },
|
||||
{1.430292f, 1.430292f, 1.170925f, 1.000000f, 0.875461f, 0.769256f, 0.673826f}
|
||||
};
|
||||
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* LanczosFilterGenerator::LanczosFilterGenerator
|
||||
*
|
||||
* @brief
|
||||
* LanczosFilterGenerator constructor
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
LanczosFilterGenerator::LanczosFilterGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* LanczosFilterGenerator::~LanczosFilterGenerator
|
||||
*
|
||||
* @brief
|
||||
* LanczosFilterGenerator destructor
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
LanczosFilterGenerator::~LanczosFilterGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* LanczosFilterGenerator::GenerateLanczosCoeff
|
||||
*
|
||||
* @brief
|
||||
* Generate 4-tap, 128 phase filter coefficients for lanczos kernel
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
void LanczosFilterGenerator::GenerateLanczosCoeff(
|
||||
float* pFilter, ///< [out] Filter coefficients
|
||||
float attenuation, ///< [in] Lanczos kernel parameter
|
||||
float kernelInterval, ///< [in] Input interval for the fitler kernel
|
||||
uint32 taps, ///< [in] Number of filter taps
|
||||
uint32 phases, ///< [in] Number of filter phases
|
||||
CoefType coefMode) ///< [in] Kernel type for coefficients
|
||||
{
|
||||
uint32 totalNumberofCoef = phases * taps;
|
||||
|
||||
float attenby2 = attenuation * taps * 0.5f;
|
||||
|
||||
switch(coefMode)
|
||||
{
|
||||
case CoefType::StandardLanczos:
|
||||
attenby2 = 1/attenby2;
|
||||
break;
|
||||
|
||||
case CoefType::TruncatedLanczos:
|
||||
{
|
||||
uint32 targetTaps = taps + AddedTap;
|
||||
attenby2 = 1/(attenuation*targetTaps*0.5f);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint32 currentPhase;
|
||||
uint32 currentTap;
|
||||
|
||||
for(currentPhase = 0; currentPhase < phases; currentPhase++)
|
||||
{
|
||||
float sumPerPhase = 0.0f;
|
||||
for(currentTap = 1; currentTap <= taps; currentTap++)
|
||||
{
|
||||
uint32 mainFilterIndex = (currentTap * phases) - currentPhase;
|
||||
float mainFilterInput = static_cast<float>(M_PI) * (static_cast<float>(2*mainFilterIndex)/totalNumberofCoef - 1.0f);
|
||||
mainFilterInput *= kernelInterval;
|
||||
float tapValue = 0.0f;
|
||||
|
||||
switch(coefMode)
|
||||
{
|
||||
case CoefType::ModifiedLanczos:
|
||||
case CoefType::StandardLanczos:
|
||||
case CoefType::TruncatedLanczos:
|
||||
|
||||
tapValue = Lanczos(mainFilterInput, attenby2);
|
||||
break;
|
||||
|
||||
case CoefType::TruncatedSinc:
|
||||
|
||||
tapValue = (kernelInterval < taps/2.0f)?Sinc(mainFilterInput):Lanczos(mainFilterInput, attenuation);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sumPerPhase += tapValue;
|
||||
pFilter[currentPhase*taps + currentTap-1] = tapValue;
|
||||
}
|
||||
// Normalize each filter phase
|
||||
for(currentTap = 0; currentTap < taps; currentTap++)
|
||||
{
|
||||
pFilter[currentPhase*taps + currentTap] /= sumPerPhase;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* LanczosFilterGenerator::GenerateSincCoeff
|
||||
*
|
||||
* @brief
|
||||
* Generate 4-tap, 32 phase filter coefficients for UV Sinc kernel
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
void LanczosFilterGenerator::GenerateSincCoeff(
|
||||
float* pFilter, ///< [out] Filter coefficients
|
||||
float attenuation, ///< [in] Lanczos kernel parameter
|
||||
float kernelInterval, ///< [in] Input interval for the fitler kernel
|
||||
uint32 taps, ///< [in] Number of filter taps
|
||||
uint32 phases) ///< [in] Number of filter phases
|
||||
{
|
||||
uint32 totalNumberofCoef = phases * taps;
|
||||
uint32 currentPhase = 0;
|
||||
uint32 currentTap = 0;
|
||||
|
||||
for (currentPhase = 0; currentPhase < phases; currentPhase++)
|
||||
{
|
||||
float sumPerPhase = 0.0f;
|
||||
for (currentTap = 1; currentTap <= taps; currentTap++)
|
||||
{
|
||||
uint32 mainFilterIndex = currentTap * phases - currentPhase;
|
||||
float PiX = static_cast<float>(M_PI) * (static_cast<float>(2 * mainFilterIndex) / totalNumberofCoef - 1.0f);
|
||||
PiX = PiX * kernelInterval;
|
||||
float tapValue = 0.0f;
|
||||
tapValue = Sinc(PiX)*Sinc(PiX*attenuation);
|
||||
sumPerPhase += tapValue;
|
||||
pFilter[currentPhase*taps + currentTap - 1] = tapValue;
|
||||
}
|
||||
// Normalize each filter phase
|
||||
for (currentTap = 0; currentTap < taps; currentTap++)
|
||||
{
|
||||
pFilter[currentPhase*taps + currentTap] /= sumPerPhase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* LanczosFilterGenerator::Sinc
|
||||
*
|
||||
* @brief
|
||||
* Calculates the value of the sinc function at the given input argument
|
||||
*
|
||||
* @return
|
||||
* Returns the value of sinc function at the given input
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
float LanczosFilterGenerator::Sinc(
|
||||
float input) ///< [in] Kernel input
|
||||
{
|
||||
if (fabs(input) > Epsilon)
|
||||
{
|
||||
float sinus = static_cast<float>(sin(input));
|
||||
float result = sinus / input;
|
||||
return result;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* LanczosFilterGenerator::Lanczos
|
||||
*
|
||||
* @brief
|
||||
* Calculates the value of the Lanczos function at the given input
|
||||
* argument and attenuation factor
|
||||
*
|
||||
* @return
|
||||
* Returns the value of Lanczos kernel at the given input arguments
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
float LanczosFilterGenerator::Lanczos(
|
||||
float input, ///< [in] Kernel input value
|
||||
float attenuation) ///< [in] Kernel parameter
|
||||
{
|
||||
return (Sinc(input) * Sinc(attenuation*input));
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* LanczosFilterGenerator::Interpolate
|
||||
*
|
||||
* @brief
|
||||
* Interpolate a point on a straight line
|
||||
*
|
||||
* @return
|
||||
* Return the interpolated value
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
float LanczosFilterGenerator::Interpolate(
|
||||
float dbValue, ///< [in] Frequency domain gain
|
||||
float sharpMin, ///< [in] Minimum sharpness
|
||||
float sharpMax, ///< [in] Maximum sharpness
|
||||
float dbMin, ///< [in] Minimum frequency domain gain
|
||||
float dbMax) ///< [in] Maximum frequency domain gain
|
||||
{
|
||||
float slope = (dbMax - dbMin)/(sharpMax - sharpMin);
|
||||
return (slope*(dbValue - sharpMin) + dbMin);
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* LanczosFilterGenerator::Ratio2Attenuation
|
||||
*
|
||||
* @brief
|
||||
* Interpolate the attenuation factor using the pre-generated table
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
float LanczosFilterGenerator::Ratio2Attenuation(
|
||||
float ratio, ///< [in] Scaling ratio
|
||||
float sharpness) ///< [in] Sharpness control
|
||||
{
|
||||
float sharpMax = static_cast<float>(MaxSharpness);
|
||||
float sharpMin = static_cast<float>(MinSharpness);
|
||||
float sharpFlat = (sharpMax + sharpMin)/2.0f;
|
||||
float dbMax;
|
||||
float dbMin;
|
||||
float dbValue;
|
||||
float attenuation;
|
||||
// index to closest table entries for the corresponding dbValue
|
||||
int32 tableIndex0;
|
||||
int32 tableIndex1;
|
||||
|
||||
if (ratio >= 1.0f)
|
||||
{
|
||||
if (sharpness < 0)
|
||||
{
|
||||
dbMax = UpdBFlat;
|
||||
dbMin = UpdBFuzzy;
|
||||
sharpMax = sharpFlat;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbMax = UpdBSharp;
|
||||
dbMin = UpdBFlat;
|
||||
sharpMin = sharpFlat;
|
||||
}
|
||||
dbValue = Interpolate(sharpness, sharpMin, sharpMax, dbMin, dbMax);
|
||||
|
||||
tableIndex0 = 0 ;
|
||||
while ((tableIndex0 < UpdBPoints - 1) && LancUpScaledBTable[0][tableIndex0] > dbValue && (tableIndex0 < UpdBPoints-1))
|
||||
{
|
||||
tableIndex0++;
|
||||
}
|
||||
tableIndex1 = tableIndex0 + 1;
|
||||
if (tableIndex0 == UpdBPoints-1)
|
||||
{
|
||||
tableIndex1 = tableIndex0;
|
||||
tableIndex0--;
|
||||
}
|
||||
|
||||
sharpMax = LancUpScaledBTable[1][tableIndex0];
|
||||
sharpMin = LancUpScaledBTable[1][tableIndex1];
|
||||
dbMax = LancUpScaledBTable[0][tableIndex0];
|
||||
dbMin = LancUpScaledBTable[0][tableIndex1];
|
||||
attenuation = Interpolate(dbValue, dbMax, dbMin, sharpMax, sharpMin);
|
||||
|
||||
return attenuation;
|
||||
}
|
||||
else if (ratio < ThresholdRatioLow)
|
||||
{
|
||||
if (sharpness < 0)
|
||||
{
|
||||
dbMax = DowndBFlat; //LancDownScaledBTable[0][UpdBPoints/2];
|
||||
dbMin = DowndBFuzzy; //LancDownScaledBTable[0][UpdBPoints-1];
|
||||
sharpMax = sharpFlat;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbMax = DowndBSharp; // LancDownScaledBTable[0][0];
|
||||
dbMin = DowndBFlat; // LancDownScaledBTable[0][UpdBPoints/2];
|
||||
sharpMin = sharpFlat;
|
||||
}
|
||||
dbValue = Interpolate(sharpness, sharpMin, sharpMax, dbMin, dbMax);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
dbMin = Interpolate(ratio, ThresholdRatioLow, ThresholdRatioUp, DowndBFlat, UpdBFlat);
|
||||
float dbflat= Interpolate(ratio, ThresholdRatioLow, ThresholdRatioUp, DowndBFlat, UpdBFlat);
|
||||
dbMax = Interpolate(ratio, ThresholdRatioLow, ThresholdRatioUp, DowndBSharp, UpdBSharp);
|
||||
sharpMax = float(MaxSharpness);
|
||||
sharpMin = float(MinSharpness);
|
||||
|
||||
if (sharpness < 0)
|
||||
{
|
||||
// interpole between [dbmin, dbflat]
|
||||
dbMax = dbflat;
|
||||
dbValue = Interpolate(sharpness, sharpMin, 0.0f, dbMin, dbMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
// interpole between [dbflat, dbmax]
|
||||
dbMin = dbflat;
|
||||
dbValue = Interpolate(sharpness, 0.0f, sharpMax, dbMin, dbMax);
|
||||
}
|
||||
|
||||
// dbValue must be in the Lancsos db_table, otherwise it must be clipped
|
||||
|
||||
if (dbValue > LancDownScaledBTable[0][0]) //desired attenuation is not reachable
|
||||
{
|
||||
dbValue = LancDownScaledBTable[0][0];
|
||||
}
|
||||
else if (dbValue < LancDownScaledBTable[0][DowndBPoints-1]) //desired attenuation is not reachable
|
||||
{
|
||||
dbValue = LancDownScaledBTable[0][DowndBPoints-1];
|
||||
}
|
||||
}
|
||||
// find the closest index and interpolate to find the dB value
|
||||
tableIndex1 = 0;
|
||||
while ( (LancDownScaledBTable[0][tableIndex1] > dbValue) && (tableIndex1 < DowndBPoints - 1) )
|
||||
{
|
||||
tableIndex1++;
|
||||
}
|
||||
tableIndex0 = tableIndex1 - 1;
|
||||
if (tableIndex1 == 0)
|
||||
{
|
||||
tableIndex0 = tableIndex1;
|
||||
tableIndex1 = 1;
|
||||
}
|
||||
|
||||
// compute the attenuation factor required to reach the dB at Nyquist
|
||||
// interpolate on the scale axes
|
||||
int32_t row0 = static_cast<int>(0.5f + ratio*DowndBScales);
|
||||
int32_t row1;
|
||||
if (static_cast<float>(row0)/DowndBScales < ratio)
|
||||
{
|
||||
row1 = row0 + 1;
|
||||
if (row1 > DowndBScales)
|
||||
{
|
||||
row1 = DowndBScales;
|
||||
row0 = row1 - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
row1 = row0;
|
||||
row0--;
|
||||
if (row0 < 1)
|
||||
{
|
||||
row0 = 1;
|
||||
row1 = 2;
|
||||
}
|
||||
}
|
||||
float ratioLow = static_cast<float>(row0)/DowndBScales;
|
||||
float ratioUp = static_cast<float>(row1)/DowndBScales;
|
||||
|
||||
sharpMax = Interpolate(ratio, ratioLow, ratioUp, LancDownScaledBTable[row0][tableIndex0],
|
||||
LancDownScaledBTable[row1][tableIndex0]);
|
||||
|
||||
sharpMin = Interpolate(ratio, ratioLow, ratioUp, LancDownScaledBTable[row0][tableIndex1],
|
||||
LancDownScaledBTable[row1][tableIndex1]);
|
||||
|
||||
dbMax = LancDownScaledBTable[0][tableIndex0];
|
||||
dbMin = LancDownScaledBTable[0][tableIndex1];
|
||||
attenuation = Interpolate(dbValue, dbMax, dbMin, sharpMax, sharpMin);
|
||||
return attenuation;
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* LanczosFilterGenerator::Ratio2CuttOff
|
||||
*
|
||||
* @brief
|
||||
* Maps the scaling ratio to the required input interval (Equation holds for 8-tap filter Only)
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
float LanczosFilterGenerator::Ratio2CuttOff(
|
||||
float ratio) ///< [in] Scaling ratio
|
||||
{
|
||||
float cutoffParam = static_cast<float>
|
||||
(PCoef3*(pow(ratio,3.0f)) + PCoef2*(pow(ratio,2.0f)) +
|
||||
PCoef1*ratio + PCoef0);
|
||||
|
||||
return cutoffParam;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
void LanczosFilterGenerator::ConvertScalingCoeffsToUint(
|
||||
uint16* pUintFilter,
|
||||
const float* pFloatFilter,
|
||||
const uint32 numTaps,
|
||||
const uint32 numPhases)
|
||||
{
|
||||
constexpr uint16 QuantFrac = 10;
|
||||
constexpr uint16 CoefOutFrac = 12;
|
||||
int32 error = 0;
|
||||
int32 halfError = 0;
|
||||
int16 quantVal = 0;
|
||||
int32 sum = 0;
|
||||
uint16 loc = 0;
|
||||
float filterVal = 0.0;
|
||||
|
||||
if (pUintFilter != nullptr)
|
||||
{
|
||||
for (uint32 p = 0; p < (numPhases / 2 + 1); p++)
|
||||
{
|
||||
sum = 0;
|
||||
for (uint32 t = 0; t < numTaps; t++)
|
||||
{
|
||||
filterVal = pFloatFilter[(p * numTaps) + t];
|
||||
quantVal = static_cast<int16>(filterVal * (float)(1 << QuantFrac));
|
||||
pUintFilter[(p * numTaps) + t] = static_cast<uint16>(quantVal);
|
||||
sum += quantVal;
|
||||
}
|
||||
error = sum - static_cast<int16>(1 << QuantFrac);
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
halfError = error / 2;
|
||||
// split adjustment between center taps
|
||||
// _loc = (_num_taps / 2) - 1;
|
||||
MaxLoc(&pFloatFilter[p * numTaps], numTaps, loc);
|
||||
quantVal = static_cast<int16>(pUintFilter[(p * numTaps) + loc]);
|
||||
quantVal -= halfError;
|
||||
pUintFilter[(p * numTaps) + loc ] = static_cast<uint16>(quantVal);
|
||||
quantVal = static_cast<int16>(pUintFilter[(p * numTaps) + loc - 1]);
|
||||
quantVal -= halfError;
|
||||
pUintFilter[(p * numTaps) + loc - 1] = static_cast<uint16>(quantVal);
|
||||
}
|
||||
if (CoefOutFrac > QuantFrac)
|
||||
{
|
||||
for (uint32 t = 0; t < numTaps; t++)
|
||||
{
|
||||
pUintFilter[(p * numTaps) + t] = pUintFilter[(p * numTaps) + t] << (CoefOutFrac - QuantFrac);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
void LanczosFilterGenerator::MaxLoc(
|
||||
const float* pFilter,
|
||||
uint32 numTaps,
|
||||
uint16& maxLoc)
|
||||
{
|
||||
float maxVal = 0;
|
||||
maxLoc = (numTaps / 2) - 1;
|
||||
for (uint32 i = 0; i < numTaps; i++)
|
||||
{
|
||||
if (pFilter[i] > maxVal)
|
||||
{
|
||||
maxVal = pFilter[i];
|
||||
maxLoc = i;
|
||||
}
|
||||
}
|
||||
if (maxLoc == 0)
|
||||
{
|
||||
maxLoc = 1;//safeguard condition in order to avoid getting values out of the
|
||||
// array boundaries.
|
||||
}
|
||||
}
|
||||
|
||||
/** Minimum of two values: */
|
||||
#define MIN2( A, B ) ( (A)<(B) ? (A) : (B) )
|
||||
/** Maximum of two values: */
|
||||
#define MAX2( A, B ) ( (A)>(B) ? (A) : (B) )
|
||||
|
||||
// =====================================================================================================================
|
||||
void LanczosFilterGenerator::GenerateLanczosCoeff(
|
||||
float* pCoef, ///< [in] coef buffer
|
||||
float scalingRatio, ///< [in] scaling ratio
|
||||
uint32 tapCount, ///< [in] number of taps
|
||||
uint32 phaseCount,
|
||||
float kernelInterval,
|
||||
float attenuation,
|
||||
float sharpness)
|
||||
{
|
||||
if (pCoef != nullptr)
|
||||
{
|
||||
LanczosFilterGenerator::CoefType coefType = LanczosFilterGenerator::CoefType::ModifiedLanczos;
|
||||
|
||||
|
||||
// 4-tap and 8-tap filters use two different kernel functions for their coefficients
|
||||
// the parameters for each filter mode is assigned separately
|
||||
if (tapCount == 4) //TapCount4
|
||||
{
|
||||
coefType =
|
||||
(scalingRatio < 1) ? LanczosFilterGenerator::CoefType::TruncatedLanczos :
|
||||
LanczosFilterGenerator::CoefType::ModifiedLanczos;
|
||||
|
||||
if (scalingRatio < 1)
|
||||
{
|
||||
kernelInterval = LanczosFilterGenerator::Ratio2CuttOff(1 / scalingRatio);
|
||||
attenuation = (scalingRatio <= 1) ? 1.0f : 1.0f / (MIN2(kernelInterval, (tapCount + 2) / 2.0f));
|
||||
kernelInterval = MIN2(kernelInterval, tapCount / 2.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
attenuation = LanczosFilterGenerator::Ratio2Attenuation(1 / scalingRatio, sharpness);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
coefType = (scalingRatio <= 1) ?
|
||||
LanczosFilterGenerator::CoefType::TruncatedLanczos : LanczosFilterGenerator::CoefType::TruncatedSinc;
|
||||
kernelInterval = LanczosFilterGenerator::Ratio2CuttOff(1 / scalingRatio);
|
||||
attenuation = (scalingRatio <= 1) ? 1.0f : 1.0f / (MIN2(kernelInterval, (tapCount + 2) / 2.0f));
|
||||
kernelInterval = MIN2(kernelInterval, tapCount / 2.0f);
|
||||
}
|
||||
|
||||
LanczosFilterGenerator::GenerateLanczosCoeff(pCoef, attenuation, kernelInterval, tapCount, phaseCount, coefType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/* Copyright 2025 Advanced Micro Devices, Inc.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Authors: AMD
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LANCZOSFILTERGENERATOR_H_
|
||||
#define _LANCZOSFILTERGENERATOR_H_
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
|
||||
|
||||
/**
|
||||
***************************************************************************************************
|
||||
* @brief This is the filter for generating Lanczos coefficinets
|
||||
*
|
||||
***************************************************************************************************
|
||||
*/
|
||||
class LanczosFilterGenerator {
|
||||
|
||||
public:
|
||||
|
||||
enum class CoefType : uint32_t
|
||||
{
|
||||
ModifiedLanczos = 0, ///< Modified Lanczos kernel
|
||||
StandardLanczos = 1, ///< Standard Lanczos kernel
|
||||
TruncatedLanczos = 2, ///< Standard Lanczos for (n+m) taps truncated to n taps
|
||||
TruncatedSinc = 3, ///< Truncated Sinc Kernel
|
||||
Count
|
||||
};
|
||||
static void GenerateLanczosCoeff(
|
||||
float* pFilter,
|
||||
float attenuation,
|
||||
float kernelInterval,
|
||||
uint32 taps,
|
||||
uint32 phases,
|
||||
CoefType coefMode);
|
||||
|
||||
static void GenerateLanczosCoeff(
|
||||
float* pCoef,
|
||||
float scalingRatio,
|
||||
uint32 tapCount,
|
||||
uint32 phaseCount,
|
||||
float kernelInterval = 1.0f,
|
||||
float attenuation = 1.0f,
|
||||
float sharpness = 0.0f );
|
||||
|
||||
static void ConvertScalingCoeffsToUint(
|
||||
uint16* pUintFilter,
|
||||
const float* pFloatFilter,
|
||||
const uint32 num_taps,
|
||||
const uint32 num_phases);
|
||||
|
||||
static void GenerateSincCoeff(
|
||||
float* pFilter,
|
||||
float attenuation, float kernelInterval,
|
||||
uint32 taps, uint32 phases);
|
||||
static float Ratio2Attenuation(float ratio, float sharpness);
|
||||
static float Ratio2CuttOff(float ratio);
|
||||
static void MaxLoc(const float* pFilter,
|
||||
uint32 NumTaps,
|
||||
uint16& MaxLoc);
|
||||
|
||||
protected:
|
||||
|
||||
static const double Epsilon;
|
||||
static const uint32 AddedTap = 2; // Number of taps to add for truncated coefficient generation
|
||||
static const uint32 UpdBScales = 1;
|
||||
static const int32 UpdBPoints = 7;
|
||||
static const int32 DowndBScales = 8;
|
||||
static const int32 DowndBPoints = 11;
|
||||
static const int32 MinSharpness = -50;
|
||||
static const int32 MaxSharpness = 50;
|
||||
static const float UpdBFuzzy;
|
||||
static const float UpdBFlat;
|
||||
static const float UpdBSharp;
|
||||
static const float DowndBFuzzy;
|
||||
static const float DowndBFlat;
|
||||
static const float DowndBSharp;
|
||||
static const float ThresholdRatioLow;
|
||||
static const float ThresholdRatioUp;
|
||||
static const float LancDownScaledBTable[DowndBScales+1][DowndBPoints];
|
||||
static const float LancUpScaledBTable[UpdBScales+1][UpdBPoints];
|
||||
static const float PCoef0;
|
||||
static const float PCoef1;
|
||||
static const float PCoef2;
|
||||
static const float PCoef3;
|
||||
static float Sinc(float input);
|
||||
static float Lanczos(float input, float attenuation);
|
||||
static float Interpolate(float dbValue, float sharpmin, float sharpmax, float dbmin, float dbmax);
|
||||
|
||||
private:
|
||||
|
||||
LanczosFilterGenerator();
|
||||
virtual ~LanczosFilterGenerator();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2025 Advanced Micro Devices, Inc.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Authors: AMD
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lanczos_adaptor.h"
|
||||
#include "lanczosFilterGenerator.h"
|
||||
|
||||
#define MaxHwNumTabs 8
|
||||
#define HwNumPhases 64
|
||||
#define HwNumTabsChroma 2
|
||||
|
||||
void generate_lanczos_coeff(float scaling_ratio, uint32_t hw_tap, uint32_t hw_phases, uint16_t *coeff)
|
||||
{
|
||||
float filterCoeffs[MaxHwNumTabs * HwNumPhases] = {0};
|
||||
|
||||
LanczosFilterGenerator::GenerateLanczosCoeff(filterCoeffs, scaling_ratio, hw_tap, hw_phases);
|
||||
LanczosFilterGenerator::ConvertScalingCoeffsToUint(coeff, filterCoeffs, hw_tap, hw_phases);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2025 Advanced Micro Devices, Inc.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Authors: AMD
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void generate_lanczos_coeff(float scaling_ratio, uint32_t hw_tap, uint32_t hw_phases, uint16_t *coeff);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
# Copyright 2022 Advanced Micro Devices, Inc.
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
c_args_lanczos = cc.get_supported_arguments([
|
||||
'-Wall',
|
||||
'-Wextra',
|
||||
'-Wno-unused',
|
||||
'-Wno-unused-parameter',
|
||||
'-Wno-unused-command-line-argument',
|
||||
'-Wno-ignored-qualifiers',
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-self-assign',
|
||||
'-Wno-implicit-fallthrough',
|
||||
'-Werror=comment',
|
||||
'-Werror=missing-braces',
|
||||
'-Werror=override-init',
|
||||
'-Werror=enum-conversion',
|
||||
'-Werror=enum-compare',
|
||||
'-Werror=maybe-uninitialized',
|
||||
])
|
||||
|
||||
c_args_lanczos += [
|
||||
# '-DGM_SIM',
|
||||
]
|
||||
|
||||
lanczos_files = files(
|
||||
'lanczos_adaptor.h',
|
||||
'lanczos_adaptor.cpp',
|
||||
'lanczosFilter/src/lanczosFilterGenerator.h',
|
||||
'lanczosFilter/src/lanczosFilterGenerator.cpp',
|
||||
)
|
||||
|
||||
inc_amd_lanczos = include_directories(
|
||||
'lanczosFilter/src',
|
||||
)
|
||||
|
||||
liblanczos = static_library(
|
||||
'liblanczos.a',
|
||||
lanczos_files,
|
||||
install : false,
|
||||
c_args : c_args_lanczos,
|
||||
include_directories : inc_amd_lanczos
|
||||
)
|
||||
@@ -28,4 +28,5 @@ endif
|
||||
if with_gallium_radeonsi
|
||||
subdir('vpelib')
|
||||
subdir('gmlib')
|
||||
subdir('lanczoslib')
|
||||
endif
|
||||
|
||||
@@ -166,7 +166,7 @@ libradeonsi = static_library(
|
||||
driver_radeonsi = declare_dependency(
|
||||
compile_args : ['-DGALLIUM_RADEONSI'] + libradeonsi_cflags,
|
||||
link_with : radeonsi_gfx_libs + [
|
||||
libradeonsi, libradeonwinsys, libamdgpuwinsys, libamd_common, libamd_common_llvm, libvpe, libgm
|
||||
libradeonsi, libradeonwinsys, libamdgpuwinsys, libamd_common, libamd_common_llvm, libvpe, libgm, liblanczos
|
||||
],
|
||||
dependencies : idep_nir,
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <si_pipe.h>
|
||||
#include "si_vpe.h"
|
||||
#include "gmlib/tonemap_adaptor.h"
|
||||
#include "lanczoslib/lanczos_adaptor.h"
|
||||
|
||||
#define SI_VPE_LOG_LEVEL_DEFAULT 0
|
||||
#define SI_VPE_LOG_LEVEL_INFO 1
|
||||
@@ -605,6 +606,93 @@ si_vpe_set_surface_info(struct vpe_video_processor *vpeproc,
|
||||
return VPE_STATUS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
si_vpe_reuse_scaling_info(struct vpe_video_processor *vpeproc,
|
||||
struct vpe_stream *stream,
|
||||
float *scaling_ratios)
|
||||
{
|
||||
if (vpeproc->lanczos_info) {
|
||||
uint8_t scaling_pass_num = (vpeproc->geometric_passes)? vpeproc->geometric_passes : 1;
|
||||
struct vpe_scaling_lanczos_info *lanczof = vpeproc->lanczos_info;
|
||||
uint8_t idx;
|
||||
|
||||
for (idx = 0; idx < scaling_pass_num; idx++) {
|
||||
if (lanczof[idx].scaling_ratios[0] == 0 && lanczof[idx].scaling_ratios[1] == 0)
|
||||
break;
|
||||
else if (lanczof[idx].scaling_ratios[0] == scaling_ratios[0] &&
|
||||
lanczof[idx].scaling_ratios[1] == scaling_ratios[1]) {
|
||||
SIVPE_INFO(vpeproc->log_level, "Reuse Scaling Coeff from array[%d]\n", idx);
|
||||
memcpy(&stream->polyphase_scaling_coeffs,
|
||||
&lanczof[idx].filterCoeffs,
|
||||
sizeof(struct vpe_scaling_filter_coeffs));
|
||||
stream->use_external_scaling_coeffs = true;
|
||||
stream->scaling_info.taps.h_taps = stream->polyphase_scaling_coeffs.taps.h_taps;
|
||||
stream->scaling_info.taps.v_taps = stream->polyphase_scaling_coeffs.taps.v_taps;
|
||||
stream->scaling_info.taps.h_taps_c = stream->polyphase_scaling_coeffs.taps.h_taps_c;
|
||||
stream->scaling_info.taps.v_taps_c = stream->polyphase_scaling_coeffs.taps.v_taps_c;
|
||||
return true;
|
||||
} else {
|
||||
SIVPE_DBG(vpeproc->log_level, "Scaling Coeff in array[%d] is not match\n", idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
si_vpe_init_polyphase_filter(struct vpe_video_processor *vpeproc,
|
||||
struct vpe_stream *stream,
|
||||
float *scaling_ratios)
|
||||
{
|
||||
uint32_t hw_num_taps[2] = {4, 4};
|
||||
uint32_t hTaps = stream->scaling_info.taps.h_taps;
|
||||
uint32_t vTaps = stream->scaling_info.taps.v_taps;
|
||||
uint32_t hw_num_phases = 64;
|
||||
uint8_t scaling_pass_num = (vpeproc->geometric_passes)? vpeproc->geometric_passes : 1;
|
||||
|
||||
if (hTaps > 0)
|
||||
hw_num_taps[0] = hTaps;
|
||||
if (vTaps > 0)
|
||||
hw_num_taps[1] = vTaps;
|
||||
|
||||
stream->use_external_scaling_coeffs = true;
|
||||
stream->polyphase_scaling_coeffs.taps.h_taps = hw_num_taps[0];
|
||||
stream->polyphase_scaling_coeffs.taps.v_taps = hw_num_taps[1];
|
||||
stream->polyphase_scaling_coeffs.taps.h_taps_c = 2;
|
||||
stream->polyphase_scaling_coeffs.taps.v_taps_c = 2;
|
||||
stream->polyphase_scaling_coeffs.nb_phases = hw_num_phases;
|
||||
|
||||
if (scaling_ratios[0] > 0)
|
||||
generate_lanczos_coeff(scaling_ratios[0], hw_num_taps[0], hw_num_phases, stream->polyphase_scaling_coeffs.horiz_polyphase_coeffs);
|
||||
|
||||
if (scaling_ratios[1] > 0)
|
||||
generate_lanczos_coeff(scaling_ratios[1], hw_num_taps[1], hw_num_phases, stream->polyphase_scaling_coeffs.vert_polyphase_coeffs);
|
||||
|
||||
/* backup the scaling ratio and coeff info for re-using in next round */
|
||||
if (!vpeproc->lanczos_info)
|
||||
vpeproc->lanczos_info = (struct vpe_scaling_lanczos_info *)CALLOC(scaling_pass_num, sizeof(struct vpe_scaling_lanczos_info));
|
||||
|
||||
if (vpeproc->lanczos_info) {
|
||||
struct vpe_scaling_lanczos_info *lanczof = vpeproc->lanczos_info;
|
||||
uint8_t idx;
|
||||
|
||||
for (idx = 0; idx < scaling_pass_num; idx++) {
|
||||
if (lanczof[idx].scaling_ratios[0] == 0 && lanczof[idx].scaling_ratios[1] == 0) {
|
||||
lanczof[idx].scaling_ratios[0] = scaling_ratios[0];
|
||||
lanczof[idx].scaling_ratios[1] = scaling_ratios[1];
|
||||
memcpy(&lanczof[idx].filterCoeffs,
|
||||
&stream->polyphase_scaling_coeffs,
|
||||
sizeof(struct vpe_scaling_filter_coeffs));
|
||||
SIVPE_INFO(vpeproc->log_level, "Backup Scaling Coeff into to array[%d]\n", idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx == scaling_pass_num)
|
||||
SIVPE_INFO(vpeproc->log_level, "Backup Scaling Coeff failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
si_vpe_set_stream_in_param(struct vpe_video_processor *vpeproc,
|
||||
const struct pipe_vpp_desc *process_properties,
|
||||
@@ -614,6 +702,7 @@ si_vpe_set_stream_in_param(struct vpe_video_processor *vpeproc,
|
||||
struct vpe_scaling_info *scaling_info = &stream->scaling_info;
|
||||
struct vpe_blend_info *blend_info = &stream->blend_info;
|
||||
struct vpe_color_adjust *color_adj = &stream->color_adj;
|
||||
float scaling_ratios[2] = { 1.0, 1.0 };
|
||||
|
||||
/* Init: scaling_info */
|
||||
scaling_info->src_rect.x = process_properties->src_region.x0;
|
||||
@@ -629,7 +718,19 @@ si_vpe_set_stream_in_param(struct vpe_video_processor *vpeproc,
|
||||
scaling_info->taps.v_taps_c = 2;
|
||||
scaling_info->taps.h_taps_c = 2;
|
||||
|
||||
vpe_get_optimal_num_of_taps(vpe_handle, scaling_info);
|
||||
/* Get current scaling ratio */
|
||||
if (stream->scaling_info.dst_rect.width > 1)
|
||||
scaling_ratios[0] = (float)stream->scaling_info.src_rect.width / (float)stream->scaling_info.dst_rect.width;
|
||||
if (stream->scaling_info.dst_rect.height > 1)
|
||||
scaling_ratios[1] = (float)stream->scaling_info.src_rect.height / (float)stream->scaling_info.dst_rect.height;
|
||||
|
||||
if (!si_vpe_reuse_scaling_info(vpeproc, stream, scaling_ratios)) {
|
||||
/* Failed to reuse scaling info,
|
||||
* it means the new scaling coeff have to be generated
|
||||
*/
|
||||
vpe_get_optimal_num_of_taps(vpe_handle, scaling_info);
|
||||
si_vpe_init_polyphase_filter(vpeproc, stream, scaling_ratios);
|
||||
}
|
||||
|
||||
blend_info->blending = false;
|
||||
blend_info->pre_multiplied_alpha = false;
|
||||
@@ -856,6 +957,9 @@ si_vpe_processor_destroy(struct pipe_video_codec *codec)
|
||||
if (vpeproc->geometric_scaling_ratios)
|
||||
FREE(vpeproc->geometric_scaling_ratios);
|
||||
|
||||
if (vpeproc->lanczos_info)
|
||||
FREE(vpeproc->lanczos_info);
|
||||
|
||||
if (vpeproc->geometric_buf[0])
|
||||
vpeproc->geometric_buf[0]->destroy(vpeproc->geometric_buf[0]);
|
||||
|
||||
|
||||
@@ -48,6 +48,11 @@
|
||||
|
||||
#define VPE_MAX_GEOMETRIC_DOWNSCALE 4.f
|
||||
|
||||
struct vpe_scaling_lanczos_info {
|
||||
float scaling_ratios[2];
|
||||
struct vpe_scaling_filter_coeffs filterCoeffs;
|
||||
};
|
||||
|
||||
/* For Hooking VPE as a decoder instance */
|
||||
struct vpe_video_processor {
|
||||
struct pipe_video_codec base;
|
||||
@@ -83,6 +88,9 @@ struct vpe_video_processor {
|
||||
float *geometric_scaling_ratios;
|
||||
uint8_t geometric_passes;
|
||||
struct pipe_video_buffer *geometric_buf[2];
|
||||
|
||||
/* For Lanczos Coeff */
|
||||
struct vpe_scaling_lanczos_info *lanczos_info;
|
||||
};
|
||||
|
||||
struct pipe_video_codec*
|
||||
|
||||
Reference in New Issue
Block a user