Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ea21f71
Update Bilateralfilter in MCSTF
Logaprakash-mcw Jun 25, 2026
40c34c2
Add CLI: --mcstf-ref-range for param->mcstfFrameRange
Logaprakash-mcw Jun 25, 2026
7f13246
Implement Multi-threaded workflow for bilateralFilter in MCSTF
Logaprakash-mcw Jun 25, 2026
7b8f7d3
Implement Multithreading for Motion Estimation in MCSTF
Logaprakash-mcw Jun 26, 2026
4946fd3
mcstf: Add AVX2 implementation for ME kernels
GUNASRIJ11 Jun 26, 2026
7990512
mcstf: Add AVX2 implementation for applyMotion
GUNASRIJ11 Jun 28, 2026
69abeb1
mcstf: disable MCSTF when bFrameAdaptive=0
GUNASRIJ11 May 25, 2026
adeae33
mcstf: add AVX2 implementations
GUNASRIJ11 Jun 28, 2026
5c91204
mcstf: Prune unused computations
GUNASRIJ11 Jun 29, 2026
479dcc2
mcstf: Generate lowres planes using HM-equivalent averaging
GUNASRIJ11 Jun 29, 2026
cb39dfa
Remove unused frame_subsample_luma primitive
GUNASRIJ11 Jun 29, 2026
4a63489
mcstf: Add AVX2 implementation for bilateralFilter
GUNASRIJ11 Jun 29, 2026
5914a1a
Enable profiling stats for MCSTF
Logaprakash-mcw Jun 29, 2026
4ca99dd
Fix build warnings
Logaprakash-mcw Jul 1, 2026
4b00e46
mcstf: Move AVX2 implementation to common/vec
GUNASRIJ11 Jul 17, 2026
66fe2ac
test:Add testbench support for MCSTF
kirithika7 Jul 14, 2026
9f5c576
cleanup: Rename MCSTF subsample function
kirithika7 Jul 15, 2026
42632ed
mcstf: Decouple MCSTF HME from badapt flow
kirithika7 Jul 7, 2026
c824ad9
Code cleanup for MCSTF
Logaprakash-mcw Jul 6, 2026
faaae54
mcstf: Add --selective-mcstf CLI option to enable noise-based MCSTF f…
kirithika7 Jul 20, 2026
72c1d53
Fix segfault in MCSTF
Akilan-Sivakumar Jul 20, 2026
dbc98cd
mcstf:code cleanup
kirithika7 Jul 23, 2026
e88d474
mcstf:fix code quality issues
kirithika7 Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions doc/reST/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1358,15 +1358,33 @@ Temporal / motion search options

Motion-compensated spatio-temporal filtering (MCSTF) improves the compression
efficiency of videos that contain a high level of noise. It introduces a
temporal filter before encoding and this filter is applied only to the I- and P-frames.
temporal filter before encoding, and this filter is applied unconditionally
to every I- and P-frame regardless of how noisy the source content actually is.
It utilizes previously generated motion vectors across different video content
resolutions to find the best temporal correspondence for low-pass filtering. Here,
motion estimation is applied between the central picture and each future or past
picture, thereby generating multiple motion-compensated predictions, which are then
combined by using adaptive filtering to produce a final noise-reduced picture.
To filter frames selectively based on their estimated noise level instead, use
:option:`--selective-mcstf`.
Default: disabled

Note : MCSTF should be enabled only with frame threads 1
.. option:: --mcstf-ref-range

Number of reference frames to consider on each side (past and future) of the
current frame during Motion-compensated spatio-temporal filtering. A higher value
includes more temporal neighbors for filtering, which can improve noise reduction
at the cost of increased memory usage and processing time. The valid range is 1 to 4.
Default: 2

.. option:: --selective-mcstf, --no-selective-mcstf

Gate MCSTF with a per-GOP noise estimate: skip the filter for GOPs whose
starting I/IDR frame (or scenecut) is estimated as clean (low noise), and
apply it only where it helps. Implicitly enables :option:`--mcstf` if it
was not already turned on. When :option:`--csv-log-level` is 2 or higher,
adds a Frame Noise and IsMCSTFEnabled column to the per-frame CSV log.
Default: disabled

