diff --git a/src/amd/lanczoslib/lanczosFilter/src/lanczosFilterGenerator.cpp b/src/amd/lanczoslib/lanczosFilter/src/lanczosFilterGenerator.cpp new file mode 100644 index 00000000000..ae3d061ee07 --- /dev/null +++ b/src/amd/lanczoslib/lanczosFilter/src/lanczosFilterGenerator.cpp @@ -0,0 +1,566 @@ +/* Copyright 2025 Advanced Micro Devices, Inc. + * SPDX-License-Identifier: MIT + * + * Authors: AMD + * + */ +#include "lanczosFilterGenerator.h" +#define _USE_MATH_DEFINES +#include + +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(M_PI) * (static_cast(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(M_PI) * (static_cast(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(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(MaxSharpness); + float sharpMin = static_cast(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(0.5f + ratio*DowndBScales); + int32_t row1; + if (static_cast(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(row0)/DowndBScales; + float ratioUp = static_cast(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 + (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(filterVal * (float)(1 << QuantFrac)); + pUintFilter[(p * numTaps) + t] = static_cast(quantVal); + sum += quantVal; + } + error = sum - static_cast(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(pUintFilter[(p * numTaps) + loc]); + quantVal -= halfError; + pUintFilter[(p * numTaps) + loc ] = static_cast(quantVal); + quantVal = static_cast(pUintFilter[(p * numTaps) + loc - 1]); + quantVal -= halfError; + pUintFilter[(p * numTaps) + loc - 1] = static_cast(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); + } +} diff --git a/src/amd/lanczoslib/lanczosFilter/src/lanczosFilterGenerator.h b/src/amd/lanczoslib/lanczosFilter/src/lanczosFilterGenerator.h new file mode 100644 index 00000000000..9b6095e4d23 --- /dev/null +++ b/src/amd/lanczoslib/lanczosFilter/src/lanczosFilterGenerator.h @@ -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 + +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 diff --git a/src/amd/lanczoslib/lanczos_adaptor.cpp b/src/amd/lanczoslib/lanczos_adaptor.cpp new file mode 100644 index 00000000000..ef2edc2f7b9 --- /dev/null +++ b/src/amd/lanczoslib/lanczos_adaptor.cpp @@ -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); +} \ No newline at end of file diff --git a/src/amd/lanczoslib/lanczos_adaptor.h b/src/amd/lanczoslib/lanczos_adaptor.h new file mode 100644 index 00000000000..83e64dc1866 --- /dev/null +++ b/src/amd/lanczoslib/lanczos_adaptor.h @@ -0,0 +1,21 @@ +/* Copyright 2025 Advanced Micro Devices, Inc. + * SPDX-License-Identifier: MIT + * + * Authors: AMD + * + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void generate_lanczos_coeff(float scaling_ratio, uint32_t hw_tap, uint32_t hw_phases, uint16_t *coeff); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/amd/lanczoslib/meson.build b/src/amd/lanczoslib/meson.build new file mode 100644 index 00000000000..a82985b9692 --- /dev/null +++ b/src/amd/lanczoslib/meson.build @@ -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 +) diff --git a/src/amd/meson.build b/src/amd/meson.build index 146f2bed97d..aae0bf636c2 100644 --- a/src/amd/meson.build +++ b/src/amd/meson.build @@ -28,4 +28,5 @@ endif if with_gallium_radeonsi subdir('vpelib') subdir('gmlib') + subdir('lanczoslib') endif diff --git a/src/gallium/drivers/radeonsi/meson.build b/src/gallium/drivers/radeonsi/meson.build index 5281f40948d..03bd067a177 100644 --- a/src/gallium/drivers/radeonsi/meson.build +++ b/src/gallium/drivers/radeonsi/meson.build @@ -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, ) diff --git a/src/gallium/drivers/radeonsi/si_vpe.c b/src/gallium/drivers/radeonsi/si_vpe.c index a308e18da87..a38fdcfc38d 100644 --- a/src/gallium/drivers/radeonsi/si_vpe.c +++ b/src/gallium/drivers/radeonsi/si_vpe.c @@ -33,6 +33,7 @@ #include #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]); diff --git a/src/gallium/drivers/radeonsi/si_vpe.h b/src/gallium/drivers/radeonsi/si_vpe.h index 01f402d9ec5..78060b419ee 100644 --- a/src/gallium/drivers/radeonsi/si_vpe.h +++ b/src/gallium/drivers/radeonsi/si_vpe.h @@ -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*