Spatial/intra options
=====================
Expand Down
2 changes: 1 addition & 1 deletion source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ option(NATIVE_BUILD "Target the build CPU" OFF)
option(STATIC_LINK_CRT "Statically link C and C++ runtimes for release builds" OFF)
mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)
# X265_BUILD must be incremented each time the public API is changed
set(X265_BUILD 216)
set(X265_BUILD 217)
configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
"${PROJECT_BINARY_DIR}/x265.def")
configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
Expand Down
7 changes: 5 additions & 2 deletions source/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ if(ENABLE_ASSEMBLY AND X86)
set(SSE3 vec/dct-sse3.cpp)
set(SSSE3 vec/dct-ssse3.cpp)
set(SSE41 vec/dct-sse41.cpp)
set(AVX2 vec/temporalfilter-avx2.cpp)

if(MSVC)
set(PRIMITIVES ${SSE3} ${SSSE3} ${SSE41})
set(PRIMITIVES ${SSE3} ${SSSE3} ${SSE41} ${AVX2})
set(WARNDISABLE "/wd4100") # unreferenced formal parameter
if(INTEL_CXX)
add_definitions(/Qwd111) # statement is unreachable
Expand All @@ -39,6 +40,7 @@ if(ENABLE_ASSEMBLY AND X86)
# x64 implies SSE4, so only add /arch:SSE2 if building for Win32
set_source_files_properties(${SSE3} ${SSSE3} ${SSE41} PROPERTIES COMPILE_FLAGS "${WARNDISABLE} /arch:SSE2")
endif()
set_source_files_properties(${AVX2} PROPERTIES COMPILE_FLAGS "${WARNDISABLE} /arch:AVX2")
endif()
if(GCC)
if(CLANG)
Expand All @@ -48,10 +50,11 @@ if(ENABLE_ASSEMBLY AND X86)
set(WARNDISABLE "-Wno-unused-parameter")
endif()
if(INTEL_CXX OR CLANG OR (NOT CC_VERSION VERSION_LESS 4.3))
set(PRIMITIVES ${SSE3} ${SSSE3} ${SSE41})
set(PRIMITIVES ${SSE3} ${SSSE3} ${SSE41} ${AVX2})
set_source_files_properties(${SSE3} PROPERTIES COMPILE_FLAGS "${WARNDISABLE} -msse3")
set_source_files_properties(${SSSE3} PROPERTIES COMPILE_FLAGS "${WARNDISABLE} -mssse3")
set_source_files_properties(${SSE41} PROPERTIES COMPILE_FLAGS "${WARNDISABLE} -msse4.1")
set_source_files_properties(${AVX2} PROPERTIES COMPILE_FLAGS "${WARNDISABLE} -mavx2")
endif()
endif()
set(VEC_PRIMITIVES vec/vec-primitives.cpp ${PRIMITIVES})
Expand Down
2 changes: 0 additions & 2 deletions source/common/loongarch64/asm-primitives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,6 @@ void setupAssemblyPrimitives(EncoderPrimitives &p, int cpuMask)
// mc
p.frameInitLowres = PFX(frame_init_lowres_core_lsx);
p.frameInitLowerRes = PFX(frame_init_lowres_core_lsx);
p.frameSubSampleLuma = PFX(frame_subsample_luma_lsx);

p.fix8Unpack = PFX(cutree_fix8_unpack_lsx);
p.fix8Pack = PFX(cutree_fix8_pack_lsx);
Expand Down Expand Up @@ -1333,7 +1332,6 @@ void setupAssemblyPrimitives(EncoderPrimitives &p, int cpuMask)
// mc
p.frameInitLowres = PFX(frame_init_lowres_core_lasx);
p.frameInitLowerRes = PFX(frame_init_lowres_core_lasx);
p.frameSubSampleLuma = PFX(frame_subsample_luma_lasx);

p.fix8Unpack = PFX(cutree_fix8_unpack_lasx);
p.fix8Pack = PFX(cutree_fix8_pack_lasx);
Expand Down
117 changes: 0 additions & 117 deletions source/common/loongarch64/mc-a2.S
Original file line number Diff line number Diff line change
Expand Up @@ -278,123 +278,6 @@ function x265_frame_init_lowres_core_lasx
blt zero, t0, .LOOP_HEIGHT_FRAME_INIT_LOWRES_CORE_LASX
endfunc

.macro CALCULATE_FRAMESUBSAMPLELUMA_LSX_8 offset0
// load inRow[offset0 ~ offset0+15], inRowBelow[offset0 ~ offset0+15]
vldx vr1, a0, \offset0
vldx vr3, t0, \offset0
vaddwev.h.bu vr4, vr1, vr3
vaddwod.h.bu vr5, vr1, vr3
vsrari.h vr4, vr4, 1
vsrari.h vr5, vr5, 1
vadd.h vr6, vr4, vr5
vsrarni.b.h vr6, vr6, 1
.endm

/*-----------------------------------------------------------------------------
void x265_frame_subsample_luma_lsx(const pixel* src0, pixel* dst0,
intptr_t src_stride, intptr_t dst_stride, int width, int height)
-----------------------------------------------------------------------------*/
function x265_frame_subsample_luma_lsx
andi t4, a4, 7 // width % 8
srli.d a4, a4, 3 // width / 8

.LOOP_HEIGHT_FRAME_SUBSAMPLE_LUMA_LSX:
add.d t0, a0, a2 // *inRowBelow
addi.d t1, zero, 0
addi.d t2, zero, 0
addi.d t8, zero, 0 // index

.LOOP_WIDTH_FRAME_SUBSAMPLE_LUMA_LSX:
bge t8, a4, .HANDLING_MANTISSA_FRAMESUBSAMPLELUMA_LSX_7
CALCULATE_FRAMESUBSAMPLELUMA_LSX_8 t1
add.d t3, a1, t2
vstelm.d vr6, t3, 0, 0

// prepare for next inner-loop
addi.d t1, t1, 16
addi.d t2, t2, 8
addi.d t8, t8, 1 // index++
b .LOOP_WIDTH_FRAME_SUBSAMPLE_LUMA_LSX

.HANDLING_MANTISSA_FRAMESUBSAMPLELUMA_LSX_7:
bge zero, t4, .PREPARE_FOR_NEXT_OUTERLOOP_FRAMESUBSAMPLELUMA_LSX
CALCULATE_FRAMESUBSAMPLELUMA_LSX_8 t1 // calculate mantissa
addi.d t8, zero, 0
add.d t3, a1, t2
.irp offset0, 0, 1, 2, 3, 4, 5, 6
vstelm.b vr6, t3, 0, \offset0
addi.d t8, t8, 1
addi.d t3, t3, 1
bge t8, t4, .PREPARE_FOR_NEXT_OUTERLOOP_FRAMESUBSAMPLELUMA_LSX
.endr

.PREPARE_FOR_NEXT_OUTERLOOP_FRAMESUBSAMPLELUMA_LSX:
// prepare for next outer-loop
add.d a0, a0, a2
add.d a0, a0, a2
add.d a1, a1, a3
addi.d a5, a5, -1
blt zero, a5, .LOOP_HEIGHT_FRAME_SUBSAMPLE_LUMA_LSX
endfunc

.macro CALCULATE_FRAMESUBSAMPLELUMA_LASX_16 offset0
// load inRow[offset0 ~ offset0+31], inRowBelow[offset0 ~ offset0+31]
xvldx xr1, a0, \offset0
xvldx xr3, t0, \offset0
xvaddwev.h.bu xr4, xr1, xr3
xvaddwod.h.bu xr5, xr1, xr3
xvsrari.h xr4, xr4, 1
xvsrari.h xr5, xr5, 1
xvadd.h xr6, xr4, xr5
xvsrarni.b.h xr6, xr6, 1
xvpermi.d xr6, xr6, 0b00001000
.endm

/*-----------------------------------------------------------------------------
void x265_frame_subsample_luma_lasx(const pixel* src0, pixel* dst0,
intptr_t src_stride, intptr_t dst_stride, int width, int height)
-----------------------------------------------------------------------------*/
function x265_frame_subsample_luma_lasx
andi t4, a4, 15 // width % 16
srli.d a4, a4, 4 // width / 16

.LOOP_HEIGHT_FRAME_SUBSAMPLE_LUMA_LASX:
add.d t0, a0, a2 // *inRowBelow
addi.d t1, zero, 0
addi.d t2, zero, 0
addi.d t8, zero, 0 // index

.LOOP_WIDTH_FRAME_SUBSAMPLE_LUMA_LASX:
bge t8, a4, .HANDLING_MANTISSA_FRAMESUBSAMPLELUMA_LASX_15
CALCULATE_FRAMESUBSAMPLELUMA_LASX_16 t1
vstx vr6, a1, t2
addi.d t1, t1, 32
addi.d t2, t2, 16
addi.d t8, t8, 1 // index++
b .LOOP_WIDTH_FRAME_SUBSAMPLE_LUMA_LASX

.HANDLING_MANTISSA_FRAMESUBSAMPLELUMA_LASX_15:
bge zero, t4, .PREPARE_FOR_NEXT_OUTERLOOP_FRAMESUBSAMPLELUMA_LASX
CALCULATE_FRAMESUBSAMPLELUMA_LASX_16 t1 // calculate mantissa
addi.d t8, zero, 0
add.d t3, a1, t2
.irp offset0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
vstelm.b vr6, t3, 0, \offset0
addi.d t8, t8, 1
addi.d t3, t3, 1
bge t8, t4, .PREPARE_FOR_NEXT_OUTERLOOP_FRAMESUBSAMPLELUMA_LASX
.endr

.PREPARE_FOR_NEXT_OUTERLOOP_FRAMESUBSAMPLELUMA_LASX:
// prepare for next loop
add.d a0, a0, a2
add.d a0, a0, a2
add.d a1, a1, a3
addi.d a5, a5, -1
blt zero, a5, .LOOP_HEIGHT_FRAME_SUBSAMPLE_LUMA_LASX
endfunc


.macro HANDLING_MANTISSA_MC_A2_3 size, dst0, dst1, dst2, count, dst_adress, \
dst_stride, index0, index1, index2, terminate_lable
addi.d \count, \count, -1
Expand Down
2 changes: 0 additions & 2 deletions source/common/loongarch64/pixel.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,6 @@ void x265_addAvg_64x64_lasx(const int16_t* src0, const int16_t* src1, pixel* dst

void x265_frame_init_lowres_core_lsx(const pixel *src0, pixel *dst0, pixel *dsth, pixel *dstv, pixel *dstc, intptr_t src_stride, intptr_t dst_stride, int width, int height);
void x265_frame_init_lowres_core_lasx(const pixel *src0, pixel *dst0, pixel *dsth, pixel *dstv, pixel *dstc, intptr_t src_stride, intptr_t dst_stride, int width, int height);
void x265_frame_subsample_luma_lsx(const pixel *src0, pixel *dst0, intptr_t src_stride, intptr_t dst_stride, int width, int height);
void x265_frame_subsample_luma_lasx(const pixel *src0, pixel *dst0, intptr_t src_stride, intptr_t dst_stride, int width, int height);
void x265_cutree_fix8_unpack_lsx(double *dst, uint16_t *src, int count);
void x265_cutree_fix8_unpack_lasx(double *dst, uint16_t *src, int count);
void x265_cutree_fix8_pack_lsx(uint16_t *dst, double *src, int count);
Expand Down
39 changes: 18 additions & 21 deletions source/common/lowres.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
#include "lowres.h"
#include "mv.h"

namespace X265_NS {
// forward declaration - defined in pixel.cpp, no SIMD override for MCSTF path
void frameInitLowresCoreMCSTF(const pixel* src0, pixel* dst0, pixel* dsth, pixel* dstv, pixel* dstc,
intptr_t src_stride, intptr_t dst_stride, int width, int height);
}

using namespace X265_NS;

/*
Expand Down Expand Up @@ -194,11 +200,6 @@ bool Lowres::create(x265_param* param, PicYuv *origPic, uint32_t qgSize)
}
}

for (int i = 0; i < 4; i++)
{
CHECKED_MALLOC(lowresMcstfMvs[0][i], MV, cuCount);
}

for (int i = 0; i < bframes + 2; i++)
{
CHECKED_MALLOC(lowresMvs[0][i], MV, cuCount);
Expand Down Expand Up @@ -287,10 +288,6 @@ void Lowres::destroy(x265_param* param)
}
}

for (int i = 0; i < 4; i++)
{
X265_FREE(lowresMcstfMvs[0][i]);
}
X265_FREE(qpAqOffset);
X265_FREE(invQscaleFactor);
X265_FREE(qpCuTreeOffset);
Expand Down Expand Up @@ -344,11 +341,13 @@ void Lowres::destroy(x265_param* param)
}
}
// (re) initialize lowres state
void Lowres::init(PicYuv *origPic, int poc)
void Lowres::init(PicYuv* origPic, int poc, bool bEnableTemporalFilter)
{
bLastMiniGopBFrame = false;
bKeyframe = false; // Not a keyframe unless identified by lookahead
bIsFadeEnd = false;
filterThisGOP = false;
noiseScore = -1;
frameNum = poc;
leadingBframes = 0;
indB = 0;
Expand All @@ -368,21 +367,17 @@ void Lowres::init(PicYuv *origPic, int poc)
lowresMvs[1][i][0].x = 0x7FFF;
}

for (int i = 0; i < 4; i++)
{
lowresMcstfMvs[0][i][0].x = 0x7FFF;
}

for (int i = 0; i < bframes + 2; i++)
intraMbs[i] = 0;
if (origPic->m_param->rc.vbvBufferSize)
for (int i = 0; i < X265_LOOKAHEAD_MAX + 1; i++)
plannedType[i] = X265_TYPE_AUTO;

/* downscale and generate 4 hpel planes for lookahead */
primitives.frameInitLowres(origPic->m_picOrg[0],
lowresPlane[0], lowresPlane[1], lowresPlane[2], lowresPlane[3],
origPic->m_stride, lumaStride, width, lines);
if (bEnableTemporalFilter)
frameInitLowresCoreMCSTF(origPic->m_picOrg[0], lowresPlane[0], lowresPlane[1], lowresPlane[2], lowresPlane[3], origPic->m_stride, lumaStride, width, lines);
else
primitives.frameInitLowres(origPic->m_picOrg[0], lowresPlane[0], lowresPlane[1], lowresPlane[2], lowresPlane[3], origPic->m_stride, lumaStride, width, lines);

/* extend hpel planes for motion search */
extendPicBorder(lowresPlane[0], lumaStride, width, lines, origPic->m_lumaMarginX, origPic->m_lumaMarginY);
Expand All @@ -392,9 +387,11 @@ void Lowres::init(PicYuv *origPic, int poc)

if (origPic->m_param->bEnableHME || origPic->m_param->bEnableTemporalFilter)
{
primitives.frameInitLowerRes(lowresPlane[0],
lowerResPlane[0], lowerResPlane[1], lowerResPlane[2], lowerResPlane[3],
lumaStride, lumaStride/2, (width / 2), (lines / 2));
if (bEnableTemporalFilter)
frameInitLowresCoreMCSTF(lowresPlane[0], lowerResPlane[0], lowerResPlane[1], lowerResPlane[2], lowerResPlane[3], lumaStride, lumaStride / 2, (width / 2), (lines / 2));
else
primitives.frameInitLowerRes(lowresPlane[0], lowerResPlane[0], lowerResPlane[1], lowerResPlane[2], lowerResPlane[3], lumaStride, lumaStride / 2, (width / 2), (lines / 2));

extendPicBorder(lowerResPlane[0], lumaStride/2, width/2, lines/2, origPic->m_lumaMarginX/2, origPic->m_lumaMarginY/2);
extendPicBorder(lowerResPlane[1], lumaStride/2, width/2, lines/2, origPic->m_lumaMarginX/2, origPic->m_lumaMarginY/2);
extendPicBorder(lowerResPlane[2], lumaStride/2, width/2, lines/2, origPic->m_lumaMarginX/2, origPic->m_lumaMarginY/2);
Expand Down
5 changes: 3 additions & 2 deletions source/common/lowres.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ struct Lowres : public ReferencePlanes
bool bKeyframe;
bool bLastMiniGopBFrame;
bool bIsFadeEnd;
bool filterThisGOP; // Set by lookahead noise gate: true = GOP start was noisy, apply MCSTF
int32_t noiseScore; // Raw noise score from estimateNoise() at GOP start; -1 for non-GOP-start frames

double ipCostRatio;

Expand All @@ -192,7 +194,6 @@ struct Lowres : public ReferencePlanes
uint16_t* lowresCosts[X265_BFRAME_MAX + 2][X265_BFRAME_MAX + 2];
int32_t* lowresMvCosts[2][X265_BFRAME_MAX + 2];
MV* lowresMvs[2][X265_BFRAME_MAX + 2];
MV* lowresMcstfMvs[2][4];
uint32_t maxBlocksInRow;
uint32_t maxBlocksInCol;
uint32_t maxBlocksInRowFullRes;
Expand Down Expand Up @@ -253,7 +254,7 @@ struct Lowres : public ReferencePlanes

bool create(x265_param* param, PicYuv *origPic, uint32_t qgSize);
void destroy(x265_param* param);
void init(PicYuv *origPic, int poc);
void init(PicYuv* origPic, int poc, bool bEnableTemporalFilter);
};
}

Expand Down
Loading
Loading