From e2e977b6c74cb7fb30540a5a392001565c84adc7 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 9 Jan 2019 19:31:49 +0530 Subject: [PATCH 01/93] Add crf ratecontrol support Add new rate control mode-crf that takes the cli -rc 2 and -crf value in [0-51] --- CMakeLists.txt | 4 +- Source/API/EbApi.h | 1 + Source/App/EbAppConfig.c | 4 + Source/App/EbAppConfig.h | 1 + Source/App/EbAppContext.c | 1 + Source/Lib/Codec/EbDefinitions.h | 5 + Source/Lib/Codec/EbEncDecProcess.c | 2 +- Source/Lib/Codec/EbEncHandle.c | 12 +- Source/Lib/Codec/EbEntropyCoding.c | 2 +- .../Lib/Codec/EbInitialRateControlProcess.c | 4 +- Source/Lib/Codec/EbPacketizationProcess.c | 2 +- Source/Lib/Codec/EbPictureControlSet.c | 1 + Source/Lib/Codec/EbPictureControlSet.h | 9 +- Source/Lib/Codec/EbPictureManagerProcess.c | 4 + Source/Lib/Codec/EbRateControlProcess.c | 121 +++++++++++++++++- Source/Lib/Codec/EbRateControlProcess.h | 9 ++ .../Lib/Codec/EbResourceCoordinationProcess.c | 2 +- Source/Lib/Codec/EbUtility.h | 2 +- 18 files changed, 169 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9132b353..a10191a86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,8 +57,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_ASM_NASM_FLAGS "-DUNIX64") set(CMAKE_ASM_NASM_FLAGS_DEBUG "-g null") set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64) - set(CMAKE_EXE_LINKER_FLAGS "-z noexecstack -z relro -z now -pie ") - set(CMAKE_SHARED_LINKER_FLAGS "-z noexecstack -z relro -z now -pie ") + set(CMAKE_EXE_LINKER_FLAGS "-z noexecstack -z relro -z now -pie -lm") + set(CMAKE_SHARED_LINKER_FLAGS "-z noexecstack -z relro -z now -pie -lm") endif() if(CMAKE_SYSTEM_NAME STREQUAL "Windows") diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index 5798126d6..fe5ec38a2 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -191,6 +191,7 @@ extern "C" { uint32_t targetBitRate; uint32_t maxQpAllowed; uint32_t minQpAllowed; + uint32_t crf; // bitstream options uint8_t codeVpsSpsPps; diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index df7c4901f..4c50435ea 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -84,6 +84,7 @@ #define RECOVERY_POINT_TOKEN "-recovery-point" // no Eval #define RATE_CONTROL_ENABLE_TOKEN "-rc" #define TARGET_BIT_RATE_TOKEN "-tbr" +#define CRF_TOKEN "-crf" #define MAX_QP_TOKEN "-max-qp" #define MIN_QP_TOKEN "-min-qp" #define TEMPORAL_ID "-temporal-id" // no Eval @@ -171,6 +172,7 @@ static void SetCfgIntraRefreshType (const char *value, EbConfig_t * static void SetHierarchicalLevels (const char *value, EbConfig_t *cfg) { cfg->hierarchicalLevels = strtol(value, NULL, 0); }; static void SetCfgPredStructure (const char *value, EbConfig_t *cfg) { cfg->predStructure = strtol(value, NULL, 0); }; static void SetCfgQp (const char *value, EbConfig_t *cfg) {cfg->qp = strtoul(value, NULL, 0);}; +static void SetCfgCrf (const char *value, EbConfig_t *cfg) {cfg->crf = strtoul(value, NULL, 0); }; static void SetCfgUseQpFile (const char *value, EbConfig_t *cfg) {cfg->useQpFile = (EB_BOOL)strtol(value, NULL, 0); }; static void SetDisableDlfFlag (const char *value, EbConfig_t *cfg) {cfg->disableDlfFlag = (EB_BOOL)strtoul(value, NULL, 0);}; static void SetEnableSaoFlag (const char *value, EbConfig_t *cfg) {cfg->enableSaoFlag = (EB_BOOL)strtoul(value, NULL, 0);}; @@ -295,6 +297,7 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, RATE_CONTROL_ENABLE_TOKEN, "RateControlMode", SetRateControlMode }, { SINGLE_INPUT, LOOK_AHEAD_DIST_TOKEN, "LookAheadDistance", SetLookAheadDistance}, { SINGLE_INPUT, TARGET_BIT_RATE_TOKEN, "TargetBitRate", SetTargetBitRate }, + { SINGLE_INPUT, CRF_TOKEN, "CRF", SetCfgCrf }, { SINGLE_INPUT, MAX_QP_TOKEN, "MaxQpAllowed", SetMaxQpAllowed }, { SINGLE_INPUT, MIN_QP_TOKEN, "MinQpAllowed", SetMinQpAllowed }, @@ -410,6 +413,7 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->maxQpAllowed = 48; configPtr->minQpAllowed = 10; configPtr->baseLayerSwitchMode = 0; + configPtr->crf = 28; configPtr->encMode = 9; configPtr->intraPeriod = -2; configPtr->intraRefreshType = 1; diff --git a/Source/App/EbAppConfig.h b/Source/App/EbAppConfig.h index df1540220..fbaca0778 100644 --- a/Source/App/EbAppConfig.h +++ b/Source/App/EbAppConfig.h @@ -327,6 +327,7 @@ typedef struct EbConfig_s uint32_t targetBitRate; uint32_t maxQpAllowed; uint32_t minQpAllowed; + uint32_t crf; /**************************************** * TUNE diff --git a/Source/App/EbAppContext.c b/Source/App/EbAppContext.c index 2e2d4d4e7..0b8d1b27b 100644 --- a/Source/App/EbAppContext.c +++ b/Source/App/EbAppContext.c @@ -181,6 +181,7 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.maxQpAllowed = config->maxQpAllowed; callbackData->ebEncParameters.minQpAllowed = config->minQpAllowed; callbackData->ebEncParameters.qp = config->qp; + callbackData->ebEncParameters.crf = config->crf; callbackData->ebEncParameters.useQpFile = (EB_BOOL)config->useQpFile; callbackData->ebEncParameters.disableDlfFlag = (EB_BOOL)config->disableDlfFlag; callbackData->ebEncParameters.enableSaoFlag = (EB_BOOL)config->enableSaoFlag; diff --git a/Source/Lib/Codec/EbDefinitions.h b/Source/Lib/Codec/EbDefinitions.h index 92057de0f..c9bf388a9 100644 --- a/Source/Lib/Codec/EbDefinitions.h +++ b/Source/Lib/Codec/EbDefinitions.h @@ -1012,6 +1012,11 @@ typedef enum EB_SEI { #define MIN_QP_VALUE 0 #define MAX_QP_VALUE 51 #define MAX_CHROMA_MAP_QP_VALUE 57 +#define BASE_FRAME_DURATION 0.04 +#define MIN_FRAME_DURATION 0.01 +#define MAX_FRAME_DURATION 1.00 +#define SAD_SATD_CONSTANT 5 + //***Transforms*** diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 0ad55346c..34ca5e77a 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -1391,7 +1391,7 @@ static void EncDecConfigureLcu( { //RC is off - if (sequenceControlSetPtr->staticConfig.rateControlMode == 0 && sequenceControlSetPtr->staticConfig.improveSharpness == 0 && sequenceControlSetPtr->staticConfig.bitRateReduction == 0) { + if ((sequenceControlSetPtr->staticConfig.rateControlMode != 1) && sequenceControlSetPtr->staticConfig.improveSharpness == 0 && sequenceControlSetPtr->staticConfig.bitRateReduction == 0) { contextPtr->qp = pictureQp; } //RC is on diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index d26c4ed8b..f6d6aa53d 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2064,6 +2064,7 @@ void CopyApiFromApp( // Rate Control sequenceControlSetPtr->staticConfig.sceneChangeDetection = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->sceneChangeDetection; sequenceControlSetPtr->staticConfig.rateControlMode = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->rateControlMode; + sequenceControlSetPtr->staticConfig.crf = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->crf; sequenceControlSetPtr->staticConfig.lookAheadDistance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->lookAheadDistance; sequenceControlSetPtr->staticConfig.framesToBeEncoded = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->framesToBeEncoded; @@ -2566,11 +2567,14 @@ static EB_ERRORTYPE VerifySettings(\ SVT_LOG("Error Instance %u: The constrained intra must be [0 - 1] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->rateControlMode > 1) { - SVT_LOG("Error Instance %u: The rate control mode must be [0 - 1] \n", channelNumber + 1); + if (config->rateControlMode > 2) { + SVT_LOG("Error Instance %u: The rate control mode must be [0 - 2] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - + if ((config->rateControlMode == 2) && (config->crf > 51)) + { + SVT_LOG("Error instance %u:The crf value must be [0-51] \n", channelNumber + 1); + } if (config->tune > 0 && config->bitRateReduction == 1){ SVT_LOG("Error Instance %u: Bit Rate Reduction is not supported for OQ mode (Tune = 1 ) and VMAF mode (Tune = 2)\n", channelNumber + 1); return_error = EB_ErrorBadParameter; @@ -2855,6 +2859,8 @@ static void PrintLibParams( SVT_LOG("\nSVT [config]: HierarchicalLevels / BaseLayerSwitchMode / PredStructure\t\t: %d / %d / %d ", config->hierarchicalLevels, config->baseLayerSwitchMode, config->predStructure); if (config->rateControlMode == 1) SVT_LOG("\nSVT [config]: RCMode / TargetBitrate / LookaheadDistance / SceneChange\t\t: VBR / %d / %d / %d ", config->targetBitRate, config->lookAheadDistance, config->sceneChangeDetection); + else if(config->rateControlMode == 2) + SVT_LOG("\nSVT [config]: RCMode / TargetQuality / LookaheadDistance / SceneChange\t\t: CRF / %d / %d / %d ", config->crf, config->lookAheadDistance, config->sceneChangeDetection); else SVT_LOG("\nSVT [config]: BRC Mode / QP / LookaheadDistance / SceneChange\t\t\t: CQP / %d / %d / %d ", config->qp, config->lookAheadDistance, config->sceneChangeDetection); diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index a996c1c15..5438d0fa2 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -4916,7 +4916,7 @@ EB_ERRORTYPE ComputeProfileTierLevelInfo( scsPtr->profileIdc = scsPtr->staticConfig.profile; - if (scsPtr->staticConfig.rateControlMode == 0){ + if (scsPtr->staticConfig.rateControlMode != 1){ // level calculation if ((lumaSampleRate <= maxLumaSampleRate[0]) && (lumaPictureSize <= maxLumaPictureSize[0]) && (lumaWidthSquare <= maxLumaPictureSize[0] * 8) && (lumaHeightSquare <= maxLumaPictureSize[0] * 8)) scsPtr->levelIdc = 30; //1*30 diff --git a/Source/Lib/Codec/EbInitialRateControlProcess.c b/Source/Lib/Codec/EbInitialRateControlProcess.c index 2a4695997..9f13d637a 100644 --- a/Source/Lib/Codec/EbInitialRateControlProcess.c +++ b/Source/Lib/Codec/EbInitialRateControlProcess.c @@ -952,7 +952,7 @@ void* InitialRateControlKernel(void *inputPtr) pictureControlSetPtr, inputResultsPtr); - if (sequenceControlSetPtr->staticConfig.rateControlMode) + if (sequenceControlSetPtr->staticConfig.rateControlMode == 1) { if (sequenceControlSetPtr->staticConfig.lookAheadDistance != 0){ @@ -1039,7 +1039,7 @@ void* InitialRateControlKernel(void *inputPtr) else pictureControlSetPtr->endOfSequenceRegion = EB_FALSE; - if (sequenceControlSetPtr->staticConfig.rateControlMode) + if (sequenceControlSetPtr->staticConfig.rateControlMode == 1) { // Determine offset from the Head Ptr for HLRC histogram queue and set the life count if (sequenceControlSetPtr->staticConfig.lookAheadDistance != 0){ diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index baa562e03..bc6de1ee2 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -203,7 +203,7 @@ void* PacketizationKernel(void *inputPtr) lcuTotalCount = pictureControlSetPtr->lcuTotalCount; // LCU Loop - if (sequenceControlSetPtr->staticConfig.rateControlMode > 0){ + if (sequenceControlSetPtr->staticConfig.rateControlMode == 1){ EB_U64 sadBits[NUMBER_OF_SAD_INTERVALS]= {0}; EB_U32 count[NUMBER_OF_SAD_INTERVALS] = {0}; diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index 383f33253..388cc4db4 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -649,6 +649,7 @@ EB_ERRORTYPE PictureParentControlSetCtor( EB_U32 cuIdx; for (cuIdx = 0; cuIdx < 21; ++cuIdx){ + contigousCand[cuIdx*maxOisCand] = (OisCandidate_t) { 0 }; objectPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[cuIdx] = &contigousCand[cuIdx*maxOisCand]; } } diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index 3cec0ef99..8985b468b 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -147,8 +147,11 @@ typedef struct PictureControlSet_s EbObjectWrapper_t *refPicPtrArray[MAX_NUM_OF_REF_PIC_LIST]; EB_U8 refPicQpArray[MAX_NUM_OF_REF_PIC_LIST]; - EB_PICTURE refSliceTypeArray[MAX_NUM_OF_REF_PIC_LIST]; - + + EB_PICTURE refSliceTypeArray[MAX_NUM_OF_REF_PIC_LIST]; + + EB_U8 refPicTemporalLayerArray[MAX_NUM_OF_REF_PIC_LIST]; + // GOP EB_U64 pictureNumber; EB_U8 temporalLayerIndex; @@ -182,6 +185,7 @@ typedef struct PictureControlSet_s EB_U8 pictureQp; EB_U8 difCuDeltaQpDepth; EB_U8 useDeltaQp; + EB_U64 sadCost; // LCU Array EB_U8 lcuMaxDepth; @@ -381,6 +385,7 @@ typedef struct PictureParentControlSet_s EB_BOOL tablesUpdated; EB_BOOL percentageUpdated; EB_U32 targetBitRate; + EB_U32 crf; EB_BOOL minTargetRateAssigned; EB_U32 frameRate; EB_BOOL frameRateIsUpdated; diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index 30fe2e101..98ca66f9b 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -725,6 +725,8 @@ void* PictureManagerKernel(void *inputPtr) EB_MEMSET(ChildPictureControlSetPtr->refPicQpArray, 0, 2 * sizeof(EB_U8)); EB_MEMSET(ChildPictureControlSetPtr->refSliceTypeArray, 0, 2 * sizeof(EB_PICTURE)); + + EB_MEMSET(ChildPictureControlSetPtr->refPicTemporalLayerArray, 0, 2 * sizeof(EB_U8)); // Configure List0 if ((entryPictureControlSetPtr->sliceType == EB_P_PICTURE) || (entryPictureControlSetPtr->sliceType == EB_B_PICTURE)) { @@ -741,6 +743,7 @@ void* PictureManagerKernel(void *inputPtr) ChildPictureControlSetPtr->refPicQpArray[REF_LIST_0] = ((EbReferenceObject_t*) referenceEntryPtr->referenceObjectPtr->objectPtr)->qp; ChildPictureControlSetPtr->refSliceTypeArray[REF_LIST_0] = ((EbReferenceObject_t*) referenceEntryPtr->referenceObjectPtr->objectPtr)->sliceType; + ChildPictureControlSetPtr->refPicTemporalLayerArray[REF_LIST_0] = ((EbReferenceObject_t*)referenceEntryPtr->referenceObjectPtr->objectPtr)->tmpLayerIdx; // Increment the Reference's liveCount by the number of tiles in the input picture EbObjectIncLiveCount( @@ -773,6 +776,7 @@ void* PictureManagerKernel(void *inputPtr) ChildPictureControlSetPtr->refPicQpArray[REF_LIST_1] = ((EbReferenceObject_t*) referenceEntryPtr->referenceObjectPtr->objectPtr)->qp; ChildPictureControlSetPtr->refSliceTypeArray[REF_LIST_1] = ((EbReferenceObject_t*) referenceEntryPtr->referenceObjectPtr->objectPtr)->sliceType; + ChildPictureControlSetPtr->refPicTemporalLayerArray[REF_LIST_1] = ((EbReferenceObject_t*)referenceEntryPtr->referenceObjectPtr->objectPtr)->tmpLayerIdx; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index f91dd2842..305622fb6 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -4,6 +4,7 @@ */ #include +#include #include "EbDefinitions.h" #include "EbRateControlProcess.h" @@ -347,9 +348,23 @@ EB_ERRORTYPE RateControlContextCtor( contextPtr->extraBitsGen = 0; contextPtr->maxRateAdjustDeltaQP = 0; + contextPtr->shorttermComplexSum = 0; + contextPtr->shorttermComplexCount = 0; + contextPtr->qcompress = 0.6; return EB_ErrorNone; } + +double qp2qScale(double qp) +{ + return 0.85 * pow(2.0, (qp - 12.0) / 6.0); +} + +double qScale2qp(double qScale) +{ + return 12.0 + 6.0 * (double)LOG2(qScale / 0.85); +} + void HighLevelRcInputPictureMode2( PictureParentControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr, @@ -928,6 +943,86 @@ void HighLevelRcInputPictureMode2( } EbReleaseMutex(sequenceControlSetPtr->encodeContextPtr->rateTableUpdateMutex); } +void FrameLevelRcInputPictureMode3( + PictureControlSet_t *pictureControlSetPtr, + SequenceControlSet_t *sequenceControlSetPtr, + RateControlContext_t *contextPtr, + EB_U32 bestOisCuIndex) + { + + double q = 0; + EB_U16 lcuTotalCount = pictureControlSetPtr->lcuTotalCount; + pictureControlSetPtr->sadCost = 0; + // Calculate the sad cost from the rcMEDistortion and OISDistortion by looping over the LCUs + if (pictureControlSetPtr->sliceType == EB_I_PICTURE) + { + for (EB_U16 lcuIndex = 0; lcuIndex < lcuTotalCount; lcuIndex++) + + pictureControlSetPtr->sadCost += pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[1][bestOisCuIndex].distortion + + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[2][bestOisCuIndex].distortion + + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[3][bestOisCuIndex].distortion + + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[4][bestOisCuIndex].distortion; + } + else + { + for (EB_U16 lcuIndex = 0; lcuIndex < lcuTotalCount; lcuIndex++) + pictureControlSetPtr->sadCost += pictureControlSetPtr->ParentPcsPtr->rcMEdistortion[lcuIndex]; + } + + pictureControlSetPtr->sadCost /= SAD_SATD_CONSTANT; + + if (pictureControlSetPtr->temporalLayerIndex > 1 && pictureControlSetPtr->sliceType != EB_I_PICTURE) + { + //Take the average of reference frame's QP and tune with Offset + double q0, q1; + EB_BOOL i0, i1; + EB_U8 d0, d1; + i0 = pictureControlSetPtr->refSliceTypeArray[0] == EB_I_PICTURE; + q0 = i0 ? pictureControlSetPtr->refPicQpArray[0] : (pictureControlSetPtr->refPicQpArray[0] - MOD_QP_OFFSET_LAYER_ARRAY[pictureControlSetPtr->ParentPcsPtr->hierarchicalLevels][pictureControlSetPtr->refPicTemporalLayerArray[0]]); + if (pictureControlSetPtr->sliceType == EB_P_PICTURE) + q = q0; + else + { + i1 = pictureControlSetPtr->refSliceTypeArray[1] == EB_I_PICTURE; + q1 = i1 ? pictureControlSetPtr->refPicQpArray[1] : (pictureControlSetPtr->refPicQpArray[1] - MOD_QP_OFFSET_LAYER_ARRAY[pictureControlSetPtr->ParentPcsPtr->hierarchicalLevels][pictureControlSetPtr->refPicTemporalLayerArray[1]]); + d0 = (EB_U8)ABS(((EB_S64)pictureControlSetPtr->pictureNumber - (EB_S64)pictureControlSetPtr->ParentPcsPtr->refPicPocArray[0])); + d1 = (EB_U8)ABS(((EB_S64)pictureControlSetPtr->pictureNumber - (EB_S64)pictureControlSetPtr->ParentPcsPtr->refPicPocArray[1])); + + if (i0&&i1) + q = (q0 + q1) / 2.0; + else if (i0) + q = q1; + else if (i1) + q = q0; + else + q = (q0*d1 + q1 * d0) / (d0 + d1); + } + q += MOD_QP_OFFSET_LAYER_ARRAY[pictureControlSetPtr->ParentPcsPtr->hierarchicalLevels][pictureControlSetPtr->temporalLayerIndex]; + } + else + { + double blurredComplexity; + + /*Calculate the blurred Complexity of the Frame */ + contextPtr->shorttermComplexSum *= 0.5; + contextPtr->shorttermComplexCount *= 0.5; + contextPtr->shorttermComplexSum += pictureControlSetPtr->sadCost / (contextPtr->frameDuration / BASE_FRAME_DURATION); + contextPtr->shorttermComplexCount++; + blurredComplexity = contextPtr->shorttermComplexSum / contextPtr->shorttermComplexCount; + q = pow(blurredComplexity, 1 - contextPtr->qcompress); + q /= contextPtr->rateFactorConstant; + if (pictureControlSetPtr->sliceType != EB_I_PICTURE) + q = qScale2qp(q) + MOD_QP_OFFSET_LAYER_ARRAY[pictureControlSetPtr->ParentPcsPtr->hierarchicalLevels][pictureControlSetPtr->temporalLayerIndex]; + else + q = qScale2qp(q); + } + if (pictureControlSetPtr->pictureNumber == 0) + pictureControlSetPtr->pictureQp = (EB_U8)contextPtr->crf; + else + pictureControlSetPtr->pictureQp = (EB_U8)(q + 0.5); + pictureControlSetPtr->pictureQp = (EB_U8)CLIP3((EB_U8)sequenceControlSetPtr->staticConfig.minQpAllowed,(EB_U8)sequenceControlSetPtr->staticConfig.maxQpAllowed, pictureControlSetPtr->pictureQp); +} + void FrameLevelRcInputPictureMode2( PictureControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr, @@ -2273,8 +2368,19 @@ void* RateControlKernel(void *inputPtr) contextPtr->vbFillThreshold2 = (contextPtr->virtualBufferSize << 3) >> 3; contextPtr->baseLayerFramesAvgQp = sequenceControlSetPtr->qp; contextPtr->baseLayerIntraFramesAvgQp = sequenceControlSetPtr->qp; + contextPtr->crf = sequenceControlSetPtr->staticConfig.crf; + + if (sequenceControlSetPtr->staticConfig.rateControlMode == 2) + { + int bFrames = pictureControlSetPtr->ParentPcsPtr->predStructure == EB_PRED_LOW_DELAY_P ? 80 : 120; + double baseCplx = pictureControlSetPtr->lcuTotalCount * bFrames * 16; + double frameDuration = 1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION); + contextPtr->rateFactorConstant = pow(baseCplx, 1 - contextPtr->qcompress) / qp2qScale(contextPtr->crf); + contextPtr->frameDuration = CLIP3(MIN_FRAME_DURATION, MAX_FRAME_DURATION, frameDuration); + + } } - if (sequenceControlSetPtr->staticConfig.rateControlMode) + if (sequenceControlSetPtr->staticConfig.rateControlMode == 1) { pictureControlSetPtr->ParentPcsPtr->intraSelectedOrgQp = 0; HighLevelRcInputPictureMode2( @@ -2284,7 +2390,6 @@ void* RateControlKernel(void *inputPtr) contextPtr, contextPtr->highLevelRateControlPtr); - } // Frame level RC @@ -2350,6 +2455,16 @@ void* RateControlKernel(void *inputPtr) pictureControlSetPtr->pictureQp = pictureControlSetPtr->pictureQp; } + else if (sequenceControlSetPtr->staticConfig.rateControlMode == 2) + + { + FrameLevelRcInputPictureMode3( + pictureControlSetPtr, + sequenceControlSetPtr, + contextPtr, + bestOisCuIndex); + + } else{ FrameLevelRcInputPictureMode2( pictureControlSetPtr, @@ -2553,7 +2668,7 @@ void* RateControlKernel(void *inputPtr) contextPtr->rateControlParamQueue[intervalIndexTemp - 1]; } - if (sequenceControlSetPtr->staticConfig.rateControlMode != 0){ + if (sequenceControlSetPtr->staticConfig.rateControlMode == 1){ contextPtr->previousVirtualBufferLevel = contextPtr->virtualBufferLevel; diff --git a/Source/Lib/Codec/EbRateControlProcess.h b/Source/Lib/Codec/EbRateControlProcess.h index 94b0ca042..a660c3c60 100644 --- a/Source/Lib/Codec/EbRateControlProcess.h +++ b/Source/Lib/Codec/EbRateControlProcess.h @@ -244,6 +244,15 @@ typedef struct RateControlContext_s EB_U64 frameRate; + //CRF + EB_U32 crf; + double shorttermComplexSum; + double shorttermComplexCount; + double rateFactorConstant; + double qcompress; + double frameDuration; + + EB_U64 virtualBufferSize; EB_S64 virtualBufferLevelInitialValue; diff --git a/Source/Lib/Codec/EbResourceCoordinationProcess.c b/Source/Lib/Codec/EbResourceCoordinationProcess.c index 976aede28..4e862c55a 100644 --- a/Source/Lib/Codec/EbResourceCoordinationProcess.c +++ b/Source/Lib/Codec/EbResourceCoordinationProcess.c @@ -728,7 +728,7 @@ void* ResourceCoordinationKernel(void *inputPtr) // Rate Control // Set the ME Distortion and OIS Historgrams to zero - if (sequenceControlSetPtr->staticConfig.rateControlMode){ + if (sequenceControlSetPtr->staticConfig.rateControlMode==1){ EB_MEMSET(pictureControlSetPtr->meDistortionHistogram, 0, NUMBER_OF_SAD_INTERVALS*sizeof(EB_U16)); EB_MEMSET(pictureControlSetPtr->oisDistortionHistogram, 0, NUMBER_OF_INTRA_SAD_INTERVALS*sizeof(EB_U16)); } diff --git a/Source/Lib/Codec/EbUtility.h b/Source/Lib/Codec/EbUtility.h index 8d9b0abc7..5702706e3 100644 --- a/Source/Lib/Codec/EbUtility.h +++ b/Source/Lib/Codec/EbUtility.h @@ -108,7 +108,7 @@ extern EB_U32 EndianSwap(EB_U32 ui); #define POW2_CHECK(x) ((x) == ((x) & (-((EB_S32)(x))))) #define ROUND_UP_MUL_8(x) ((x) + ((8 - ((x) & 0x7)) & 0x7)) #define ROUND_UP_MULT(x,mult) ((x) + (((mult) - ((x) & ((mult)-1))) & ((mult)-1))) - +#define LOG2(x) (log((double)(x)) * 1.4426950408889640513713538072172) // rounds down to the next power of two #define FLOOR_POW2(x) \ MULTI_LINE_MACRO_BEGIN \ From 808457fb13f737ba6bc5ba6120b36fd1b87c86c3 Mon Sep 17 00:00:00 2001 From: Bhavna Hariharan Date: Tue, 11 Dec 2018 15:57:07 -0800 Subject: [PATCH 02/93] Add CLI support for vbv --- Source/API/EbApi.h | 2 ++ Source/App/EbAppConfig.c | 8 ++++++++ Source/App/EbAppConfig.h | 2 ++ Source/App/EbAppContext.c | 2 ++ 4 files changed, 14 insertions(+) diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index 6d3294d78..a1e234b0c 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -202,6 +202,8 @@ typedef struct EB_H265_ENC_CONFIGURATION uint32_t logicalProcessors; // number of logical processor to run on int32_t targetSocket; // target socket to run on uint8_t switchThreadsToRtPriority; // switch to real time mode + uint32_t vbvMaxrate; + uint32_t vbvBufsize; // ASM Type uint32_t asmType; // level of optimization to use. diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index 6a08a8969..f62e883fa 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -71,6 +71,8 @@ #define RECOVERY_POINT_TOKEN "-recovery-point" // no Eval #define RATE_CONTROL_ENABLE_TOKEN "-rc" #define TARGET_BIT_RATE_TOKEN "-tbr" +#define VBV_MAX_RATE_TOKEN "-vbv-maxrate" +#define VBV_BUFFER_SIZE_TOKEN "-vbv-bufsize" #define MAX_QP_TOKEN "-max-qp" #define MIN_QP_TOKEN "-min-qp" #define TEMPORAL_ID "-temporal-id" // no Eval @@ -177,6 +179,8 @@ static void SetEnableConstrainedIntra (const char *value, EbConfig_t * static void SetCfgTune (const char *value, EbConfig_t *cfg) {cfg->tune = (uint8_t)strtoul(value, NULL, 0); }; static void SetBitRateReduction (const char *value, EbConfig_t *cfg) {cfg->bitRateReduction = (EB_BOOL)strtol(value, NULL, 0); }; static void SetImproveSharpness (const char *value, EbConfig_t *cfg) {cfg->improveSharpness = (EB_BOOL)strtol(value, NULL, 0);}; +static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0); }; +static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0); }; static void SetVideoUsabilityInfo (const char *value, EbConfig_t *cfg) {cfg->videoUsabilityInfo = strtol(value, NULL, 0);}; static void SetHighDynamicRangeInput (const char *value, EbConfig_t *cfg) {cfg->highDynamicRangeInput = strtol(value, NULL, 0);}; static void SetAccessUnitDelimiter (const char *value, EbConfig_t *cfg) {cfg->accessUnitDelimiter = strtol(value, NULL, 0);}; @@ -275,6 +279,8 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, TARGET_BIT_RATE_TOKEN, "TargetBitRate", SetTargetBitRate }, { SINGLE_INPUT, MAX_QP_TOKEN, "MaxQpAllowed", SetMaxQpAllowed }, { SINGLE_INPUT, MIN_QP_TOKEN, "MinQpAllowed", SetMinQpAllowed }, + { SINGLE_INPUT, VBV_MAX_RATE_TOKEN, "vbvMaxRate", SetVbvMaxrate }, + { SINGLE_INPUT, VBV_BUFFER_SIZE_TOKEN, "vbvBufsize", SetVbvBufsize }, // DLF { SINGLE_INPUT, LOOP_FILTER_DISABLE_TOKEN, "LoopFilterDisable", SetDisableDlfFlag }, @@ -375,6 +381,8 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->minQpAllowed = 10; configPtr->baseLayerSwitchMode = 0; configPtr->encMode = 9; + configPtr->vbvMaxRate = 0; + configPtr->vbvBufsize = 0; configPtr->intraPeriod = -2; configPtr->intraRefreshType = 1; configPtr->hierarchicalLevels = 3; diff --git a/Source/App/EbAppConfig.h b/Source/App/EbAppConfig.h index d04336cc2..019981b7e 100644 --- a/Source/App/EbAppConfig.h +++ b/Source/App/EbAppConfig.h @@ -304,6 +304,8 @@ typedef struct EbConfig_s uint32_t targetBitRate; uint32_t maxQpAllowed; uint32_t minQpAllowed; + uint32_t vbvMaxRate; + uint32_t vbvBufsize; /**************************************** * TUNE diff --git a/Source/App/EbAppContext.c b/Source/App/EbAppContext.c index d22b99e2f..6ae09d40c 100644 --- a/Source/App/EbAppContext.c +++ b/Source/App/EbAppContext.c @@ -180,6 +180,8 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.maxQpAllowed = config->maxQpAllowed; callbackData->ebEncParameters.minQpAllowed = config->minQpAllowed; callbackData->ebEncParameters.qp = config->qp; + callbackData->ebEncParameters.vbvMaxrate = config->vbvMaxRate; + callbackData->ebEncParameters.vbvBufsize = config->vbvBufsize; callbackData->ebEncParameters.useQpFile = (EB_BOOL)config->useQpFile; callbackData->ebEncParameters.disableDlfFlag = (EB_BOOL)config->disableDlfFlag; callbackData->ebEncParameters.enableSaoFlag = (EB_BOOL)config->enableSaoFlag; From de9724914833ed2e8ca2cda6a320835a8fc5d029 Mon Sep 17 00:00:00 2001 From: Bhavna Hariharan Date: Tue, 18 Dec 2018 15:33:42 -0800 Subject: [PATCH 03/93] Enable vbv for abr mode --- Source/Lib/Codec/EbEncHandle.c | 2 + Source/Lib/Codec/EbRateControlProcess.c | 154 +++++++++++++++++++++++- Source/Lib/Codec/EbRateControlProcess.h | 5 + 3 files changed, 159 insertions(+), 2 deletions(-) diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index e90a5e406..4d4864241 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2059,6 +2059,8 @@ void CopyApiFromApp( // Rate Control sequenceControlSetPtr->staticConfig.sceneChangeDetection = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->sceneChangeDetection; sequenceControlSetPtr->staticConfig.rateControlMode = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->rateControlMode; + sequenceControlSetPtr->staticConfig.vbvMaxrate = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvMaxrate; + sequenceControlSetPtr->staticConfig.vbvBufsize = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufsize; sequenceControlSetPtr->staticConfig.lookAheadDistance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->lookAheadDistance; sequenceControlSetPtr->staticConfig.framesToBeEncoded = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->framesToBeEncoded; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index f91dd2842..f0b6e4f7a 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -348,8 +348,65 @@ EB_ERRORTYPE RateControlContextCtor( contextPtr->maxRateAdjustDeltaQP = 0; + contextPtr->bufferFill = 0; return EB_ErrorNone; } + +EB_U64 predictBits(SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp, EB_U32 qp) +{ + EB_U64 totalBits = 0; + if (hlRateControlHistogramPtrTemp->isCoded) { + // If the frame is already coded, use the actual number of bits + totalBits = hlRateControlHistogramPtrTemp->totalNumBitsCoded; + } + else { + RateControlTables_t *rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qp]; + EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[hlRateControlHistogramPtrTemp->temporalLayerIndex]; + EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; + EB_U32 predBitsRefQp = 0; + EB_U32 numOfFullLcus = 0; + EB_U32 areaInPixel = sequenceControlSetPtr->lumaWidth * sequenceControlSetPtr->lumaHeight; + + if (hlRateControlHistogramPtrTemp->sliceType == EB_I_PICTURE) { + // Loop over block in the frame and calculated the predicted bits at reg QP + EB_U32 i; + EB_U32 accum = 0; + for (i = 0; i < NUMBER_OF_INTRA_SAD_INTERVALS; ++i) + { + accum += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); + } + + predBitsRefQp = accum; + numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; + totalBits += predBitsRefQp; + } + else { + EB_U32 i; + EB_U32 accum = 0; + EB_U32 accumIntra = 0; + for (i = 0; i < NUMBER_OF_SAD_INTERVALS; ++i) + { + accum += (EB_U32)(hlRateControlHistogramPtrTemp->meDistortionHistogram[i] * sadBitsArrayPtr[i]); + accumIntra += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); + + } + if (accum > accumIntra * 3) + predBitsRefQp = accumIntra; + else + predBitsRefQp = accum; + numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; + totalBits += predBitsRefQp; + } + + // Scale for in complete LCSs + // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries + totalBits = hlRateControlHistogramPtrTemp->predBitsRefQp[qp] * (EB_U64)areaInPixel / (numOfFullLcus << 12); + } + hlRateControlHistogramPtrTemp->predBitsRefQp[qp] = totalBits; + return totalBits; +} + + void HighLevelRcInputPictureMode2( PictureParentControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr, @@ -397,6 +454,7 @@ void HighLevelRcInputPictureMode2( EB_Bit_Number *sadBitsArrayPtr; EB_Bit_Number *intraSadBitsArrayPtr; EB_U32 predBitsRefQp; + EB_U32 bestQp=0; for (temporalLayerIndex = 0; temporalLayerIndex< EB_MAX_TEMPORAL_LAYERS; temporalLayerIndex++){ pictureControlSetPtr->bitsPerSwPerLayer[temporalLayerIndex] = 0; @@ -412,6 +470,7 @@ void HighLevelRcInputPictureMode2( if (sequenceControlSetPtr->staticConfig.lookAheadDistance != 0){ + // Increamenting the head of the hlRateControlHistorgramQueue and clean up the entores hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]); while ((hlRateControlHistogramPtrTemp->lifeCount == 0) && hlRateControlHistogramPtrTemp->passedToHlrc){ @@ -545,6 +604,7 @@ void HighLevelRcInputPictureMode2( highLevelRateControlPtr->predBitsRefQpPerSw[refQpTableIndex] = 0; } + bestQp = qpSearchMin; bitConstraintPerSw = highLevelRateControlPtr->bitConstraintPerSw * pictureControlSetPtr->framesInSw / (sequenceControlSetPtr->staticConfig.lookAheadDistance + 1); // Update the target rate for the sliding window based on the status of RC @@ -593,7 +653,7 @@ void HighLevelRcInputPictureMode2( endOfSequenceFlag = EB_FALSE; while (!endOfSequenceFlag && - queueEntryIndexTemp <= queueEntryIndexHeadTemp + sequenceControlSetPtr->staticConfig.lookAheadDistance){ + queueEntryIndexTemp <= queueEntryIndexHeadTemp + sequenceControlSetPtr->staticConfig.lookAheadDistance){ //iterate through lookahead number of frames queueEntryIndexTemp2 = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[queueEntryIndexTemp2]); @@ -677,6 +737,7 @@ void HighLevelRcInputPictureMode2( } else{ bestQpFound = EB_TRUE; + bestQp = refQpIndex; } if (refQpTableIndex == previousSelectedRefQp){ @@ -692,6 +753,84 @@ void HighLevelRcInputPictureMode2( } } + if (contextPtr->vbvMaxrate && contextPtr->vbvBufsize) + { + /* Lookahead VBV: If lookahead is done, raise the quantizer as necessary + * such that no frames in the lookahead overflow and such that the buffer + * is in a reasonable state by the end of the lookahead. */ + EB_U32 loopTerminate = 0; + EB_U32 q = bestQp; + EB_U32 q0 = bestQp; + + + queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - encodeContextPtr->hlRateControlHistorgramQueue[encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); + queueEntryIndexHeadTemp += encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; + queueEntryIndexHeadTemp = (queueEntryIndexHeadTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? + queueEntryIndexHeadTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : + queueEntryIndexHeadTemp; + + queueEntryIndexTemp = queueEntryIndexHeadTemp; + + EB_U32 currentInd = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; + + /* Avoid an infinite loop. */ + for (EB_U32 iterations = 0; iterations < 1000 && loopTerminate != 3; iterations++) + { + hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[currentInd]); + + double curBits = (double)predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, q); + double bufferFillCur = highLevelRateControlPtr->bufferFill - curBits; + double targetFill; + double fps = 1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION); + double totalDuration = fps; + queueEntryIndexTemp = currentInd + 1; + + /* Loop over the planned future frames. */ + for (EB_U32 j = 0; bufferFillCur >= 0; j++) + { + queueEntryIndexTemp2 = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; + hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[queueEntryIndexTemp2]); + if (totalDuration >= 1.0) + break; + totalDuration += fps; + double wantedFrameSize = contextPtr->vbvMaxrate * fps; + if (bufferFillCur + wantedFrameSize <= contextPtr->vbvBufsize) + bufferFillCur += wantedFrameSize; + curBits = (double)predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, q); + bufferFillCur -= curBits; + queueEntryIndexTemp++; + } + /* Try to get the buffer at least 50% filled, but don't set an impossible goal. */ + double finalDur = 1; + targetFill = MIN(highLevelRateControlPtr->bufferFill + totalDuration * contextPtr->vbvMaxrate * 0.5, contextPtr->vbvBufsize * (1 - 0.5 * finalDur)); + if (bufferFillCur < targetFill) + { + q++; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 1; + continue; + } + /* Try to get the buffer not more than 80% filled, but don't set an impossible goal. */ + targetFill = CLIP3(contextPtr->vbvBufsize * (1 - 0.2 * finalDur), contextPtr->vbvBufsize, highLevelRateControlPtr->bufferFill - totalDuration * contextPtr->vbvMaxrate * 0.5); + if (bufferFillCur > targetFill) + { + q--; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 2; + continue; + } + break; + } + q = MAX(q0 / 2, q); + selectedRefQp = q; + } + #if RC_UPDATE_TARGET_RATE selectedOrgRefQp = selectedRefQp; if (sequenceControlSetPtr->intraPeriodLength != -1 && pictureControlSetPtr->pictureNumber % ((sequenceControlSetPtr->intraPeriodLength + 1)) == 0 && @@ -878,6 +1017,7 @@ void HighLevelRcInputPictureMode2( sequenceControlSetPtr->staticConfig.maxQpAllowed, selectedRefQp); } + // Set the QP previousSelectedRefQp = selectedRefQp; if (pictureControlSetPtr->pictureNumber > maxCodedPoc && pictureControlSetPtr->temporalLayerIndex < 2 && !pictureControlSetPtr->endOfSequenceRegion){ @@ -911,6 +1051,9 @@ void HighLevelRcInputPictureMode2( } #endif pictureControlSetPtr->targetBitsBestPredQp = pictureControlSetPtr->predBitsRefQp[pictureControlSetPtr->bestPredQp]; + + //contextPtr->bufferFill -= pictureControlSetPtr->targetBitsBestPredQp; + highLevelRateControlPtr->bufferFill -= pictureControlSetPtr->targetBitsBestPredQp; //if (pictureControlSetPtr->sliceType == 2) // { //SVT_LOG("\nTID: %d\t", pictureControlSetPtr->temporalLayerIndex); @@ -2242,6 +2385,8 @@ void* RateControlKernel(void *inputPtr) contextPtr->highLevelRateControlPtr->channelBitRatePerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerFrame * (sequenceControlSetPtr->staticConfig.lookAheadDistance + 1); contextPtr->highLevelRateControlPtr->bitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; + contextPtr->highLevelRateControlPtr->bufferFill = (EB_U64)(sequenceControlSetPtr->staticConfig.vbvBufsize * 0.9); + #if RC_UPDATE_TARGET_RATE contextPtr->highLevelRateControlPtr->previousUpdatedBitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; #endif @@ -2273,6 +2418,9 @@ void* RateControlKernel(void *inputPtr) contextPtr->vbFillThreshold2 = (contextPtr->virtualBufferSize << 3) >> 3; contextPtr->baseLayerFramesAvgQp = sequenceControlSetPtr->qp; contextPtr->baseLayerIntraFramesAvgQp = sequenceControlSetPtr->qp; + contextPtr->vbvMaxrate = sequenceControlSetPtr->staticConfig.vbvMaxrate; + contextPtr->vbvBufsize = sequenceControlSetPtr->staticConfig.vbvBufsize; + contextPtr->bufferFill = contextPtr->vbvMaxrate; } if (sequenceControlSetPtr->staticConfig.rateControlMode) { @@ -2337,7 +2485,9 @@ void* RateControlKernel(void *inputPtr) pictureControlSetPtr->pictureQp = (EB_U8)CLIP3((EB_S32)sequenceControlSetPtr->staticConfig.minQpAllowed, (EB_S32)sequenceControlSetPtr->staticConfig.maxQpAllowed, (EB_S32)(sequenceControlSetPtr->qp) + contextPtr->maxRateAdjustDeltaQP); } else{ - pictureControlSetPtr->pictureQp = (EB_U8)CLIP3((EB_S32)sequenceControlSetPtr->staticConfig.minQpAllowed, (EB_S32)sequenceControlSetPtr->staticConfig.maxQpAllowed, (EB_S32)(sequenceControlSetPtr->qp + MOD_QP_OFFSET_LAYER_ARRAY[pictureControlSetPtr->ParentPcsPtr->hierarchicalLevels][pictureControlSetPtr->temporalLayerIndex])); + pictureControlSetPtr->pictureQp = (EB_U8)CLIP3((EB_S32)sequenceControlSetPtr->staticConfig.minQpAllowed, + (EB_S32)sequenceControlSetPtr->staticConfig.maxQpAllowed, + (EB_S32)(sequenceControlSetPtr->qp + MOD_QP_OFFSET_LAYER_ARRAY[pictureControlSetPtr->ParentPcsPtr->hierarchicalLevels][pictureControlSetPtr->temporalLayerIndex])); } } diff --git a/Source/Lib/Codec/EbRateControlProcess.h b/Source/Lib/Codec/EbRateControlProcess.h index 94b0ca042..6e503353c 100644 --- a/Source/Lib/Codec/EbRateControlProcess.h +++ b/Source/Lib/Codec/EbRateControlProcess.h @@ -227,6 +227,7 @@ typedef struct HighLevelRateControlContext_s EB_U32 prevIntraSelectedRefQp; EB_U32 prevIntraOrgSelectedRefQp; EB_U64 previousUpdatedBitConstraintPerSw; + EB_U64 bufferFill; #endif @@ -243,6 +244,10 @@ typedef struct RateControlContext_s EB_U64 rateControlParamQueueHeadIndex; EB_U64 frameRate; + EB_U32 vbvMaxrate; + EB_U32 vbvBufsize; + EB_U64 bufferFill; + EB_U64 virtualBufferSize; From 488721bcc94e26a627eb612d285b053fb9f4af2e Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Tue, 8 Jan 2019 14:34:14 +0530 Subject: [PATCH 04/93] Added VBV frame level support --- Source/Lib/Codec/EbEncodeContext.c | 2 +- .../Codec/EbInitialRateControlReorderQueue.c | 2 +- Source/Lib/Codec/EbPictureControlSet.c | 3 +- Source/Lib/Codec/EbRateControlProcess.c | 51 +++++++++++++------ 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/Source/Lib/Codec/EbEncodeContext.c b/Source/Lib/Codec/EbEncodeContext.c index 01f2d350f..c7790f179 100644 --- a/Source/Lib/Codec/EbEncodeContext.c +++ b/Source/Lib/Codec/EbEncodeContext.c @@ -145,7 +145,7 @@ EB_ERRORTYPE EncodeContextCtor( // High level Rate Control histogram Queue encodeContextPtr->hlRateControlHistorgramQueueHeadIndex = 0; - EB_MALLOC(HlRateControlHistogramEntry_t**, encodeContextPtr->hlRateControlHistorgramQueue, sizeof(HlRateControlHistogramEntry_t*) * HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH, EB_N_PTR); + EB_CALLOC(HlRateControlHistogramEntry_t**, encodeContextPtr->hlRateControlHistorgramQueue, sizeof(HlRateControlHistogramEntry_t*) * HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH, 1, EB_N_PTR); for(pictureIndex=0; pictureIndex < HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH; ++pictureIndex) { return_error = HlRateControlHistogramEntryCtor( diff --git a/Source/Lib/Codec/EbInitialRateControlReorderQueue.c b/Source/Lib/Codec/EbInitialRateControlReorderQueue.c index a5d687f8c..70c28390c 100644 --- a/Source/Lib/Codec/EbInitialRateControlReorderQueue.c +++ b/Source/Lib/Codec/EbInitialRateControlReorderQueue.c @@ -23,7 +23,7 @@ EB_ERRORTYPE HlRateControlHistogramEntryCtor( HlRateControlHistogramEntry_t **entryDblPtr, EB_U32 pictureNumber) { - EB_MALLOC(HlRateControlHistogramEntry_t*, *entryDblPtr, sizeof(HlRateControlHistogramEntry_t), EB_N_PTR); + EB_CALLOC(HlRateControlHistogramEntry_t*, *entryDblPtr, sizeof(HlRateControlHistogramEntry_t), 1, EB_N_PTR); (*entryDblPtr)->pictureNumber = pictureNumber; (*entryDblPtr)->lifeCount = 0; diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index 383f33253..05ec9c879 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -582,8 +582,7 @@ EB_ERRORTYPE PictureParentControlSetCtor( EB_U16 lcuIndex; EB_U32 regionInPictureWidthIndex; EB_U32 regionInPictureHeightIndex; - - EB_MALLOC(PictureParentControlSet_t*, objectPtr, sizeof(PictureParentControlSet_t), EB_N_PTR); + EB_CALLOC(PictureParentControlSet_t*, objectPtr, sizeof(PictureParentControlSet_t), 1, EB_N_PTR); *objectDblPtr = (EB_PTR)objectPtr; objectPtr->sequenceControlSetWrapperPtr = (EbObjectWrapper_t *)EB_NULL; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index f0b6e4f7a..1feecf0ea 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -400,7 +400,7 @@ EB_U64 predictBits(SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t // Scale for in complete LCSs // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries - totalBits = hlRateControlHistogramPtrTemp->predBitsRefQp[qp] * (EB_U64)areaInPixel / (numOfFullLcus << 12); + totalBits = totalBits * (EB_U64)areaInPixel / (numOfFullLcus << 12); } hlRateControlHistogramPtrTemp->predBitsRefQp[qp] = totalBits; return totalBits; @@ -454,7 +454,7 @@ void HighLevelRcInputPictureMode2( EB_Bit_Number *sadBitsArrayPtr; EB_Bit_Number *intraSadBitsArrayPtr; EB_U32 predBitsRefQp; - EB_U32 bestQp=0; + EB_U32 bestQp = 0; for (temporalLayerIndex = 0; temporalLayerIndex< EB_MAX_TEMPORAL_LAYERS; temporalLayerIndex++){ pictureControlSetPtr->bitsPerSwPerLayer[temporalLayerIndex] = 0; @@ -584,6 +584,7 @@ void HighLevelRcInputPictureMode2( // Store the predBitsRefQp for the first frame in the window to PCS pictureControlSetPtr->predBitsRefQp[refQpIndexTemp] = hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp]; + bestQp = refQpIndexTemp; } } @@ -734,6 +735,7 @@ void HighLevelRcInputPictureMode2( minLaBitDistance = (EB_U64)ABS((EB_S64)highLevelRateControlPtr->predBitsRefQpPerSw[refQpIndex] - (EB_S64)bitConstraintPerSw); selectedRefQpTableIndex = refQpTableIndex; selectedRefQp = refQpIndex; + } else{ bestQpFound = EB_TRUE; @@ -751,6 +753,7 @@ void HighLevelRcInputPictureMode2( refQpTableIndex = (EB_U32)(refQpTableIndex + qpStep); } + bestQp = previousSelectedRefQp; } if (contextPtr->vbvMaxrate && contextPtr->vbvBufsize) @@ -783,14 +786,19 @@ void HighLevelRcInputPictureMode2( double targetFill; double fps = 1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION); double totalDuration = fps; - queueEntryIndexTemp = currentInd + 1; + queueEntryIndexTemp = currentInd; /* Loop over the planned future frames. */ for (EB_U32 j = 0; bufferFillCur >= 0; j++) { queueEntryIndexTemp2 = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[queueEntryIndexTemp2]); - if (totalDuration >= 1.0) + /* This is set to false, so the last frame would go inside the loop */ + endOfSequenceFlag = EB_FALSE; + + if ((endOfSequenceFlag || + queueEntryIndexTemp >= sequenceControlSetPtr->staticConfig.lookAheadDistance) + || (totalDuration >= 1.0)) break; totalDuration += fps; double wantedFrameSize = contextPtr->vbvMaxrate * fps; @@ -847,8 +855,7 @@ void HighLevelRcInputPictureMode2( if (highLevelRateControlPtr->predBitsRefQpPerSw[refQpIndex] == 0){ - // Finding the predicted bits for each frame in the sliding window at the reference Qp(s) - //queueEntryIndexTemp = encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; + /* Finding the predicted bits for each frame in the sliding window at the reference Qp(s) */ queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - encodeContextPtr->hlRateControlHistorgramQueue[encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); queueEntryIndexHeadTemp += encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; queueEntryIndexHeadTemp = (queueEntryIndexHeadTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? @@ -857,11 +864,10 @@ void HighLevelRcInputPictureMode2( queueEntryIndexTemp = queueEntryIndexHeadTemp; - // This is set to false, so the last frame would go inside the loop + /* This is set to false, so the last frame would go inside the loop */ endOfSequenceFlag = EB_FALSE; while (!endOfSequenceFlag && - //queueEntryIndexTemp <= encodeContextPtr->hlRateControlHistorgramQueueHeadIndex+sequenceControlSetPtr->staticConfig.lookAheadDistance){ queueEntryIndexTemp <= queueEntryIndexHeadTemp + sequenceControlSetPtr->staticConfig.lookAheadDistance){ queueEntryIndexTemp2 = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; @@ -893,7 +899,6 @@ void HighLevelRcInputPictureMode2( if (hlRateControlHistogramPtrTemp->sliceType == EB_I_PICTURE){ // Loop over block in the frame and calculated the predicted bits at reg QP - { unsigned i; EB_U32 accum = 0; @@ -926,14 +931,13 @@ void HighLevelRcInputPictureMode2( hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp] += predBitsRefQp; } - // Scale for in complete - // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries + /* Scale for in complete + predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries */ hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp] = hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp] * (EB_U64)areaInPixel / (numOfFullLcus << 12); } highLevelRateControlPtr->predBitsRefQpPerSw[refQpIndex] += hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp]; - // Store the predBitsRefQp for the first frame in the window to PCS - // if(encodeContextPtr->hlRateControlHistorgramQueueHeadIndex == queueEntryIndexTemp2) + /* Store the predBitsRefQp for the first frame in the window to PCS */ if (queueEntryIndexHeadTemp == queueEntryIndexTemp2) pictureControlSetPtr->predBitsRefQp[refQpIndexTemp] = hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp]; @@ -1050,10 +1054,10 @@ void HighLevelRcInputPictureMode2( } } #endif - pictureControlSetPtr->targetBitsBestPredQp = pictureControlSetPtr->predBitsRefQp[pictureControlSetPtr->bestPredQp]; + //pictureControlSetPtr->targetBitsBestPredQp = pictureControlSetPtr->predBitsRefQp[pictureControlSetPtr->bestPredQp]; //contextPtr->bufferFill -= pictureControlSetPtr->targetBitsBestPredQp; - highLevelRateControlPtr->bufferFill -= pictureControlSetPtr->targetBitsBestPredQp; + //highLevelRateControlPtr->bufferFill -= pictureControlSetPtr->targetBitsBestPredQp; //if (pictureControlSetPtr->sliceType == 2) // { //SVT_LOG("\nTID: %d\t", pictureControlSetPtr->temporalLayerIndex); @@ -2732,6 +2736,23 @@ void* RateControlKernel(void *inputPtr) } + /* update VBV plan */ + if (contextPtr->vbvMaxrate && contextPtr->vbvBufsize) + { + EB_S64 bufferfill_temp = (EB_S64)(contextPtr->highLevelRateControlPtr->bufferFill); + + bufferfill_temp -= parentPictureControlSetPtr->totalNumBits; + bufferfill_temp = MAX(bufferfill_temp, 0); + bufferfill_temp = (EB_S64) (bufferfill_temp + (contextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); + bufferfill_temp = MIN(bufferfill_temp, contextPtr->vbvBufsize); + contextPtr->highLevelRateControlPtr->bufferFill = (EB_U64)(bufferfill_temp); + + //printf("parentPictureControlSetPtr->totalNumBits = %lld \t contextPtr->highLevelRateControlPtr->bufferFill = %lld \t parentPictureControlSetPtr->pictureNumber = %lld \t parentPictureControlSetPtr->sliceType = %d \n", parentPictureControlSetPtr->totalNumBits, contextPtr->highLevelRateControlPtr->bufferFill, parentPictureControlSetPtr->pictureNumber, parentPictureControlSetPtr->sliceType); + + //printf("totalNumBits = %lld \t bufferFill = %lld \t pictureNumber = %lld \t sliceType = %d \t qp = %d \n", parentPictureControlSetPtr->totalNumBits, contextPtr->highLevelRateControlPtr->bufferFill, parentPictureControlSetPtr->pictureNumber, parentPictureControlSetPtr->sliceType, parentPictureControlSetPtr->bestPredQp); + + } + //printf("totalNumBits = %lld \t pictureNumber = %lld \t sliceType = %d \t qp = %d \n", parentPictureControlSetPtr->totalNumBits, parentPictureControlSetPtr->pictureNumber, parentPictureControlSetPtr->sliceType, parentPictureControlSetPtr->bestPredQp); } // Queue variables From 8f74ea205234bdc8eba3a99ed7db1c1ea191b5fb Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Tue, 22 Jan 2019 15:16:23 +0530 Subject: [PATCH 05/93] 1. Fixed Index comparision to compare with current Index + lookAheadDistance 2. Added Check for FrameIndex not exceding No of Frame to Encode --- Source/Lib/Codec/EbRateControlProcess.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 1feecf0ea..3f50db6ff 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -797,7 +797,8 @@ void HighLevelRcInputPictureMode2( endOfSequenceFlag = EB_FALSE; if ((endOfSequenceFlag || - queueEntryIndexTemp >= sequenceControlSetPtr->staticConfig.lookAheadDistance) + queueEntryIndexTemp >= (currentInd + sequenceControlSetPtr->staticConfig.lookAheadDistance)) || + (queueEntryIndexTemp >= sequenceControlSetPtr->staticConfig.framesToBeEncoded) || (totalDuration >= 1.0)) break; totalDuration += fps; @@ -838,7 +839,6 @@ void HighLevelRcInputPictureMode2( q = MAX(q0 / 2, q); selectedRefQp = q; } - #if RC_UPDATE_TARGET_RATE selectedOrgRefQp = selectedRefQp; if (sequenceControlSetPtr->intraPeriodLength != -1 && pictureControlSetPtr->pictureNumber % ((sequenceControlSetPtr->intraPeriodLength + 1)) == 0 && @@ -2747,8 +2747,6 @@ void* RateControlKernel(void *inputPtr) bufferfill_temp = MIN(bufferfill_temp, contextPtr->vbvBufsize); contextPtr->highLevelRateControlPtr->bufferFill = (EB_U64)(bufferfill_temp); - //printf("parentPictureControlSetPtr->totalNumBits = %lld \t contextPtr->highLevelRateControlPtr->bufferFill = %lld \t parentPictureControlSetPtr->pictureNumber = %lld \t parentPictureControlSetPtr->sliceType = %d \n", parentPictureControlSetPtr->totalNumBits, contextPtr->highLevelRateControlPtr->bufferFill, parentPictureControlSetPtr->pictureNumber, parentPictureControlSetPtr->sliceType); - //printf("totalNumBits = %lld \t bufferFill = %lld \t pictureNumber = %lld \t sliceType = %d \t qp = %d \n", parentPictureControlSetPtr->totalNumBits, contextPtr->highLevelRateControlPtr->bufferFill, parentPictureControlSetPtr->pictureNumber, parentPictureControlSetPtr->sliceType, parentPictureControlSetPtr->bestPredQp); } From 8f3b2a40fd3d59ce6bac6422e90faed025177423 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 23 Jan 2019 20:55:32 +0530 Subject: [PATCH 06/93] Enforce vbv buffer update to happen in decode order --- Source/Lib/Codec/EbEncodeContext.c | 3 ++ Source/Lib/Codec/EbEncodeContext.h | 4 ++ Source/Lib/Codec/EbPacketizationProcess.c | 13 +++++- .../Lib/Codec/EbPacketizationReorderQueue.h | 1 + Source/Lib/Codec/EbRateControlProcess.c | 44 ++++++------------- Source/Lib/Codec/EbRateControlProcess.h | 5 --- 6 files changed, 33 insertions(+), 37 deletions(-) diff --git a/Source/Lib/Codec/EbEncodeContext.c b/Source/Lib/Codec/EbEncodeContext.c index c7790f179..a171d0ebb 100644 --- a/Source/Lib/Codec/EbEncodeContext.c +++ b/Source/Lib/Codec/EbEncodeContext.c @@ -206,6 +206,9 @@ EB_ERRORTYPE EncodeContextCtor( // Rate Control encodeContextPtr->availableTargetBitRate = 10000000; encodeContextPtr->availableTargetBitRateChanged = EB_FALSE; + encodeContextPtr->bufferFill = 0; + encodeContextPtr->vbvBufsize = 0; + encodeContextPtr->vbvMaxrate = 0; // Rate Control Bit Tables EB_MALLOC(RateControlTables_t*, encodeContextPtr->rateControlTablesArray, sizeof(RateControlTables_t) * TOTAL_NUMBER_OF_INITIAL_RC_TABLES_ENTRY, EB_N_PTR); diff --git a/Source/Lib/Codec/EbEncodeContext.h b/Source/Lib/Codec/EbEncodeContext.h index e37bb0771..e89d67ad0 100644 --- a/Source/Lib/Codec/EbEncodeContext.h +++ b/Source/Lib/Codec/EbEncodeContext.h @@ -144,6 +144,10 @@ typedef struct EncodeContext_s // Rate Control EB_U32 availableTargetBitRate; EB_BOOL availableTargetBitRateChanged; + EB_U32 vbvMaxrate; + EB_U32 vbvBufsize; + EB_U64 bufferFill; + EB_U32 previousSelectedRefQp; EB_U64 maxCodedPoc; diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index b664e826b..c0770068f 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -495,7 +495,7 @@ void* PacketizationKernel(void *inputPtr) // Send the number of bytes per frame to RC pictureControlSetPtr->ParentPcsPtr->totalNumBits = outputStreamPtr->nFilledLen << 3; - + queueEntryPtr->actualBits = pictureControlSetPtr->ParentPcsPtr->totalNumBits; // Code EOS NUT if (outputStreamPtr->nFlags & EB_BUFFERFLAG_EOS && sequenceControlSetPtr->staticConfig.codeEosNal == 1) { @@ -562,7 +562,18 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->nTickCount = (EB_U32)latency; EbPostFullObject(outputStreamWrapperPtr); + /* update VBV plan */ + if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) + { + EB_S64 bufferfill_temp = (EB_S64)(encodeContextPtr->bufferFill); + + bufferfill_temp -= queueEntryPtr->actualBits; + bufferfill_temp = MAX(bufferfill_temp, 0); + bufferfill_temp = (EB_S64)(bufferfill_temp + (encodeContextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); + bufferfill_temp = MIN(bufferfill_temp, encodeContextPtr->vbvBufsize); + encodeContextPtr->bufferFill = (EB_U64)(bufferfill_temp); + } // Reset the Reorder Queue Entry queueEntryPtr->pictureNumber += PACKETIZATION_REORDER_QUEUE_MAX_DEPTH; queueEntryPtr->outputStreamWrapperPtr = (EbObjectWrapper_t *)EB_NULL; diff --git a/Source/Lib/Codec/EbPacketizationReorderQueue.h b/Source/Lib/Codec/EbPacketizationReorderQueue.h index 7b83a51c7..de89e08c1 100644 --- a/Source/Lib/Codec/EbPacketizationReorderQueue.h +++ b/Source/Lib/Codec/EbPacketizationReorderQueue.h @@ -20,6 +20,7 @@ typedef struct PacketizationReorderEntry_s { EB_U64 startTimeSeconds; EB_U64 startTimeuSeconds; + EB_U64 actualBits; } PacketizationReorderEntry_t; extern EB_ERRORTYPE PacketizationReorderEntryCtor( diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 3f50db6ff..2a95ac808 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -347,8 +347,6 @@ EB_ERRORTYPE RateControlContextCtor( contextPtr->extraBitsGen = 0; contextPtr->maxRateAdjustDeltaQP = 0; - - contextPtr->bufferFill = 0; return EB_ErrorNone; } @@ -756,7 +754,7 @@ void HighLevelRcInputPictureMode2( bestQp = previousSelectedRefQp; } - if (contextPtr->vbvMaxrate && contextPtr->vbvBufsize) + if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) { /* Lookahead VBV: If lookahead is done, raise the quantizer as necessary * such that no frames in the lookahead overflow and such that the buffer @@ -780,9 +778,8 @@ void HighLevelRcInputPictureMode2( for (EB_U32 iterations = 0; iterations < 1000 && loopTerminate != 3; iterations++) { hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[currentInd]); - double curBits = (double)predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, q); - double bufferFillCur = highLevelRateControlPtr->bufferFill - curBits; + double bufferFillCur = encodeContextPtr->bufferFill - curBits; double targetFill; double fps = 1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION); double totalDuration = fps; @@ -801,9 +798,10 @@ void HighLevelRcInputPictureMode2( (queueEntryIndexTemp >= sequenceControlSetPtr->staticConfig.framesToBeEncoded) || (totalDuration >= 1.0)) break; + totalDuration += fps; - double wantedFrameSize = contextPtr->vbvMaxrate * fps; - if (bufferFillCur + wantedFrameSize <= contextPtr->vbvBufsize) + double wantedFrameSize = encodeContextPtr->vbvMaxrate * fps; + if (bufferFillCur + wantedFrameSize <= encodeContextPtr->vbvBufsize) bufferFillCur += wantedFrameSize; curBits = (double)predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, q); bufferFillCur -= curBits; @@ -811,7 +809,7 @@ void HighLevelRcInputPictureMode2( } /* Try to get the buffer at least 50% filled, but don't set an impossible goal. */ double finalDur = 1; - targetFill = MIN(highLevelRateControlPtr->bufferFill + totalDuration * contextPtr->vbvMaxrate * 0.5, contextPtr->vbvBufsize * (1 - 0.5 * finalDur)); + targetFill = MIN(encodeContextPtr->bufferFill + totalDuration * encodeContextPtr->vbvMaxrate * 0.5, encodeContextPtr->vbvBufsize * (1 - 0.5 * finalDur)); if (bufferFillCur < targetFill) { q++; @@ -823,7 +821,7 @@ void HighLevelRcInputPictureMode2( continue; } /* Try to get the buffer not more than 80% filled, but don't set an impossible goal. */ - targetFill = CLIP3(contextPtr->vbvBufsize * (1 - 0.2 * finalDur), contextPtr->vbvBufsize, highLevelRateControlPtr->bufferFill - totalDuration * contextPtr->vbvMaxrate * 0.5); + targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.2 * finalDur), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); if (bufferFillCur > targetFill) { q--; @@ -2327,7 +2325,7 @@ void* RateControlKernel(void *inputPtr) { // Context RateControlContext_t *contextPtr = (RateControlContext_t*)inputPtr; - // EncodeContext_t *encodeContextPtr; + EncodeContext_t *encodeContextPtr; RateControlIntervalParamContext_t *rateControlParamPtr; @@ -2376,9 +2374,11 @@ void* RateControlKernel(void *inputPtr) pictureControlSetPtr = (PictureControlSet_t*)rateControlTasksPtr->pictureControlSetWrapperPtr->objectPtr; sequenceControlSetPtr = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; + encodeContextPtr = (EncodeContext_t*)sequenceControlSetPtr->encodeContextPtr; #if DEADLOCK_DEBUG SVT_LOG("POC %lld RC IN \n", pictureControlSetPtr->pictureNumber); #endif + // High level RC if (pictureControlSetPtr->pictureNumber == 0){ @@ -2388,9 +2388,7 @@ void* RateControlKernel(void *inputPtr) contextPtr->highLevelRateControlPtr->channelBitRatePerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerFrame * (sequenceControlSetPtr->staticConfig.lookAheadDistance + 1); contextPtr->highLevelRateControlPtr->bitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; - - contextPtr->highLevelRateControlPtr->bufferFill = (EB_U64)(sequenceControlSetPtr->staticConfig.vbvBufsize * 0.9); - + encodeContextPtr->bufferFill = (EB_U64)(sequenceControlSetPtr->staticConfig.vbvBufsize * 0.9); #if RC_UPDATE_TARGET_RATE contextPtr->highLevelRateControlPtr->previousUpdatedBitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; #endif @@ -2422,9 +2420,8 @@ void* RateControlKernel(void *inputPtr) contextPtr->vbFillThreshold2 = (contextPtr->virtualBufferSize << 3) >> 3; contextPtr->baseLayerFramesAvgQp = sequenceControlSetPtr->qp; contextPtr->baseLayerIntraFramesAvgQp = sequenceControlSetPtr->qp; - contextPtr->vbvMaxrate = sequenceControlSetPtr->staticConfig.vbvMaxrate; - contextPtr->vbvBufsize = sequenceControlSetPtr->staticConfig.vbvBufsize; - contextPtr->bufferFill = contextPtr->vbvMaxrate; + encodeContextPtr->vbvMaxrate = sequenceControlSetPtr->staticConfig.vbvMaxrate; + encodeContextPtr->vbvBufsize = sequenceControlSetPtr->staticConfig.vbvBufsize; } if (sequenceControlSetPtr->staticConfig.rateControlMode) { @@ -2736,21 +2733,6 @@ void* RateControlKernel(void *inputPtr) } - /* update VBV plan */ - if (contextPtr->vbvMaxrate && contextPtr->vbvBufsize) - { - EB_S64 bufferfill_temp = (EB_S64)(contextPtr->highLevelRateControlPtr->bufferFill); - - bufferfill_temp -= parentPictureControlSetPtr->totalNumBits; - bufferfill_temp = MAX(bufferfill_temp, 0); - bufferfill_temp = (EB_S64) (bufferfill_temp + (contextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); - bufferfill_temp = MIN(bufferfill_temp, contextPtr->vbvBufsize); - contextPtr->highLevelRateControlPtr->bufferFill = (EB_U64)(bufferfill_temp); - - //printf("totalNumBits = %lld \t bufferFill = %lld \t pictureNumber = %lld \t sliceType = %d \t qp = %d \n", parentPictureControlSetPtr->totalNumBits, contextPtr->highLevelRateControlPtr->bufferFill, parentPictureControlSetPtr->pictureNumber, parentPictureControlSetPtr->sliceType, parentPictureControlSetPtr->bestPredQp); - - } - //printf("totalNumBits = %lld \t pictureNumber = %lld \t sliceType = %d \t qp = %d \n", parentPictureControlSetPtr->totalNumBits, parentPictureControlSetPtr->pictureNumber, parentPictureControlSetPtr->sliceType, parentPictureControlSetPtr->bestPredQp); } // Queue variables diff --git a/Source/Lib/Codec/EbRateControlProcess.h b/Source/Lib/Codec/EbRateControlProcess.h index 6e503353c..94b0ca042 100644 --- a/Source/Lib/Codec/EbRateControlProcess.h +++ b/Source/Lib/Codec/EbRateControlProcess.h @@ -227,7 +227,6 @@ typedef struct HighLevelRateControlContext_s EB_U32 prevIntraSelectedRefQp; EB_U32 prevIntraOrgSelectedRefQp; EB_U64 previousUpdatedBitConstraintPerSw; - EB_U64 bufferFill; #endif @@ -244,10 +243,6 @@ typedef struct RateControlContext_s EB_U64 rateControlParamQueueHeadIndex; EB_U64 frameRate; - EB_U32 vbvMaxrate; - EB_U32 vbvBufsize; - EB_U64 bufferFill; - EB_U64 virtualBufferSize; From f1e1a1727858d8246d1b875815bb9366014a5483 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 23 Jan 2019 15:34:42 +0530 Subject: [PATCH 07/93] Add new cli -hrd (default 0) + enable flags to signal hrd --- Source/API/EbApi.h | 1 + Source/App/EbAppConfig.c | 4 ++++ Source/App/EbAppConfig.h | 1 + Source/App/EbAppContext.c | 1 + Source/Lib/Codec/EbEncHandle.c | 23 +++++++++++++++++++++++ 5 files changed, 30 insertions(+) diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index a1e234b0c..18fd1cc5f 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -204,6 +204,7 @@ typedef struct EB_H265_ENC_CONFIGURATION uint8_t switchThreadsToRtPriority; // switch to real time mode uint32_t vbvMaxrate; uint32_t vbvBufsize; + uint32_t hrdFlag; // ASM Type uint32_t asmType; // level of optimization to use. diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index f62e883fa..c4679b2b1 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -73,6 +73,7 @@ #define TARGET_BIT_RATE_TOKEN "-tbr" #define VBV_MAX_RATE_TOKEN "-vbv-maxrate" #define VBV_BUFFER_SIZE_TOKEN "-vbv-bufsize" +#define HRD_TOKEN "-hrd" #define MAX_QP_TOKEN "-max-qp" #define MIN_QP_TOKEN "-min-qp" #define TEMPORAL_ID "-temporal-id" // no Eval @@ -181,6 +182,7 @@ static void SetBitRateReduction (const char *value, EbConfig_t * static void SetImproveSharpness (const char *value, EbConfig_t *cfg) {cfg->improveSharpness = (EB_BOOL)strtol(value, NULL, 0);}; static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0); }; static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0); }; +static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0); }; static void SetVideoUsabilityInfo (const char *value, EbConfig_t *cfg) {cfg->videoUsabilityInfo = strtol(value, NULL, 0);}; static void SetHighDynamicRangeInput (const char *value, EbConfig_t *cfg) {cfg->highDynamicRangeInput = strtol(value, NULL, 0);}; static void SetAccessUnitDelimiter (const char *value, EbConfig_t *cfg) {cfg->accessUnitDelimiter = strtol(value, NULL, 0);}; @@ -281,6 +283,7 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, MIN_QP_TOKEN, "MinQpAllowed", SetMinQpAllowed }, { SINGLE_INPUT, VBV_MAX_RATE_TOKEN, "vbvMaxRate", SetVbvMaxrate }, { SINGLE_INPUT, VBV_BUFFER_SIZE_TOKEN, "vbvBufsize", SetVbvBufsize }, + { SINGLE_INPUT, HRD_TOKEN, "hrd", SetHrdFlag }, // DLF { SINGLE_INPUT, LOOP_FILTER_DISABLE_TOKEN, "LoopFilterDisable", SetDisableDlfFlag }, @@ -383,6 +386,7 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->encMode = 9; configPtr->vbvMaxRate = 0; configPtr->vbvBufsize = 0; + configPtr->hrdFlag = 0; configPtr->intraPeriod = -2; configPtr->intraRefreshType = 1; configPtr->hierarchicalLevels = 3; diff --git a/Source/App/EbAppConfig.h b/Source/App/EbAppConfig.h index 019981b7e..3f56af9e7 100644 --- a/Source/App/EbAppConfig.h +++ b/Source/App/EbAppConfig.h @@ -329,6 +329,7 @@ typedef struct EbConfig_s uint32_t enableTemporalId; EB_BOOL switchThreadsToRtPriority; EB_BOOL fpsInVps; + uint32_t hrdFlag; /**************************************** * Annex A Parameters diff --git a/Source/App/EbAppContext.c b/Source/App/EbAppContext.c index 6ae09d40c..51fb81b77 100644 --- a/Source/App/EbAppContext.c +++ b/Source/App/EbAppContext.c @@ -185,6 +185,7 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.useQpFile = (EB_BOOL)config->useQpFile; callbackData->ebEncParameters.disableDlfFlag = (EB_BOOL)config->disableDlfFlag; callbackData->ebEncParameters.enableSaoFlag = (EB_BOOL)config->enableSaoFlag; + callbackData->ebEncParameters.hrdFlag = (EB_BOOL)config->hrdFlag; callbackData->ebEncParameters.useDefaultMeHme = (EB_BOOL)config->useDefaultMeHme; callbackData->ebEncParameters.enableHmeFlag = (EB_BOOL)config->enableHmeFlag; callbackData->ebEncParameters.searchAreaWidth = config->searchAreaWidth; diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index 4d4864241..b10c7bb2a 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2108,6 +2108,7 @@ void CopyApiFromApp( sequenceControlSetPtr->staticConfig.frameRateDenominator = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->frameRateDenominator; sequenceControlSetPtr->staticConfig.frameRateNumerator = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->frameRateNumerator; sequenceControlSetPtr->staticConfig.reconEnabled = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->reconEnabled; + sequenceControlSetPtr->staticConfig.hrdFlag = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->hrdFlag; // if HDR is set videoUsabilityInfo should be set to 1 if (sequenceControlSetPtr->staticConfig.highDynamicRangeInput == 1) { @@ -2128,6 +2129,16 @@ void CopyApiFromApp( sequenceControlSetPtr->staticConfig.lookAheadDistance = ComputeDefaultLookAhead(&sequenceControlSetPtr->staticConfig); } + //Set required flags to signal vbv status when hrd is enabled + if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) + { + sequenceControlSetPtr->staticConfig.videoUsabilityInfo = 1; + sequenceControlSetPtr->videoUsabilityInfoPtr->vuiHrdParametersPresentFlag = 1; + sequenceControlSetPtr->staticConfig.bufferingPeriodSEI = 1; + sequenceControlSetPtr->staticConfig.pictureTimingSEI = 1; + sequenceControlSetPtr->videoUsabilityInfoPtr->hrdParametersPtr->nalHrdParametersPresentFlag = 1; + sequenceControlSetPtr->videoUsabilityInfoPtr->hrdParametersPtr->cpbDpbDelaysPresentFlag = 1; + } @@ -2417,6 +2428,18 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } + if (config->hrdFlag > 1) + { + printf("Error Instance %u: hrdFlag must be [0 - 1]\n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } + + if (config->hrdFlag == 1 && ((config->vbvBufsize <= 0) || (config->vbvMaxrate <= 0))) + { + printf("Error instance %u: hrd requires vbv max rate and vbv bufsize to be greater than 0 ", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } + if ( config->enableSaoFlag > 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid SAO. SAO range must be [0 - 1]\n",channelNumber+1); return_error = EB_ErrorBadParameter; From 8c2f324e346b400bb827d6fdc8ca142a8cb34b6b Mon Sep 17 00:00:00 2001 From: kirithika Date: Thu, 24 Jan 2019 15:22:36 +0530 Subject: [PATCH 08/93] Add support for HRD SEI signalling --- Source/Lib/Codec/EbDefinitions.h | 3 + Source/Lib/Codec/EbEntropyCoding.c | 5 +- Source/Lib/Codec/EbPacketizationProcess.c | 95 ++++++++++++++++++++++- Source/Lib/Codec/EbSei.c | 4 +- 4 files changed, 102 insertions(+), 5 deletions(-) diff --git a/Source/Lib/Codec/EbDefinitions.h b/Source/Lib/Codec/EbDefinitions.h index da4d48747..2ccc5666e 100644 --- a/Source/Lib/Codec/EbDefinitions.h +++ b/Source/Lib/Codec/EbDefinitions.h @@ -1045,6 +1045,9 @@ typedef enum EB_SEI { #define LOW_LCU_VARIANCE 10 #define MEDIUM_LCU_VARIANCE 50 +//HRD constants +#define BR_SHIFT 6 +#define CPB_SHIFT 4 // INTRA restriction for global motion #define INTRA_GLOBAL_MOTION_NON_MOVING_INDEX_TH 2 diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index e9b96e3d9..a60c4fd77 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -7983,7 +7983,7 @@ EB_ERRORTYPE CodeBufferingPeriodSEI( WriteCodeCavlc( bitstreamPtr, bufferingPeriodPtr->auCpbRemovalDelayDeltaMinus1, - vuiPtr->hrdParametersPtr->initialCpbRemovalDelayLengthMinus1 + 1); + vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1); if (bufferingPeriodPtr->rapCpbParamsPresentFlag){ // cpb_delay_offset @@ -8055,6 +8055,7 @@ EB_ERRORTYPE CodeBufferingPeriodSEI( } + EB_ERRORTYPE CodePictureTimingSEI( OutputBitstreamUnit_t *bitstreamPtr, AppPictureTimingSei_t *picTimingSeiPtr, @@ -8092,7 +8093,7 @@ EB_ERRORTYPE CodePictureTimingSEI( WriteCodeCavlc( bitstreamPtr, picTimingSeiPtr->auCpbRemovalDelayMinus1, - vuiPtr->hrdParametersPtr->duCpbRemovalDelayLengthMinus1 + 1); + vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1); // pic_dpb_output_delay WriteCodeCavlc( diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index c0770068f..77918358d 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -18,6 +18,78 @@ #include "EbRateControlProcess.h" #include "EbTime.h" +void HrdFullness(SequenceControlSet_t *sequenceControlSetPtr, PictureControlSet_t *pictureControlSetptr, AppBufferingPeriodSei_t *seiBP) +{ + EB_U32 i; + const AppVideoUsabilityInfo_t* vui = sequenceControlSetPtr->videoUsabilityInfoPtr; + const AppHrdParameters_t* hrd = vui->hrdParametersPtr; + EB_U32 num = 90000; + EB_U32 denom = (hrd->bitRateValueMinus1[pictureControlSetptr->temporalLayerIndex][0][hrd->cpbCountMinus1[0]] + 1) << (hrd->bitRateScale + BR_SHIFT); + EB_U64 cpbState = sequenceControlSetPtr->encodeContextPtr->bufferFill; + EB_U64 cpbSize = (hrd->cpbSizeValueMinus1[pictureControlSetptr->temporalLayerIndex][0][hrd->cpbCountMinus1[0]] + 1) << (hrd->cpbSizeScale + CPB_SHIFT); + + for (i = 0; i < hrd->cpbCountMinus1[0]+1; i++) + { + seiBP->initialCpbRemovalDelay[0][i] = (EB_U32)(num * cpbState / denom); + seiBP->initialCpbRemovalDelayOffset[0][i] = (EB_U32)(num * cpbSize / denom - seiBP->initialCpbRemovalDelay[0][i]); + } +} + +static inline EB_S32 calcScale(EB_U32 x) +{ + EB_U8 lut[16] = { 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; + EB_S32 y, z = (((x & 0xffff) - 1) >> 27) & 16; + x >>= z; + z += y = (((x & 0xff) - 1) >> 28) & 8; + x >>= y; + z += y = (((x & 0xf) - 1) >> 29) & 4; + x >>= y; + return z + lut[x & 0xf]; +} + +static inline EB_S32 calcLength(EB_U32 x) +{ + EB_U8 lut[16] = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; + EB_S32 y, z = (((x >> 16) - 1) >> 27) & 16; + x >>= z ^ 16; + z += y = ((x - 0x100) >> 28) & 8; + x >>= y ^ 8; + z += y = ((x - 0x10) >> 29) & 4; + x >>= y ^ 4; + return z + lut[x]; +} + +void InitHRD(SequenceControlSet_t *scsPtr) +{ + EB_U32 i, j, k; + AppHrdParameters_t *hrd = scsPtr->videoUsabilityInfoPtr->hrdParametersPtr; + + // normalize HRD size and rate to the value / scale notation + hrd->bitRateScale = (EB_U32)CLIP3(0, 15, calcScale(scsPtr->staticConfig.vbvMaxrate) - BR_SHIFT); + hrd->cpbSizeScale = (EB_U32)CLIP3(0, 15, calcScale(scsPtr->staticConfig.vbvBufsize) - CPB_SHIFT); + for (i = 0; i < MAX_TEMPORAL_LAYERS; i++) + for (j = 0; j < 2; j++) + for (k = 0; k < MAX_CPB_COUNT; k++) + { + hrd->bitRateValueMinus1[i][j][k] = (scsPtr->staticConfig.vbvMaxrate >> (hrd->bitRateScale + BR_SHIFT))-1; + hrd->cpbSizeValueMinus1[i][j][k] = (scsPtr->staticConfig.vbvBufsize >> (hrd->cpbSizeScale + CPB_SHIFT))-1; + } + EB_U32 bitRateUnscale = ((scsPtr->staticConfig.vbvMaxrate >> (hrd->bitRateScale + BR_SHIFT)) << (hrd->bitRateScale + BR_SHIFT)); + EB_U32 cpbSizeUnscale = ((scsPtr->staticConfig.vbvBufsize >> (hrd->cpbSizeScale + CPB_SHIFT)) << (hrd->cpbSizeScale + CPB_SHIFT)); + + // arbitrary +#define MAX_DURATION 0.5 + + EB_U32 maxCpbOutputDelay = (EB_U32)MIN((EB_U32)scsPtr->staticConfig.intraPeriodLength* MAX_DURATION *scsPtr->videoUsabilityInfoPtr->vuiTimeScale / scsPtr->videoUsabilityInfoPtr->vuiNumUnitsInTick, MAX_UNSIGNED_VALUE); + EB_U32 maxDpbOutputDelay = (EB_U32)(scsPtr->maxDpbSize* MAX_DURATION *scsPtr->videoUsabilityInfoPtr->vuiTimeScale / scsPtr->videoUsabilityInfoPtr->vuiNumUnitsInTick); + EB_U32 maxDelay = (EB_U32)(90000.0 * cpbSizeUnscale / bitRateUnscale + 0.5); + hrd->initialCpbRemovalDelayLengthMinus1 = (EB_U32)((2 + CLIP3(4, 22, 32 - calcLength(maxDelay)))-1); + hrd->auCpbRemovalDelayLengthMinus1 = (EB_U32)((CLIP3(4, 31, 32 - calcLength(maxCpbOutputDelay)))-1); + hrd->dpbOutputDelayLengthMinus1 = (EB_U32)((CLIP3(4, 31, 32 - calcLength(maxDpbOutputDelay)))-1); + +#undef MAX_DURATION +} + EB_ERRORTYPE PacketizationContextCtor( PacketizationContext_t **contextDblPtr, EbFifo_t *entropyCodingInputFifoPtr, @@ -74,6 +146,7 @@ void* PacketizationKernel(void *inputPtr) EB_PICTURE sliceType; + EB_U64 prevBPpictureNumber =0; for(;;) { // Get EntropyCoding Results @@ -144,6 +217,9 @@ void* PacketizationKernel(void *inputPtr) ComputeMaxDpbBuffer( sequenceControlSetPtr); + if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) + InitHRD(sequenceControlSetPtr); + // Code the VPS EncodeVPS( pictureControlSetPtr->bitstreamPtr, @@ -435,7 +511,13 @@ void* PacketizationKernel(void *inputPtr) pictureControlSetPtr->sliceType == EB_I_PICTURE && sequenceControlSetPtr->staticConfig.videoUsabilityInfo && (sequenceControlSetPtr->videoUsabilityInfoPtr->hrdParametersPtr->nalHrdParametersPresentFlag || sequenceControlSetPtr->videoUsabilityInfoPtr->hrdParametersPtr->vclHrdParametersPresentFlag)) - { + { + //Calculating the hrdfullness based on the vbv buffer fill status + if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) + { + HrdFullness(sequenceControlSetPtr, pictureControlSetPtr, &sequenceControlSetPtr->bufferingPeriod); + prevBPpictureNumber = pictureControlSetPtr->ParentPcsPtr->decodeOrder; + } EncodeBufferingPeriodSEI( pictureControlSetPtr->bitstreamPtr, &sequenceControlSetPtr->bufferingPeriod, @@ -444,6 +526,17 @@ void* PacketizationKernel(void *inputPtr) } if(sequenceControlSetPtr->staticConfig.pictureTimingSEI) { + if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) + { + // The aucpbremoval delay specifies how many clock ticks the + // access unit associated with the picture timing SEI message has to + // wait after removal of the access unit with the most recent + // buffering period SEI message + const AppVideoUsabilityInfo_t* vui = sequenceControlSetPtr->videoUsabilityInfoPtr; + const AppHrdParameters_t* hrd = vui->hrdParametersPtr; + sequenceControlSetPtr->picTimingSei.auCpbRemovalDelayMinus1 = (EB_U32)((MIN(MAX(1, (EB_S32)pictureControlSetPtr->ParentPcsPtr->decodeOrder - (EB_S32)prevBPpictureNumber), (1 << hrd->auCpbRemovalDelayLengthMinus1)))-1); + sequenceControlSetPtr->picTimingSei.picDpbOutputDelay = (EB_U32)(sequenceControlSetPtr->maxDpbSize + pictureControlSetPtr->pictureNumber - pictureControlSetPtr->ParentPcsPtr->decodeOrder); + } EncodePictureTimingSEI( pictureControlSetPtr->bitstreamPtr, diff --git a/Source/Lib/Codec/EbSei.c b/Source/Lib/Codec/EbSei.c index c56f32144..0ddbba06c 100644 --- a/Source/Lib/Codec/EbSei.c +++ b/Source/Lib/Codec/EbSei.c @@ -338,7 +338,7 @@ EB_U32 GetPictureTimingSEILength( if(vuiPtr->hrdParametersPtr->cpbDpbDelaysPresentFlag) { // au_cpb_removal_delay_minus1 - seiLength += vuiPtr->hrdParametersPtr->duCpbRemovalDelayLengthMinus1 + 1; + seiLength += vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1; // pic_dpb_output_delay seiLength += vuiPtr->hrdParametersPtr->dpbOutputDelayLengthMinus1 + 1; @@ -400,7 +400,7 @@ EB_U32 GetBufPeriodSEILength( seiLength += 1; // au_cpb_removal_delay_delta_minus1 - seiLength += vuiPtr->hrdParametersPtr->initialCpbRemovalDelayLengthMinus1 + 1; + seiLength += vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1; if(bufferingPeriodPtr->rapCpbParamsPresentFlag) { // cpb_delay_offset From bfd35f2045805ddbcb34e95c7ea591629e3b44b5 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 30 Jan 2019 23:07:20 +0530 Subject: [PATCH 09/93] Add support for Active Parameter Set SEI --- Source/Lib/Codec/EbEntropyCoding.c | 93 +++++++++++++++++++++++ Source/Lib/Codec/EbEntropyCoding.h | 4 + Source/Lib/Codec/EbPacketizationProcess.c | 8 ++ Source/Lib/Codec/EbSei.c | 36 +++++++++ Source/Lib/Codec/EbSei.h | 14 ++++ Source/Lib/Codec/EbSequenceControlSet.c | 11 +++ Source/Lib/Codec/EbSequenceControlSet.h | 3 + 7 files changed, 169 insertions(+) diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index a60c4fd77..871b84704 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -8054,7 +8054,41 @@ EB_ERRORTYPE CodeBufferingPeriodSEI( return return_error; } +EB_ERRORTYPE CodeActiveParameterSetSEI( + OutputBitstreamUnit_t *bitstreamPtr, + AppActiveparameterSetSei_t *activeParameterSet) +{ + EB_U32 i; + EB_ERRORTYPE return_error = EB_ErrorNone; + //active_vps_id + WriteCodeCavlc(bitstreamPtr, activeParameterSet->activeVideoParameterSetid, 4); + //self_contained_flag + WriteFlagCavlc(bitstreamPtr, activeParameterSet->selfContainedCvsFlag); + //no_param_set_update_flag + WriteFlagCavlc(bitstreamPtr, activeParameterSet->noParameterSetUpdateFlag); + //num_sps_ids_minus1 + WriteUvlc(bitstreamPtr, activeParameterSet->numSpsIdsMinus1); + //active_seq_param_set_id + for (i = 0; i <= activeParameterSet->numSpsIdsMinus1; i++) + { + WriteUvlc(bitstreamPtr, activeParameterSet->activeSeqParameterSetId); + } + if (bitstreamPtr->writtenBitsCount % 8 != 0) { + // bit_equal_to_one + WriteFlagCavlc( + bitstreamPtr, + 1); + + while (bitstreamPtr->writtenBitsCount % 8 != 0) { + // bit_equal_to_zero + WriteFlagCavlc( + bitstreamPtr, + 0); + } + } + return return_error; +} EB_ERRORTYPE CodePictureTimingSEI( OutputBitstreamUnit_t *bitstreamPtr, @@ -8306,6 +8340,65 @@ EB_ERRORTYPE EncodeBufferingPeriodSEI( return return_error; } +EB_ERRORTYPE EncodeActiveParameterSetsSEI( + Bitstream_t *bitstreamPtr, + AppActiveparameterSetSei_t *activeParameterSet) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + unsigned payloadType = ACTIVE_PARAMETER_SETS; + + // Note: payloadSize is fixed temporarily, this may change in future based on what is sent in CodeActiveParameterSet + unsigned payloadSize; + + OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)bitstreamPtr->outputBitstreamPtr; + + CodeNALUnitHeader( + outputBitstreamPtr, + NAL_UNIT_PREFIX_SEI, + 0); + + payloadSize = GetActiveParameterSetSEILength( + activeParameterSet); + + for (; payloadType >= 0xff; payloadType -= 0xff) { + OutputBitstreamWrite( + outputBitstreamPtr, + 0xff, + 8); + } + OutputBitstreamWrite( + outputBitstreamPtr, + payloadType, + 8); + + for (; payloadSize >= 0xff; payloadSize -= 0xff) { + OutputBitstreamWrite( + outputBitstreamPtr, + 0xff, + 8); + } + OutputBitstreamWrite( + outputBitstreamPtr, + payloadSize, + 8); + + // Active Parameter Set SEI data + CodeActiveParameterSetSEI( + outputBitstreamPtr, + activeParameterSet); + + // Byte Align the Bitstream + OutputBitstreamWrite( + outputBitstreamPtr, + 1, + 1); + + OutputBitstreamWriteAlignZero( + outputBitstreamPtr); + + return return_error; +} + EB_ERRORTYPE EncodeRegUserDataSEI( Bitstream_t *bitstreamPtr, RegistedUserData_t *regUserDataSeiPtr) diff --git a/Source/Lib/Codec/EbEntropyCoding.h b/Source/Lib/Codec/EbEntropyCoding.h index 613b1ddad..828cf5843 100644 --- a/Source/Lib/Codec/EbEntropyCoding.h +++ b/Source/Lib/Codec/EbEntropyCoding.h @@ -167,6 +167,10 @@ extern EB_ERRORTYPE EncodePictureTimingSEI( EncodeContext_t *encodeContextPtr, EB_U8 pictStruct); +extern EB_ERRORTYPE EncodeActiveParameterSetsSEI( + Bitstream_t *bitstreamPtr, + AppActiveparameterSetSei_t *activeParameterSet); + extern EB_ERRORTYPE EncodeRegUserDataSEI( Bitstream_t *bitstreamPtr, diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 77918358d..58a61028a 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -253,6 +253,14 @@ void* PacketizationKernel(void *inputPtr) contextPtr->ppsConfig); } + if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) + { + sequenceControlSetPtr->activeParameterSet.selfContainedCvsFlag = EB_TRUE; + sequenceControlSetPtr->activeParameterSet.noParameterSetUpdateFlag = EB_TRUE; + EncodeActiveParameterSetsSEI( + pictureControlSetPtr->bitstreamPtr, + &sequenceControlSetPtr->activeParameterSet); + } // Flush the Bitstream FlushBitstream( pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); diff --git a/Source/Lib/Codec/EbSei.c b/Source/Lib/Codec/EbSei.c index 0ddbba06c..2c2586fde 100644 --- a/Source/Lib/Codec/EbSei.c +++ b/Source/Lib/Codec/EbSei.c @@ -264,6 +264,17 @@ void EbBufferingPeriodSeiCtor( return; } +void EbActiveParameterSetSeiCtor( + AppActiveparameterSetSei_t *activeParameterSetSei) +{ + activeParameterSetSei->activeVideoParameterSetid = 0; + activeParameterSetSei->selfContainedCvsFlag = EB_FALSE; + activeParameterSetSei->noParameterSetUpdateFlag = EB_FALSE; + activeParameterSetSei->numSpsIdsMinus1 = 0; + activeParameterSetSei->activeSeqParameterSetId = 0; +// activeParameterSetSei->layerSpsIdx = 0; + return; +} void EbRecoveryPointSeiCtor( AppRecoveryPoint_t *recoveryPointSeiPtr) @@ -437,6 +448,31 @@ EB_U32 GetBufPeriodSEILength( return seiLength; } +EB_U32 GetActiveParameterSetSEILength( + AppActiveparameterSetSei_t *activeParameterSet) +{ + EB_U32 seiLength = 0; + + // active_video_parameter_set_id + seiLength += GetUvlcCodeLength(activeParameterSet->activeVideoParameterSetid); + + // self_contained_cvs_flag + seiLength += 1; + + // no_param_set_update_flag + seiLength += 1; + + //num_sps_ids_minus1 + seiLength += GetUvlcCodeLength(activeParameterSet->numSpsIdsMinus1); + + //active_seq_param_set_id + seiLength += GetUvlcCodeLength(activeParameterSet->activeSeqParameterSetId); + + seiLength = (seiLength + 7) >> 3; + + return seiLength; +} + EB_U32 GetRecoveryPointSEILength( AppRecoveryPoint_t *recoveryPointSeiPtr) { diff --git a/Source/Lib/Codec/EbSei.h b/Source/Lib/Codec/EbSei.h index 3c5ae29f0..22d9e396f 100644 --- a/Source/Lib/Codec/EbSei.h +++ b/Source/Lib/Codec/EbSei.h @@ -167,6 +167,14 @@ extern "C" { } AppBufferingPeriodSei_t; + typedef struct AppActiveParameterSetsSei_s { + EB_U32 activeVideoParameterSetid; + EB_BOOL selfContainedCvsFlag; + EB_BOOL noParameterSetUpdateFlag; + EB_U32 numSpsIdsMinus1; + EB_U32 activeSeqParameterSetId; + + } AppActiveparameterSetSei_t; typedef struct AppRecoveryPoint_s { @@ -296,6 +304,9 @@ extern void EbPictureTimeingSeiCtor( extern void EbBufferingPeriodSeiCtor( AppBufferingPeriodSei_t *bufferingPeriodPtr); +extern void EbActiveParameterSetSeiCtor( + AppActiveparameterSetSei_t *activeParameterPtr); + extern void EbRecoveryPointSeiCtor( AppRecoveryPoint_t *recoveryPointSeiPtr); @@ -307,6 +318,9 @@ extern EB_U32 GetBufPeriodSEILength( AppBufferingPeriodSei_t *bufferingPeriodPtr, AppVideoUsabilityInfo_t *vuiPtr); +extern EB_U32 GetActiveParameterSetSEILength( + AppActiveparameterSetSei_t *activeParameterSet); + extern EB_U32 GetRecoveryPointSEILength( AppRecoveryPoint_t *recoveryPointSeiPtr); #ifdef __cplusplus diff --git a/Source/Lib/Codec/EbSequenceControlSet.c b/Source/Lib/Codec/EbSequenceControlSet.c index 1a2438234..7cfa79134 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.c +++ b/Source/Lib/Codec/EbSequenceControlSet.c @@ -141,6 +141,10 @@ EB_ERRORTYPE EbSequenceControlSetCtor( EbBufferingPeriodSeiCtor( &sequenceControlSetPtr->bufferingPeriod); + //Initilaize Active Parametr Set SEI + EbActiveParameterSetSeiCtor( + &sequenceControlSetPtr->activeParameterSet); + // Initialize picture timing SEI EbRecoveryPointSeiCtor( &sequenceControlSetPtr->recoveryPoint); @@ -236,6 +240,13 @@ EB_ERRORTYPE CopySequenceControlSet( writeCount += sizeof(AppBufferingPeriodSei_t); + EB_MEMCPY( + &dst->activeParameterSet, + &src->activeParameterSet, + sizeof(AppActiveparameterSetSei_t)); + + writeCount += sizeof(AppActiveparameterSetSei_t); + EB_MEMCPY( &dst->recoveryPoint, &src->recoveryPoint, diff --git a/Source/Lib/Codec/EbSequenceControlSet.h b/Source/Lib/Codec/EbSequenceControlSet.h index 398289478..e2d05a210 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.h +++ b/Source/Lib/Codec/EbSequenceControlSet.h @@ -178,6 +178,9 @@ typedef struct SequenceControlSet_s EB_U8 maxEncMode; + //Active parameter Set Sei + AppActiveparameterSetSei_t activeParameterSet; + } SequenceControlSet_t; typedef struct EbSequenceControlSetInitData_s From 38b744d4b07c1174a3482b8a46a71f130936f63f Mon Sep 17 00:00:00 2001 From: Aruna Matheswaran Date: Fri, 25 Jan 2019 18:26:43 +0530 Subject: [PATCH 10/93] Control VBV bufferFill access using mutex --- Source/Lib/Codec/EbEncodeContext.c | 5 ++++- Source/Lib/Codec/EbEncodeContext.h | 2 +- Source/Lib/Codec/EbPacketizationProcess.c | 3 +++ Source/Lib/Codec/EbRateControlProcess.c | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Lib/Codec/EbEncodeContext.c b/Source/Lib/Codec/EbEncodeContext.c index a171d0ebb..5c9965025 100644 --- a/Source/Lib/Codec/EbEncodeContext.c +++ b/Source/Lib/Codec/EbEncodeContext.c @@ -226,7 +226,10 @@ EB_ERRORTYPE EncodeContextCtor( encodeContextPtr->scBuffer = 0; encodeContextPtr->scFrameIn = 0; encodeContextPtr->scFrameOut = 0; - encodeContextPtr->encMode = SPEED_CONTROL_INIT_MOD; + encodeContextPtr->encMode = SPEED_CONTROL_INIT_MOD; + + EB_CREATEMUTEX(EB_HANDLE, encodeContextPtr->bufferFillMutex, sizeof(EB_HANDLE), EB_MUTEX); + encodeContextPtr->previousSelectedRefQp = 32; encodeContextPtr->maxCodedPoc = 0; encodeContextPtr->maxCodedPocSelectedRefQp = 32; diff --git a/Source/Lib/Codec/EbEncodeContext.h b/Source/Lib/Codec/EbEncodeContext.h index e89d67ad0..a5541989f 100644 --- a/Source/Lib/Codec/EbEncodeContext.h +++ b/Source/Lib/Codec/EbEncodeContext.h @@ -147,7 +147,7 @@ typedef struct EncodeContext_s EB_U32 vbvMaxrate; EB_U32 vbvBufsize; EB_U64 bufferFill; - + EB_HANDLE bufferFillMutex; EB_U32 previousSelectedRefQp; EB_U64 maxCodedPoc; diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 58a61028a..bbcdd1731 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -664,6 +664,7 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->nTickCount = (EB_U32)latency; EbPostFullObject(outputStreamWrapperPtr); /* update VBV plan */ + EbBlockOnMutex(encodeContextPtr->bufferFillMutex); if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) { EB_S64 bufferfill_temp = (EB_S64)(encodeContextPtr->bufferFill); @@ -675,6 +676,8 @@ void* PacketizationKernel(void *inputPtr) encodeContextPtr->bufferFill = (EB_U64)(bufferfill_temp); } + EbReleaseMutex(encodeContextPtr->bufferFillMutex); + // Reset the Reorder Queue Entry queueEntryPtr->pictureNumber += PACKETIZATION_REORDER_QUEUE_MAX_DEPTH; queueEntryPtr->outputStreamWrapperPtr = (EbObjectWrapper_t *)EB_NULL; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 2a95ac808..7ec3b302f 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -756,6 +756,7 @@ void HighLevelRcInputPictureMode2( if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) { + EbBlockOnMutex(encodeContextPtr->bufferFillMutex); /* Lookahead VBV: If lookahead is done, raise the quantizer as necessary * such that no frames in the lookahead overflow and such that the buffer * is in a reasonable state by the end of the lookahead. */ @@ -836,6 +837,7 @@ void HighLevelRcInputPictureMode2( } q = MAX(q0 / 2, q); selectedRefQp = q; + EbReleaseMutex(encodeContextPtr->bufferFillMutex); } #if RC_UPDATE_TARGET_RATE selectedOrgRefQp = selectedRefQp; From 853a45eacfe25ca04734fd03e0dc8cda4e2f37ee Mon Sep 17 00:00:00 2001 From: Aruna Matheswaran Date: Thu, 31 Jan 2019 20:56:50 +0530 Subject: [PATCH 11/93] Add cli -vbv-init and remove hard-coded vbv buffer initialization --- Source/API/EbApi.h | 1 + Source/App/EbAppConfig.c | 4 ++++ Source/App/EbAppConfig.h | 1 + Source/App/EbAppContext.c | 1 + Source/Lib/Codec/EbEncHandle.c | 5 +++++ Source/Lib/Codec/EbRateControlProcess.c | 2 +- 6 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index 18fd1cc5f..96e3797a8 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -205,6 +205,7 @@ typedef struct EB_H265_ENC_CONFIGURATION uint32_t vbvMaxrate; uint32_t vbvBufsize; uint32_t hrdFlag; + uint64_t vbvBufInit; // ASM Type uint32_t asmType; // level of optimization to use. diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index c4679b2b1..7fdcce440 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -73,6 +73,7 @@ #define TARGET_BIT_RATE_TOKEN "-tbr" #define VBV_MAX_RATE_TOKEN "-vbv-maxrate" #define VBV_BUFFER_SIZE_TOKEN "-vbv-bufsize" +#define VBV_BUFFER_INIT_TOKEN "-vbv-init" #define HRD_TOKEN "-hrd" #define MAX_QP_TOKEN "-max-qp" #define MIN_QP_TOKEN "-min-qp" @@ -182,6 +183,7 @@ static void SetBitRateReduction (const char *value, EbConfig_t * static void SetImproveSharpness (const char *value, EbConfig_t *cfg) {cfg->improveSharpness = (EB_BOOL)strtol(value, NULL, 0);}; static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0); }; static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0); }; +static void SetVbvBufInit (const char *value, EbConfig_t *cfg) { cfg->vbvBufInit = strtoul(value, NULL, 0); }; static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0); }; static void SetVideoUsabilityInfo (const char *value, EbConfig_t *cfg) {cfg->videoUsabilityInfo = strtol(value, NULL, 0);}; static void SetHighDynamicRangeInput (const char *value, EbConfig_t *cfg) {cfg->highDynamicRangeInput = strtol(value, NULL, 0);}; @@ -284,6 +286,7 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, VBV_MAX_RATE_TOKEN, "vbvMaxRate", SetVbvMaxrate }, { SINGLE_INPUT, VBV_BUFFER_SIZE_TOKEN, "vbvBufsize", SetVbvBufsize }, { SINGLE_INPUT, HRD_TOKEN, "hrd", SetHrdFlag }, + { SINGLE_INPUT, VBV_BUFFER_INIT_TOKEN, "vbvBufInit", SetVbvBufInit}, // DLF { SINGLE_INPUT, LOOP_FILTER_DISABLE_TOKEN, "LoopFilterDisable", SetDisableDlfFlag }, @@ -386,6 +389,7 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->encMode = 9; configPtr->vbvMaxRate = 0; configPtr->vbvBufsize = 0; + configPtr->vbvBufInit = 90; configPtr->hrdFlag = 0; configPtr->intraPeriod = -2; configPtr->intraRefreshType = 1; diff --git a/Source/App/EbAppConfig.h b/Source/App/EbAppConfig.h index 3f56af9e7..29870e9b0 100644 --- a/Source/App/EbAppConfig.h +++ b/Source/App/EbAppConfig.h @@ -306,6 +306,7 @@ typedef struct EbConfig_s uint32_t minQpAllowed; uint32_t vbvMaxRate; uint32_t vbvBufsize; + uint64_t vbvBufInit; /**************************************** * TUNE diff --git a/Source/App/EbAppContext.c b/Source/App/EbAppContext.c index 51fb81b77..54ec66686 100644 --- a/Source/App/EbAppContext.c +++ b/Source/App/EbAppContext.c @@ -182,6 +182,7 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.qp = config->qp; callbackData->ebEncParameters.vbvMaxrate = config->vbvMaxRate; callbackData->ebEncParameters.vbvBufsize = config->vbvBufsize; + callbackData->ebEncParameters.vbvBufInit = config->vbvBufInit; callbackData->ebEncParameters.useQpFile = (EB_BOOL)config->useQpFile; callbackData->ebEncParameters.disableDlfFlag = (EB_BOOL)config->disableDlfFlag; callbackData->ebEncParameters.enableSaoFlag = (EB_BOOL)config->enableSaoFlag; diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index b10c7bb2a..2cdeaedc6 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2061,6 +2061,7 @@ void CopyApiFromApp( sequenceControlSetPtr->staticConfig.rateControlMode = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->rateControlMode; sequenceControlSetPtr->staticConfig.vbvMaxrate = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvMaxrate; sequenceControlSetPtr->staticConfig.vbvBufsize = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufsize; + sequenceControlSetPtr->staticConfig.vbvBufInit = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufInit; sequenceControlSetPtr->staticConfig.lookAheadDistance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->lookAheadDistance; sequenceControlSetPtr->staticConfig.framesToBeEncoded = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->framesToBeEncoded; @@ -2674,6 +2675,10 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } + if (config->vbvBufInit > 100) { + printf("Error instance %u: Invalid vbvBufInit [0 - 100]\n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } return return_error; } diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 7ec3b302f..21c167d4b 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2390,7 +2390,7 @@ void* RateControlKernel(void *inputPtr) contextPtr->highLevelRateControlPtr->channelBitRatePerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerFrame * (sequenceControlSetPtr->staticConfig.lookAheadDistance + 1); contextPtr->highLevelRateControlPtr->bitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; - encodeContextPtr->bufferFill = (EB_U64)(sequenceControlSetPtr->staticConfig.vbvBufsize * 0.9); + encodeContextPtr->bufferFill = (EB_U64)(sequenceControlSetPtr->staticConfig.vbvBufsize * sequenceControlSetPtr->staticConfig.vbvBufInit / 100); #if RC_UPDATE_TARGET_RATE contextPtr->highLevelRateControlPtr->previousUpdatedBitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; #endif From 87f77291bbe6c8655bd2ebb65999507981495274 Mon Sep 17 00:00:00 2001 From: anaghdin Date: Sat, 26 Jan 2019 21:44:16 -0800 Subject: [PATCH 12/93] If rate control is ON, base layer picture will not be processed until the feedback of the previous base layer has arrived. The impact on speed to be evaluated --- Source/Lib/Codec/EbPictureManagerProcess.c | 4 ++++ Source/Lib/Codec/EbPictureManagerQueue.h | 1 + Source/Lib/Codec/EbRateControlProcess.c | 24 ++++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index 30fe2e101..15fec27c6 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -434,6 +434,7 @@ void* PictureManagerKernel(void *inputPtr) referenceEntryPtr->releaseEnable = EB_TRUE; referenceEntryPtr->referenceAvailable = EB_FALSE; referenceEntryPtr->isUsedAsReferenceFlag = pictureControlSetPtr->isUsedAsReferenceFlag; + referenceEntryPtr->feedbackArrived = EB_FALSE; encodeContextPtr->referencePictureQueueTailIndex = (encodeContextPtr->referencePictureQueueTailIndex == REFERENCE_QUEUE_MAX_DEPTH - 1) ? 0 : encodeContextPtr->referencePictureQueueTailIndex + 1; @@ -584,6 +585,7 @@ void* PictureManagerKernel(void *inputPtr) availabilityFlag = (availabilityFlag == EB_FALSE) ? EB_FALSE : // Don't update if already False (refPoc > currentInputPoc) ? EB_FALSE : // The Reference has not been received as an Input Picture yet, then its availability is false + (sequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived) ? EB_FALSE : (referenceEntryPtr->referenceAvailable) ? EB_TRUE : // The Reference has been completed EB_FALSE; // The Reference has not been completed } @@ -620,6 +622,7 @@ void* PictureManagerKernel(void *inputPtr) availabilityFlag = (availabilityFlag == EB_FALSE) ? EB_FALSE : // Don't update if already False (refPoc > currentInputPoc) ? EB_FALSE : // The Reference has not been received as an Input Picture yet, then its availability is false + (sequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived) ? EB_FALSE : (referenceEntryPtr->referenceAvailable) ? EB_TRUE : // The Reference has been completed EB_FALSE; // The Reference has not been completed } @@ -628,6 +631,7 @@ void* PictureManagerKernel(void *inputPtr) if(availabilityFlag == EB_TRUE) { + //printf("PICTURE MANAGER RELEASE %d\n", (int)entryPictureControlSetPtr->pictureNumber); // Get New Empty Child PCS from PCS Pool EbGetEmptyObject( contextPtr->pictureControlSetFifoPtrArray[0], diff --git a/Source/Lib/Codec/EbPictureManagerQueue.h b/Source/Lib/Codec/EbPictureManagerQueue.h index c221bed47..922bcaca9 100644 --- a/Source/Lib/Codec/EbPictureManagerQueue.h +++ b/Source/Lib/Codec/EbPictureManagerQueue.h @@ -51,6 +51,7 @@ typedef struct ReferenceQueueEntry_s { EB_BOOL isUsedAsReferenceFlag; EB_U64 rcGroupIndex; + EB_BOOL feedbackArrived; } ReferenceQueueEntry_t; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 21c167d4b..799fccda4 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2665,6 +2665,30 @@ void* RateControlKernel(void *inputPtr) parentPictureControlSetPtr = (PictureParentControlSet_t*)rateControlTasksPtr->pictureControlSetWrapperPtr->objectPtr; sequenceControlSetPtr = (SequenceControlSet_t*)parentPictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; + + // Update feedback arrived in referencepictureQueue + // printf("RC FEEDBACK ARRIVED %d\n", (int)parentPictureControlSetPtr->pictureNumber); + + ReferenceQueueEntry_t *referenceEntryPtr; + EB_U32 referenceQueueIndex; + encodeContextPtr = sequenceControlSetPtr->encodeContextPtr; + referenceQueueIndex = encodeContextPtr->referencePictureQueueHeadIndex; + // Find the Reference in the Reference Queue + do { + + referenceEntryPtr = encodeContextPtr->referencePictureQueue[referenceQueueIndex]; + + if (referenceEntryPtr->pictureNumber == parentPictureControlSetPtr->pictureNumber) { + + // Set the feedback arrived + referenceEntryPtr->feedbackArrived = EB_TRUE; + } + + // Increment the referenceQueueIndex Iterator + referenceQueueIndex = (referenceQueueIndex == REFERENCE_QUEUE_MAX_DEPTH - 1) ? 0 : referenceQueueIndex + 1; + + } while ((referenceQueueIndex != encodeContextPtr->referencePictureQueueTailIndex) && (referenceEntryPtr->pictureNumber != parentPictureControlSetPtr->pictureNumber)); + // Frame level RC if (sequenceControlSetPtr->intraPeriodLength == -1 || sequenceControlSetPtr->staticConfig.rateControlMode == 0){ rateControlParamPtr = contextPtr->rateControlParamQueue[0]; From 708bd265314b13efae0925ad953f4e5d00b73e41 Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Mon, 18 Feb 2019 11:54:56 +0530 Subject: [PATCH 13/93] VBV Code clean up and made VBV as function call --- Source/Lib/Codec/EbRateControlProcess.c | 304 +++++++++++++----------- 1 file changed, 159 insertions(+), 145 deletions(-) diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 799fccda4..54236c2be 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -350,61 +350,6 @@ EB_ERRORTYPE RateControlContextCtor( return EB_ErrorNone; } -EB_U64 predictBits(SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp, EB_U32 qp) -{ - EB_U64 totalBits = 0; - if (hlRateControlHistogramPtrTemp->isCoded) { - // If the frame is already coded, use the actual number of bits - totalBits = hlRateControlHistogramPtrTemp->totalNumBitsCoded; - } - else { - RateControlTables_t *rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qp]; - EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[hlRateControlHistogramPtrTemp->temporalLayerIndex]; - EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; - EB_U32 predBitsRefQp = 0; - EB_U32 numOfFullLcus = 0; - EB_U32 areaInPixel = sequenceControlSetPtr->lumaWidth * sequenceControlSetPtr->lumaHeight; - - if (hlRateControlHistogramPtrTemp->sliceType == EB_I_PICTURE) { - // Loop over block in the frame and calculated the predicted bits at reg QP - EB_U32 i; - EB_U32 accum = 0; - for (i = 0; i < NUMBER_OF_INTRA_SAD_INTERVALS; ++i) - { - accum += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); - } - - predBitsRefQp = accum; - numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; - totalBits += predBitsRefQp; - } - else { - EB_U32 i; - EB_U32 accum = 0; - EB_U32 accumIntra = 0; - for (i = 0; i < NUMBER_OF_SAD_INTERVALS; ++i) - { - accum += (EB_U32)(hlRateControlHistogramPtrTemp->meDistortionHistogram[i] * sadBitsArrayPtr[i]); - accumIntra += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); - - } - if (accum > accumIntra * 3) - predBitsRefQp = accumIntra; - else - predBitsRefQp = accum; - numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; - totalBits += predBitsRefQp; - } - - // Scale for in complete LCSs - // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries - totalBits = totalBits * (EB_U64)areaInPixel / (numOfFullLcus << 12); - } - hlRateControlHistogramPtrTemp->predBitsRefQp[qp] = totalBits; - return totalBits; -} - - void HighLevelRcInputPictureMode2( PictureParentControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr, @@ -754,91 +699,6 @@ void HighLevelRcInputPictureMode2( bestQp = previousSelectedRefQp; } - if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) - { - EbBlockOnMutex(encodeContextPtr->bufferFillMutex); - /* Lookahead VBV: If lookahead is done, raise the quantizer as necessary - * such that no frames in the lookahead overflow and such that the buffer - * is in a reasonable state by the end of the lookahead. */ - EB_U32 loopTerminate = 0; - EB_U32 q = bestQp; - EB_U32 q0 = bestQp; - - - queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - encodeContextPtr->hlRateControlHistorgramQueue[encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); - queueEntryIndexHeadTemp += encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; - queueEntryIndexHeadTemp = (queueEntryIndexHeadTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? - queueEntryIndexHeadTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : - queueEntryIndexHeadTemp; - - queueEntryIndexTemp = queueEntryIndexHeadTemp; - - EB_U32 currentInd = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; - - /* Avoid an infinite loop. */ - for (EB_U32 iterations = 0; iterations < 1000 && loopTerminate != 3; iterations++) - { - hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[currentInd]); - double curBits = (double)predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, q); - double bufferFillCur = encodeContextPtr->bufferFill - curBits; - double targetFill; - double fps = 1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION); - double totalDuration = fps; - queueEntryIndexTemp = currentInd; - - /* Loop over the planned future frames. */ - for (EB_U32 j = 0; bufferFillCur >= 0; j++) - { - queueEntryIndexTemp2 = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; - hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[queueEntryIndexTemp2]); - /* This is set to false, so the last frame would go inside the loop */ - endOfSequenceFlag = EB_FALSE; - - if ((endOfSequenceFlag || - queueEntryIndexTemp >= (currentInd + sequenceControlSetPtr->staticConfig.lookAheadDistance)) || - (queueEntryIndexTemp >= sequenceControlSetPtr->staticConfig.framesToBeEncoded) - || (totalDuration >= 1.0)) - break; - - totalDuration += fps; - double wantedFrameSize = encodeContextPtr->vbvMaxrate * fps; - if (bufferFillCur + wantedFrameSize <= encodeContextPtr->vbvBufsize) - bufferFillCur += wantedFrameSize; - curBits = (double)predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, q); - bufferFillCur -= curBits; - queueEntryIndexTemp++; - } - /* Try to get the buffer at least 50% filled, but don't set an impossible goal. */ - double finalDur = 1; - targetFill = MIN(encodeContextPtr->bufferFill + totalDuration * encodeContextPtr->vbvMaxrate * 0.5, encodeContextPtr->vbvBufsize * (1 - 0.5 * finalDur)); - if (bufferFillCur < targetFill) - { - q++; - q = CLIP3( - sequenceControlSetPtr->staticConfig.minQpAllowed, - sequenceControlSetPtr->staticConfig.maxQpAllowed, - q); - loopTerminate |= 1; - continue; - } - /* Try to get the buffer not more than 80% filled, but don't set an impossible goal. */ - targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.2 * finalDur), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); - if (bufferFillCur > targetFill) - { - q--; - q = CLIP3( - sequenceControlSetPtr->staticConfig.minQpAllowed, - sequenceControlSetPtr->staticConfig.maxQpAllowed, - q); - loopTerminate |= 2; - continue; - } - break; - } - q = MAX(q0 / 2, q); - selectedRefQp = q; - EbReleaseMutex(encodeContextPtr->bufferFillMutex); - } #if RC_UPDATE_TARGET_RATE selectedOrgRefQp = selectedRefQp; if (sequenceControlSetPtr->intraPeriodLength != -1 && pictureControlSetPtr->pictureNumber % ((sequenceControlSetPtr->intraPeriodLength + 1)) == 0 && @@ -2323,6 +2183,153 @@ void HighLevelRcFeedBackPicture( } } + +EB_U64 predictBits(SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp, EB_U32 qp) +{ + EB_U64 totalBits = 0; + if (hlRateControlHistogramPtrTemp->isCoded) { + // If the frame is already coded, use the actual number of bits + totalBits = hlRateControlHistogramPtrTemp->totalNumBitsCoded; + } + else { + RateControlTables_t *rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qp]; + EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[hlRateControlHistogramPtrTemp->temporalLayerIndex]; + EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; + EB_U32 predBitsRefQp = 0; + EB_U32 numOfFullLcus = 0; + EB_U32 areaInPixel = sequenceControlSetPtr->lumaWidth * sequenceControlSetPtr->lumaHeight; + + if (hlRateControlHistogramPtrTemp->sliceType == EB_I_PICTURE) { + // Loop over block in the frame and calculated the predicted bits at reg QP + EB_U32 i; + EB_U32 accum = 0; + for (i = 0; i < NUMBER_OF_INTRA_SAD_INTERVALS; ++i) + { + accum += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); + } + + predBitsRefQp = accum; + numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; + totalBits += predBitsRefQp; + } + else { + EB_U32 i; + EB_U32 accum = 0; + EB_U32 accumIntra = 0; + for (i = 0; i < NUMBER_OF_SAD_INTERVALS; ++i) + { + accum += (EB_U32)(hlRateControlHistogramPtrTemp->meDistortionHistogram[i] * sadBitsArrayPtr[i]); + accumIntra += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); + + } + if (accum > accumIntra * 3) + predBitsRefQp = accumIntra; + else + predBitsRefQp = accum; + numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; + totalBits += predBitsRefQp; + } + + // Scale for in complete LCSs + // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries + totalBits = totalBits * (EB_U64)areaInPixel / (numOfFullLcus << 12); + } + hlRateControlHistogramPtrTemp->predBitsRefQp[qp] = totalBits; + return totalBits; +} + +EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr) +{ + EB_S32 loopTerminate = 0; + EB_U32 q = pictureControlSetPtr->pictureQp; + EB_U32 q0 = pictureControlSetPtr->pictureQp; + // Queue variables + EB_U32 queueEntryIndexTemp; + EB_U32 queueEntryIndexTemp2; + EB_U32 queueEntryIndexHeadTemp; + EB_BOOL endOfSequenceFlag = EB_TRUE; + + HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp; + + /* Lookahead VBV: If lookahead is done, raise the quantizer as necessary + * such that no frames in the lookahead overflow and such that the buffer + * is in a reasonable state by the end of the lookahead. */ + + queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - encodeContextPtr->hlRateControlHistorgramQueue[encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); + queueEntryIndexHeadTemp += encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; + queueEntryIndexHeadTemp = (queueEntryIndexHeadTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? + queueEntryIndexHeadTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : + queueEntryIndexHeadTemp; + + queueEntryIndexTemp = queueEntryIndexHeadTemp; + + EB_S32 currentInd = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; + + + /* Avoid an infinite loop. */ + for (EB_S32 iterations = 0; iterations < 1000 && loopTerminate != 3; iterations++) + { + hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[currentInd]); + double curBits = (double)predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, q); + double bufferFillCur = encodeContextPtr->bufferFill - curBits; + double targetFill; + double fps = 1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION); + double totalDuration = fps; + queueEntryIndexTemp = currentInd; + + /* Loop over the planned future frames. */ + for (EB_S32 j = 0; bufferFillCur >= 0; j++) + { + queueEntryIndexTemp2 = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; + hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[queueEntryIndexTemp2]); + /* This is set to false, so the last frame would go inside the loop */ + endOfSequenceFlag = EB_FALSE; + + if ((endOfSequenceFlag || + queueEntryIndexTemp >= (currentInd + sequenceControlSetPtr->staticConfig.lookAheadDistance)) || + (queueEntryIndexTemp >= sequenceControlSetPtr->staticConfig.framesToBeEncoded) + || (totalDuration >= 1.0)) + break; + + totalDuration += fps; + double wantedFrameSize = encodeContextPtr->vbvMaxrate * fps; + if (bufferFillCur + wantedFrameSize <= encodeContextPtr->vbvBufsize) + bufferFillCur += wantedFrameSize; + curBits = (double)predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, q); + bufferFillCur -= curBits; + queueEntryIndexTemp++; + } + /* Try to get the buffer at least 50% filled, but don't set an impossible goal. */ + double finalDur = 1; + targetFill = MIN(encodeContextPtr->bufferFill + totalDuration * encodeContextPtr->vbvMaxrate * 0.5, encodeContextPtr->vbvBufsize * (1 - 0.5 * finalDur)); + if (bufferFillCur < targetFill) + { + q++; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 1; + continue; + } + /* Try to get the buffer not more than 80% filled, but don't set an impossible goal. */ + targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.2 * finalDur), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); + if (bufferFillCur > targetFill) + { + q--; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 2; + continue; + } + break; + } + q = MAX(q0 / 2, q); + return (EB_U8)q; +} + void* RateControlKernel(void *inputPtr) { // Context @@ -2435,9 +2442,8 @@ void* RateControlKernel(void *inputPtr) contextPtr, contextPtr->highLevelRateControlPtr); - - } - + } + // Frame level RC if (sequenceControlSetPtr->intraPeriodLength == -1 || sequenceControlSetPtr->staticConfig.rateControlMode == 0){ rateControlParamPtr = contextPtr->rateControlParamQueue[0]; @@ -2631,8 +2637,16 @@ void* RateControlKernel(void *inputPtr) } } } - pictureControlSetPtr->ParentPcsPtr->averageQp = 0; - for (lcuCodingOrder = 0; lcuCodingOrder < lcuTotalCount; ++lcuCodingOrder) { + pictureControlSetPtr->ParentPcsPtr->averageQp = 0; + + if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) + { + EbBlockOnMutex(encodeContextPtr->bufferFillMutex); + pictureControlSetPtr->pictureQp = (EB_U8)Vbv_Buf_Calc(pictureControlSetPtr, sequenceControlSetPtr, encodeContextPtr); + + EbReleaseMutex(encodeContextPtr->bufferFillMutex); + } + for (lcuCodingOrder = 0; lcuCodingOrder < lcuTotalCount; ++lcuCodingOrder) { lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuCodingOrder]; lcuPtr->qp = (EB_U8)pictureControlSetPtr->pictureQp; From 32dc411cb9d359a9d2251ab3ac37e3a81ffbed69 Mon Sep 17 00:00:00 2001 From: anaghdin Date: Mon, 18 Feb 2019 13:06:47 -0800 Subject: [PATCH 14/93] Fix for the deadlock encoding frames fewer than 48 --- Source/Lib/Codec/EbPictureManagerProcess.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index 15fec27c6..bba7acd5f 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -585,7 +585,7 @@ void* PictureManagerKernel(void *inputPtr) availabilityFlag = (availabilityFlag == EB_FALSE) ? EB_FALSE : // Don't update if already False (refPoc > currentInputPoc) ? EB_FALSE : // The Reference has not been received as an Input Picture yet, then its availability is false - (sequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived) ? EB_FALSE : + (!encodeContextPtr->terminatingSequenceFlagReceived && (sequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived)) ? EB_FALSE : (referenceEntryPtr->referenceAvailable) ? EB_TRUE : // The Reference has been completed EB_FALSE; // The Reference has not been completed } @@ -622,7 +622,7 @@ void* PictureManagerKernel(void *inputPtr) availabilityFlag = (availabilityFlag == EB_FALSE) ? EB_FALSE : // Don't update if already False (refPoc > currentInputPoc) ? EB_FALSE : // The Reference has not been received as an Input Picture yet, then its availability is false - (sequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived) ? EB_FALSE : + (!encodeContextPtr->terminatingSequenceFlagReceived && (sequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived)) ? EB_FALSE : (referenceEntryPtr->referenceAvailable) ? EB_TRUE : // The Reference has been completed EB_FALSE; // The Reference has not been completed } From 88d1befed9e1dd8ad63206e8676f1c891154b122 Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 22 Feb 2019 16:34:29 +0530 Subject: [PATCH 15/93] Move vbv algorithm before using picture Qp for other RC context updates --- Source/Lib/Codec/EbRateControlProcess.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 54236c2be..71d656b38 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2612,6 +2612,13 @@ void* RateControlKernel(void *inputPtr) pictureControlSetPtr->pictureQp); } } + if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) + { + EbBlockOnMutex(encodeContextPtr->bufferFillMutex); + pictureControlSetPtr->pictureQp = (EB_U8)Vbv_Buf_Calc(pictureControlSetPtr, sequenceControlSetPtr, encodeContextPtr); + + EbReleaseMutex(encodeContextPtr->bufferFillMutex); + } pictureControlSetPtr->ParentPcsPtr->pictureQp = pictureControlSetPtr->pictureQp; if (pictureControlSetPtr->ParentPcsPtr->temporalLayerIndex == 0 && sequenceControlSetPtr->staticConfig.lookAheadDistance != 0){ contextPtr->baseLayerFramesAvgQp = (3 * contextPtr->baseLayerFramesAvgQp + pictureControlSetPtr->pictureQp + 2) >> 2; @@ -2639,13 +2646,6 @@ void* RateControlKernel(void *inputPtr) } pictureControlSetPtr->ParentPcsPtr->averageQp = 0; - if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) - { - EbBlockOnMutex(encodeContextPtr->bufferFillMutex); - pictureControlSetPtr->pictureQp = (EB_U8)Vbv_Buf_Calc(pictureControlSetPtr, sequenceControlSetPtr, encodeContextPtr); - - EbReleaseMutex(encodeContextPtr->bufferFillMutex); - } for (lcuCodingOrder = 0; lcuCodingOrder < lcuTotalCount; ++lcuCodingOrder) { lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuCodingOrder]; From f4d19b8135e89bc50944f955861aaa26070f2783 Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 22 Feb 2019 19:35:33 +0530 Subject: [PATCH 16/93] Fix Warning: When NAL Unit is non-VCL NAL, TemporalId shall be greater than or equal to the TemporalId of the access unit containing the NAL unit --- Source/Lib/Codec/EbEntropyCoding.c | 5 +++-- Source/Lib/Codec/EbEntropyCoding.h | 3 ++- Source/Lib/Codec/EbPacketizationProcess.c | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index 871b84704..32e255790 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -8212,7 +8212,8 @@ EB_ERRORTYPE EncodePictureTimingSEI( AppPictureTimingSei_t *picTimingSeiPtr, AppVideoUsabilityInfo_t *vuiPtr, EncodeContext_t *encodeContextPtr, - EB_U8 pictStruct) + EB_U8 pictStruct, + EB_U8 temporalId) { EB_ERRORTYPE return_error = EB_ErrorNone; @@ -8226,7 +8227,7 @@ EB_ERRORTYPE EncodePictureTimingSEI( CodeNALUnitHeader( outputBitstreamPtr, NAL_UNIT_PREFIX_SEI, - 0); + temporalId); payloadSize = GetPictureTimingSEILength( picTimingSeiPtr, diff --git a/Source/Lib/Codec/EbEntropyCoding.h b/Source/Lib/Codec/EbEntropyCoding.h index 828cf5843..b39a5ab54 100644 --- a/Source/Lib/Codec/EbEntropyCoding.h +++ b/Source/Lib/Codec/EbEntropyCoding.h @@ -165,7 +165,8 @@ extern EB_ERRORTYPE EncodePictureTimingSEI( AppPictureTimingSei_t *picTimingSeiPtr, AppVideoUsabilityInfo_t *vuiPtr, EncodeContext_t *encodeContextPtr, - EB_U8 pictStruct); + EB_U8 pictStruct, + EB_U8 temporalId); extern EB_ERRORTYPE EncodeActiveParameterSetsSEI( Bitstream_t *bitstreamPtr, diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index bbcdd1731..5ad7cefe5 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -551,7 +551,8 @@ void* PacketizationKernel(void *inputPtr) &sequenceControlSetPtr->picTimingSei, sequenceControlSetPtr->videoUsabilityInfoPtr, sequenceControlSetPtr->encodeContextPtr, - pictureControlSetPtr->ParentPcsPtr->pictStruct); + pictureControlSetPtr->ParentPcsPtr->pictStruct, + pictureControlSetPtr->temporalId); } From cddec5a4f093bd4b77e51a988abcc925ddf4f782 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 25 Feb 2019 16:58:34 +0530 Subject: [PATCH 17/93] Fix bug:The value of rbsp_stop_one_bit shall be equal to 1 in vps --- Source/Lib/Codec/EbEntropyCoding.c | 68 +++++++++++++++--------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index 32e255790..d4befb992 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -5289,46 +5289,48 @@ static void CodeVPS( WriteFlagCavlc( bitstreamPtr, scsPtr->staticConfig.fpsInVps == 1 ? EB_TRUE : EB_FALSE); + if (scsPtr->staticConfig.fpsInVps == 1) + { - if (scsPtr->staticConfig.frameRateDenominator != 0 && scsPtr->staticConfig.frameRateNumerator != 0) { - - // vps_num_units_in_tick - WriteCodeCavlc( - bitstreamPtr, - scsPtr->staticConfig.frameRateNumerator, - 32); + if (scsPtr->staticConfig.frameRateDenominator != 0 && scsPtr->staticConfig.frameRateNumerator != 0) { - // vps_time_scale - WriteCodeCavlc( - bitstreamPtr, - scsPtr->staticConfig.frameRateDenominator, - 32); - } - else { - // vps_num_units_in_tick - WriteCodeCavlc( - bitstreamPtr, - scsPtr->frameRate > 1000 ? scsPtr->frameRate : scsPtr->frameRate << 16, - 32); + // vps_num_units_in_tick + WriteCodeCavlc( + bitstreamPtr, + scsPtr->staticConfig.frameRateNumerator, + 32); - // vps_time_scale - WriteCodeCavlc( - bitstreamPtr, - 1 << 16, - 32); - } + // vps_time_scale + WriteCodeCavlc( + bitstreamPtr, + scsPtr->staticConfig.frameRateDenominator, + 32); + } + else { + // vps_num_units_in_tick + WriteCodeCavlc( + bitstreamPtr, + scsPtr->frameRate > 1000 ? scsPtr->frameRate : scsPtr->frameRate << 16, + 32); - // vps_poc_proportional_to_timing_flag - WriteFlagCavlc( - bitstreamPtr, - 0); + // vps_time_scale + WriteCodeCavlc( + bitstreamPtr, + 1 << 16, + 32); + } - // vps_num_hrd_parameters - WriteUvlc( - bitstreamPtr, - 0); + // vps_poc_proportional_to_timing_flag + WriteFlagCavlc( + bitstreamPtr, + 0); + // vps_num_hrd_parameters + WriteUvlc( + bitstreamPtr, + 0); + } // "vps_extension_flag" WriteFlagCavlc( From 96524c4481618be57fbd31bc1dc96bff218e3829 Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 26 Feb 2019 18:43:57 +0530 Subject: [PATCH 18/93] Fix cbr signalling --- Source/Lib/Codec/EbPacketizationProcess.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 5ad7cefe5..d6730b49f 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -73,6 +73,8 @@ void InitHRD(SequenceControlSet_t *scsPtr) { hrd->bitRateValueMinus1[i][j][k] = (scsPtr->staticConfig.vbvMaxrate >> (hrd->bitRateScale + BR_SHIFT))-1; hrd->cpbSizeValueMinus1[i][j][k] = (scsPtr->staticConfig.vbvBufsize >> (hrd->cpbSizeScale + CPB_SHIFT))-1; + if (scsPtr->staticConfig.vbvMaxrate == scsPtr->staticConfig.targetBitRate) + hrd->cbrFlag[i][j][k] = 1; } EB_U32 bitRateUnscale = ((scsPtr->staticConfig.vbvMaxrate >> (hrd->bitRateScale + BR_SHIFT)) << (hrd->bitRateScale + BR_SHIFT)); EB_U32 cpbSizeUnscale = ((scsPtr->staticConfig.vbvBufsize >> (hrd->cpbSizeScale + CPB_SHIFT)) << (hrd->cpbSizeScale + CPB_SHIFT)); @@ -214,6 +216,7 @@ void* PacketizationKernel(void *inputPtr) ComputeProfileTierLevelInfo( sequenceControlSetPtr); + ComputeMaxDpbBuffer( sequenceControlSetPtr); From a199b68aa4c8379aedcd46f439f3e5f6199d48a0 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 27 Feb 2019 20:06:40 +0530 Subject: [PATCH 19/93] Fix Hrd Issue This fix ensures right signalling of Picture timing for keyframes>0 --- Source/Lib/Codec/EbPacketizationProcess.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index d6730b49f..b319f656f 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -148,7 +148,8 @@ void* PacketizationKernel(void *inputPtr) EB_PICTURE sliceType; - EB_U64 prevBPpictureNumber =0; + EB_U64 prevBPpictureNumber; + EB_U64 lastBpnumber; for(;;) { // Get EntropyCoding Results @@ -517,7 +518,7 @@ void* PacketizationKernel(void *inputPtr) // Parsing the linked list and find the user data SEI msgs and code them sequenceControlSetPtr->picTimingSei.picStruct = 0; - + prevBPpictureNumber = pictureControlSetPtr->pictureNumber == 0 ? 0 : lastBpnumber; if( sequenceControlSetPtr->staticConfig.bufferingPeriodSEI && pictureControlSetPtr->sliceType == EB_I_PICTURE && sequenceControlSetPtr->staticConfig.videoUsabilityInfo && @@ -527,7 +528,7 @@ void* PacketizationKernel(void *inputPtr) if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) { HrdFullness(sequenceControlSetPtr, pictureControlSetPtr, &sequenceControlSetPtr->bufferingPeriod); - prevBPpictureNumber = pictureControlSetPtr->ParentPcsPtr->decodeOrder; + lastBpnumber= pictureControlSetPtr->ParentPcsPtr->decodeOrder; } EncodeBufferingPeriodSEI( pictureControlSetPtr->bitstreamPtr, @@ -546,7 +547,7 @@ void* PacketizationKernel(void *inputPtr) const AppVideoUsabilityInfo_t* vui = sequenceControlSetPtr->videoUsabilityInfoPtr; const AppHrdParameters_t* hrd = vui->hrdParametersPtr; sequenceControlSetPtr->picTimingSei.auCpbRemovalDelayMinus1 = (EB_U32)((MIN(MAX(1, (EB_S32)pictureControlSetPtr->ParentPcsPtr->decodeOrder - (EB_S32)prevBPpictureNumber), (1 << hrd->auCpbRemovalDelayLengthMinus1)))-1); - sequenceControlSetPtr->picTimingSei.picDpbOutputDelay = (EB_U32)(sequenceControlSetPtr->maxDpbSize + pictureControlSetPtr->pictureNumber - pictureControlSetPtr->ParentPcsPtr->decodeOrder); + sequenceControlSetPtr->picTimingSei.picDpbOutputDelay = (EB_U32)((sequenceControlSetPtr->maxDpbSize-1) + pictureControlSetPtr->pictureNumber - pictureControlSetPtr->ParentPcsPtr->decodeOrder); } EncodePictureTimingSEI( From e692a043be1f01bde9b4730017d4666852091fbd Mon Sep 17 00:00:00 2001 From: kirithika Date: Thu, 28 Feb 2019 10:53:06 +0530 Subject: [PATCH 20/93] Fix uninitialized BPnumber value error when hrd is disabled --- Source/Lib/Codec/EbPacketizationProcess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index b319f656f..dd2372ac3 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -149,7 +149,7 @@ void* PacketizationKernel(void *inputPtr) EB_PICTURE sliceType; EB_U64 prevBPpictureNumber; - EB_U64 lastBpnumber; + EB_U64 lastBpnumber=0; for(;;) { // Get EntropyCoding Results From c8a7166e3f899bc3a66f0aaa345434c8e8aa2326 Mon Sep 17 00:00:00 2001 From: kirithika Date: Thu, 28 Feb 2019 19:14:01 +0530 Subject: [PATCH 21/93] Fix Hrd signalling for fixed GOP length --- Source/Lib/Codec/EbEncodeContext.c | 2 +- Source/Lib/Codec/EbEncodeContext.h | 2 +- Source/Lib/Codec/EbPacketizationProcess.c | 22 +++++++++++++++------ Source/Lib/Codec/EbPictureControlSet.c | 1 + Source/Lib/Codec/EbPictureControlSet.h | 1 + Source/Lib/Codec/EbPictureDecisionProcess.c | 7 ++++++- 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Source/Lib/Codec/EbEncodeContext.c b/Source/Lib/Codec/EbEncodeContext.c index 5c9965025..ee0b5be08 100644 --- a/Source/Lib/Codec/EbEncodeContext.c +++ b/Source/Lib/Codec/EbEncodeContext.c @@ -179,7 +179,7 @@ EB_ERRORTYPE EncodeContextCtor( encodeContextPtr->initialPicture = EB_TRUE; encodeContextPtr->lastIdrPicture = 0; - + encodeContextPtr->lastIdrPictureOrder = 0; // Sequence Termination Flags encodeContextPtr->terminatingPictureNumber = ~0u; encodeContextPtr->terminatingSequenceFlagReceived = EB_FALSE; diff --git a/Source/Lib/Codec/EbEncodeContext.h b/Source/Lib/Codec/EbEncodeContext.h index a5541989f..14f8df8d4 100644 --- a/Source/Lib/Codec/EbEncodeContext.h +++ b/Source/Lib/Codec/EbEncodeContext.h @@ -114,7 +114,7 @@ typedef struct EncodeContext_s EB_BOOL initialPicture; EB_U64 lastIdrPicture; // the most recently occured IDR picture (in decode order) - + EB_U64 lastIdrPictureOrder; // Sequence Termination Flags EB_U64 terminatingPictureNumber; EB_BOOL terminatingSequenceFlagReceived; diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index dd2372ac3..0fdb41366 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -147,9 +147,8 @@ void* PacketizationKernel(void *inputPtr) EB_U32 packetizationQp; EB_PICTURE sliceType; - - EB_U64 prevBPpictureNumber; - EB_U64 lastBpnumber=0; + EB_U32 refDecOrder; + for(;;) { // Get EntropyCoding Results @@ -518,7 +517,6 @@ void* PacketizationKernel(void *inputPtr) // Parsing the linked list and find the user data SEI msgs and code them sequenceControlSetPtr->picTimingSei.picStruct = 0; - prevBPpictureNumber = pictureControlSetPtr->pictureNumber == 0 ? 0 : lastBpnumber; if( sequenceControlSetPtr->staticConfig.bufferingPeriodSEI && pictureControlSetPtr->sliceType == EB_I_PICTURE && sequenceControlSetPtr->staticConfig.videoUsabilityInfo && @@ -528,7 +526,6 @@ void* PacketizationKernel(void *inputPtr) if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) { HrdFullness(sequenceControlSetPtr, pictureControlSetPtr, &sequenceControlSetPtr->bufferingPeriod); - lastBpnumber= pictureControlSetPtr->ParentPcsPtr->decodeOrder; } EncodeBufferingPeriodSEI( pictureControlSetPtr->bitstreamPtr, @@ -546,7 +543,20 @@ void* PacketizationKernel(void *inputPtr) // buffering period SEI message const AppVideoUsabilityInfo_t* vui = sequenceControlSetPtr->videoUsabilityInfoPtr; const AppHrdParameters_t* hrd = vui->hrdParametersPtr; - sequenceControlSetPtr->picTimingSei.auCpbRemovalDelayMinus1 = (EB_U32)((MIN(MAX(1, (EB_S32)pictureControlSetPtr->ParentPcsPtr->decodeOrder - (EB_S32)prevBPpictureNumber), (1 << hrd->auCpbRemovalDelayLengthMinus1)))-1); + if (sequenceControlSetPtr->intraRefreshType == CRA_REFRESH) + { + if (pictureControlSetPtr->sliceType == EB_I_PICTURE) + refDecOrder = (EB_S64)((pictureControlSetPtr->pictureNumber - (sequenceControlSetPtr->intraPeriodLength + 1) - ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1))) < 0 ? + 0 : (EB_U32)((pictureControlSetPtr->pictureNumber - (sequenceControlSetPtr->intraPeriodLength + 1) - ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1))); + else + refDecOrder = (EB_S64)((((pictureControlSetPtr->pictureNumber + ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1)) / (sequenceControlSetPtr->intraPeriodLength + 1) * (sequenceControlSetPtr->intraPeriodLength + 1))) - ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1)) < 0 ? + 0: (EB_U32)((((pictureControlSetPtr->pictureNumber + ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1)) / (sequenceControlSetPtr->intraPeriodLength + 1) * (sequenceControlSetPtr->intraPeriodLength + 1))) - ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1)); + } + else + { + refDecOrder = (EB_U32)pictureControlSetPtr->ParentPcsPtr->lastIdrPictureOrder; + } + sequenceControlSetPtr->picTimingSei.auCpbRemovalDelayMinus1 = (EB_U32)((MIN(MAX(1, (EB_S32)(pictureControlSetPtr->ParentPcsPtr->decodeOrder - refDecOrder)), (1 << hrd->auCpbRemovalDelayLengthMinus1)))-1); sequenceControlSetPtr->picTimingSei.picDpbOutputDelay = (EB_U32)((sequenceControlSetPtr->maxDpbSize-1) + pictureControlSetPtr->pictureNumber - pictureControlSetPtr->ParentPcsPtr->decodeOrder); } diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index 05ec9c879..24af4aea3 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -598,6 +598,7 @@ EB_ERRORTYPE PictureParentControlSetCtor( objectPtr->totalNumBits = 0; objectPtr->lastIdrPicture = 0; + objectPtr->lastIdrPictureOrder = 0; objectPtr->lcuTotalCount = pictureLcuWidth * pictureLcuHeight; diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index 3cec0ef99..45902c0ba 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -404,6 +404,7 @@ typedef struct PictureParentControlSet_s EB_U64 averageQp; EB_U64 lastIdrPicture; + EB_U64 lastIdrPictureOrder; EB_U64 startTimeSeconds; EB_U64 startTimeuSeconds; diff --git a/Source/Lib/Codec/EbPictureDecisionProcess.c b/Source/Lib/Codec/EbPictureDecisionProcess.c index 1c95341d4..160b3020f 100644 --- a/Source/Lib/Codec/EbPictureDecisionProcess.c +++ b/Source/Lib/Codec/EbPictureDecisionProcess.c @@ -1302,7 +1302,12 @@ void* PictureDecisionKernel(void *inputPtr) else { pictureControlSetPtr->decodeOrder = pictureControlSetPtr->pictureNumber; } - + if (sequenceControlSetPtr->intraRefreshType == IDR_REFRESH) + { + pictureControlSetPtr->lastIdrPictureOrder = encodeContextPtr->lastIdrPictureOrder; + if (pictureControlSetPtr->sliceType == EB_I_PICTURE) + encodeContextPtr->lastIdrPictureOrder = pictureControlSetPtr->decodeOrder; + } EbBlockOnMutex(encodeContextPtr->terminatingConditionsMutex); encodeContextPtr->terminatingSequenceFlagReceived = (pictureControlSetPtr->endOfSequenceFlag == EB_TRUE) ? From 54eceb49fe911475c9e85a888de4364c3bc03fcc Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Tue, 5 Mar 2019 10:35:17 +0530 Subject: [PATCH 22/93] -Fix 1. encoded bitrate will be within target Bitrate limit 2. previously It was going beyond target Bitrate and was within Max Bitrate, if Max rate is Greater than Target Bitrate --- Source/Lib/Codec/EbRateControlProcess.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 71d656b38..945b34d03 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2247,9 +2247,8 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet EB_U32 queueEntryIndexTemp; EB_U32 queueEntryIndexTemp2; EB_U32 queueEntryIndexHeadTemp; - EB_BOOL endOfSequenceFlag = EB_TRUE; - HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp; + EB_BOOL bitrateFlag; /* Lookahead VBV: If lookahead is done, raise the quantizer as necessary * such that no frames in the lookahead overflow and such that the buffer @@ -2262,7 +2261,7 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet queueEntryIndexHeadTemp; queueEntryIndexTemp = queueEntryIndexHeadTemp; - + bitrateFlag = encodeContextPtr->vbvMaxrate <= encodeContextPtr->availableTargetBitRate; EB_S32 currentInd = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; @@ -2282,11 +2281,8 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet { queueEntryIndexTemp2 = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[queueEntryIndexTemp2]); - /* This is set to false, so the last frame would go inside the loop */ - endOfSequenceFlag = EB_FALSE; - if ((endOfSequenceFlag || - queueEntryIndexTemp >= (currentInd + sequenceControlSetPtr->staticConfig.lookAheadDistance)) || + if ((queueEntryIndexTemp >= (currentInd + sequenceControlSetPtr->staticConfig.lookAheadDistance)) || (queueEntryIndexTemp >= sequenceControlSetPtr->staticConfig.framesToBeEncoded) || (totalDuration >= 1.0)) break; @@ -2314,7 +2310,7 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet } /* Try to get the buffer not more than 80% filled, but don't set an impossible goal. */ targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.2 * finalDur), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); - if (bufferFillCur > targetFill) + if ((bitrateFlag) && (bufferFillCur > targetFill)) { q--; q = CLIP3( From 429edb5f855c9392f884524f894f6b0e4639389c Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 12 Mar 2019 12:32:08 +0530 Subject: [PATCH 23/93] Add code to copy the pcs context into the Packetization Reorder Queue Entry --- Source/Lib/Codec/EbPacketizationProcess.c | 68 +++++++++---------- .../Lib/Codec/EbPacketizationReorderQueue.c | 21 +++++- .../Lib/Codec/EbPacketizationReorderQueue.h | 13 ++++ 3 files changed, 66 insertions(+), 36 deletions(-) diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 0fdb41366..667e34e80 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -147,8 +147,7 @@ void* PacketizationKernel(void *inputPtr) EB_U32 packetizationQp; EB_PICTURE sliceType; - EB_U32 refDecOrder; - + EB_U64 refDecOrder = 0; for(;;) { // Get EntropyCoding Results @@ -534,42 +533,17 @@ void* PacketizationKernel(void *inputPtr) sequenceControlSetPtr->encodeContextPtr); } - if(sequenceControlSetPtr->staticConfig.pictureTimingSEI) { + if (sequenceControlSetPtr->staticConfig.pictureTimingSEI) { if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) { - // The aucpbremoval delay specifies how many clock ticks the - // access unit associated with the picture timing SEI message has to - // wait after removal of the access unit with the most recent - // buffering period SEI message - const AppVideoUsabilityInfo_t* vui = sequenceControlSetPtr->videoUsabilityInfoPtr; - const AppHrdParameters_t* hrd = vui->hrdParametersPtr; - if (sequenceControlSetPtr->intraRefreshType == CRA_REFRESH) - { - if (pictureControlSetPtr->sliceType == EB_I_PICTURE) - refDecOrder = (EB_S64)((pictureControlSetPtr->pictureNumber - (sequenceControlSetPtr->intraPeriodLength + 1) - ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1))) < 0 ? - 0 : (EB_U32)((pictureControlSetPtr->pictureNumber - (sequenceControlSetPtr->intraPeriodLength + 1) - ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1))); - else - refDecOrder = (EB_S64)((((pictureControlSetPtr->pictureNumber + ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1)) / (sequenceControlSetPtr->intraPeriodLength + 1) * (sequenceControlSetPtr->intraPeriodLength + 1))) - ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1)) < 0 ? - 0: (EB_U32)((((pictureControlSetPtr->pictureNumber + ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1)) / (sequenceControlSetPtr->intraPeriodLength + 1) * (sequenceControlSetPtr->intraPeriodLength + 1))) - ((1 << sequenceControlSetPtr->staticConfig.hierarchicalLevels) - 1)); - } - else - { - refDecOrder = (EB_U32)pictureControlSetPtr->ParentPcsPtr->lastIdrPictureOrder; - } - sequenceControlSetPtr->picTimingSei.auCpbRemovalDelayMinus1 = (EB_U32)((MIN(MAX(1, (EB_S32)(pictureControlSetPtr->ParentPcsPtr->decodeOrder - refDecOrder)), (1 << hrd->auCpbRemovalDelayLengthMinus1)))-1); - sequenceControlSetPtr->picTimingSei.picDpbOutputDelay = (EB_U32)((sequenceControlSetPtr->maxDpbSize-1) + pictureControlSetPtr->pictureNumber - pictureControlSetPtr->ParentPcsPtr->decodeOrder); + queueEntryPtr->picTimingEntry->decodeOrder = pictureControlSetPtr->ParentPcsPtr->decodeOrder; + queueEntryPtr->picTimingEntry->picStruct = pictureControlSetPtr->ParentPcsPtr->pictStruct; + queueEntryPtr->picTimingEntry->temporalId = pictureControlSetPtr->temporalId; + queueEntryPtr->sliceType = pictureControlSetPtr->sliceType; + queueEntryPtr->picTimingEntry->poc = pictureControlSetPtr->pictureNumber; } - EncodePictureTimingSEI( - pictureControlSetPtr->bitstreamPtr, - &sequenceControlSetPtr->picTimingSei, - sequenceControlSetPtr->videoUsabilityInfoPtr, - sequenceControlSetPtr->encodeContextPtr, - pictureControlSetPtr->ParentPcsPtr->pictStruct, - pictureControlSetPtr->temporalId); - } - if(sequenceControlSetPtr->staticConfig.recoveryPointSeiFlag){ EncodeRecoveryPointSEI( pictureControlSetPtr->bitstreamPtr, @@ -675,8 +649,34 @@ void* PacketizationKernel(void *inputPtr) finishTimeSeconds, finishTimeuSeconds, &latency); - + //Block to update decode order of the lsat + if (queueEntryPtr->sliceType == EB_I_PICTURE) + { + refDecOrder = queueEntryPtr->pictureNumber; + } outputStreamPtr->nTickCount = (EB_U32)latency; + if (sequenceControlSetPtr->staticConfig.pictureTimingSEI) { + if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) + { + // The aucpbremoval delay specifies how many clock ticks the + // access unit associated with the picture timing SEI message has to + // wait after removal of the access unit with the most recent + // buffering period SEI message + const AppVideoUsabilityInfo_t* vui = sequenceControlSetPtr->videoUsabilityInfoPtr; + const AppHrdParameters_t* hrd = vui->hrdParametersPtr; + sequenceControlSetPtr->picTimingSei.auCpbRemovalDelayMinus1 = (EB_U32)((MIN(MAX(1, (EB_S32)(queueEntryPtr->picTimingEntry->decodeOrder - refDecOrder)), (1 << hrd->auCpbRemovalDelayLengthMinus1))) - 1); + sequenceControlSetPtr->picTimingSei.picDpbOutputDelay = (EB_U32)((sequenceControlSetPtr->maxDpbSize - 1) + queueEntryPtr->picTimingEntry->poc - queueEntryPtr->picTimingEntry->decodeOrder); + } + +/* EncodePictureTimingSEI( + queueEntryPtr->bitStreamPtr2, + &sequenceControlSetPtr->picTimingSei, + sequenceControlSetPtr->videoUsabilityInfoPtr, + sequenceControlSetPtr->encodeContextPtr, + queueEntryPtr->picTimingEntry->picStruct, + queueEntryPtr->picTimingEntry->temporalId);*/ + + } EbPostFullObject(outputStreamWrapperPtr); /* update VBV plan */ EbBlockOnMutex(encodeContextPtr->bufferFillMutex); diff --git a/Source/Lib/Codec/EbPacketizationReorderQueue.c b/Source/Lib/Codec/EbPacketizationReorderQueue.c index 0ba41e3a3..a101c0d1f 100644 --- a/Source/Lib/Codec/EbPacketizationReorderQueue.c +++ b/Source/Lib/Codec/EbPacketizationReorderQueue.c @@ -5,16 +5,33 @@ #include #include "EbPacketizationReorderQueue.h" +#include "EbPictureControlSet.h" +EB_ERRORTYPE PicTimingCtor( + PicTimingEntry_t* *entryPtr) +{ + EB_MALLOC(PicTimingEntry_t*, *entryPtr, sizeof(PicTimingEntry_t), EB_N_PTR); + (*entryPtr)->picStruct = 0; + (*entryPtr)->temporalId = 0; + (*entryPtr)->decodeOrder = 0; + return EB_ErrorNone; +} EB_ERRORTYPE PacketizationReorderEntryCtor( PacketizationReorderEntry_t **entryDblPtr, EB_U32 pictureNumber) { + EB_ERRORTYPE return_error = EB_ErrorNone; EB_MALLOC(PacketizationReorderEntry_t*, *entryDblPtr, sizeof(PacketizationReorderEntry_t), EB_N_PTR); - + return_error = PicTimingCtor( + &(*entryDblPtr)->picTimingEntry); + return_error = BitstreamCtor( + &(*entryDblPtr)->bitStreamPtr2, + PACKETIZATION_PROCESS_BUFFER_SIZE); + return_error = BitstreamCtor( + &(*entryDblPtr)->bitStreamPtr3, + PACKETIZATION_PROCESS_BUFFER_SIZE); (*entryDblPtr)->pictureNumber = pictureNumber; (*entryDblPtr)->outputStreamWrapperPtr = (EbObjectWrapper_t *)EB_NULL; - return EB_ErrorNone; } diff --git a/Source/Lib/Codec/EbPacketizationReorderQueue.h b/Source/Lib/Codec/EbPacketizationReorderQueue.h index de89e08c1..08a9cded0 100644 --- a/Source/Lib/Codec/EbPacketizationReorderQueue.h +++ b/Source/Lib/Codec/EbPacketizationReorderQueue.h @@ -8,12 +8,21 @@ #include "EbDefinitions.h" #include "EbSystemResourceManager.h" +#include "EbEntropyCodingObject.h" #ifdef __cplusplus extern "C" { #endif /************************************************ * Packetization Reorder Queue Entry ************************************************/ + typedef struct PicTimingEntry_s + { + EB_PICT_STRUCT picStruct; + EB_U8 temporalId; + EB_U64 decodeOrder; + EB_U64 poc; + } PicTimingEntry_t; + typedef struct PacketizationReorderEntry_s { EB_U64 pictureNumber; EbObjectWrapper_t *outputStreamWrapperPtr; @@ -21,6 +30,10 @@ typedef struct PacketizationReorderEntry_s { EB_U64 startTimeSeconds; EB_U64 startTimeuSeconds; EB_U64 actualBits; + EB_PICTURE sliceType; + PicTimingEntry_t *picTimingEntry; + Bitstream_t *bitStreamPtr2; + Bitstream_t *bitStreamPtr3; } PacketizationReorderEntry_t; extern EB_ERRORTYPE PacketizationReorderEntryCtor( From d857190052b04b78db3912824dfb8f8daa66afcc Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 12 Mar 2019 18:04:59 +0530 Subject: [PATCH 24/93] Add code to splice the bitstream and insert pic timing SEI --- Source/Lib/Codec/EbPacketizationProcess.c | 80 ++++++++++++++----- .../Lib/Codec/EbPacketizationReorderQueue.c | 1 + .../Lib/Codec/EbPacketizationReorderQueue.h | 1 + 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 667e34e80..0b9ff1b03 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -263,20 +263,20 @@ void* PacketizationKernel(void *inputPtr) pictureControlSetPtr->bitstreamPtr, &sequenceControlSetPtr->activeParameterSet); } - // Flush the Bitstream - FlushBitstream( - pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - - // Copy SPS & PPS to the Output Bitstream - CopyRbspBitstreamToPayload( - pictureControlSetPtr->bitstreamPtr, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); + // // Flush the Bitstream + // FlushBitstream( + // pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + // + // // Copy SPS & PPS to the Output Bitstream + // CopyRbspBitstreamToPayload( + // pictureControlSetPtr->bitstreamPtr, + // outputStreamPtr->pBuffer, + // (EB_U32*) &(outputStreamPtr->nFilledLen), + // (EB_U32*) &(outputStreamPtr->nAllocLen), + //encodeContextPtr); + // } + } - - // Bitstream Written Loop // This loop writes the result of entropy coding into the bitstream { @@ -500,8 +500,8 @@ void* PacketizationKernel(void *inputPtr) } } - // Reset the bitstream - ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + //// Reset the bitstream + //ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); // Encode slice header and write it into the bitstream. packetizationQp = pictureControlSetPtr->pictureQp; @@ -533,6 +533,18 @@ void* PacketizationKernel(void *inputPtr) sequenceControlSetPtr->encodeContextPtr); } + // Flush the Bitstream + FlushBitstream( + pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + + // Copy Slice Header to the Output Bitstream + CopyRbspBitstreamToPayload( + pictureControlSetPtr->bitstreamPtr, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr); + queueEntryPtr->startSplicing = outputStreamPtr->nFilledLen; if (sequenceControlSetPtr->staticConfig.pictureTimingSEI) { if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) { @@ -544,6 +556,9 @@ void* PacketizationKernel(void *inputPtr) } } + // Reset the bitstream + ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + if(sequenceControlSetPtr->staticConfig.recoveryPointSeiFlag){ EncodeRecoveryPointSEI( pictureControlSetPtr->bitstreamPtr, @@ -650,10 +665,6 @@ void* PacketizationKernel(void *inputPtr) finishTimeuSeconds, &latency); //Block to update decode order of the lsat - if (queueEntryPtr->sliceType == EB_I_PICTURE) - { - refDecOrder = queueEntryPtr->pictureNumber; - } outputStreamPtr->nTickCount = (EB_U32)latency; if (sequenceControlSetPtr->staticConfig.pictureTimingSEI) { if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) @@ -667,15 +678,42 @@ void* PacketizationKernel(void *inputPtr) sequenceControlSetPtr->picTimingSei.auCpbRemovalDelayMinus1 = (EB_U32)((MIN(MAX(1, (EB_S32)(queueEntryPtr->picTimingEntry->decodeOrder - refDecOrder)), (1 << hrd->auCpbRemovalDelayLengthMinus1))) - 1); sequenceControlSetPtr->picTimingSei.picDpbOutputDelay = (EB_U32)((sequenceControlSetPtr->maxDpbSize - 1) + queueEntryPtr->picTimingEntry->poc - queueEntryPtr->picTimingEntry->decodeOrder); } + // Reset the bitstream + ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); -/* EncodePictureTimingSEI( + EncodePictureTimingSEI( queueEntryPtr->bitStreamPtr2, &sequenceControlSetPtr->picTimingSei, sequenceControlSetPtr->videoUsabilityInfoPtr, sequenceControlSetPtr->encodeContextPtr, queueEntryPtr->picTimingEntry->picStruct, - queueEntryPtr->picTimingEntry->temporalId);*/ + queueEntryPtr->picTimingEntry->temporalId); + + // Flush the Bitstream + FlushBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)queueEntryPtr->bitStreamPtr2->outputBitstreamPtr; + EB_U32 bufferWrittenBytesCount = outputBitstreamPtr->writtenBitsCount >> 3; + EB_U32 startinBytes = queueEntryPtr->startSplicing >> 3; + EB_U32 totalBytes = outputStreamPtr->nAllocLen; + for (EB_U32 i = 0; i < bufferWrittenBytesCount; i++) + { + for (EB_U32 j = totalBytes; j > totalBytes; j--) + { + outputStreamPtr->pBuffer[totalBytes + 1] = outputStreamPtr->pBuffer[totalBytes]; + } + } + // Copy SPS & PPS to the Output Bitstream + CopyRbspBitstreamToPayload( + queueEntryPtr->bitStreamPtr2, + outputStreamPtr->pBuffer, + (EB_U32*) &(startinBytes), + (EB_U32*) &(outputStreamPtr->nAllocLen), + ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr); + } + if (queueEntryPtr->sliceType == EB_I_PICTURE) + { + refDecOrder = queueEntryPtr->pictureNumber; } EbPostFullObject(outputStreamWrapperPtr); /* update VBV plan */ diff --git a/Source/Lib/Codec/EbPacketizationReorderQueue.c b/Source/Lib/Codec/EbPacketizationReorderQueue.c index a101c0d1f..7a3748fe2 100644 --- a/Source/Lib/Codec/EbPacketizationReorderQueue.c +++ b/Source/Lib/Codec/EbPacketizationReorderQueue.c @@ -32,6 +32,7 @@ EB_ERRORTYPE PacketizationReorderEntryCtor( PACKETIZATION_PROCESS_BUFFER_SIZE); (*entryDblPtr)->pictureNumber = pictureNumber; (*entryDblPtr)->outputStreamWrapperPtr = (EbObjectWrapper_t *)EB_NULL; + (*entryDblPtr)->startSplicing = 0; return EB_ErrorNone; } diff --git a/Source/Lib/Codec/EbPacketizationReorderQueue.h b/Source/Lib/Codec/EbPacketizationReorderQueue.h index 08a9cded0..6a349a193 100644 --- a/Source/Lib/Codec/EbPacketizationReorderQueue.h +++ b/Source/Lib/Codec/EbPacketizationReorderQueue.h @@ -34,6 +34,7 @@ typedef struct PacketizationReorderEntry_s { PicTimingEntry_t *picTimingEntry; Bitstream_t *bitStreamPtr2; Bitstream_t *bitStreamPtr3; + EB_U32 startSplicing; } PacketizationReorderEntry_t; extern EB_ERRORTYPE PacketizationReorderEntryCtor( From 0d74be5f209d0a98ba10092026e7623305b6c0e0 Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 12 Mar 2019 23:34:25 +0530 Subject: [PATCH 25/93] Fix bugs and cleanup --- Source/Lib/Codec/EbPacketizationProcess.c | 45 ++++++++++++----------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 0b9ff1b03..16de9100d 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -263,20 +263,18 @@ void* PacketizationKernel(void *inputPtr) pictureControlSetPtr->bitstreamPtr, &sequenceControlSetPtr->activeParameterSet); } - // // Flush the Bitstream - // FlushBitstream( - // pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - // - // // Copy SPS & PPS to the Output Bitstream - // CopyRbspBitstreamToPayload( - // pictureControlSetPtr->bitstreamPtr, - // outputStreamPtr->pBuffer, - // (EB_U32*) &(outputStreamPtr->nFilledLen), - // (EB_U32*) &(outputStreamPtr->nAllocLen), - //encodeContextPtr); - // } - - } + // Flush the Bitstream + FlushBitstream( + pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + + // Copy SPS & PPS to the Output Bitstream + CopyRbspBitstreamToPayload( + pictureControlSetPtr->bitstreamPtr, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr); + } // Bitstream Written Loop // This loop writes the result of entropy coding into the bitstream { @@ -500,8 +498,8 @@ void* PacketizationKernel(void *inputPtr) } } - //// Reset the bitstream - //ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + // Reset the bitstream + ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); // Encode slice header and write it into the bitstream. packetizationQp = pictureControlSetPtr->pictureQp; @@ -693,22 +691,25 @@ void* PacketizationKernel(void *inputPtr) FlushBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)queueEntryPtr->bitStreamPtr2->outputBitstreamPtr; EB_U32 bufferWrittenBytesCount = outputBitstreamPtr->writtenBitsCount >> 3; - EB_U32 startinBytes = queueEntryPtr->startSplicing >> 3; - EB_U32 totalBytes = outputStreamPtr->nAllocLen; + EB_U32 startinBytes = queueEntryPtr->startSplicing; + EB_U32 totalBytes = outputStreamPtr->nFilledLen; for (EB_U32 i = 0; i < bufferWrittenBytesCount; i++) { - for (EB_U32 j = totalBytes; j > totalBytes; j--) + for (EB_U32 j = totalBytes; j > startinBytes; j--) { - outputStreamPtr->pBuffer[totalBytes + 1] = outputStreamPtr->pBuffer[totalBytes]; + outputStreamPtr->pBuffer[j] = outputStreamPtr->pBuffer[j-1]; } + totalBytes++; + startinBytes++; } // Copy SPS & PPS to the Output Bitstream CopyRbspBitstreamToPayload( queueEntryPtr->bitStreamPtr2, outputStreamPtr->pBuffer, - (EB_U32*) &(startinBytes), + (EB_U32*) &(queueEntryPtr->startSplicing), (EB_U32*) &(outputStreamPtr->nAllocLen), - ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr); + sequenceControlSetPtr->encodeContextPtr); + outputStreamPtr->nFilledLen += bufferWrittenBytesCount; } if (queueEntryPtr->sliceType == EB_I_PICTURE) From 22f80ea9772c8769ea6b0a631c847b55eff98d40 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 13 Mar 2019 23:12:15 +0530 Subject: [PATCH 26/93] Refactor byte shifting code with memcpy+cleanup --- Source/Lib/Codec/EbPacketizationProcess.c | 24 ++++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 16de9100d..7f09efd5c 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -535,7 +535,7 @@ void* PacketizationKernel(void *inputPtr) FlushBitstream( pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - // Copy Slice Header to the Output Bitstream + // Copy Buffering Period SEI to the Output Bitstream CopyRbspBitstreamToPayload( pictureControlSetPtr->bitstreamPtr, outputStreamPtr->pBuffer, @@ -654,6 +654,9 @@ void* PacketizationKernel(void *inputPtr) double latency = 0.0; EB_U64 finishTimeSeconds = 0; EB_U64 finishTimeuSeconds = 0; + EB_U32 bufferWrittenBytesCount = 0; + EB_U32 startinBytes = 0; + EB_U32 totalBytes = 0; EbFinishTime((uint64_t*)&finishTimeSeconds, (uint64_t*)&finishTimeuSeconds); EbComputeOverallElapsedTimeMs( @@ -690,19 +693,12 @@ void* PacketizationKernel(void *inputPtr) // Flush the Bitstream FlushBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)queueEntryPtr->bitStreamPtr2->outputBitstreamPtr; - EB_U32 bufferWrittenBytesCount = outputBitstreamPtr->writtenBitsCount >> 3; - EB_U32 startinBytes = queueEntryPtr->startSplicing; - EB_U32 totalBytes = outputStreamPtr->nFilledLen; - for (EB_U32 i = 0; i < bufferWrittenBytesCount; i++) - { - for (EB_U32 j = totalBytes; j > startinBytes; j--) - { - outputStreamPtr->pBuffer[j] = outputStreamPtr->pBuffer[j-1]; - } - totalBytes++; - startinBytes++; - } - // Copy SPS & PPS to the Output Bitstream + bufferWrittenBytesCount = outputBitstreamPtr->writtenBitsCount >> 3; + startinBytes = queueEntryPtr->startSplicing; + totalBytes = outputStreamPtr->nFilledLen; + //Shift the bitstream by size of picture timing SEI + memcpy(outputStreamPtr->pBuffer + startinBytes + bufferWrittenBytesCount, outputStreamPtr->pBuffer + startinBytes, totalBytes - startinBytes); + // Copy Picture Timing SEI to the Output Bitstream CopyRbspBitstreamToPayload( queueEntryPtr->bitStreamPtr2, outputStreamPtr->pBuffer, From 5f52a2f4b8dd61bf2f92e5d2171baefe7b9b69b5 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 13 Mar 2019 17:20:11 +0530 Subject: [PATCH 27/93] Add support for strict cbr to prevent hrd buffer overflow --- Source/Lib/Codec/EbDefinitions.h | 2 + Source/Lib/Codec/EbEntropyCoding.c | 17 +++++ Source/Lib/Codec/EbEntropyCoding.h | 4 ++ Source/Lib/Codec/EbPacketizationProcess.c | 79 +++++++++++++++++++++-- 4 files changed, 97 insertions(+), 5 deletions(-) diff --git a/Source/Lib/Codec/EbDefinitions.h b/Source/Lib/Codec/EbDefinitions.h index 2ccc5666e..17c1a5881 100644 --- a/Source/Lib/Codec/EbDefinitions.h +++ b/Source/Lib/Codec/EbDefinitions.h @@ -891,6 +891,8 @@ typedef enum EB_SEI { #define MAX_PICTURE_WIDTH_SIZE 9344u #define MAX_PICTURE_HEIGHT_SIZE 5120u +#define FILLER_DATA_OVERHEAD 6 + #define INTERNAL_BIT_DEPTH 8 // to be modified #define MAX_SAMPLE_VALUE ((1 << INTERNAL_BIT_DEPTH) - 1) #define MAX_SAMPLE_VALUE_10BIT 0x3FF diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index d4befb992..5f80d4b07 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -8402,6 +8402,23 @@ EB_ERRORTYPE EncodeActiveParameterSetsSEI( return return_error; } +EB_ERRORTYPE EncodeFillerData( + Bitstream_t *bitstreamPtr, + EB_U32 fillerBytes, + EB_U8 temporalId) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + unsigned payloadType = FILLER_PAYLOAD; + + OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)bitstreamPtr->outputBitstreamPtr; + + CodeNALUnitHeader( + outputBitstreamPtr, + NAL_UNIT_FILLER_DATA, + temporalId); + return return_error; +} + EB_ERRORTYPE EncodeRegUserDataSEI( Bitstream_t *bitstreamPtr, RegistedUserData_t *regUserDataSeiPtr) diff --git a/Source/Lib/Codec/EbEntropyCoding.h b/Source/Lib/Codec/EbEntropyCoding.h index b39a5ab54..465a9d819 100644 --- a/Source/Lib/Codec/EbEntropyCoding.h +++ b/Source/Lib/Codec/EbEntropyCoding.h @@ -172,6 +172,10 @@ extern EB_ERRORTYPE EncodeActiveParameterSetsSEI( Bitstream_t *bitstreamPtr, AppActiveparameterSetSei_t *activeParameterSet); +extern EB_ERRORTYPE EncodeFillerData( + Bitstream_t *bitstreamPtr, + EB_U32 fillerBytes, + EB_U8 temporalId); extern EB_ERRORTYPE EncodeRegUserDataSEI( Bitstream_t *bitstreamPtr, diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 7f09efd5c..eb009bbaa 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -148,6 +148,8 @@ void* PacketizationKernel(void *inputPtr) EB_PICTURE sliceType; EB_U64 refDecOrder = 0; + EB_U64 filler; + EB_U32 fillerBytes; for(;;) { // Get EntropyCoding Results @@ -657,6 +659,8 @@ void* PacketizationKernel(void *inputPtr) EB_U32 bufferWrittenBytesCount = 0; EB_U32 startinBytes = 0; EB_U32 totalBytes = 0; + EB_U64 buffer=0; + EB_U64 bufferRate = 0; EbFinishTime((uint64_t*)&finishTimeSeconds, (uint64_t*)&finishTimeuSeconds); EbComputeOverallElapsedTimeMs( @@ -712,7 +716,6 @@ void* PacketizationKernel(void *inputPtr) { refDecOrder = queueEntryPtr->pictureNumber; } - EbPostFullObject(outputStreamWrapperPtr); /* update VBV plan */ EbBlockOnMutex(encodeContextPtr->bufferFillMutex); if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) @@ -721,13 +724,79 @@ void* PacketizationKernel(void *inputPtr) bufferfill_temp -= queueEntryPtr->actualBits; bufferfill_temp = MAX(bufferfill_temp, 0); - bufferfill_temp = (EB_S64)(bufferfill_temp + (encodeContextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); + buffer=bufferfill_temp = (EB_S64)(bufferfill_temp + (encodeContextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); bufferfill_temp = MIN(bufferfill_temp, encodeContextPtr->vbvBufsize); encodeContextPtr->bufferFill = (EB_U64)(bufferfill_temp); - + EbReleaseMutex(encodeContextPtr->bufferFillMutex); + bufferRate = encodeContextPtr->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16); + //Block to write filler data to prevent cpb overflow + if ((sequenceControlSetPtr->staticConfig.vbvMaxrate == sequenceControlSetPtr->staticConfig.targetBitRate)&& !(outputStreamPtr->nFlags & EB_BUFFERFLAG_EOS)) + { + if (buffer > encodeContextPtr->vbvBufsize) + { + filler = buffer - encodeContextPtr->vbvBufsize; + fillerBytes = ((EB_U32)(filler >> 3)) - FILLER_DATA_OVERHEAD; + // Reset the bitstream + ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + + EncodeFillerData(queueEntryPtr->bitStreamPtr2, fillerBytes, queueEntryPtr->picTimingEntry->temporalId); + + // Flush the Bitstream + FlushBitstream( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + + // Copy filler bits to the Output Bitstream + CopyRbspBitstreamToPayload( + queueEntryPtr->bitStreamPtr2, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr); + + for (EB_U32 i = 0; i < fillerBytes; i++) + { + ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + // Flush the Bitstream + OutputBitstreamWrite(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr, 0xff, 8); + FlushBitstream( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + CopyRbspBitstreamToPayload( + queueEntryPtr->bitStreamPtr2, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr); + } + ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + // Byte Align the Bitstream: rbsp_trailing_bits + OutputBitstreamWrite( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr, + 1, + 1); + FlushBitstream( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + CopyRbspBitstreamToPayload( + queueEntryPtr->bitStreamPtr2, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr); + ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + OutputBitstreamWriteAlignZero( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + // Flush the Bitstream + FlushBitstream( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + CopyRbspBitstreamToPayload( + queueEntryPtr->bitStreamPtr2, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr); + } + } } - EbReleaseMutex(encodeContextPtr->bufferFillMutex); - + EbPostFullObject(outputStreamWrapperPtr); // Reset the Reorder Queue Entry queueEntryPtr->pictureNumber += PACKETIZATION_REORDER_QUEUE_MAX_DEPTH; queueEntryPtr->outputStreamWrapperPtr = (EbObjectWrapper_t *)EB_NULL; From dd24050d0a6cdda5cda7d8ac52adea4e57136563 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 13 Mar 2019 17:20:11 +0530 Subject: [PATCH 28/93] Add code to consider filler bits for RC --- Source/Lib/Codec/EbEntropyCoding.c | 1 - Source/Lib/Codec/EbPacketizationProcess.c | 144 +++++++++++----------- 2 files changed, 71 insertions(+), 74 deletions(-) diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index 5f80d4b07..8486d4ad6 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -8408,7 +8408,6 @@ EB_ERRORTYPE EncodeFillerData( EB_U8 temporalId) { EB_ERRORTYPE return_error = EB_ErrorNone; - unsigned payloadType = FILLER_PAYLOAD; OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)bitstreamPtr->outputBitstreamPtr; diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index eb009bbaa..4cccaff93 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -150,6 +150,7 @@ void* PacketizationKernel(void *inputPtr) EB_U64 refDecOrder = 0; EB_U64 filler; EB_U32 fillerBytes; + EB_U64 bufferRate; for(;;) { // Get EntropyCoding Results @@ -597,7 +598,71 @@ void* PacketizationKernel(void *inputPtr) (EB_U32*) &(outputStreamPtr->nFilledLen), (EB_U32*) &(outputStreamPtr->nAllocLen), encodeContextPtr); - + bufferRate = encodeContextPtr->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16); + if ((sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) && (sequenceControlSetPtr->staticConfig.vbvMaxrate == sequenceControlSetPtr->staticConfig.targetBitRate)) + { + pictureControlSetPtr->ParentPcsPtr->totalNumBits = outputStreamPtr->nFilledLen << 3; + EB_S64 buffer = (EB_S64)(encodeContextPtr->bufferFill); + + buffer -= pictureControlSetPtr->ParentPcsPtr->totalNumBits; + buffer = MAX(buffer, 0); + buffer = (EB_S64)(buffer + bufferRate); + //Block to write filler data to prevent vbv overflow + if ((EB_U64)buffer > encodeContextPtr->vbvBufsize) + { + filler = (EB_U64)buffer - encodeContextPtr->vbvBufsize; + fillerBytes = ((EB_U32)(filler >> 3)) - FILLER_DATA_OVERHEAD; + // Reset the bitstream + ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + + EncodeFillerData(pictureControlSetPtr->bitstreamPtr, fillerBytes, pictureControlSetPtr->temporalId); + + // Flush the Bitstream + FlushBitstream( + pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + + // Copy Filler Bits to the Output Bitstream + CopyRbspBitstreamToPayload( + pictureControlSetPtr->bitstreamPtr, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr); + + for (EB_U32 i = 0; i < fillerBytes; i++) + { + ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + OutputBitstreamWrite(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr, 0xff, 8); + // Flush the Bitstream + FlushBitstream( + pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + CopyRbspBitstreamToPayload( + pictureControlSetPtr->bitstreamPtr, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr); + } + ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + // Byte Align the Bitstream: rbsp_trailing_bits + OutputBitstreamWrite( + pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr, + 1, + 1); + + OutputBitstreamWriteAlignZero( + pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + // Flush the Bitstream + FlushBitstream( + pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + CopyRbspBitstreamToPayload( + pictureControlSetPtr->bitstreamPtr, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr); + } + } // Send the number of bytes per frame to RC pictureControlSetPtr->ParentPcsPtr->totalNumBits = outputStreamPtr->nFilledLen << 3; queueEntryPtr->actualBits = pictureControlSetPtr->ParentPcsPtr->totalNumBits; @@ -659,8 +724,6 @@ void* PacketizationKernel(void *inputPtr) EB_U32 bufferWrittenBytesCount = 0; EB_U32 startinBytes = 0; EB_U32 totalBytes = 0; - EB_U64 buffer=0; - EB_U64 bufferRate = 0; EbFinishTime((uint64_t*)&finishTimeSeconds, (uint64_t*)&finishTimeuSeconds); EbComputeOverallElapsedTimeMs( @@ -716,6 +779,7 @@ void* PacketizationKernel(void *inputPtr) { refDecOrder = queueEntryPtr->pictureNumber; } + EbPostFullObject(outputStreamWrapperPtr); /* update VBV plan */ EbBlockOnMutex(encodeContextPtr->bufferFillMutex); if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) @@ -724,79 +788,13 @@ void* PacketizationKernel(void *inputPtr) bufferfill_temp -= queueEntryPtr->actualBits; bufferfill_temp = MAX(bufferfill_temp, 0); - buffer=bufferfill_temp = (EB_S64)(bufferfill_temp + (encodeContextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); + bufferfill_temp = (EB_S64)(bufferfill_temp + (encodeContextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); bufferfill_temp = MIN(bufferfill_temp, encodeContextPtr->vbvBufsize); encodeContextPtr->bufferFill = (EB_U64)(bufferfill_temp); - EbReleaseMutex(encodeContextPtr->bufferFillMutex); - bufferRate = encodeContextPtr->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16); - //Block to write filler data to prevent cpb overflow - if ((sequenceControlSetPtr->staticConfig.vbvMaxrate == sequenceControlSetPtr->staticConfig.targetBitRate)&& !(outputStreamPtr->nFlags & EB_BUFFERFLAG_EOS)) - { - if (buffer > encodeContextPtr->vbvBufsize) - { - filler = buffer - encodeContextPtr->vbvBufsize; - fillerBytes = ((EB_U32)(filler >> 3)) - FILLER_DATA_OVERHEAD; - // Reset the bitstream - ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - - EncodeFillerData(queueEntryPtr->bitStreamPtr2, fillerBytes, queueEntryPtr->picTimingEntry->temporalId); - - // Flush the Bitstream - FlushBitstream( - queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - - // Copy filler bits to the Output Bitstream - CopyRbspBitstreamToPayload( - queueEntryPtr->bitStreamPtr2, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); - - for (EB_U32 i = 0; i < fillerBytes; i++) - { - ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - // Flush the Bitstream - OutputBitstreamWrite(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr, 0xff, 8); - FlushBitstream( - queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - CopyRbspBitstreamToPayload( - queueEntryPtr->bitStreamPtr2, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); - } - ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - // Byte Align the Bitstream: rbsp_trailing_bits - OutputBitstreamWrite( - queueEntryPtr->bitStreamPtr2->outputBitstreamPtr, - 1, - 1); - FlushBitstream( - queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - CopyRbspBitstreamToPayload( - queueEntryPtr->bitStreamPtr2, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); - ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - OutputBitstreamWriteAlignZero( - queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - // Flush the Bitstream - FlushBitstream( - queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - CopyRbspBitstreamToPayload( - queueEntryPtr->bitStreamPtr2, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); - } - } + } - EbPostFullObject(outputStreamWrapperPtr); + EbReleaseMutex(encodeContextPtr->bufferFillMutex); + // Reset the Reorder Queue Entry queueEntryPtr->pictureNumber += PACKETIZATION_REORDER_QUEUE_MAX_DEPTH; queueEntryPtr->outputStreamWrapperPtr = (EbObjectWrapper_t *)EB_NULL; From 713643e2efce3e6bf1ad7a0fd2b9e0c8a0f86f8f Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Tue, 19 Mar 2019 14:24:13 +0530 Subject: [PATCH 29/93] Merged Master to VBV-DEV branch --- .travis.yml | 54 + Build/linux/build.sh | 3 - CMakeLists.txt | 15 +- Config/Sample.cfg | 8 + Docs/10bit_packed.png | Bin 0 -> 12645 bytes Docs/10bit_unpacked.png | Bin 0 -> 21597 bytes Docs/10bit_yuv420p.png | Bin 0 -> 10885 bytes Docs/3-layer-hierarchical-low-delay.png | Bin 0 -> 77709 bytes Docs/3-layer-hierarchical-low-delay2.png | Bin 0 -> 19251 bytes Docs/3-layer-hierarchical-low-delay3.png | Bin 0 -> 18330 bytes Docs/64x64_after_2bit_compression.png | Bin 0 -> 10857 bytes Docs/64x64_after_unrolling.png | Bin 0 -> 6661 bytes Docs/64x64_block_after_unrolling.png | Bin 0 -> 56043 bytes Docs/8bit_yuv420p_1.png | Bin 0 -> 10501 bytes Docs/SVT-HEVC_Encoder_User_Guide.pdf | Bin 1122651 -> 0 bytes Docs/encoding_mode_res_table.png | Bin 0 -> 37814 bytes Docs/svt-hevc_encoder_user_guide.md | 480 ++ NOTICES.md | 12 + README.md | 118 +- Source/API/EbApi.h | 570 ++- Source/App/EbAppConfig.c | 152 +- Source/App/EbAppConfig.h | 34 + Source/App/EbAppContext.c | 81 +- Source/App/EbAppMain.c | 25 +- Source/App/EbAppProcessCmd.c | 592 +-- Source/Lib/ASM_AVX2/EbIntraPrediction_AVX2.h | 20 +- .../EbIntraPrediction_Intrinsic_AVX2.c | 369 ++ Source/Lib/ASM_AVX2/EbNoiseExtractAVX2.c | 47 +- .../EbIntraPrediction16bit_Intrinsic_SSE2.c | 178 +- .../EbIntraPrediction_Intrinsic_SSE2.c | 319 +- Source/Lib/ASM_SSE2/EbIntraPrediction_SSE2.h | 14 - .../EbIntraPrediction16bit_Intrinsic_SSSE3.c | 30 +- Source/Lib/C_DEFAULT/EbDeblockingFilter_C.c | 10 +- Source/Lib/C_DEFAULT/EbTransforms_C.c | 36 +- Source/Lib/Codec/EbBitstreamUnit.c | 5 +- Source/Lib/Codec/EbBitstreamUnit.h | 3 +- Source/Lib/Codec/EbCodingLoop.c | 3977 ++++++++--------- Source/Lib/Codec/EbCodingUnit.c | 2 + Source/Lib/Codec/EbDeblockingFilter.c | 636 ++- Source/Lib/Codec/EbDeblockingFilter.h | 2 +- Source/Lib/Codec/EbDefinitions.h | 24 + Source/Lib/Codec/EbEncDecProcess.c | 227 +- Source/Lib/Codec/EbEncDecProcess.h | 4 +- Source/Lib/Codec/EbEncHandle.c | 435 +- Source/Lib/Codec/EbEntropyCoding.c | 798 +++- Source/Lib/Codec/EbEntropyCoding.h | 15 +- Source/Lib/Codec/EbEntropyCodingUtil.h | 5 +- .../Lib/Codec/EbInitialRateControlProcess.c | 18 +- Source/Lib/Codec/EbInterPrediction.c | 131 +- Source/Lib/Codec/EbIntraPrediction.c | 2131 ++++----- Source/Lib/Codec/EbIntraPrediction.h | 50 +- Source/Lib/Codec/EbMcp.c | 115 +- Source/Lib/Codec/EbModeDecision.c | 23 +- .../EbModeDecisionConfigurationProcess.c | 14 +- Source/Lib/Codec/EbModeDecisionProcess.c | 4 +- Source/Lib/Codec/EbMotionEstimation.c | 10 - Source/Lib/Codec/EbPacketizationProcess.c | 90 +- Source/Lib/Codec/EbPictureAnalysisProcess.c | 284 +- Source/Lib/Codec/EbPictureBufferDesc.c | 15 +- Source/Lib/Codec/EbPictureBufferDesc.h | 7 +- Source/Lib/Codec/EbPictureControlSet.c | 44 +- Source/Lib/Codec/EbPictureControlSet.h | 7 +- Source/Lib/Codec/EbPictureOperators.h | 2 + Source/Lib/Codec/EbProductCodingLoop.c | 33 +- Source/Lib/Codec/EbRateControlProcess.c | 4 - Source/Lib/Codec/EbRateDistortionCost.c | 2 +- Source/Lib/Codec/EbReferenceObject.c | 1 + .../Lib/Codec/EbResourceCoordinationProcess.c | 18 +- ...EbSampleAdaptiveOffsetGenerationDecision.c | 33 +- Source/Lib/Codec/EbSei.c | 78 + Source/Lib/Codec/EbSei.h | 33 + Source/Lib/Codec/EbSequenceControlSet.c | 54 +- Source/Lib/Codec/EbSequenceControlSet.h | 19 +- .../Codec/EbSourceBasedOperationsProcess.c | 6 +- Source/Lib/Codec/EbThreads.c | 67 +- Source/Lib/Codec/EbTransQuantBuffers.c | 1 + Source/Lib/Codec/EbTransformUnit.h | 9 +- Source/Lib/Codec/EbTransforms.c | 31 +- Source/SimpleApp/EbSimpleAppContext.c | 58 +- Source/SimpleApp/EbSimpleAppContext.h | 1 + Source/SimpleApp/EbSimpleAppMain.c | 96 +- Tests/README.txt | 14 + ...alTests.py => SVT-HEVC_FunctionalTests.py} | 157 +- appveyor.yml | 24 + ...hevc-add-libsvt-hevc-encoder-wrapper.patch | 169 +- ffmpeg_plugin/libsvt_hevc.c | 440 -- 86 files changed, 7708 insertions(+), 5885 deletions(-) create mode 100644 .travis.yml create mode 100644 Docs/10bit_packed.png create mode 100644 Docs/10bit_unpacked.png create mode 100644 Docs/10bit_yuv420p.png create mode 100644 Docs/3-layer-hierarchical-low-delay.png create mode 100644 Docs/3-layer-hierarchical-low-delay2.png create mode 100644 Docs/3-layer-hierarchical-low-delay3.png create mode 100644 Docs/64x64_after_2bit_compression.png create mode 100644 Docs/64x64_after_unrolling.png create mode 100644 Docs/64x64_block_after_unrolling.png create mode 100644 Docs/8bit_yuv420p_1.png delete mode 100644 Docs/SVT-HEVC_Encoder_User_Guide.pdf create mode 100644 Docs/encoding_mode_res_table.png create mode 100644 Docs/svt-hevc_encoder_user_guide.md create mode 100644 NOTICES.md create mode 100644 Tests/README.txt rename Tests/{SVT_FunctionalTests.py => SVT-HEVC_FunctionalTests.py} (87%) create mode 100644 appveyor.yml delete mode 100644 ffmpeg_plugin/libsvt_hevc.c diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..35fab3aa4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,54 @@ +language: c +dist: xenial + +addons: + apt: + packages: + - cmake + - yasm + homebrew: + packages: + - yasm + +jobs: + include: + # General Linux build job + - name: Build + script: + - cd Build/linux + - ./build.sh release + # General macOS build job + - name: macOS build + os: osx + script: + - cd Build/linux + - ./build.sh release + # Coveralls test job + - name: Test & Coveralls + before_install: + - pip install --user cpp-coveralls + script: + - cd Build/linux + - ./build.sh release + after_success: + - coveralls + + # FFmpeg interation build + - name: FFmpeg patch + script: + # Build and install SVT-HEVC + - cd $TRAVIS_BUILD_DIR + - cd Build + - cmake .. + - make -j$(nproc) + - sudo make install + # Apply SVT-HEVC plugin and enable libsvthevc to FFmpeg + - git clone https://github.com/FFmpeg/FFmpeg ffmpeg + - cd ffmpeg + - git checkout release/4.1 + - git apply $TRAVIS_BUILD_DIR/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch + - git apply $TRAVIS_BUILD_DIR/ffmpeg_plugin/0002-doc-Add-libsvt_hevc-encoder-docs.patch + - export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib + - export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig + - ./configure --enable-libsvthevc + - make --quiet -j$(nproc) diff --git a/Build/linux/build.sh b/Build/linux/build.sh index 8c7c44d61..707c2c9c2 100755 --- a/Build/linux/build.sh +++ b/Build/linux/build.sh @@ -56,7 +56,6 @@ RANLIB_COMPILER=gcc-ranlib CMAKE_COMPILER=$GCC_COMPILER if [ $# -eq 0 ]; then - debug release elif [ "$1" = "clean" ]; then clean @@ -66,10 +65,8 @@ elif [ "$1" = "release" ]; then release elif [ "$1" = "all" ]; then - debug release elif [ "$1" = "gcc" ]; then - debug release else echo "build.sh " diff --git a/CMakeLists.txt b/CMakeLists.txt index a9132b353..a65a7105f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,8 +64,12 @@ endif() if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set(CMAKE_ASM_NASM_FLAGS "-DWIN64") set(CMAKE_ASM_NASM_FLAGS_DEBUG "") - set(CMAKE_EXE_LINKER_FLAGS " ") - set(CMAKE_SHARED_LINKER_FLAGS " ") + if (MSVC) + set(CMAKE_C_FLAGS " /GS /sdl ") + set(CMAKE_CXX_FLAGS " /GS /sdl ") + set(CMAKE_EXE_LINKER_FLAGS " /NXCompat /DynamicBase ") + set(CMAKE_SHARED_LINKER_FLAGS " /NXCompat /DynamicBase ") + endif() endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU") @@ -77,6 +81,13 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU") set(CMAKE_C_FLAGS_DEBUG "-O0 -g") endif() +# Prepare for Coveralls +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules) +if(CMAKE_COMPILER_IS_GNUCXX) + include(CodeCoverage) + setup_target_for_coverage(${PROJECT_NAME}_coverage ${PROJECT_TEST_NAME} coverage) +endif() + # Add Subdirectories add_subdirectory (Source/Lib) add_subdirectory (Source/App) diff --git a/Config/Sample.cfg b/Config/Sample.cfg index 92ab4a729..3c300445f 100644 --- a/Config/Sample.cfg +++ b/Config/Sample.cfg @@ -102,6 +102,14 @@ RecoveryPoint : 0 # SEI message (0= OFF, 1=ON ) TemporalId : 1 # 0: OFF, 1: Insert temporal ID in NAL units SwitchThreadsToRtPriority : 1 # 0: OFF, 1: Switch threads to real time priority (works on Linux only) FPSInVPS : 0 # 0: OFF, 1: Enable VPS timing info +MaxCLL : 0 # SEI message (0=OFF, 1=ON ) +MaxFALL : 0 # SEI message (0=OFF, 1=ON ) +UseMasterDisplay : 0 # SEI message. Enables or disables the MasterDisplayColorVolume (0=OFF, 1=ON ) +MasterDisplay : G(0,0)B(0,0)R(0,0)WP(0,0)L(0,0) + # SEI message. Signals SMPTE ST 2086 display info in the format "G(%hu,%hu)B(%hu,%hu)R(%hu,%hu)WP(%hu,%hu)L(%u,%u)" where %hu,%u are unsigned 16bit and 32 bit integers +DolbyVisionProfile : 0 # SEI message (0=OFF, 8.1 or 81 : Send bitstreams compliant to Dolby Vision Profile 81) +DolbyVisionRpuFile : Dolby.txt # SEI message. Dolby Vision RPU metadata file path. +NaluFile : Nalu.txt # SEI message. CEA 608/708 dynamic metadata file path. ====================== Platform Specific Flags =============================== AsmType : 1 # Assembly instruction set (0: non-AVX2, 1: up to AVX512 (Default: set based on platform capabilities)) diff --git a/Docs/10bit_packed.png b/Docs/10bit_packed.png new file mode 100644 index 0000000000000000000000000000000000000000..19f4b799aae6b7de3a5628485204318220fd2b8d GIT binary patch literal 12645 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 literal 0 HcmV?d00001 diff --git a/Docs/10bit_unpacked.png b/Docs/10bit_unpacked.png new file mode 100644 index 0000000000000000000000000000000000000000..b4578e7382eb39e84bb8d68d2cd26a3722e9e64b GIT binary patch literal 21597 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≈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̅zv_^d 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 ZygyC`XEnWWpr@sxrMbt#$8qmI&wU!&2V$H& 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

&t*m8*tog#95IV45$E>}g082E>Vq^43E zH$A6Al{l|1p+|_O$m0w38OnnV>MC6wE?h$u3J^e7BlXGAUHDs8;c*tNuL`q|9P)_Z z)g$oh8U#S1QMvVwW7{M_`BRV4j0`2Ac$OxWl(-ZEh|q)WiNgad<=9Yl>wJQl3kQ&c znbUr?{7nHuUG0ywQ!O6M^Oz`3NHbd+`C%mp;2!nqH0KRz7zWa!Y!c7vlV87fEu(_RTIFs~AAE;xRkmDm z<)6G@pGu!(P7jv+i}ZE*U8&Uin&Df0I`Ze@>Vmk;>5>HIz`Dh#Kf%o^{hr9C?vTe{ zpHw1%7Ip;CpchIR*;mWJz-!Lmt|WlUV^A*tQvDfJ~m_HY?XLqd#-l&KZ5 ziWKmxK|R-}t;sU9A)&#Pyb1y6lM@dTvMX}1t+keT-n>}jncK5uh$tY;K%u-XD7Ym- z?4ZsMf5`w*MFAsZUpcl z`Rr;40j%?t{4UC<&!Rr1-gu|pc$L8Y#`q+~FR#QeFAQ&P6+FgGU`{XP|I4=hVL&pO zP?&rrh+SakXK&!m&k?{kf0<`vc63psZ*{Xe+P%`3y?-oZiB-#Gnohq|8C5$rytvf` zKLV#V`yd>wPpCDc_(W5+cQ68470tm;yvbhTn(P>iW zi@K|Hza3x*it_Y1u*#@s)a4dtDeEpTUWSxBm*#mAvW_I%pOSW4JlWbrB>H#hGh44A zXv%xfU`p8g^g-*@&IOz#OIv=?EYgD}J(%_BUx@oM(y~mA{|y1~=*Ipjivj)bY_&5% zL5%nUnI*5yeNYcPJ2B34HFt)Xa>?2}0XkI2Ycsc?mZ znSu0RW~BTe|KR*Qle$u_=uOK}6^UEK|J-iRJu`k=9#TiE>`}I%t=zF%=FQHv+%yA+Pmb@6Rrq!LUdwzq0(ewp@+qkbl530hqLMOXwnXVnCp*_10T}*l zYzQE&N`>KwYM^C+IKYLpL8N%SyPscTK2UqmC9!cg@EJZsL?iqWSz2xsz{7dUIT#fh%- z$27W=r&0?5_B6ls_mny6#_WeL%;FrKmebx!iq(R*gL4CcoeGTj%yP|7&N@g zIiVMW8#_vWFpv-ZXWodRQlIDv9 zMkn}Cq+CHj*s>DTxT}bP7#czDU9V?3`O>fADb%-BZB>9~tidw*y(B;C&#&bSnNp$1bpB9Mz_n0c z1pa9M2LFgJ)21jW(k%V$Qx~&CZ4_+dvw%kzq4IK;2!J*K0hAbnd?mLtcHxk8)KOwI zJzVLO_$rC7Y}FyWg9yQv~pm<{JGxS{=5^(-WT^ahN7^tOoUk_I{H6 zZc@vaZYkg-qEE;An`2Dft`lFUcR&DMT?nB4TfGKjupB2-%#*9OQVGmfs`!T+N3_}r z#8dRzCKi(~v~F9DYDj0^u^qHZyl|n=-4qn>&B-74a^Jd{N<3M+97}MVl{ZQcX2Ajd z4e+xSSsw8orj*c9`yJfiuW49S1@Z?k%$wQuCsaT|@e3wzx%O-Ym4pkn{y3w+Ev~>F zBc%Hv*2TrN^AS3IE*8z<=sS`LcK<#c@%(z6A~xKd`@r+tkpBg_jb z9ZJx&o4L7m?+@?L7azb>RI6EbwLzVe{yX(Yrrol5UXK0@`&^xWax{Y$_Y#dC#?~(DNL7&48Ox9oye}H=CqRAH54%>N z{(YhOt52rFhQ*gYBY+7@*s%$;Q+d7D<3Vd~5qsR%*~i6$0cQ6ndmvFBo(U-xcDe}+ zpA0(QPd!mmZczHsbzDHA(-)KsYhVk%el?cBP%C4Uo>7}@QY&EPbRw0;FH*INO?^;nhrS6Wrm;K1t{ucu-cN5QagK8_j_W=Q%Cqzvs z-I>4@(Ua#XN4;nZ!C5Q4SEcy0O>VXhs^@;UhvtVl1;x#eZt*yo6^;C+%{!HjE$(72 zmtnB1CvF_NcbKy?-Vx}#<8ZT#(f`Gr{HtKwnkrW*El1rc=@Bgg0QHFVHcmNd*_%0} z>kcIqT^yaZhXk-#d4*kuQqY_Q{FDccw-;^|ov;whyBE~UtOFVi(i9x@w^9h@RIZF(}E4PHbK;VXm{f+1sz_>IT* zN$Lf%A#9>D^%Ap$i|UrEF-xcA5q&g?s)}`NB5Xf?OZ;ef0e+1DPPTIPJ9Av}H_DY< zmAfm5Z{4-%M1ohXRn5Mlhf9pWCi-COY&y>^AM@@JOW(^}5#e`sF*uZ#cg%yAOO}b{ zTnocVc?=fdmY(6!<A{iye{-TFf!A*bE9oyl)gDQH zthPZOJ~|eh4FQ=a`_m3%4N-GkT7)A@Kn#~*5!agon)UCdG_(R<1nsKa{FwC-|Dh_$ ztwh*E)|)yqIrb3Rqa$+LYT*#Q2{f(l;>Sb=dM(c0Y@;rA7bx?f&NTky8;4=NSnPt| zRFdGpx2l;})bK}|)F+3qW01rKI~34Hur9%ymEe9=#hmA6uM8Kd*fq*mnWC=_EAR)Y zhf`fx0;cBNUmD94+(X||^rOWgXczaDDB=9HaRT{e2VyFfZ&~B9li+LhD0N4ICM*Zn zzB!tD6m)t6>NUi!pv+|&{}Fg-Hdo6xkQFV5nrhz;4gGeh$zAW-V0>!rEy!0%Fl7Xh z4av4CYH+{*z|E$$H4U#mc$tIt;8w_!#DcB2QO5U(J54d0>@{B_(X=qlWU)yuR=>Au zY8zZO;y-U}w0S6FDBvn|Rf#mAqL-m6qYy)w5MNTm*} z$=zJ&Qa=JMJMT(u+^;|iYDp8t@@%~QMVJA%J@vxmSOQCqF`rWs`QB+wuryut}cV8R~(KvV90!XKZZpS-;fGdTV*4t#wo;SD-(uv+YIiLK5fe z?!aRcCe$N-);mF^U*boiD_e_`7G9Zx0Xkf(1C3LUGkR>8>Q5I?LEfPxkTT@nV7wXaQ7}{}%Tl87#bi{|NyoAc1MK%sDi;=x!fO z7y-CL12@I9v~$?ot3i#4_-PT4|GQRT=wwTDwOeo*TmdZ>vre=?0Mi^WBsc^r;~@Wh z0Gt)%dy$7$dubj`{|`(miM>lKOZ|5ZwApf;q=olQUQ?3EXq*}zT<5<^3%fN#eha-M z?=t211S*A6g4AS$OgR62Nnsy$rzoy{&`)Dl;Q@uX#i{4{%!;*PZLpw{9ZACsvXQd#MAJ#{sa<0!jhBiF zEuI|QI<8LdDC&1`PYm&`=qBCR(-kmmPGD2%HUVw-9QC5Z<+yFqCk}1m%p}`)?9U%e zcadGS`q-7g#Xg()hKHrbXSdlT23 zkeCH?E{l*QWD20jHL|-z(0KWhl3ggc*Is387BOKH-P~SxZ{pDL&qhOXuBWR z7RM6qG$`FFXb!h0lq9>7yLgR$t~p*?ztf110B8!rFE7L|vBp2N{D}02(>AQ=3O6urM*tqRCvphbcjy$5cJ96%mdwYGR(GqG&FBg*URGq^@P%q+Mf}KM)Y!v|CMZE z;MOQirvkbcbCcSy68G-yYmcRpjf7+^;Q+M==|?B|o|v}mB+12_U!DwSwpcuCC6oR< z;}nH%*jWcV*M5fpB;JYdog)COVPY$F&0t0-{av%KtHD27ewZ=cAVa{&Wa*l%aOi}f zV1xf`k^XkF+e;=9_b$V>nDx=@5(q&anFynPdN3Q^f2F?o^rI&O`h3=-3K|Clwx&?% zhu46~Yu1v}(<;!OdM;dDTRqf-OA3Hd%?T$wz^cd_hviGGML4T!?0k;RcPb_2rg4sp zhUVH@shZh4+Sq^_o^@6DS^Qy(MU1pqPV^01zbKQtlewq!xC(C#K2|=n{z4~DYFm?l z0-<*Re*$B$W}j@)gu|AB0S9G;4OI+7e-!(W6t|p-KX&(Kut-^l0~P&dvCohP|6$xk z&&hLPAC!w>GRB4r<2f9e!nekWH$yX&?%RO?+8AeNf53Gscx0~qUDdKwv#f=XwMwC9Rw5!BQ7uvi09;>Os+U{n;M!Ig` zd^y>cuG0z?y3>IeU-kBEx*$k5tXqq;s+76G#0-jVFM65NYZbr!;aDchR*ASB?$bM_ zu8IW|4iiMa>E<1&Akvg5q>{vYYpOcN8!@>^RSeMOPs(X~OS|(u3Zg1qMXj~L zkbXA<%*`Bs0!Mjc1Lkna`tTd?J}}Kq$T5^s`fd;y&-pHK9zS81;%cF^ZFu%!p88C9 z{3_){rX`!b4eCT$OuY{TovPl1s-?45DUgZ$WJnar3UHA8 z6nP>VrZfnFp3%N^EcQ;Y{RNObE$k za9qyw8o~k{YKD}dmX%blt4|vj&LF1q$O%REj%<*2hR|aVYGv$ACC#SWG*XTOlx*oN}lATL+Fj=V+}@zht4Lo?;b>e-rJ{E_L#B%LN1i)3f2^x~e= zdAYO9Far4JSr|{(hCEf0EZoFQTfBv~hgwOgExbktHrNh3`<4m+R)GLggAjo5(pdx5 zy*MWT{Dqg|M_WJfm+*s(9UBB7 z=0+Wb075AXg(A&@i%xUErOP1z1~7;a8{xdZ=mTSjq29LjpNn~d0FKs}ZjM`)WKzKq z#K5<`|Ko0{1U-6$Rn6pexe58bqsHmP*)7dHt+qwtj4lowE(`R?O&jsJhmnV9&%UY!~rYuvqAYI2A&&}M^ z^VwuPf1(B7|DJP=xx#l5EjvEzKKIc|JIij=@Fl~id%QPzgG&DCPma{g-Pr}}^m9SQm&7lo=6&$_q6$x}{yrK=ITV!TYDEvpj5-ajNn}{1yA-*=FfQ}08xp8m3u zztG_5-^UC6FUih`;SjUdGN(nT}}A zvW} z*>Y`7&@EpjJs8ZE(keDn!ZrBlJX`}~NVDmU`tbSr)U23VLl{@rc;lPejoe~kLreo5 ztzH|*f(Tt6viU_o2M)KA+pQ+6M?Y-jmpj_Ic5qi>9-oIlaF4R*bLC4Q%InC52HCuq-Fumyi*X_3`@w1Cn|9uIUWhihF^p$_xmg+ zLd)m^Ex98YyzqI=(chFgbUAhLxqI<(o+_L)C?vj*k&=T%W`2h1u|RM_-h9KWL~62g zz#YYY*x19X$X!B#rglu1nNKbiGp#%hfgvFTjqg* zF#}kpTsO-fEBb4!cV7(Eo%{UbxR)^0+v-4DlBH^yAi~F3|D-tC($w@y6q__+*9e^q z9^2|A2u2?(3pbvy?Y5?SjW11nrb;C1{q)|2PPfca$Fj4NbUOcMKIy-@c8~LCg6F0J zVo7zdV;=R*RnRHW&dI==SAM!KwUW%7eNM@q`1Y?AKL)P@cVSzg?_ZZUh%~UO@ zmr10lA^)A+TAILbZHfxC57aZKgQE9$l>Hz79_Xuw?&R_ z4qCpjv+aMN!|WIJP_O{{#UQ;v0OG6`2zd!Ye9!+AyQo_nOb z=M!CrZlO(YHZXNHO(Rc*bQHcN5#J}G&+q#q+KYsTT75>Oot;KfR*momx zLoer=O-_8BqvaeI0ZeInO4%qjJ+&G#bIc(VskArTFM@#OJkTe82UD0A`abV-V5XLL z#?cG~KR3AT%rr!K`0#lIvufn2=sa`CA#KP6Cw#8;H)!OaP97*}jH1P6>Tja)FjxC$ zG2;msnUfVf=vi%xGNBmdf5@YL(uc{XfOnFv<;zncSG zqWc~m%IFz?+bSfg_6-|5_V&!6#mCzcgMjIX4M%!Yh2JjnGquigT6)w1oG`I~^f426 z=l9)9s$Wlc^V3YD)W2(T{h(lbZ{0x6`t@*_`M4wO#Fpf>`(cB}2@U$PgWoN{RsC-p zMv}aUtEfOB(KMP73h}gf@9*RD?pqxNL=?MvOXv|YCnR;+!mg1In+u%s4fML$`}Z>i1L(Y{gkYzX#UAOcO42nucgOab@m<>rb&w6xOA9SFJWlX1N5@i z>wE=k1^DEU9q$NB;Er;ofsx)$cc;UA!A64UX?;k&|IXs)iOLt?x z`%uO<5t>RSt_SS}SPM2axaRl@<|S${8m8%KX75grSe0@%3b4Qi5WNtkXTK_^A@ctUOaf+c!NV`}8%>c)JJoYhk1goep6q5IFg$ok@M% z;wtm*i_o{vS*Tx{mO_wa9%onz?!HRHy_!?(QpB(;p{XXMq>Ynk2(Pvp)aiL68^_Mr zHuwtHnp<`zliQv&3GMS>4RRTWs8GJ1P&iWb*H}94{?9VEsT*wZ?~|5MKN@~5!pcVU zc*xedp>Jx}mfXpH3hxDz8zdzA8}WDF(c_}}KNRejxz3;VziEy)<~tc72_TuBBx9Py zSACT~ZB|}$rg_J4TEX1yW$qqMmfZBz^yfU9FpSG!VEC7}sj#c+(-+m%e06vo<$Nj~#870uOyp;`*B4Z{s#n)3tS^t}@V9tavLn44p1zZ7Wl4JcQa+Z7@oVJ|rq4Z&ws_&!fJJL*Xs=b{OM7PS`gazbtzK>k=kC$ti z)WofOt?D;_IsN1{Em zDR3uRinXYYB8QY^0zK&zf^w3E+slR|s*6*+aVOEMzp$*>1dFT(C|Liv{88Crl$IT&FJcGG&I1*qHNO=X z8oS7uDyt-Bf0x9$GKG4x_FkM63>d&Mc2ZtF_Y+iASZ2KgiGD2vg7v!UzmDag3ly=R zoYvS_b0j)n>Q_Lv_qWBo(pDbzA(JR#E=7lZNs0PR=yKNxDBMxMlLk}$2&8cM3d4#D z23D@|`BJA&L_8&O-{(F%=*(5d=(&(3rF78$?c@)VRtl@|s}YIDY5^x9uIU-J+rz!m z042OBK*46rdVs%6dFpgVki)C|sryjiOiJzGQy^TXx~^iVaL0~G;O_0u@9t-=c>+j6 zmt)gmq;%E(#(t1s2&uZ%XOv0x7(hiXGRZvksm>p&%r;ngfIVw*1!jLX#U(-wpKtc! zuLfqWb~^jb!yCfJ7Ak}NB`NQUXwEK)4@bz5MsyQMM*2-I!+C+rztyM7c)BP#4iP{B zl%aNzbS-$bk<-ntVDoIct-On&U5;2|=4t(7|LTf^vveN1E1A z`9fN68#jz6%t1Saa)gt&L5{9bi`Bp?i+TTT7_0BiRBps(vW z_dm=&VBo6) zioASYu}=q)9QU?OF9vqVZFD26M;RLhFUQ+Dk+2!t645ZeI$T8gRJjHNIXBTc-jRhJ zG01am0YNm=kC!MtjeMY}&6L9o0T#_)2r-9L9|t47K)sE5{9PlN7)jkSj&5VuKhwbL z?H%t7NLb+3=bSzhOP-288=?xoLTw}nfuA^?l#&#n?2Z34qF-03hSAGCu#fDe^WchF ztFh(FMOpUr(6-pwe5BS+X&BN4^go!4TE0z#3= zEjQP8`x(EzOAMy%>QMdPo(4lxxcg7Ap%V=0g&wcuvlee5fQz-C00zZ1^BkC*iSB^7 z#S+Y)@{{8jiRSO4+5WZ!5HPR`F8IrE<}*y(-bqr_(>&etu1jQJ9a3#$9Lt>OcK*dE zDzjBm(BEgJ_>Gbnj(U3L|yn;`-i6oFlxFMuG!Z*+ht%Kx4zi#5VAU|zY9XSiR$ zXMmA?lwx^`#!8F|d5u1?rNQz5%pz3DbdKKGF3f#|>4k#@TA@Xvb`wakJE{w+Gavrc zZp+$5%ic5y5^97J<-f>hE&1&dwK>SN)KzeAt>MJlZrIHo|M&057CxmZd*XB5~SQoOj`Uct2~m+|C{Mh}?HMEU)9 z1?GRczh=`Y&-1GH=t-@g;7*3hsrwhohPQaVo_G}W>sE1zix($s+1jYC_6Wd23b*dC zKj2j|S-fayrf=L!j{oIQ_6kv4q zOLVi>jD(yPBm`vE+a}cKsW~3XP^w60m?!LhW?svd$4|>woFLNVTQ9aXB<#y>^*Ea# z_wKJQ3*a9QX_M&id;MsTjK+59UQA8>YF+*Mr#3OrBg2w~ z^2mmU^wt8sq`<@=QPDtSR%&vYFUjs+(=yh+q~ai2zQX^2O@_zMu?@S;>J%1`U2*WDFb?#J%?b14 zb45kjm~oc32a?d1P{Bjjdr1xw6cwT0WSjG4HdHKoi4>|;rWxSSC)RNjZW<~&dXI-@ z3^dgN8IH7PFV- zZ)0n%q7_=AXfQlXA#2uLkJtOo=|k-8*Lt;jOA)|KQbMtpioIEI5AWFB)%|HXqQSS4 zD<4tNu;f(JOnfJ%Bff?O4-WA`$DP}ov_hUVboSIfzzKnt%BV!xTR;AlM}fx|{=I(5 z^Bsnym)qJwLQGp4tTAeQ#pgPhAJCs?5z+WLiYKdTKB+O&PtU)pg$*_h-dyr}|Gnc_ zx+(=4jhVpftJ(jk8T{(8;hBOq^MCrvbE|ngedUFk)3?=!`m-|Ja^y~TMh_?P+-DmC zudy=iFrVe%AX-imR52>-rSb)5NRxj3esi+h^KablPUSQLDnL2fW2{oKv|Mq2!#_v(v-fi> z&v0z{yguIg>NH08`|#fEd1^MhernkL1^yMajjpFrOJ62*-rJ3y5Pw{%i6a%w6)i2? zs!9s&Vxp?0Oc?!htUr6;sBcGj&z+mZC}g2PFrR9-uUiDrgHqR_o8MwfeYEEUnzvBu zU%j8z7SP$9g1Da8nfTfI(4W1ZRhr!Y7hhVidvEL7)8M+mX*`!?o--W1^9Am`3)>sY zhz;caX?|zL*XGyYH{@b}?+9r*(Sd&a*K@x*|Em|&1?&^w3#F+dMe$20y$n%@{bJHz z*)=eI&SC92Bb0D}^FW(U9z!j#PPX5gbHq~A6H6Y3HunXGAhA|1jQt@dmOKBpypQ{0 zyisN_fsx8vGn1nCYqd&jJHxZY`mNQC$oG+jDMMDX>i4fB79_p1 zg?S3u?Rf8o9o`>`ZrG!Ycyt*;$fov-)L$Kt<_Gt^xV*FZnpQHa{dR?H$$qeE5Jn3+ zriDolqv3(j7Iho9fxe_muAH4{xR-6Q$)xLTI<9=qZzjaqV%G5o*WQq5-9u&>h6te@>=#uI-D3X8M)qA^l(KqZw4?DVdJD##w~A!33g-3e7}&2SIu>EXMgwQ!0IdyGL* z0>4M-xqL}p%u!Vx9Ua$$-n>K4YFP7|)ub_=aVoxjlaiJIWE8%7ul{WqKD{WfZ5BMw z7R2F|o==iRQ2JtSMhIPWZMsTSB!6~O!7Ex+lS}s*Aw{=xz9N>NJ-5#Wgb+_Kw^;Ok z{E$Ay)`odqoz}O00?p_2fmj9KOixa(3w-`PPxJ43r2j8J$Ci3db6O!ZznC+m zVPSiZhob}4SUuc8$*Q;dV1z!=#ExO-i-G)Y)XXQ$sl7-tB4wjzpL^2q#)Fqu&I9eJ zDhZ1dMMFjD)F08UeQc*O-*h@I7{A96e5G&0^3oh9ikJIo-3vJq$p@nLjoKexirz4zm<2E6hZKtqVBbW>_Rdy@d=r#h zS);(DxtCJ)T#4dwxHDm=d)$I!fT93*c^I3+rPWa71Q+gIIUc~~da=H!*rtq$C(B) z(rzXO^td;iAxrGtD6`+t<)w^U-hrSZHe<@P&>iAfQ)Ae*9heqnesS?O zUg;k%o#pPJVSN%_Y9gM(Tz3#r{SIQoZ@3_uPJ$mz*KXeh+;a-SyV#MS;v??PBzhc` zYeXdpd_grIRa^fBH2*@c|AeCcNB6Qt%i4*%H+?@a&pJeT;1`LNRej|=HiMlYpW(v~ zLfc?V?aH_6@Zud@aA%{y4_dO~Sg=DnwTygEx;C@~nh8fZsygBZRqpWQaN^b>eQ{4Q zd9X%P{UgcmfeZ=yJJAPuR7pDeAt@3^UJI)AqNUn z0aJHZqa6jq}(sz$lGLpY71;F?v3-Yo=>t z9H+~uNf2=Blc0#Ah`oYijj&tLc;{$Q$L^W?K&v)RScule6JV3ye<#Vgn|G#&La+L0 zdT2M6gOef3gfgUmN($r0llQ*Kv*sf-z7kYw7MD)>1={UU`P&%l+BE{JpUE|cUqb4m zHi}~USYxB315u534Fbc2NWSV@!fEbIfS(kOM)57$s?buT5frfxtV$duK2*lwQf6x}HWI9FfVWEEvhOWmQdm!#)1dPGVe^{PV| z3=EyV9Pr-e%cQNi%5=wMA{7KuBt8qSb0xHSEKHhH=M9BT?es2L<`Mw*z=W8xl>)+nUqigU&k+rfc?j1i4dg$5%x zb6*-?km^E+ok8muj zQwsec6X}M=cHzkfFU3yFU9rw$uV(p-j#8jdet)L!>+IF{Z)la%c(D&h<5j1m54$m3 zh1S?A4@MvksZZqxnB*dt8yl+w#Wdp~VfOq+VV=$q)60p6E;ftWC%StqwrRetP&RSu zEj3G3U6kd8Ax$bgB|f~otgQ;2S#8piVj3|=UDSHAl~zew_BaYhG^gVYP|4>4LYoo} zDU3$)a7=0JP+3!&USWx|`c{_UOT?XpQw7cMv)W1b~q({8w59^r(d1b6zsb6eM=HENU%FG6 zSoC?8!s9m3oAUaYKI}wSN8i;DPrD2JD%612yq`di%VNQJCc2hr(6Ni^i!xe&0$%KW zY?R4%lPBuRX#Dv}xv0*qs|hLhhUu-H?xkXRe|S!lfh6tbyw}5&5>QcP{m@`r;#-en z=)F`HIzyu3@9ygE`O(pJ4dTtCRbXB;Gqb6ztJq01 zaB3p*k-_M>o{i(I*H!)6VLp@Mgvn^3+KzzVq!gZ>h^*xD#V5GK1dTav*klJ_J-yz1 zuQp7@+S>ZQr_Ufw$cpqVj{Pfol$1uS()v3IQ8IM8sINwhShq1LPg>XVwo*ri$)D9oGS&HuY?&x8pwjed~q9JNmUU1%0y`aJ=LK>e+ zh|;WXV_Q@@#ko}8`S6*o8?X99doX|*DFU3kz9g_5sua?SDL#AZ)HTqM?Sb+pY>aoBKUtKZ?X;kr-&`Rx zPqjVT56``4z==t2lqp`Z{eoHTF8=45NDR$+KSR~P-Q zZ1#7y+hzPmht>9mcHr5D>?lE~WPKWhZug~W%9|~vVmw`MM|QEJtEcWEdk}f@@)dGJ zQ5#c{uY%houP!y+|2#zTtL*uopVa-YPDTB?PK#9k+I^5$MFRUjN!^fvyJ;v)qN+px n`b^IsaN+A);Qyte0$n7#1VhZ>_@Je$J literal 0 HcmV?d00001 diff --git a/Docs/64x64_after_2bit_compression.png b/Docs/64x64_after_2bit_compression.png new file mode 100644 index 0000000000000000000000000000000000000000..71f73eb14546e6fa6ecc84640aae37d371091716 GIT binary patch literal 10857 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�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


H^vZv=a#QAICnRKA-`h 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

U~BE#e{6RmIM~a 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

~a?uJF~Z`?Zz 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

mXYCzQE@_g7+jTVvtmT9amJ|fc?`-W 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

NIveXRr1e=V0x> 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%0A^PlX#WM(4vJLH!8}DzU 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 WX^Pgi2HESxa;c~^j+cH3+@L-l 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 E2X3~kP5=M^ diff --git a/Docs/encoding_mode_res_table.png b/Docs/encoding_mode_res_table.png new file mode 100644 index 0000000000000000000000000000000000000000..9a02fd762fa0b6b48b6c1333931da009b07c723b GIT binary patch literal 37814 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

b(cN zDI3u3#xc>C^m*OxQA3tR(A=zwu9V zvDYT_-`mdOk&D2KW5J7Sx~_vN(4x>CRoH<++lf&hTCyzFy+G3FVD+P4I|$tRJY1Pt zx7Ea`s!NLz(dbO0#l*M+486Li?DUeiW{*wRg2E3Z6cw5q${Uw#Wf<&%8Ym>orhvcM zp9C5x{AS!8?V7Fr*?-UPHF;i7ZrYkkr1BZ9prtK7_71%~Q?@Mbt$Fk(I1RK4ZbvZx z!5ewz$=hm=`tnV822d9AydLx&f+~yJpLczH+gogglhmnCTchBi7ehscG!p9X1(a|n z;&vDI4irVv?W~G=I^jbvz31*X2VkHH(Z|N$qUye7 zPYq2yun7c41!K$0tAw^o%J;H2V=;|3b0a?xV()0ZcdMlV8c>XxEkJM^f!6B*KW!b| zz?^nF0)>0^Y)UH)5{-L4UiOhwO0!vYH_DpPbW%`I=)8_Pu2AeMxZnEC_fj<3cut9e zoQ38;u(#N>x4uz>!}Y`pDm@25pt)!J5cCum-gE?PjB(!nmb9(;^*p_k>At_w|GEBO z@&ZdqWaM-(q(jf49EYZ2k76zC`4vLjBH-tmeNxx&IU&=-w+>m&$QM8Rp2$fexoT-}L2& ztQdd6n~nqAHysD&_MY=ahJ=hfhZ>y!VGMT<@ursWW*T(`+Wa>sKpBtvYJn(4X@b1> zng<(x2x?of(pCYY_Q3lC{wZV{zk>zuRi!4RQa7v{8RQz60;BY+h!rb67W>8XA@R!i z=TPIiKYZ>Yz<+5gfea%`-_wBA4JglFgBO$KA*-mlP3NI5=fJUD1JoVV3H6_0z&?xP zK89)xK6Sjp1f(~94Nc+)Build requirements + - Visual Studio\* 2017 + - YASM Assembler version 1.2.0 or later + - Download the yasm exe from the following [link](http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win64.exe) + - Rename yasm-1.3.0-win64.exe to yasm.exe + - Copy yasm.exe into a location that is in your system PATH environment variable + - CMake 3.5 or later [link](https://github.com/Kitware/CMake/releases/download/v3.13.0/cmake-3.13.0-win64-x64.msi) + +Build instructions +- Generate the Visual Studio\* 2017 project files by following the steps below in a windows command line prompt: + +- In the main repository directory go under the \\Build\windows location +- Run generate\_vs17.bat [such would generate the visual studio project files] +- Open the "svt-hevc.sln" using Visual Studio\* 2017 and click on Build --> Build Solution + +- Binaries Location + - Binaries can be found under \\Bin/Release or \\Bin/Debug, depending on whether Debug or Release were selected in the build mode + +#### *Linux\* Operating Systems (64-bit)* + +Build requirements + - GCC 5.4.0 + - CMake 3.5.1 + - YASM Assembler version 1.2.0 or later + +Build instructions + - In the main repository, run + - >mkdir build && cd build && cmake .. && make -j \`nproc\` && sudo make install +- Binaries Location + - Binaries can be found under Bin/Release + + +## Installation + +For the binaries to operate properly on your system, the following conditions have to be met: + +#### *Windows\**: + - On any of the Windows\* operating systems listed in section 2.3, Install Visual Studio 2017 + - Once the installation is complete, copy the binaries to a location making sure that both the sample application "SvtHevcEncApp.exe" and library "SvtHevcEnc.dll" are in the same folder. + - Open the command line at the chosen location and run the sample application to encode. + +#### *Linux\**: + - On any of the Linux\* operating systems listed in section 2.3, copy the binaries under a location of your choice. + - Change the permissions on the sample application "SvtHevcEncApp" executable by running the command: + - >chmod +x SvtHevcEncApp + - To enable 100% CPU utilization for the real-time SvtHevcEncApp, run the command: + - >sudo sysctl -w kernel.sched\_rt\_runtime\_us=1000000 + - Open terminal and cd into your directory, then run the sample application to encode + +## Sample Application Guide + +This section describes how to run the sample encoder application that uses the SVT-HEVC Encoder library. It describes the input video format, the command line input parameters and the resulting outputs. + +### Input Video Format + +The SVT-HEVC Encoder supports the following input formats: + +8-bit yuv420p +
+![alt](8bit_yuv420p_1.png) + +
+ +10-bit yuv420p10le +
+![alt](10bit_yuv420p.png) + + +### Compressed 10-bit format + +In order to reduce the size of the input original YUV file, the SVT-HEVC Encoder uses a compressed 10-bit format allowing the software to achieve a higher speed and channel density levels. The conversion between the 10-bit yuv420p10le and the compressed 10-bit format is a lossless operation and is performed using the following steps. + +#### Unpack the 10 bit picture + +This step consists of separating the 10 bit video samples into 8 bit and 2 bit planes so that each 10-bit picture will be represented as two separate pictures as shown in the figure below. As a result of the operation, the 2 least significant bits of the 10 bits will be written into a full byte. + + ![alt](10bit_unpacked.png) + +
+10-bit yuv420p10le unpacked + +#### Compress the 2 bit Plane + +The unpacking steps separates the 10bits into a group of 8 bits and a group of 2 bits, where the 2 bits are stored in a byte. In this step, every group of consecutive 4 bytes, each containing 2bits from the unpacking step, are compressed into one byte. As a result, each 10bit picture will be represented as two separate pictures as shown in the figure below. + + ![alt](10bit_packed.png) + +#### Unroll the 64x64 + +Now for a faster read of the samples, every 64x64 block of the 2 bit picture should be written into a one dimensional array. Therefore, the top left 64x64 sample block which is now written into a 16 bytes x 64 bytes after the compression of the 2bit samples, will be written into a 1024 bytes x 1 byte array as shown in the picture below. + + ![alt](64x64_block_after_unrolling.png) + + +### Running the encoder + +This section describes how to run the sample encoder application SvtHevcEncApp.exe (on Windows\*) or SvtHevcEncApp (on Linux\*) from the command line, including descriptions of the most commonly used input parameters and outputs. + +The sample application typically takes the following command line parameters: + +>-c filename [**Optional**] + +A text file that contains encoder parameters such as input file name, quantization parameter etc. Refer to the comments in the Config/Sample.cfg for specific details. The list of encoder parameters are also listed below. Note that command line parameters take precedence over the parameters included in the configuration file when there is a conflict. + +>-tune integer **[Optional]** + +This token sets the encoder to run in the visual quality optimized mode (when set to 0), PSNR/SSIM optimized mode (when set to 1 [default setting]), or VMAF optimized mode (when set to 2) + +>-i filename **[Required]** + +A YUV file (e.g. 8 bit 4:2:0 planar) containing the video sequence that will be encoded. The dimensions of each image are specified by –w and –h as indicated below. + +>-b filename **[Optional]** + +The resulting encoded bit stream file in binary format. If none specified, no output bit stream will be produced by the encoder. + +>-w integer **[Required]** + +The width of each input image in units of picture luma pixels, e.g. 1920 + +>-h integer **[Required]**] + +The height of each input image in units of picture luma pixels, e.g. 1080 + +>-n integer **[Optional]** + +The number of frames of the sequence to encode. e.g. 100. If the input frame count is larger than the number of frames in the input video, the encoder will loop back to the first frame when it's done. + +>-intra-period integer **[Optional]** + +The intra period defines the interval of frames after which you insert an Intra refresh. It is strongly recommended to use (multiple of 8) -1 the closest to 1 second (e.g. 55, 47, 31, 23 should be used for 60, 50, 30, (24 or 25) respectively) + +>-rc integer **[Optional]** + +This token sets the bitrate control encoding mode [1: Variable Bitrate, 0: Constant QP]. When rc is set to 1, it's best to match the –lad (lookahead distance described in the next section) parameter to the -intra-period. When –rc is set to 0, a qp value is expected with the use of the –q command line option otherwise a default value is assigned (25). + +>-speed-ctrl integer **[Optional]** + +This token sets the encoder to automatically choose the best quality encoding mode that allows the encoder to run at a real-time speed set by the –fps parameter (described in the next section). + +For example, the following command encodes 100 frames of the YUV video sequence into the bin bit stream file. The picture is 1920 luma pixels wide and 1080 pixels high using the Sample.cfg configuration. The QP equals 30 and the md5 checksum is not included in the bit stream. + +> SvtHevcEncApp.exe -c Sample.cfg -i CrowdRun\_1920x1080.yuv -w 1920 -h 1080 -n 100 -q 30 -intra-period 31 -b CrowdRun\_1920x1080\_qp30.bin + +It should be noted that not all the encoder parameters present in the Sample.cfg can be changed using the command line. + +#### List of all configuration parameters + +The encoder parameters present in the Sample.cfg file are listed in this table below along with their status of support, command line parameter and the range of values that the parameters can take. + + +| **Encoder Parameter as shown in the configuration file** | **Command Line parameter** | **Range** | **Default** | **Description** | +| --- | --- | --- | --- | --- | +| **Channel Number** | -nch | [1 - 6] | 1 | Number of encode instances | +| **ConfigFile** | -c | any string | null | Configuration file path | +| **InputFile** | -i | any string | null | Input file path and name | +| **StreamFile** | -b | any string | null | Output bitstream file path and name | +| **ErrorFile** | -errlog | any string | stderr | Error log displaying configuration or encode errors | +| **ReconFile** | -o | any string | null | Output reconstructed yuv used for debug purposes. **Note:** using this feature will affect the speed of the encoder significantly. This should only be used for debugging purposes. | +| **UseQpFile** | -use-q-file | [0, 1] | 0 | When set to 1, overwrite the picture qp assignment using qp values in QpFile | +| **QpFile** | -qp-file | any string | null | Path to qp file | +| **EncoderMode** | -encMode | [0 - 12] | 9 | A preset defining the quality vs density tradeoff point that the encoding is to be performed at. (e.g. 0 is the highest quality mode, 12 is the highest density mode). Section 3.4 outlines the preset availability per resolution | +| **Tune** | -tune | [0, 1, 2] | 1 | 0 = SQ - visually optimized mode, 1 = OQ - PSNR / SSIM optimized mode, 2 = VMAF - VMAF optimized mode | +| **EncoderBitDepth** | -bit-depth | [8, 10] | 8 | Specifies the bit depth of input video | +| **EncoderColorFormat** | -color-format | [1, 2, 3] | 1 | Specifies the chroma subsampling of input video(1: 420, 2: 422, 3: 444) | +| **CompressedTenBitFormat** | -compressed-ten-bit-format | [0, 1] | 0 | Offline packing of the 2bits: requires two bits packed input (0: OFF, 1: ON) | +| **SourceWidth** | -w | [64 - 8192] | 0 | Input source width | +| **SourceHeight** | -h | [64 - 4320] | 0 | Input source height | +| **FrameToBeEncoded** | -n | [0 - 2^31 -1] | 0 | Number of frames to be encoded, if number of frames is > number of frames in file, the encoder will loop to the beginning and continue the encode. 0 encodes the full clip. | +| **BufferedInput** | -nb | [-1, 1 to 2^31 -1] | -1 | number of frames to preload to the RAM before the start of the encode. If -nb = 100 and –n 1000 --> the encoder will encode the first 100 frames of the video 10 times. Use -1 to not preload any frames. | +| **Profile** | -profile | [1,2] | 2 | 1: Main, 2: Main 10 | +| **Tier** | -tier | [0, 1] | 0 | 0: Main, 1: High | +| **Level** | -level | [1, 2, 2.1,3, 3.1, 4, 4.1, 5, 5.1, 5.2, 6, 6.1, 6.2] | 0 | 0 to 6.2 [0 for auto determine Level] | +| **FrameRate** | -fps | [0 - 2^64 -1] | 25 | If the number is less than 1000, the input frame rate is an integer number between 1 and 60, else the input number is in Q16 format (shifted by 16 bits) [Max allowed is 240 fps]. If FrameRateNumerator and FrameRateDenominator are both !=0 the encoder will ignore this parameter | +| **FrameRateNumerator** | -fps-num | [0 - 2^64 -1] | 0 | Frame rate numerator e.g. 6000When zero, the encoder will use –fps if FrameRateDenominator is also zero, otherwise an error is returned | +| **FrameRateDenominator** | -fps-denom | [0 - 2^64 -1] | 0 | Frame rate denominator e.g. 100When zero, the encoder will use –fps if FrameRateNumerator is also zero, otherwise an error is returned | +| **Injector** | -inj | [0,1] | 0 | Enable injection of input frames at the specified framerate (0: OFF, 1: ON) | +| **InjectorFrameRate** | -inj-frm-rt | [1 - 240] | 60 | Frame Rate used for the injector. Recommended to match the encoder speed. | +| **SpeedControlFlag** | -speed-ctrl | [0,1] | 0 | Enables the Speed Control functionality to achieve the real-time encoding speed defined by –fps. When this parameter is set to 1 it forces –inj to be 1 -inj-frm-rt to be set to the –fps. | +| **InterlacedVideo** | -interlaced-video | [0,1] | 0 | 1 : encoder will signal interlaced signal in the stream
0 : assumes progressive signal | +| **SeparateFields** | -separate-fields | [0,1] | 0 | 1 : Interlaced input, application will separate top and bottom fields and encode it as progressive.
0 : Treat video as progressive video | +| **HierarchicalLevels** | -hierarchical-levels | [0 – 3] | 3 | 0 : Flat
1: 2-Level Hierarchy
2: 3-Level Hierarchy
3: 4-Level HierarchyMinigop Size = (2^HierarchicalLevels)
(e.g. 3 == > 7B pyramid, 2 ==> 3B Pyramid)Refer to Appendix A.1 | +| **BaseLayerSwitchMode** | -base-layer-switch-mode | [0,1] | 0 | 0 : Use B-frames in the base layer pointing to the same past picture
1 : Use P-frames in the base layerRefer to Appendix A.1 | +| **PredStructure** | -pred-struct | [0 – 2] | 2 | 0: Low Delay P1: Low Delay B
2: Random AccessRefer to Appendix A.1 | +| **IntraPeriod** | -intra-period | [-2 - 255] | -2 | Distance Between Intra Frame inserted. -1 denotes no intra update. -2 denotes auto. | +| **IntraRefreshType** | -irefresh-type | [1,2] | 1 | 1: CRA (Open GOP)2: IDR (Closed GOP) | +| **QP** | -q | [0 - 51] | 25 | Initial quantization parameter for the Intra pictures used when RateControlMode 0 (CQP) | +| **LoopFilterDisable** | -dlf | [0, 1] | 0 | When set to 1 disables the Deblocking Loop Filtering | +| **SAO** | -sao | [0,1] | 1 | When set to 0 the encoder will not use the Sample Adaptive Filter | +| **UseDefaultMeHme** | -use-default-me-hme | [0, 1] | 1 | 0 : Overwrite Default ME HME parameters
1 : Use default ME HME parameters, dependent on width and height | +| **HME** | -hme | [0,1] | 1 | Enable HME, 0 = OFF, 1 = ON | +| **SearchAreaWidth** | -search-w | [1 - 256] | Depends on input resolution | Search Area in Width | +| **SearchAreaHeight** | -search-h | [1 - 256] | Depends on input resolution | Search Area in Height | +| **ConstrainedIntra** | -constrd-intra | [0,1] | 0 | Allow the use of Constrained Intra, when enabled, this features yields to sending two PPSs in the HEVC Elementary streams
0 = OFF, 1 = ON | +| **RateControlMode** | -rc | [0,1] | 0 | 0 : CQP , 1 : VBR | +| **TargetBitRate** | -tbr | Any Number | 7000000 | Target bitrate in bits / second. Only used when RateControlMode is set to 1 | +| **MaxQpAllowed** | -max-qp | [0 - 51] | 48 | Maximum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be >= MinQpAllowed | +| **MinQpAllowed** | -min-qp | [0 - 50] | 10 | Minimum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be < MaxQpAllowed | +| **LookAheadDistance** | -lad | [0 - 250] | Depending on BRC mode | When RateControlMode is set to 1 it's best to set this parameter to be equal to the Intra period value (such is the default set by the encoder), When CQP is chosen, then a (2 \* minigopsize +1) look ahead is recommended. | +| **SceneChangeDetection** | -scd | [0,1] | 1 | Enables or disables the scene change detection algorithm
0 = OFF, 1 = ON | +| **BitRateReduction** | -brr | [0,1] | 1 | Enables visual quality algorithms to reduce the output bitrate with minimal or no subjective visual quality impact. (no support for –tune 1 or -tune 2)
0 = OFF, 1 = ON | +| **ImproveSharpness** | -sharp | [0,1] | 1 | This is a visual quality knob that allows the use of adaptive quantization within the picture and enables visual quality algorithms that improve the sharpness of the background. This feature is only available for 4k and 8k resolutions (no support for –tune 1 or -tune 2)
0 = OFF, 1 = ON | +| **VideoUsabilityInfo** | -vid-info | [0,1] | 0 | Enables or disables sending a vui structure in the HEVC Elementary bitstream. 0 = OFF, 1 = ON | +| **HighDynamicRangeInput** | -hdr | [0,1] | 0 | When set to 1, signals HDR10 input in the output HEVC elementary bitstream and forces VideoUsabilityInfo to 1.
0 = OFF, 1 = ON | +| **AccessUnitDelimiter** | -ua-delm | [0,1] | 0 | SEI message, 0 = OFF, 1 = ON | +| **BufferingPeriod** | -pbuff | [0,1] | 0 | SEI message, 0 = OFF, 1 = ON | +| **PictureTiming** | -tpic | [0,1] | 0 | SEI message, 0 = OFF, 1 = ON.
If 1, VideoUsabilityInfo should be also set to 1. | +| **RegisteredUserData** | -reg-user-data | [0,1] | 0 | SEI message, 0 = OFF, 1 = ON | +| **UnregisteredUserData** | -unreg-user-data | [0,1] | 0 | SEI message, 0 = OFF, 1 = ON | +| **RecoveryPoint** | -recovery-point | [0,1] | 0 | SEI message, 0 = OFF, 1 = ON | +| **TemporalId** | -temporal-id | [0,1] | 1 | 0 = OFF, 1 = Insert temporal ID in NAL units | +| **AsmType** | -asm | [0,1] | 1 | Assembly instruction set
(0: C Only, 1: Automatically select highest assembly instruction set supported) | +| **LogicalProcessorNumber** | -lp | [0, total number of logical processor] | 0 | The number of logical processor which encoder threads run on.Refer to Appendix A.2 | +| **TargetSocket** | -ss | [-1,1] | -1 | For dual socket systems, this can specify which socket the encoder runs on.Refer to Appendix A.2 | +| **SwitchThreadsToRtPriority** | -rt | [0,1] | 1 | Enables or disables threads to real time priority, 0 = OFF, 1 = ON (only works on Linux) | +| **FPSInVPS** | -fpsinvps | [0,1] | 0 | Enables or disables the VPS timing info, 0 = OFF, 1 = ON | +| **MaxCLL** | -max-cll | [0 , 2^16-1] | 0 | Maximum content light level (MaxCLL) as required by the Consumer Electronics Association 861.3 specification. Applicable for HDR content. If specified, signalled only when HighDynamicRangeInput is set to 1 | +| **MaxFALL** | -max-fall | [0 , 2^16-1] | 0 | Maximum Frame Average light level (MaxFALL) as required by the Consumer Electronics Association 861.3 specification. Applicable for HDR content. If specified, signalled only when HighDynamicRangeInput is set to 1 | +| **UseMasterDisplay** | -use-master-display | [0,1] | 0 | Enables or disables the MasterDisplayColorVolume, 0 = OFF, 1 = ON | +| **MasterDisplay** | -master-display | For R, G, B and whitepoint [0, 2^16-1]. For max, min luminance [0, 2^32-1] | 0 | SMPTE ST 2086 mastering display color volume SEI info, specified as a string. The string format is “G(%hu,%hu)B(%hu,%hu)R(%hu,% hu)WP(%hu,%hu)L(%u,%u)” where %hu are unsigned 16bit integers and %u are unsigned 32bit integers. The SEI includes X, Y display primaries for RGB channels and white point (WP) in units of 0.00002 and max, min luminance (L) values in units of 0.0001 candela per meter square. Applicable for HDR content. Example for a P3D65 1000-nits monitor,G(13250,34500)B(7500,3 000)R(34000,16000)WP(15635,16 450)L(10000000,1) | +| **DolbyVisionRpuFile** | -dolby-vision-rpu | any string | null | Path to the file containing Dolby Vision RPU metadata | +| **DolbyVisionProfile** | -dolby-vision-profile | 8.1 or 81 | 0 | Generate bitstreams confirming to the specified Dolby Vision profile 8.1. When specified, enables HighDynamicRangeInput automatically. Applicable only for 10-bit input content. MasterDisplay should be set for using dolby vision profile 81. Pass the dynamic metadata through DolbyVisionRpuFile option | +| **NaluFile** | -nalu-file | any string | null | Path to the file containing CEA 608/708 metadata. Text file should contain the userSEI in POC order as per below format: /. Currently only PREFIX_SEI messages are supported | + +### Encoding presets table + +The table below shows the preset availability per encoding mode and resolution. + +![alt](encoding_mode_res_table.png) + + +## Best Known Configurations (BKC) + +This section outlines the best known hardware and software configurations that would allow the SVT-HEVC Encoder to run with the highest computational performance. For the CQP mode, the output bit stream will not change if these BKCs have not been applied. + + +### Hardware BKC + +The SVT-HEVC Encoder is optimized for use on Xeon® Scalable Processors products. For best multichannel encode, servers should be set up with at least one 2666 Mhz DDR4 RAM DIMM per RAM channel per socket. For example, a dual Xeon Platinum 8180 server is best set up with 12 x 2666 Mhz DDR4 RAM DIMM. + + +### Software BKC + +#### *10 bit Input YUV* + +Due to the large size of 10-bit video, using the compressed YUV format as shown in section 3.2 allows for the best performance of the encoder. + +#### *Windows\* OS (Tested on Windows\* Server 2016)* + +Visual Studio 2017 offers Profile Guided Optimization (PGO) to improve compiler optimization for the application. The tool uses an instrumented build to generate a set of profile information of the most frequently used code and optimal paths. The profile is then used to provide extra information for the compiler to optimize the application. To take advantage of PGO, build using the following: + +1. Open the solution file with Visual Studio 2017 and build code in Release mode +2. Right click SvtHevcEncApp project from the Solution Explorer -> Profile Guided Optimization -> Instrument (Repeat for SvtHevcEnc) +3. Right click SvtHevcEncApp project from the Solution Explorer -> Properties -> Debugging +4. Add configuration parameters and run encoder (e.g. 1280x720 video encode of 300 frames) +5. Right click SvtHevcEncApp project from the Solution Explorer -> Profile Guided Optimization -> Run Instrumented/Optimized Application +6. Right click SvtHevcEncApp project from the Solution Explorer -> Profile Guided Optimization -> Optimize (Repeat for SvtHevcEnc) + +#### *Linux\* OS (Tested on Ubuntu\* Server 18.04 and 16.04)* + +Some Linux\* Operating systems and kernels assign CPU utilization limits to applications running on servers. Therefore, to allow the application to utilize up to ~100% of the CPUs assigned to it, it is best to run the following commands before and when running the encoder: + +> sudo sysctl -w kernel.sched\_rt\_runtime\_us=1000000 +- this command should be executed every time the server is rebooted + +> SvtHevcEncApp -i input.yuv -w 3840 -h 2160 & + export PID=$! + sudo chrt -f -a -p 99 $PID +- this command should be executed with "-rt 1" with the SVT-HEVC application to allow run-time priorities + +The above section is not needed for Windows\* as it does not perform the CPU utilization limitation on the application. + +### Command line BKC + +The SVT-HEVC encoder achieves the best performance when restricting each channel to only one socket on either Windows\* or Linux\* operating systems. For example, when running four channels on a dual socket system, it's best to pin two channels to each socket and not split every channel on both sockets. + +LogicalProcessorNumber (-lp) and TargetSocket (-ss) parameters can be used to management the threads. Or you can use OS commands like below. + +For example, in order to run a 6-stream 4kp60 simultaneous encode on a Xeon Platinum 8180 system the following command lines should be used: + +#### *Running Windows\* Server 2016:* + +>start /node 0 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out1.bin -n 5000 –nb 500 + +>start /node 0 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out2.bin -n 5000 –nb 500 + +>start /node 0 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out3.bin -n 5000 –nb 500 + +>start /node 1 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out3.bin -n 5000 –nb 500 + +>start /node 1 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out4.bin -n 5000 –nb 500 + +>start /node 1 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out5.bin -n 5000 –nb 500 + +#### *Running Ubuntu\* 18.04:* + +>taskset 0x0000000FFFFFFF0000000FFFFFFF ./SvtHevcEncApp -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out1.bin -n 5000 –nb 500 & + +>taskset 0x0000000FFFFFFF0000000FFFFFFF ./SvtHevcEncApp -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out2.bin -n 5000 –nb 500 & + +>taskset 0x0000000FFFFFFF0000000FFFFFFF ./SvtHevcEncApp -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out3.bin -n 5000 –nb 500 & + +>taskset 0xFFFFFFF0000000FFFFFFF0000000 ./SvtHevcEncApp -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out3.bin -n 5000 –nb 500 & + +>taskset 0xFFFFFFF0000000FFFFFFF0000000./SvtHevcEncApp -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out4.bin -n 5000 –nb 500 & + +>taskset 0xFFFFFFF0000000FFFFFFF0000000./SvtHevcEncApp -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -fps 60 -b out5.bin -n 5000 –nb 500 & + +
+Similarly, in order to run a 2-stream 8kp50 simultaneous encode on a Xeon Platinum 8180 system the following command lines should be used: + +#### *Running Windows\* Server 2016:* + +>start /node 0 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 7680 -h 4320 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 20000000 -fps 50 -b out1.bin -n 5000 –nb 500 + +>start /node 1 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 7680 -h 4320 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 20000000 -fps 50 -b out1.bin -n 5000 –nb 500 + +#### *Running Ubuntu 18.04\*:* + +>taskset 0x0000000FFFFFFF0000000FFFFFFF ./SvtHevcEncApp -encMode 12 -tune 0 -w 7680 -h 4320 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 20000000 -fps 50 -b out1.bin -n 5000 –nb 500 & + +>taskset 0xFFFFFFF0000000FFFFFFF0000000./SvtHevcEncApp -encMode 12 -tune 0 -w 7680 -h 4320 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 20000000 -fps 50 -b out1.bin -n 5000 –nb 500 & + +
+Where 0x0000000FFFFFFF0000000FFFFFFF and 0xFFFFFFF0000000FFFFFFF0000000 are masks for sockets 0 and 1 respectively on a dual 8180 system. + +## Appendix A Encoder Parameters + +### Hierarchical coding structure parameters + +The GOP is constructed assuming a prediction structure (PredStructure: LowDelay/Random-Access) and hierarchical levels (HierarchicalLevels: number of hierarchical layers). + +The prediction structure (PredStructure) in SVT-HEVC encoder supports two main structures which are: + +- Low Delay (P or B): In a LowDelay structure, pictures within a mini-GOP refer to the previously encoded pictures in display order. In other words, pictures with display order N can only be referenced by pictures with display order greater than N, and it can only refer pictures with picture order lower than N. The LowDelay prediction structure can be flat structured (e.g. IPPPPPPP….) or hierarchically structured as described in Figure A where a 3-Layer Hierarchical-LowDelay-P Structure is shown. + +![alt](3-layer-hierarchical-low-delay.png) + +In a LowDelay structure, B/b pictures can be used instead of P/p pictures. However, the reference picture list 0 and the reference picture list 1 will contain the same reference picture as described in Figure B. + +![alt](3-layer-hierarchical-low-delay2.png) + +- Random Access: In this prediction structure, the B/b pictures can refer to reference pictures from both directions (past and future). Figure C shows an example of a three-layer random access prediction structure. In this figure, the B picture at temporal layer 0 can be replaced with a P picture and this is can be done through the configuration parameter "BaseLayerSwitchMode" by setting it to "1". + +![alt](3-layer-hierarchical-low-delay3.png) + +In the SVT-HEVC code, the GOP structure is constructed in the Picture Decision process which performs multi-picture level decisions, including setting the prediction structure, setting the picture type, and scene change detection. Since the prior Picture Analysis processes stage is multithreaded, inputs to the Picture Decision Process can arrive out-of-display-order, so a reordering queue is used to enforce processing of pictures in display order. The algorithms employed in the Picture Decision process are dependent on prior pictures' statistics, so the order in which pictures are processed must be strictly enforced. Additionally, the Picture Decision process uses the reorder queue to hold input pictures until they are ready to be sent to the Motion Analysis process, following the proper prediction structure. + +### Thread management parameters + +LogicalProcessorNumber (-lp) and TargetSocket (-ss) parameters are used to management thread affinity on Windows and Ubuntu OS. These are some examples how you use them together. + +If LogicalProcessorNumber and TargetSocket are not set, threads are managed by OS thread scheduler. + +>SvtHevcEncApp.exe -i in.yuv -w 3840 -h 2160 –lp 40 + +If only LogicalProcessorNumber is set, threads run on 40 logical processors. Threads may run on dual sockets if 40 is larger than logical processor number of a socket. + +NOTE: On Windows, thread affinity can be set only by group on system with more than 64 logical processors. So, if 40 is larger than logical processor number of a single socket, threads run on all logical processors of both sockets. + +>SvtHevcEncApp.exe -i in.yuv -w 3840 -h 2160 –ss 1 + +If only TargetSocket is set, threads run on all the logical processors of socket 1. + +>SvtHevcEncApp.exe -i in.yuv -w 3840 -h 2160 –lp 20 –ss 0 + +If both LogicalProcessorNumber and TargetSocket are set, threads run on 20 logical processors of socket 0. Threads guaranteed to run only on socket 0 if 20 is larger than logical processor number of socket 0. + + + +## Legal Disclaimer + +Optimization Notice: Intel compilers may or may not optimize to the same degree for non-Intel microprocessors for optimizations that are not unique to Intel microprocessors. These optimizations include SSE2, SSE3, and SSSE3 instruction sets and other optimizations. Intel does not guarantee the availability, functionality, or effectiveness of any optimization on microprocessors not manufactured by Intel. Microprocessor-dependent optimizations in this product are intended for use with Intel microprocessors. Certain optimizations not specific to Intel microarchitecture are reserved for Intel microprocessors. Please refer to the applicable product User and Reference Guides for more information regarding the specific instruction sets covered by this notice. + +Notice Revision #20110804 + +Intel technologies features and benefits depend on system configuration and may require enabled hardware, software or service activation. Performance varies depending on system configuration. No computer system can be absolutely secure. Check with your system manufacturer or retailer. + +No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document. + +Intel disclaims all express and implied warranties, including without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement, as well as any warranty arising from course of performance, course of dealing, or usage in trade. + +The products and services described may contain defects or errors known as errata which may cause deviations from published specifications. Current characterized errata are available on request. ** ** No product or component can be absolutely secure. + +This document contains information on products, services and/or processes in development. All information provided here is subject to change without notice. Contact your Intel representative to obtain the latest forecast, schedule, specifications and roadmaps. + +Intel, Intel Xeon, Intel Core, the Intel logo and others are trademarks of Intel Corporation and its subsidiaries in the U.S. and/or other countries. + +\*Other names and brands may be claimed as the property of others. + +Copyright 2019 Intel Corporation. diff --git a/NOTICES.md b/NOTICES.md new file mode 100644 index 000000000..3e45555d4 --- /dev/null +++ b/NOTICES.md @@ -0,0 +1,12 @@ +# Notices and Disclaimers + +Optimization Notice: Intel's compilers may or may not optimize to the same degree for non-Intel microprocessors for optimizations that are not unique to Intel microprocessors. These optimizations include SSE2, SSE3, and SSSE3 instruction sets and other optimizations. Intel does not guarantee the availability, functionality, or effectiveness of any optimization on microprocessors not manufactured by Intel. Microprocessor-dependent optimizations in this product are intended for use with Intel microprocessors. Certain optimizations not specific to Intel microarchitecture are reserved for Intel microprocessors. Please refer to the applicable product User and Reference Guides for more information regarding the specific instruction sets covered by this notice. +Notice Revision #20110804 +Intel technologies’ features and benefits depend on system configuration and may require enabled hardware, software or service activation. Performance varies depending on system configuration. No computer system can be absolutely secure. Check with your system manufacturer or retailer. +No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document except under the terms of the OSI-approved BSD+Patent license. See LICENSE for details. +Intel disclaims all express and implied warranties, including without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement, as well as any warranty arising from course of performance, course of dealing, or usage in trade. +The products and services described may contain defects or errors known as errata which may cause deviations from published specifications. Current characterized errata are available on request. No product or component can be absolutely secure. +This document contains information on products, services and/or processes in development. All information provided here is subject to change without notice. Contact your Intel representative to obtain the latest forecast, schedule, specifications and roadmaps. +Intel, the Intel logo, Intel Core, Intel Xeon, and others are trademarks of Intel Corporation or its subsidiaries in the U.S. and/or other countries. +*Other names and brands may be claimed as the property of others. +Copyright 2019 Intel Corporation diff --git a/README.md b/README.md index a5119bcef..e8d56379b 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,40 @@ - # Scalable Video Technology for HEVC Encoder (SVT-HEVC Encoder) +[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/intel/SVT-HEVC?branch=master&svg=true)](https://ci.appveyor.com/project/intel/SVT-HEVC) +[![Travis Build Status](https://travis-ci.com/intel/SVT-HEVC.svg?branch=master)](https://travis-ci.com/intel/SVT-HEVC) +[![Coverage Status](https://coveralls.io/repos/github/intel/SVT-HEVC/badge.svg?branch=master)](https://coveralls.io/github/intel/SVT-HEVC?branch=master) The Scalable Video Technology for HEVC Encoder (SVT-HEVC Encoder) is an HEVC-compliant encoder library core that achieves excellent density-quality tradeoffs, and is highly optimized for Intel® Xeon™ Scalable Processor and Xeon™ D processors. +The whitepaper for SVT-HEVC can be found here: https://01.org/svt + This encoder has been optimized to achieve excellent performance levels using 13 density-quality presets (please refer to the user guide for more details) on a system with a dual Intel® Xeon® Scalable CPU targeting: -- Real-time encoding of up to one 8Kp60/10-bit streams on the Platinum 8180 with M11 in the subjective quality mode +- Real-time encoding of up to one 8Kp60/10-bit streams on the Platinum 8180 with M11 in the visual quality mode + +- Real-time encoding of up to two 8Kp50/10-bit streams on the Platinum 8180 with M12 in the visual quality mode -- Real-time encoding of up to two 8Kp50/10-bit streams on the Platinum 8180 with M12 in the subjective quality mode +- Real-time encoding of up to four 4Kp60/10-bit streams on the Gold 6148 with M12 in the visual quality mode -- Real-time encoding of up to four 4Kp60/10-bit streams on the Gold 6148 with M12 in the subjective quality mode +- Real-time encoding of up to six 4Kp60/10-bit streams on the Platinum 8180 with M12 in the visual quality mode -- Real-time encoding of up to six 4Kp60/10-bit streams on the Platinum 8180 with M12 in the subjective quality mode +SVT-HEVC Encoder also supports 3 modes: -SVT-HEVC Encoder also supports 2 modes: +- A visually optimized mode for visual quality (-tune 0) -- A Subjectively optimized mode (-tune 0) +- An PSNR/SSIM optimized mode for PSNR / SSIM benchmarking (-tune 1 (Default setting)) -- An Objectively optimized mode for PSNR / SSIM / VMAF benchmarking (-tune 1 (Default setting)) +- An VMAF optimized mode for VMAF benchmarking (-tune 2) The encoder can also run the ABR profile below on one Intel® Xeon-D™ D-2191: -| ABR Profile | +| ABR Profile | |---------------------------------------| -| 1 x 4kp60/10-bit Stream (@M11) | -| 1 x 1080p60/10-bit Stream (@M10) | -| 1 x 720p60/8-bit Stream (@M9) | -| 1 x 480p60/8-bit Stream (@M9) | -| 1 x 360p60/8-bit Stream (@M9) | +| 1 x 4kp60/10-bit Stream (@M11) | +| 1 x 1080p60/10-bit Stream (@M10) | +| 1 x 720p60/8-bit Stream (@M9) | +| 1 x 480p60/8-bit Stream (@M9) | +| 1 x 360p60/8-bit Stream (@M9) | # License @@ -37,7 +43,7 @@ Scalable Video Technology is licensed under the OSI-approved BSD+Patent license. # Documentation More details about the encoder usage can be found under: -- [SVT-HEVC Encoder User Guide](Docs/SVT-HEVC_Encoder_User_Guide.pdf) +- [SVT-HEVC Encoder User Guide](Docs/svt-hevc_encoder_user_guide.md) # System Requirements @@ -47,13 +53,13 @@ SVT-HEVC may run on any Windows* or Linux* 64 bit operating systems. The list be * __Windows* Operating Systems (64-bit):__ - - Windows* Server 2016 + - Windows* Server 2016 * __Linux* Operating Systems (64-bit):__ - - Ubuntu* 16.04 Server LTS + - Ubuntu* 16.04 Server LTS - - Ubuntu* 18.04 Server LTS + - Ubuntu* 18.04 Server LTS ## Hardware @@ -68,79 +74,75 @@ In order to achieve the performance targeted by the encoder, the specific CPU mo In order to run the highest resolution supported by the encoder, at least 64GB of RAM is required to run a single 8kp50/10-bit encode. The encoder application will display an error if the system does not have enough RAM to support such. The following table shows the minimum amount of RAM required for some standard resolutions of 10bit video per stream: -| Resolution | Minimum Footprint (GB)| +| Resolution | Minimum Footprint (GB)| |-----------------------|-----------------------| -| 8k | 64 | -| 4k | 16 | -| 1080p | 6 | -| 720p/1080i | 4 | -| 480p | 3 | +| 8k | 64 | +| 4k | 16 | +| 1080p | 6 | +| 720p/1080i | 4 | +| 480p | 3 | # Build and Install ## Windows* Operating Systems (64-bit): * __Build Requirements__ - - Visual Studio* 2017 (can be downloaded [here](https://www.visualstudio.com/vs/older-downloads/)) - - CMake 3.5 or later (can be downloaded [here](https://github.com/Kitware/CMake/releases/download/v3.13.0/cmake-3.13.0-win64-x64.msi)) - - YASM Assembler version 1.2.0 or later - - Download the yasm exe from the following [link](http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win64.exe) - - Rename yasm-1.3.0-win64.exe to yasm.exe - - Copy yasm.exe into a location that is in the PATH environment variable + - Visual Studio* 2017 (can be downloaded [here](https://www.visualstudio.com/vs/older-downloads/)) + - CMake 3.5 or later (can be downloaded [here](https://github.com/Kitware/CMake/releases/download/v3.13.0/cmake-3.13.0-win64-x64.msi)) + - YASM Assembler version 1.2.0 or later + - Download the yasm exe from the following [link](http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win64.exe) + - Rename yasm-1.3.0-win64.exe to yasm.exe + - Copy yasm.exe into a location that is in the PATH environment variable * __Build Instructions__ - - Generate the Visual Studio* 2017 project files by following the steps below in a windows command line prompt: - - cd Build\windows - - generate_vs17.bat - - Open "svt-hevc.sln" using Visual Studio* 2017 and build the solution + - Generate the Visual Studio* 2017 project files by following the steps below in a windows command line prompt: + - In the main repository directory go under the \Build\windows location + - Run generate_vs17.bat [such would generate the visual studio project files] + - Open the "svt-hevc.sln" using Visual Studio* 2017 and click on Build -- > Build Solution * __Binaries Location__ - - Post build, binaries can be found under Bin/Release and / or Bin/Debug + - Binaries can be found under \Bin/Release or \Bin/Debug, depending on whether Debug or Release were selected in the build mode * __Installation__ -- For the binaries to operate properly on your system, the following conditions have to be met: - - On any of the Windows* Operating Systems listed in the OS requirements section, install Visual Studio* 2017 - - Once the installation is complete, copy the binaries to a location making sure that both the sample application "SvtHevcEncApp.exe” and library "SvtHevcEnc.dll” are in the same folder. - - Open the command prompt window at the chosen location and run the sample application to encode. SvtHevcEncApp.exe -i [in.yuv] -w [width] -h [height] -b [out.265]. - - Sample application supports reading from pipe. E.g. ffmpeg -i [input.mp4] -nostdin -f rawvideo -pix_fmt yuv420p - | SvtHevcEncApp.exe -i stdin -n [number_of_frames_to_encode] -w [width] -h [height]. + For the binaries to operate properly on your system, the following conditions have to be met: + - On any of the Windows* Operating Systems listed in the OS requirements section, install Visual Studio* 2017 + - Once the installation is complete, copy the binaries to a location making sure that both the sample application "SvtHevcEncApp.exe” and library "SvtHevcEnc.dll” are in the same folder. + - Open the command prompt window at the chosen location and run the sample application to encode. SvtHevcEncApp.exe -i [in.yuv] -w [width] -h [height] -b [out.265]. + - Sample application supports reading from pipe. E.g. ffmpeg -i [input.mp4] -nostdin -f rawvideo -pix_fmt yuv420p - | SvtHevcEncApp.exe -i stdin -n [number_of_frames_to_encode] -w [width] -h [height]. ## Linux* Operating Systems (64-bit): * __Build Requirements__ - - GCC 5.4.0 or later - - CMake 3.5.1 or later - - YASM Assembler version 1.2.0 or later + - GCC 5.4.0 or later + - CMake 3.5.1 or later + - YASM Assembler version 1.2.0 or later * __Build Instructions__ - - cd Build/linux - - ./build.sh (if none specified, both release and debug will be built) + - In the main repository, run + - mkdir build && cd build && cmake .. && make -j ```nproc``` && sudo make install * __Sample Binaries location__ - - Binaries can be found under Bin/Release and / or Bin/Debug + - Binaries can be found under Bin/Release * __Installation__ For the binaries to operate properly on your system, the following conditions have to be met: - - On any of the Linux* Operating Systems listed above, copy the binaries under a location of your choice. - - Change the permissions on the sample application “SvtHevcEncApp” executable by running the command: chmod +x SvtHevcEncApp - - cd into your chosen location - - Run the sample application to encode. ./SvtHevcEncApp -i [in.yuv] -w [width] -h [height] -b [out.265]. - - Sample application supports reading from pipe. E.g. ffmpeg -i [input.mp4] -nostdin -f rawvideo -pix_fmt yuv420p - | ./SvtHevcEncApp -i stdin -n [number_of_frames_to_encode] -w [width] -h [height]. + - On any of the Linux* Operating Systems listed above, copy the binaries under a location of your choice. + - Change the permissions on the sample application “SvtHevcEncApp” executable by running the command: chmod +x SvtHevcEncApp + - cd into your chosen location + - Run the sample application to encode. ./SvtHevcEncApp -i [in.yuv] -w [width] -h [height] -b [out.265]. + - Sample application supports reading from pipe. E.g. ffmpeg -i [input.mp4] -nostdin -f rawvideo -pix_fmt yuv420p - | ./SvtHevcEncApp -i stdin -n [number_of_frames_to_encode] -w [width] -h [height]. # Demo features and limitations -- **Resolution support:** This version supports only multiple-of-8 resolutions in width for 8-bit video input and in width and height for 10-bit video input. - - **VBR BRC mode:** The VBR functionality implemented in SVT-HEVC Encoder is a demo feature to allow for an easier integration of product level BRC. The algorithm implemented would allow the encoder to generate an output bit stream matching, with a best effort, the target bitrate. The algorithm does not guarantee a certain maximum bitrate or maximum buffer size [does not follow HRD compliance]. When set to encode in VBR mode, the encoder does not produce a bit-exact output from one run to another. - **Speed Control output:** The speed control functionality implemented for SVT-HEVC Encoder is a demo feature showcasing the capability of the library to adapt to the resources available on the fly in order to generate the best possible video quality while maintaining a real-time encoding speed. When set to use the Speed Control mode, the encoder does not produce a bit-exact output from one run to another. - **Multi-instance support:** The multi-instance functionality is a demo feature implemented in the SVT-HEVC Encoder sample application as an example of one sample application using multiple encoding libraries. Encoding using the multi-instance support is limited to only 6 simultaneous streams. For example two channels encoding on Windows: SvtHevcEncApp.exe -nch 2 -c firstchannel.cfg secondchannel.cfg -- **Separate Fields:** Using the separate fields functionality migh result in a corrupted video output. - # How to Contribute -We welcome community contributions to the Scalable Video Technology. Thank you for your time! By contributing to the project, you agree to the license and copyright terms therein and to the release of your contribution under these terms. +We welcome community contributions to the SVT-HEVC Encoder. Thank you for your time! By contributing to the project, you agree to the license and copyright terms therein and to the release of your contribution under these terms. ## Contribution process @@ -153,3 +155,7 @@ We welcome community contributions to the Scalable Video Technology. Thank you f # How to Report Bugs and Provide Feedback Use the "Issues" tab on Github + +# Notices and Disclaimers + +The notices and disclaimers can be found [here](NOTICES.md) diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index 96e3797a8..d929c73ef 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -14,7 +14,7 @@ extern "C" { #endif // __cplusplus - // API Version +// API Version #define SVT_VERSION_MAJOR 1 #define SVT_VERSION_MINOR 3 #define SVT_VERSION_PATCHLEVEL 0 @@ -64,38 +64,62 @@ extern "C" { // pic flags uint32_t nFlags; + + // nalu info + uint8_t naluFound; + uint32_t naluPOC; + uint32_t naluPrefix; + uint32_t naluNalType; + uint32_t naluPayloadType; + uint8_t* naluBase64Encode; + } EB_BUFFERHEADERTYPE; typedef struct EB_COMPONENTTYPE { uint32_t nSize; - void* pComponentPrivate; - void* pApplicationPrivate; + void* pComponentPrivate; + void* pApplicationPrivate; } EB_COMPONENTTYPE; typedef enum EB_ERRORTYPE { - EB_ErrorNone = 0, - EB_ErrorInsufficientResources = (int32_t)0x80001000, - EB_ErrorUndefined = (int32_t)0x80001001, - EB_ErrorInvalidComponent = (int32_t)0x80001004, - EB_ErrorBadParameter = (int32_t)0x80001005, - EB_ErrorDestroyThreadFailed = (int32_t)0x80002012, - EB_ErrorSemaphoreUnresponsive = (int32_t)0x80002021, - EB_ErrorDestroySemaphoreFailed = (int32_t)0x80002022, - EB_ErrorCreateMutexFailed = (int32_t)0x80002030, - EB_ErrorMutexUnresponsive = (int32_t)0x80002031, - EB_ErrorDestroyMutexFailed = (int32_t)0x80002032, - EB_NoErrorEmptyQueue = (int32_t)0x80002033, - EB_ErrorMax = 0x7FFFFFFF + EB_ErrorNone = 0, + EB_ErrorInsufficientResources = (int32_t)0x80001000, + EB_ErrorUndefined = (int32_t)0x80001001, + EB_ErrorInvalidComponent = (int32_t)0x80001004, + EB_ErrorBadParameter = (int32_t)0x80001005, + EB_ErrorDestroyThreadFailed = (int32_t)0x80002012, + EB_ErrorSemaphoreUnresponsive = (int32_t)0x80002021, + EB_ErrorDestroySemaphoreFailed = (int32_t)0x80002022, + EB_ErrorCreateMutexFailed = (int32_t)0x80002030, + EB_ErrorMutexUnresponsive = (int32_t)0x80002031, + EB_ErrorDestroyMutexFailed = (int32_t)0x80002032, + EB_NoErrorEmptyQueue = (int32_t)0x80002033, + EB_ErrorMax = 0x7FFFFFFF } EB_ERRORTYPE; #define EB_BUFFERFLAG_EOS 0x00000001 -// For 8-bit and 10-bit packed inputs, the luma, cb, and cr fields should be used -// for the three input picture planes. However, for 10-bit unpacked planes the -// lumaExt, cbExt, and crExt fields should be used hold the extra 2-bits of -// precision while the luma, cb, and cr fields hold the 8-bit data. +typedef struct EB_SEI_MESSAGE +{ + uint32_t payloadSize; + unsigned char *payload; + uint32_t payloadType; + +}EB_SEI_MESSAGE; + +typedef enum EB_COLOR_FORMAT { + EB_YUV400, + EB_YUV420, + EB_YUV422, + EB_YUV444 +} EB_COLOR_FORMAT; + +/* For 8-bit and 10-bit packed inputs, the luma, cb, and cr fields should be used + * for the three input picture planes. However, for 10-bit unpacked planes the + * lumaExt, cbExt, and crExt fields should be used hold the extra 2-bits of + * precision while the luma, cb, and cr fields hold the 8-bit data. */ typedef struct EB_H265_ENC_INPUT { // Hosts 8 bit or 16 bit input YUV420p / YUV420p10le @@ -111,190 +135,566 @@ typedef struct EB_H265_ENC_INPUT uint32_t yStride; uint32_t crStride; uint32_t cbStride; + EB_SEI_MESSAGE dolbyVisionRpu; } EB_H265_ENC_INPUT; -// Will contain the EbEncApi which will live in the EncHandle class -// Only modifiable during config-time. +/* Will contain the EbEncApi which will live in the EncHandle class. + * Only modifiable during config-time. */ typedef struct EB_H265_ENC_CONFIGURATION { // Encoding preset - uint8_t encMode; // [0, 12](for tune 0 and >= 4k resolution), [0, 10](for >= 1080p resolution), [0, 9](for all resolution and modes) - uint8_t tune; // encoder tuning for Visual Quality [0], PSNR/SSIM [1] - uint8_t latencyMode; // lossless change + + /* A preset defining the quality vs density tradeoff point that the encoding + * is to be performed at. 0 is the highest quality mode, 12 is the highest + * density mode. + * + * [0, 12] for tune 0 and >= 4k resolution. + * [0, 10] for >= 1080p resolution. + * [0, 9] for all resolution and modes. + * + * Default is 9. */ + uint8_t encMode; + + /* Encoder tuning for specific aim. + * + * 0 = SQ - visually optimized mode. + * 1 = OQ - PSNR / SSIM optimized mode. + * 2 = VMAF - VMAF optimized mode. + * + * Default is 1. */ + uint8_t tune; + + /* Flag to enable for lower latency mode. The change is lossless. + * + * Default is 0. */ + uint8_t latencyMode; + // GOP Structure + + /* The intra period defines the interval of frames after which you insert an + * Intra refresh. It is strongly recommended to set the value to multiple of + * 8 minus 1 the closest to 1 second (e.g. 55, 47, 31, 23 should be used for + * 60, 50, 30, (24 or 25) respectively. + * + * -1 = no intra update. + * -2 = auto. + * + * Deault is -2. */ int32_t intraPeriodLength; + + /* Random access. + * + * 1 = CRA, open GOP. + * 2 = IDR, closed GOP. + * + * Default is 1. */ uint32_t intraRefreshType; + + /* Number of hierarchical layers used to construct GOP. + * Minigop size = 2^HierarchicalLevels. + * + * Default is 3. */ uint32_t hierarchicalLevels; + /* Prediction structure used to construct GOP. There are two main structures + * supported, which are: Low Delay (P or B) and Random Access. + * + * In Low Delay structure, pictures within a mini GOP refer to the previously + * encoded pictures in display order. In other words, pictures with display + * order N can only be referenced by pictures with display order greater than + * N, and it can only refer pictures with picture order lower than N. The Low + * Delay structure can be flat structured (e.g. IPPPPPPP) or hierarchically + * structured. B/b pictures can be used instead of P/p pictures. However, the + * reference picture list 0 and the reference picture list 1 will contain the + * same reference picture. + * + * In Random Access structure, the B/b pictures can refer to reference pictures + * from both directions (past and future). + * + * Default is 2. */ uint8_t predStructure; + + /* Decides whether to use B picture or P picture in the base layer. + * + * 0 = B Picture. + * 1 = P Picture. + * + * Default is 0. */ uint32_t baseLayerSwitchMode; + // Input Info + + /* The width of input source in units of picture luma pixels. + * + * Default is 0. */ uint32_t sourceWidth; + + /* The height of input source in units of picture luma pixels. + * + * Default is 0. */ uint32_t sourceHeight; + + /* The frequecy of images being displayed. If the number is less than 1000, + * the input frame rate is an integer number between 1 and 60, else the input + * number is in Q16 format, shifted by 16 bits, where max allowed is 240 fps. + * If FrameRateNumerator and FrameRateDenominator are both not equal to zero, + * the encoder will ignore this parameter. + * + * Default is 25. */ uint32_t frameRate; + + /* Frame rate numerator. When zero, the encoder will use fps if + * FrameRateDenominator is also zero, otherwise an error is returned. + * + * Default is 0. */ int32_t frameRateNumerator; + + /* Frame rate denominator. When zero, the encoder will use fps if + * FrameRateNumerator is also zero, otherwise an error is returned. + * + * Default is 0. */ int32_t frameRateDenominator; + + /* Specifies the bit depth of input video. + * + * 8 = 8 bit. + * 10 = 10 bit. + * + * Default is 8. */ uint32_t encoderBitDepth; + + EB_COLOR_FORMAT encoderColorFormat; + + /* Offline packing of the 2bits: requires two bits packed input. + * + * Default is 0. */ uint32_t compressedTenBitFormat; + + /* Number of frames of sequence to be encoded. If number of frames is greater + * than the number of frames in file, the encoder will loop to the beginning + * and continue the encode. + * + * 0 = encodes the full clip. + * + * Default is 0. */ uint64_t framesToBeEncoded; + // Visual quality optimizations only applicable when tune = 1 + + /* Enables subjective quality algorithms to reduce the output bitrate with + * minimal or no subjective visual quality impact. Only applicable to tune 0. + * + * Default is 1. */ uint8_t bitRateReduction; + + /* The visual quality knob that allows the use of adaptive quantization + * within the picture and enables visual quality algorithms that improve the + * sharpness of the background. Only available for 4k and 8k resolutions and + * tune 0. + * + * Default is 1. */ uint8_t improveSharpness; + // Interlaced Video + + /* Supplemental enhancement information messages for interlaced video. + * + * 0 = progressive signal. + * 1 = interlaced signal. + * + * Default is 0. */ uint8_t interlacedVideo; + // Quantization + + /* Initial quantization parameter for the Intra pictures used under constant + * qp rate control mode. + * + * Default is 25. */ uint32_t qp; + + /* Path to file that contains qp values. + * + * Default is null.*/ uint8_t useQpFile; + // Deblock Filter + + /* Flag to disable the Deblocking Loop Filtering. + * + * Default is 0. */ uint8_t disableDlfFlag; + // SAO + + /* Flag to enable the use of Sample Adaptive Offset Filtering. + * + * Default is 1. */ uint8_t enableSaoFlag; + // Motion Estimation Tools + + /* Flag to enable the use of default ME HME parameters. + * + * Default is 1. */ uint8_t useDefaultMeHme; + + /* Flag to enable HME. + * + * Default is 1. */ uint8_t enableHmeFlag; + // ME Parameters + + /* Number of search positions in the horizontal direction. + * + * Default depends on input resolution. */ uint32_t searchAreaWidth; + + /* Number of search positions in the vertical direction. + * + * Default depends on input resolution. */ uint32_t searchAreaHeight; + // MD Parameters + + /* Enable the use of Constrained Intra, which yields sending two picture + * parameter sets in the elementary streams . + * + * Default is 0. */ uint8_t constrainedIntra; + // Rate Control + + /* Rate control mode. + * + * 0 = Constant QP. + * 1 = Variable BitRate. + * + * Default is 0. */ uint32_t rateControlMode; + + /* Flag to enable the scene change detection algorithm. + * + * Default is 1. */ uint32_t sceneChangeDetection; + + /* When RateControlMode is set to 1 it's best to set this parameter to be + * equal to the Intra period value (such is the default set by the encoder). + * When CQP is chosen, then a (2 * minigopsize +1) look ahead is recommended. + * + * Default depends on rate control mode.*/ uint32_t lookAheadDistance; + + /* Target bitrate in bits/second, only apllicable when rate control mode is + * set to 1. + * + * Default is 7000000. */ uint32_t targetBitRate; + + /* Maxium QP value allowed for rate control use, only apllicable when rate + * control mode is set to 1. It has to be greater or equal to minQpAllowed. + * + * Default is 48. */ uint32_t maxQpAllowed; + + /* Minimum QP value allowed for rate control use, only apllicable when rate + * control mode is set to 1. It has to be smaller or equal to maxQpAllowed. + * + * Default is 10. */ uint32_t minQpAllowed; + // bitstream options + + /* Flag to code VPS / SPS / PPS. + * + * Default is 1. */ uint8_t codeVpsSpsPps; + + /* Flag to code end of squence Network Abstraction Layer. + * + * Default is 1. */ uint8_t codeEosNal; + + /* Flag to enable sending extra information to enhance the use of video for + * display purposes. + * + * Default is 0. */ uint32_t videoUsabilityInfo; + + /* Flag to signal that the input yuv is HDR BT2020 using SMPTE ST2048, requires + * VideoUsabilityInfo to be set to 1. Only applicable for 10bit input. + * + * Default is 0. */ uint32_t highDynamicRangeInput; + + /* Flag to simplify the detection of boundary between access units. + * + * Default is 0. */ uint32_t accessUnitDelimiter; + + /* Flag to enable buffering period supplemental enhancement information. + * + * Default is 0. */ uint32_t bufferingPeriodSEI; + + /* Flag to enable picture timeing supplemental enhancement information. + * + * Default is 0. */ uint32_t pictureTimingSEI; + + /* Flag to enable registered user data supplemental enhancement information. + * + * Default is 0. */ uint32_t registeredUserDataSeiFlag; + + /* Flag to enable unregistered user data supplemental enhancement information. + * + * Default is 0. */ uint32_t unregisteredUserDataSeiFlag; + + /* Flag to enable recovery point supplemental enhancement information. + * + * Default is 0. */ uint32_t recoveryPointSeiFlag; + + /* Flag to insert temporal ID in Network Abstraction Layer units. + * + * Default is 1. */ uint32_t enableTemporalId; + + /* Defined set of coding tools to create bitstream. + * + * 1 = Main, allows bit depth of 8. + * 2 = Main 10, allows bit depth of 8 to 10. + * + * Default is 2. */ uint32_t profile; + + /* Constraints for bitstream in terms of max bitrate and max buffer size. + * + * 0 = Main, for most applications. + * 1 = High, for demanding applications. + * + * Default is 0. */ uint32_t tier; + + /* Constraints for bitstream in terms of max bitrate and max buffer size. + * + * 0 = auto determination. + * + * Default is 0. */ uint32_t level; + + /* Flag to enable VPS timing info. + * + * Default is 0. */ uint8_t fpsInVps; - // Application Specific parameters - uint32_t channelId; // when multiple instances are running within the same application - uint32_t activeChannelCount; // how many channels are active - // Threads management - uint32_t logicalProcessors; // number of logical processor to run on - int32_t targetSocket; // target socket to run on - uint8_t switchThreadsToRtPriority; // switch to real time mode + // VBV Parameters + uint32_t vbvMaxrate; uint32_t vbvBufsize; uint32_t hrdFlag; uint64_t vbvBufInit; - // ASM Type - uint32_t asmType; // level of optimization to use. + /* ID assigned to each channel when multiple instances are running within the + * same application. */ + uint32_t channelId; + /* Active channel count. */ + uint32_t activeChannelCount; + + + // Threads management + + /* The number of logical processor which encoder threads run on. If + * LogicalProcessorNumber and TargetSocket are not set, threads are managed by + * OS thread scheduler. */ + uint32_t logicalProcessors; + + /* Target socket to run on. For dual socket systems, this can specify which + * socket the encoder runs on. + * + * -1 = Both Sockets. + * 0 = Socket 0. + * 1 = Socket 1. + * + * Default is -1. */ + int32_t targetSocket; + + /* Flag to enable threads to real time priority. Running with sudo privilege + * utilizes full resource. Only applicable to Linux. + * + * Default is 1. */ + uint8_t switchThreadsToRtPriority; + + + // ASM Type + + /* Assembly instruction set used by encoder. + * + * 0 = non-AVX2, C only. + * 1 = up to AVX512, auto-select highest assembly insturction set supported. + * + * Default is 1. */ + uint32_t asmType; + + // Demo features - uint32_t speedControlFlag; // dynamically change the encoding preset to meet the average speed defined in injectorFrameRate + + /* Flag to enable the Speed Control functionality to achieve the real-time + * encoding speed defined by dynamically changing the encoding preset to meet + * the average speed defined in injectorFrameRate. When this parameter is set + * to 1 it forces inj to be 1 -inj-frm-rt to be set to the fps. + * + * Default is 0. */ + uint32_t speedControlFlag; + + /* Frame Rate used for the injector. Recommended to match the encoder speed. + * + * Default is 60. */ int32_t injectorFrameRate; + // Debug tools + + /* Output reconstructed yuv used for debug purposes. The value is set through + * ReconFile token (-o) and using the feature will affect the speed of encoder. + * + * Default is 0. */ uint32_t reconEnabled; + // SEI + uint16_t maxCLL; + uint16_t maxFALL; + + uint8_t useMasteringDisplayColorVolume; + uint8_t useNaluFile; + uint32_t dolbyVisionProfile; + + // Master Display Color Volume Parameters + uint16_t displayPrimaryX[3]; + uint16_t displayPrimaryY[3]; + uint16_t whitePointX, whitePointY; + uint32_t maxDisplayMasteringLuminance; + uint32_t minDisplayMasteringLuminance; + } EB_H265_ENC_CONFIGURATION; + // API calls: -/*****************************************/ -/******* STEP 1: Init the Handle *********/ -/*****************************************/ +/* STEP 1: Call the library to construct a Component Handle. + * + * Parameter: + * @ **pHandle Handle to be called in the future for manipulating the + * component. + * @ *pAppData Callback data. + * @ *configPtr Pointer passed back to the client during callbacks, it will be + * loaded with default params from the library. */ EB_API EB_ERRORTYPE EbInitHandle( - EB_COMPONENTTYPE** pHandle, - void* pAppData, - EB_H265_ENC_CONFIGURATION *configPtr); // configPtr will be loaded with default params from the library - -/***************************************************/ -/******* STEP 2: Update the encoder params *********/ -/***************************************************/ + EB_COMPONENTTYPE **pHandle, + void *pAppData, + EB_H265_ENC_CONFIGURATION *configPtr); + +/* STEP 2: Set all configuration parameters. + * + * Parameter: + * @ *h265EncComponent Encoder handler. + * @ *pComponentParameterStructure Encoder and buffer configurations will be copied to the library. */ EB_API EB_ERRORTYPE EbH265EncSetParameter( EB_COMPONENTTYPE *h265EncComponent, - EB_H265_ENC_CONFIGURATION *pComponentParameterStructure); // pComponentParameterStructure contents will be copied to the library + EB_H265_ENC_CONFIGURATION *pComponentParameterStructure); -/***************************************************/ -/******* STEP 3: Init the encoder libray ***********/ -/***************************************************/ +/* STEP 3: Initialize encoder and allocates memory to necessary buffers. + * + * Parameter: + * @ *h265EncComponent Encoder handler. */ EB_API EB_ERRORTYPE EbInitEncoder( - EB_COMPONENTTYPE *h265EncComponent); + EB_COMPONENTTYPE *h265EncComponent); -/***************************************************/ -/****** OPTIONAL: Get the stream header NAL ********/ -/***************************************************/ +/* OPTIONAL: Get VPS / SPS / PPS headers at init time. + * + * Parameter: + * @ *h265EncComponent Encoder handler. + * @ **outputStreamPtr Output stream. */ EB_API EB_ERRORTYPE EbH265EncStreamHeader( EB_COMPONENTTYPE *h265EncComponent, EB_BUFFERHEADERTYPE **outputStreamPtr); -/***************************************************/ -/******** OPTIONAL: Get the stream EOS NAL *********/ -/***************************************************/ +/* OPTIONAL: Get the end of sequence Network Abstraction Layer. + * + * Parameter: + * @ *h265EncComponent Encoder handler. + * @ **outputStreamPtr Output stream. */ EB_API EB_ERRORTYPE EbH265EncEosNal( EB_COMPONENTTYPE *h265EncComponent, EB_BUFFERHEADERTYPE **outputStreamPtr); -/***************************************************/ -/***** STEP 4: Send input pictures to encode *******/ -/***************************************************/ +/* STEP 4: Send the picture. + * + * Parameter: + * @ *h265EncComponent Encoder handler. + * @ *pBuffer Header pointer, picture buffer. */ EB_API EB_ERRORTYPE EbH265EncSendPicture( - EB_COMPONENTTYPE *h265EncComponent, - EB_BUFFERHEADERTYPE *pBuffer); - -/***************************************************/ -/****** STEP 5: Get output slices to encode ********/ -/***************************************************/ + EB_COMPONENTTYPE *h265EncComponent, + EB_BUFFERHEADERTYPE *pBuffer); + +/* STEP 5: Receive packet. + * + * Parameter: + * @ *h265EncComponent Encoder handler. + * @ **pBuffer Header pointer to return packet with. + * @ picSendDone Flag to decide whether to call non blocking function. */ EB_API EB_ERRORTYPE EbH265GetPacket( - EB_COMPONENTTYPE *h265EncComponent, - EB_BUFFERHEADERTYPE **pBuffer, - uint8_t picSendDone); + EB_COMPONENTTYPE *h265EncComponent, + EB_BUFFERHEADERTYPE **pBuffer, + uint8_t picSendDone); -/***************************************************/ -/******* STEP 5-1: Release output buffer ***********/ -/***************************************************/ +/* STEP 5-1: Release output buffer back into the pool. + * + * Parameter: + * @ **pBuffer Header pointer that contains the output packet. */ EB_API void EbH265ReleaseOutBuffer( - EB_BUFFERHEADERTYPE **pBuffer); + EB_BUFFERHEADERTYPE **pBuffer); -/***************************************************/ -/*** OPTIONAL: Get output reconstructed picture ****/ -/***************************************************/ +/* OPTIONAL: Fill buffer with reconstructed picture. + * + * Parameter: + * @ *h265EncComponent Encoder handler. + * @ *pBuffer Output buffer. */ EB_API EB_ERRORTYPE EbH265GetRecon( - EB_COMPONENTTYPE *h265EncComponent, - EB_BUFFERHEADERTYPE *pBuffer); + EB_COMPONENTTYPE *h265EncComponent, + EB_BUFFERHEADERTYPE *pBuffer); -/***************************************************/ -/******* STEP 6: De-Init the encoder libray ********/ -/***************************************************/ +/* STEP 6: Deinitialize encoder library. + * + * Parameter: + * @ *h265EncComponent Encoder handler. */ EB_API EB_ERRORTYPE EbDeinitEncoder( - EB_COMPONENTTYPE *h265EncComponent); + EB_COMPONENTTYPE *h265EncComponent); -/***************************************************/ -/******* STEP 7: De-Init the encoder libray ********/ -/***************************************************/ +/* STEP 7: Deconstruct encoder handler. + * + * Parameter: + * @ *h265EncComponent Encoder handler. */ EB_API EB_ERRORTYPE EbDeinitHandle( - EB_COMPONENTTYPE *h265EncComponent); + EB_COMPONENTTYPE *h265EncComponent); #ifdef __cplusplus } diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index 7fdcce440..30de03bcb 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "EbAppConfig.h" #include "EbApi.h" @@ -40,6 +41,7 @@ #define FRAME_RATE_NUMERATOR_TOKEN "-fps-num" #define FRAME_RATE_DENOMINATOR_TOKEN "-fps-denom" #define ENCODER_BIT_DEPTH "-bit-depth" +#define ENCODER_COLOR_FORMAT "-color-format" #define INPUT_COMPRESSED_TEN_BIT_FORMAT "-compressed-ten-bit-format" #define ENCMODE_TOKEN "-encMode" #define HIERARCHICAL_LEVELS_TOKEN "-hierarchical-levels" // no Eval @@ -48,7 +50,7 @@ #define PROFILE_TOKEN "-profile" #define TIER_TOKEN "-tier" #define LEVEL_TOKEN "-level" -#define LATENCY_MODE "-latency-mode" // no Eval +//#define LATENCY_MODE "-latency-mode" // no Eval #define INTERLACED_VIDEO_TOKEN "-interlaced-video" #define SEPERATE_FILDS_TOKEN "-separate-fields" #define INTRA_REFRESH_TYPE_TOKEN "-irefresh-type" // no Eval @@ -69,6 +71,13 @@ #define REG_USER_DATA_TOKEN "-reg-user-data" // no Eval #define UNREG_USER_DATA_TOKEN "-unreg-user-data" // no Eval #define RECOVERY_POINT_TOKEN "-recovery-point" // no Eval +#define MAXCLL_TOKEN "-max-cll" +#define MAXFALL_TOKEN "-max-fall" +#define USE_MASTER_DISPLAY_TOKEN "-use-master-display" +#define MASTER_DISPLAY_TOKEN "-master-display" +#define DOLBY_VISION_PROFILE_TOKEN "-dolby-vision-profile" +#define DOLBY_VISION_RPU_FILE_TOKEN "-dolby-vision-rpu" +#define NALU_FILE_TOKEN "-nalu-file" #define RATE_CONTROL_ENABLE_TOKEN "-rc" #define TARGET_BIT_RATE_TOKEN "-tbr" #define VBV_MAX_RATE_TOKEN "-vbv-maxrate" @@ -138,24 +147,33 @@ static void SetCfgQpFile (const char *value, EbConfig_t * if (cfg->qpFile) { fclose(cfg->qpFile); } FOPEN(cfg->qpFile,value, "r"); }; +static void SetCfgDolbyVisionRpuFile (const char *value, EbConfig_t *cfg) +{ + if (cfg->dolbyVisionRpuFile) { fclose(cfg->dolbyVisionRpuFile); } + FOPEN(cfg->dolbyVisionRpuFile, value, "rb"); +}; +static void SetNaluFile(const char *value, EbConfig_t *cfg) +{ + if (cfg->naluFile) { fclose(cfg->naluFile); } + FOPEN(cfg->naluFile, value, "rb"); + cfg->useNaluFile = EB_TRUE; +}; static void SetCfgSourceWidth (const char *value, EbConfig_t *cfg) {cfg->sourceWidth = strtoul(value, NULL, 0);}; static void SetInterlacedVideo (const char *value, EbConfig_t *cfg) {cfg->interlacedVideo = (EB_BOOL) strtoul(value, NULL, 0);}; static void SetSeperateFields (const char *value, EbConfig_t *cfg) {cfg->separateFields = (EB_BOOL) strtoul(value, NULL, 0);}; static void SetCfgSourceHeight (const char *value, EbConfig_t *cfg) {cfg->sourceHeight = strtoul(value, NULL, 0) >> cfg->separateFields;}; -static void SetCfgFramesToBeEncoded (const char *value, EbConfig_t *cfg) {cfg->framesToBeEncoded = strtol(value, NULL, 0) << cfg->separateFields;}; +static void SetCfgFramesToBeEncoded (const char *value, EbConfig_t *cfg) {cfg->framesToBeEncoded = strtoll(value, NULL, 0) << cfg->separateFields;}; static void SetBufferedInput (const char *value, EbConfig_t *cfg) {cfg->bufferedInput = (strtol(value, NULL, 0) != -1 && cfg->separateFields) ? strtol(value, NULL, 0) << cfg->separateFields : strtol(value, NULL, 0);}; static void SetFrameRate (const char *value, EbConfig_t *cfg) { cfg->frameRate = strtoul(value, NULL, 0); - if (cfg->frameRate > 1000 ){ - cfg->frameRate = cfg->frameRate; - } - else{ + if (cfg->frameRate <= 1000 ){ cfg->frameRate = cfg->frameRate << 16; } } static void SetFrameRateNumerator (const char *value, EbConfig_t *cfg) {cfg->frameRateNumerator = strtoul(value, NULL, 0);}; static void SetFrameRateDenominator (const char *value, EbConfig_t *cfg) {cfg->frameRateDenominator = strtoul(value, NULL, 0);}; static void SetEncoderBitDepth (const char *value, EbConfig_t *cfg) {cfg->encoderBitDepth = strtoul(value, NULL, 0);} +static void SetEncoderColorFormat (const char *value, EbConfig_t *cfg) {cfg->encoderColorFormat = strtoul(value, NULL, 0);} static void SetcompressedTenBitFormat (const char *value, EbConfig_t *cfg) {cfg->compressedTenBitFormat = strtoul(value, NULL, 0);} static void SetBaseLayerSwitchMode (const char *value, EbConfig_t *cfg) {cfg->baseLayerSwitchMode = (EB_BOOL) strtoul(value, NULL, 0);}; static void SetencMode (const char *value, EbConfig_t *cfg) {cfg->encMode = (uint8_t)strtoul(value, NULL, 0);}; @@ -193,6 +211,17 @@ static void SetPictureTimingSEI (const char *value, EbConfig_t * static void SetRegisteredUserDataSEI (const char *value, EbConfig_t *cfg) {cfg->registeredUserDataSeiFlag = (EB_BOOL)strtol(value, NULL, 0);}; static void SetUnRegisteredUserDataSEI (const char *value, EbConfig_t *cfg) {cfg->unregisteredUserDataSeiFlag = (EB_BOOL)strtol(value, NULL, 0);}; static void SetRecoveryPointSEI (const char *value, EbConfig_t *cfg) {cfg->recoveryPointSeiFlag = (EB_BOOL)strtol(value, NULL, 0);}; +static void SetMaxCLL (const char *value, EbConfig_t *cfg) {cfg->maxCLL = (uint16_t)strtoul(value, NULL, 0);}; +static void SetMaxFALL (const char *value, EbConfig_t *cfg) {cfg->maxFALL = (uint16_t)strtoul(value, NULL, 0);}; +static void SetMasterDisplayFlag (const char *value, EbConfig_t *cfg) {cfg->useMasteringDisplayColorVolume = (EB_BOOL)strtol(value, NULL, 0);}; +static void SetMasterDisplay (const char *value, EbConfig_t *cfg) { + if (cfg->useMasteringDisplayColorVolume) + EB_STRCPY(cfg->masteringDisplayColorVolumeString, EB_STRLEN(value, MAX_STRING_LENGTH) + 1, value); +}; +static void SetDolbyVisionProfile (const char *value, EbConfig_t *cfg) { + if (strtoul(value, NULL, 0) != 0 || EB_STRCMP(value, "0") == 0) + cfg->dolbyVisionProfile = (uint32_t)(10 * strtod(value, NULL)); +}; static void SetEnableTemporalId (const char *value, EbConfig_t *cfg) {cfg->enableTemporalId = strtol(value, NULL, 0);}; static void SetProfile (const char *value, EbConfig_t *cfg) {cfg->profile = strtol(value, NULL, 0);}; static void SetTier (const char *value, EbConfig_t *cfg) {cfg->tier = strtol(value, NULL, 0);}; @@ -206,14 +235,11 @@ static void SetInjector (const char *value, EbConfig_t * static void SpeedControlFlag (const char *value, EbConfig_t *cfg) {cfg->speedControlFlag = strtol(value, NULL, 0); }; static void SetInjectorFrameRate (const char *value, EbConfig_t *cfg) { cfg->injectorFrameRate = strtoul(value, NULL, 0); - if (cfg->injectorFrameRate > 1000 ){ - cfg->injectorFrameRate = cfg->injectorFrameRate; - } - else{ + if (cfg->injectorFrameRate <= 1000 ){ cfg->injectorFrameRate = cfg->injectorFrameRate << 16; } } -static void SetLatencyMode (const char *value, EbConfig_t *cfg) {cfg->latencyMode = (uint8_t)strtol(value, NULL, 0);}; +//static void SetLatencyMode (const char *value, EbConfig_t *cfg) {cfg->latencyMode = (uint8_t)strtol(value, NULL, 0);}; static void SetAsmType (const char *value, EbConfig_t *cfg) {cfg->asmType = (uint32_t)strtoul(value, NULL, 0); }; static void SetLogicalProcessors (const char *value, EbConfig_t *cfg) {cfg->logicalProcessors = (uint32_t)strtoul(value, NULL, 0);}; static void SetTargetSocket (const char *value, EbConfig_t *cfg) {cfg->targetSocket = (int32_t)strtol(value, NULL, 0);}; @@ -266,6 +292,7 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, FRAME_RATE_NUMERATOR_TOKEN, "FrameRateNumerator", SetFrameRateNumerator }, { SINGLE_INPUT, FRAME_RATE_DENOMINATOR_TOKEN, "FrameRateDenominator", SetFrameRateDenominator }, { SINGLE_INPUT, ENCODER_BIT_DEPTH, "EncoderBitDepth", SetEncoderBitDepth }, + { SINGLE_INPUT, ENCODER_COLOR_FORMAT, "EncoderColorFormat", SetEncoderColorFormat}, { SINGLE_INPUT, INPUT_COMPRESSED_TEN_BIT_FORMAT, "CompressedTenBitFormat", SetcompressedTenBitFormat }, { SINGLE_INPUT, HIERARCHICAL_LEVELS_TOKEN, "HierarchicalLevels", SetHierarchicalLevels }, @@ -325,6 +352,13 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, REG_USER_DATA_TOKEN, "RegisteredUserData", SetRegisteredUserDataSEI }, { SINGLE_INPUT, UNREG_USER_DATA_TOKEN, "UnregisteredUserData", SetUnRegisteredUserDataSEI }, { SINGLE_INPUT, RECOVERY_POINT_TOKEN, "RecoveryPoint", SetRecoveryPointSEI }, + { SINGLE_INPUT, MAXCLL_TOKEN, "MaxCLL", SetMaxCLL }, + { SINGLE_INPUT, MAXFALL_TOKEN, "MaxFALL", SetMaxFALL }, + { SINGLE_INPUT, USE_MASTER_DISPLAY_TOKEN, "UseMasterDisplay", SetMasterDisplayFlag }, + { SINGLE_INPUT, MASTER_DISPLAY_TOKEN, "MasterDisplay", SetMasterDisplay }, + { SINGLE_INPUT, DOLBY_VISION_PROFILE_TOKEN, "DolbyVisionProfile", SetDolbyVisionProfile }, + { SINGLE_INPUT, DOLBY_VISION_RPU_FILE_TOKEN, "DolbyVisionRpuFile", SetCfgDolbyVisionRpuFile }, + { SINGLE_INPUT, NALU_FILE_TOKEN, "NaluFile", SetNaluFile }, { SINGLE_INPUT, TEMPORAL_ID, "TemporalId", SetEnableTemporalId }, { SINGLE_INPUT, FPSINVPS_TOKEN, "FPSInVPS", SetFpsInVps }, // Latency @@ -337,7 +371,7 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, PROFILE_TOKEN, "Profile", SetProfile }, { SINGLE_INPUT, TIER_TOKEN, "Tier", SetTier }, { SINGLE_INPUT, LEVEL_TOKEN, "Level", SetLevel }, - { SINGLE_INPUT, LATENCY_MODE, "LatencyMode", SetLatencyMode }, +// { SINGLE_INPUT, LATENCY_MODE, "LatencyMode", SetLatencyMode }, // Asm Type { SINGLE_INPUT, ASM_TYPE_TOKEN, "AsmType", SetAsmType }, @@ -363,6 +397,7 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->frameRateNumerator = 0; configPtr->frameRateDenominator = 0; configPtr->encoderBitDepth = 8; + configPtr->encoderColorFormat = EB_YUV420; configPtr->compressedTenBitFormat = 0; configPtr->sourceWidth = 0; configPtr->sourceHeight = 0; @@ -417,6 +452,25 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->recoveryPointSeiFlag = EB_FALSE; configPtr->enableTemporalId = 1; + // SEI + configPtr->maxCLL = 0; + configPtr->maxFALL = 0; + configPtr->useMasteringDisplayColorVolume = EB_FALSE; + configPtr->useNaluFile = EB_FALSE; + configPtr->dolbyVisionProfile = 0; + configPtr->dolbyVisionRpuFile = NULL; + configPtr->naluFile = NULL; + configPtr->displayPrimaryX[0] = 0; + configPtr->displayPrimaryX[1] = 0; + configPtr->displayPrimaryX[2] = 0; + configPtr->displayPrimaryY[0] = 0; + configPtr->displayPrimaryY[1] = 0; + configPtr->displayPrimaryY[2] = 0; + configPtr->whitePointX = 0; + configPtr->whitePointY = 0; + configPtr->maxDisplayMasteringLuminance = 0; + configPtr->minDisplayMasteringLuminance = 0; + configPtr->switchThreadsToRtPriority = EB_TRUE; configPtr->fpsInVps = EB_FALSE; @@ -436,6 +490,14 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->eosFlag = EB_FALSE; // Computational Performance Parameters + configPtr->performanceContext.libStartTime[0] = 0; + configPtr->performanceContext.libStartTime[1] = 0; + + configPtr->performanceContext.encodeStartTime[0] = 0; + configPtr->performanceContext.encodeStartTime[1] = 0; + + configPtr->performanceContext.totalExecutionTime = 0; + configPtr->performanceContext.totalEncodeTime = 0; configPtr->performanceContext.frameCount = 0; configPtr->performanceContext.averageSpeed = 0; configPtr->performanceContext.startsTime = 0; @@ -494,6 +556,16 @@ void EbConfigDtor(EbConfig_t *configPtr) configPtr->qpFile = (FILE *)NULL; } + if (configPtr->naluFile) { + fclose(configPtr->naluFile); + configPtr->naluFile = (FILE *)NULL; + } + + if (configPtr->dolbyVisionRpuFile) { + fclose(configPtr->dolbyVisionRpuFile); + configPtr->dolbyVisionRpuFile = (FILE *)NULL; + } + return; } @@ -723,6 +795,11 @@ static EB_ERRORTYPE VerifySettings(EbConfig_t *config, uint32_t channelNumber) return_error = EB_ErrorBadParameter; } + if (config->framesToBeEncoded >= LLONG_MAX) { + fprintf(config->errorLogFile, "SVT [Error]: Instance %u: FrameToBeEncoded must be less than 2^64 - 1\n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } + if (config->bufferedInput < -1) { fprintf(config->errorLogFile, "SVT [Error]: Instance %u: Invalid BufferedInput. BufferedInput must greater or equal to -1\n", channelNumber + 1); return_error = EB_ErrorBadParameter; @@ -770,6 +847,11 @@ static EB_ERRORTYPE VerifySettings(EbConfig_t *config, uint32_t channelNumber) return_error = EB_ErrorBadParameter; } + if (config->useNaluFile == 1 && config->naluFile == NULL) { + fprintf(config->errorLogFile, "SVT [Error]: Instance %u : Invalid Nalu File\n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } + return return_error; } @@ -884,7 +966,6 @@ EB_BOOL is_negative_number( return EB_TRUE; } -#define SIZE_OF_ONE_FRAME_IN_BYTES(width, height,is16bit) ( ( ((width)*(height)*3)>>1 )<inputFile); } - frameSize = SIZE_OF_ONE_FRAME_IN_BYTES(config->inputPaddedWidth, config->inputPaddedHeight, (uint8_t)((config->encoderBitDepth == 10) ? 1 : 0)); + frameSize = config->inputPaddedWidth * config->inputPaddedHeight; // Luma + frameSize += 2 * (frameSize >> (3 - config->encoderColorFormat)); // Add Chroma + frameSize = frameSize << ((config->encoderBitDepth == 10) ? 1 : 0); if (frameSize == 0) return -1; @@ -914,6 +997,39 @@ int32_t ComputeFramesToBeEncoded( } +static EB_ERRORTYPE ParseMasteringDisplayColorVolumeSEI( + EbConfig_t* config) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + uint8_t *context = NULL, *token = NULL; + const uint8_t *delimeter = (uint8_t*) "(,)GBRWPL"; + uint32_t primaries[10]; + uint32_t i = 0, j = 0, k = 0; + + if (strstr(config->masteringDisplayColorVolumeString, "G") != NULL && strstr(config->masteringDisplayColorVolumeString, "B") != NULL && strstr(config->masteringDisplayColorVolumeString, "R") != NULL && strstr(config->masteringDisplayColorVolumeString, "WP") != NULL && strstr(config->masteringDisplayColorVolumeString, "L") != NULL) { + for (token = (uint8_t*)EB_STRTOK(config->masteringDisplayColorVolumeString, delimeter, &context); token != NULL; token = (uint8_t*)EB_STRTOK(NULL, delimeter, &context)) { + primaries[i++] = (uint32_t)strtoul((char*)token, NULL, 0); + } + if (i == 10 && token == NULL) { + for (j = 0; j < 3; j++) { + config->displayPrimaryX[j] = primaries[k++]; + config->displayPrimaryY[j] = primaries[k++]; + } + config->whitePointX = primaries[k++]; + config->whitePointY = primaries[k++]; + config->maxDisplayMasteringLuminance = primaries[k++]; + config->minDisplayMasteringLuminance = primaries[k++]; + } + else { + return_error = EB_ErrorBadParameter; + } + } + else { + return_error = EB_ErrorBadParameter; + } + return return_error; +} + /****************************************** * Read Command Line ******************************************/ @@ -1027,6 +1143,12 @@ EB_ERRORTYPE ReadCommandLine( configs[index]->injector = 1; } + // Parse Master Display Color + if (return_errors[index] == EB_ErrorNone && configs[index]->useMasteringDisplayColorVolume == EB_TRUE) { + return_errors[index] = ParseMasteringDisplayColorVolumeSEI(configs[index]); + if (return_errors[index] != EB_ErrorNone) + fprintf(configs[index]->errorLogFile, "SVT [Error]: Instance %u: Couldn't parse MasterDisplay info. Make sure its passed in this format \"G(%%hu,%%hu)B(%%hu,%%hu)R(%%hu,%%hu)WP(%%hu,%%hu)L(%%u,%%u)\"", index + 1); + } } return_error = (EB_ERRORTYPE)(return_error & return_errors[index]); } @@ -1048,4 +1170,4 @@ EB_ERRORTYPE ReadCommandLine( } return return_error; -} \ No newline at end of file +} diff --git a/Source/App/EbAppConfig.h b/Source/App/EbAppConfig.h index 29870e9b0..e2b41583d 100644 --- a/Source/App/EbAppConfig.h +++ b/Source/App/EbAppConfig.h @@ -174,12 +174,20 @@ extern rsize_t strnlen_ss(const char *s, rsize_t smax); #define MAX_CHANNEL_NUMBER 6 #define MAX_NUM_TOKENS 200 +#define MAX_STRING_LENGTH 1024 + #ifdef _MSC_VER #define FOPEN(f,s,m) fopen_s(&f,s,m) #else #define FOPEN(f,s,m) f=fopen(s,m) #endif +#ifdef _MSC_VER +#define EB_STRTOK(str,delim,next) strtok_s((char*)str,(const char*)delim,(char**)next) +#else +#define EB_STRTOK(str,delim,next) strtok_r((char*)str,(const char*)delim,(char**)next) +#endif + /**************************************** * Padding ****************************************/ @@ -195,6 +203,12 @@ typedef struct EbPerformanceContext_s { /**************************************** * Computational Performance Data ****************************************/ + uint64_t libStartTime[2]; // [sec, micro_sec] including init time + uint64_t encodeStartTime[2]; // [sec, micro_sec] first frame sent + + double totalExecutionTime; // includes init + double totalEncodeTime; // not including init + uint64_t totalLatency; uint32_t maxLatency; @@ -232,6 +246,7 @@ typedef struct EbConfig_s uint32_t injector; uint32_t speedControlFlag; uint32_t encoderBitDepth; + uint32_t encoderColorFormat; uint32_t compressedTenBitFormat; uint32_t sourceWidth; uint32_t sourceHeight; @@ -367,6 +382,25 @@ typedef struct EbConfig_s uint64_t processedFrameCount; uint64_t processedByteCount; + /**************************************** + * SEI parameters + ****************************************/ + uint16_t maxCLL; + uint16_t maxFALL; + EB_BOOL useMasteringDisplayColorVolume; + char masteringDisplayColorVolumeString[MAX_STRING_LENGTH]; + uint32_t dolbyVisionProfile; + FILE* dolbyVisionRpuFile; + EB_BOOL useNaluFile; + FILE* naluFile; + + // Master Display Color Volume Parameters + uint16_t displayPrimaryX[3]; + uint16_t displayPrimaryY[3]; + uint16_t whitePointX, whitePointY; + uint32_t maxDisplayMasteringLuminance; + uint32_t minDisplayMasteringLuminance; + } EbConfig_t; extern void EbConfigCtor(EbConfig_t *configPtr); diff --git a/Source/App/EbAppContext.c b/Source/App/EbAppContext.c index 54ec66686..f0e2aded4 100644 --- a/Source/App/EbAppContext.c +++ b/Source/App/EbAppContext.c @@ -209,19 +209,42 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.recoveryPointSeiFlag = config->recoveryPointSeiFlag; callbackData->ebEncParameters.enableTemporalId = config->enableTemporalId; callbackData->ebEncParameters.encoderBitDepth = config->encoderBitDepth; + callbackData->ebEncParameters.encoderColorFormat = (EB_COLOR_FORMAT)config->encoderColorFormat; callbackData->ebEncParameters.compressedTenBitFormat = config->compressedTenBitFormat; callbackData->ebEncParameters.profile = config->profile; + if(config->encoderColorFormat >= EB_YUV422 && config->profile != 4) + { + printf("\nWarning: input profile is not correct, force converting it from %d to MainREXT for YUV422 or YUV444 cases \n", config->profile); + callbackData->ebEncParameters.profile = 4; + } callbackData->ebEncParameters.tier = config->tier; callbackData->ebEncParameters.level = config->level; callbackData->ebEncParameters.injectorFrameRate = config->injectorFrameRate; callbackData->ebEncParameters.speedControlFlag = config->speedControlFlag; - callbackData->ebEncParameters.latencyMode = config->latencyMode; + //callbackData->ebEncParameters.latencyMode = config->latencyMode; callbackData->ebEncParameters.asmType = config->asmType; callbackData->ebEncParameters.reconEnabled = config->reconFile ? EB_TRUE : EB_FALSE; callbackData->ebEncParameters.codeVpsSpsPps = 1; callbackData->ebEncParameters.fpsInVps = config->fpsInVps; callbackData->ebEncParameters.switchThreadsToRtPriority = config->switchThreadsToRtPriority; + callbackData->ebEncParameters.maxCLL = config->maxCLL; + callbackData->ebEncParameters.maxFALL = config->maxFALL; + callbackData->ebEncParameters.useMasteringDisplayColorVolume = config->useMasteringDisplayColorVolume; + callbackData->ebEncParameters.dolbyVisionProfile = config->dolbyVisionProfile; + callbackData->ebEncParameters.useNaluFile = config->useNaluFile; + + callbackData->ebEncParameters.displayPrimaryX[0] = config->displayPrimaryX[0]; + callbackData->ebEncParameters.displayPrimaryX[1] = config->displayPrimaryX[1]; + callbackData->ebEncParameters.displayPrimaryX[2] = config->displayPrimaryX[2]; + callbackData->ebEncParameters.displayPrimaryY[0] = config->displayPrimaryY[0]; + callbackData->ebEncParameters.displayPrimaryY[1] = config->displayPrimaryY[1]; + callbackData->ebEncParameters.displayPrimaryY[2] = config->displayPrimaryY[2]; + callbackData->ebEncParameters.whitePointX = config->whitePointX; + callbackData->ebEncParameters.whitePointY = config->whitePointY; + callbackData->ebEncParameters.maxDisplayMasteringLuminance = config->maxDisplayMasteringLuminance; + callbackData->ebEncParameters.minDisplayMasteringLuminance = config->minDisplayMasteringLuminance; + return return_error; } @@ -234,6 +257,8 @@ EB_ERRORTYPE AllocateFrameBuffer( EB_ERRORTYPE return_error = EB_ErrorNone; const int32_t tenBitPackedMode = (config->encoderBitDepth > 8) && (config->compressedTenBitFormat == 0) ? 1 : 0; + const EB_COLOR_FORMAT colorFormat = (EB_COLOR_FORMAT)config->encoderColorFormat; // Chroma subsampling + const uint8_t subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; // Determine size of each plane const size_t luma8bitSize = @@ -243,15 +268,15 @@ EB_ERRORTYPE AllocateFrameBuffer( (1 << tenBitPackedMode); - const size_t chroma8bitSize = luma8bitSize >> 2; + const size_t chroma8bitSize = luma8bitSize >> (3 - colorFormat); const size_t luma10bitSize = (config->encoderBitDepth > 8 && tenBitPackedMode == 0) ? luma8bitSize : 0; const size_t chroma10bitSize = (config->encoderBitDepth > 8 && tenBitPackedMode == 0) ? chroma8bitSize : 0; // Determine EB_H265_ENC_INPUT* inputPtr = (EB_H265_ENC_INPUT*)pBuffer; inputPtr->yStride = config->inputPaddedWidth; - inputPtr->crStride = config->inputPaddedWidth >> 1; - inputPtr->cbStride = config->inputPaddedWidth >> 1; + inputPtr->crStride = config->inputPaddedWidth >> subWidthCMinus1; + inputPtr->cbStride = config->inputPaddedWidth >> subWidthCMinus1; if (luma8bitSize) { EB_APP_MALLOC(uint8_t*, inputPtr->luma, luma8bitSize, EB_N_PTR, EB_ErrorInsufficientResources); } @@ -294,6 +319,10 @@ EB_ERRORTYPE AllocateFrameBuffer( inputPtr->crExt = 0; } + if (config->dolbyVisionProfile == 81 && config->dolbyVisionRpuFile) { + EB_APP_MALLOC(uint8_t*, inputPtr->dolbyVisionRpu.payload, 1024, EB_N_PTR, EB_ErrorInsufficientResources); + } + return return_error; } @@ -332,13 +361,11 @@ EB_ERRORTYPE AllocateOutputReconBuffers( { EB_ERRORTYPE return_error = EB_ErrorNone; - const size_t lumaSize = - config->inputPaddedWidth * - config->inputPaddedHeight; + const size_t lumaSize = config->inputPaddedWidth * config->inputPaddedHeight; // both u and v - const size_t chromaSize = lumaSize >> 1; + const size_t chromaSize = lumaSize >> (3 - config->encoderColorFormat); const size_t tenBit = (config->encoderBitDepth > 8); - const size_t frameSize = (lumaSize + chromaSize) << tenBit; + const size_t frameSize = (lumaSize + 2 * chromaSize) << tenBit; // ... Recon Port EB_APP_MALLOC(EB_BUFFERHEADERTYPE*, callbackData->reconBuffer, sizeof(EB_BUFFERHEADERTYPE), EB_N_PTR, EB_ErrorInsufficientResources); @@ -379,48 +406,37 @@ EB_ERRORTYPE PreloadFramesIntoRam( EbConfig_t *config) { EB_ERRORTYPE return_error = EB_ErrorNone; - int32_t processedFrameCount; - int32_t filledLen; - int32_t inputPaddedWidth = config->inputPaddedWidth; - int32_t inputPaddedHeight = config->inputPaddedHeight; - int32_t readSize; - uint8_t *ebInputPtr; + int32_t processedFrameCount; + int32_t filledLen; + int32_t inputPaddedWidth = config->inputPaddedWidth; + int32_t inputPaddedHeight = config->inputPaddedHeight; + int32_t readSize; + uint8_t *ebInputPtr; FILE *inputFile = config->inputFile; - if (config->encoderBitDepth == 10 && config->compressedTenBitFormat == 1) - { - + if (config->encoderBitDepth == 10 && config->compressedTenBitFormat == 1) { readSize = (inputPaddedWidth*inputPaddedHeight * 3) / 2 + (inputPaddedWidth / 4 * inputPaddedHeight * 3) / 2; - - } - else - { - - readSize = inputPaddedWidth * inputPaddedHeight * 3 * (config->encoderBitDepth > 8 ? 2 : 1) / 2; - + } else { + readSize = inputPaddedWidth * inputPaddedHeight; //Luma + readSize += 2 * (readSize >> (3 - config->encoderColorFormat)); // Add Chroma + readSize *= (config->encoderBitDepth > 8 ? 2 : 1); //10 bit } EB_APP_MALLOC(uint8_t **, config->sequenceBuffer, sizeof(uint8_t*) * config->bufferedInput, EB_N_PTR, EB_ErrorInsufficientResources); - for (processedFrameCount = 0; processedFrameCount < config->bufferedInput; ++processedFrameCount) { EB_APP_MALLOC(uint8_t*, config->sequenceBuffer[processedFrameCount], readSize, EB_N_PTR, EB_ErrorInsufficientResources); // Interlaced Video if (config->separateFields) { EB_BOOL is16bit = config->encoderBitDepth > 8; if (is16bit == 0 || (is16bit == 1 && config->compressedTenBitFormat == 0)) { - const int32_t tenBitPackedMode = (config->encoderBitDepth > 8) && (config->compressedTenBitFormat == 0) ? 1 : 0; - const size_t luma8bitSize = - (config->inputPaddedWidth) * (config->inputPaddedHeight) * - (1 << tenBitPackedMode); const size_t chroma8bitSize = luma8bitSize >> 2; - filledLen = 0; ProcessInputFieldBufferingMode( @@ -432,11 +448,9 @@ EB_ERRORTYPE PreloadFramesIntoRam( config->sequenceBuffer[processedFrameCount] + luma8bitSize + chroma8bitSize, (uint32_t)inputPaddedWidth, (uint32_t)inputPaddedHeight, - is16bit); if (readSize != filledLen) { - fseek(inputFile, 0, SEEK_SET); filledLen = 0; @@ -449,7 +463,6 @@ EB_ERRORTYPE PreloadFramesIntoRam( config->sequenceBuffer[processedFrameCount] + luma8bitSize + chroma8bitSize, (uint32_t)inputPaddedWidth, (uint32_t)inputPaddedHeight, - is16bit); } diff --git a/Source/App/EbAppMain.c b/Source/App/EbAppMain.c index e2b23cc07..ab393c70c 100644 --- a/Source/App/EbAppMain.c +++ b/Source/App/EbAppMain.c @@ -56,6 +56,9 @@ volatile int32_t keepRunning = 1; void EventHandler(int32_t dummy) { (void)dummy; keepRunning = 0; + + // restore default signal handler + signal(SIGINT, SIG_DFL); } void AssignAppThreadGroup(uint8_t targetSocket) { @@ -97,13 +100,8 @@ int32_t main(int32_t argc, char* argv[]) EbConfig_t *configs[MAX_CHANNEL_NUMBER]; // Encoder Configuration - uint64_t encodingStartTimesSeconds[MAX_CHANNEL_NUMBER]; // Array holding start time of each instance - uint64_t encodingFinishTimesSeconds[MAX_CHANNEL_NUMBER]; // Array holding finish time of each instance - uint64_t encodingStartTimesuSeconds[MAX_CHANNEL_NUMBER]; // Array holding start time of each instance - uint64_t encodingFinishTimesuSeconds[MAX_CHANNEL_NUMBER]; // Array holding finish time of each instance - - uint32_t numChannels = 0; - uint32_t instanceCount=0; + uint32_t numChannels = 0; + uint32_t instanceCount=0; EbAppContext_t *appCallbacks[MAX_CHANNEL_NUMBER]; // Instances App callback data signal(SIGINT, EventHandler); printf("-------------------------------------------\n"); @@ -158,6 +156,8 @@ int32_t main(int32_t argc, char* argv[]) configs[instanceCount]->activeChannelCount = numChannels; configs[instanceCount]->channelId = instanceCount; + EbStartTime((uint64_t*)&configs[instanceCount]->performanceContext.libStartTime[0], (uint64_t*)&configs[instanceCount]->performanceContext.libStartTime[1]); + return_errors[instanceCount] = InitEncoder(configs[instanceCount], appCallbacks[instanceCount], instanceCount); return_error = (EB_ERRORTYPE)(return_error | return_errors[instanceCount]); } @@ -176,7 +176,7 @@ int32_t main(int32_t argc, char* argv[]) exitConditionsRecon[instanceCount] = configs[instanceCount]->reconFile ? APP_ExitConditionNone : APP_ExitConditionError; exitConditionsInput[instanceCount] = APP_ExitConditionNone; channelActive[instanceCount] = EB_TRUE; - EbStartTime((uint64_t*)&encodingStartTimesSeconds[instanceCount], (uint64_t*)&encodingStartTimesuSeconds[instanceCount]); + EbStartTime((uint64_t*)&configs[instanceCount]->performanceContext.encodeStartTime[0], (uint64_t*)&configs[instanceCount]->performanceContext.encodeStartTime[1]); } else { exitConditions[instanceCount] = APP_ExitConditionError; @@ -212,7 +212,6 @@ int32_t main(int32_t argc, char* argv[]) if (((exitConditionsRecon[instanceCount] == APP_ExitConditionFinished || !configs[instanceCount]->reconFile) && exitConditionsOutput[instanceCount] == APP_ExitConditionFinished && exitConditionsInput[instanceCount] == APP_ExitConditionFinished)|| ((exitConditionsRecon[instanceCount] == APP_ExitConditionError && configs[instanceCount]->reconFile) || exitConditionsOutput[instanceCount] == APP_ExitConditionError || exitConditionsInput[instanceCount] == APP_ExitConditionError)){ channelActive[instanceCount] = EB_FALSE; - EbFinishTime((uint64_t*)&encodingFinishTimesSeconds[instanceCount], (uint64_t*)&encodingFinishTimesuSeconds[instanceCount]); if (configs[instanceCount]->reconFile) exitConditions[instanceCount] = (APPEXITCONDITIONTYPE)(exitConditionsRecon[instanceCount] | exitConditionsOutput[instanceCount] | exitConditionsInput[instanceCount]); else @@ -271,16 +270,20 @@ int32_t main(int32_t argc, char* argv[]) // Interlaced Video if (configs[instanceCount]->interlacedVideo || configs[instanceCount]->separateFields) { - printf("\nChannel %u\nAverage Speed:\t\t%.2f fields per sec\nAverage Latency:\t%.0f ms\nMax Latency:\t\t%u ms\n", + printf("\nChannel %u\nAverage Speed:\t\t%.0f fields per sec\nTotal Encoding Time:\t\t%.0f ms\nTotal Execution Time:\t\t%.2f ms\nAverage Latency:\t%.0f ms\nMax Latency:\t\t%u ms\n", (uint32_t)(instanceCount + 1), configs[instanceCount]->performanceContext.averageSpeed, + configs[instanceCount]->performanceContext.totalEncodeTime * 1000, + configs[instanceCount]->performanceContext.totalExecutionTime * 1000, configs[instanceCount]->performanceContext.averageLatency, (uint32_t)(configs[instanceCount]->performanceContext.maxLatency)); } else { - printf("\nChannel %u\nAverage Speed:\t\t%.2f fps\nAverage Latency:\t%.0f ms\nMax Latency:\t\t%u ms\n", + printf("\nChannel %u\nAverage Speed:\t\t%.2f fps\nTotal Encoding Time:\t%.0f ms\nTotal Execution Time:\t%.0f ms\nAverage Latency:\t%.0f ms\nMax Latency:\t\t%u ms\n", (uint32_t)(instanceCount + 1), configs[instanceCount]->performanceContext.averageSpeed, + configs[instanceCount]->performanceContext.totalEncodeTime * 1000, + configs[instanceCount]->performanceContext.totalExecutionTime * 1000, configs[instanceCount]->performanceContext.averageLatency, (uint32_t)(configs[instanceCount]->performanceContext.maxLatency)); diff --git a/Source/App/EbAppProcessCmd.c b/Source/App/EbAppProcessCmd.c index 0aaa8813c..5f72d36e4 100644 --- a/Source/App/EbAppProcessCmd.c +++ b/Source/App/EbAppProcessCmd.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "EbAppContext.h" #include "EbAppConfig.h" @@ -21,7 +22,8 @@ #define CLIP3(MinVal, MaxVal, a) (((a)<(MinVal)) ? (MinVal) : (((a)>(MaxVal)) ? (MaxVal) :(a))) #define FUTURE_WINDOW_WIDTH 4 -#define SIZE_OF_ONE_FRAME_IN_BYTES(width, height,is16bit) ( ( ((width)*(height)*3)>>1 )<>(3-csp)) )<inputPaddedWidth; - int64_t inputPaddedHeight = config->inputPaddedHeight; - - uint64_t sourceLumaRowSize = (uint64_t)(inputPaddedWidth << is16bit); - - uint64_t sourceChromaRowSize = sourceLumaRowSize >> 1; - + uint8_t is16bit) +{ + const int64_t inputPaddedWidth = config->inputPaddedWidth; + const int64_t inputPaddedHeight = config->inputPaddedHeight; + const EB_COLOR_FORMAT colorFormat = (EB_COLOR_FORMAT)config->encoderColorFormat; + const uint8_t subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const uint8_t subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + //const uint8_t is16bit = (config->encoderBitDepth > 8) ? 1 : 0; + uint64_t sourceLumaRowSize = (uint64_t)(inputPaddedWidth << is16bit); + uint64_t sourceChromaRowSize = sourceLumaRowSize >> subWidthCMinus1; uint8_t *ebInputPtr; uint32_t inputRowIndex; @@ -656,7 +658,7 @@ void ProcessInputFieldStandardMode( fseeko64(inputFile, (long)sourceChromaRowSize, SEEK_CUR); } - for (inputRowIndex = 0; inputRowIndex < inputPaddedHeight >> 1; inputRowIndex++) { + for (inputRowIndex = 0; inputRowIndex < inputPaddedHeight >> subHeightCMinus1; inputRowIndex++) { headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, sourceChromaRowSize, inputFile); // Skip 1 chroma row (only fields) @@ -670,7 +672,7 @@ void ProcessInputFieldStandardMode( // => no action - for (inputRowIndex = 0; inputRowIndex < inputPaddedHeight >> 1; inputRowIndex++) { + for (inputRowIndex = 0; inputRowIndex < inputPaddedHeight >> subHeightCMinus1; inputRowIndex++) { headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, sourceChromaRowSize, inputFile); // Skip 1 chroma row (only fields) @@ -753,29 +755,29 @@ int32_t GetNextQpFromQpFile( return qp; } -void ReadInputFrames( +static void ReadInputFrames( EbConfig_t *config, uint8_t is16bit, EB_BUFFERHEADERTYPE *headerPtr) { - - uint64_t readSize; - uint32_t inputPaddedWidth = config->inputPaddedWidth; - uint32_t inputPaddedHeight = config->inputPaddedHeight; + const uint32_t inputPaddedWidth = config->inputPaddedWidth; + const uint32_t inputPaddedHeight = config->inputPaddedHeight; FILE *inputFile = config->inputFile; - uint8_t *ebInputPtr; EB_H265_ENC_INPUT* inputPtr = (EB_H265_ENC_INPUT*)headerPtr->pBuffer; + const EB_COLOR_FORMAT colorFormat = (EB_COLOR_FORMAT)config->encoderColorFormat; + const uint8_t subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const uint8_t subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + //const uint8_t is16bit = (config->encoderBitDepth > 8) ? 1 : 0; - uint64_t frameSize = (uint64_t)((inputPaddedWidth*inputPaddedHeight * 3) / 2 + (inputPaddedWidth / 4 * inputPaddedHeight * 3) / 2); inputPtr->yStride = inputPaddedWidth; - inputPtr->crStride = inputPaddedWidth >> 1; - inputPtr->cbStride = inputPaddedWidth >> 1; + inputPtr->crStride = inputPaddedWidth >> subWidthCMinus1; + inputPtr->cbStride = inputPaddedWidth >> subWidthCMinus1; + inputPtr->dolbyVisionRpu.payloadSize = 0; if (config->bufferedInput == -1) { - if (is16bit == 0 || (is16bit == 1 && config->compressedTenBitFormat == 0)) { - readSize = (uint64_t)SIZE_OF_ONE_FRAME_IN_BYTES(inputPaddedWidth, inputPaddedHeight, is16bit); + uint32_t readSize = SIZE_OF_ONE_FRAME_IN_BYTES(inputPaddedWidth, inputPaddedHeight, colorFormat, is16bit); headerPtr->nFilledLen = 0; @@ -810,302 +812,98 @@ void ReadInputFrames( if (config->processedFrameCount % 2 == 0) { fseek(inputFile, -(long)(readSize << 1), SEEK_CUR); } - } - else { - uint64_t lumaReadSize = (uint64_t)inputPaddedWidth*inputPaddedHeight << is16bit; - ebInputPtr = inputPtr->luma; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize, inputFile); - ebInputPtr = inputPtr->cb; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->cr; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - inputPtr->luma = inputPtr->luma + ((config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING) << is16bit); - inputPtr->cb = inputPtr->cb + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1)) << is16bit); - inputPtr->cr = inputPtr->cr + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1)) << is16bit); + } else { + const uint32_t lumaReadSize = inputPaddedWidth * inputPaddedHeight << is16bit; + const uint32_t chromaReadSize = lumaReadSize >> (3 - colorFormat); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->luma, 1, lumaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cb, 1, chromaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cr, 1, chromaReadSize, inputFile); if (readSize != headerPtr->nFilledLen) { fseek(inputFile, 0, SEEK_SET); - ebInputPtr = inputPtr->luma; - headerPtr->nFilledLen = (uint32_t)fread(ebInputPtr, 1, lumaReadSize, inputFile); - ebInputPtr = inputPtr->cb; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->cr; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->luma, 1, lumaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cb, 1, chromaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cr, 1, chromaReadSize, inputFile); inputPtr->luma = inputPtr->luma + ((config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING)); - inputPtr->cb = inputPtr->cb + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - inputPtr->cr = inputPtr->cr + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); + inputPtr->cb = inputPtr->cb + (((config->inputPaddedWidth >> subWidthCMinus1)*(TOP_INPUT_PADDING >> subHeightCMinus1) + (LEFT_INPUT_PADDING >> subWidthCMinus1))); + inputPtr->cr = inputPtr->cr + (((config->inputPaddedWidth >> subWidthCMinus1)*(TOP_INPUT_PADDING >> subHeightCMinus1) + (LEFT_INPUT_PADDING >> subWidthCMinus1))); } } - } - // 10-bit Compressed Unpacked Mode - else if (is16bit == 1 && config->compressedTenBitFormat == 1) { + } else { + // 10-bit Compressed Unpacked Mode + const uint32_t lumaReadSize = inputPaddedWidth * inputPaddedHeight; + const uint32_t chromaReadSize = lumaReadSize >> (3 - colorFormat); + const uint32_t nbitLumaReadSize = (inputPaddedWidth / 4) * inputPaddedHeight; + const uint32_t nbitChromaReadSize = nbitLumaReadSize >> (3 - colorFormat); // Fill the buffer with a complete frame headerPtr->nFilledLen = 0; + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->luma, 1, lumaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cb, 1, chromaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cr, 1, chromaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->lumaExt, 1, nbitLumaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cbExt, 1, nbitChromaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->crExt, 1, nbitChromaReadSize, inputFile); - uint64_t lumaReadSize = (uint64_t)inputPaddedWidth*inputPaddedHeight; - uint64_t nbitlumaReadSize = (uint64_t)(inputPaddedWidth / 4)*inputPaddedHeight; - - ebInputPtr = inputPtr->luma; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize, inputFile); - ebInputPtr = inputPtr->cb; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->cr; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - - inputPtr->luma = inputPtr->luma + config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING; - inputPtr->cb = inputPtr->cb + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); - inputPtr->cr = inputPtr->cr + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); - - - ebInputPtr = inputPtr->lumaExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, nbitlumaReadSize, inputFile); - ebInputPtr = inputPtr->cbExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, nbitlumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->crExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, nbitlumaReadSize >> 2, inputFile); - - inputPtr->lumaExt = inputPtr->lumaExt + ((config->inputPaddedWidth >> 2)*TOP_INPUT_PADDING + (LEFT_INPUT_PADDING >> 2)); - inputPtr->cbExt = inputPtr->cbExt + (((config->inputPaddedWidth >> 1) >> 2)*(TOP_INPUT_PADDING >> 1) + ((LEFT_INPUT_PADDING >> 1) >> 2)); - inputPtr->crExt = inputPtr->crExt + (((config->inputPaddedWidth >> 1) >> 2)*(TOP_INPUT_PADDING >> 1) + ((LEFT_INPUT_PADDING >> 1) >> 2)); - - readSize = ((lumaReadSize * 3) >> 1) + ((nbitlumaReadSize * 3) >> 1); - - if (readSize != headerPtr->nFilledLen) { - + if (headerPtr->nFilledLen != (lumaReadSize + nbitLumaReadSize + 2 * (chromaReadSize + nbitChromaReadSize))) { fseek(inputFile, 0, SEEK_SET); - ebInputPtr = inputPtr->luma; - headerPtr->nFilledLen = (uint32_t)fread(ebInputPtr, 1, lumaReadSize, inputFile); - ebInputPtr = inputPtr->cb; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->cr; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - - inputPtr->luma = inputPtr->luma + config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING; - inputPtr->cb = inputPtr->cb + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); - inputPtr->cr = inputPtr->cr + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); - - ebInputPtr = inputPtr->lumaExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, nbitlumaReadSize, inputFile); - ebInputPtr = inputPtr->cbExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, nbitlumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->crExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, nbitlumaReadSize >> 2, inputFile); - - inputPtr->lumaExt = inputPtr->lumaExt + ((config->inputPaddedWidth >> 2)*TOP_INPUT_PADDING + (LEFT_INPUT_PADDING >> 2)); - inputPtr->cbExt = inputPtr->cbExt + (((config->inputPaddedWidth >> 1) >> 2)*(TOP_INPUT_PADDING >> 1) + ((LEFT_INPUT_PADDING >> 1) >> 2)); - inputPtr->crExt = inputPtr->crExt + (((config->inputPaddedWidth >> 1) >> 2)*(TOP_INPUT_PADDING >> 1) + ((LEFT_INPUT_PADDING >> 1) >> 2)); - + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->luma, 1, lumaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cb, 1, chromaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cr, 1, chromaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->lumaExt, 1, nbitLumaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->cbExt, 1, nbitChromaReadSize, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(inputPtr->crExt, 1, nbitChromaReadSize, inputFile); } - } - - // 10-bit Unpacked Mode - else { - - readSize = (uint64_t)SIZE_OF_ONE_FRAME_IN_BYTES(inputPaddedWidth, inputPaddedHeight, 1); - - headerPtr->nFilledLen = 0; - - // Interlaced Video - if (config->separateFields) { - - ProcessInputFieldStandardMode( - config, - headerPtr, - inputFile, - inputPtr->luma, - inputPtr->cb, - inputPtr->cr, - 0); - - ProcessInputFieldStandardMode( - config, - headerPtr, - inputFile, - inputPtr->lumaExt, - inputPtr->cbExt, - inputPtr->crExt, - 0); - - if (readSize != headerPtr->nFilledLen) { - - fseek(inputFile, 0, SEEK_SET); - headerPtr->nFilledLen = 0; - - ProcessInputFieldStandardMode( - config, - headerPtr, - inputFile, - inputPtr->luma, - inputPtr->cb, - inputPtr->cr, - 0); - - ProcessInputFieldStandardMode( - config, - headerPtr, - inputFile, - inputPtr->lumaExt, - inputPtr->cbExt, - inputPtr->crExt, - 0); - } - - // Reset the pointer position after a top field - if (config->processedFrameCount % 2 == 0) { - fseek(inputFile, -(long)(readSize << 1), SEEK_CUR); - } - - } - else { - - - uint64_t lumaReadSize = (uint64_t)inputPaddedWidth*inputPaddedHeight; - - ebInputPtr = inputPtr->luma; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize, inputFile); - ebInputPtr = inputPtr->cb; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->cr; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - - inputPtr->luma = inputPtr->luma + ((config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING)); - inputPtr->cb = inputPtr->cb + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - inputPtr->cr = inputPtr->cr + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - - ebInputPtr = inputPtr->lumaExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize, inputFile); - ebInputPtr = inputPtr->cbExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->crExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - - inputPtr->lumaExt = inputPtr->lumaExt + ((config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING)); - inputPtr->cbExt = inputPtr->cbExt + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - inputPtr->crExt = inputPtr->crExt + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - - if (readSize != headerPtr->nFilledLen) { - - fseek(inputFile, 0, SEEK_SET); - ebInputPtr = inputPtr->luma; - headerPtr->nFilledLen = (uint32_t)fread(ebInputPtr, 1, lumaReadSize, inputFile); - ebInputPtr = inputPtr->cb; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->cr; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - - inputPtr->luma = inputPtr->luma + ((config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING)); - inputPtr->cb = inputPtr->cb + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - inputPtr->cr = inputPtr->cr + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - - ebInputPtr = inputPtr->lumaExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize, inputFile); - ebInputPtr = inputPtr->cbExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - ebInputPtr = inputPtr->crExt; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - - inputPtr->lumaExt = inputPtr->lumaExt + ((config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING)); - inputPtr->cbExt = inputPtr->cbExt + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - inputPtr->crExt = inputPtr->crExt + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - - } - - } - - - - - } - - } - else { - if (config->encoderBitDepth == 10 && config->compressedTenBitFormat == 1) - { + } else { + if (is16bit && config->compressedTenBitFormat == 1) { // Determine size of each plane const size_t luma8bitSize = config->inputPaddedWidth * config->inputPaddedHeight; - const size_t chroma8bitSize = luma8bitSize >> 2; + const size_t chroma8bitSize = luma8bitSize >> (3 - colorFormat); const size_t luma2bitSize = luma8bitSize / 4; //4-2bit pixels into 1 byte - const size_t chroma2bitSize = luma2bitSize >> 2; + const size_t chroma2bitSize = luma2bitSize >> (3 - colorFormat); EB_H265_ENC_INPUT* inputPtr = (EB_H265_ENC_INPUT*)headerPtr->pBuffer; inputPtr->yStride = config->inputPaddedWidth; - inputPtr->crStride = config->inputPaddedWidth >> 1; - inputPtr->cbStride = config->inputPaddedWidth >> 1; + inputPtr->crStride = config->inputPaddedWidth >> subWidthCMinus1; + inputPtr->cbStride = config->inputPaddedWidth >> subWidthCMinus1; inputPtr->luma = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput]; inputPtr->cb = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize; inputPtr->cr = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + chroma8bitSize; - inputPtr->luma = inputPtr->luma + ((config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING)); - inputPtr->cb = inputPtr->cb + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - inputPtr->cr = inputPtr->cr + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1))); - - if (is16bit) { - inputPtr->lumaExt = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + 2 * chroma8bitSize; - inputPtr->cbExt = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + 2 * chroma8bitSize + luma2bitSize; - inputPtr->crExt = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + 2 * chroma8bitSize + luma2bitSize + chroma2bitSize; - - inputPtr->lumaExt = inputPtr->lumaExt + config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING; - inputPtr->cbExt = inputPtr->cbExt + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); - inputPtr->crExt = inputPtr->crExt + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); + inputPtr->lumaExt = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + 2 * chroma8bitSize; + inputPtr->cbExt = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + 2 * chroma8bitSize + luma2bitSize; + inputPtr->crExt = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + 2 * chroma8bitSize + luma2bitSize + chroma2bitSize; - } - - headerPtr->nFilledLen = (uint32_t)frameSize; - } - else - { - const int32_t tenBitPackedMode = (config->encoderBitDepth > 8) && (config->compressedTenBitFormat == 0) ? 1 : 0; + headerPtr->nFilledLen = luma8bitSize + luma2bitSize + 2 * (chroma8bitSize + chroma2bitSize); + } else { + //Normal unpacked mode:yuv420p10le yuv422p10le yuv444p10le // Determine size of each plane - const size_t luma8bitSize = - (config->inputPaddedWidth) * - (config->inputPaddedHeight) * - (1 << tenBitPackedMode); - - const size_t chroma8bitSize = luma8bitSize >> 2; - - const size_t luma10bitSize = (config->encoderBitDepth > 8 && tenBitPackedMode == 0) ? luma8bitSize : 0; - const size_t chroma10bitSize = (config->encoderBitDepth > 8 && tenBitPackedMode == 0) ? chroma8bitSize : 0; + const size_t lumaSize = (config->inputPaddedWidth * config->inputPaddedHeight) << is16bit; + const size_t chromaSize = lumaSize >> (3 - colorFormat); EB_H265_ENC_INPUT* inputPtr = (EB_H265_ENC_INPUT*)headerPtr->pBuffer; inputPtr->yStride = config->inputPaddedWidth; - inputPtr->crStride = config->inputPaddedWidth >> 1; - inputPtr->cbStride = config->inputPaddedWidth >> 1; + inputPtr->crStride = config->inputPaddedWidth >> subWidthCMinus1; + inputPtr->cbStride = config->inputPaddedWidth >> subWidthCMinus1; inputPtr->luma = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput]; - inputPtr->cb = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize; - inputPtr->cr = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + chroma8bitSize; - inputPtr->luma = inputPtr->luma + ((config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING) << tenBitPackedMode); - inputPtr->cb = inputPtr->cb + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1)) << tenBitPackedMode); - inputPtr->cr = inputPtr->cr + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1)) << tenBitPackedMode); + inputPtr->cb = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + lumaSize; + inputPtr->cr = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + lumaSize + chromaSize; - - if (is16bit) { - inputPtr->lumaExt = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + 2 * chroma8bitSize; - inputPtr->cbExt = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + 2 * chroma8bitSize + luma10bitSize; - inputPtr->crExt = config->sequenceBuffer[config->processedFrameCount % config->bufferedInput] + luma8bitSize + 2 * chroma8bitSize + luma10bitSize + chroma10bitSize; - inputPtr->lumaExt = inputPtr->lumaExt + config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING; - inputPtr->cbExt = inputPtr->cbExt + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); - inputPtr->crExt = inputPtr->crExt + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); - - } - - headerPtr->nFilledLen = (uint32_t)(uint64_t)SIZE_OF_ONE_FRAME_IN_BYTES(inputPaddedWidth, inputPaddedHeight, is16bit); + headerPtr->nFilledLen = (uint32_t)(uint64_t)SIZE_OF_ONE_FRAME_IN_BYTES(inputPaddedWidth, inputPaddedHeight, colorFormat, is16bit); } - - } // If we reached the end of file, loop over again @@ -1155,14 +953,130 @@ void SendQpOnTheFly( return; } +void SendNaluOnTheFly( + EbConfig_t *config, + EB_BUFFERHEADERTYPE *headerPtr) +{ + { + char line[1024]; + EB_ERRORTYPE return_error = EB_ErrorNone; + uint32_t poc = 0; + uint8_t *prefix = NULL; + uint32_t nalType = 0; + uint32_t payloadType = 0; + uint8_t *base64Encode = NULL; + uint8_t *context = NULL; + + if (fgets(line, sizeof(line), config->naluFile) != NULL) { + poc = strtol(EB_STRTOK(line, " ", &context), NULL, 0); + if (return_error == EB_ErrorNone && *context != 0) { + prefix = (uint8_t*)EB_STRTOK(NULL, " ", &context); + } + else { + return_error = EB_ErrorBadParameter; + } + if (return_error == EB_ErrorNone && *context != 0) { + nalType = strtol(EB_STRTOK(NULL, "/", &context), NULL, 0); + } + else { + return_error = EB_ErrorBadParameter; + } + if (return_error == EB_ErrorNone && *context != 0) { + payloadType = strtol(EB_STRTOK(NULL, " ", &context), NULL, 0); + } + else { + return_error = EB_ErrorBadParameter; + } + if (return_error == EB_ErrorNone && *context != 0) { + base64Encode = (uint8_t*)EB_STRTOK(NULL, "\n", &context); + } + else { + return_error = EB_ErrorBadParameter; + } + + headerPtr->naluPOC = poc; + if (prefix != NULL && !EB_STRCMP((char*)prefix, "PREFIX")) + headerPtr->naluPrefix = 0; + headerPtr->naluNalType = nalType; + headerPtr->naluPayloadType = payloadType; + headerPtr->naluBase64Encode = base64Encode; + headerPtr->naluFound = EB_TRUE; + } + else { + return_error = EB_ErrorBadParameter; + } + if (return_error != EB_ErrorNone) { + printf("\nSVT [Warning]: Nalu file cannot be parsed correctly \n "); + headerPtr->naluFound = EB_FALSE; + //config->useNaluFile = EB_FALSE; + return; + } + } + return; +} + +#define START_CODE 0x00000001 +#define START_CODE_BYTES 4 + +int ParseDolbyVisionRPUMetadata( + EbConfig_t *config, + EB_BUFFERHEADERTYPE *headerPtr) +{ + uint8_t byteVal = 0; + uint32_t code = 0; + uint32_t bytesRead = 0; + EB_H265_ENC_INPUT* inputPtr = (EB_H265_ENC_INPUT*)headerPtr->pBuffer; + FILE* ptr = config->dolbyVisionRpuFile; + + if (!headerPtr->pts) { + while (bytesRead++ < 4 && fread(&byteVal, sizeof(uint8_t), 1, ptr)) + code = (code << 8) | byteVal; + + if (code != START_CODE) { + printf("Warning : Invalid Dolby Vision RPU startcode in POC %" PRId64 "\n", headerPtr->pts); + return 1; + } + } + + bytesRead = 0; + while (fread(&byteVal, sizeof(uint8_t), 1, ptr)) + { + code = (code << 8) | byteVal; + if (bytesRead++ < 3) + continue; + if (bytesRead >= 1024) { + printf("Warning : Invalid Dolby Vision RPU size in POC %" PRId64 "\n", headerPtr->pts); + return 1; + } + + if (code != START_CODE) + inputPtr->dolbyVisionRpu.payload[inputPtr->dolbyVisionRpu.payloadSize++] = (code >> (3 * 8)) & 0xFF; + else + return 0; + + } + + int ShiftBytes = START_CODE_BYTES - (bytesRead - inputPtr->dolbyVisionRpu.payloadSize); + int bytesLeft = bytesRead - inputPtr->dolbyVisionRpu.payloadSize; + code = (code << ShiftBytes * 8); + for (int i = 0; i < bytesLeft; i++) { + inputPtr->dolbyVisionRpu.payload[inputPtr->dolbyVisionRpu.payloadSize++] = (code >> (3 * 8)) & 0xFF; + code = (code << 8); + } + if (!inputPtr->dolbyVisionRpu.payloadSize) { + printf("Warning : Dolby Vision RPU not found for POC %" PRId64 "\n", headerPtr->pts); + return 1; + } + return 0; + +} + //************************************/ // ProcessInputBuffer // Reads yuv frames from file and copy // them into the input buffer /************************************/ -APPEXITCONDITIONTYPE ProcessInputBuffer( - EbConfig_t *config, - EbAppContext_t *appCallBack) +APPEXITCONDITIONTYPE ProcessInputBuffer(EbConfig_t *config, EbAppContext_t *appCallBack) { uint8_t is16bit = (uint8_t)(config->encoderBitDepth > 8); EB_BUFFERHEADERTYPE *headerPtr = appCallBack->inputBufferPool; @@ -1173,9 +1087,12 @@ APPEXITCONDITIONTYPE ProcessInputBuffer( int64_t inputPaddedWidth = config->inputPaddedWidth; int64_t inputPaddedHeight = config->inputPaddedHeight; int64_t framesToBeEncoded = config->framesToBeEncoded; - uint64_t frameSize = (uint64_t)((inputPaddedWidth*inputPaddedHeight * 3) / 2 + (inputPaddedWidth / 4 * inputPaddedHeight * 3) / 2); int64_t totalBytesToProcessCount; int64_t remainingByteCount; + const EB_COLOR_FORMAT colorFormat = (EB_COLOR_FORMAT)config->encoderColorFormat; + int ret; + uint32_t compressed10bitFrameSize = (inputPaddedWidth*inputPaddedHeight) + 2 * ((inputPaddedWidth*inputPaddedWidth) >> (3 - colorFormat)); + compressed10bitFrameSize += compressed10bitFrameSize / 4; if (config->injector && config->processedFrameCount) { @@ -1183,8 +1100,8 @@ APPEXITCONDITIONTYPE ProcessInputBuffer( } totalBytesToProcessCount = (framesToBeEncoded < 0) ? -1 : (config->encoderBitDepth == 10 && config->compressedTenBitFormat == 1) ? - framesToBeEncoded * (int64_t)frameSize : - framesToBeEncoded * SIZE_OF_ONE_FRAME_IN_BYTES(inputPaddedWidth, inputPaddedHeight, is16bit); + framesToBeEncoded * compressed10bitFrameSize: + framesToBeEncoded * SIZE_OF_ONE_FRAME_IN_BYTES(inputPaddedWidth, inputPaddedHeight, colorFormat, is16bit); remainingByteCount = (totalBytesToProcessCount < 0) ? -1 : totalBytesToProcessCount - (int64_t)config->processedByteCount; @@ -1207,6 +1124,12 @@ APPEXITCONDITIONTYPE ProcessInputBuffer( config, headerPtr); + // Configuration parameters changed on the fly + if (config->useNaluFile && config->naluFile) + SendNaluOnTheFly( + config, + headerPtr); + if (keepRunning == 0 && !config->stopEncoder) { config->stopEncoder = EB_TRUE; } @@ -1217,6 +1140,15 @@ APPEXITCONDITIONTYPE ProcessInputBuffer( headerPtr->nFlags = 0; + if (config->dolbyVisionProfile == 81 && config->dolbyVisionRpuFile) { + ret = ParseDolbyVisionRPUMetadata( + config, + headerPtr); + if (ret) { + printf("\n Warning : Dolby vision RPU not parsed for POC %" PRId64 "\t", headerPtr->pts); + } + } + // Send the picture EbH265EncSendPicture(componentHandle, headerPtr); @@ -1250,32 +1182,23 @@ APPEXITCONDITIONTYPE ProcessOutputStreamBuffer( EbAppContext_t *appCallBack, uint8_t picSendDone) { - APPPORTACTIVETYPE *portState = &appCallBack->outputStreamPortActive; - EB_BUFFERHEADERTYPE *headerPtr; - EB_COMPONENTTYPE *componentHandle = (EB_COMPONENTTYPE*)appCallBack->svtEncoderHandle; - APPEXITCONDITIONTYPE return_value = APP_ExitConditionNone; - EB_ERRORTYPE stream_status = EB_ErrorNone; + APPPORTACTIVETYPE *portState = &appCallBack->outputStreamPortActive; + EB_BUFFERHEADERTYPE *headerPtr; + EB_COMPONENTTYPE *componentHandle = (EB_COMPONENTTYPE*)appCallBack->svtEncoderHandle; + APPEXITCONDITIONTYPE return_value = APP_ExitConditionNone; + EB_ERRORTYPE stream_status = EB_ErrorNone; // Per channel variables - FILE *streamFile = config->bitstreamFile; - uint32_t startFrame = (config->framesToBeEncoded == 0 || config->framesToBeEncoded < LONG_ENCODE_FRAME_ENCODE || (SPEED_MEASUREMENT_INTERVAL < START_STEADY_STATE)) ? 0 : START_STEADY_STATE; - uint64_t *startsTime = &config->performanceContext.startsTime ; - uint64_t *startuTime = &config->performanceContext.startuTime ; + FILE *streamFile = config->bitstreamFile; - uint64_t *totalLatency = &config->performanceContext.totalLatency; - uint32_t *maxLatency = &config->performanceContext.maxLatency; + uint64_t *totalLatency = &config->performanceContext.totalLatency; + uint32_t *maxLatency = &config->performanceContext.maxLatency; // System performance variables - static uint64_t allChannelsStartsTime = 0; - static uint64_t allChannelsStartuTime = 0; - static int32_t frameCount = 0; - - // for a long encode, ignore startup time to get the steady state speed - static uint32_t allChannelsStartFrame = START_STEADY_STATE; + static int32_t frameCount = 0; // Local variables - uint64_t finishsTime = 0; - uint64_t finishuTime = 0; - double duration = 0.0; + uint64_t finishsTime = 0; + uint64_t finishuTime = 0; // non-blocking call until all input frames are sent stream_status = EbH265GetPacket(componentHandle, &headerPtr, picSendDone); @@ -1291,25 +1214,23 @@ APPEXITCONDITIONTYPE ProcessOutputStreamBuffer( ++(config->performanceContext.frameCount); *totalLatency += (uint64_t)headerPtr->nTickCount; *maxLatency = (headerPtr->nTickCount > *maxLatency) ? headerPtr->nTickCount : *maxLatency; - - // Reset counters for long encodes - if (config->performanceContext.frameCount - 1 == startFrame) { - EbStartTime((uint64_t*)startsTime, (uint64_t*)startuTime); - *maxLatency = 0; - *totalLatency = 0; - } - if ((uint32_t)frameCount == allChannelsStartFrame && allChannelsStartFrame <= SPEED_MEASUREMENT_INTERVAL) { - EbStartTime((uint64_t*)&allChannelsStartsTime, (uint64_t*)&allChannelsStartuTime); - } - + EbFinishTime((uint64_t*)&finishsTime, (uint64_t*)&finishuTime); + // total execution time, inc init time + EbComputeOverallElapsedTime( + config->performanceContext.libStartTime[0], + config->performanceContext.libStartTime[1], + finishsTime, + finishuTime, + &config->performanceContext.totalExecutionTime); + // total encode time EbComputeOverallElapsedTime( - *startsTime, - *startuTime, + config->performanceContext.encodeStartTime[0], + config->performanceContext.encodeStartTime[1], finishsTime, finishuTime, - &duration); + &config->performanceContext.totalEncodeTime); // Write Stream Data to file if (streamFile) { @@ -1351,42 +1272,15 @@ APPEXITCONDITIONTYPE ProcessOutputStreamBuffer( fflush(stdout); // Queue the buffer again if the port is still active - if (*portState != APP_PortActive) { - if ((config->framesToBeEncoded < SPEED_MEASUREMENT_INTERVAL) || (config->framesToBeEncoded - startFrame) < SPEED_MEASUREMENT_INTERVAL) { - config->performanceContext.averageSpeed = (config->performanceContext.frameCount - startFrame) / duration; - config->performanceContext.averageLatency = config->performanceContext.totalLatency / (double)(config->performanceContext.frameCount - startFrame); - } - } - - - if ((((config->framesToBeEncoded != 0) && (config->framesToBeEncoded > SPEED_MEASUREMENT_INTERVAL)) - && ((int64_t)config->performanceContext.frameCount == (config->framesToBeEncoded - (int32_t)startFrame)))) { - config->performanceContext.averageSpeed = (config->performanceContext.frameCount - startFrame) / duration; - config->performanceContext.averageLatency = config->performanceContext.totalLatency / (double)(config->performanceContext.frameCount - startFrame); + { + config->performanceContext.averageSpeed = (config->performanceContext.frameCount) / config->performanceContext.totalEncodeTime; + config->performanceContext.averageLatency = config->performanceContext.totalLatency / (double)(config->performanceContext.frameCount); } if (!(frameCount % SPEED_MEASUREMENT_INTERVAL)) { - if (frameCount < (int32_t)allChannelsStartFrame && (frameCount >= (int32_t)LONG_ENCODE_FRAME_ENCODE) && (allChannelsStartFrame <= SPEED_MEASUREMENT_INTERVAL)) { - EbComputeOverallElapsedTime( - allChannelsStartsTime, - allChannelsStartuTime, - finishsTime, - finishuTime, - &duration); - - printf("\n"); - printf("Average System Encoding Speed: %.2f\n", (double)(frameCount - allChannelsStartFrame) / duration); - } - else { - EbComputeOverallElapsedTime( - *startsTime, - *startuTime, - finishsTime, - finishuTime, - &duration); - + { printf("\n"); - printf("Average System Encoding Speed: %.2f\n", (double)(frameCount - (int32_t)startFrame) / duration); + printf("Average System Encoding Speed: %.2f\n", (double)(frameCount) / config->performanceContext.totalEncodeTime); } } } diff --git a/Source/Lib/ASM_AVX2/EbIntraPrediction_AVX2.h b/Source/Lib/ASM_AVX2/EbIntraPrediction_AVX2.h index f45877007..1a9b4db74 100644 --- a/Source/Lib/ASM_AVX2/EbIntraPrediction_AVX2.h +++ b/Source/Lib/ASM_AVX2/EbIntraPrediction_AVX2.h @@ -39,7 +39,15 @@ extern void IntraModeVerticalLuma_AVX2_INTRIN( EB_U8 *refSamples, //input parameter, pointer to the reference samples EB_U8 *predictionPtr, //output parameter, pointer to the prediction const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr - const EB_BOOL skip //skip one row + const EB_BOOL skip //skip one row + ); + +extern void IntraModeVerticalChroma_AVX2_INTRIN( + const EB_U32 size, //input parameter, denotes the size of the current PU + EB_U8 *refSamples, //input parameter, pointer to the reference samples + EB_U8 *predictionPtr, //output parameter, pointer to the prediction + const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr + const EB_BOOL skip //skip one row ); extern void IntraModeDCLuma_AVX2_INTRIN( @@ -47,7 +55,15 @@ extern void IntraModeDCLuma_AVX2_INTRIN( EB_U8 *refSamples, //input parameter, pointer to the reference samples EB_U8 *predictionPtr, //output parameter, pointer to the prediction const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr - const EB_BOOL skip) ; //skip one row + const EB_BOOL skip) ; //skip one row + +extern void IntraModeDCChroma_AVX2_INTRIN( + const EB_U32 size, //input parameter, denotes the size of the current PU + EB_U8 *refSamples, //input parameter, pointer to the reference samples + EB_U8 *predictionPtr, //output parameter, pointer to the prediction + const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr + const EB_BOOL skip); //skip one row + extern void IntraModeAngular_2_AVX2_INTRIN( diff --git a/Source/Lib/ASM_AVX2/EbIntraPrediction_Intrinsic_AVX2.c b/Source/Lib/ASM_AVX2/EbIntraPrediction_Intrinsic_AVX2.c index 51f2b77f6..67477d0f4 100644 --- a/Source/Lib/ASM_AVX2/EbIntraPrediction_Intrinsic_AVX2.c +++ b/Source/Lib/ASM_AVX2/EbIntraPrediction_Intrinsic_AVX2.c @@ -1860,3 +1860,372 @@ void IntraModeAngular_34_AVX2_INTRIN( } } } + +void IntraModeVerticalChroma_AVX2_INTRIN( + const EB_U32 size, //input parameter, denotes the size of the current PU + EB_U8 *refSamples, //input parameter, pointer to the reference samples + EB_U8 *predictionPtr, //output parameter, pointer to the prediction + const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr + const EB_BOOL skip) //skip one row +{ + EB_U32 pStride = predictionBufferStride; + EB_U32 topOffset = (size << 1) + 1; + + // Jing: + // TODO: add size == 32 for 444 + if (!skip) { + if (size == 32) { + __m256i xmm0; + EB_U64 size_to_write; + EB_U32 count; + + // Each storeu calls stores 32 bytes. Hence each iteration stores 8 * 32 bytes. + // Depending on skip, we need 4 or 2 iterations to store 32x32 bytes. + size_to_write = 4 >> (skip ? 1 : 0); + pStride = pStride << (skip ? 1 : 0); + + xmm0 = _mm256_loadu_si256((__m256i *)(refSamples + topOffset)); + + for (count = 0; count < size_to_write; count ++) { + _mm256_storeu_si256((__m256i *)(predictionPtr), xmm0); + _mm256_storeu_si256((__m256i *)(predictionPtr + pStride), xmm0); + _mm256_storeu_si256((__m256i *)(predictionPtr + 2 * pStride), xmm0); + _mm256_storeu_si256((__m256i *)(predictionPtr + 3 * pStride), xmm0); + + predictionPtr += (pStride << 2); + _mm256_storeu_si256((__m256i *)(predictionPtr), xmm0); + _mm256_storeu_si256((__m256i *)(predictionPtr + pStride), xmm0); + _mm256_storeu_si256((__m256i *)(predictionPtr + 2 * pStride), xmm0); + _mm256_storeu_si256((__m256i *)(predictionPtr + 3 * pStride), xmm0); + + predictionPtr += (pStride << 2); + } + } else if (size == 16) { + __m128i xmm0 = _mm_loadu_si128((__m128i *)(refSamples + topOffset)); + _mm_storeu_si128((__m128i *)predictionPtr, xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); + predictionPtr = predictionPtr + (pStride << 2); + _mm_storeu_si128((__m128i *)predictionPtr, xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); + predictionPtr = predictionPtr + (pStride << 2); + _mm_storeu_si128((__m128i *)predictionPtr, xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); + predictionPtr = predictionPtr + (pStride << 2); + _mm_storeu_si128((__m128i *)predictionPtr, xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); + } + else if (size == 8) { + __m128i xmm0 = _mm_loadl_epi64((__m128i *)(refSamples + topOffset)); + _mm_storel_epi64((__m128i *)(predictionPtr), xmm0); + _mm_storel_epi64((__m128i *)(predictionPtr + pStride), xmm0); + _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), xmm0); + _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), xmm0); + predictionPtr = predictionPtr + (pStride << 2); + _mm_storel_epi64((__m128i *)predictionPtr, xmm0); + _mm_storel_epi64((__m128i *)(predictionPtr + pStride), xmm0); + _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), xmm0); + _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), xmm0); + } + else { + EB_U32 top = *(EB_U32*)(refSamples + topOffset); + *(EB_U32*)(predictionPtr) = top; + *(EB_U32*)(predictionPtr + pStride) = top; + *(EB_U32*)(predictionPtr + 2 * pStride) = top; + *(EB_U32*)(predictionPtr + 3 * pStride) = top; + } + } + else { + pStride <<= 1; + if (size == 32) { + EB_U32 columnIndex, rowIndex; + EB_U32 writeIndex; + EB_U32 topOffset = (size << 1) + 1; + EB_U32 rowStride = skip ? 2 : 1; + + for (columnIndex = 0; columnIndex < size; ++columnIndex) { + writeIndex = columnIndex; + for (rowIndex = 0; rowIndex < size; rowIndex += rowStride) { + predictionPtr[writeIndex] = refSamples[topOffset + columnIndex]; + writeIndex += rowStride * predictionBufferStride; + } + } + } else if (size == 16) { + + __m128i xmm0 = _mm_loadu_si128((__m128i *)(refSamples + topOffset)); + _mm_storeu_si128((__m128i *)(predictionPtr), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); + predictionPtr = predictionPtr + (pStride << 2); + _mm_storeu_si128((__m128i *)(predictionPtr), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); + } + else if (size == 8) { + + __m128i xmm0 = _mm_loadl_epi64((__m128i *)(refSamples + topOffset)); + _mm_storel_epi64((__m128i *)predictionPtr, xmm0); + _mm_storel_epi64((__m128i *)(predictionPtr + pStride), xmm0); + _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), xmm0); + _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), xmm0); + } + else { + EB_U32 top = *(EB_U32*)(refSamples + topOffset); + *(EB_U32*)(predictionPtr) = top; + *(EB_U32*)(predictionPtr + pStride) = top; + } + } +} + +void IntraModeDCChroma_AVX2_INTRIN( + const EB_U32 size, //input parameter, denotes the size of the current PU + EB_U8 *refSamples, //input parameter, pointer to the reference samples + EB_U8 *predictionPtr, //output parameter, pointer to the prediction + const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr + const EB_BOOL skip) //skip one row +{ + __m128i xmm0 = _mm_setzero_si128(); + EB_U32 pStride = predictionBufferStride; + EB_U32 topOffset = (size << 1) + 1; + EB_U32 leftOffset = 0; + + //Jing: + //TODO: add size == 32 for 444 + if (!skip) { + if (size == 32) { + __m256i xmm_sum,xmm_sadleft,xmm_sadtop,xmm_toptmp,xmm_lefttmp ,xmm_set,xmm_sum128_2,xmm_sum256,xmm_predictionDcValue; + __m256i xmm1 = _mm256_setzero_si256(); + __m128i xmm_sumhi,xmm_sumlo,xmm_sum1,xmm_sum128,xmm_sumhitmp,xmm_sumlotmp,xmm_movelotmp,xmm_movehitmp; + + xmm_sumhi = xmm_sumlo = xmm_sum128 = xmm_sumhitmp = xmm_sumlotmp = _mm_setzero_si128(); + xmm_sum = xmm_sadleft = xmm_sadtop = xmm_toptmp = xmm_lefttmp = _mm256_setzero_si256(); + + xmm_toptmp =_mm256_sad_epu8( _mm256_set_m128i ( _mm_loadu_si128( (__m128i *)(refSamples + topOffset +16) ),_mm_loadu_si128((__m128i *)(refSamples + topOffset))),xmm1); + xmm_lefttmp =_mm256_sad_epu8( _mm256_set_m128i( _mm_loadu_si128((__m128i *)(refSamples + leftOffset+16)),_mm_loadu_si128((__m128i *)(refSamples + leftOffset))),xmm1); + + xmm_sum = _mm256_add_epi32(xmm_toptmp, xmm_lefttmp ) ; + xmm_sum = _mm256_hadd_epi32 (xmm_sum,xmm_sum); + xmm_sumlo = _mm256_extracti128_si256(xmm_sum,0); + xmm_sumhi = _mm256_extracti128_si256(xmm_sum,1); + + xmm_movelotmp = _mm_move_epi64 (xmm_sumlo); + xmm_movehitmp = _mm_move_epi64 (xmm_sumhi); + + xmm_sum1 = _mm_add_epi32(xmm_movelotmp,xmm_movehitmp); + + xmm_sum1 = _mm_hadd_epi32(xmm_sum1,xmm_sum1); + + xmm_sum256 = _mm256_castsi128_si256(xmm_sum1); + + xmm_set = _mm256_castsi128_si256(_mm_set1_epi32(32)); + + xmm_sum128_2 = _mm256_add_epi32(xmm_sum256, xmm_set); // add offset + xmm_predictionDcValue = _mm256_srli_epi32(xmm_sum128_2,6); //_mm256_srli_epi32 + + + __m128i dc128 = _mm256_castsi256_si128(xmm_predictionDcValue); + + EB_U8 dc = _mm_cvtsi128_si32 (dc128); + xmm_predictionDcValue = _mm256_set1_epi8(dc);//_mm_broadcastb_epi8 + + + EB_U32 count; + + for (count = 0; count < 2; ++count) { + + _mm256_storeu_si256((__m256i *) predictionPtr, xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 1 * pStride), xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 2 * pStride), xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 3 * pStride), xmm_predictionDcValue); + + predictionPtr += (pStride << 2); + + _mm256_storeu_si256((__m256i *) predictionPtr, xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 1 * pStride), xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 2 * pStride), xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 3 * pStride), xmm_predictionDcValue); + + predictionPtr += (pStride << 2); + + _mm256_storeu_si256((__m256i *) predictionPtr, xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 1 * pStride), xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 2 * pStride), xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 3 * pStride), xmm_predictionDcValue); + + predictionPtr += (pStride << 2); + + _mm256_storeu_si256((__m256i *) predictionPtr, xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 1 * pStride), xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 2 * pStride), xmm_predictionDcValue); + _mm256_storeu_si256((__m256i *)(predictionPtr + 3 * pStride), xmm_predictionDcValue); + + predictionPtr += (pStride << 2); + } + } else if (size == 16) { + __m128i sum, predictionDcValue; + + sum = _mm_add_epi32(_mm_sad_epu8(_mm_loadu_si128((__m128i *)(refSamples + topOffset)), xmm0), + _mm_sad_epu8(_mm_loadu_si128((__m128i *)(refSamples + leftOffset)), xmm0)); + + predictionDcValue = _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(_mm_srli_si128(sum, 8), sum), _mm_cvtsi32_si128(16)), 5); + predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi32(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi64(predictionDcValue, predictionDcValue); + + _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); + + } + else if (size == 8) { + __m128i sum, predictionDcValue; + + sum = _mm_add_epi32(_mm_sad_epu8(_mm_loadl_epi64((__m128i *)(refSamples + topOffset)), xmm0), + _mm_sad_epu8(_mm_loadl_epi64((__m128i *)(refSamples + leftOffset)), xmm0)); + + predictionDcValue = _mm_srli_epi32(_mm_add_epi32(sum, _mm_cvtsi32_si128(8)), 4); + predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi32(predictionDcValue, predictionDcValue); + + _mm_storel_epi64((__m128i *)(predictionPtr), predictionDcValue); + _mm_storel_epi64((__m128i *)(predictionPtr + pStride), predictionDcValue); + _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); + _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); + predictionPtr += (pStride << 2); + _mm_storel_epi64((__m128i *)(predictionPtr), predictionDcValue); + _mm_storel_epi64((__m128i *)(predictionPtr + pStride), predictionDcValue); + _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); + _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); + } + else { + __m128i sum, predictionDcValue; + + sum = _mm_add_epi32(_mm_sad_epu8(_mm_cvtsi32_si128(*(EB_U32*)(refSamples + topOffset)), xmm0), + _mm_sad_epu8(_mm_cvtsi32_si128(*(EB_U32*)(refSamples + leftOffset)), xmm0)); + + predictionDcValue = _mm_srli_epi32(_mm_add_epi32(sum, _mm_cvtsi32_si128(4)), 3); + predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); + + *(EB_U32*)predictionPtr = _mm_cvtsi128_si32(predictionDcValue); + *(EB_U32*)(predictionPtr + pStride) = _mm_cvtsi128_si32(predictionDcValue); + *(EB_U32*)(predictionPtr + 2 * pStride) = _mm_cvtsi128_si32(predictionDcValue); + *(EB_U32*)(predictionPtr + 3 * pStride) = _mm_cvtsi128_si32(predictionDcValue); + } + } + else { + + pStride <<= 1; + if (size == 32) { + EB_U32 sum = 0; + EB_U32 index; + EB_U32 columnIndex, rowIndex; + EB_U32 writeIndex; + EB_U32 leftOffset = 0; + EB_U32 topOffset = (size << 1) + 1; + EB_U32 predictionDcValue = 128; // needs to be changed to a macro based on bit depth + EB_U32 rowStride = skip ? 2 : 1; + + // top reference samples + for (index = 0; index< size; index++) { + sum += refSamples[topOffset + index]; + } + + // left reference samples + for (index = 0; index< size; index++) { + sum += refSamples[leftOffset + index]; + } + + predictionDcValue = (EB_U8)((sum + size) >> Log2f(size << 1)); + + // Generate the prediction + for (rowIndex = 0; rowIndex < size; rowIndex += rowStride) { + writeIndex = rowIndex * predictionBufferStride; + for (columnIndex = 0; columnIndex < size; ++columnIndex) { + predictionPtr[writeIndex] = (EB_U8)predictionDcValue; + ++writeIndex; + } + } + + } else if (size == 16) { + + __m128i sum, predictionDcValue; + + sum = _mm_add_epi32(_mm_sad_epu8(_mm_loadu_si128((__m128i *)(refSamples + topOffset)), xmm0), + _mm_sad_epu8(_mm_loadu_si128((__m128i *)(refSamples + leftOffset)), xmm0)); + + predictionDcValue = _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(_mm_srli_si128(sum, 8), sum), _mm_cvtsi32_si128(16)), 5); + predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi32(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi64(predictionDcValue, predictionDcValue); + + _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); + } + else if (size == 8) { + __m128i sum, predictionDcValue; + + sum = _mm_add_epi32(_mm_sad_epu8(_mm_loadl_epi64((__m128i *)(refSamples + topOffset)), xmm0), + _mm_sad_epu8(_mm_loadl_epi64((__m128i *)(refSamples + leftOffset)), xmm0)); + + predictionDcValue = _mm_srli_epi32(_mm_add_epi32(sum, _mm_cvtsi32_si128(8)), 4); + predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi32(predictionDcValue, predictionDcValue); + + _mm_storel_epi64((__m128i *)(predictionPtr), predictionDcValue); + _mm_storel_epi64((__m128i *)(predictionPtr + pStride), predictionDcValue); + _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); + _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); + } + else { + __m128i sum, predictionDcValue; + + sum = _mm_add_epi32(_mm_sad_epu8(_mm_cvtsi32_si128(*(EB_U32*)(refSamples + topOffset)), xmm0), + _mm_sad_epu8(_mm_cvtsi32_si128(*(EB_U32*)(refSamples + leftOffset)), xmm0)); + + predictionDcValue = _mm_srli_epi32(_mm_add_epi32(sum, _mm_cvtsi32_si128(4)), 3); + predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); + predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); + + *(EB_U32*)predictionPtr = _mm_cvtsi128_si32(predictionDcValue); + *(EB_U32*)(predictionPtr + pStride) = _mm_cvtsi128_si32(predictionDcValue); + } + } +} + diff --git a/Source/Lib/ASM_AVX2/EbNoiseExtractAVX2.c b/Source/Lib/ASM_AVX2/EbNoiseExtractAVX2.c index 91107b4f7..aaa1813e9 100644 --- a/Source/Lib/ASM_AVX2/EbNoiseExtractAVX2.c +++ b/Source/Lib/ASM_AVX2/EbNoiseExtractAVX2.c @@ -741,30 +741,35 @@ void noiseExtractChromaStrong_AVX2_INTRIN( EB_U8 *ptrDenoised, *ptrDenoisedInterm, *ptrDenoisedCr, *ptrDenoisedIntermCr; EB_U32 strideOut, strideOutCr; + + EB_U32 colorFormat=inputPicturePtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat==EB_YUV444?1:2)-1; + EB_U16 subHeightCMinus1 = (colorFormat>=EB_YUV422?1:2)-1; + __m256i top, curr, bottom, currPrev, currNext, topPrev, topNext, bottomPrev, bottomNext, topCr, currCr, bottomCr, currPrevCr, currNextCr, topPrevCr, topNextCr, bottomPrevCr, bottomNextCr; (void)lcuOriginX; { - picHeight = inputPicturePtr->height / 2; - picWidth = inputPicturePtr->width / 2; - lcuHeight = MIN(MAX_LCU_SIZE / 2, picHeight - lcuOriginY); + picHeight = inputPicturePtr->height >> subHeightCMinus1; + picWidth = inputPicturePtr->width >> subWidthCMinus1; + lcuHeight = MIN(MAX_LCU_SIZE >> subHeightCMinus1, picHeight - lcuOriginY); - lcuHeight = ((lcuOriginY + MAX_LCU_SIZE / 2 >= picHeight) || (lcuOriginY == 0)) ? lcuHeight - 1 : lcuHeight; + lcuHeight = ((lcuOriginY + (MAX_LCU_SIZE >> subHeightCMinus1) >= picHeight) || (lcuOriginY == 0)) ? lcuHeight - 1 : lcuHeight; strideIn = inputPicturePtr->strideCb; - inputOriginIndex = inputPicturePtr->originX / 2 + (inputPicturePtr->originY / 2 + lcuOriginY) * inputPicturePtr->strideCb; + inputOriginIndex = (inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * inputPicturePtr->strideCb; ptrIn = &(inputPicturePtr->bufferCb[inputOriginIndex]); - inputOriginIndexPad = denoisedPicturePtr->originX / 2 + (denoisedPicturePtr->originY / 2 + lcuOriginY) * denoisedPicturePtr->strideCb; + inputOriginIndexPad = (denoisedPicturePtr->originX >> subWidthCMinus1) + ((denoisedPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * denoisedPicturePtr->strideCb; strideOut = denoisedPicturePtr->strideCb; ptrDenoised = &(denoisedPicturePtr->bufferCb[inputOriginIndexPad]); ptrDenoisedInterm = ptrDenoised; strideInCr = inputPicturePtr->strideCr; - inputOriginIndex = inputPicturePtr->originX / 2 + (inputPicturePtr->originY / 2 + lcuOriginY) * inputPicturePtr->strideCr; + inputOriginIndex = (inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * inputPicturePtr->strideCr; ptrInCr = &(inputPicturePtr->bufferCr[inputOriginIndex]); - inputOriginIndexPad = denoisedPicturePtr->originX / 2 + (denoisedPicturePtr->originY / 2 + lcuOriginY) * denoisedPicturePtr->strideCr; + inputOriginIndexPad = (denoisedPicturePtr->originX >> subWidthCMinus1) + ((denoisedPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * denoisedPicturePtr->strideCr; strideOutCr = denoisedPicturePtr->strideCr; ptrDenoisedCr = &(denoisedPicturePtr->bufferCr[inputOriginIndexPad]); ptrDenoisedIntermCr = ptrDenoisedCr; @@ -879,7 +884,7 @@ void noiseExtractChromaStrong_AVX2_INTRIN( } - lcuHeight = MIN(MAX_LCU_SIZE / 2, picHeight - lcuOriginY); + lcuHeight = MIN(MAX_LCU_SIZE >> subHeightCMinus1, picHeight - lcuOriginY); for (jj = 0; jj < lcuHeight; jj++){ for (ii = 0; ii < picWidth; ii++){ @@ -919,36 +924,42 @@ void noiseExtractChromaWeak_AVX2_INTRIN( EB_U32 strideOut, strideOutCr; + EB_U32 colorFormat=inputPicturePtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat==EB_YUV444?1:2)-1; + EB_U16 subHeightCMinus1 = (colorFormat>=EB_YUV422?1:2)-1; + __m256i top, curr, bottom, currPrev, currNext, topPrev, topNext, bottomPrev, bottomNext, topCr, currCr, bottomCr, currPrevCr, currNextCr, topPrevCr, topNextCr, bottomPrevCr, bottomNextCr; (void)lcuOriginX; + ////gaussian matrix(Chroma) //a = (1 * p[0] + 2 * p[1] + 1 * p[2] + // 2 * p[0 + stride] + 4 * p[1 + stride] + 2 * p[2 + stride] + // 1 * p[0 + 2 * stride] + 2 * p[1 + 2 * stride] + 1 * p[2 + 2 * stride]) / 16; { - picHeight = inputPicturePtr->height / 2; - picWidth = inputPicturePtr->width / 2; + picHeight = inputPicturePtr->height >> subHeightCMinus1; + picWidth = inputPicturePtr->width >> subWidthCMinus1 ; + - lcuHeight = MIN(MAX_LCU_SIZE / 2, picHeight - lcuOriginY); + lcuHeight = MIN(MAX_LCU_SIZE >> subHeightCMinus1, picHeight - lcuOriginY); - lcuHeight = ((lcuOriginY + MAX_LCU_SIZE / 2 >= picHeight) || (lcuOriginY == 0)) ? lcuHeight - 1 : lcuHeight; + lcuHeight = ((lcuOriginY + (MAX_LCU_SIZE >> subHeightCMinus1) >= picHeight) || (lcuOriginY == 0)) ? lcuHeight - 1 : lcuHeight; strideIn = inputPicturePtr->strideCb; - inputOriginIndex = inputPicturePtr->originX / 2 + (inputPicturePtr->originY / 2 + lcuOriginY)* inputPicturePtr->strideCb; + inputOriginIndex = (inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) + lcuOriginY)* inputPicturePtr->strideCb; ptrIn = &(inputPicturePtr->bufferCb[inputOriginIndex]); - inputOriginIndexPad = denoisedPicturePtr->originX / 2 + (denoisedPicturePtr->originY / 2 + lcuOriginY)* denoisedPicturePtr->strideCb; + inputOriginIndexPad = (denoisedPicturePtr->originX >> subWidthCMinus1) + ((denoisedPicturePtr->originY >> subHeightCMinus1) + lcuOriginY)* denoisedPicturePtr->strideCb; strideOut = denoisedPicturePtr->strideCb; ptrDenoised = &(denoisedPicturePtr->bufferCb[inputOriginIndexPad]); ptrDenoisedInterm = ptrDenoised; strideInCr = inputPicturePtr->strideCr; - inputOriginIndex = inputPicturePtr->originX / 2 + (inputPicturePtr->originY / 2 + lcuOriginY) * inputPicturePtr->strideCr; + inputOriginIndex = (inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * inputPicturePtr->strideCr; ptrInCr = &(inputPicturePtr->bufferCr[inputOriginIndex]); - inputOriginIndexPad = denoisedPicturePtr->originX / 2 + (denoisedPicturePtr->originY / 2 + lcuOriginY) * denoisedPicturePtr->strideCr; + inputOriginIndexPad = (denoisedPicturePtr->originX >> subWidthCMinus1) + ((denoisedPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * denoisedPicturePtr->strideCr; strideOutCr = denoisedPicturePtr->strideCr; ptrDenoisedCr = &(denoisedPicturePtr->bufferCr[inputOriginIndexPad]); ptrDenoisedIntermCr = ptrDenoisedCr; @@ -1059,7 +1070,7 @@ void noiseExtractChromaWeak_AVX2_INTRIN( } - lcuHeight = MIN(MAX_LCU_SIZE / 2, picHeight - lcuOriginY); + lcuHeight = MIN(MAX_LCU_SIZE >> subHeightCMinus1, picHeight - lcuOriginY); for (jj = 0; jj < lcuHeight; jj++){ for (ii = 0; ii < picWidth; ii++){ diff --git a/Source/Lib/ASM_SSE2/EbIntraPrediction16bit_Intrinsic_SSE2.c b/Source/Lib/ASM_SSE2/EbIntraPrediction16bit_Intrinsic_SSE2.c index bce75f7c7..1a397b84e 100644 --- a/Source/Lib/ASM_SSE2/EbIntraPrediction16bit_Intrinsic_SSE2.c +++ b/Source/Lib/ASM_SSE2/EbIntraPrediction16bit_Intrinsic_SSE2.c @@ -226,8 +226,60 @@ void IntraModeVerticalChroma16bit_SSE2_INTRIN( { EB_U32 pStride = predictionBufferStride; EB_U32 topOffset = (size << 1) + 1; - if (!skip) { + if (size == 32) { + __m128i top_0_7 = _mm_loadu_si128((__m128i *)(refSamples + topOffset)); + __m128i top_8_15 = _mm_loadu_si128((__m128i *)(refSamples + topOffset + 8)); + __m128i top_16_23 = _mm_loadu_si128((__m128i *)(refSamples + topOffset + 16)); + __m128i top_24_31 = _mm_loadu_si128((__m128i *)(refSamples + topOffset + 24)); + EB_U64 count, size_to_write; + + pStride <<= (skip ? 1 : 0); + + // Each 2 storeu calls stores 32 bytes. Hence each iteration stores 8 * 32 bytes. + // Depending on skip, we need 4 or 2 iterations to store 32x32 bytes. + size_to_write = 4 >> (skip ? 1 : 0); + + for (count = 0; count < size_to_write; ++count) { + _mm_storeu_si128((__m128i *)(predictionPtr), top_0_7); + _mm_storeu_si128((__m128i *)(predictionPtr+8), top_8_15); + _mm_storeu_si128((__m128i *)(predictionPtr+16), top_16_23); + _mm_storeu_si128((__m128i *)(predictionPtr+24), top_24_31); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride), top_0_7); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+8), top_8_15); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+16), top_16_23); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+24), top_24_31); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride), top_0_7); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+8), top_8_15); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+16), top_16_23); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+24), top_24_31); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride), top_0_7); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+8), top_8_15); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+16), top_16_23); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+24), top_24_31); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)(predictionPtr), top_0_7); + _mm_storeu_si128((__m128i *)(predictionPtr+8), top_8_15); + _mm_storeu_si128((__m128i *)(predictionPtr+16), top_16_23); + _mm_storeu_si128((__m128i *)(predictionPtr+24), top_24_31); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride), top_0_7); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+8), top_8_15); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+16), top_16_23); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+24), top_24_31); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride), top_0_7); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+8), top_8_15); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+16), top_16_23); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+24), top_24_31); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride), top_0_7); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+8), top_8_15); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+16), top_16_23); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+24), top_24_31); + predictionPtr += (pStride << 2); + } + return; + } + + if (!skip) { if (size == 16) { __m128i top_0_7 = _mm_loadu_si128((__m128i *)(refSamples + topOffset)); __m128i top_8_15 = _mm_loadu_si128((__m128i *)(refSamples + topOffset + 8)); @@ -708,10 +760,120 @@ void IntraModeHorizontalChroma16bit_SSE2_INTRIN( { EB_U32 pStride = predictionBufferStride; EB_U32 leftOffset = 0; - + if (!skip) { - - if (size == 16) { + if (size == 32) { + EB_U32 count; + __m128i left0_7, left8_15, left0_3, left4_7, left8_11, left12_15, left01, left23, left45, left67, left89, left10_11; + __m128i left12_13, left14_15, left0, left1, left2, left3, left4, left5, left6, left7, left8, left9, left10, left11; + __m128i left12, left13, left14, left15; + + for (count = 0; count < 2; ++count) { + left0_7 = _mm_loadu_si128((__m128i *)(refSamples + leftOffset)); + left8_15 = _mm_loadu_si128((__m128i *)(refSamples + leftOffset + 8)); + refSamples += 16; + + left0_3 = _mm_unpacklo_epi16(left0_7, left0_7); + left4_7 = _mm_unpackhi_epi16(left0_7, left0_7); + left8_11 = _mm_unpacklo_epi16(left8_15, left8_15); + left12_15 = _mm_unpackhi_epi16(left8_15, left8_15); + + left01 = _mm_unpacklo_epi32(left0_3, left0_3); + left23 = _mm_unpackhi_epi32(left0_3, left0_3); + left45 = _mm_unpacklo_epi32(left4_7, left4_7); + left67 = _mm_unpackhi_epi32(left4_7, left4_7); + left89 = _mm_unpacklo_epi32(left8_11, left8_11); + left10_11 = _mm_unpackhi_epi32(left8_11, left8_11); + left12_13 = _mm_unpacklo_epi32(left12_15, left12_15); + left14_15 = _mm_unpackhi_epi32(left12_15, left12_15); + + left0 = _mm_unpacklo_epi64(left01, left01); + left1 = _mm_unpackhi_epi64(left01, left01); + left2 = _mm_unpacklo_epi64(left23, left23); + left3 = _mm_unpackhi_epi64(left23, left23); + left4 = _mm_unpacklo_epi64(left45, left45); + left5 = _mm_unpackhi_epi64(left45, left45); + left6 = _mm_unpacklo_epi64(left67, left67); + left7 = _mm_unpackhi_epi64(left67, left67); + left8 = _mm_unpacklo_epi64(left89, left89); + left9 = _mm_unpackhi_epi64(left89, left89); + left10 = _mm_unpacklo_epi64(left10_11, left10_11); + left11 = _mm_unpackhi_epi64(left10_11, left10_11); + left12 = _mm_unpacklo_epi64(left12_13, left12_13); + left13 = _mm_unpackhi_epi64(left12_13, left12_13); + left14 = _mm_unpacklo_epi64(left14_15, left14_15); + left15 = _mm_unpackhi_epi64(left14_15, left14_15); + + _mm_storeu_si128((__m128i *)(predictionPtr), left0); + _mm_storeu_si128((__m128i *)(predictionPtr + 8), left0); + _mm_storeu_si128((__m128i *)(predictionPtr + 16), left0); + _mm_storeu_si128((__m128i *)(predictionPtr + 24), left0); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), left1); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 8), left1); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 16), left1); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 24), left1); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), left2); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 8), left2); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 16), left2); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 24), left2); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), left3); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 8), left3); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 16), left3); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 24), left3); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)(predictionPtr), left4); + _mm_storeu_si128((__m128i *)(predictionPtr + 8), left4); + _mm_storeu_si128((__m128i *)(predictionPtr + 16), left4); + _mm_storeu_si128((__m128i *)(predictionPtr + 24), left4); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), left5); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 8), left5); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 16), left5); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 24), left5); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), left6); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 8), left6); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 16), left6); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 24), left6); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), left7); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 8), left7); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 16), left7); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 24), left7); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)(predictionPtr), left8); + _mm_storeu_si128((__m128i *)(predictionPtr + 8), left8); + _mm_storeu_si128((__m128i *)(predictionPtr + 16), left8); + _mm_storeu_si128((__m128i *)(predictionPtr + 24), left8); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), left9); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 8), left9); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 16), left9); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 24), left9); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), left10); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 8), left10); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 16), left10); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 24), left10); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), left11); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 8), left11); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 16), left11); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 24), left11); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)(predictionPtr), left12); + _mm_storeu_si128((__m128i *)(predictionPtr + 8), left12); + _mm_storeu_si128((__m128i *)(predictionPtr + 16), left12); + _mm_storeu_si128((__m128i *)(predictionPtr + 24), left12); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride), left13); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 8), left13); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 16), left13); + _mm_storeu_si128((__m128i *)(predictionPtr + pStride + 24), left13); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), left14); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 8), left14); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 16), left14); + _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride + 24), left14); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), left15); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 8), left15); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 16), left15); + _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride + 24), left15); + predictionPtr += (pStride << 2); + } + } else if (size == 16) { __m128i left0_7, left8_15, left0_3, left4_7, left8_11, left12_15; __m128i left_01, left_23, left_45, left_67, left_89, left_10_11, left_12_13, left_14_15; __m128i left0, left1, left2, left3, left4, left5, left6, left7, left8, left9, left10, left11, left12, left13, left14, left15; @@ -838,7 +1000,13 @@ void IntraModeHorizontalChroma16bit_SSE2_INTRIN( else { pStride <<= 1; __m128i skip_mask = _mm_set1_epi32(0x0000FFFF); - if (size == 16) { + if (size == 32) { + __m128i left0_14_even, left_0_6_even, left_8_14_even, left02, left46, left8_10, left12_14; + __m128i left0, left2, left4, left6, left8, left10, left12, left14; + MACRO_HORIZONTAL_LUMA_32X16(0) + predictionPtr += (pStride << 2); + MACRO_HORIZONTAL_LUMA_32X16(16) + } else if (size == 16) { __m128i left0_14_even, left0_6_even, left8_14_even, left02, left46, left8_10, left12_14; __m128i left0, left2, left4, left6, left8, left10, left12, left14; diff --git a/Source/Lib/ASM_SSE2/EbIntraPrediction_Intrinsic_SSE2.c b/Source/Lib/ASM_SSE2/EbIntraPrediction_Intrinsic_SSE2.c index 5be8ff280..f7a4320cd 100644 --- a/Source/Lib/ASM_SSE2/EbIntraPrediction_Intrinsic_SSE2.c +++ b/Source/Lib/ASM_SSE2/EbIntraPrediction_Intrinsic_SSE2.c @@ -95,90 +95,6 @@ _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride), left14);\ _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+16), left14); -void IntraModeVerticalChroma_SSE2_INTRIN( - const EB_U32 size, //input parameter, denotes the size of the current PU - EB_U8 *refSamples, //input parameter, pointer to the reference samples - EB_U8 *predictionPtr, //output parameter, pointer to the prediction - const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr - const EB_BOOL skip) //skip one row -{ - EB_U32 pStride = predictionBufferStride; - EB_U32 topOffset = (size << 1) + 1; - - if (!skip) { - - if (size == 16) { - __m128i xmm0 = _mm_loadu_si128((__m128i *)(refSamples + topOffset)); - _mm_storeu_si128((__m128i *)predictionPtr, xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); - predictionPtr = predictionPtr + (pStride << 2); - _mm_storeu_si128((__m128i *)predictionPtr, xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); - predictionPtr = predictionPtr + (pStride << 2); - _mm_storeu_si128((__m128i *)predictionPtr, xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); - predictionPtr = predictionPtr + (pStride << 2); - _mm_storeu_si128((__m128i *)predictionPtr, xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); - } - else if (size == 8) { - __m128i xmm0 = _mm_loadl_epi64((__m128i *)(refSamples + topOffset)); - _mm_storel_epi64((__m128i *)(predictionPtr), xmm0); - _mm_storel_epi64((__m128i *)(predictionPtr + pStride), xmm0); - _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), xmm0); - _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), xmm0); - predictionPtr = predictionPtr + (pStride << 2); - _mm_storel_epi64((__m128i *)predictionPtr, xmm0); - _mm_storel_epi64((__m128i *)(predictionPtr + pStride), xmm0); - _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), xmm0); - _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), xmm0); - } - else { - EB_U32 top = *(EB_U32*)(refSamples + topOffset); - *(EB_U32*)(predictionPtr) = top; - *(EB_U32*)(predictionPtr + pStride) = top; - *(EB_U32*)(predictionPtr + 2 * pStride) = top; - *(EB_U32*)(predictionPtr + 3 * pStride) = top; - } - } - else { - pStride <<= 1; - if (size == 16) { - - __m128i xmm0 = _mm_loadu_si128((__m128i *)(refSamples + topOffset)); - _mm_storeu_si128((__m128i *)(predictionPtr), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); - predictionPtr = predictionPtr + (pStride << 2); - _mm_storeu_si128((__m128i *)(predictionPtr), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), xmm0); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), xmm0); - } - else if (size == 8) { - - __m128i xmm0 = _mm_loadl_epi64((__m128i *)(refSamples + topOffset)); - _mm_storel_epi64((__m128i *)predictionPtr, xmm0); - _mm_storel_epi64((__m128i *)(predictionPtr + pStride), xmm0); - _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), xmm0); - _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), xmm0); - } - else { - EB_U32 top = *(EB_U32*)(refSamples + topOffset); - *(EB_U32*)(predictionPtr) = top; - *(EB_U32*)(predictionPtr + pStride) = top; - } - } -} void IntraModeHorizontalLuma_SSE2_INTRIN( const EB_U32 size, //input parameter, denotes the size of the current PU @@ -492,10 +408,91 @@ void IntraModeHorizontalChroma_SSE2_INTRIN( { EB_U32 pStride = predictionBufferStride; EB_U32 leftOffset = 0; + //Jing: + //Add size == 32 for 444 + __m128i left0_15, left0_7, left8_15, left0_3, left4_7, left8_11, left12_15, left01, left23, left45, left67, left89, left10_11; + __m128i left12_13, left14_15, left0, left1, left2, left3, left4, left5, left6, left7, left8, left9, left10, left11; + __m128i left12, left13, left14, left15; if (!skip) { + if (size == 32) { + EB_U8 count; + for (count = 0; count < 2; ++count) { + left0_15 = _mm_loadu_si128((__m128i *)(refSamples + leftOffset)); + refSamples += 16; + + left0_7 = _mm_unpacklo_epi8(left0_15, left0_15); + left8_15 = _mm_unpackhi_epi8(left0_15, left0_15); - if (size == 16) { + left0_3 = _mm_unpacklo_epi16(left0_7, left0_7); + left4_7 = _mm_unpackhi_epi16(left0_7, left0_7); + left8_11 = _mm_unpacklo_epi16(left8_15, left8_15); + left12_15 = _mm_unpackhi_epi16(left8_15, left8_15); + + left01 = _mm_unpacklo_epi32(left0_3, left0_3); + left23 = _mm_unpackhi_epi32(left0_3, left0_3); + left45 = _mm_unpacklo_epi32(left4_7, left4_7); + left67 = _mm_unpackhi_epi32(left4_7, left4_7); + left89 = _mm_unpacklo_epi32(left8_11, left8_11); + left10_11 = _mm_unpackhi_epi32(left8_11, left8_11); + left12_13 = _mm_unpacklo_epi32(left12_15, left12_15); + left14_15 = _mm_unpackhi_epi32(left12_15, left12_15); + + left0 = _mm_unpacklo_epi64(left01, left01); + left1 = _mm_unpackhi_epi64(left01, left01); + left2 = _mm_unpacklo_epi64(left23, left23); + left3 = _mm_unpackhi_epi64(left23, left23); + left4 = _mm_unpacklo_epi64(left45, left45); + left5 = _mm_unpackhi_epi64(left45, left45); + left6 = _mm_unpacklo_epi64(left67, left67); + left7 = _mm_unpackhi_epi64(left67, left67); + left8 = _mm_unpacklo_epi64(left89, left89); + left9 = _mm_unpackhi_epi64(left89, left89); + left10 = _mm_unpacklo_epi64(left10_11, left10_11); + left11 = _mm_unpackhi_epi64(left10_11, left10_11); + left12 = _mm_unpacklo_epi64(left12_13, left12_13); + left13 = _mm_unpackhi_epi64(left12_13, left12_13); + left14 = _mm_unpacklo_epi64(left14_15, left14_15); + left15 = _mm_unpackhi_epi64(left14_15, left14_15); + + _mm_storeu_si128((__m128i *)predictionPtr, left0); + _mm_storeu_si128((__m128i *)(predictionPtr + 16), left0); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride), left1); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+16), left1); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride), left2); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+16), left2); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride), left3); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+16), left3); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)predictionPtr, left4); + _mm_storeu_si128((__m128i *)(predictionPtr + 16), left4); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride), left5); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+16), left5); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride), left6); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+16), left6); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride), left7); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+16), left7); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)predictionPtr, left8); + _mm_storeu_si128((__m128i *)(predictionPtr + 16), left8); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride), left9); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+16), left9); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride), left10); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+16), left10); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride), left11); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+16), left11); + predictionPtr += (pStride << 2); + _mm_storeu_si128((__m128i *)predictionPtr, left12); + _mm_storeu_si128((__m128i *)(predictionPtr + 16), left12); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride), left13); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+16), left13); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride), left14); + _mm_storeu_si128((__m128i *)(predictionPtr+2*pStride+16), left14); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride), left15); + _mm_storeu_si128((__m128i *)(predictionPtr+3*pStride+16), left15); + predictionPtr += (pStride << 2); + } + } else if (size == 16) { __m128i xmm0, xmm2, xmm4, xmm6, xmm8, xmm10, xmm12, xmm14; xmm0 = _mm_loadu_si128((__m128i *)(refSamples+leftOffset)); xmm8 = _mm_unpackhi_epi8(xmm0, xmm0); @@ -570,7 +567,14 @@ void IntraModeHorizontalChroma_SSE2_INTRIN( else { pStride <<= 1; __m128i xmm15 = _mm_set1_epi16(0x00FF); - if (size == 16) { + if (size == 32) { + __m128i left0_14_even, left0_6_even, left8_14_even, left8_10, left12_14, left02, left46, skip_mask; + skip_mask = _mm_set1_epi16(0x00FF); + pStride <<= 1; + MACRO_HORIZONTAL_LUMA_32X16(0) + predictionPtr += (pStride << 2); + MACRO_HORIZONTAL_LUMA_32X16(16) + } else if (size == 16) { __m128i xmm0, xmm2, xmm4, xmm6; xmm0 = _mm_and_si128(_mm_loadu_si128((__m128i *)(refSamples + leftOffset)), xmm15); @@ -622,149 +626,6 @@ void IntraModeHorizontalChroma_SSE2_INTRIN( } } -void IntraModeDCChroma_SSE2_INTRIN( - const EB_U32 size, //input parameter, denotes the size of the current PU - EB_U8 *refSamples, //input parameter, pointer to the reference samples - EB_U8 *predictionPtr, //output parameter, pointer to the prediction - const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr - const EB_BOOL skip) //skip one row -{ - __m128i xmm0 = _mm_setzero_si128(); - EB_U32 pStride = predictionBufferStride; - EB_U32 topOffset = (size << 1) + 1; - EB_U32 leftOffset = 0; - - if (!skip) { - - if (size == 16) { - __m128i sum, predictionDcValue; - - sum = _mm_add_epi32(_mm_sad_epu8(_mm_loadu_si128((__m128i *)(refSamples + topOffset)), xmm0), - _mm_sad_epu8(_mm_loadu_si128((__m128i *)(refSamples + leftOffset)), xmm0)); - - predictionDcValue = _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(_mm_srli_si128(sum, 8), sum), _mm_cvtsi32_si128(16)), 5); - predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi32(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi64(predictionDcValue, predictionDcValue); - - _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); - predictionPtr += (pStride << 2); - _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); - predictionPtr += (pStride << 2); - _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); - predictionPtr += (pStride << 2); - _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); - - } - else if (size == 8) { - __m128i sum, predictionDcValue; - - sum = _mm_add_epi32(_mm_sad_epu8(_mm_loadl_epi64((__m128i *)(refSamples + topOffset)), xmm0), - _mm_sad_epu8(_mm_loadl_epi64((__m128i *)(refSamples + leftOffset)), xmm0)); - - predictionDcValue = _mm_srli_epi32(_mm_add_epi32(sum, _mm_cvtsi32_si128(8)), 4); - predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi32(predictionDcValue, predictionDcValue); - - _mm_storel_epi64((__m128i *)(predictionPtr), predictionDcValue); - _mm_storel_epi64((__m128i *)(predictionPtr + pStride), predictionDcValue); - _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); - _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); - predictionPtr += (pStride << 2); - _mm_storel_epi64((__m128i *)(predictionPtr), predictionDcValue); - _mm_storel_epi64((__m128i *)(predictionPtr + pStride), predictionDcValue); - _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); - _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); - } - else { - __m128i sum, predictionDcValue; - - sum = _mm_add_epi32(_mm_sad_epu8(_mm_cvtsi32_si128(*(EB_U32*)(refSamples + topOffset)), xmm0), - _mm_sad_epu8(_mm_cvtsi32_si128(*(EB_U32*)(refSamples + leftOffset)), xmm0)); - - predictionDcValue = _mm_srli_epi32(_mm_add_epi32(sum, _mm_cvtsi32_si128(4)), 3); - predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); - - *(EB_U32*)predictionPtr = _mm_cvtsi128_si32(predictionDcValue); - *(EB_U32*)(predictionPtr + pStride) = _mm_cvtsi128_si32(predictionDcValue); - *(EB_U32*)(predictionPtr + 2 * pStride) = _mm_cvtsi128_si32(predictionDcValue); - *(EB_U32*)(predictionPtr + 3 * pStride) = _mm_cvtsi128_si32(predictionDcValue); - } - } - else { - - pStride <<= 1; - - if (size == 16) { - - __m128i sum, predictionDcValue; - - sum = _mm_add_epi32(_mm_sad_epu8(_mm_loadu_si128((__m128i *)(refSamples + topOffset)), xmm0), - _mm_sad_epu8(_mm_loadu_si128((__m128i *)(refSamples + leftOffset)), xmm0)); - - predictionDcValue = _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(_mm_srli_si128(sum, 8), sum), _mm_cvtsi32_si128(16)), 5); - predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi32(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi64(predictionDcValue, predictionDcValue); - - _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); - predictionPtr += (pStride << 2); - _mm_storeu_si128((__m128i *)(predictionPtr), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); - _mm_storeu_si128((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); - } - else if (size == 8) { - __m128i sum, predictionDcValue; - - sum = _mm_add_epi32(_mm_sad_epu8(_mm_loadl_epi64((__m128i *)(refSamples + topOffset)), xmm0), - _mm_sad_epu8(_mm_loadl_epi64((__m128i *)(refSamples + leftOffset)), xmm0)); - - predictionDcValue = _mm_srli_epi32(_mm_add_epi32(sum, _mm_cvtsi32_si128(8)), 4); - predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi32(predictionDcValue, predictionDcValue); - - _mm_storel_epi64((__m128i *)(predictionPtr), predictionDcValue); - _mm_storel_epi64((__m128i *)(predictionPtr + pStride), predictionDcValue); - _mm_storel_epi64((__m128i *)(predictionPtr + 2 * pStride), predictionDcValue); - _mm_storel_epi64((__m128i *)(predictionPtr + 3 * pStride), predictionDcValue); - } - else { - __m128i sum, predictionDcValue; - - sum = _mm_add_epi32(_mm_sad_epu8(_mm_cvtsi32_si128(*(EB_U32*)(refSamples + topOffset)), xmm0), - _mm_sad_epu8(_mm_cvtsi32_si128(*(EB_U32*)(refSamples + leftOffset)), xmm0)); - - predictionDcValue = _mm_srli_epi32(_mm_add_epi32(sum, _mm_cvtsi32_si128(4)), 3); - predictionDcValue = _mm_unpacklo_epi8(predictionDcValue, predictionDcValue); - predictionDcValue = _mm_unpacklo_epi16(predictionDcValue, predictionDcValue); - - *(EB_U32*)predictionPtr = _mm_cvtsi128_si32(predictionDcValue); - *(EB_U32*)(predictionPtr + pStride) = _mm_cvtsi128_si32(predictionDcValue); - } - } -} - void IntraModePlanar16bit_SSE2_INTRIN( const EB_U32 size, //input parameter, denotes the size of the current PU EB_U16 *refSamples, //input parameter, pointer to the reference samples diff --git a/Source/Lib/ASM_SSE2/EbIntraPrediction_SSE2.h b/Source/Lib/ASM_SSE2/EbIntraPrediction_SSE2.h index 7cf0fc3e7..726b6a433 100644 --- a/Source/Lib/ASM_SSE2/EbIntraPrediction_SSE2.h +++ b/Source/Lib/ASM_SSE2/EbIntraPrediction_SSE2.h @@ -22,13 +22,6 @@ extern void IntraModeVerticalLuma16bit_SSE2_INTRIN( const EB_U32 predictionBufferStride, const EB_BOOL skip); -extern void IntraModeVerticalChroma_SSE2_INTRIN( - const EB_U32 size, //input parameter, denotes the size of the current PU - EB_U8 *refSamples, //input parameter, pointer to the reference samples - EB_U8 *predictionPtr, //output parameter, pointer to the prediction - const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr - const EB_BOOL skip //skip one row - ); extern void IntraModeVerticalChroma16bit_SSE2_INTRIN( @@ -76,13 +69,6 @@ void IntraModePlanar16bit_SSE2_INTRIN( const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr const EB_BOOL skip); //skip half rows -extern void IntraModeDCChroma_SSE2_INTRIN( - const EB_U32 size, //input parameter, denotes the size of the current PU - EB_U8 *refSamples, //input parameter, pointer to the reference samples - EB_U8 *predictionPtr, //output parameter, pointer to the prediction - const EB_U32 predictionBufferStride, //input parameter, denotes the stride for the prediction ptr - const EB_BOOL skip); //skip one row - extern void IntraModeAngular16bit_34_SSE2_INTRIN( const EB_U32 size, //input parameter, denotes the size of the current PU EB_U16 *refSamples, //input parameter, pointer to the reference samples diff --git a/Source/Lib/ASM_SSSE3/EbIntraPrediction16bit_Intrinsic_SSSE3.c b/Source/Lib/ASM_SSSE3/EbIntraPrediction16bit_Intrinsic_SSSE3.c index 344baf729..863cb50b0 100644 --- a/Source/Lib/ASM_SSSE3/EbIntraPrediction16bit_Intrinsic_SSSE3.c +++ b/Source/Lib/ASM_SSSE3/EbIntraPrediction16bit_Intrinsic_SSSE3.c @@ -62,7 +62,7 @@ EB_EXTERN void IntraModeDCChroma16bit_SSSE3_INTRIN( _mm_storeu_si128((__m128i *)(predictionPtr+pStride), sum); } } - else {/* if (size == 16) {*/ + else if (size == 16 ) { __m128i sum = _mm_setr_epi16(16, 0, 0, 0, 0, 0, 0, 0); sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)refSamples)); sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)(refSamples+8))); @@ -116,5 +116,33 @@ EB_EXTERN void IntraModeDCChroma16bit_SSSE3_INTRIN( _mm_storeu_si128((__m128i *)(predictionPtr+pStride), sum); _mm_storeu_si128((__m128i *)(predictionPtr+pStride+8), sum); } + } else { ///* if (size == 32) {*/ + __m128i sum = _mm_setr_epi16(32, 0, 0, 0, 0, 0, 0, 0); + sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)refSamples)); + sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)(refSamples+8))); + sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)(refSamples+16))); + sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)(refSamples+24))); + sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)(refSamples+topOffset))); + sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)(refSamples+topOffset+8))); + sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)(refSamples+topOffset+16))); + sum = _mm_add_epi16(sum, _mm_loadu_si128((__m128i *)(refSamples+topOffset+24))); + sum = _mm_hadd_epi16(sum, sum); + sum = _mm_hadd_epi16(sum, sum); + sum = _mm_hadd_epi16(sum, sum); + sum = _mm_srli_epi16(sum, 6); + sum = _mm_unpacklo_epi16(sum, sum); + sum = _mm_unpacklo_epi32(sum, sum); + sum = _mm_unpacklo_epi64(sum, sum); + for (unsigned int i=0; i<(EB_U32)(skip ? 8: 16); i++) { + _mm_storeu_si128((__m128i *)predictionPtr, sum); + _mm_storeu_si128((__m128i *)(predictionPtr+8), sum); + _mm_storeu_si128((__m128i *)(predictionPtr+16), sum); + _mm_storeu_si128((__m128i *)(predictionPtr+24), sum); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride), sum); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+8), sum); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+16), sum); + _mm_storeu_si128((__m128i *)(predictionPtr+pStride+24), sum); + predictionPtr += 2 * pStride; + } } } diff --git a/Source/Lib/C_DEFAULT/EbDeblockingFilter_C.c b/Source/Lib/C_DEFAULT/EbDeblockingFilter_C.c index 211dd6b0f..90ae93c30 100644 --- a/Source/Lib/C_DEFAULT/EbDeblockingFilter_C.c +++ b/Source/Lib/C_DEFAULT/EbDeblockingFilter_C.c @@ -441,7 +441,7 @@ void Luma4SampleEdgeDLFCore16bit( /** Chroma2SampleEdgeDLFCore() is used to conduct the deblocking filter upon a particular 2 sample chroma edge. This method doesn't care about whether the deblocking -fitler will be turned ON/OFF on this 2 sample chroma edge. +filter will be turned ON/OFF on this 2 sample chroma edge. @param reconPic (input) reconPic is the pointer to reconstructed picture to be filtered. @@ -483,7 +483,7 @@ void Chroma2SampleEdgeDLFCore( EB_S16 delta; // Cb - // fitler the first sample + // filter the first sample q0 = edgeStartSampleCb; q1 = edgeStartSampleCb + filterStride; p0 = edgeStartSampleCb - filterStride; @@ -501,7 +501,7 @@ void Chroma2SampleEdgeDLFCore( (*q0) = (EB_U8)CLIP3(0, MAX_CHROMA_SAMPLE_VALUE, (*q0) - delta); // Cr - // fitler the first sample + // filter the first sample q0 = edgeStartSampleCr; q1 = edgeStartSampleCr + filterStride; p0 = edgeStartSampleCr - filterStride; @@ -538,7 +538,7 @@ void Chroma2SampleEdgeDLFCore16bit( EB_S16 delta; // Cb - // fitler the first sample + // filter the first sample q0 = edgeStartSampleCb; q1 = edgeStartSampleCb + filterStride; p0 = edgeStartSampleCb - filterStride; @@ -556,7 +556,7 @@ void Chroma2SampleEdgeDLFCore16bit( (*q0) = (EB_U16)CLIP3(0, MAX_CHROMA_SAMPLE_VALUE_10BIT, (*q0) - delta); // Cr - // fitler the first sample + // filter the first sample q0 = edgeStartSampleCr; q1 = edgeStartSampleCr + filterStride; p0 = edgeStartSampleCr - filterStride; diff --git a/Source/Lib/C_DEFAULT/EbTransforms_C.c b/Source/Lib/C_DEFAULT/EbTransforms_C.c index 5a3288a30..4e3d4f945 100644 --- a/Source/Lib/C_DEFAULT/EbTransforms_C.c +++ b/Source/Lib/C_DEFAULT/EbTransforms_C.c @@ -135,23 +135,23 @@ void QuantizeInvQuantize( #define EB_INTRA_CHROMA_DM 4 #define MAX_TU_SIZE 32 -enum COMPONENT_TYPE -{ - COMPONENT_LUMA = 0, // luma - COMPONENT_CHROMA = 1, // chroma (Cb+Cr) - COMPONENT_CHROMA_CB = 2, // chroma Cb - COMPONENT_CHROMA_CR = 3, // chroma Cr - COMPONENT_ALL = 4, // Y+Cb+Cr - COMPONENT_NONE = 15 -}; - - -enum COEFF_SCAN_TYPE2 -{ - SCAN_DIAG2 = 0, // diagonal scan - SCAN_HOR2, // first scan is horizontal - SCAN_VER2 // first scan is vertical -}; +//enum COMPONENT_TYPE +//{ +// COMPONENT_LUMA = 0, // luma +// COMPONENT_CHROMA = 1, // chroma (Cb+Cr) +// COMPONENT_CHROMA_CB = 2, // chroma Cb +// COMPONENT_CHROMA_CR = 3, // chroma Cr +// COMPONENT_ALL = 4, // Y+Cb+Cr +// COMPONENT_NONE = 15 +//}; +// +// +//enum COEFF_SCAN_TYPE2 +//{ +// SCAN_DIAG2 = 0, // diagonal scan +// SCAN_HOR2, // first scan is horizontal +// SCAN_VER2 // first scan is vertical +//}; /************************************** * Static Arrays @@ -2116,4 +2116,4 @@ void InvDstTransform4x4( transformInnerArrayStride, dstStride, shift2nd); -} \ No newline at end of file +} diff --git a/Source/Lib/Codec/EbBitstreamUnit.c b/Source/Lib/Codec/EbBitstreamUnit.c index bd4aba03e..b595c82cf 100644 --- a/Source/Lib/Codec/EbBitstreamUnit.c +++ b/Source/Lib/Codec/EbBitstreamUnit.c @@ -166,7 +166,8 @@ EB_ERRORTYPE OutputBitstreamRBSPToPayload( EB_BYTE outputBuffer, EB_U32 *outputBufferIndex, EB_U32 *outputBufferSize, - EB_U32 startLocation) + EB_U32 startLocation, + NalUnitType nalType) { EB_ERRORTYPE return_error = EB_ErrorNone; @@ -197,7 +198,7 @@ EB_ERRORTYPE OutputBitstreamRBSPToPayload( } // add emulation code - if( (zeroByteCount == 2) && ((readBytePtr[readLocation] & 0xfc) == 0) && ((*outputBufferIndex) < (*outputBufferSize)) ) { + if( (zeroByteCount == 2) && ((readBytePtr[readLocation] & 0xfc) == 0) && ((*outputBufferIndex) < (*outputBufferSize)) && nalType != NAL_UNIT_UNSPECIFIED_62 ) { writeBytePtr[writeLocation++] = 0x03; zeroByteCount = 0; *outputBufferIndex += 1; diff --git a/Source/Lib/Codec/EbBitstreamUnit.h b/Source/Lib/Codec/EbBitstreamUnit.h index ca6e483f4..e522033b5 100644 --- a/Source/Lib/Codec/EbBitstreamUnit.h +++ b/Source/Lib/Codec/EbBitstreamUnit.h @@ -57,7 +57,8 @@ extern EB_ERRORTYPE OutputBitstreamRBSPToPayload( EB_BYTE outputBuffer, EB_U32 *outputBufferIndex, EB_U32 *outputBufferSize, - EB_U32 startLocation); + EB_U32 startLocation, + NalUnitType nalType); #ifdef __cplusplus } #endif diff --git a/Source/Lib/Codec/EbCodingLoop.c b/Source/Lib/Codec/EbCodingLoop.c index a47e0208a..fbd56f8db 100644 --- a/Source/Lib/Codec/EbCodingLoop.c +++ b/Source/Lib/Codec/EbCodingLoop.c @@ -46,6 +46,10 @@ typedef void (*EB_ENCODE_LOOP_FUNC_PTR)( EB_U32 useDeltaQp, CabacEncodeContext_t *cabacEncodeCtxPtr, EB_U32 intraLumaMode, + EB_U32 componentMask, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 tuSize, CabacCost_t *CabacCost, EB_U32 dZoffset) ; @@ -53,9 +57,13 @@ typedef void (*EB_GENERATE_RECON_FUNC_PTR)( EncDecContext_t *contextPtr, EB_U32 originX, EB_U32 originY, + EB_U32 componentMask, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 tuSize, EbPictureBufferDesc_t *predSamples, // no basis/offset EbPictureBufferDesc_t *residual16bit, // no basis/offset - EB_S16 *transformScratchBuffer); + EB_S16 *transformScratchBuffer); typedef void (*EB_ENCODE_LOOP_INTRA_4x4_FUNC_PTR)( EncDecContext_t *contextPtr, @@ -83,7 +91,7 @@ typedef void (*EB_GENERATE_RECON_INTRA_4x4_FUNC_PTR)( EbPictureBufferDesc_t *predSamples, // no basis/offset EbPictureBufferDesc_t *residual16bit, // no basis/offset EB_S16 *transformScratchBuffer, - EB_U32 componentMask); + EB_U32 componentMask); typedef EB_ERRORTYPE(*EB_GENERATE_INTRA_SAMPLES_FUNC_PTR)( EB_BOOL constrainedIntraFlag, //input parameter, indicates if constrained intra is switched on/off @@ -91,22 +99,80 @@ typedef EB_ERRORTYPE(*EB_GENERATE_INTRA_SAMPLES_FUNC_PTR)( EB_U32 originX, EB_U32 originY, EB_U32 size, + EB_U32 lcuSize, EB_U32 cuDepth, NeighborArrayUnit_t *modeTypeNeighborArray, NeighborArrayUnit_t *lumaReconNeighborArray, NeighborArrayUnit_t *cbReconNeighborArray, NeighborArrayUnit_t *crReconNeighborArray, void *refWrapperPtr, + EB_COLOR_FORMAT colorFormat, EB_BOOL pictureLeftBoundary, EB_BOOL pictureTopBoundary, EB_BOOL pictureRightBoundary); + +typedef EB_ERRORTYPE(*EB_GENERATE_LUMA_INTRA_SAMPLES_FUNC_PTR)( + EB_BOOL constrainedIntraFlag, //input parameter, indicates if constrained intra is switched on/off + EB_BOOL strongIntraSmoothingFlag, + EB_U32 originX, + EB_U32 originY, + EB_U32 size, + EB_U32 lcuSize, + EB_U32 cuDepth, + NeighborArrayUnit_t *modeTypeNeighborArray, + NeighborArrayUnit_t *lumaReconNeighborArray, + NeighborArrayUnit_t *cbReconNeighborArray, + NeighborArrayUnit_t *crReconNeighborArray, + void *refWrapperPtr, + EB_BOOL pictureLeftBoundary, + EB_BOOL pictureTopBoundary, + EB_BOOL pictureRightBoundary); + +typedef EB_ERRORTYPE(*EB_GENERATE_CHROMA_INTRA_SAMPLES_FUNC_PTR)( + EB_BOOL constrainedIntraFlag, //input parameter, indicates if constrained intra is switched on/off + EB_BOOL strongIntraSmoothingFlag, + EB_U32 originX, + EB_U32 originY, + EB_U32 size, + EB_U32 lcuSize, + EB_U32 cuDepth, + NeighborArrayUnit_t *modeTypeNeighborArray, + NeighborArrayUnit_t *lumaReconNeighborArray, + NeighborArrayUnit_t *cbReconNeighborArray, + NeighborArrayUnit_t *crReconNeighborArray, + void *refWrapperPtr, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_BOOL pictureLeftBoundary, + EB_BOOL pictureTopBoundary, + EB_BOOL pictureRightBoundary); + typedef EB_ERRORTYPE(*EB_ENC_PASS_INTRA_FUNC_PTR)( + void *refSamples, + EB_U32 originX, + EB_U32 originY, + EB_U32 puSize, + EB_U32 puChromaSize, + EbPictureBufferDesc_t *predictionPtr, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 lumaMode, + EB_U32 chromaMode, + EB_U32 componentMask); + +typedef EB_ERRORTYPE(*EB_ENC_PASS_INTRA4X4_FUNC_PTR)( void *referenceSamples, EB_U32 originX, EB_U32 originY, EB_U32 puSize, - EbPictureBufferDesc_t *predictionPtr, - EB_U32 lumaMode); + EB_U32 chromaPuSize, + EbPictureBufferDesc_t *predictionPtr, + EB_U32 lumaMode, + EB_U32 chromaMode, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 componentMask); + typedef EB_ERRORTYPE (*EB_LCU_INTERNAL_DLF_FUNC_PTR)( EbPictureBufferDesc_t *reconpicture, EB_U32 lcuPosx, @@ -134,7 +200,7 @@ typedef void (*EB_LCU_PIC_EDGE_DLF_FUNC_PTR)( EB_U32 lcuPos_y, EB_U32 lcuWidth, EB_U32 lcuHeight, - PictureControlSet_t *pictureControlSetPtr); + PictureControlSet_t *pictureControlSetPtr); void AddChromaEncDec( PictureControlSet_t *pictureControlSetPtr, @@ -264,11 +330,15 @@ static void EncodePassUpdateReconSampleNeighborArrays( EB_U32 originX, EB_U32 originY, EB_U32 size, + EB_U32 componentMask, + EB_COLOR_FORMAT colorFormat, EB_BOOL is16bit) { - if (is16bit == EB_TRUE){ + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; - { + if (is16bit == EB_TRUE){ + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { // Recon Samples - Luma NeighborArrayUnit16bitSampleWrite( lumaReconSampleNeighborArray, @@ -283,18 +353,19 @@ static void EncodePassUpdateReconSampleNeighborArrays( NEIGHBOR_ARRAY_UNIT_FULL_MASK); } + if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { // Recon Samples - Cb NeighborArrayUnit16bitSampleWrite( cbReconSampleNeighborArray, (EB_U16*)(reconBuffer->bufferCb), reconBuffer->strideCb, - (reconBuffer->originX + originX) >> 1, - (reconBuffer->originY + originY) >> 1, - originX >> 1, - originY >> 1, - size >> 1, - size >> 1, + (reconBuffer->originX + originX) >> subWidthCMinus1, + (reconBuffer->originY + originY) >> subHeightCMinus1, + originX >> subWidthCMinus1, + originY >> subHeightCMinus1, + size > MIN_PU_SIZE ? (size >> subWidthCMinus1) : size, + size > MIN_PU_SIZE ? (size >> subWidthCMinus1) : size, NEIGHBOR_ARRAY_UNIT_FULL_MASK); // Recon Samples - Cr @@ -302,19 +373,17 @@ static void EncodePassUpdateReconSampleNeighborArrays( crReconSampleNeighborArray, (EB_U16*)(reconBuffer->bufferCr), reconBuffer->strideCr, - (reconBuffer->originX + originX) >> 1, - (reconBuffer->originY + originY) >> 1, - originX >> 1, - originY >> 1, - size >> 1, - size >> 1, + (reconBuffer->originX + originX) >> subWidthCMinus1, + (reconBuffer->originY + originY) >> subHeightCMinus1, + originX >> subWidthCMinus1, + originY >> subHeightCMinus1, + size > MIN_PU_SIZE ? (size >> subWidthCMinus1) : size, + size > MIN_PU_SIZE ? (size >> subWidthCMinus1) : size, NEIGHBOR_ARRAY_UNIT_FULL_MASK); } - } - else { - - { + } else { + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { // Recon Samples - Luma NeighborArrayUnitSampleWrite( lumaReconSampleNeighborArray, @@ -329,18 +398,19 @@ static void EncodePassUpdateReconSampleNeighborArrays( NEIGHBOR_ARRAY_UNIT_FULL_MASK); } + if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { // Recon Samples - Cb NeighborArrayUnitSampleWrite( cbReconSampleNeighborArray, reconBuffer->bufferCb, reconBuffer->strideCb, - (reconBuffer->originX + originX) >> 1, - (reconBuffer->originY + originY) >> 1, - originX >> 1, - originY >> 1, - size >> 1, - size >> 1, + (reconBuffer->originX + originX) >> subWidthCMinus1, + (reconBuffer->originY + originY) >> subHeightCMinus1, + originX >> subWidthCMinus1, + originY >> subHeightCMinus1, + size > MIN_PU_SIZE ? (size >> subWidthCMinus1) : size, + size > MIN_PU_SIZE ? (size >> subWidthCMinus1) : size, NEIGHBOR_ARRAY_UNIT_FULL_MASK); // Recon Samples - Cr @@ -348,12 +418,12 @@ static void EncodePassUpdateReconSampleNeighborArrays( crReconSampleNeighborArray, reconBuffer->bufferCr, reconBuffer->strideCr, - (reconBuffer->originX + originX) >> 1, - (reconBuffer->originY + originY) >> 1, - originX >> 1, - originY >> 1, - size >> 1, - size >> 1, + (reconBuffer->originX + originX) >> subWidthCMinus1, + (reconBuffer->originY + originY) >> subHeightCMinus1, + originX >> subWidthCMinus1, + originY >> subHeightCMinus1, + size > MIN_PU_SIZE ? (size >> subWidthCMinus1) : size, + size > MIN_PU_SIZE ? (size >> subWidthCMinus1) : size, NEIGHBOR_ARRAY_UNIT_FULL_MASK); } } @@ -457,31 +527,47 @@ static void EncodeLoop( EB_U32 useDeltaQp, CabacEncodeContext_t *cabacEncodeCtxPtr, EB_U32 intraLumaMode, + EB_U32 componentMask, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 tuSize, CabacCost_t *CabacCost, EB_U32 dZoffset) - - { - EB_U32 chromaQp = cbQp; + EB_U32 chromaQp = cbQp; CodingUnit_t *cuPtr = contextPtr->cuPtr; - TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; - EB_PICTURE sliceType = lcuPtr->pictureControlSetPtr->sliceType; + TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; + EB_PICTURE sliceType = lcuPtr->pictureControlSetPtr->sliceType; EB_U32 temporalLayerIndex = lcuPtr->pictureControlSetPtr->temporalLayerIndex; EB_U32 qp = cuPtr->qp; - EB_U32 tuSize = (contextPtr->cuStats->size == 64) ? 32 : contextPtr->cuStats->size; EbPictureBufferDesc_t *inputSamples = contextPtr->inputSamples; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + EB_U16 tuChromaOffset = 0; + if (colorFormat == EB_YUV422 && secondChroma) { + tuChromaOffset = tuSize >> 1; + } + + const EB_U32 inputLumaOffset = ((originY + inputSamples->originY) * inputSamples->strideY) + (originX + inputSamples->originX); + const EB_U32 predLumaOffset = ((predSamples->originY+originY) * predSamples->strideY) + (predSamples->originX+originX); + const EB_U32 scratchLumaOffset = ((originY & (64 - 1)) * 64) + (originX & (64 - 1)); - const EB_U32 inputLumaOffset = ((originY + inputSamples->originY) * inputSamples->strideY) + (originX + inputSamples->originX); - const EB_U32 inputCbOffset = (((originY + inputSamples->originY) >> 1) * inputSamples->strideCb) + ((originX + inputSamples->originX) >> 1); - const EB_U32 inputCrOffset = (((originY + inputSamples->originY) >> 1) * inputSamples->strideCr) + ((originX + inputSamples->originX) >> 1); - const EB_U32 predLumaOffset = (( predSamples->originY+originY) * predSamples->strideY ) + ( predSamples->originX+originX); - const EB_U32 predCbOffset = (((predSamples->originY+originY) >> 1) * predSamples->strideCb) + ((predSamples->originX+originX) >> 1); - const EB_U32 predCrOffset = (((predSamples->originY+originY) >> 1) * predSamples->strideCr) + ((predSamples->originX+originX) >> 1); - const EB_U32 scratchLumaOffset = ((originY & (64 - 1)) * 64) + (originX & (64 - 1)); - const EB_U32 scratchCbOffset = (((originY & (64 - 1)) >> 1) * 32) + ((originX & (64 - 1)) >> 1); - const EB_U32 scratchCrOffset = (((originY & (64 - 1)) >> 1) * 32) + ((originX & (64 - 1)) >> 1); + const EB_U32 inputCbOffset = ((originX + inputSamples->originX) >> subWidthCMinus1) + + (((originY + tuChromaOffset + inputSamples->originY) >> subHeightCMinus1) * inputSamples->strideCb); + const EB_U32 inputCrOffset = ((originX + inputSamples->originX) >> subWidthCMinus1) + + (((originY + tuChromaOffset + inputSamples->originY) >> subHeightCMinus1) * inputSamples->strideCr); + + const EB_U32 predCbOffset = ((predSamples->originX+originX) >> subWidthCMinus1) + + (((predSamples->originY+originY+tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCb); + const EB_U32 predCrOffset = ((predSamples->originX+originX) >> subWidthCMinus1) + + (((predSamples->originY+originY+tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCr); + + const EB_U32 scratchCbOffset = ((originX & (64 - 1)) >> subWidthCMinus1) + + ((((originY + tuChromaOffset) & (64 - 1)) >> subHeightCMinus1) * (64 >> subWidthCMinus1)); + const EB_U32 scratchCrOffset = ((originX & (64 - 1)) >> subWidthCMinus1) + + ((((originY + tuChromaOffset) & (64 - 1)) >> subHeightCMinus1) * (64 >> subWidthCMinus1)); EB_U8 enableContouringQCUpdateFlag; @@ -493,37 +579,33 @@ static void EncodeLoop( //********************************** // Luma //********************************** - { + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { PictureResidual( inputSamples->bufferY + inputLumaOffset, inputSamples->strideY, predSamples->bufferY + predLumaOffset, predSamples->strideY, ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, + residual16bit->strideY, //64, tuSize, tuSize); EstimateTransform( ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, + residual16bit->strideY, //64, ((EB_S16*)transform16bit->bufferY) + scratchLumaOffset, - 64, + transform16bit->strideY, //64, tuSize, transformScratchBuffer, BIT_INCREMENT_8BIT, (EB_BOOL)(tuSize == MIN_PU_SIZE), contextPtr->transCoeffShapeLuma); - - UnifiedQuantizeInvQuantize( - contextPtr, - lcuPtr->pictureControlSetPtr, ((EB_S16*)transform16bit->bufferY) + scratchLumaOffset, - 64, + transform16bit->strideY, //64, ((EB_S16*)coeffSamplesTB->bufferY) + scratchLumaOffset, ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, qp, @@ -532,7 +614,6 @@ static void EncodeLoop( sliceType, &(countNonZeroCoeffs[0]), contextPtr->transCoeffShapeLuma, - contextPtr->cleanSparseCeoffPfEncDec, contextPtr->pmpMaskingLevelEncDec, cuPtr->predictionModeFlag, @@ -546,209 +627,279 @@ static void EncodeLoop( intraLumaMode, EB_INTRA_CHROMA_DM, CabacCost); - + tuPtr->lumaCbf = countNonZeroCoeffs[0] ? EB_TRUE : EB_FALSE; + if (tuSize > MIN_PU_SIZE) { tuPtr->isOnlyDc[0] = (countNonZeroCoeffs[0] == 1 && (((EB_S16*)residual16bit->bufferY) + scratchLumaOffset)[0] != 0 && tuSize != 32) ? EB_TRUE : EB_FALSE; - if (contextPtr->transCoeffShapeLuma && tuPtr->lumaCbf && tuPtr->isOnlyDc[0] == EB_FALSE) { - if (contextPtr->transCoeffShapeLuma == N2_SHAPE || contextPtr->transCoeffShapeLuma == N4_SHAPE) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - (tuSize >> 1)); - } + if (contextPtr->transCoeffShapeLuma && tuPtr->lumaCbf && tuPtr->isOnlyDc[0] == EB_FALSE) { + if (contextPtr->transCoeffShapeLuma == N2_SHAPE || contextPtr->transCoeffShapeLuma == N4_SHAPE) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + residual16bit->strideY, //64, + (tuSize >> 1)); + } - if (contextPtr->transCoeffShapeLuma == N4_SHAPE) { + if (contextPtr->transCoeffShapeLuma == N4_SHAPE) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + residual16bit->strideY, //64, + (tuSize >> 2)); + } + } + } else { + if (contextPtr->transCoeffShapeLuma && tuPtr->lumaCbf) { PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - (tuSize >> 2)); + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + residual16bit->strideY, //64, + (tuSize >> 1)); + + if (contextPtr->transCoeffShapeLuma == N4_SHAPE) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + residual16bit->strideY, //64, + (tuSize >> 2)); + } } } } - { + if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { //********************************** // Cb //********************************** PictureResidual( - inputSamples->bufferCb + inputCbOffset, - inputSamples->strideCb, - predSamples->bufferCb + predCbOffset, - predSamples->strideCb, - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - 32, - tuSize >> 1, - tuSize >> 1); - // For the case that DC path chosen for chroma, we check the DC values and determine to use DC or N2Shape for chroma. Since there is only one flag for ChromaShaping, we do the prediction of Cr and Cb and decide on the chroma shaping - if (contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE) { - EB_S64 sumResidual = SumResidual_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - tuSize >> 1, - 32); - sumResidual = (ABS(sumResidual) / (tuSize / 2) / (tuSize / 2)); // Normalized based on the size. For chroma, tusize/2 +Tusize/2 - if (sumResidual > 0) { - contextPtr->transCoeffShapeChroma = N2_SHAPE; - } - } - PictureResidual( - inputSamples->bufferCr + inputCrOffset, - inputSamples->strideCr, - predSamples->bufferCr + predCrOffset, - predSamples->strideCr, - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - tuSize >> 1, - tuSize >> 1); - if (contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE) { + inputSamples->bufferCb + inputCbOffset, + inputSamples->strideCb, + predSamples->bufferCb + predCbOffset, + predSamples->strideCb, + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize); + + // For the case that DC path chosen for chroma, we check the DC values and determine to use DC or N2Shape for chroma. Since there is only one flag for ChromaShaping, we do the prediction of Cr and Cb and decide on the chroma shaping + if (tuSize > MIN_PU_SIZE && contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE) { EB_S64 sumResidual = SumResidual_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - tuSize >> 1, - 32); - sumResidual = (ABS(sumResidual) / (tuSize / 2) / (tuSize / 2)); // Normalized based on the size. For chroma, tusize/2 +Tusize/2 - if (sumResidual > 0) { - contextPtr->transCoeffShapeChroma = N2_SHAPE; - } - } + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + tuSize >> subWidthCMinus1, + residual16bit->strideCb); + + // Normalized based on the size. + sumResidual = (ABS(sumResidual) / (tuSize >> subWidthCMinus1) / (tuSize >> subWidthCMinus1)); + if (sumResidual > 0) { + contextPtr->transCoeffShapeChroma = N2_SHAPE; + } + } EstimateTransform( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - 32, - ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, - 32, - tuSize >> 1, - transformScratchBuffer, - BIT_INCREMENT_8BIT, - EB_FALSE, - contextPtr->transCoeffShapeChroma); + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, + transform16bit->strideCb, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + transformScratchBuffer, + BIT_INCREMENT_8BIT, + EB_FALSE, + contextPtr->transCoeffShapeChroma); UnifiedQuantizeInvQuantize( - contextPtr, - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, - 32, - ((EB_S16*)coeffSamplesTB->bufferCb) + scratchCbOffset, - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - chromaQp, - inputSamples->bitDepth, - tuSize >> 1, - sliceType, - &(countNonZeroCoeffs[1]), - contextPtr->transCoeffShapeChroma, - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - useDeltaQp == EB_TRUE ? contextPtr->forceCbfFlag : 0, - enableContouringQCUpdateFlag, - COMPONENT_CHROMA, - temporalLayerIndex, - 0, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost), - - tuPtr->cbCbf = countNonZeroCoeffs[1] ? EB_TRUE : EB_FALSE; + contextPtr, + lcuPtr->pictureControlSetPtr, + ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, + transform16bit->strideCb, + ((EB_S16*)coeffSamplesTB->bufferCb) + scratchCbOffset, + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + chromaQp, + inputSamples->bitDepth, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + sliceType, + &(countNonZeroCoeffs[1]), + contextPtr->transCoeffShapeChroma, + contextPtr->cleanSparseCeoffPfEncDec, + contextPtr->pmpMaskingLevelEncDec, + cuPtr->predictionModeFlag, + useDeltaQp == EB_TRUE ? contextPtr->forceCbfFlag : 0, + enableContouringQCUpdateFlag, + COMPONENT_CHROMA, + temporalLayerIndex, + 0, + cabacEncodeCtxPtr, + contextPtr->fullLambda, + intraLumaMode, + EB_INTRA_CHROMA_DM, + CabacCost); + + if (secondChroma) { + tuPtr->cbCbf2 = countNonZeroCoeffs[1] ? EB_TRUE : EB_FALSE; + tuPtr->isOnlyDc2[0] = (countNonZeroCoeffs[1] == 1 && (((EB_S16*)residual16bit->bufferCb) + scratchCbOffset)[0] != 0) ? + EB_TRUE : + EB_FALSE; + } else { + tuPtr->cbCbf = countNonZeroCoeffs[1] ? EB_TRUE : EB_FALSE; + + if (tuSize > MIN_PU_SIZE) { + tuPtr->isOnlyDc[1] = (countNonZeroCoeffs[1] == 1 && (((EB_S16*)residual16bit->bufferCb) + scratchCbOffset)[0] != 0) ? + EB_TRUE : + EB_FALSE; + + if (contextPtr->transCoeffShapeChroma && tuPtr->cbCbf && tuPtr->isOnlyDc[1] == EB_FALSE) { + if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + (tuSize >> (1 + subWidthCMinus1))); + } - tuPtr->isOnlyDc[1] = (countNonZeroCoeffs[1] == 1 && (((EB_S16*)residual16bit->bufferCb) + scratchCbOffset)[0] != 0) ? - EB_TRUE : - EB_FALSE; - if (contextPtr->transCoeffShapeChroma && tuPtr->cbCbf && tuPtr->isOnlyDc[1] == EB_FALSE) { - if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - residual16bit->strideCb, - (tuSize >> 2)); + if (contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + (tuSize >> (2 + subWidthCMinus1))); + } + } + } else { + if (contextPtr->transCoeffShapeChroma && tuPtr->cbCbf) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + (tuSize >> 1)); + + if (contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + (tuSize >> 2)); + } + } } - - if (contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - residual16bit->strideCb, - (tuSize >> 3)); - } - } + } //********************************** // Cr //********************************** - EstimateTransform( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, - 32, - tuSize >> 1, - transformScratchBuffer, - BIT_INCREMENT_8BIT, - EB_FALSE, - contextPtr->transCoeffShapeChroma); - - - { - UnifiedQuantizeInvQuantize( - - contextPtr, - - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, - 32, - ((EB_S16*)coeffSamplesTB->bufferCr) + scratchCrOffset, - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - chromaQp, - inputSamples->bitDepth, - tuSize >> 1, - sliceType, - &(countNonZeroCoeffs[2]), - contextPtr->transCoeffShapeChroma, - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - 0, - enableContouringQCUpdateFlag, - COMPONENT_CHROMA, - temporalLayerIndex, - 0, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost); + PictureResidual( + inputSamples->bufferCr + inputCrOffset, + inputSamples->strideCr, + predSamples->bufferCr + predCrOffset, + predSamples->strideCr, + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize); + + if (tuSize > MIN_PU_SIZE && contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE) { + EB_S64 sumResidual = SumResidual_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + tuSize >> subWidthCMinus1, + residual16bit->strideCr); + sumResidual = (ABS(sumResidual) / (tuSize >> subWidthCMinus1) / (tuSize >> subWidthCMinus1)); + if (sumResidual > 0) { + contextPtr->transCoeffShapeChroma = N2_SHAPE; + } } - tuPtr->crCbf = countNonZeroCoeffs[2] ? EB_TRUE : EB_FALSE; - - tuPtr->isOnlyDc[2] = (countNonZeroCoeffs[2] == 1 && (((EB_S16*)residual16bit->bufferCr) + scratchCbOffset)[0] != 0) ? - EB_TRUE : - EB_FALSE; - if (contextPtr->transCoeffShapeChroma && tuPtr->crCbf && tuPtr->isOnlyDc[2] == EB_FALSE) { - - if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - residual16bit->strideCr, - (tuSize >> 2)); - } - - if (contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - residual16bit->strideCr, - (tuSize >> 3)); - } - } + EstimateTransform( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, + transform16bit->strideCr, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + transformScratchBuffer, + BIT_INCREMENT_8BIT, + EB_FALSE, + contextPtr->transCoeffShapeChroma); + + UnifiedQuantizeInvQuantize( + contextPtr, + lcuPtr->pictureControlSetPtr, + ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, + transform16bit->strideCr, + ((EB_S16*)coeffSamplesTB->bufferCr) + scratchCrOffset, + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + chromaQp, + inputSamples->bitDepth, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + sliceType, + &(countNonZeroCoeffs[2]), + contextPtr->transCoeffShapeChroma, + contextPtr->cleanSparseCeoffPfEncDec, + contextPtr->pmpMaskingLevelEncDec, + cuPtr->predictionModeFlag, + 0, + enableContouringQCUpdateFlag, + COMPONENT_CHROMA, + temporalLayerIndex, + 0, + cabacEncodeCtxPtr, + contextPtr->fullLambda, + intraLumaMode, + EB_INTRA_CHROMA_DM, + CabacCost); + + if ((componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) && secondChroma) { + tuPtr->crCbf2 = countNonZeroCoeffs[2] ? EB_TRUE : EB_FALSE; + tuPtr->isOnlyDc2[1] = (countNonZeroCoeffs[2] == 1 && (((EB_S16*)residual16bit->bufferCr) + scratchCbOffset)[0] != 0) ? + EB_TRUE : + EB_FALSE; + } else { + tuPtr->crCbf = countNonZeroCoeffs[2] ? EB_TRUE : EB_FALSE; + + if (tuSize > MIN_PU_SIZE) { + tuPtr->isOnlyDc[2] = (countNonZeroCoeffs[2] == 1 && (((EB_S16*)residual16bit->bufferCr) + scratchCbOffset)[0] != 0) ? + EB_TRUE : + EB_FALSE; + if (contextPtr->transCoeffShapeChroma && tuPtr->crCbf && tuPtr->isOnlyDc[2] == EB_FALSE) { + + if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + (tuSize >> (1 + subWidthCMinus1))); + } + + if (contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + (tuSize >> (2 + subWidthCMinus1))); + } + } + } else { + if (contextPtr->transCoeffShapeChroma && tuPtr->crCbf) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + (tuSize >> 1)); + + if (contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + (tuSize >> 2)); + } + } + } + } } - tuPtr->transCoeffShapeLuma = contextPtr->transCoeffShapeLuma; - tuPtr->transCoeffShapeChroma = contextPtr->transCoeffShapeChroma; - tuPtr->nzCoefCount[0] = (EB_U16)countNonZeroCoeffs[0]; - tuPtr->nzCoefCount[1] = (EB_U16)countNonZeroCoeffs[1]; - tuPtr->nzCoefCount[2] = (EB_U16)countNonZeroCoeffs[2]; + + if ((componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) && secondChroma) { + tuPtr->nzCoefCount2[0] = (EB_U16)countNonZeroCoeffs[1]; + tuPtr->nzCoefCount2[1] = (EB_U16)countNonZeroCoeffs[2]; + tuPtr->transCoeffShapeChroma2 = contextPtr->transCoeffShapeChroma; + } else { + tuPtr->transCoeffShapeLuma = contextPtr->transCoeffShapeLuma; + tuPtr->transCoeffShapeChroma = contextPtr->transCoeffShapeChroma; + tuPtr->nzCoefCount[0] = (EB_U16)countNonZeroCoeffs[0]; + tuPtr->nzCoefCount[1] = (EB_U16)countNonZeroCoeffs[1]; + tuPtr->nzCoefCount[2] = (EB_U16)countNonZeroCoeffs[2]; + } + return; } @@ -775,6 +926,10 @@ static void EncodeGenerateRecon( EncDecContext_t *contextPtr, EB_U32 originX, EB_U32 originY, + EB_U32 componentMask, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 tuSize, EbPictureBufferDesc_t *predSamples, // no basis/offset EbPictureBufferDesc_t *residual16bit, // no basis/offset EB_S16 *transformScratchBuffer) @@ -787,28 +942,36 @@ static void EncodeGenerateRecon( EB_U32 reconChromaOffset; CodingUnit_t *cuPtr = contextPtr->cuPtr; - TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; - EB_U32 tuSize = (contextPtr->cuStats->size == 64) ? 32 : contextPtr->cuStats->size; + TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; EbPictureBufferDesc_t *reconSamples = predSamples; + + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + const EB_U16 shift_bit = (tuSize == MIN_PU_SIZE) ? 0 : subWidthCMinus1; + EB_U16 tuChromaOffset = 0; + if (colorFormat == EB_YUV422 && secondChroma) { + tuChromaOffset = tuSize >> 1; + } + EB_BOOL cbCbf=secondChroma?tuPtr->cbCbf2:tuPtr->cbCbf; + EB_BOOL crCbf=secondChroma?tuPtr->crCbf2:tuPtr->crCbf; // *Note - The prediction is built in-place in the Recon buffer. It is overwritten with Reconstructed // samples if the CBF==1 && SKIP==False //********************************** // Luma //********************************** - - { + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { predLumaOffset = (predSamples->originY+originY) * predSamples->strideY + (predSamples->originX+originX); scratchLumaOffset = ((originY & (63)) * 64) + (originX & (63)); reconLumaOffset = (reconSamples->originY+originY) * reconSamples->strideY + (reconSamples->originX+originX); if (tuPtr->lumaCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { EncodeInvTransform( - tuPtr->transCoeffShapeLuma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[0], + (tuSize==MIN_PU_SIZE)?EB_FALSE:(tuPtr->transCoeffShapeLuma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[0]), ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, + residual16bit->strideY, ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, + residual16bit->strideY, tuSize, transformScratchBuffer, BIT_INCREMENT_8BIT, @@ -818,7 +981,7 @@ static void EncodeGenerateRecon( predSamples->bufferY + predLumaOffset, predSamples->strideY, ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, + residual16bit->strideY, reconSamples->bufferY + reconLumaOffset, reconSamples->strideY, tuSize, @@ -830,65 +993,71 @@ static void EncodeGenerateRecon( // Chroma //********************************** - { - predChromaOffset = (((predSamples->originY+originY) >> 1) * predSamples->strideCb) + ((predSamples->originX+originX) >> 1); - scratchChromaOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - reconChromaOffset = (((reconSamples->originY+originY) >> 1) * reconSamples->strideCb) + ((reconSamples->originX+originX) >> 1); + if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { + predChromaOffset = ((predSamples->originX + originX) >> subWidthCMinus1) + + (((predSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCb); + scratchChromaOffset = ((originX & 63) >> subWidthCMinus1) + + (((originY+tuChromaOffset) & 63) >> subHeightCMinus1) * (64 >> subWidthCMinus1); + reconChromaOffset = ((reconSamples->originX + originX) >> subWidthCMinus1) + + (((reconSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * reconSamples->strideCb); + //********************************** // Cb //********************************** - if (tuPtr->cbCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - + if (cbCbf== EB_TRUE && cuPtr->skipFlag == EB_FALSE) { EncodeInvTransform( - tuPtr->transCoeffShapeChroma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[1], + (tuSize==MIN_PU_SIZE)?EB_FALSE:(secondChroma ? (tuPtr->transCoeffShapeChroma2 == ONLY_DC_SHAPE || tuPtr->isOnlyDc2[0]) : (tuPtr->transCoeffShapeChroma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[1])), ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, + residual16bit->strideCb, ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, - tuSize>>1, + residual16bit->strideCb, + tuSize >> shift_bit, transformScratchBuffer, BIT_INCREMENT_8BIT, EB_FALSE); - AdditionKernel_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1][tuSize >> 4]( + AdditionKernel_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1][tuSize >> (3 + shift_bit)]( predSamples->bufferCb + predChromaOffset, predSamples->strideCb, ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, + residual16bit->strideCb, reconSamples->bufferCb + reconChromaOffset, reconSamples->strideCb, - tuSize>>1, - tuSize>>1); + tuSize >> shift_bit, + tuSize >> shift_bit); } //********************************** // Cr //********************************** - predChromaOffset = (((predSamples->originY+originY) >> 1) * predSamples->strideCr) + ((predSamples->originX+originX) >> 1); - scratchChromaOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - reconChromaOffset = (((reconSamples->originY+originY) >> 1) * reconSamples->strideCr) + ((reconSamples->originX+originX) >> 1); - if (tuPtr->crCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - + predChromaOffset = ((predSamples->originX+originX) >> subWidthCMinus1) + + (((predSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCr); + scratchChromaOffset = ((originX & (63)) >> subWidthCMinus1) + + (((originY+tuChromaOffset) & 63) >> subHeightCMinus1) * (64 >> subWidthCMinus1); + reconChromaOffset = ((reconSamples->originX+originX) >> subWidthCMinus1) + + (((reconSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * reconSamples->strideCr); + + if (crCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { EncodeInvTransform( - tuPtr->transCoeffShapeChroma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[2], + (tuSize==MIN_PU_SIZE)?EB_FALSE:(secondChroma ? (tuPtr->transCoeffShapeChroma2 == ONLY_DC_SHAPE || tuPtr->isOnlyDc2[1]) : (tuPtr->transCoeffShapeChroma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[2])), ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, + residual16bit->strideCr, ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, - tuSize>>1, + residual16bit->strideCr, + tuSize >> shift_bit, transformScratchBuffer, BIT_INCREMENT_8BIT, EB_FALSE); - AdditionKernel_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1][tuSize >> 4]( + AdditionKernel_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1][tuSize >> (3 + shift_bit)]( predSamples->bufferCr + predChromaOffset, predSamples->strideCr, ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, + residual16bit->strideCr, reconSamples->bufferCr + reconChromaOffset, reconSamples->strideCr, - tuSize>>1, - tuSize>>1); + tuSize >> shift_bit, + tuSize >> shift_bit); } } @@ -928,33 +1097,45 @@ static void EncodeLoop16bit( EB_U32 useDeltaQp, CabacEncodeContext_t *cabacEncodeCtxPtr, EB_U32 intraLumaMode, + EB_U32 componentMask, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 tuSize, CabacCost_t *CabacCost, EB_U32 dZoffset) - { - + EB_U32 chromaQp = cbQp; + CodingUnit_t *cuPtr = contextPtr->cuPtr; + TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; + EB_PICTURE sliceType = lcuPtr->pictureControlSetPtr->sliceType; + EB_U32 temporalLayerIndex = lcuPtr->pictureControlSetPtr->temporalLayerIndex; + EB_U32 qp = cuPtr->qp; + EbPictureBufferDesc_t *inputSamples16bit = contextPtr->inputSample16bitBuffer; //64x64 for 16bit, whole frame for 8bit + EbPictureBufferDesc_t *predSamples16bit = predSamples; + + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + EB_U16 tuChromaOffset = 0; + if (colorFormat == EB_YUV422 && secondChroma) { + tuChromaOffset = tuSize >> 1; + } - EB_U32 chromaQp = cbQp; + const EB_U32 inputLumaOffset = ((originY & 63) * inputSamples16bit->strideY) + (originX & 63); + const EB_U32 predLumaOffset = ((predSamples16bit->originY + originY) * predSamples16bit->strideY) + (predSamples16bit->originX + originX); + const EB_U32 scratchLumaOffset = ((originY & 63) * 64) + (originX & 63); - CodingUnit_t *cuPtr = contextPtr->cuPtr; - TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; - EB_PICTURE sliceType = lcuPtr->pictureControlSetPtr->sliceType; - EB_U32 temporalLayerIndex = lcuPtr->pictureControlSetPtr->temporalLayerIndex; - EB_U32 qp = cuPtr->qp; - EB_U32 tuSize = (contextPtr->cuStats->size == 64) ? 32 : contextPtr->cuStats->size; - EbPictureBufferDesc_t *inputSamples16bit = contextPtr->inputSample16bitBuffer; - EbPictureBufferDesc_t *predSamples16bit = predSamples; + const EB_U32 inputCbOffset = ((((originY + tuChromaOffset) & 63) >> subHeightCMinus1) * inputSamples16bit->strideCb) + ((originX & 63) >> subWidthCMinus1); + const EB_U32 inputCrOffset = ((((originY + tuChromaOffset) & 63) >> subHeightCMinus1) * inputSamples16bit->strideCr) + ((originX & 63) >> subWidthCMinus1); - const EB_U32 inputLumaOffset = ((originY & (63)) * inputSamples16bit->strideY ) + (originX & (63)); - const EB_U32 inputCbOffset = (((originY & (63)) >> 1) * inputSamples16bit->strideCb) + ((originX & (63)) >> 1); - const EB_U32 inputCrOffset = (((originY & (63)) >> 1) * inputSamples16bit->strideCr) + ((originX & (63)) >> 1); + const EB_U32 predCbOffset = ((predSamples->originX + originX) >> subWidthCMinus1) + + (((predSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCb); + const EB_U32 predCrOffset = ((predSamples->originX + originX) >> subWidthCMinus1) + + (((predSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCr); - const EB_U32 predLumaOffset = (( predSamples16bit->originY+originY) * predSamples16bit->strideY ) + ( predSamples16bit->originX+originX); - const EB_U32 predCbOffset = (((predSamples16bit->originY+originY) >> 1) * predSamples16bit->strideCb) + ((predSamples16bit->originX+originX) >> 1); - const EB_U32 predCrOffset = (((predSamples16bit->originY+originY) >> 1) * predSamples16bit->strideCr) + ((predSamples16bit->originX+originX) >> 1); - const EB_U32 scratchLumaOffset = ((originY & (63)) * 64) + (originX & (63)); - const EB_U32 scratchCbOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - const EB_U32 scratchCrOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); + const EB_U32 scratchCbOffset = ((originX & (64 - 1)) >> subWidthCMinus1) + + ((((originY + tuChromaOffset) & (64 - 1)) >> subHeightCMinus1) * (64 >> subWidthCMinus1)); + const EB_U32 scratchCrOffset = ((originX & (64 - 1)) >> subWidthCMinus1) + + ((((originY + tuChromaOffset) & (64 - 1)) >> subHeightCMinus1) * (64 >> subWidthCMinus1)); EB_U8 enableContouringQCUpdateFlag; @@ -963,42 +1144,36 @@ static void EncodeLoop16bit( lcuPtr->index, cuPtr->leafIndex) && (cuPtr->qp < lcuPtr->pictureControlSetPtr->pictureQp); - //Update QP for Quant + //Update QP for Quant qp += QP_BD_OFFSET; - chromaQp += QP_BD_OFFSET; - - { - //********************************** - // Luma - //********************************** + + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { PictureResidual16bit( ((EB_U16*)inputSamples16bit->bufferY) + inputLumaOffset, inputSamples16bit->strideY, ((EB_U16*)predSamples16bit->bufferY) + predLumaOffset, predSamples16bit->strideY, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - tuSize, - tuSize); + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + 64, + tuSize, + tuSize); - EncodeTransform( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - ((EB_S16*)transform16bit->bufferY) + scratchLumaOffset, - 64, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - (EB_BOOL)(tuSize == MIN_PU_SIZE), + EncodeTransform( + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + 64, + ((EB_S16*)transform16bit->bufferY) + scratchLumaOffset, + 64, + tuSize, + transformScratchBuffer, + BIT_INCREMENT_10BIT, + (EB_BOOL)(tuSize == MIN_PU_SIZE), contextPtr->transCoeffShapeLuma); UnifiedQuantizeInvQuantize( - contextPtr, - - lcuPtr->pictureControlSetPtr, + lcuPtr->pictureControlSetPtr, ((EB_S16*)transform16bit->bufferY) + scratchLumaOffset, 64, ((EB_S16*)coeffSamplesTB->bufferY) + scratchLumaOffset, @@ -1008,10 +1183,10 @@ static void EncodeLoop16bit( tuSize, sliceType, &(countNonZeroCoeffs[0]), - contextPtr->transCoeffShapeLuma, - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, + contextPtr->transCoeffShapeLuma, + contextPtr->cleanSparseCeoffPfEncDec, + contextPtr->pmpMaskingLevelEncDec, + cuPtr->predictionModeFlag, 0, enableContouringQCUpdateFlag, COMPONENT_LUMA, @@ -1022,213 +1197,288 @@ static void EncodeLoop16bit( intraLumaMode, EB_INTRA_CHROMA_DM, CabacCost); - + tuPtr->lumaCbf = countNonZeroCoeffs[0] ? EB_TRUE : EB_FALSE; + if (tuSize > MIN_PU_SIZE) { tuPtr->isOnlyDc[0] = (countNonZeroCoeffs[0] == 1 && (((EB_S16*)residual16bit->bufferY) + scratchLumaOffset)[0] != 0 && tuSize != 32) ? EB_TRUE : EB_FALSE; - if (contextPtr->transCoeffShapeLuma && tuPtr->lumaCbf && tuPtr->isOnlyDc[0] == EB_FALSE) { - if (contextPtr->transCoeffShapeLuma == N2_SHAPE || contextPtr->transCoeffShapeLuma == N4_SHAPE) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - (tuSize >> 1)); + + if (contextPtr->transCoeffShapeLuma && tuPtr->lumaCbf && tuPtr->isOnlyDc[0] == EB_FALSE) { + if (contextPtr->transCoeffShapeLuma == N2_SHAPE || contextPtr->transCoeffShapeLuma == N4_SHAPE) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + 64, + (tuSize >> 1)); + } + + if (contextPtr->transCoeffShapeLuma == N4_SHAPE) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + 64, + (tuSize >> 2)); + } } + } else { + if (contextPtr->transCoeffShapeLuma && tuPtr->lumaCbf) { - if (contextPtr->transCoeffShapeLuma == N4_SHAPE) { PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - (tuSize >> 2)); + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + 64, + (tuSize >> 1)); + + if (contextPtr->transCoeffShapeLuma == N4_SHAPE) { + + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + 64, + (tuSize >> 2)); + } } } } - { + if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { //********************************** // Cb //********************************** PictureResidual16bit( - ((EB_U16*)inputSamples16bit->bufferCb) + inputCbOffset, - inputSamples16bit->strideCb, - ((EB_U16*)predSamples16bit->bufferCb) + predCbOffset, - predSamples16bit->strideCb, - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - 32, - tuSize >> 1, - tuSize >> 1); - - // For the case that DC path chosen for chroma, we check the DC values and determine to use DC or N2Shape for chroma. Since there is only one flag for ChromaShaping, we do the prediction of Cr and Cb and decide on the chroma shaping - if (contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE) { - EB_S64 sumResidual = SumResidual_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - tuSize >> 1, - 32); - - sumResidual = (ABS(sumResidual) / (tuSize / 2) / (tuSize / 2)); // Normalized based on the size. For chroma, tusize/2 +Tusize/2 - if (sumResidual > (1 << BIT_INCREMENT_10BIT)) { - contextPtr->transCoeffShapeChroma = N2_SHAPE; - } - } - - PictureResidual16bit( - ((EB_U16*)inputSamples16bit->bufferCr) + inputCrOffset, - inputSamples16bit->strideCr, - ((EB_U16*)predSamples16bit->bufferCr) + predCrOffset, - predSamples16bit->strideCr, - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - tuSize >> 1, - tuSize >> 1); - - if (contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE) { + ((EB_U16*)inputSamples16bit->bufferCb) + inputCbOffset, + inputSamples16bit->strideCb, + ((EB_U16*)predSamples16bit->bufferCb) + predCbOffset, + predSamples16bit->strideCb, + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize); + // For the case that DC path chosen for chroma, we check the DC values and determine to use DC or N2Shape for chroma. Since there is only one flag for ChromaShaping, we do the prediction of Cr and Cb and decide on the chroma shaping + if (tuSize > MIN_PU_SIZE && contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE) { EB_S64 sumResidual = SumResidual_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - tuSize >> 1, - 32); - sumResidual = (ABS(sumResidual) / (tuSize / 2) / (tuSize / 2)); // Normalized based on the size. For chroma, tusize/2 +Tusize/2 - if (sumResidual > (1<transCoeffShapeChroma = N2_SHAPE; - } - } - + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + tuSize >> subWidthCMinus1, + residual16bit->strideCb); + sumResidual = (ABS(sumResidual) / (tuSize >> subWidthCMinus1) / (tuSize >> subWidthCMinus1)); // Normalized based on the size. For chroma, tusize/2 +Tusize/2 + if (sumResidual > (1 << BIT_INCREMENT_10BIT)) { + contextPtr->transCoeffShapeChroma = N2_SHAPE; + } + } EncodeTransform( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - 32, - ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, - 32, - tuSize >> 1, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - EB_FALSE, - contextPtr->transCoeffShapeChroma); + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, + transform16bit->strideCb, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + transformScratchBuffer, + BIT_INCREMENT_10BIT, + EB_FALSE, + contextPtr->transCoeffShapeChroma); UnifiedQuantizeInvQuantize( - contextPtr, - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, - 32, - ((EB_S16*)coeffSamplesTB->bufferCb) + scratchCbOffset, - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - chromaQp, - EB_10BIT, - tuSize >> 1, - sliceType, - &(countNonZeroCoeffs[1]), - contextPtr->transCoeffShapeChroma, - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - 0, - enableContouringQCUpdateFlag, - COMPONENT_CHROMA, - temporalLayerIndex, - 0, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost); - - tuPtr->cbCbf = countNonZeroCoeffs[1] ? EB_TRUE : EB_FALSE; + contextPtr, + lcuPtr->pictureControlSetPtr, + ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, + transform16bit->strideCb, + ((EB_S16*)coeffSamplesTB->bufferCb) + scratchCbOffset, + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + chromaQp, + EB_10BIT, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + sliceType, + &(countNonZeroCoeffs[1]), + contextPtr->transCoeffShapeChroma, + contextPtr->cleanSparseCeoffPfEncDec, + contextPtr->pmpMaskingLevelEncDec, + cuPtr->predictionModeFlag, + 0, //useDeltaQp == EB_TRUE ? contextPtr->forceCbfFlag : 0 + enableContouringQCUpdateFlag, + COMPONENT_CHROMA, + temporalLayerIndex, + 0, + cabacEncodeCtxPtr, + contextPtr->fullLambda, + intraLumaMode, + EB_INTRA_CHROMA_DM, + CabacCost); + + + if ((componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) && secondChroma) { + tuPtr->cbCbf2 = countNonZeroCoeffs[1] ? EB_TRUE : EB_FALSE; + tuPtr->isOnlyDc2[0] = (countNonZeroCoeffs[1] == 1 && (((EB_S16*)residual16bit->bufferCb) + scratchCbOffset)[0] != 0) ? + EB_TRUE : + EB_FALSE; + } else { + tuPtr->cbCbf = countNonZeroCoeffs[1] ? EB_TRUE : EB_FALSE; + + if (tuSize > MIN_PU_SIZE) { + tuPtr->isOnlyDc[1] = (countNonZeroCoeffs[1] == 1 && (((EB_S16*)residual16bit->bufferCb) + scratchCbOffset)[0] != 0) ? + EB_TRUE : + EB_FALSE; + + if (contextPtr->transCoeffShapeChroma && tuPtr->cbCbf && tuPtr->isOnlyDc[1] == EB_FALSE) { + if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + (tuSize >> (1 + subWidthCMinus1))); + } - tuPtr->isOnlyDc[1] = (countNonZeroCoeffs[1] == 1 && (((EB_S16*)residual16bit->bufferCb) + scratchCbOffset)[0] != 0) ? - EB_TRUE : - EB_FALSE; - if (contextPtr->transCoeffShapeChroma && tuPtr->cbCbf && tuPtr->isOnlyDc[1] == EB_FALSE) { - if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - residual16bit->strideCb, - (tuSize >> 2)); + if (contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + (tuSize >> (2 + subWidthCMinus1))); + } + } + } else { + if (contextPtr->transCoeffShapeChroma && tuPtr->cbCbf) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + (tuSize >> 1)); + + if (contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, + residual16bit->strideCb, + (tuSize >> 2)); + } + } } - - if (contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - residual16bit->strideCb, - (tuSize >> 3)); - } - } + } //********************************** // Cr //********************************** - EncodeTransform( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, - 32, - tuSize >> 1, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - EB_FALSE, - contextPtr->transCoeffShapeChroma); + PictureResidual16bit( + ((EB_U16*)inputSamples16bit->bufferCr) + inputCrOffset, + inputSamples16bit->strideCr, + ((EB_U16*)predSamples16bit->bufferCr) + predCrOffset, + predSamples16bit->strideCr, + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize); + + if (tuSize > MIN_PU_SIZE && contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE) { + EB_S64 sumResidual = SumResidual_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + tuSize >> subWidthCMinus1, + residual16bit->strideCr); - UnifiedQuantizeInvQuantize( + sumResidual = (ABS(sumResidual) / (tuSize >> subWidthCMinus1) / (tuSize >> subWidthCMinus1)); // Normalized based on the size. For chroma, tusize/2 +Tusize/2 + if (sumResidual > (1 << BIT_INCREMENT_10BIT)) { + contextPtr->transCoeffShapeChroma = N2_SHAPE; + } + } - contextPtr, + EncodeTransform( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, + transform16bit->strideCr, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + transformScratchBuffer, + BIT_INCREMENT_10BIT, + EB_FALSE, + contextPtr->transCoeffShapeChroma); + + + { + UnifiedQuantizeInvQuantize( + contextPtr, + lcuPtr->pictureControlSetPtr, + ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, + transform16bit->strideCr, + ((EB_S16*)coeffSamplesTB->bufferCr) + scratchCrOffset, + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + chromaQp, + EB_10BIT, + tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize, + sliceType, + &(countNonZeroCoeffs[2]), + contextPtr->transCoeffShapeChroma, + contextPtr->cleanSparseCeoffPfEncDec, + contextPtr->pmpMaskingLevelEncDec, + cuPtr->predictionModeFlag, + useDeltaQp == EB_TRUE ? contextPtr->forceCbfFlag : 0, //Jing: double check here, not align with Cb + enableContouringQCUpdateFlag, + COMPONENT_CHROMA, + temporalLayerIndex, + 0, + cabacEncodeCtxPtr, + contextPtr->fullLambda, + intraLumaMode, + EB_INTRA_CHROMA_DM, + CabacCost); + } - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, - 32, - ((EB_S16*)coeffSamplesTB->bufferCr) + scratchCrOffset, - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - chromaQp, - EB_10BIT, - tuSize >> 1, - sliceType, - &(countNonZeroCoeffs[2]), - contextPtr->transCoeffShapeChroma, - - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - useDeltaQp == EB_TRUE ? contextPtr->forceCbfFlag : 0, - enableContouringQCUpdateFlag, - COMPONENT_CHROMA, - temporalLayerIndex, - 0, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost); + if ((componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) && secondChroma) { + + tuPtr->crCbf2 = countNonZeroCoeffs[2] ? EB_TRUE : EB_FALSE; + tuPtr->isOnlyDc2[1] = (countNonZeroCoeffs[2] == 1 && (((EB_S16*)residual16bit->bufferCr) + scratchCbOffset)[0] != 0) ? + EB_TRUE : + EB_FALSE; + } else { + tuPtr->crCbf = countNonZeroCoeffs[2] ? EB_TRUE : EB_FALSE; + + if (tuSize > MIN_PU_SIZE) { + tuPtr->isOnlyDc[2] = (countNonZeroCoeffs[2] == 1 && (((EB_S16*)residual16bit->bufferCr) + scratchCbOffset)[0] != 0) ? + EB_TRUE : + EB_FALSE; + if (contextPtr->transCoeffShapeChroma && tuPtr->crCbf && tuPtr->isOnlyDc[2] == EB_FALSE) { + + if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + (tuSize >> (1 + subWidthCMinus1))); + } + if (contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + (tuSize >> (2 + subWidthCMinus1))); + } + } + } else { + if (contextPtr->transCoeffShapeChroma && tuPtr->crCbf) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + (tuSize >> 1)); + + if (contextPtr->transCoeffShapeChroma == PF_N4) { + PfZeroOutUselessQuadrants( + ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, + residual16bit->strideCr, + (tuSize >> 2)); + } + } + } + } + } - tuPtr->crCbf = countNonZeroCoeffs[2] ? EB_TRUE : EB_FALSE; - tuPtr->isOnlyDc[2] = (countNonZeroCoeffs[2] == 1 && (((EB_S16*)residual16bit->bufferCr) + scratchCbOffset)[0] != 0) ? - EB_TRUE : - EB_FALSE; - if (contextPtr->transCoeffShapeChroma && tuPtr->crCbf && tuPtr->isOnlyDc[2] == EB_FALSE) { - if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - residual16bit->strideCr, - (tuSize >> 2)); - } - - if (contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - residual16bit->strideCr, - (tuSize >> 3)); - } - } - } - tuPtr->transCoeffShapeLuma = contextPtr->transCoeffShapeLuma; - tuPtr->transCoeffShapeChroma = contextPtr->transCoeffShapeChroma; - tuPtr->nzCoefCount[0] = (EB_U16)countNonZeroCoeffs[0]; - tuPtr->nzCoefCount[1] = (EB_U16)countNonZeroCoeffs[1]; - tuPtr->nzCoefCount[2] = (EB_U16)countNonZeroCoeffs[2]; - return; + if ((componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) && secondChroma) { + tuPtr->nzCoefCount2[0] = (EB_U16)countNonZeroCoeffs[1]; + tuPtr->nzCoefCount2[1] = (EB_U16)countNonZeroCoeffs[2]; + tuPtr->transCoeffShapeChroma2 = contextPtr->transCoeffShapeChroma; + } else { + tuPtr->transCoeffShapeLuma = contextPtr->transCoeffShapeLuma; + tuPtr->transCoeffShapeChroma = contextPtr->transCoeffShapeChroma; + tuPtr->nzCoefCount[0] = (EB_U16)countNonZeroCoeffs[0]; + tuPtr->nzCoefCount[1] = (EB_U16)countNonZeroCoeffs[1]; + tuPtr->nzCoefCount[2] = (EB_U16)countNonZeroCoeffs[2]; + } + return; } + /********************************************************** * Encode Generate Recon * @@ -1252,891 +1502,57 @@ static void EncodeGenerateRecon16bit( EncDecContext_t *contextPtr, EB_U32 originX, EB_U32 originY, + EB_U32 componentMask, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 tuSize, EbPictureBufferDesc_t *predSamples, // no basis/offset EbPictureBufferDesc_t *residual16bit, // no basis/offset EB_S16 *transformScratchBuffer) { + EB_U32 predLumaOffset; + EB_U32 predChromaOffset; + EB_U32 scratchLumaOffset; + EB_U32 scratchChromaOffset; + EB_U32 reconLumaOffset; + EB_U32 reconChromaOffset; - EB_U32 predLumaOffset; - EB_U32 predChromaOffset; - EB_U32 scratchLumaOffset; - EB_U32 scratchChromaOffset; - EB_U32 reconLumaOffset; - EB_U32 reconChromaOffset; - - CodingUnit_t *cuPtr = contextPtr->cuPtr; - TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; - EB_U32 tuSize = (contextPtr->cuStats->size == 64) ? 32 : contextPtr->cuStats->size; - - //********************************** - // Luma - //********************************** - - { - predLumaOffset = (predSamples->originY + originY)* predSamples->strideY + (predSamples->originX + originX); - scratchLumaOffset = ((originY & (63)) * 64) + (originX & (63)); - reconLumaOffset = (predSamples->originY + originY)* predSamples->strideY + (predSamples->originX + originX); - if (tuPtr->lumaCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - - EncodeInvTransform( - tuPtr->transCoeffShapeLuma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[0], - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - (EB_BOOL)(tuSize == MIN_PU_SIZE)); - - AdditionKernel_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( - (EB_U16*)predSamples->bufferY + predLumaOffset, - predSamples->strideY, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - (EB_U16*)predSamples->bufferY + reconLumaOffset, - predSamples->strideY, - tuSize, - tuSize); - - } - - } - - //********************************** - // Chroma - //********************************** - - { - - //********************************** - // Cb - //********************************** - predChromaOffset = (((predSamples->originY + originY) >> 1) * predSamples->strideCb) + ((predSamples->originX + originX) >> 1); - scratchChromaOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - reconChromaOffset = (((predSamples->originY + originY) >> 1) * predSamples->strideCb) + ((predSamples->originX + originX) >> 1); - if (tuPtr->cbCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - - EncodeInvTransform( - tuPtr->transCoeffShapeChroma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[1], - ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, - ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, - tuSize >> 1, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - EB_FALSE); - - AdditionKernel_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( - (EB_U16*)predSamples->bufferCb + predChromaOffset, - predSamples->strideCb, - ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, - (EB_U16*)predSamples->bufferCb + reconChromaOffset, - predSamples->strideCb, - tuSize >> 1, - tuSize >> 1); - - } - - //********************************** - // Cr - //********************************** - predChromaOffset = (((predSamples->originY + originY) >> 1) * predSamples->strideCr) + ((predSamples->originX + originX) >> 1); - scratchChromaOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - reconChromaOffset = (((predSamples->originY + originY) >> 1) * predSamples->strideCr) + ((predSamples->originX + originX) >> 1); - if (tuPtr->crCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - - EncodeInvTransform( - tuPtr->transCoeffShapeChroma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[2], - ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, - ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, - tuSize >> 1, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - EB_FALSE); - - AdditionKernel_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( - (EB_U16*)predSamples->bufferCr + predChromaOffset, - predSamples->strideCr, - ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, - (EB_U16*)predSamples->bufferCr + reconChromaOffset, - predSamples->strideCr, - tuSize >> 1, - tuSize >> 1); - } - } - - return; -} - -static EB_ENCODE_LOOP_FUNC_PTR EncodeLoopFunctionTable[2] = -{ - EncodeLoop, - EncodeLoop16bit -}; - -EB_GENERATE_RECON_FUNC_PTR EncodeGenerateReconFunctionPtr[2] = -{ - EncodeGenerateRecon, - EncodeGenerateRecon16bit -}; - -static void EncodeLoopIntra4x4( - EncDecContext_t *contextPtr, - LargestCodingUnit_t *lcuPtr, - EB_U32 originX, - EB_U32 originY, - EB_U32 cbQp, - EbPictureBufferDesc_t *predSamples, // no basis/offset - EbPictureBufferDesc_t *coeffSamplesTB, // lcu based - EbPictureBufferDesc_t *residual16bit, // no basis/offset - EbPictureBufferDesc_t *transform16bit, // no basis/offset - EB_S16 *transformScratchBuffer, - EB_U32 *countNonZeroCoeffs, - EB_U32 componentMask, - EB_U32 useDeltaQp, - CabacEncodeContext_t *cabacEncodeCtxPtr, - EB_U32 intraLumaMode, - CabacCost_t *CabacCost, - EB_U32 dZoffset) - -{ - - EB_U32 chromaQp = cbQp; CodingUnit_t *cuPtr = contextPtr->cuPtr; - TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; - EB_PICTURE sliceType = lcuPtr->pictureControlSetPtr->sliceType; - EB_U32 temporalLayerIndex = lcuPtr->pictureControlSetPtr->temporalLayerIndex; - EB_U32 qp = cuPtr->qp; - EB_U32 tuSize = MIN_PU_SIZE; - EbPictureBufferDesc_t *inputSamples = contextPtr->inputSamples; - - - const EB_U32 inputLumaOffset = ((originY + inputSamples->originY) * inputSamples->strideY) + (originX + inputSamples->originX); - const EB_U32 inputCbOffset = (((originY + inputSamples->originY) >> 1) * inputSamples->strideCb) + ((originX + inputSamples->originX) >> 1); - const EB_U32 inputCrOffset = (((originY + inputSamples->originY) >> 1) * inputSamples->strideCr) + ((originX + inputSamples->originX) >> 1); - const EB_U32 predLumaOffset = (( predSamples->originY+originY) * predSamples->strideY ) + ( predSamples->originX+originX); - const EB_U32 predCbOffset = (((predSamples->originY+originY) >> 1) * predSamples->strideCb) + ((predSamples->originX+originX) >> 1); - const EB_U32 predCrOffset = (((predSamples->originY+originY) >> 1) * predSamples->strideCr) + ((predSamples->originX+originX) >> 1); - const EB_U32 scratchLumaOffset = ((originY & (64 - 1)) * 64) + (originX & (64 - 1)); - const EB_U32 scratchCbOffset = (((originY & (64 - 1)) >> 1) * 32) + ((originX & (64 - 1)) >> 1); - const EB_U32 scratchCrOffset = (((originY & (64 - 1)) >> 1) * 32) + ((originX & (64 - 1)) >> 1); - - EB_U8 enableContouringQCUpdateFlag; - - enableContouringQCUpdateFlag = DeriveContouringClass( - lcuPtr->pictureControlSetPtr->ParentPcsPtr, - lcuPtr->index, - cuPtr->leafIndex) && (cuPtr->qp < lcuPtr->pictureControlSetPtr->pictureQp); - - //********************************** - // Luma - //********************************** - if (componentMask == PICTURE_BUFFER_DESC_FULL_MASK || componentMask == PICTURE_BUFFER_DESC_LUMA_MASK) { - - PictureResidual( - inputSamples->bufferY + inputLumaOffset, - inputSamples->strideY, - predSamples->bufferY + predLumaOffset, - predSamples->strideY, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - tuSize, - tuSize); - - EstimateTransform( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - ((EB_S16*)transform16bit->bufferY) + scratchLumaOffset, - 64, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_8BIT, - (EB_BOOL)(tuSize == MIN_PU_SIZE), - contextPtr->transCoeffShapeLuma); - - - UnifiedQuantizeInvQuantize( - - contextPtr, - - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferY) + scratchLumaOffset, - 64, - ((EB_S16*)coeffSamplesTB->bufferY) + scratchLumaOffset, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - qp, - inputSamples->bitDepth, - tuSize, - sliceType, - &(countNonZeroCoeffs[0]), - contextPtr->transCoeffShapeLuma, - - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - 0, - enableContouringQCUpdateFlag, - COMPONENT_LUMA, - temporalLayerIndex, - dZoffset, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost); - - tuPtr->lumaCbf = countNonZeroCoeffs[0] ? EB_TRUE : EB_FALSE; - - if (contextPtr->transCoeffShapeLuma && tuPtr->lumaCbf) { - - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - (tuSize >> 1)); - - if (contextPtr->transCoeffShapeLuma == N4_SHAPE) { - - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - (tuSize >> 2)); - } - } - - } - - if (componentMask == PICTURE_BUFFER_DESC_FULL_MASK || componentMask == PICTURE_BUFFER_DESC_CHROMA_MASK){ - - //********************************** - // Cb - //********************************** - PictureResidual( - inputSamples->bufferCb + inputCbOffset, - inputSamples->strideCb, - predSamples->bufferCb + predCbOffset, - predSamples->strideCb, - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - 32, - tuSize, - tuSize); - - EstimateTransform( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - 32, - ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, - 32, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_8BIT, - EB_FALSE, - contextPtr->transCoeffShapeChroma); - - - UnifiedQuantizeInvQuantize( - - contextPtr, - - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, - 32, - ((EB_S16*)coeffSamplesTB->bufferCb) + scratchCbOffset, - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - chromaQp, - inputSamples->bitDepth, - tuSize, - sliceType, - &(countNonZeroCoeffs[1]), - contextPtr->transCoeffShapeChroma, - - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - useDeltaQp == EB_TRUE ? contextPtr->forceCbfFlag : 0, - enableContouringQCUpdateFlag, - COMPONENT_CHROMA, - temporalLayerIndex, - 0, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost); - - tuPtr->cbCbf = countNonZeroCoeffs[1] ? EB_TRUE : EB_FALSE; - - if (contextPtr->transCoeffShapeChroma && tuPtr->cbCbf) { - - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - residual16bit->strideCb, - (tuSize >> 1)); - - if (contextPtr->transCoeffShapeChroma == PF_N4) { - - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - residual16bit->strideCb, - (tuSize >> 2)); - } - } - //********************************** - // Cr - //********************************** - - PictureResidual( - inputSamples->bufferCr + inputCrOffset, - inputSamples->strideCr, - predSamples->bufferCr + predCrOffset, - predSamples->strideCr, - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - tuSize, - tuSize); - - EstimateTransform( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, - 32, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_8BIT, - EB_FALSE, - contextPtr->transCoeffShapeChroma); - - - UnifiedQuantizeInvQuantize( - - contextPtr, - - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, - 32, - ((EB_S16*)coeffSamplesTB->bufferCr) + scratchCrOffset, - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - chromaQp, - inputSamples->bitDepth, - tuSize, - sliceType, - &(countNonZeroCoeffs[2]), - contextPtr->transCoeffShapeChroma, - - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - 0, - enableContouringQCUpdateFlag, - COMPONENT_CHROMA, - temporalLayerIndex, - 0, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost); - - tuPtr->crCbf = countNonZeroCoeffs[2] ? EB_TRUE : EB_FALSE; - if (contextPtr->transCoeffShapeChroma && tuPtr->crCbf) { - - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - (tuSize >> 1)); - - if (contextPtr->transCoeffShapeChroma == PF_N4) { - - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - (tuSize >> 2)); - } - } - } - - tuPtr->transCoeffShapeLuma = contextPtr->transCoeffShapeLuma; - tuPtr->transCoeffShapeChroma = contextPtr->transCoeffShapeChroma; - tuPtr->nzCoefCount[0] = (EB_U16)countNonZeroCoeffs[0]; - tuPtr->nzCoefCount[1] = (EB_U16)countNonZeroCoeffs[1]; - tuPtr->nzCoefCount[2] = (EB_U16)countNonZeroCoeffs[2]; - - return; -} - -static void EncodeGenerateReconIntra4x4( - EncDecContext_t *contextPtr, - EB_U32 originX, - EB_U32 originY, - EbPictureBufferDesc_t *predSamples, // no basis/offset - EbPictureBufferDesc_t *residual16bit, // no basis/offset - EB_S16 *transformScratchBuffer, - EB_U32 componentMask) -{ - EB_U32 predLumaOffset; - EB_U32 predChromaOffset; - EB_U32 scratchLumaOffset; - EB_U32 scratchChromaOffset; - EB_U32 reconLumaOffset; - EB_U32 reconChromaOffset; - - CodingUnit_t *cuPtr = contextPtr->cuPtr; - TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; - EB_U32 tuSize = MIN_PU_SIZE; - EbPictureBufferDesc_t *reconSamples = predSamples; + TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; + + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + const EB_U16 shift_bit = (tuSize == MIN_PU_SIZE) ? 0 : subWidthCMinus1; + EB_U16 tuChromaOffset = 0; + if (colorFormat == EB_YUV422 && secondChroma) { + tuChromaOffset = tuSize >> 1; + } + EB_BOOL cbCbf=secondChroma?tuPtr->cbCbf2:tuPtr->cbCbf; + EB_BOOL crCbf=secondChroma?tuPtr->crCbf2:tuPtr->crCbf; // *Note - The prediction is built in-place in the Recon buffer. It is overwritten with Reconstructed // samples if the CBF==1 && SKIP==False - //********************************** - // Luma - //********************************** - - if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { - - predLumaOffset = (predSamples->originY+originY) * predSamples->strideY + (predSamples->originX+originX); - scratchLumaOffset = ((originY & (63)) * 64) + (originX & (63)); - reconLumaOffset = (reconSamples->originY+originY) * reconSamples->strideY + (reconSamples->originX+originX); - if (tuPtr->lumaCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - - EncodeInvTransform( - // DC_ONLY Sahpe not supported for DST - DEFAULT_SHAPE, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_8BIT, - (EB_BOOL)(tuSize == MIN_PU_SIZE)); - - AdditionKernel_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1][tuSize >> 3]( - predSamples->bufferY + predLumaOffset, - predSamples->strideY, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - reconSamples->bufferY + reconLumaOffset, - reconSamples->strideY, - tuSize, - tuSize); - } - } - - //********************************** - // Chroma - //********************************** - - if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { - - predChromaOffset = (((predSamples->originY+originY) >> 1) * predSamples->strideCb) + ((predSamples->originX+originX) >> 1); - scratchChromaOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - reconChromaOffset = (((reconSamples->originY+originY) >> 1) * reconSamples->strideCb) + ((reconSamples->originX+originX) >> 1); - //********************************** - // Cb - //********************************** - if (tuPtr->cbCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - - EncodeInvTransform( - // DC_ONLY Sahpe not supported for DST - DEFAULT_SHAPE, - ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, - ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_8BIT, - EB_FALSE); - - AdditionKernel_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1][tuSize >> 3]( - predSamples->bufferCb + predChromaOffset, - predSamples->strideCb, - ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, - reconSamples->bufferCb + reconChromaOffset, - reconSamples->strideCb, - tuSize, - tuSize); - } - - //********************************** - // Cr - //********************************** - predChromaOffset = (((predSamples->originY+originY) >> 1) * predSamples->strideCr) + ((predSamples->originX+originX) >> 1); - scratchChromaOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - reconChromaOffset = (((reconSamples->originY+originY) >> 1) * reconSamples->strideCr) + ((reconSamples->originX+originX) >> 1); - if (tuPtr->crCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - - EncodeInvTransform( - // DC_ONLY Sahpe not supported for DST - DEFAULT_SHAPE, - ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, - ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_8BIT, - EB_FALSE); - - AdditionKernel_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1][tuSize >> 3]( - predSamples->bufferCr + predChromaOffset, - predSamples->strideCr, - ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, - reconSamples->bufferCr + reconChromaOffset, - reconSamples->strideCr, - tuSize, - tuSize); - } - } - - return; -} - -static void EncodeLoopIntra4x416bit( - EncDecContext_t *contextPtr, - LargestCodingUnit_t *lcuPtr, - EB_U32 originX, - EB_U32 originY, - EB_U32 cbQp, - EbPictureBufferDesc_t *predSamples, // no basis/offset - EbPictureBufferDesc_t *coeffSamplesTB, // lcu based - EbPictureBufferDesc_t *residual16bit, // no basis/offset - EbPictureBufferDesc_t *transform16bit, // no basis/offset - EB_S16 *transformScratchBuffer, - EB_U32 *countNonZeroCoeffs, - EB_U32 componentMask, - EB_U32 useDeltaQp, - CabacEncodeContext_t *cabacEncodeCtxPtr, - EB_U32 intraLumaMode, - CabacCost_t *CabacCost, - EB_U32 dZoffset) - -{ - - EB_U32 chromaQp = cbQp; - - CodingUnit_t *cuPtr = contextPtr->cuPtr; - TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; - EB_PICTURE sliceType = lcuPtr->pictureControlSetPtr->sliceType; - EB_U32 temporalLayerIndex = lcuPtr->pictureControlSetPtr->temporalLayerIndex; - EB_U32 qp = cuPtr->qp; - EB_U32 tuSize = MIN_PU_SIZE; - EbPictureBufferDesc_t *inputSamples16bit = contextPtr->inputSample16bitBuffer; - EbPictureBufferDesc_t *predSamples16bit = predSamples; - - const EB_U32 inputLumaOffset = ((originY & (63)) * inputSamples16bit->strideY ) + (originX & (63)); - const EB_U32 inputCbOffset = (((originY & (63)) >> 1) * inputSamples16bit->strideCb) + ((originX & (63)) >> 1); - const EB_U32 inputCrOffset = (((originY & (63)) >> 1) * inputSamples16bit->strideCr) + ((originX & (63)) >> 1); - - const EB_U32 predLumaOffset = (( predSamples16bit->originY+originY) * predSamples16bit->strideY ) + ( predSamples16bit->originX+originX); - const EB_U32 predCbOffset = (((predSamples16bit->originY+originY) >> 1) * predSamples16bit->strideCb) + ((predSamples16bit->originX+originX) >> 1); - const EB_U32 predCrOffset = (((predSamples16bit->originY+originY) >> 1) * predSamples16bit->strideCr) + ((predSamples16bit->originX+originX) >> 1); - const EB_U32 scratchLumaOffset = ((originY & (63)) * 64) + (originX & (63)); - const EB_U32 scratchCbOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - const EB_U32 scratchCrOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - - EB_U8 enableContouringQCUpdateFlag; - - enableContouringQCUpdateFlag = DeriveContouringClass( - lcuPtr->pictureControlSetPtr->ParentPcsPtr, - lcuPtr->index, - cuPtr->leafIndex) && (cuPtr->qp < lcuPtr->pictureControlSetPtr->pictureQp); - - //Update QP for Quant - qp += QP_BD_OFFSET; - - chromaQp += QP_BD_OFFSET; - - if ( componentMask == PICTURE_BUFFER_DESC_FULL_MASK || componentMask == PICTURE_BUFFER_DESC_LUMA_MASK){ - - //********************************** - // Luma - //********************************** - PictureResidual16bit( - ((EB_U16*)inputSamples16bit->bufferY) + inputLumaOffset, - inputSamples16bit->strideY, - ((EB_U16*)predSamples16bit->bufferY) + predLumaOffset, - predSamples16bit->strideY, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - tuSize, - tuSize); - - EncodeTransform( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - ((EB_S16*)transform16bit->bufferY) + scratchLumaOffset, - 64, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - (EB_BOOL)(tuSize == MIN_PU_SIZE), - contextPtr->transCoeffShapeLuma); - - UnifiedQuantizeInvQuantize( - - contextPtr, - - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferY) + scratchLumaOffset, - 64, - ((EB_S16*)coeffSamplesTB->bufferY) + scratchLumaOffset, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - qp, - EB_10BIT, - tuSize, - sliceType, - &(countNonZeroCoeffs[0]), - contextPtr->transCoeffShapeLuma, - - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - 0, - enableContouringQCUpdateFlag, - COMPONENT_LUMA, - temporalLayerIndex, - dZoffset, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost); - - tuPtr->lumaCbf = countNonZeroCoeffs[0] ? EB_TRUE : EB_FALSE; - - if (contextPtr->transCoeffShapeLuma && tuPtr->lumaCbf) { - - if (contextPtr->transCoeffShapeLuma == N2_SHAPE || contextPtr->transCoeffShapeLuma == N4_SHAPE) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - (tuSize >> 1)); - } - - if (contextPtr->transCoeffShapeLuma == N4_SHAPE) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - (tuSize >> 2)); - } - } - - - - } - - if (componentMask == PICTURE_BUFFER_DESC_FULL_MASK || componentMask == PICTURE_BUFFER_DESC_CHROMA_MASK){ - - //********************************** - // Cb - //********************************** - PictureResidual16bit( - ((EB_U16*)inputSamples16bit->bufferCb) + inputCbOffset, - inputSamples16bit->strideCb, - ((EB_U16*)predSamples16bit->bufferCb) + predCbOffset, - predSamples16bit->strideCb, - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - 32, - tuSize, - tuSize); - - EncodeTransform( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - 32, - ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, - 32, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - EB_FALSE, - contextPtr->transCoeffShapeChroma); - - UnifiedQuantizeInvQuantize( - - contextPtr, - - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferCb) + scratchCbOffset, - 32, - ((EB_S16*)coeffSamplesTB->bufferCb) + scratchCbOffset, - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - chromaQp, - EB_10BIT, - tuSize, - sliceType, - &(countNonZeroCoeffs[1]), - contextPtr->transCoeffShapeChroma, - - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - 0, - enableContouringQCUpdateFlag, - COMPONENT_CHROMA, - temporalLayerIndex, - 0, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost); - - tuPtr->cbCbf = countNonZeroCoeffs[1] ? EB_TRUE : EB_FALSE; - - if (contextPtr->transCoeffShapeChroma && tuPtr->cbCbf) { - - if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - residual16bit->strideCb, - (tuSize >> 2)); - } - - if (contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCb) + scratchCbOffset, - residual16bit->strideCb, - (tuSize >> 3)); - } - } - - - //********************************** - // Cr - //********************************** - PictureResidual16bit( - ((EB_U16*)inputSamples16bit->bufferCr) + inputCrOffset, - inputSamples16bit->strideCr, - ((EB_U16*)predSamples16bit->bufferCr) + predCrOffset, - predSamples16bit->strideCr, - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - tuSize, - tuSize); - - EncodeTransform( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - 32, - ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, - 32, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - EB_FALSE, - contextPtr->transCoeffShapeChroma); - - UnifiedQuantizeInvQuantize( - - contextPtr, - - lcuPtr->pictureControlSetPtr, - ((EB_S16*)transform16bit->bufferCr) + scratchCrOffset, - 32, - ((EB_S16*)coeffSamplesTB->bufferCr) + scratchCrOffset, - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - chromaQp, - EB_10BIT, - tuSize, - sliceType, - &(countNonZeroCoeffs[2]), - contextPtr->transCoeffShapeChroma, - - contextPtr->cleanSparseCeoffPfEncDec, - contextPtr->pmpMaskingLevelEncDec, - cuPtr->predictionModeFlag, - useDeltaQp == EB_TRUE ? contextPtr->forceCbfFlag : 0, - enableContouringQCUpdateFlag, - COMPONENT_CHROMA, - temporalLayerIndex, - 0, - cabacEncodeCtxPtr, - contextPtr->fullLambda, - intraLumaMode, - EB_INTRA_CHROMA_DM, - CabacCost); - - tuPtr->crCbf = countNonZeroCoeffs[2] ? EB_TRUE : EB_FALSE; - - if (contextPtr->transCoeffShapeChroma && tuPtr->crCbf) { - - if (contextPtr->transCoeffShapeChroma == PF_N2 || contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - residual16bit->strideCr, - (tuSize >> 2)); - } - - if (contextPtr->transCoeffShapeChroma == PF_N4) { - PfZeroOutUselessQuadrants( - ((EB_S16*)residual16bit->bufferCr) + scratchCrOffset, - residual16bit->strideCr, - (tuSize >> 3)); - } - } - - - } - - tuPtr->transCoeffShapeLuma = contextPtr->transCoeffShapeLuma; - tuPtr->transCoeffShapeChroma = contextPtr->transCoeffShapeChroma; - tuPtr->nzCoefCount[0] = (EB_U16)countNonZeroCoeffs[0]; - tuPtr->nzCoefCount[1] = (EB_U16)countNonZeroCoeffs[1]; - tuPtr->nzCoefCount[2] = (EB_U16)countNonZeroCoeffs[2]; - return; -} - -static void EncodeGenerateReconIntra4x416bit( - EncDecContext_t *contextPtr, - EB_U32 originX, - EB_U32 originY, - EbPictureBufferDesc_t *predSamples, // no basis/offset - EbPictureBufferDesc_t *residual16bit, // no basis/offset - EB_S16 *transformScratchBuffer, - EB_U32 componentMask) -{ - - EB_U32 predLumaOffset; - EB_U32 predChromaOffset; - EB_U32 scratchLumaOffset; - EB_U32 scratchChromaOffset; - EB_U32 reconLumaOffset; - EB_U32 reconChromaOffset; - - CodingUnit_t *cuPtr = contextPtr->cuPtr; - TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; - EB_U32 tuSize = MIN_PU_SIZE; - - //********************************** - // Luma - //********************************** - - if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { - - predLumaOffset = (predSamples->originY + originY)* predSamples->strideY + (predSamples->originX + originX); - scratchLumaOffset = ((originY & (63)) * 64) + (originX & (63)); + //********************************** + // Luma + //********************************** + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { + predLumaOffset = (predSamples->originY+originY) * predSamples->strideY + (predSamples->originX+originX); + scratchLumaOffset = ((originY & (63)) * 64) + (originX & (63)); reconLumaOffset = (predSamples->originY + originY)* predSamples->strideY + (predSamples->originX + originX); - if (tuPtr->lumaCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - EncodeInvTransform( - // DC_ONLY Sahpe not supported for DST - DEFAULT_SHAPE, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, - 64, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - (EB_BOOL)(tuSize == MIN_PU_SIZE)); + if (tuPtr->lumaCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { + EncodeInvTransform( + (tuSize==MIN_PU_SIZE)?EB_FALSE:(tuPtr->transCoeffShapeLuma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[0]), + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + 64, + ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, + 64, + tuSize, + transformScratchBuffer, + BIT_INCREMENT_10BIT, + (EB_BOOL)(tuSize == MIN_PU_SIZE)); - AdditionKernel_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( + AdditionKernel_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( (EB_U16*)predSamples->bufferY + predLumaOffset, predSamples->strideY, ((EB_S16*)residual16bit->bufferY) + scratchLumaOffset, @@ -2145,103 +1561,115 @@ static void EncodeGenerateReconIntra4x416bit( predSamples->strideY, tuSize, tuSize); + } + } + //********************************** + // Chroma + //********************************** - } - - } + if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { + predChromaOffset = (((predSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCb) + + ((predSamples->originX + originX) >> subWidthCMinus1); + scratchChromaOffset = (((originY + tuChromaOffset) & 63) >> subHeightCMinus1) * (64 >> subWidthCMinus1) + + ((originX & 63) >> subWidthCMinus1); + reconChromaOffset = (((predSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCb) + + ((predSamples->originX + originX) >> subWidthCMinus1); - //********************************** - // Chroma - //********************************** - if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { - - //********************************** - // Cb - //********************************** - predChromaOffset = (((predSamples->originY + originY) >> 1) * predSamples->strideCb) + ((predSamples->originX + originX) >> 1); - scratchChromaOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - reconChromaOffset = (((predSamples->originY + originY) >> 1) * predSamples->strideCb) + ((predSamples->originX + originX) >> 1); - if (tuPtr->cbCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - - EncodeInvTransform( - // DC_ONLY Sahpe not supported for DST - DEFAULT_SHAPE, - ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, - ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - EB_FALSE); + //********************************** + // Cb + //********************************** + if (cbCbf== EB_TRUE && cuPtr->skipFlag == EB_FALSE) { + EncodeInvTransform( + (tuSize==MIN_PU_SIZE)?EB_FALSE:(secondChroma ? (tuPtr->transCoeffShapeChroma2 == ONLY_DC_SHAPE || tuPtr->isOnlyDc2[0]) : (tuPtr->transCoeffShapeChroma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[1])), + ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, + residual16bit->strideCb, + ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, + residual16bit->strideCb, + tuSize >> shift_bit, + transformScratchBuffer, + BIT_INCREMENT_10BIT, + EB_FALSE); - AdditionKernel_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( + AdditionKernel_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( (EB_U16*)predSamples->bufferCb + predChromaOffset, - predSamples->strideCb, - ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, - 32, + predSamples->strideCb, + ((EB_S16*)residual16bit->bufferCb) + scratchChromaOffset, + residual16bit->strideCb, (EB_U16*)predSamples->bufferCb + reconChromaOffset, predSamples->strideCb, - tuSize, - tuSize); - - } + tuSize >> shift_bit, + tuSize >> shift_bit); + } - //********************************** - // Cr - //********************************** - predChromaOffset = (((predSamples->originY + originY) >> 1) * predSamples->strideCr) + ((predSamples->originX + originX) >> 1); - scratchChromaOffset = (((originY & (63)) >> 1) * 32) + ((originX & (63)) >> 1); - reconChromaOffset = (((predSamples->originY + originY) >> 1) * predSamples->strideCr) + ((predSamples->originX + originX) >> 1); - if (tuPtr->crCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { - - EncodeInvTransform( - // DC_ONLY Sahpe not supported for DST - DEFAULT_SHAPE, - ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, - ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, - tuSize, - transformScratchBuffer, - BIT_INCREMENT_10BIT, - EB_FALSE); + //********************************** + // Cr + //********************************** + predChromaOffset = (((predSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCr) + + ((predSamples->originX + originX) >> subWidthCMinus1); + scratchChromaOffset = (((originY + tuChromaOffset) & 63) >> subHeightCMinus1) * (64 >> subWidthCMinus1) + + ((originX & 63) >> subWidthCMinus1); + reconChromaOffset = (((predSamples->originY + originY + tuChromaOffset) >> subHeightCMinus1) * predSamples->strideCr) + + ((predSamples->originX + originX) >> subWidthCMinus1); + + if (crCbf == EB_TRUE && cuPtr->skipFlag == EB_FALSE) { + EncodeInvTransform( + (tuSize==MIN_PU_SIZE)?EB_FALSE:(secondChroma ? (tuPtr->transCoeffShapeChroma2 == ONLY_DC_SHAPE || tuPtr->isOnlyDc2[1]) : (tuPtr->transCoeffShapeChroma == ONLY_DC_SHAPE || tuPtr->isOnlyDc[2])), + ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, + residual16bit->strideCr, + ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, + residual16bit->strideCr, + tuSize >> shift_bit, + transformScratchBuffer, + BIT_INCREMENT_10BIT, + EB_FALSE); - AdditionKernel_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( + AdditionKernel_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( (EB_U16*)predSamples->bufferCr + predChromaOffset, - predSamples->strideCr, - ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, - 32, + predSamples->strideCr, + ((EB_S16*)residual16bit->bufferCr) + scratchChromaOffset, + residual16bit->strideCr, (EB_U16*)predSamples->bufferCr + reconChromaOffset, predSamples->strideCr, - tuSize, - tuSize); - - } - } + tuSize >> shift_bit, + tuSize >> shift_bit); + } + } - return; + return; } -static EB_ENCODE_LOOP_INTRA_4x4_FUNC_PTR EncodeLoopIntra4x4FunctionTable[2] = +static EB_ENCODE_LOOP_FUNC_PTR EncodeLoopFunctionTable[2] = { - EncodeLoopIntra4x4, - EncodeLoopIntra4x416bit + EncodeLoop, + EncodeLoop16bit }; -EB_GENERATE_RECON_INTRA_4x4_FUNC_PTR EncodeGenerateReconIntra4x4FunctionPtr[2] = +EB_GENERATE_RECON_FUNC_PTR EncodeGenerateReconFunctionPtr[2] = { - EncodeGenerateReconIntra4x4, - EncodeGenerateReconIntra4x416bit + EncodeGenerateRecon, + EncodeGenerateRecon16bit }; + EB_GENERATE_INTRA_SAMPLES_FUNC_PTR GenerateIntraReferenceSamplesFuncTable[2] = { GenerateIntraReferenceSamplesEncodePass, GenerateIntraReference16bitSamplesEncodePass }; +EB_GENERATE_LUMA_INTRA_SAMPLES_FUNC_PTR GenerateLumaIntraReferenceSamplesFuncTable[2] = +{ + GenerateLumaIntraReferenceSamplesEncodePass, + GenerateLumaIntraReference16bitSamplesEncodePass +}; + +EB_GENERATE_CHROMA_INTRA_SAMPLES_FUNC_PTR GenerateChromaIntraReferenceSamplesFuncTable[2] = +{ + GenerateChromaIntraReferenceSamplesEncodePass, + GenerateChromaIntraReference16bitSamplesEncodePass +}; + EB_ENC_PASS_INTRA_FUNC_PTR EncodePassIntraPredictionFuncTable[2] = { EncodePassIntraPrediction, @@ -2766,7 +2194,7 @@ EB_ERRORTYPE QpmDeriveBeaAndSkipQpmFlagLcu( && (lcuPtr->pictureControlSetPtr->ParentPcsPtr->edgeResultsPtr[lcuIndex].edgeBlockNum > 0)) ? EB_TRUE : EB_FALSE; - contextPtr->backgorundEnhancement = EB_FALSE; + contextPtr->backgorundEnhancement = EB_FALSE; contextPtr->backgorundEnhancement = EB_FALSE; @@ -3268,8 +2696,13 @@ void EncodePassPackLcu( EB_U32 lcuOriginX, EB_U32 lcuOriginY, EB_U32 lcuWidth, - EB_U32 lcuHeight) { + EB_U32 lcuHeight) +{ + const EB_COLOR_FORMAT colorFormat = inputPicture->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + //TODO: Jing, need change here later if ((sequenceControlSetPtr->staticConfig.compressedTenBitFormat == 1)) { @@ -3313,12 +2746,17 @@ void EncodePassPackLcu( } else { - const EB_U32 inputLumaOffset = ((lcuOriginY + inputPicture->originY) * inputPicture->strideY) + (lcuOriginX + inputPicture->originX); - const EB_U32 inputBitIncLumaOffset = ((lcuOriginY + inputPicture->originY) * inputPicture->strideBitIncY) + (lcuOriginX + inputPicture->originX); - const EB_U32 inputCbOffset = (((lcuOriginY + inputPicture->originY) >> 1) * inputPicture->strideCb) + ((lcuOriginX + inputPicture->originX) >> 1); - const EB_U32 inputBitIncCbOffset = (((lcuOriginY + inputPicture->originY) >> 1) * inputPicture->strideBitIncCb) + ((lcuOriginX + inputPicture->originX) >> 1); - const EB_U32 inputCrOffset = (((lcuOriginY + inputPicture->originY) >> 1) * inputPicture->strideCr) + ((lcuOriginX + inputPicture->originX) >> 1); - const EB_U32 inputBitIncCrOffset = (((lcuOriginY + inputPicture->originY) >> 1) * inputPicture->strideBitIncCr) + ((lcuOriginX + inputPicture->originX) >> 1); + const EB_U32 inputLumaOffset = ((lcuOriginY + inputPicture->originY) * inputPicture->strideY) + (lcuOriginX + inputPicture->originX); + const EB_U32 inputBitIncLumaOffset = ((lcuOriginY + inputPicture->originY) * inputPicture->strideBitIncY) + (lcuOriginX + inputPicture->originX); + const EB_U32 inputCbOffset = ((lcuOriginX + inputPicture->originX) >> subWidthCMinus1) + + (((lcuOriginY + inputPicture->originY) >> subHeightCMinus1) * inputPicture->strideCb); + const EB_U32 inputCrOffset = ((lcuOriginX + inputPicture->originX) >> subWidthCMinus1) + + (((lcuOriginY + inputPicture->originY) >> subHeightCMinus1) * inputPicture->strideCr); + + const EB_U32 inputBitIncCrOffset = ((lcuOriginX + inputPicture->originX) >> subWidthCMinus1) + + (((lcuOriginY + inputPicture->originY) >> subHeightCMinus1) * inputPicture->strideBitIncCr); + const EB_U32 inputBitIncCbOffset = ((lcuOriginX + inputPicture->originX) >> subWidthCMinus1) + + (((lcuOriginY + inputPicture->originY) >> subHeightCMinus1) * inputPicture->strideBitIncCb); Pack2D_SRC( inputPicture->bufferY + inputLumaOffset, @@ -3337,9 +2775,9 @@ void EncodePassPackLcu( inputPicture->bufferBitIncCb + inputBitIncCbOffset, inputPicture->strideBitIncCr, (EB_U16 *)contextPtr->inputSample16bitBuffer->bufferCb, - MAX_LCU_SIZE_CHROMA, - lcuWidth >> 1, - lcuHeight >> 1); + MAX_LCU_SIZE >> subWidthCMinus1, + lcuWidth >> subWidthCMinus1, + lcuHeight >> subHeightCMinus1); Pack2D_SRC( @@ -3348,9 +2786,9 @@ void EncodePassPackLcu( inputPicture->bufferBitIncCr + inputBitIncCrOffset, inputPicture->strideBitIncCr, (EB_U16 *)contextPtr->inputSample16bitBuffer->bufferCr, - MAX_LCU_SIZE_CHROMA, - lcuWidth >> 1, - lcuHeight >> 1); + MAX_LCU_SIZE >> subWidthCMinus1, + lcuWidth >> subWidthCMinus1, + lcuHeight >> subHeightCMinus1); } } @@ -3385,8 +2823,9 @@ EB_EXTERN void EncodePass( EB_BOOL enableSaoFlag, EncDecContext_t *contextPtr) { - EB_BOOL is16bit = contextPtr->is16bit; + EB_COLOR_FORMAT colorFormat = contextPtr->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; EbPictureBufferDesc_t *reconBuffer = is16bit ? pictureControlSetPtr->reconPicture16bitPtr : pictureControlSetPtr->reconPicturePtr; EbPictureBufferDesc_t *coeffBufferTB = lcuPtr->quantizedCoeff; @@ -3397,85 +2836,85 @@ EB_EXTERN void EncodePass( mdcontextPtr = contextPtr->mdContext; inputPicture = contextPtr->inputSamples = (EbPictureBufferDesc_t*)pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; - LcuStat_t *lcuStatPtr = &(pictureControlSetPtr->ParentPcsPtr->lcuStatArray[tbAddr]); + LcuStat_t *lcuStatPtr = &(pictureControlSetPtr->ParentPcsPtr->lcuStatArray[tbAddr]); + // TMVP - TmvpUnit_t *tmvpMapWritePtr; - EB_U32 tmvpMapHorizontalStartIndex; - EB_U32 tmvpMapVerticalStartIndex; - EB_U32 tmvpMapHorizontalEndIndex; - EB_U32 tmvpMapVerticalEndIndex; - EB_U32 tmvpMapIndex; - EB_U32 mvCompressionUnitSizeMinus1 = (1 << LOG_MV_COMPRESS_UNIT_SIZE) - 1; + TmvpUnit_t *tmvpMapWritePtr; + EB_U32 tmvpMapHorizontalStartIndex; + EB_U32 tmvpMapVerticalStartIndex; + EB_U32 tmvpMapHorizontalEndIndex; + EB_U32 tmvpMapVerticalEndIndex; + EB_U32 tmvpMapIndex; + EB_U32 mvCompressionUnitSizeMinus1 = (1 << LOG_MV_COMPRESS_UNIT_SIZE) - 1; // DLF - EB_U32 startIndex; - EB_U8 blk4x4IndexX; - - EB_U8 blk4x4IndexY; - EB_BOOL availableCoeff; + EB_U32 startIndex; + EB_U8 blk4x4IndexX; + EB_U8 blk4x4IndexY; + EB_BOOL availableCoeff; // QP Neighbor Arrays - EB_BOOL isDeltaQpNotCoded = EB_TRUE; + EB_BOOL isDeltaQpNotCoded = EB_TRUE; // LCU Stats - EB_U32 lcuWidth = MIN(sequenceControlSetPtr->lcuSize, sequenceControlSetPtr->lumaWidth - lcuOriginX); - EB_U32 lcuHeight = MIN(sequenceControlSetPtr->lcuSize, sequenceControlSetPtr->lumaHeight - lcuOriginY); + EB_U32 lcuWidth = MIN(sequenceControlSetPtr->lcuSize, sequenceControlSetPtr->lumaWidth - lcuOriginX); + EB_U32 lcuHeight = MIN(sequenceControlSetPtr->lcuSize, sequenceControlSetPtr->lumaHeight - lcuOriginY); // SAO - EB_S64 saoLumaBestCost; - EB_S64 saoChromaBestCost; + EB_S64 saoLumaBestCost; + EB_S64 saoChromaBestCost; // MV merge mode - EB_U32 yCbf; - EB_U32 cbCbf; - EB_U32 crCbf; - EB_U64 yCoeffBits; - EB_U64 cbCoeffBits; - EB_U64 crCoeffBits; - EB_U64 yFullDistortion[DIST_CALC_TOTAL]; - EB_U64 yTuFullDistortion[DIST_CALC_TOTAL]; - EB_U32 countNonZeroCoeffs[3]; - EB_U64 yTuCoeffBits; - EB_U64 cbTuCoeffBits; - EB_U64 crTuCoeffBits; - EB_U32 lumaShift; - EB_U32 scratchLumaOffset; - EncodeContext_t *encodeContextPtr; - EB_U32 lcuRowIndex = lcuOriginY / MAX_LCU_SIZE; + EB_U32 yCbf=0; + EB_U32 cbCbf=0; + EB_U32 crCbf=0; + EB_U32 cbCbf2=0; + EB_U32 crCbf2=0; + EB_U64 yCoeffBits; + EB_U64 cbCoeffBits; + EB_U64 crCoeffBits; + EB_U64 yFullDistortion[DIST_CALC_TOTAL]; + EB_U64 yTuFullDistortion[DIST_CALC_TOTAL]; + EB_U32 countNonZeroCoeffs[3]; + EB_U64 yTuCoeffBits; + EB_U64 cbTuCoeffBits; + EB_U64 crTuCoeffBits; + EB_U32 lumaShift; + EB_U32 scratchLumaOffset; + EB_U32 lcuRowIndex = lcuOriginY / MAX_LCU_SIZE; + EncodeContext_t *encodeContextPtr = NULL; // Dereferencing early - NeighborArrayUnit_t *epModeTypeNeighborArray = pictureControlSetPtr->epModeTypeNeighborArray; - NeighborArrayUnit_t *epIntraLumaModeNeighborArray = pictureControlSetPtr->epIntraLumaModeNeighborArray; - NeighborArrayUnit_t *epMvNeighborArray = pictureControlSetPtr->epMvNeighborArray; - NeighborArrayUnit_t *epLumaReconNeighborArray = is16bit ? pictureControlSetPtr->epLumaReconNeighborArray16bit : pictureControlSetPtr->epLumaReconNeighborArray; - NeighborArrayUnit_t *epCbReconNeighborArray = is16bit ? pictureControlSetPtr->epCbReconNeighborArray16bit : pictureControlSetPtr->epCbReconNeighborArray; - NeighborArrayUnit_t *epCrReconNeighborArray = is16bit ? pictureControlSetPtr->epCrReconNeighborArray16bit : pictureControlSetPtr->epCrReconNeighborArray; - NeighborArrayUnit_t *epSkipFlagNeighborArray = pictureControlSetPtr->epSkipFlagNeighborArray; - NeighborArrayUnit_t *epLeafDepthNeighborArray = pictureControlSetPtr->epLeafDepthNeighborArray; - - EB_BOOL constrainedIntraFlag = pictureControlSetPtr->constrainedIntraFlag; - EB_BOOL enableStrongIntraSmoothing = sequenceControlSetPtr->enableStrongIntraSmoothing; - CodingUnit_t **codedLeafArrayPtr = lcuPtr->codedLeafArrayPtr; + NeighborArrayUnit_t *epModeTypeNeighborArray = pictureControlSetPtr->epModeTypeNeighborArray; + NeighborArrayUnit_t *epIntraLumaModeNeighborArray = pictureControlSetPtr->epIntraLumaModeNeighborArray; + NeighborArrayUnit_t *epMvNeighborArray = pictureControlSetPtr->epMvNeighborArray; + NeighborArrayUnit_t *epLumaReconNeighborArray = is16bit ? pictureControlSetPtr->epLumaReconNeighborArray16bit : pictureControlSetPtr->epLumaReconNeighborArray; + NeighborArrayUnit_t *epCbReconNeighborArray = is16bit ? pictureControlSetPtr->epCbReconNeighborArray16bit : pictureControlSetPtr->epCbReconNeighborArray; + NeighborArrayUnit_t *epCrReconNeighborArray = is16bit ? pictureControlSetPtr->epCrReconNeighborArray16bit : pictureControlSetPtr->epCrReconNeighborArray; + NeighborArrayUnit_t *epSkipFlagNeighborArray = pictureControlSetPtr->epSkipFlagNeighborArray; + NeighborArrayUnit_t *epLeafDepthNeighborArray = pictureControlSetPtr->epLeafDepthNeighborArray; + + EB_BOOL constrainedIntraFlag = pictureControlSetPtr->constrainedIntraFlag; + EB_BOOL enableStrongIntraSmoothing = sequenceControlSetPtr->enableStrongIntraSmoothing; + CodingUnit_t **codedLeafArrayPtr = lcuPtr->codedLeafArrayPtr; EB_BOOL dlfEnableFlag = (EB_BOOL)(!sequenceControlSetPtr->staticConfig.disableDlfFlag) && (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag || - sequenceControlSetPtr->staticConfig.reconEnabled); - + sequenceControlSetPtr->staticConfig.reconEnabled); dlfEnableFlag = contextPtr->allowEncDecMismatch ? EB_FALSE : dlfEnableFlag; const EB_BOOL isIntraLCU = contextPtr->mdContext->limitIntra ? isIntraPresent(lcuPtr) : EB_TRUE; EB_BOOL doRecon = (EB_BOOL)(contextPtr->mdContext->limitIntra == 0 || isIntraLCU == 1) || - pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag || - sequenceControlSetPtr->staticConfig.reconEnabled; + pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag || + sequenceControlSetPtr->staticConfig.reconEnabled; - - CabacCost_t *cabacCost = pictureControlSetPtr->cabacCost; - EntropyCoder_t *coeffEstEntropyCoderPtr = pictureControlSetPtr->coeffEstEntropyCoderPtr; - EB_U8 cuItr; - EB_U32 dZoffset = 0; + CabacCost_t *cabacCost = pictureControlSetPtr->cabacCost; + EntropyCoder_t *coeffEstEntropyCoderPtr = pictureControlSetPtr->coeffEstEntropyCoderPtr; + EB_U8 cuItr; + EB_U32 dZoffset = 0; if (!lcuStatPtr->stationaryEdgeOverTimeFlag && sequenceControlSetPtr->staticConfig.improveSharpness && pictureControlSetPtr->ParentPcsPtr->picNoiseClass < PIC_NOISE_CLASS_3_1) { EB_S16 cuDeltaQp = (EB_S16)(lcuPtr->qp - pictureControlSetPtr->ParentPcsPtr->averageQp); @@ -3526,7 +2965,6 @@ EB_EXTERN void EncodePass( encodeContextPtr = ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr; if (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag == EB_TRUE) { - // TMVP init tmvpMapWritePtr = &(contextPtr->referenceObjectWritePtr->tmvpMap[tbAddr]); tmvpMapIndex = 0; @@ -3535,9 +2973,7 @@ EB_EXTERN void EncodePass( if (is16bit) { reconBuffer = ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->referencePicture16bit; - } - - else { + } else { reconBuffer = ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->referencePicture; } } @@ -3569,16 +3005,14 @@ EB_EXTERN void EncodePass( // CU Loop cuItr = 0; while (cuItr < CU_MAX_COUNT) { - if (codedLeafArrayPtr[cuItr]->splitFlag == EB_FALSE){ - // PU Stack variables PredictionUnit_t *puPtr = (PredictionUnit_t *)EB_NULL; // done EbPictureBufferDesc_t *residualBuffer = contextPtr->residualBuffer; EbPictureBufferDesc_t *transformBuffer = contextPtr->transformBuffer; EB_S16 *transformInnerArrayPtr = contextPtr->transformInnerArrayPtr; const CodedUnitStats_t *cuStats = contextPtr->cuStats = GetCodedUnitStats(cuItr); - CodingUnit_t *cuPtr = contextPtr->cuPtr = lcuPtr->codedLeafArrayPtr[cuItr]; + CodingUnit_t *cuPtr = contextPtr->cuPtr = lcuPtr->codedLeafArrayPtr[cuItr]; _mm_prefetch((const char *)cuStats, _MM_HINT_T0); @@ -3599,8 +3033,9 @@ EB_EXTERN void EncodePass( lcuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) ? contextPtr->qpmQp : pictureControlSetPtr->pictureQp; cuPtr->orgDeltaQp = cuPtr->deltaQp; - if (!contextPtr->skipQpmFlag && (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && (contextPtr->cuStats->depth <= pictureControlSetPtr->difCuDeltaQpDepth)){ - + if (!contextPtr->skipQpmFlag && + (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && + (contextPtr->cuStats->depth <= pictureControlSetPtr->difCuDeltaQpDepth)) { EncQpmDeriveDeltaQPForEachLeafLcu( sequenceControlSetPtr, pictureControlSetPtr, @@ -3615,14 +3050,24 @@ EB_EXTERN void EncodePass( contextPtr); } - EB_U8 fastEl = (contextPtr->fastEl && contextPtr->cuStats->size > 8); - EB_U64 yCoeffBitsTemp = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yCoeffBits; - EB_S16 yDc = 0; - EB_U16 yCountNonZeroCoeffs = 0; - EB_U32 yBitsThsld = (contextPtr->cuStats->size > 32) ? contextPtr->yBitsThsld : (contextPtr->cuStats->size > 16) ? (contextPtr->yBitsThsld >> 1) : (contextPtr->yBitsThsld >> 2); + EB_U8 fastEl = (contextPtr->fastEl && contextPtr->cuStats->size > 8); + EB_U64 yCoeffBitsTemp = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yCoeffBits; + EB_S16 yDc = 0; + EB_U16 yCountNonZeroCoeffs = 0; + EB_U32 yBitsThsld = (contextPtr->cuStats->size > 32) ? contextPtr->yBitsThsld : (contextPtr->cuStats->size > 16) ? (contextPtr->yBitsThsld >> 1) : (contextPtr->yBitsThsld >> 2); - if (cuPtr->predictionModeFlag == INTRA_MODE && cuPtr->predictionUnitArray->intraLumaMode != EB_INTRA_MODE_4x4){ + EB_U8 qpScaled = CLIP3((EB_S8)MIN_QP_VALUE, (EB_S8)MAX_CHROMA_MAP_QP_VALUE, (EB_S8)(cuPtr->qp + pictureControlSetPtr->cbQpOffset + pictureControlSetPtr->sliceCbQpOffset)); + EB_U8 cbQp = 0; + + if (colorFormat == EB_YUV420) { + cbQp = MapChromaQp(qpScaled); + } else { + cbQp = MIN(qpScaled, 51); + } + + if (cuPtr->predictionModeFlag == INTRA_MODE && + cuPtr->predictionUnitArray->intraLumaMode != EB_INTRA_MODE_4x4) { contextPtr->totIntraCodedArea += cuStats->size*cuStats->size; if (pictureControlSetPtr->sliceType != EB_I_PICTURE){ contextPtr->intraCodedAreaLCU[tbAddr] += cuStats->size*cuStats->size; @@ -3646,24 +3091,62 @@ EB_EXTERN void EncodePass( // Transform Loop (not supported) { - // Generate Intra Reference Samples - - GenerateIntraReferenceSamplesFuncTable[is16bit]( - constrainedIntraFlag, - enableStrongIntraSmoothing, - contextPtr->cuOriginX, - contextPtr->cuOriginY, - cuStats->size, - cuStats->depth, - epModeTypeNeighborArray, - epLumaReconNeighborArray, - epCbReconNeighborArray, - epCrReconNeighborArray, - is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, - (contextPtr->cuOriginX == 0), - (contextPtr->cuOriginY == 0), - (contextPtr->cuOriginX + cuStats->size) == sequenceControlSetPtr->lumaWidth ? EB_TRUE : EB_FALSE); + if (colorFormat == EB_YUV420) { + GenerateIntraReferenceSamplesFuncTable[is16bit]( + constrainedIntraFlag, + enableStrongIntraSmoothing, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cuStats->size, + MAX_LCU_SIZE, + cuStats->depth, + epModeTypeNeighborArray, + epLumaReconNeighborArray, + epCbReconNeighborArray, + epCrReconNeighborArray, + is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, + colorFormat, + (contextPtr->cuOriginX == 0), + (contextPtr->cuOriginY == 0), + (contextPtr->cuOriginX + cuStats->size) == sequenceControlSetPtr->lumaWidth ? EB_TRUE : EB_FALSE); + } else if (colorFormat == EB_YUV422 || colorFormat == EB_YUV444) { + GenerateLumaIntraReferenceSamplesFuncTable[is16bit]( + constrainedIntraFlag, + enableStrongIntraSmoothing, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cuStats->size, + MAX_LCU_SIZE, + cuStats->depth, + epModeTypeNeighborArray, + epLumaReconNeighborArray, + epCbReconNeighborArray, + epCrReconNeighborArray, + is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, + (contextPtr->cuOriginX == 0), + (contextPtr->cuOriginY == 0), + (contextPtr->cuOriginX + cuStats->size) == sequenceControlSetPtr->lumaWidth ? EB_TRUE : EB_FALSE); + + GenerateChromaIntraReferenceSamplesFuncTable[is16bit]( + constrainedIntraFlag, + enableStrongIntraSmoothing, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cuStats->size, + MAX_LCU_SIZE, + cuStats->depth, + epModeTypeNeighborArray, + epLumaReconNeighborArray, + epCbReconNeighborArray, + epCrReconNeighborArray, + is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, + colorFormat, + EB_FALSE, + (contextPtr->cuOriginX == 0), + (contextPtr->cuOriginY == 0), + (contextPtr->cuOriginX + cuStats->size) == sequenceControlSetPtr->lumaWidth ? EB_TRUE : EB_FALSE); + } // Prediction EncodePassIntraPredictionFuncTable[is16bit]( @@ -3671,17 +3154,15 @@ EB_EXTERN void EncodePass( contextPtr->cuOriginX + reconBuffer->originX, contextPtr->cuOriginY + reconBuffer->originY, cuStats->size, + cuStats->size >> subWidthCMinus1, //chroma PU size, for 444 chroma PU size is the same as luma reconBuffer, - (EB_U32)puPtr->intraLumaMode); - - - + colorFormat, + EB_FALSE, + (EB_U32)puPtr->intraLumaMode, + EB_INTRA_CHROMA_DM, + PICTURE_BUFFER_DESC_FULL_MASK ); // Encode Transform Unit -INTRA- { - - EB_U8 qpScaled = CLIP3((EB_S8)MIN_QP_VALUE, (EB_S8)MAX_CHROMA_MAP_QP_VALUE, (EB_S8)(cuPtr->qp + pictureControlSetPtr->cbQpOffset + pictureControlSetPtr->sliceCbQpOffset)); - EB_U8 cbQp = MapChromaQp(qpScaled); - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? EB_FALSE : lcuPtr->pictureLeftEdgeFlag && ((contextPtr->cuOriginX & (63)) == 0) && (contextPtr->cuOriginY == lcuOriginY); @@ -3690,31 +3171,29 @@ EB_EXTERN void EncodePass( pictureControlSetPtr, contextPtr, tbAddr, - lcuStatPtr->stationaryEdgeOverTimeFlag, pictureControlSetPtr->temporalLayerIndex > 0 ? lcuStatPtr->pmStationaryEdgeOverTimeFlag : lcuStatPtr->stationaryEdgeOverTimeFlag); - // Set Fast El coef shaping method contextPtr->transCoeffShapeLuma = DEFAULT_SHAPE; - contextPtr->transCoeffShapeChroma = DEFAULT_SHAPE; + contextPtr->transCoeffShapeChroma = DEFAULT_SHAPE; if (fastEl && contextPtr->pmpMaskingLevelEncDec > MASK_THSHLD_1) { yDc = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yDc[0]; yCountNonZeroCoeffs = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yCountNonZeroCoeffs[0]; - if ((cuPtr->rootCbf == 0) || ((yCoeffBitsTemp <= yBitsThsld) && yDc < YDC_THSHLD_1 && yCountNonZeroCoeffs <= 1)) { + if ((cuPtr->rootCbf == 0) || + ((yCoeffBitsTemp <= yBitsThsld) && yDc < YDC_THSHLD_1 && yCountNonZeroCoeffs <= 1)) { // Skip pass for cuPtr->rootCbf == 0 caused some VQ issues in chroma, so DC path is used instead contextPtr->transCoeffShapeLuma = ONLY_DC_SHAPE; contextPtr->transCoeffShapeChroma = ONLY_DC_SHAPE; - } - else if ((yCoeffBitsTemp <= yBitsThsld * 4)) { + } else if ((yCoeffBitsTemp <= yBitsThsld * 4)) { contextPtr->transCoeffShapeLuma = N4_SHAPE; - if ((cuStats->size >> 1) > 8) + if ((cuStats->size >> 1) > 8) { contextPtr->transCoeffShapeChroma = N4_SHAPE; - else + } else { contextPtr->transCoeffShapeChroma = N2_SHAPE; - } - else if ((yCoeffBitsTemp <= yBitsThsld * 16)) { + } + } else if ((yCoeffBitsTemp <= yBitsThsld * 16)) { contextPtr->transCoeffShapeLuma = N2_SHAPE; contextPtr->transCoeffShapeChroma = N2_SHAPE; } @@ -3735,6 +3214,10 @@ EB_EXTERN void EncodePass( useDeltaQpSegments, (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, (EB_U32)puPtr->intraLumaMode, + PICTURE_BUFFER_DESC_FULL_MASK, + colorFormat, + EB_FALSE, + (contextPtr->cuStats->size == 64) ? 32 : contextPtr->cuStats->size, pictureControlSetPtr->cabacCost, cuPtr->deltaQp > 0 ? 0 : dZoffset); @@ -3742,21 +3225,15 @@ EB_EXTERN void EncodePass( contextPtr, contextPtr->cuOriginX, contextPtr->cuOriginY, + PICTURE_BUFFER_DESC_FULL_MASK, + colorFormat, + EB_FALSE, + (contextPtr->cuStats->size == 64) ? 32 : contextPtr->cuStats->size, reconBuffer, residualBuffer, transformInnerArrayPtr); - } - // Update the Intra-specific Neighbor Arrays - EncodePassUpdateIntraModeNeighborArrays( - epModeTypeNeighborArray, - epIntraLumaModeNeighborArray, - (EB_U8)cuPtr->predictionUnitArray->intraLumaMode, - contextPtr->cuOriginX, - contextPtr->cuOriginY, - cuStats->size); - // Update Recon Samples-INTRA- EncodePassUpdateReconSampleNeighborArrays( epLumaReconNeighborArray, @@ -3766,10 +3243,105 @@ EB_EXTERN void EncodePass( contextPtr->cuOriginX, contextPtr->cuOriginY, cuStats->size, + PICTURE_BUFFER_DESC_FULL_MASK, + colorFormat, is16bit); - } // Transform Loop + if (colorFormat == EB_YUV422) { + GenerateChromaIntraReferenceSamplesFuncTable[is16bit]( + constrainedIntraFlag, + enableStrongIntraSmoothing, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cuStats->size, + MAX_LCU_SIZE, + cuStats->depth, + epModeTypeNeighborArray, + epLumaReconNeighborArray, + epCbReconNeighborArray, + epCrReconNeighborArray, + is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, + colorFormat, + EB_TRUE, + (contextPtr->cuOriginX == 0), + EB_FALSE, + (contextPtr->cuOriginX + cuStats->size) == sequenceControlSetPtr->lumaWidth ? EB_TRUE : EB_FALSE); + + // Prediction + EncodePassIntraPredictionFuncTable[is16bit]( + is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, + contextPtr->cuOriginX + reconBuffer->originX, + contextPtr->cuOriginY + reconBuffer->originY, + cuStats->size, + cuStats->size>>1, + reconBuffer, + colorFormat, + EB_TRUE, + (EB_U32)puPtr->intraLumaMode, + EB_INTRA_CHROMA_DM, + PICTURE_BUFFER_DESC_CHROMA_MASK); + + //EncodeLoop + { + EncodeLoopFunctionTable[is16bit]( + contextPtr, + lcuPtr, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cbQp, + reconBuffer, + coeffBufferTB, + residualBuffer, + transformBuffer, + transformInnerArrayPtr, + countNonZeroCoeffs, + useDeltaQpSegments, + (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, + (EB_U32)puPtr->intraLumaMode, + PICTURE_BUFFER_DESC_CHROMA_MASK, + colorFormat, + EB_TRUE, + (contextPtr->cuStats->size == 64) ? 32 : contextPtr->cuStats->size, + pictureControlSetPtr->cabacCost, + cuPtr->deltaQp > 0 ? 0 : dZoffset); + + EncodeGenerateReconFunctionPtr[is16bit]( + contextPtr, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + PICTURE_BUFFER_DESC_CHROMA_MASK, + colorFormat, + EB_TRUE, + (contextPtr->cuStats->size == 64) ? 32 : contextPtr->cuStats->size, + reconBuffer, + residualBuffer, + transformInnerArrayPtr); + + // Update Recon Samples-INTRA- + EncodePassUpdateReconSampleNeighborArrays( + epLumaReconNeighborArray, + epCbReconNeighborArray, + epCrReconNeighborArray, + reconBuffer, + contextPtr->cuOriginX, + contextPtr->cuOriginY+(cuStats->size>>1), + cuStats->size, + PICTURE_BUFFER_DESC_CHROMA_MASK, + colorFormat, + is16bit); + } + } + + // Update the Intra-specific Neighbor Arrays + EncodePassUpdateIntraModeNeighborArrays( + epModeTypeNeighborArray, + epIntraLumaModeNeighborArray, + (EB_U8)cuPtr->predictionUnitArray->intraLumaMode, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cuStats->size); + // set up the bS based on PU boundary for DLF if (dlfEnableFlag){ // Update the cbf map for DLF @@ -3792,19 +3364,11 @@ EB_EXTERN void EncodePass( pictureControlSetPtr->verticalEdgeBSArray[tbAddr]); } - } // Partition Loop - - } - - else if (cuPtr->predictionModeFlag == INTRA_MODE) { //cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4 - - + } else if (cuPtr->predictionModeFlag == INTRA_MODE) { //************************* // INTRA 4x4 //************************* - - contextPtr->totIntraCodedArea += cuStats->size*cuStats->size; if (pictureControlSetPtr->sliceType != EB_I_PICTURE) { contextPtr->intraCodedAreaLCU[tbAddr] += cuStats->size*cuStats->size; @@ -3812,7 +3376,7 @@ EB_EXTERN void EncodePass( // Partition Loop EB_U8 partitionIndex; - + EB_U8 componentMask = PICTURE_BUFFER_DESC_LUMA_MASK; for (partitionIndex = 0; partitionIndex < 4; partitionIndex++) { // Partition Loop @@ -3821,11 +3385,17 @@ EB_EXTERN void EncodePass( EB_U16 partitionOriginX = contextPtr->cuOriginX + INTRA_4x4_OFFSET_X[partitionIndex]; EB_U16 partitionOriginY = contextPtr->cuOriginY + INTRA_4x4_OFFSET_Y[partitionIndex]; - EB_BOOL pictureLeftBoundary = (lcuPtr->pictureLeftEdgeFlag == EB_TRUE && ((partitionOriginX & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; - EB_BOOL pictureTopBoundary = (lcuPtr->pictureTopEdgeFlag == EB_TRUE && ((partitionOriginY & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; - EB_BOOL pictureRightBoundary = (lcuPtr->pictureRightEdgeFlag == EB_TRUE && (((partitionOriginX + MIN_PU_SIZE) & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; + EB_BOOL pictureLeftBoundary = + (lcuPtr->pictureLeftEdgeFlag == EB_TRUE && ((partitionOriginX & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; + EB_BOOL pictureTopBoundary = + (lcuPtr->pictureTopEdgeFlag == EB_TRUE && ((partitionOriginY & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; + EB_BOOL pictureRightBoundary = + (lcuPtr->pictureRightEdgeFlag == EB_TRUE && (((partitionOriginX + MIN_PU_SIZE) & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; EB_U8 intraLumaMode = lcuPtr->intra4x4Mode[((MD_SCAN_TO_RASTER_SCAN[cuItr] - 21) << 2) + partitionIndex]; + EB_U8 intraLumaModeForChroma = lcuPtr->intra4x4Mode[((MD_SCAN_TO_RASTER_SCAN[cuItr] - 21) << 2)]; + + //printf("Intra 4x4 block (%d, %d), luma mode is %d\n", partitionOriginX, partitionOriginY, intraLumaMode); // Set the PU Loop Variables puPtr = cuPtr->predictionUnitArray; @@ -3840,163 +3410,92 @@ EB_EXTERN void EncodePass( epModeTypeNeighborArray); // Generate Intra Reference Samples - if (partitionIndex) { - - if (is16bit){ - GenerateLumaIntraReference16bitSamplesEncodePass( - constrainedIntraFlag, - enableStrongIntraSmoothing, - partitionOriginX, - partitionOriginY, - MIN_PU_SIZE, - MAX_LCU_SIZE, - cuStats->depth, - epModeTypeNeighborArray, - epLumaReconNeighborArray, - epCbReconNeighborArray, - epCrReconNeighborArray, - contextPtr->intraRefPtr16, - pictureLeftBoundary, - pictureTopBoundary, - pictureRightBoundary); + GenerateLumaIntraReferenceSamplesFuncTable[is16bit]( + constrainedIntraFlag, + enableStrongIntraSmoothing, + partitionOriginX, + partitionOriginY, + MIN_PU_SIZE, + MAX_LCU_SIZE, + cuStats->depth, + epModeTypeNeighborArray, + epLumaReconNeighborArray, + epCbReconNeighborArray, + epCrReconNeighborArray, + is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, + pictureLeftBoundary, + pictureTopBoundary, + pictureRightBoundary); + + componentMask = PICTURE_BUFFER_DESC_LUMA_MASK; + if (partitionIndex == 0 || + (colorFormat == EB_YUV422 && partitionIndex == 2) || + (colorFormat == EB_YUV444)) { + // For the Intra4x4 case, the Chroma for the CU is coded as a single 4x4 block. + // This changes how the right picture boundary is interpreted for the Luma and Chroma blocks + // as there is not a one-to-one relationship between the luma/chroma blocks. This effects + // only the right picture edge check and not the left or top boundary checks as the block size + // has no influence on those checks. + if (colorFormat == EB_YUV444) { + pictureRightBoundary = (lcuPtr->pictureRightEdgeFlag == EB_TRUE && (((partitionOriginX + MIN_PU_SIZE) & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; + } else { + pictureRightBoundary = (lcuPtr->pictureRightEdgeFlag == EB_TRUE && ((((partitionOriginX / 2) + MIN_PU_SIZE) & ((MAX_LCU_SIZE / 2) - 1)) == 0)) ? EB_TRUE : EB_FALSE; } - else { - - GenerateLumaIntraReferenceSamplesEncodePass( + componentMask = PICTURE_BUFFER_DESC_FULL_MASK; + GenerateChromaIntraReferenceSamplesFuncTable[is16bit]( constrainedIntraFlag, enableStrongIntraSmoothing, - partitionOriginX, - partitionOriginY, - MIN_PU_SIZE, + (colorFormat == EB_YUV444) ? partitionOriginX : contextPtr->cuOriginX, + (colorFormat == EB_YUV444) ? partitionOriginY : contextPtr->cuOriginY, + (colorFormat == EB_YUV444) ? MIN_PU_SIZE: (MIN_PU_SIZE << 1), //Jing: really a mess here, clean up later MAX_LCU_SIZE, cuStats->depth, epModeTypeNeighborArray, epLumaReconNeighborArray, epCbReconNeighborArray, epCrReconNeighborArray, - contextPtr->intraRefPtr, - pictureLeftBoundary, - pictureTopBoundary, - pictureRightBoundary); - } - + is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, + colorFormat, + (colorFormat == EB_YUV444) ? EB_FALSE : (EB_BOOL)partitionIndex, + pictureLeftBoundary, + pictureTopBoundary, + pictureRightBoundary); } - else { - if (is16bit){ - GenerateLumaIntraReference16bitSamplesEncodePass( - constrainedIntraFlag, - enableStrongIntraSmoothing, - partitionOriginX, - partitionOriginY, - MIN_PU_SIZE, - MAX_LCU_SIZE, - cuStats->depth, - epModeTypeNeighborArray, - epLumaReconNeighborArray, - epCbReconNeighborArray, - epCrReconNeighborArray, - contextPtr->intraRefPtr16, - pictureLeftBoundary, - pictureTopBoundary, - pictureRightBoundary); - - // For the Intra4x4 case, the Chroma for the CU is coded as a single 4x4 block. - // This changes how the right picture boundary is interpreted for the Luma and Chroma blocks - // as there is not a one-to-one relationship between the luma/chroma blocks. This effects - // only the right picture edge check and not the left or top boundary checks as the block size - // has no influence on those checks. - pictureRightBoundary = (lcuPtr->pictureRightEdgeFlag == EB_TRUE && ((((partitionOriginX / 2) + MIN_PU_SIZE) & ((MAX_LCU_SIZE / 2) - 1)) == 0)) ? EB_TRUE : EB_FALSE; - GenerateChromaIntraReference16bitSamplesEncodePass( - constrainedIntraFlag, - enableStrongIntraSmoothing, - partitionOriginX, - partitionOriginY, - MIN_PU_SIZE << 1, - MAX_LCU_SIZE, - cuStats->depth, - epModeTypeNeighborArray, - epLumaReconNeighborArray, - epCbReconNeighborArray, - epCrReconNeighborArray, - contextPtr->intraRefPtr16, - pictureLeftBoundary, - pictureTopBoundary, - pictureRightBoundary); - } - else { - GenerateLumaIntraReferenceSamplesEncodePass( - constrainedIntraFlag, - enableStrongIntraSmoothing, - partitionOriginX, - partitionOriginY, + // Prediction + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { + EncodePassIntraPredictionFuncTable[is16bit]( + is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, + partitionOriginX + reconBuffer->originX, + partitionOriginY + reconBuffer->originY, MIN_PU_SIZE, - MAX_LCU_SIZE, - cuStats->depth, - epModeTypeNeighborArray, - epLumaReconNeighborArray, - epCbReconNeighborArray, - epCrReconNeighborArray, - contextPtr->intraRefPtr, - pictureLeftBoundary, - pictureTopBoundary, - pictureRightBoundary); - - // For the Intra4x4 case, the Chroma for the CU is coded as a single 4x4 block. - // This changes how the right picture boundary is interpreted for the Luma and Chroma blocks - // as there is not a one-to-one relationship between the luma/chroma blocks. This effects - // only the right picture edge check and not the left or top boundary checks as the block size - // has no influence on those checks. - pictureRightBoundary = (lcuPtr->pictureRightEdgeFlag == EB_TRUE && ((((partitionOriginX / 2) + MIN_PU_SIZE) & ((MAX_LCU_SIZE / 2) - 1)) == 0)) ? EB_TRUE : EB_FALSE; - GenerateChromaIntraReferenceSamplesEncodePass( - constrainedIntraFlag, - enableStrongIntraSmoothing, - partitionOriginX, - partitionOriginY, - MIN_PU_SIZE << 1, - MAX_LCU_SIZE, - cuStats->depth, - epModeTypeNeighborArray, - epLumaReconNeighborArray, - epCbReconNeighborArray, - epCrReconNeighborArray, - contextPtr->intraRefPtr, - pictureLeftBoundary, - pictureTopBoundary, - pictureRightBoundary); - } + MIN_PU_SIZE, + reconBuffer, + colorFormat, + EB_FALSE, //4x4, always 1st block + intraLumaMode, + EB_INTRA_CHROMA_DM, + PICTURE_BUFFER_DESC_LUMA_MASK); } - // Prediction - if (is16bit){ - EncodePassIntra4x4Prediction16bit( - contextPtr->intraRefPtr16, - partitionOriginX + reconBuffer->originX, - partitionOriginY + reconBuffer->originY, - MIN_PU_SIZE, - MIN_PU_SIZE, - reconBuffer, - intraLumaMode, - EB_INTRA_CHROMA_DM, - partitionIndex ? PICTURE_BUFFER_DESC_LUMA_MASK : PICTURE_BUFFER_DESC_FULL_MASK); - } - else{ - EncodePassIntra4x4Prediction( - contextPtr->intraRefPtr, - partitionOriginX + reconBuffer->originX, - partitionOriginY + reconBuffer->originY, - MIN_PU_SIZE, - MIN_PU_SIZE, - reconBuffer, - intraLumaMode, - EB_INTRA_CHROMA_DM, - partitionIndex ? PICTURE_BUFFER_DESC_LUMA_MASK : PICTURE_BUFFER_DESC_FULL_MASK); - } + if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { + // Jing: + // For 422 intra4x4, the mode for chroma is the mode of 1st luma 4x4 + EncodePassIntraPredictionFuncTable[is16bit]( + is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, + partitionOriginX + reconBuffer->originX, + partitionOriginY + reconBuffer->originY, + MIN_PU_SIZE, + MIN_PU_SIZE, + reconBuffer, + colorFormat, + EB_FALSE, //4x4, always 1st block + (colorFormat == EB_YUV444) ? intraLumaMode : intraLumaModeForChroma,//420/422 use 1st luma 4x4 mode + EB_INTRA_CHROMA_DM, + PICTURE_BUFFER_DESC_CHROMA_MASK); + } // Encode Transform Unit -INTRA- - EB_U8 qpScaled = CLIP3((EB_S8)MIN_QP_VALUE, (EB_S8)MAX_CHROMA_MAP_QP_VALUE, (EB_S8)(cuPtr->qp + pictureControlSetPtr->cbQpOffset + pictureControlSetPtr->sliceCbQpOffset)); - EB_U8 cbQp = MapChromaQp(qpScaled); - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? EB_FALSE : lcuPtr->pictureLeftEdgeFlag && ((contextPtr->cuOriginX & (63)) == 0) && (contextPtr->cuOriginY == lcuOriginY); @@ -4012,7 +3511,7 @@ EB_EXTERN void EncodePass( contextPtr->transCoeffShapeLuma = DEFAULT_SHAPE; contextPtr->transCoeffShapeChroma = DEFAULT_SHAPE; - EncodeLoopIntra4x4FunctionTable[is16bit]( + EncodeLoopFunctionTable[is16bit]( contextPtr, lcuPtr, partitionOriginX, @@ -4024,21 +3523,27 @@ EB_EXTERN void EncodePass( transformBuffer, transformInnerArrayPtr, countNonZeroCoeffs, - partitionIndex ? PICTURE_BUFFER_DESC_LUMA_MASK : PICTURE_BUFFER_DESC_FULL_MASK, useDeltaQpSegments, - (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, - intraLumaMode, - pictureControlSetPtr->cabacCost, + (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, + (EB_U32)puPtr->intraLumaMode, + componentMask, + colorFormat, + EB_FALSE, //always 1st chroma block for 4x4 + MIN_PU_SIZE, + pictureControlSetPtr->cabacCost, cuPtr->deltaQp > 0 ? 0 : dZoffset); - EncodeGenerateReconIntra4x4FunctionPtr[is16bit]( - contextPtr, - partitionOriginX, - partitionOriginY, - reconBuffer, - residualBuffer, - transformInnerArrayPtr, - partitionIndex ? PICTURE_BUFFER_DESC_LUMA_MASK : PICTURE_BUFFER_DESC_FULL_MASK); + EncodeGenerateReconFunctionPtr[is16bit]( + contextPtr, + partitionOriginX, + partitionOriginY, + componentMask, + colorFormat, + EB_FALSE, + MIN_PU_SIZE, + reconBuffer, + residualBuffer, + transformInnerArrayPtr); // Update the Intra-specific Neighbor Arrays EncodePassUpdateIntraModeNeighborArrays( @@ -4058,11 +3563,12 @@ EB_EXTERN void EncodePass( partitionOriginX, partitionOriginY, MIN_PU_SIZE, + componentMask, + colorFormat, is16bit); // set up the bS based on PU boundary for DLF if (dlfEnableFlag){ - // Update the cbf map for DLF startIndex = (partitionOriginY >> 2) * (sequenceControlSetPtr->lumaWidth >> 2) + (partitionOriginX >> 2); for (blk4x4IndexY = 0; blk4x4IndexY < (MIN_PU_SIZE >> 2); ++blk4x4IndexY){ @@ -4108,12 +3614,8 @@ EB_EXTERN void EncodePass( pictureControlSetPtr->verticalEdgeBSArray[tbAddr]); } - } // Partition Loop - } - - // Inter - else if (cuPtr->predictionModeFlag == INTER_MODE) { + } else if (cuPtr->predictionModeFlag == INTER_MODE) { EB_U16 tuOriginX; EB_U16 tuOriginY; EB_U8 tuSize = 0; @@ -4126,559 +3628,628 @@ EB_EXTERN void EncodePass( //******************************** EB_BOOL doMVpred = EB_TRUE; //if QPM and Segments are used, First Cu in LCU row should have at least one coeff. - EB_BOOL isFirstCUinRow = (useDeltaQp == 1) && + EB_BOOL isFirstCUinRow = (useDeltaQp == 1) && !singleSegment && (contextPtr->cuOriginX == 0 && contextPtr->cuOriginY == lcuOriginY) ? EB_TRUE : EB_FALSE; //Motion Compensation could be avoided in the case below EB_BOOL doMC = EB_TRUE; - // Perform Merge/Skip Decision if the mode coming from MD is merge. for the First CU in Row merge will remain as is. - - if (cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) - { - if (isFirstCUinRow == EB_FALSE) - { - if (lcuPtr->chromaEncodeMode == CHROMA_MODE_BEST) - { + // Perform Merge/Skip Decision if the mode coming from MD is merge. for the First CU in Row merge will remain as is. - EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; - const EB_U32 inputCbOriginIndex = ((contextPtr->cuOriginY >> 1) + (inputPicturePtr->originY >> 1)) * inputPicturePtr->strideCb + ((contextPtr->cuOriginX >> 1) + (inputPicturePtr->originX >> 1)); - const EB_U32 cuChromaOriginIndex = (((contextPtr->cuOriginY & 63) * 32) + (contextPtr->cuOriginX & 63)) >> 1; - - contextPtr->mdContext->cuOriginX = contextPtr->cuOriginX; - contextPtr->mdContext->cuOriginY = contextPtr->cuOriginY; - contextPtr->mdContext->puItr = 0; - contextPtr->mdContext->cuSize = contextPtr->cuStats->size; - contextPtr->mdContext->cuSizeLog2 = contextPtr->cuStats->sizeLog2; - contextPtr->mdContext->cuStats = contextPtr->cuStats; - - AddChromaEncDec( - pictureControlSetPtr, - lcuPtr, - cuPtr, - contextPtr->mdContext, - contextPtr, - inputPicturePtr, - inputCbOriginIndex, - cuChromaOriginIndex, - 0); - } + if (cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) { + if (isFirstCUinRow == EB_FALSE) { + if (lcuPtr->chromaEncodeMode == CHROMA_MODE_BEST) { + // Jing: using 420 for MD related stuff + //EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; + EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; + const EB_U32 inputCbOriginIndex = ((contextPtr->cuOriginY >> 1) + (inputPicturePtr->originY >> 1)) * inputPicturePtr->strideCb + ((contextPtr->cuOriginX >> 1) + (inputPicturePtr->originX >> 1)); + const EB_U32 cuChromaOriginIndex = (((contextPtr->cuOriginY & 63) * 32) + (contextPtr->cuOriginX & 63)) >> 1; + + contextPtr->mdContext->cuOriginX = contextPtr->cuOriginX; + contextPtr->mdContext->cuOriginY = contextPtr->cuOriginY; + contextPtr->mdContext->puItr = 0; + contextPtr->mdContext->cuSize = contextPtr->cuStats->size; + contextPtr->mdContext->cuSizeLog2 = contextPtr->cuStats->sizeLog2; + contextPtr->mdContext->cuStats = contextPtr->cuStats; + + AddChromaEncDec( + pictureControlSetPtr, + lcuPtr, + cuPtr, + contextPtr->mdContext, + contextPtr, + inputPicturePtr, + inputCbOriginIndex, + cuChromaOriginIndex, + 0); + } - if ( - pictureControlSetPtr->sliceType == EB_B_PICTURE && - pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag == EB_FALSE - ) - { - EbReferenceObject_t * refObjL0, *refObjL1; - EB_U16 cuVar = (pictureControlSetPtr->ParentPcsPtr->variance[lcuPtr->index][0]); - EB_U8 INTRA_AREA_TH[MAX_TEMPORAL_LAYERS] = { 40, 30, 30, 0, 0, 0 }; - refObjL0 = (EbReferenceObject_t*)pictureControlSetPtr->refPicPtrArray[REF_LIST_0]->objectPtr; - refObjL1 = (EbReferenceObject_t*)pictureControlSetPtr->refPicPtrArray[REF_LIST_1]->objectPtr; - - if (cuVar < 200 && (refObjL0->intraCodedArea > INTRA_AREA_TH[refObjL0->tmpLayerIdx] || - refObjL1->intraCodedArea > INTRA_AREA_TH[refObjL1->tmpLayerIdx] - ) - ) - mdcontextPtr->mdEpPipeLcu[cuPtr->leafIndex].skipCost += (mdcontextPtr->mdEpPipeLcu[cuPtr->leafIndex].skipCost * 70) / 100; - } - isCuSkip = mdcontextPtr->mdEpPipeLcu[cuPtr->leafIndex].skipCost <= mdcontextPtr->mdEpPipeLcu[cuPtr->leafIndex].mergeCost ? 1 : 0; - } - } + if (pictureControlSetPtr->sliceType == EB_B_PICTURE && + pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag == EB_FALSE) { + EbReferenceObject_t * refObjL0, *refObjL1; + EB_U16 cuVar = (pictureControlSetPtr->ParentPcsPtr->variance[lcuPtr->index][0]); + EB_U8 INTRA_AREA_TH[MAX_TEMPORAL_LAYERS] = { 40, 30, 30, 0, 0, 0 }; + refObjL0 = (EbReferenceObject_t*)pictureControlSetPtr->refPicPtrArray[REF_LIST_0]->objectPtr; + refObjL1 = (EbReferenceObject_t*)pictureControlSetPtr->refPicPtrArray[REF_LIST_1]->objectPtr; + + if (cuVar < 200 && (refObjL0->intraCodedArea > INTRA_AREA_TH[refObjL0->tmpLayerIdx] || + refObjL1->intraCodedArea > INTRA_AREA_TH[refObjL1->tmpLayerIdx])) { + mdcontextPtr->mdEpPipeLcu[cuPtr->leafIndex].skipCost += + (mdcontextPtr->mdEpPipeLcu[cuPtr->leafIndex].skipCost * 70) / 100; + } + } + isCuSkip = mdcontextPtr->mdEpPipeLcu[cuPtr->leafIndex].skipCost <= mdcontextPtr->mdEpPipeLcu[cuPtr->leafIndex].mergeCost ? 1 : 0; + } + } //MC could be avoided in some cases below - if (isFirstCUinRow == EB_FALSE){ - - if (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag == EB_FALSE && constrainedIntraFlag == EB_TRUE && - cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) - { - if (isCuSkip) - { + if (isFirstCUinRow == EB_FALSE) { + if (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag == EB_FALSE && + constrainedIntraFlag == EB_TRUE && + cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) { + if (isCuSkip) { //here merge is decided to be skip in nonRef frame. doMC = EB_FALSE; doMVpred = EB_FALSE; } - } - else if (contextPtr->mdContext->limitIntra && isIntraLCU == EB_FALSE) - { - if (isCuSkip) - { + } else if (contextPtr->mdContext->limitIntra && isIntraLCU == EB_FALSE) { + if (isCuSkip) { doMC = EB_FALSE; doMVpred = EB_FALSE; } } - - } - doMC = (EB_BOOL)(doRecon | doMC); - doMVpred = (EB_BOOL)(doRecon | doMVpred); + { + // 1st Partition Loop + puPtr = cuPtr->predictionUnitArray; + if (doMVpred) + EncodePassMvPrediction( //AMVP, not merge + sequenceControlSetPtr, + pictureControlSetPtr, + tbAddr, + contextPtr); - { - // 1st Partition Loop - - - puPtr = cuPtr->predictionUnitArray; - if (doMVpred) - EncodePassMvPrediction( - sequenceControlSetPtr, - pictureControlSetPtr, - tbAddr, - contextPtr); - - // Set MvUnit - contextPtr->mvUnit.predDirection = (EB_U8)puPtr->interPredDirectionIndex; - contextPtr->mvUnit.mv[REF_LIST_0].mvUnion = puPtr->mv[REF_LIST_0].mvUnion; - contextPtr->mvUnit.mv[REF_LIST_1].mvUnion = puPtr->mv[REF_LIST_1].mvUnion; - // Inter Prediction - if (is16bit){ - - if (doMC) - EncodePassInterPrediction16bit( - &contextPtr->mvUnit, - contextPtr->cuOriginX, - contextPtr->cuOriginY, - cuStats->size, - cuStats->size, - pictureControlSetPtr, - reconBuffer, - contextPtr->mcpContext); - } - else{ - - if (doMC) - EncodePassInterPrediction( - &contextPtr->mvUnit, - contextPtr->cuOriginX, - contextPtr->cuOriginY, - cuStats->size, - cuStats->size, - pictureControlSetPtr, - reconBuffer, - contextPtr->mcpContext); + // Set MvUnit + contextPtr->mvUnit.predDirection = (EB_U8)puPtr->interPredDirectionIndex; + contextPtr->mvUnit.mv[REF_LIST_0].mvUnion = puPtr->mv[REF_LIST_0].mvUnion; + contextPtr->mvUnit.mv[REF_LIST_1].mvUnion = puPtr->mv[REF_LIST_1].mvUnion; + // Inter Prediction + if (is16bit) { + if (doMC) + EncodePassInterPrediction16bit( + &contextPtr->mvUnit, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cuStats->size, + cuStats->size, + pictureControlSetPtr, + reconBuffer, + contextPtr->mcpContext); + } else{ + if (doMC) { + EncodePassInterPrediction( + &contextPtr->mvUnit, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cuStats->size, + cuStats->size, + pictureControlSetPtr, + reconBuffer, + contextPtr->mcpContext); + } + } } + contextPtr->tuItr = (cuStats->size < MAX_LCU_SIZE) ? 0 : 1; + + // Transform Loop + cuPtr->transformUnitArray[0].lumaCbf = EB_FALSE; + cuPtr->transformUnitArray[0].cbCbf = EB_FALSE; + cuPtr->transformUnitArray[0].crCbf = EB_FALSE; + cuPtr->transformUnitArray[0].cbCbf2 = EB_FALSE; + cuPtr->transformUnitArray[0].crCbf2 = EB_FALSE; + + // initialize TU Split + yFullDistortion[DIST_CALC_RESIDUAL] = 0; + yFullDistortion[DIST_CALC_PREDICTION] = 0; + + yCoeffBits = 0; + cbCoeffBits = 0; + crCoeffBits = 0; + + //printf("sizeof %i \n",sizeof(CodingUnit_t)); + EB_U32 totTu = (cuStats->size < MAX_LCU_SIZE) ? 1 : 4; + EB_U8 tuIt; + + EB_U32 componentMask = PICTURE_BUFFER_DESC_FULL_MASK; + EB_MODETYPE predictionModeFlag = (EB_MODETYPE)cuPtr->predictionModeFlag; + + if (cuPtr->predictionUnitArray[0].mergeFlag == EB_FALSE) { + for (tuIt = 0; tuIt < totTu; tuIt++) { + contextPtr->tuItr = (cuStats->size < MAX_LCU_SIZE) ? 0 : tuIt + 1; + if (cuStats->size < MAX_LCU_SIZE) { + tuOriginX = contextPtr->cuOriginX; + tuOriginY = contextPtr->cuOriginY; + tuSize = cuStats->size; + tuSizeChroma = (cuStats->size >> (colorFormat==EB_YUV444 ? 0 : 1)); + } else { + tuOriginX = contextPtr->cuOriginX + ((tuIt & 1) << 5); + tuOriginY = contextPtr->cuOriginY + ((tuIt > 1) << 5); + tuSize = 32; + tuSizeChroma = (colorFormat == EB_YUV444 ? 32: 16); + } - } - - contextPtr->tuItr = (cuStats->size < MAX_LCU_SIZE) ? 0 : 1; - - // Transform Loop - cuPtr->transformUnitArray[0].lumaCbf = EB_FALSE; - cuPtr->transformUnitArray[0].cbCbf = EB_FALSE; - cuPtr->transformUnitArray[0].crCbf = EB_FALSE; + //TU LOOP for MV mode + Luma CBF decision. + contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? + EB_FALSE : + (tuOriginX == 0) && (tuOriginY == lcuOriginY); - // initialize TU Split - yFullDistortion[DIST_CALC_RESIDUAL] = 0; - yFullDistortion[DIST_CALC_PREDICTION] = 0; + SetPmEncDecMode( + pictureControlSetPtr, + contextPtr, + tbAddr, + lcuStatPtr->stationaryEdgeOverTimeFlag, + pictureControlSetPtr->temporalLayerIndex > 0 ? lcuStatPtr->pmStationaryEdgeOverTimeFlag : lcuStatPtr->stationaryEdgeOverTimeFlag); - yCoeffBits = 0; - cbCoeffBits = 0; - crCoeffBits = 0; + // Set Fast El coef shaping method + contextPtr->transCoeffShapeLuma = DEFAULT_SHAPE; + contextPtr->transCoeffShapeChroma = DEFAULT_SHAPE; + + if (fastEl && isFirstCUinRow == EB_FALSE && contextPtr->pmpMaskingLevelEncDec > MASK_THSHLD_1) { + yDc = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yDc[tuIt]; + yCountNonZeroCoeffs = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yCountNonZeroCoeffs[tuIt]; + + if ((cuPtr->rootCbf == 0) || ((yCoeffBitsTemp <= yBitsThsld) && yDc < YDC_THSHLD_1 && yCountNonZeroCoeffs <= 1)) { + // Skip pass for cuPtr->rootCbf == 0 caused some VQ issues in chroma, so DC path is used instead + contextPtr->transCoeffShapeLuma = ONLY_DC_SHAPE; + contextPtr->transCoeffShapeChroma = ONLY_DC_SHAPE; + } else if ((yCoeffBitsTemp <= yBitsThsld * 4)) { + contextPtr->transCoeffShapeLuma = N4_SHAPE; + if ((cuStats->size >> 1) > 8) { + contextPtr->transCoeffShapeChroma = N4_SHAPE; + } else { + contextPtr->transCoeffShapeChroma = N2_SHAPE; + } + } else if ((yCoeffBitsTemp <= yBitsThsld * 16)) { + contextPtr->transCoeffShapeLuma = N2_SHAPE; + contextPtr->transCoeffShapeChroma = N2_SHAPE; + } + } - //SVT_LOG("sizeof %i \n",sizeof(CodingUnit_t)); - EB_U32 totTu = (cuStats->size < MAX_LCU_SIZE) ? 1 : 4; - EB_U8 tuIt; + EncodeLoopFunctionTable[is16bit]( + contextPtr, + lcuPtr, + tuOriginX, + tuOriginY, + cbQp, + reconBuffer, + coeffBufferTB, + residualBuffer, + transformBuffer, + transformInnerArrayPtr, + countNonZeroCoeffs, + useDeltaQpSegments, + (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, + 0, + PICTURE_BUFFER_DESC_FULL_MASK, + colorFormat, + EB_FALSE, + tuSize, + pictureControlSetPtr->cabacCost, + cuPtr->deltaQp > 0 ? 0 : dZoffset); - EB_U8 qpScaled = CLIP3((EB_S8)MIN_QP_VALUE, (EB_S8)MAX_CHROMA_MAP_QP_VALUE, (EB_S8)(cuPtr->qp + pictureControlSetPtr->cbQpOffset + pictureControlSetPtr->sliceCbQpOffset)); - EB_U8 cbQp = MapChromaQp(qpScaled); + //Jing: For 422, do for the 2nd chroma + if (colorFormat == EB_YUV422) { + EB_U32 tmpCountNonZeroCoeffs[3]; + EncodeLoopFunctionTable[is16bit]( + contextPtr, + lcuPtr, + tuOriginX, + tuOriginY, + cbQp, + reconBuffer, + coeffBufferTB, + residualBuffer, + transformBuffer, + transformInnerArrayPtr, + tmpCountNonZeroCoeffs, //Jing: beware of this + useDeltaQpSegments, + (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, + 0, + PICTURE_BUFFER_DESC_CHROMA_MASK, + colorFormat, + EB_TRUE, + tuSize, + pictureControlSetPtr->cabacCost, + cuPtr->deltaQp > 0 ? 0 : dZoffset); + // Jing, seems not useful here, never used ... + //countNonZeroCoeffs[1] += tmpCountNonZeroCoeffs[1]; + //countNonZeroCoeffs[2] += tmpCountNonZeroCoeffs[2]; + } - EB_U32 componentMask = PICTURE_BUFFER_DESC_FULL_MASK; - EB_MODETYPE predictionModeFlag = (EB_MODETYPE)cuPtr->predictionModeFlag; + // SKIP the CBF zero mode for DC path. There are problems with cost calculations + if (contextPtr->transCoeffShapeLuma != ONLY_DC_SHAPE && colorFormat == EB_YUV420) { + // Jing: A bit mess here, seems only luma is used, but chroma everywhere and wastes calculation power + // Will clean it later for 422, only enable it for 420 now + scratchLumaOffset = ((tuOriginY & (63)) * 64) + (tuOriginX & (63)); + + // Compute Tu distortion + PictureFullDistortionLuma( + transformBuffer, + scratchLumaOffset, + residualBuffer, + scratchLumaOffset, + contextPtr->transCoeffShapeLuma == ONLY_DC_SHAPE || cuPtr->transformUnitArray[contextPtr->tuItr].isOnlyDc[0] == EB_TRUE ? 1 : (tuSize >> contextPtr->transCoeffShapeLuma), + yTuFullDistortion, + countNonZeroCoeffs[0], + predictionModeFlag); + + + lumaShift = 2 * (7 - Log2f(tuSize)); + + // Note: for square Transform, the scale is 1/(2^(7-Log2(Transform size))) + // For NSQT the scale would be 1/ (2^(7-(Log2(first Transform size)+Log2(second Transform size))/2)) + // Add Log2 of Transform size in order to calculating it multiple time in this function + + yTuFullDistortion[DIST_CALC_RESIDUAL] = (yTuFullDistortion[DIST_CALC_RESIDUAL] + (EB_U64)(1 << (lumaShift - 1))) >> lumaShift; + yTuFullDistortion[DIST_CALC_PREDICTION] = (yTuFullDistortion[DIST_CALC_PREDICTION] + (EB_U64)(1 << (lumaShift - 1))) >> lumaShift; + + yTuCoeffBits = 0; + cbTuCoeffBits = 0; + crTuCoeffBits = 0; + + // Estimate Tu Coeff bits + TuEstimateCoeffBitsEncDec( + (tuOriginY & (63)) * MAX_LCU_SIZE + (tuOriginX & (63)), + ((tuOriginY & (63)) * MAX_LCU_SIZE_CHROMA + (tuOriginX & (63))) >> 1, //Jing: 444 is different + coeffEstEntropyCoderPtr, + coeffBufferTB, + countNonZeroCoeffs, + &yTuCoeffBits, + &cbTuCoeffBits, + &crTuCoeffBits, + contextPtr->transCoeffShapeLuma == ONLY_DC_SHAPE ? 1 : (tuSize >> contextPtr->transCoeffShapeLuma), + contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE ? 1 : (tuSizeChroma >> contextPtr->transCoeffShapeChroma), + predictionModeFlag, + cabacCost); + + // CBF Tu decision + EncodeTuCalcCost( + contextPtr, + countNonZeroCoeffs, + yTuFullDistortion, + &yTuCoeffBits, + componentMask); + + yCoeffBits += yTuCoeffBits; + cbCoeffBits += cbTuCoeffBits; + crCoeffBits += crTuCoeffBits; + + yFullDistortion[DIST_CALC_RESIDUAL] += yTuFullDistortion[DIST_CALC_RESIDUAL]; + yFullDistortion[DIST_CALC_PREDICTION] += yTuFullDistortion[DIST_CALC_PREDICTION]; + //------------------------------------------------- + } + } // Transform Loop + } - if (cuPtr->predictionUnitArray[0].mergeFlag == EB_FALSE){ - - for (tuIt = 0; tuIt < totTu; tuIt++) - { - contextPtr->tuItr = (cuStats->size < MAX_LCU_SIZE) ? 0 : tuIt + 1; + //Set Final CU data flags after skip/Merge decision. + if (isFirstCUinRow == EB_FALSE) { + if (cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) { + cuPtr->skipFlag = (isCuSkip) ? EB_TRUE : EB_FALSE; + cuPtr->predictionUnitArray[0].mergeFlag = (isCuSkip) ? EB_FALSE : EB_TRUE; + } + } - if (cuStats->size < MAX_LCU_SIZE) - { + // Initialize the Transform Loop + contextPtr->tuItr = (cuStats->size < MAX_LCU_SIZE) ? 0 : 1; + yCbf = 0; + cbCbf = 0; + crCbf = 0; + cbCbf2 = 0; + crCbf2 = 0; + + for (tuIt = 0; tuIt < totTu; tuIt++) { + contextPtr->tuItr = (cuStats->size < MAX_LCU_SIZE) ? 0 : tuIt + 1; + if (cuStats->size < MAX_LCU_SIZE) { tuOriginX = contextPtr->cuOriginX; tuOriginY = contextPtr->cuOriginY; tuSize = cuStats->size; - tuSizeChroma = (cuStats->size >> 1); - } - else - { + tuSizeChroma = (tuSize >> (colorFormat==EB_YUV444 ? 0 : 1)); + } else { tuOriginX = contextPtr->cuOriginX + ((tuIt & 1) << 5); tuOriginY = contextPtr->cuOriginY + ((tuIt > 1) << 5); tuSize = 32; - tuSizeChroma = 16; + tuSizeChroma = colorFormat==EB_YUV444 ? 32 : 16; } - //TU LOOP for MV mode + Luma CBF decision. - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? - EB_FALSE : - (tuOriginX == 0) && (tuOriginY == lcuOriginY); - - SetPmEncDecMode( - pictureControlSetPtr, - contextPtr, - tbAddr, - - lcuStatPtr->stationaryEdgeOverTimeFlag, - pictureControlSetPtr->temporalLayerIndex > 0 ? lcuStatPtr->pmStationaryEdgeOverTimeFlag : lcuStatPtr->stationaryEdgeOverTimeFlag); - - - // Set Fast El coef shaping method - contextPtr->transCoeffShapeLuma = DEFAULT_SHAPE; - contextPtr->transCoeffShapeChroma = DEFAULT_SHAPE; - if (fastEl && isFirstCUinRow == EB_FALSE && contextPtr->pmpMaskingLevelEncDec > MASK_THSHLD_1) { - yDc = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yDc[tuIt]; - yCountNonZeroCoeffs = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yCountNonZeroCoeffs[tuIt]; - + if (cuPtr->skipFlag == EB_TRUE){ + cuPtr->transformUnitArray[contextPtr->tuItr].lumaCbf = EB_FALSE; + cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf = EB_FALSE; + cuPtr->transformUnitArray[contextPtr->tuItr].crCbf = EB_FALSE; + cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf2 = EB_FALSE; + cuPtr->transformUnitArray[contextPtr->tuItr].crCbf2 = EB_FALSE; + } else if (cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) { + contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? + EB_FALSE : + (tuOriginX == 0) && (tuOriginY == lcuOriginY); + + SetPmEncDecMode( + pictureControlSetPtr, + contextPtr, + tbAddr, + lcuStatPtr->stationaryEdgeOverTimeFlag, + pictureControlSetPtr->temporalLayerIndex > 0 ? lcuStatPtr->pmStationaryEdgeOverTimeFlag : lcuStatPtr->stationaryEdgeOverTimeFlag); - if ((cuPtr->rootCbf == 0) || ((yCoeffBitsTemp <= yBitsThsld) && yDc < YDC_THSHLD_1 && yCountNonZeroCoeffs <= 1)) { - // Skip pass for cuPtr->rootCbf == 0 caused some VQ issues in chroma, so DC path is used instead - contextPtr->transCoeffShapeLuma = ONLY_DC_SHAPE; - contextPtr->transCoeffShapeChroma = ONLY_DC_SHAPE; - } - else if ((yCoeffBitsTemp <= yBitsThsld * 4)) { - contextPtr->transCoeffShapeLuma = N4_SHAPE; - if ((cuStats->size >> 1) > 8) - contextPtr->transCoeffShapeChroma = N4_SHAPE; - else + // Set Fast El coef shaping method + contextPtr->transCoeffShapeLuma = DEFAULT_SHAPE; + contextPtr->transCoeffShapeChroma = DEFAULT_SHAPE; + + if (fastEl && isFirstCUinRow == EB_FALSE && contextPtr->pmpMaskingLevelEncDec > MASK_THSHLD_1) { + yDc = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yDc[tuIt]; + yCountNonZeroCoeffs = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yCountNonZeroCoeffs[tuIt]; + + if ((cuPtr->rootCbf == 0) || ((yCoeffBitsTemp <= yBitsThsld) && yDc < YDC_THSHLD_1 && yCountNonZeroCoeffs <= 1)) { + // Skip pass for cuPtr->rootCbf == 0 caused some VQ issues in chroma, so DC path is used instead + contextPtr->transCoeffShapeLuma = ONLY_DC_SHAPE; + contextPtr->transCoeffShapeChroma = ONLY_DC_SHAPE; + } else if ((yCoeffBitsTemp <= yBitsThsld * 4)) { + contextPtr->transCoeffShapeLuma = N4_SHAPE; + if ((cuStats->size >> 1) > 8) { + contextPtr->transCoeffShapeChroma = N4_SHAPE; + } else { + contextPtr->transCoeffShapeChroma = N2_SHAPE; + } + } else if ((yCoeffBitsTemp <= yBitsThsld * 16)) { + contextPtr->transCoeffShapeLuma = N2_SHAPE; contextPtr->transCoeffShapeChroma = N2_SHAPE; + } } - else if ((yCoeffBitsTemp <= yBitsThsld * 16)) { - contextPtr->transCoeffShapeLuma = N2_SHAPE; - contextPtr->transCoeffShapeChroma = N2_SHAPE; - } - } - - EncodeLoopFunctionTable[is16bit]( - contextPtr, - lcuPtr, - tuOriginX, - tuOriginY, - cbQp, - reconBuffer, - coeffBufferTB, - residualBuffer, - transformBuffer, - transformInnerArrayPtr, - countNonZeroCoeffs, - useDeltaQpSegments, - (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, - 0, - pictureControlSetPtr->cabacCost, - cuPtr->deltaQp > 0 ? 0 : dZoffset); - - // SKIP the CBF zero mode for DC path. There are problems with cost calculations - if (contextPtr->transCoeffShapeLuma != ONLY_DC_SHAPE) { - - scratchLumaOffset = ((tuOriginY & (63)) * 64) + (tuOriginX & (63)); - - // Compute Tu distortion - PictureFullDistortionLuma( - transformBuffer, - scratchLumaOffset, - residualBuffer, - scratchLumaOffset, - contextPtr->transCoeffShapeLuma == ONLY_DC_SHAPE || cuPtr->transformUnitArray[contextPtr->tuItr].isOnlyDc[0] == EB_TRUE ? 1 : (tuSize >> contextPtr->transCoeffShapeLuma), - yTuFullDistortion, - countNonZeroCoeffs[0], - predictionModeFlag); - - - lumaShift = 2 * (7 - Log2f(tuSize)); - - // Note: for square Transform, the scale is 1/(2^(7-Log2(Transform size))) - // For NSQT the scale would be 1/ (2^(7-(Log2(first Transform size)+Log2(second Transform size))/2)) - // Add Log2 of Transform size in order to calculating it multiple time in this function - - yTuFullDistortion[DIST_CALC_RESIDUAL] = (yTuFullDistortion[DIST_CALC_RESIDUAL] + (EB_U64)(1 << (lumaShift - 1))) >> lumaShift; - yTuFullDistortion[DIST_CALC_PREDICTION] = (yTuFullDistortion[DIST_CALC_PREDICTION] + (EB_U64)(1 << (lumaShift - 1))) >> lumaShift; - - yTuCoeffBits = 0; - cbTuCoeffBits = 0; - crTuCoeffBits = 0; - - // Estimate Tu Coeff bits - TuEstimateCoeffBitsEncDec( - (tuOriginY & (63)) * MAX_LCU_SIZE + (tuOriginX & (63)), - ((tuOriginY & (63)) * MAX_LCU_SIZE_CHROMA + (tuOriginX & (63))) >> 1, - coeffEstEntropyCoderPtr, - coeffBufferTB, - countNonZeroCoeffs, - &yTuCoeffBits, - &cbTuCoeffBits, - &crTuCoeffBits, - contextPtr->transCoeffShapeLuma == ONLY_DC_SHAPE ? 1 : (tuSize >> contextPtr->transCoeffShapeLuma), - contextPtr->transCoeffShapeChroma == ONLY_DC_SHAPE ? 1 : (tuSizeChroma >> contextPtr->transCoeffShapeChroma), - predictionModeFlag, - cabacCost); - - // CBF Tu decision - EncodeTuCalcCost( - contextPtr, - countNonZeroCoeffs, - yTuFullDistortion, - &yTuCoeffBits, - componentMask); - - - yCoeffBits += yTuCoeffBits; - cbCoeffBits += cbTuCoeffBits; - crCoeffBits += crTuCoeffBits; - - yFullDistortion[DIST_CALC_RESIDUAL] += yTuFullDistortion[DIST_CALC_RESIDUAL]; - yFullDistortion[DIST_CALC_PREDICTION] += yTuFullDistortion[DIST_CALC_PREDICTION]; - - } - - - } // Transform Loop - - } - - //Set Final CU data flags after skip/Merge decision. - if (isFirstCUinRow == EB_FALSE) { - if (cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) { - - cuPtr->skipFlag = (isCuSkip) ? EB_TRUE : EB_FALSE; - cuPtr->predictionUnitArray[0].mergeFlag = (isCuSkip) ? EB_FALSE : EB_TRUE; + EncodeLoopFunctionTable[is16bit]( + contextPtr, + lcuPtr, + tuOriginX, + tuOriginY, + cbQp, + reconBuffer, + coeffBufferTB, + residualBuffer, + transformBuffer, + transformInnerArrayPtr, + countNonZeroCoeffs, + useDeltaQpSegments, + (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, + 0, + PICTURE_BUFFER_DESC_FULL_MASK, + colorFormat, + EB_FALSE, + tuSize, + pictureControlSetPtr->cabacCost, + cuPtr->deltaQp > 0 ? 0 : dZoffset); - } - } + if (colorFormat == EB_YUV422) { + EncodeLoopFunctionTable[is16bit]( + contextPtr, + lcuPtr, + tuOriginX, + tuOriginY, + cbQp, + reconBuffer, + coeffBufferTB, + residualBuffer, + transformBuffer, + transformInnerArrayPtr, + countNonZeroCoeffs, + useDeltaQpSegments, + (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, + 0, + PICTURE_BUFFER_DESC_CHROMA_MASK, + colorFormat, + EB_TRUE, + tuSize, + pictureControlSetPtr->cabacCost, + cuPtr->deltaQp > 0 ? 0 : dZoffset); + } + } - // Initialize the Transform Loop - contextPtr->tuItr = (cuStats->size < MAX_LCU_SIZE) ? 0 : 1; - yCbf = 0; - cbCbf = 0; - crCbf = 0; + cuPtr->rootCbf = cuPtr->rootCbf | + cuPtr->transformUnitArray[contextPtr->tuItr].lumaCbf | + cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf | + cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf2 | + cuPtr->transformUnitArray[contextPtr->tuItr].crCbf | + cuPtr->transformUnitArray[contextPtr->tuItr].crCbf2; - for (tuIt = 0; tuIt < totTu; tuIt++) - { - contextPtr->tuItr = (cuStats->size < MAX_LCU_SIZE) ? 0 : tuIt + 1; - if (cuStats->size < MAX_LCU_SIZE) - { - tuOriginX = contextPtr->cuOriginX; - tuOriginY = contextPtr->cuOriginY; - tuSize = cuStats->size; - tuSizeChroma = (tuSize >> 1); - } - else - { - tuOriginX = contextPtr->cuOriginX + ((tuIt & 1) << 5); - tuOriginY = contextPtr->cuOriginY + ((tuIt > 1) << 5); - tuSize = 32; - tuSizeChroma = 16; - } + if (cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf) { + cuPtr->transformUnitArray[0].cbCbf = EB_TRUE; + } - if (cuPtr->skipFlag == EB_TRUE){ - cuPtr->transformUnitArray[contextPtr->tuItr].lumaCbf = EB_FALSE; - cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf = EB_FALSE; - cuPtr->transformUnitArray[contextPtr->tuItr].crCbf = EB_FALSE; - } - else if ((&cuPtr->predictionUnitArray[0])->mergeFlag == EB_TRUE) { + if (cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf2) { + cuPtr->transformUnitArray[0].cbCbf2 = EB_TRUE; + } - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? - EB_FALSE : - (tuOriginX == 0) && (tuOriginY == lcuOriginY); - SetPmEncDecMode( - pictureControlSetPtr, - contextPtr, - tbAddr, - lcuStatPtr->stationaryEdgeOverTimeFlag, - pictureControlSetPtr->temporalLayerIndex > 0 ? lcuStatPtr->pmStationaryEdgeOverTimeFlag : lcuStatPtr->stationaryEdgeOverTimeFlag); + if (cuPtr->transformUnitArray[contextPtr->tuItr].crCbf) { + cuPtr->transformUnitArray[0].crCbf = EB_TRUE; + } + if (cuPtr->transformUnitArray[contextPtr->tuItr].crCbf2) { + cuPtr->transformUnitArray[0].crCbf2 = EB_TRUE; + } - // Set Fast El coef shaping method - contextPtr->transCoeffShapeLuma = DEFAULT_SHAPE; - contextPtr->transCoeffShapeChroma = DEFAULT_SHAPE; - - if (fastEl && isFirstCUinRow == EB_FALSE && contextPtr->pmpMaskingLevelEncDec > MASK_THSHLD_1) { - yDc = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yDc[tuIt]; - yCountNonZeroCoeffs = contextPtr->mdContext->mdEpPipeLcu[cuPtr->leafIndex].yCountNonZeroCoeffs[tuIt]; - - if ((cuPtr->rootCbf == 0) || ((yCoeffBitsTemp <= yBitsThsld) && yDc < YDC_THSHLD_1 && yCountNonZeroCoeffs <= 1)) { - // Skip pass for cuPtr->rootCbf == 0 caused some VQ issues in chroma, so DC path is used instead - contextPtr->transCoeffShapeLuma = ONLY_DC_SHAPE; - contextPtr->transCoeffShapeChroma = ONLY_DC_SHAPE; - } - else if ((yCoeffBitsTemp <= yBitsThsld * 4)) { - contextPtr->transCoeffShapeLuma = N4_SHAPE; - if ((cuStats->size >> 1) > 8) - contextPtr->transCoeffShapeChroma = N4_SHAPE; - else - contextPtr->transCoeffShapeChroma = N2_SHAPE; - } - else if ((yCoeffBitsTemp <= yBitsThsld * 16)) { - contextPtr->transCoeffShapeLuma = N2_SHAPE; - contextPtr->transCoeffShapeChroma = N2_SHAPE; + if (doRecon) { + EncodeGenerateReconFunctionPtr[is16bit]( + contextPtr, + tuOriginX, + tuOriginY, + PICTURE_BUFFER_DESC_FULL_MASK, + colorFormat, + EB_FALSE, + tuSize, + reconBuffer, + residualBuffer, + transformInnerArrayPtr); + if (colorFormat == EB_YUV422) { + EncodeGenerateReconFunctionPtr[is16bit]( + contextPtr, + tuOriginX, + tuOriginY, + PICTURE_BUFFER_DESC_CHROMA_MASK, + colorFormat, + EB_TRUE, + tuSize, + reconBuffer, + residualBuffer, + transformInnerArrayPtr); } } + yCbf |= cuPtr->transformUnitArray[contextPtr->tuItr].lumaCbf; + cbCbf |= cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf; + crCbf |= cuPtr->transformUnitArray[contextPtr->tuItr].crCbf; + cbCbf2 |= cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf2; + crCbf2 |= cuPtr->transformUnitArray[contextPtr->tuItr].crCbf2; - EncodeLoopFunctionTable[is16bit]( - contextPtr, - lcuPtr, - tuOriginX, - tuOriginY, - cbQp, - reconBuffer, - coeffBufferTB, - residualBuffer, - transformBuffer, - transformInnerArrayPtr, - countNonZeroCoeffs, - useDeltaQpSegments, - (CabacEncodeContext_t*)coeffEstEntropyCoderPtr->cabacEncodeContextPtr, - 0, - pictureControlSetPtr->cabacCost, - cuPtr->deltaQp > 0 ? 0 : dZoffset); - - } - cuPtr->rootCbf = cuPtr->rootCbf | - cuPtr->transformUnitArray[contextPtr->tuItr].lumaCbf | - cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf | - cuPtr->transformUnitArray[contextPtr->tuItr].crCbf; - - if (cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf){ - cuPtr->transformUnitArray[0].cbCbf = EB_TRUE; - } - - if (cuPtr->transformUnitArray[contextPtr->tuItr].crCbf){ - cuPtr->transformUnitArray[0].crCbf = EB_TRUE; - } + if (dlfEnableFlag) { - if (doRecon) - EncodeGenerateReconFunctionPtr[is16bit]( - contextPtr, - tuOriginX, - tuOriginY, - reconBuffer, - residualBuffer, - transformInnerArrayPtr); + EB_U32 lumaStride = (sequenceControlSetPtr->lumaWidth >> 2); + TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; + // Update the cbf map for DLF + startIndex = (tuOriginY >> 2) * lumaStride + (tuOriginX >> 2); + for (blk4x4IndexY = 0; blk4x4IndexY < (tuSize >> 2); ++blk4x4IndexY){ + EB_MEMSET(&pictureControlSetPtr->cbfMapArray[startIndex], (EB_U8)tuPtr->lumaCbf, (tuSize >> 2)); + startIndex += lumaStride; + } - yCbf |= cuPtr->transformUnitArray[contextPtr->tuItr].lumaCbf; - cbCbf |= cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf; - crCbf |= cuPtr->transformUnitArray[contextPtr->tuItr].crCbf; + if (cuStats->size == MAX_LCU_SIZE) + // Set the bS on TU boundary for DLF + SetBSArrayBasedOnTUBoundary( + tuOriginX, + tuOriginY, + tuSize, + tuSize, + cuStats, + (EB_PART_MODE)cuPtr->predictionModeFlag, + lcuOriginX, + lcuOriginY, + pictureControlSetPtr, + pictureControlSetPtr->horizontalEdgeBSArray[tbAddr], + pictureControlSetPtr->verticalEdgeBSArray[tbAddr]); + } + } // Transform Loop + // Calculate Root CBF + cuPtr->rootCbf = (yCbf | cbCbf | cbCbf2 | crCbf | crCbf2 ) ? EB_TRUE : EB_FALSE; - if (dlfEnableFlag){ + // Force Skip if MergeFlag == TRUE && RootCbf == 0 + if (cuPtr->skipFlag == EB_FALSE && + cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE && + cuPtr->rootCbf == EB_FALSE ) { + cuPtr->skipFlag = EB_TRUE; + } - EB_U32 lumaStride = (sequenceControlSetPtr->lumaWidth >> 2); - TransformUnit_t *tuPtr = &cuPtr->transformUnitArray[contextPtr->tuItr]; + { + // Set the PU Loop Variables + puPtr = cuPtr->predictionUnitArray; - // Update the cbf map for DLF - startIndex = (tuOriginY >> 2) * lumaStride + (tuOriginX >> 2); - for (blk4x4IndexY = 0; blk4x4IndexY < (tuSize >> 2); ++blk4x4IndexY){ - EB_MEMSET(&pictureControlSetPtr->cbfMapArray[startIndex], (EB_U8)tuPtr->lumaCbf, (tuSize >> 2)); - startIndex += lumaStride; + // Set MvUnit + contextPtr->mvUnit.predDirection = (EB_U8)puPtr->interPredDirectionIndex; + contextPtr->mvUnit.mv[REF_LIST_0].mvUnion = puPtr->mv[REF_LIST_0].mvUnion; + contextPtr->mvUnit.mv[REF_LIST_1].mvUnion = puPtr->mv[REF_LIST_1].mvUnion; + // set up the bS based on PU boundary for DLF + if (dlfEnableFlag /*&& cuStats->size < MAX_LCU_SIZE*/ ) { + SetBSArrayBasedOnPUBoundary( + epModeTypeNeighborArray, + epMvNeighborArray, + puPtr, + cuPtr, + cuStats, + lcuOriginX, + lcuOriginY, + pictureControlSetPtr, + pictureControlSetPtr->horizontalEdgeBSArray[tbAddr], + pictureControlSetPtr->verticalEdgeBSArray[tbAddr]); } - if (cuStats->size == MAX_LCU_SIZE) - // Set the bS on TU boundary for DLF - SetBSArrayBasedOnTUBoundary( - tuOriginX, - tuOriginY, - tuSize, - tuSize, - cuStats, - (EB_PART_MODE)cuPtr->predictionModeFlag, - lcuOriginX, - lcuOriginY, - pictureControlSetPtr, - pictureControlSetPtr->horizontalEdgeBSArray[tbAddr], - pictureControlSetPtr->verticalEdgeBSArray[tbAddr]); - } - } // Transform Loop - - // Calculate Root CBF - cuPtr->rootCbf = (yCbf | cbCbf | crCbf ) ? EB_TRUE : EB_FALSE; + // Update Neighbor Arrays (Mode Type, MVs, SKIP) + { + EB_U8 skipFlag = (EB_U8)cuPtr->skipFlag; + EncodePassUpdateInterModeNeighborArrays( + epModeTypeNeighborArray, + epMvNeighborArray, + epSkipFlagNeighborArray, + &contextPtr->mvUnit, + &skipFlag, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cuStats->size); - // Force Skip if MergeFlag == TRUE && RootCbf == 0 - if (cuPtr->skipFlag == EB_FALSE && - cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE && cuPtr->rootCbf == EB_FALSE ) - { - cuPtr->skipFlag = EB_TRUE; - } + } - { - // Set the PU Loop Variables - puPtr = cuPtr->predictionUnitArray; - - // Set MvUnit - contextPtr->mvUnit.predDirection = (EB_U8)puPtr->interPredDirectionIndex; - contextPtr->mvUnit.mv[REF_LIST_0].mvUnion = puPtr->mv[REF_LIST_0].mvUnion; - contextPtr->mvUnit.mv[REF_LIST_1].mvUnion = puPtr->mv[REF_LIST_1].mvUnion; - // set up the bS based on PU boundary for DLF - if (dlfEnableFlag /*&& cuStats->size < MAX_LCU_SIZE*/ ){ - SetBSArrayBasedOnPUBoundary( - epModeTypeNeighborArray, - epMvNeighborArray, - puPtr, - cuPtr, - cuStats, - lcuOriginX, - lcuOriginY, - pictureControlSetPtr, - pictureControlSetPtr->horizontalEdgeBSArray[tbAddr], - pictureControlSetPtr->verticalEdgeBSArray[tbAddr]); - } + } // 2nd Partition Loop - // Update Neighbor Arrays (Mode Type, MVs, SKIP) - { - EB_U8 skipFlag = (EB_U8)cuPtr->skipFlag; - EncodePassUpdateInterModeNeighborArrays( - epModeTypeNeighborArray, - epMvNeighborArray, - epSkipFlagNeighborArray, - &contextPtr->mvUnit, - &skipFlag, - contextPtr->cuOriginX, - contextPtr->cuOriginY, - cuStats->size); + // Update Recon Samples Neighbor Arrays -INTER- + if (doRecon) { + EncodePassUpdateReconSampleNeighborArrays( + epLumaReconNeighborArray, + epCbReconNeighborArray, + epCrReconNeighborArray, + reconBuffer, + contextPtr->cuOriginX, + contextPtr->cuOriginY, + cuStats->size, + PICTURE_BUFFER_DESC_FULL_MASK, + colorFormat, + is16bit); + if (colorFormat == EB_YUV422) { + //Here need to update the 2nd chroma for neighbour + EncodePassUpdateReconSampleNeighborArrays( + epLumaReconNeighborArray, + epCbReconNeighborArray, + epCrReconNeighborArray, + reconBuffer, + contextPtr->cuOriginX, + contextPtr->cuOriginY+(cuStats->size>>1), + cuStats->size, + PICTURE_BUFFER_DESC_CHROMA_MASK, + colorFormat, + is16bit); + } } - - } // 2nd Partition Loop - - - // Update Recon Samples Neighbor Arrays -INTER- - - if (doRecon) - EncodePassUpdateReconSampleNeighborArrays( - epLumaReconNeighborArray, - epCbReconNeighborArray, - epCrReconNeighborArray, - reconBuffer, - contextPtr->cuOriginX, - contextPtr->cuOriginY, - cuStats->size, - is16bit); - } - else { + } else { CHECK_REPORT_ERROR_NC( - encodeContextPtr->appCallbackPtr, - EB_ENC_CL_ERROR2); + encodeContextPtr->appCallbackPtr, + EB_ENC_CL_ERROR2); } - if (dlfEnableFlag) - { + if (dlfEnableFlag) { // Assign the LCU-level QP if (cuPtr->predictionModeFlag == INTRA_MODE && puPtr->intraLumaMode == EB_INTRA_MODE_4x4) { - availableCoeff = ( - contextPtr->cuPtr->transformUnitArray[1].lumaCbf || - contextPtr->cuPtr->transformUnitArray[2].lumaCbf || - contextPtr->cuPtr->transformUnitArray[3].lumaCbf || - contextPtr->cuPtr->transformUnitArray[4].lumaCbf || + availableCoeff = ( + contextPtr->cuPtr->transformUnitArray[1].lumaCbf || + contextPtr->cuPtr->transformUnitArray[2].lumaCbf || + contextPtr->cuPtr->transformUnitArray[3].lumaCbf || + contextPtr->cuPtr->transformUnitArray[4].lumaCbf || contextPtr->cuPtr->transformUnitArray[1].crCbf || - contextPtr->cuPtr->transformUnitArray[1].cbCbf) ? EB_TRUE : EB_FALSE; - + contextPtr->cuPtr->transformUnitArray[1].cbCbf || + contextPtr->cuPtr->transformUnitArray[2].crCbf || + contextPtr->cuPtr->transformUnitArray[2].cbCbf || + contextPtr->cuPtr->transformUnitArray[3].crCbf || + contextPtr->cuPtr->transformUnitArray[3].cbCbf || + contextPtr->cuPtr->transformUnitArray[4].crCbf || // 422 case will use 3rd 4x4 for the 2nd chroma + contextPtr->cuPtr->transformUnitArray[4].cbCbf) ? EB_TRUE : EB_FALSE; } else { availableCoeff = (cuPtr->predictionModeFlag == INTER_MODE) ? (EB_BOOL)cuPtr->rootCbf : (cuPtr->transformUnitArray[cuStats->size == MAX_LCU_SIZE ? 1 : 0].lumaCbf || cuPtr->transformUnitArray[cuStats->size == MAX_LCU_SIZE ? 1 : 0].crCbf || - cuPtr->transformUnitArray[cuStats->size == MAX_LCU_SIZE ? 1 : 0].cbCbf) ? EB_TRUE : EB_FALSE; + cuPtr->transformUnitArray[cuStats->size == MAX_LCU_SIZE ? 1 : 0].crCbf2 || + cuPtr->transformUnitArray[cuStats->size == MAX_LCU_SIZE ? 1 : 0].cbCbf || + cuPtr->transformUnitArray[cuStats->size == MAX_LCU_SIZE ? 1 : 0].cbCbf2) ? EB_TRUE : EB_FALSE; } @@ -4704,7 +4275,6 @@ EB_EXTERN void EncodePass( } { - // Update Neighbor Arrays (Leaf Depth) EncodePassUpdateLeafDepthNeighborArrays( epLeafDepthNeighborArray, @@ -4810,7 +4380,6 @@ EB_EXTERN void EncodePass( } } - cuItr += DepthOffset[cuStats->depth]; } else{ diff --git a/Source/Lib/Codec/EbCodingUnit.c b/Source/Lib/Codec/EbCodingUnit.c index 5a875cb67..751a41ac1 100644 --- a/Source/Lib/Codec/EbCodingUnit.c +++ b/Source/Lib/Codec/EbCodingUnit.c @@ -8,6 +8,7 @@ #include "EbCodingUnit.h" #include "EbUtility.h" #include "EbTransformUnit.h" +#include "EbPictureControlSet.h" /* Tasks & Questions @@ -79,6 +80,7 @@ EB_ERRORTYPE LargestCodingUnitCtor( coeffInitData.maxWidth = lcuSize; coeffInitData.maxHeight = lcuSize; coeffInitData.bitDepth = EB_16BIT; + coeffInitData.colorFormat = largestCodingUnitPtr->pictureControlSetPtr->colorFormat; coeffInitData.leftPadding = 0; coeffInitData.rightPadding = 0; coeffInitData.topPadding = 0; diff --git a/Source/Lib/Codec/EbDeblockingFilter.c b/Source/Lib/Codec/EbDeblockingFilter.c index 0293e5c5d..5dea30156 100644 --- a/Source/Lib/Codec/EbDeblockingFilter.c +++ b/Source/Lib/Codec/EbDeblockingFilter.c @@ -17,9 +17,13 @@ #include "EbErrorCodes.h" #include "EbErrorHandling.h" - +#if 0 #define convertToChromaQp(iQpY) ( ((iQpY) < 0) ? (iQpY) : (((iQpY) > 57) ? ((iQpY)-6) : (EB_S32)(MapChromaQp((EB_U32)iQpY))) ) - +#else +#define convertToChromaQp(iQpY, is422or444) \ + (!(is422or444) ? ( ((iQpY) < 0) ? (iQpY) : (((iQpY) > 57) ? ((iQpY)-6) : (EB_S32)(MapChromaQp((EB_U32)iQpY))) ) \ + : ( ((iQpY) < 0) ? (iQpY) : (((iQpY) > 51) ? 51 : iQpY)) ) +#endif @@ -1435,12 +1439,19 @@ static void chroma8x8blkDLFCore( EB_U8 crTc; //EB_BOOL chromaPCMFlagArray[2]; EB_S32 CUqpIndex; + EB_COLOR_FORMAT colorFormat = reconPic->colorFormat; + const EB_S32 subWidthC = colorFormat==EB_YUV444?1:2; + const EB_S32 subHeightC = colorFormat==EB_YUV420?2:1; + const EB_S32 subWidthCMinus1 = colorFormat==EB_YUV444?0:1; + const EB_S32 subHeightCMinus1 = colorFormat==EB_YUV420?1:0; //vertical edge A filtering if (bSEdgeAArray[0] > 1) { // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y - 4, reconPictureControlSet->qpArrayStride); @@ -1451,20 +1462,22 @@ static void chroma8x8blkDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 1, centerSamplePos_y - 4, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + (centerSamplePos_y - 4) * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + (centerSamplePos_y - 4) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + (centerSamplePos_y - 4) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + (centerSamplePos_y - 4) * reconChromaPicStride + centerSamplePos_x; Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -1479,6 +1492,8 @@ static void chroma8x8blkDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y - 2, reconPictureControlSet->qpArrayStride); @@ -1489,20 +1504,22 @@ static void chroma8x8blkDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 1, centerSamplePos_y - 2, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + (centerSamplePos_y - 2) * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + (centerSamplePos_y - 2) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + (centerSamplePos_y - 2) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + (centerSamplePos_y - 2) * reconChromaPicStride + centerSamplePos_x; Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -1520,6 +1537,8 @@ static void chroma8x8blkDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -1530,6 +1549,8 @@ static void chroma8x8blkDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 1, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -1537,14 +1558,14 @@ static void chroma8x8blkDLFCore( //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -1560,6 +1581,8 @@ static void chroma8x8blkDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y + 2, reconPictureControlSet->qpArrayStride); @@ -1571,6 +1594,8 @@ static void chroma8x8blkDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 1, centerSamplePos_y + 2, reconPictureControlSet->qpArrayStride); @@ -1578,13 +1603,13 @@ static void chroma8x8blkDLFCore( neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + (centerSamplePos_y + 2) * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + (centerSamplePos_y + 2) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + (centerSamplePos_y + 2) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + (centerSamplePos_y + 2) * reconChromaPicStride + centerSamplePos_x; Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -1600,6 +1625,8 @@ static void chroma8x8blkDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 4, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -1610,20 +1637,22 @@ static void chroma8x8blkDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 4, centerSamplePos_y - 1, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 4); - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 4); + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 4); + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 4); Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -1639,6 +1668,8 @@ static void chroma8x8blkDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 2, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -1649,20 +1680,22 @@ static void chroma8x8blkDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 2, centerSamplePos_y - 1, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 2); - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 2); + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 2); + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 2); Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -1679,6 +1712,8 @@ static void chroma8x8blkDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -1688,6 +1723,8 @@ static void chroma8x8blkDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y - 1, reconPictureControlSet->qpArrayStride); @@ -1695,14 +1732,14 @@ static void chroma8x8blkDLFCore( neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -1718,6 +1755,8 @@ static void chroma8x8blkDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x + 2, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -1728,20 +1767,22 @@ static void chroma8x8blkDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x + 2, centerSamplePos_y - 1, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x + 2); - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x + 2); + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x + 2); + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x + 2); Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -1776,12 +1817,19 @@ static void chroma8x8blkDLFCore16bit( EB_U8 crTc; //EB_BOOL chromaPCMFlagArray[2]; EB_S32 CUqpIndex; + EB_COLOR_FORMAT colorFormat = reconPic->colorFormat; + const EB_S32 subWidthC = colorFormat==EB_YUV444?1:2; + const EB_S32 subHeightC = colorFormat==EB_YUV420?2:1; + const EB_S32 subWidthCMinus1 = colorFormat==EB_YUV444?0:1; + const EB_S32 subHeightCMinus1 = colorFormat==EB_YUV420?1:0; //vertical edge A filtering if (bSEdgeAArray[0] > 1) { // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y - 4, reconPictureControlSet->qpArrayStride); @@ -1792,14 +1840,16 @@ static void chroma8x8blkDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 1, centerSamplePos_y - 4, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; @@ -1809,8 +1859,8 @@ static void chroma8x8blkDLFCore16bit( cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + (centerSamplePos_y - 4) * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + (centerSamplePos_y - 4) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + (centerSamplePos_y - 4) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + (centerSamplePos_y - 4) * reconChromaPicStride + centerSamplePos_x; chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -1825,6 +1875,8 @@ static void chroma8x8blkDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y - 2, reconPictureControlSet->qpArrayStride); @@ -1835,14 +1887,16 @@ static void chroma8x8blkDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 1, centerSamplePos_y - 2, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; @@ -1852,8 +1906,8 @@ static void chroma8x8blkDLFCore16bit( cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + (centerSamplePos_y - 2) * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + (centerSamplePos_y - 2) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + (centerSamplePos_y - 2) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + (centerSamplePos_y - 2) * reconChromaPicStride + centerSamplePos_x; chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -1870,6 +1924,8 @@ static void chroma8x8blkDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -1880,6 +1936,8 @@ static void chroma8x8blkDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 1, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -1887,8 +1945,8 @@ static void chroma8x8blkDLFCore16bit( //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; @@ -1898,8 +1956,8 @@ static void chroma8x8blkDLFCore16bit( cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -1915,6 +1973,8 @@ static void chroma8x8blkDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y + 2, reconPictureControlSet->qpArrayStride); @@ -1925,14 +1985,16 @@ static void chroma8x8blkDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 1, centerSamplePos_y + 2, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; @@ -1940,8 +2002,8 @@ static void chroma8x8blkDLFCore16bit( //CHKN cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + (centerSamplePos_y + 2) * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + (centerSamplePos_y + 2) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + (centerSamplePos_y + 2) * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + (centerSamplePos_y + 2) * reconChromaPicStride + centerSamplePos_x; chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -1959,6 +2021,8 @@ static void chroma8x8blkDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 4, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -1969,14 +2033,16 @@ static void chroma8x8blkDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 4, centerSamplePos_y - 1, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; @@ -1986,9 +2052,8 @@ static void chroma8x8blkDLFCore16bit( cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 4); - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 4); - + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 4); + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 4); chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -2004,6 +2069,8 @@ static void chroma8x8blkDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 2, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -2014,14 +2081,16 @@ static void chroma8x8blkDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x - 2, centerSamplePos_y - 1, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; @@ -2030,8 +2099,8 @@ static void chroma8x8blkDLFCore16bit( //CHKN cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 2); - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 2); + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 2); + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x - 2); chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -2049,6 +2118,8 @@ static void chroma8x8blkDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -2058,6 +2129,8 @@ static void chroma8x8blkDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x, centerSamplePos_y - 1, reconPictureControlSet->qpArrayStride); @@ -2065,8 +2138,8 @@ static void chroma8x8blkDLFCore16bit( neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; @@ -2075,8 +2148,8 @@ static void chroma8x8blkDLFCore16bit( //CHKN cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + centerSamplePos_x; chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -2093,6 +2166,8 @@ static void chroma8x8blkDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x + 2, centerSamplePos_y, reconPictureControlSet->qpArrayStride); @@ -2103,14 +2178,16 @@ static void chroma8x8blkDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, centerSamplePos_x + 2, centerSamplePos_y - 1, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; @@ -2119,8 +2196,8 @@ static void chroma8x8blkDLFCore16bit( //CHKN cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x + 2); - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x + 2); + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x + 2); + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + centerSamplePos_y * reconChromaPicStride + (centerSamplePos_x + 2); chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -2171,12 +2248,19 @@ EB_ERRORTYPE LCUInternalAreaDLFCore( EB_U32 blk2x2Addr; EB_U32 twoSampleEdgeStartSamplePos_x; EB_U32 twoSampleEdgeStartSamplePos_y; - EB_U32 chromaLcuPos_x = lcuPos_x >> 1; - EB_U32 chromaLcuPos_y = lcuPos_y >> 1; - EB_U32 numVerticalChromaSampleEdges = (lcuWidth >> 4) - ((lcuWidth & 15) == 0); - EB_U32 numHorizontalChromaSampleEdges = (lcuHeight >> 4) - ((lcuHeight & 15) == 0); - EB_U32 num2SampleEdgesPerVerticalChromaSampleEdge = (lcuHeight >> 2) - 2 - (((lcuHeight & 15) == 0) << 1); - EB_U32 num2SampleEdgesPerHorizontalChromaSampleEdge = (lcuWidth >> 2) - 2 - (((lcuWidth & 15) == 0) << 1); + EB_COLOR_FORMAT colorFormat = reconpicture->colorFormat; + const EB_S32 subWidthC = colorFormat==EB_YUV444?1:2; + const EB_S32 subHeightC = colorFormat==EB_YUV420?2:1; + const EB_U32 subWidthShfitMinus1 = colorFormat==EB_YUV444?1:0; + const EB_U32 subHeightShfitMinus1 = colorFormat==EB_YUV420?0:1; + const EB_S32 subWidthCMinus1 = colorFormat==EB_YUV444?0:1; + const EB_S32 subHeightCMinus1 = colorFormat==EB_YUV420?1:0; + EB_U32 chromaLcuPos_x = lcuPos_x >> (colorFormat==EB_YUV444?0:1); + EB_U32 chromaLcuPos_y = lcuPos_y >> (colorFormat==EB_YUV420?1:0); + EB_U32 numVerticalChromaSampleEdges = (lcuWidth >> (colorFormat==EB_YUV444?3:4)) - (colorFormat==EB_YUV444?1:((lcuWidth & 15) == 0)); + EB_U32 numHorizontalChromaSampleEdges = (lcuHeight >> (colorFormat==EB_YUV420?4:3)) - (colorFormat==EB_YUV420?((lcuHeight & 15) == 0):1); + EB_U32 num2SampleEdgesPerVerticalChromaSampleEdge = (lcuHeight >> (colorFormat==EB_YUV420?2:1)) - 2 - (((lcuHeight & (colorFormat==EB_YUV420?15:7)) == 0) << 1); + EB_U32 num2SampleEdgesPerHorizontalChromaSampleEdge = (lcuWidth >> (colorFormat==EB_YUV444?1:2)) - 2 - (((lcuWidth & (colorFormat==EB_YUV444?7:15)) == 0) << 1); EB_U8 curCuQp; EB_BYTE edgeStartFilteredSamplePtr; EB_BYTE edgeStartSampleCb; @@ -2311,8 +2395,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore( twoSampleEdgeStartSamplePos_x = horizontalIdx << 3; // LCU-wise position twoSampleEdgeStartSamplePos_y = (verticalIdx << 1) + 2; // LCU-wise position blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - twoSampleEdgeStartSamplePos_x, - twoSampleEdgeStartSamplePos_y, + twoSampleEdgeStartSamplePos_x >> subWidthShfitMinus1, + twoSampleEdgeStartSamplePos_y >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = verticalEdgeBSArray[BLK4X4_ADDR_TO_VERTICAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -2320,6 +2404,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, twoSampleEdgeStartSamplePos_x + chromaLcuPos_x, twoSampleEdgeStartSamplePos_y + chromaLcuPos_y, reconPictureControlSet->qpArrayStride); @@ -2330,19 +2416,21 @@ EB_ERRORTYPE LCUInternalAreaDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, twoSampleEdgeStartSamplePos_x + chromaLcuPos_x - 1, twoSampleEdgeStartSamplePos_y + chromaLcuPos_y, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, ((EB_S32)cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, ((EB_S32)crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconpicture->bufferCb + (reconpicture->originX >> 1) + (reconpicture->originY >> 1) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); - edgeStartSampleCr = reconpicture->bufferCr + (reconpicture->originX >> 1) + (reconpicture->originY >> 1) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); + edgeStartSampleCb = reconpicture->bufferCb + (reconpicture->originX >> subWidthCMinus1) + (reconpicture->originY >> subHeightCMinus1) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); + edgeStartSampleCr = reconpicture->bufferCr + (reconpicture->originX >> subWidthCMinus1) + (reconpicture->originY >> subHeightCMinus1) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -2362,8 +2450,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore( twoSampleEdgeStartSamplePos_x = (horizontalIdx << 1) + 2; // LCU-wise position twoSampleEdgeStartSamplePos_y = verticalIdx << 3; // LCU-wise position blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - twoSampleEdgeStartSamplePos_x, - twoSampleEdgeStartSamplePos_y, + twoSampleEdgeStartSamplePos_x >> subWidthShfitMinus1, + twoSampleEdgeStartSamplePos_y >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = horizontalEdgeBSArray[BLK4X4_ADDR_TO_HORIZONTAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -2371,6 +2459,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, twoSampleEdgeStartSamplePos_x + chromaLcuPos_x, twoSampleEdgeStartSamplePos_y + chromaLcuPos_y, reconPictureControlSet->qpArrayStride); @@ -2382,19 +2472,21 @@ EB_ERRORTYPE LCUInternalAreaDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, twoSampleEdgeStartSamplePos_x + chromaLcuPos_x, twoSampleEdgeStartSamplePos_y + chromaLcuPos_y - 1, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; - edgeStartSampleCb = reconpicture->bufferCb + (reconpicture->originX >> 1) + (reconpicture->originY >> 1) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); - edgeStartSampleCr = reconpicture->bufferCr + (reconpicture->originX >> 1) + (reconpicture->originY >> 1) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); + edgeStartSampleCb = reconpicture->bufferCb + (reconpicture->originX >> subWidthCMinus1) + (reconpicture->originY >> subHeightCMinus1) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); + edgeStartSampleCr = reconpicture->bufferCr + (reconpicture->originX >> subWidthCMinus1) + (reconpicture->originY >> subHeightCMinus1) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -2448,12 +2540,24 @@ EB_ERRORTYPE LCUInternalAreaDLFCore16bit( EB_U32 blk2x2Addr; EB_U32 twoSampleEdgeStartSamplePos_x; EB_U32 twoSampleEdgeStartSamplePos_y; - EB_U32 chromaLcuPos_x = lcuPos_x >> 1; - EB_U32 chromaLcuPos_y = lcuPos_y >> 1; - EB_U32 numVerticalChromaSampleEdges = (lcuWidth >> 4) - ((lcuWidth & 15) == 0); - EB_U32 numHorizontalChromaSampleEdges = (lcuHeight >> 4) - ((lcuHeight & 15) == 0); - EB_U32 num2SampleEdgesPerVerticalChromaSampleEdge = (lcuHeight >> 2) - 2 - (((lcuHeight & 15) == 0) << 1); - EB_U32 num2SampleEdgesPerHorizontalChromaSampleEdge = (lcuWidth >> 2) - 2 - (((lcuWidth & 15) == 0) << 1); + EB_COLOR_FORMAT colorFormat = reconpicture->colorFormat; + const EB_S32 subWidthC = (colorFormat == EB_YUV444) ? 1 : 2; + const EB_S32 subHeightC = (colorFormat == EB_YUV420) ? 2 : 1; + const EB_U32 subWidthShfitMinus1 = (colorFormat == EB_YUV444) ? 1 : 0; + const EB_U32 subHeightShfitMinus1 = (colorFormat == EB_YUV420) ? 0 : 1; + const EB_S32 subWidthCMinus1 = (colorFormat == EB_YUV444) ? 0 : 1; + const EB_S32 subHeightCMinus1 = (colorFormat == EB_YUV420) ? 1 : 0; + + EB_U32 chromaLcuPos_x = lcuPos_x >> subWidthCMinus1; + EB_U32 chromaLcuPos_y = lcuPos_y >> subHeightCMinus1; + + EB_U32 numVerticalChromaSampleEdges = (lcuWidth >> (3 + subWidthCMinus1)) - (colorFormat==EB_YUV444?1:((lcuWidth & 15) == 0)); + EB_U32 numHorizontalChromaSampleEdges = (lcuHeight >> (3 + subHeightCMinus1)) - (colorFormat==EB_YUV420?((lcuHeight & 15) == 0):1); + EB_U32 num2SampleEdgesPerVerticalChromaSampleEdge = (lcuHeight >> (colorFormat==EB_YUV420?2:1)) - 2 - (((lcuHeight & (colorFormat==EB_YUV420?15:7)) == 0) << 1); + EB_U32 num2SampleEdgesPerHorizontalChromaSampleEdge = (lcuWidth >> (colorFormat==EB_YUV444?1:2)) - 2 - (((lcuWidth & (colorFormat==EB_YUV444?7:15)) == 0) << 1); + + + EB_U8 curCuQp; EB_U16 *edgeStartFilteredSamplePtr; EB_U16 *edgeStartSampleCb; @@ -2597,8 +2701,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore16bit( twoSampleEdgeStartSamplePos_x = horizontalIdx << 3; // LCU-wise position twoSampleEdgeStartSamplePos_y = (verticalIdx << 1) + 2; // LCU-wise position blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - twoSampleEdgeStartSamplePos_x, - twoSampleEdgeStartSamplePos_y, + twoSampleEdgeStartSamplePos_x >> subWidthShfitMinus1, + twoSampleEdgeStartSamplePos_y >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = verticalEdgeBSArray[BLK4X4_ADDR_TO_VERTICAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -2606,6 +2710,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, twoSampleEdgeStartSamplePos_x + chromaLcuPos_x, twoSampleEdgeStartSamplePos_y + chromaLcuPos_y, reconPictureControlSet->qpArrayStride); @@ -2616,13 +2722,15 @@ EB_ERRORTYPE LCUInternalAreaDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, twoSampleEdgeStartSamplePos_x + chromaLcuPos_x - 1, twoSampleEdgeStartSamplePos_y + chromaLcuPos_y, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, ((EB_S32)cbQp + 2) + reconPictureControlSet->tcOffset)]; @@ -2631,8 +2739,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore16bit( //CHKN cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconpicture->bufferCb + (reconpicture->originX >> 1) + (reconpicture->originY >> 1) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); - edgeStartSampleCr = (EB_U16*)reconpicture->bufferCr + (reconpicture->originX >> 1) + (reconpicture->originY >> 1) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); + edgeStartSampleCb = (EB_U16*)reconpicture->bufferCb + (reconpicture->originX >> subWidthCMinus1) + (reconpicture->originY >> subHeightCMinus1) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); + edgeStartSampleCr = (EB_U16*)reconpicture->bufferCr + (reconpicture->originX >> subWidthCMinus1) + (reconpicture->originY >> subHeightCMinus1) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -2653,8 +2761,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore16bit( twoSampleEdgeStartSamplePos_x = (horizontalIdx << 1) + 2; // LCU-wise position twoSampleEdgeStartSamplePos_y = verticalIdx << 3; // LCU-wise position blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - twoSampleEdgeStartSamplePos_x, - twoSampleEdgeStartSamplePos_y, + twoSampleEdgeStartSamplePos_x >> subWidthShfitMinus1, + twoSampleEdgeStartSamplePos_y >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = horizontalEdgeBSArray[BLK4X4_ADDR_TO_HORIZONTAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -2662,6 +2770,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, twoSampleEdgeStartSamplePos_x + chromaLcuPos_x, twoSampleEdgeStartSamplePos_y + chromaLcuPos_y, reconPictureControlSet->qpArrayStride); @@ -2672,14 +2782,16 @@ EB_ERRORTYPE LCUInternalAreaDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, twoSampleEdgeStartSamplePos_x + chromaLcuPos_x, twoSampleEdgeStartSamplePos_y + chromaLcuPos_y - 1, reconPictureControlSet->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)reconPictureControlSet->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - reconPictureControlSet->qpArrayStride; neighbourCuQp = reconPictureControlSet->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + reconPictureControlSet->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + reconPictureControlSet->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + reconPictureControlSet->tcOffset)]; @@ -2687,8 +2799,8 @@ EB_ERRORTYPE LCUInternalAreaDLFCore16bit( //CHKN cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconpicture->bufferCb + (reconpicture->originX >> 1) + (reconpicture->originY >> 1) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); - edgeStartSampleCr = (EB_U16*)reconpicture->bufferCr + (reconpicture->originX >> 1) + (reconpicture->originY >> 1) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); + edgeStartSampleCb = (EB_U16*)reconpicture->bufferCb + (reconpicture->originX >> subWidthCMinus1) + (reconpicture->originY >> subHeightCMinus1) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCb + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); + edgeStartSampleCr = (EB_U16*)reconpicture->bufferCr + (reconpicture->originX >> subWidthCMinus1) + (reconpicture->originY >> subHeightCMinus1) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_y + chromaLcuPos_y) * reconpicture->strideCr + (twoSampleEdgeStartSamplePos_x + chromaLcuPos_x); chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -2746,11 +2858,11 @@ void LCUBoundaryDLFCore( EB_U8 bSChromaEdgeB[2]; EB_U8 bSChromaEdgeC[2]; EB_U8 bSChromaEdgeD[2]; - EB_U32 num8x8ChromaBlkInTop8x8ChromablkRowMinus1 = (lcuWidth >> 4) - ((lcuWidth & 15) == 0); - EB_U32 num8x8ChromaBlkInLeft8x8ChromablkColumnMinus1 = (lcuHeight >> 4) - ((lcuHeight & 15) == 0); - EB_U32 chromaLcuPos_x = (lcuPos_x >> 1); - EB_U32 chromaLcuPos_y = (lcuPos_y >> 1); - + EB_COLOR_FORMAT colorFormat = reconpicture->colorFormat; + EB_U32 chromaLcuPos_x = lcuPos_x >> (colorFormat==EB_YUV444?0:1); + EB_U32 chromaLcuPos_y = lcuPos_y >> (colorFormat==EB_YUV420?1:0); + EB_U32 num8x8ChromaBlkInTop8x8ChromablkRowMinus1 = (lcuWidth >> (colorFormat==EB_YUV444?3:4)) - (colorFormat==EB_YUV444?1:((lcuWidth & 15) == 0)); + EB_U32 num8x8ChromaBlkInLeft8x8ChromablkColumnMinus1 = (lcuHeight >> (colorFormat==EB_YUV420?4:3)) - (colorFormat==EB_YUV420?((lcuHeight & 15) == 0):1); EB_U32 horizontalIdx; EB_U32 verticalIdx; @@ -2758,7 +2870,10 @@ void LCUBoundaryDLFCore( SequenceControlSet_t *sequenceControlSetPtr = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; EB_U32 lcuSize = sequenceControlSetPtr->lcuSize; - EB_U32 chromaLcuSize = lcuSize >> 1; + EB_U32 chromaLcuSizeX = lcuSize >> (colorFormat==EB_YUV444?0:1); + EB_U32 chromaLcuSizeY = lcuSize >> (colorFormat==EB_YUV420?1:0); + const EB_U32 subWidthShfitMinus1 = colorFormat==EB_YUV444?1:0; + const EB_U32 subHeightShfitMinus1 = colorFormat==EB_YUV420?0:1; EncodeContext_t *encodeContextPtr = ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr; @@ -2879,19 +2994,19 @@ void LCUBoundaryDLFCore( // filter the top-left corner 8x8 chroma block edgeAUpperBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - chromaLcuSize - 4, + (chromaLcuSizeY - 4) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeALowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - chromaLcuSize - 2, + (chromaLcuSizeY - 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeBLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - chromaLcuSize - 4, + (chromaLcuSizeX - 4) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); edgeBRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - chromaLcuSize - 2, + (chromaLcuSizeX - 2) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); @@ -2902,11 +3017,11 @@ void LCUBoundaryDLFCore( edgeCLowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - 2, + 2 >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeDRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - 2, + 2 >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); @@ -2937,35 +3052,35 @@ void LCUBoundaryDLFCore( // filter the top 8x8 chroma block row for (horizontalIdx = 1; horizontalIdx <= num8x8ChromaBlkInTop8x8ChromablkRowMinus1; ++horizontalIdx) { edgeAUpperBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - horizontalIdx << 3, - chromaLcuSize - 4, + horizontalIdx << (3 - subWidthShfitMinus1), + (chromaLcuSizeY - 4) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeALowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - horizontalIdx << 3, - chromaLcuSize - 2, + horizontalIdx << (3 - subWidthShfitMinus1), + (chromaLcuSizeY - 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeBLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - (horizontalIdx << 3) - 4, + ((horizontalIdx << 3) - 4) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); edgeBRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - (horizontalIdx << 3) - 2, + ((horizontalIdx << 3) - 2) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); edgeCUpperBlk2x2Addr = edgeDLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - horizontalIdx << 3, + horizontalIdx << (3 - subWidthShfitMinus1), 0, logMaxLcuSizeIn4x4blk); edgeCLowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - horizontalIdx << 3, - 2, + horizontalIdx << (3 - subWidthShfitMinus1), + 2 >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeDRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - (horizontalIdx << 3) + 2, + ((horizontalIdx << 3) + 2) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); @@ -2996,35 +3111,35 @@ void LCUBoundaryDLFCore( for (verticalIdx = 1; verticalIdx <= num8x8ChromaBlkInLeft8x8ChromablkColumnMinus1; ++verticalIdx) { edgeAUpperBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - (verticalIdx << 3) - 4, + ((verticalIdx << 3) - 4) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeALowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - (verticalIdx << 3) - 2, + ((verticalIdx << 3) - 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeBLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - chromaLcuSize - 4, - verticalIdx << 3, + (chromaLcuSizeX - 4) >> subWidthShfitMinus1, + verticalIdx << (3 - subHeightShfitMinus1), logMaxLcuSizeIn4x4blk); edgeBRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - chromaLcuSize - 2, - verticalIdx << 3, + (chromaLcuSizeX - 2) >> subWidthShfitMinus1, + verticalIdx << (3 - subHeightShfitMinus1), logMaxLcuSizeIn4x4blk); edgeCUpperBlk2x2Addr = edgeDLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - verticalIdx << 3, + verticalIdx << (3 - subHeightShfitMinus1), logMaxLcuSizeIn4x4blk); edgeCLowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - (verticalIdx << 3) + 2, + ((verticalIdx << 3) + 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeDRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - 2, - verticalIdx << 3, + 2 >> subWidthShfitMinus1, + verticalIdx << (3 - subHeightShfitMinus1), logMaxLcuSizeIn4x4blk); bSChromaEdgeA[0] = lcuVerticalEdgeBSArray[BLK4X4_ADDR_TO_VERTICAL_EDGE_BS_ARRAY_IDX(edgeAUpperBlk2x2Addr)]; @@ -3089,18 +3204,23 @@ void LCUBoundaryDLFCore16bit( EB_U8 bSChromaEdgeB[2]; EB_U8 bSChromaEdgeC[2]; EB_U8 bSChromaEdgeD[2]; - EB_U32 num8x8ChromaBlkInTop8x8ChromablkRowMinus1 = (lcuWidth >> 4) - ((lcuWidth & 15) == 0); - EB_U32 num8x8ChromaBlkInLeft8x8ChromablkColumnMinus1 = (lcuHeight >> 4) - ((lcuHeight & 15) == 0); - EB_U32 chromaLcuPos_x = (lcuPos_x >> 1); - EB_U32 chromaLcuPos_y = (lcuPos_y >> 1); + + EB_COLOR_FORMAT colorFormat = reconpicture->colorFormat; + EB_U32 chromaLcuPos_x = lcuPos_x >> (colorFormat==EB_YUV444?0:1); + EB_U32 chromaLcuPos_y = lcuPos_y >> (colorFormat==EB_YUV420?1:0); + EB_U32 num8x8ChromaBlkInTop8x8ChromablkRowMinus1 = (lcuWidth >> (colorFormat==EB_YUV444?3:4)) - (colorFormat==EB_YUV444?1:((lcuWidth & 15) == 0)); + EB_U32 num8x8ChromaBlkInLeft8x8ChromablkColumnMinus1 = (lcuHeight >> (colorFormat==EB_YUV420?4:3)) - (colorFormat==EB_YUV420?((lcuHeight & 15) == 0):1); EB_U32 horizontalIdx; EB_U32 verticalIdx; const EB_U32 logMaxLcuSizeIn4x4blk = Log2f(MAX_LCU_SIZE >> 2); SequenceControlSet_t *sequenceControlSetPtr = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; - EB_U32 lcuSize = sequenceControlSetPtr->lcuSize; - EB_U32 chromaLcuSize = lcuSize >> 1; + EB_U32 lcuSize = sequenceControlSetPtr->lcuSize; + EB_U32 chromaLcuSizeX = lcuSize >> (colorFormat==EB_YUV444?0:1); + EB_U32 chromaLcuSizeY = lcuSize >> (colorFormat==EB_YUV420?1:0); + const EB_U32 subWidthShfitMinus1 = colorFormat==EB_YUV444?1:0; + const EB_U32 subHeightShfitMinus1 = colorFormat==EB_YUV420?0:1; /***** luma component filtering *****/ // filter the top-left corner 8x8 luma block @@ -3214,19 +3334,19 @@ void LCUBoundaryDLFCore16bit( // filter the top-left corner 8x8 chroma block edgeAUpperBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - chromaLcuSize - 4, + (chromaLcuSizeY - 4) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeALowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - chromaLcuSize - 2, + (chromaLcuSizeY - 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeBLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - chromaLcuSize - 4, + (chromaLcuSizeX - 4) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); edgeBRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - chromaLcuSize - 2, + (chromaLcuSizeX - 2) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); @@ -3237,11 +3357,11 @@ void LCUBoundaryDLFCore16bit( edgeCLowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - 2, + 2 >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeDRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - 2, + 2 >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); @@ -3272,35 +3392,35 @@ void LCUBoundaryDLFCore16bit( // filter the top 8x8 chroma block row for (horizontalIdx = 1; horizontalIdx <= num8x8ChromaBlkInTop8x8ChromablkRowMinus1; ++horizontalIdx) { edgeAUpperBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - horizontalIdx << 3, - chromaLcuSize - 4, + horizontalIdx << (3 - subWidthShfitMinus1), + (chromaLcuSizeY - 4) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeALowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - horizontalIdx << 3, - chromaLcuSize - 2, + horizontalIdx << (3 - subWidthShfitMinus1), + (chromaLcuSizeY - 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeBLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - (horizontalIdx << 3) - 4, + ((horizontalIdx << 3) - 4) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); edgeBRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - (horizontalIdx << 3) - 2, + ((horizontalIdx << 3) - 2) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); edgeCUpperBlk2x2Addr = edgeDLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - horizontalIdx << 3, + horizontalIdx << (3 - subWidthShfitMinus1), 0, logMaxLcuSizeIn4x4blk); edgeCLowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - horizontalIdx << 3, - 2, + horizontalIdx << (3 - subWidthShfitMinus1), + 2 >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeDRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - (horizontalIdx << 3) + 2, + ((horizontalIdx << 3) + 2) >> subWidthShfitMinus1, 0, logMaxLcuSizeIn4x4blk); @@ -3331,35 +3451,35 @@ void LCUBoundaryDLFCore16bit( for (verticalIdx = 1; verticalIdx <= num8x8ChromaBlkInLeft8x8ChromablkColumnMinus1; ++verticalIdx) { edgeAUpperBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - (verticalIdx << 3) - 4, + ((verticalIdx << 3) - 4) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeALowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - (verticalIdx << 3) - 2, + ((verticalIdx << 3) - 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeBLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - chromaLcuSize - 4, - verticalIdx << 3, + (chromaLcuSizeX - 4) >> subWidthShfitMinus1, + verticalIdx << (3 - subHeightShfitMinus1), logMaxLcuSizeIn4x4blk); edgeBRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - chromaLcuSize - 2, - verticalIdx << 3, + (chromaLcuSizeX - 2) >> subWidthShfitMinus1, + verticalIdx << (3 - subHeightShfitMinus1), logMaxLcuSizeIn4x4blk); edgeCUpperBlk2x2Addr = edgeDLeftBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - verticalIdx << 3, + verticalIdx << (3 - subHeightShfitMinus1), logMaxLcuSizeIn4x4blk); edgeCLowerBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( 0, - (verticalIdx << 3) + 2, + ((verticalIdx << 3) + 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); edgeDRightBlk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - 2, - verticalIdx << 3, + 2 >> subWidthShfitMinus1, + verticalIdx << (3 - subHeightShfitMinus1), logMaxLcuSizeIn4x4blk); bSChromaEdgeA[0] = lcuVerticalEdgeBSArray[BLK4X4_ADDR_TO_VERTICAL_EDGE_BS_ARRAY_IDX(edgeAUpperBlk2x2Addr)]; @@ -3421,7 +3541,8 @@ void LCUPictureEdgeDLFCore( EB_U32 fourSampleEdgeStartSamplePos_y; EB_U8 bS; EB_U32 lcuSize; - EB_U32 chromaLcuSize; + EB_U32 chromaLcuSizeX; + EB_U32 chromaLcuSizeY; EB_U8 curCuQp; EB_BYTE edgeStartFilteredSamplePtr; EB_BYTE edgeStartSampleCb; @@ -3437,6 +3558,11 @@ void LCUPictureEdgeDLFCore( EB_U8 crQp; EB_U8 cbTc; EB_U8 crTc; + EB_COLOR_FORMAT colorFormat = reconPic->colorFormat; + const EB_S32 subWidthC = colorFormat==EB_YUV444?1:2; + const EB_S32 subHeightC = colorFormat==EB_YUV420?2:1; + const EB_S32 subWidthCMinus1 = colorFormat==EB_YUV444?0:1; + const EB_S32 subHeightCMinus1 = colorFormat==EB_YUV420?1:0; SequenceControlSet_t *sequenceControlSet = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; LargestCodingUnit_t *lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; @@ -3444,7 +3570,10 @@ void LCUPictureEdgeDLFCore( pictureWidthInLcu = (sequenceControlSet->lumaWidth + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; pictureHeightInLcu = (sequenceControlSet->lumaHeight + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; lcuSize = sequenceControlSet->lcuSize; - chromaLcuSize = lcuSize >> 1; + chromaLcuSizeX = lcuSize >> (colorFormat==EB_YUV444?0:1); + chromaLcuSizeY = lcuSize >> (colorFormat==EB_YUV420?1:0); + const EB_U32 subWidthShfitMinus1 = colorFormat==EB_YUV444?1:0; + const EB_U32 subHeightShfitMinus1 = colorFormat==EB_YUV420?0:1; if (lcuPos_x >> lcuPtr->sizeLog2 == pictureWidthInLcu - 1) { /***** picture right-most 4 sample horizontal edges filtering *****/ // luma component filtering @@ -3507,19 +3636,19 @@ void LCUPictureEdgeDLFCore( // chroma component filtering if ((sequenceControlSet->chromaWidth & 7) == 0) { //num4SampleHorizontalEdges = (sequenceControlSet->chromaHeight >> 3) + ((sequenceControlSet->chromaHeight & 7) != 0) - 1; - num4SampleHorizontalEdges = (lcuHeight >> 4) + (((lcuHeight >> 1) & 7) != 0); + num4SampleHorizontalEdges = (lcuHeight >> (colorFormat==EB_YUV420?4:3)) + (((lcuHeight >> (colorFormat==EB_YUV420?1:0)) & (colorFormat==EB_YUV420?7:15)) != 0); fourSampleEdgeStartSamplePos_x = sequenceControlSet->chromaWidth - 4; // Picture wise location //for(verticalIdx = 1; verticalIdx <= num4SampleHorizontalEdges; ++verticalIdx) { for (verticalIdx = (lcuPos_y == 0); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { //fourSampleEdgeStartSamplePos_y = verticalIdx << 3; // Picture wise location - fourSampleEdgeStartSamplePos_y = (lcuPos_y >> 1) + (verticalIdx << 3); // Picture wise location + fourSampleEdgeStartSamplePos_y = (lcuPos_y >> (colorFormat==EB_YUV420?1:0)) + (verticalIdx << 3); // Picture wise location //lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize-1)) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize-1)); //lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; // left 2 sample edge blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - fourSampleEdgeStartSamplePos_x & (chromaLcuSize - 1), - fourSampleEdgeStartSamplePos_y & (chromaLcuSize - 1), + (fourSampleEdgeStartSamplePos_x & (chromaLcuSizeX - 1)) >> subWidthShfitMinus1, + (fourSampleEdgeStartSamplePos_y & (chromaLcuSizeY - 1)) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = pictureControlSetPtr->horizontalEdgeBSArray[lcuIdx][BLK4X4_ADDR_TO_HORIZONTAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -3527,6 +3656,8 @@ void LCUPictureEdgeDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x, fourSampleEdgeStartSamplePos_y, pictureControlSetPtr->qpArrayStride); @@ -3537,19 +3668,21 @@ void LCUPictureEdgeDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x, fourSampleEdgeStartSamplePos_y - 1, pictureControlSetPtr->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)pictureControlSetPtr->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - pictureControlSetPtr->qpArrayStride; neighbourCuQp = pictureControlSetPtr->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + pictureControlSetPtr->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + pictureControlSetPtr->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -3563,8 +3696,8 @@ void LCUPictureEdgeDLFCore( // right 2 sample edge blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - (fourSampleEdgeStartSamplePos_x & (chromaLcuSize - 1)) + 2, - fourSampleEdgeStartSamplePos_y & (chromaLcuSize - 1), + ((fourSampleEdgeStartSamplePos_x & (chromaLcuSizeX - 1)) + 2) >> subWidthShfitMinus1, + (fourSampleEdgeStartSamplePos_y & (chromaLcuSizeY - 1)) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = pictureControlSetPtr->horizontalEdgeBSArray[lcuIdx][BLK4X4_ADDR_TO_HORIZONTAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -3572,6 +3705,8 @@ void LCUPictureEdgeDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x + 2, fourSampleEdgeStartSamplePos_y, pictureControlSetPtr->qpArrayStride); @@ -3582,19 +3717,21 @@ void LCUPictureEdgeDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x + 2, fourSampleEdgeStartSamplePos_y - 1, pictureControlSetPtr->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)pictureControlSetPtr->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - pictureControlSetPtr->qpArrayStride; neighbourCuQp = pictureControlSetPtr->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + pictureControlSetPtr->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + pictureControlSetPtr->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + (fourSampleEdgeStartSamplePos_x + 2); - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + (fourSampleEdgeStartSamplePos_x + 2); + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + (fourSampleEdgeStartSamplePos_x + 2); + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + (fourSampleEdgeStartSamplePos_x + 2); Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -3668,18 +3805,19 @@ void LCUPictureEdgeDLFCore( // chroma component filtering if ((sequenceControlSet->chromaHeight & 7) == 0) { //num4SampleVerticalEdges = (sequenceControlSet->chromaWidth >> 3) + ((sequenceControlSet->chromaWidth & 7) != 0) - 1; - num4SampleVerticalEdges = (lcuWidth >> 4) + (((lcuWidth >> 1) & 7) != 0); + num4SampleVerticalEdges = (lcuWidth >> (3+(colorFormat==EB_YUV444?0:1))) + (((lcuWidth >> (colorFormat==EB_YUV444?0:1)) & (colorFormat==EB_YUV444?15:7)) != 0); + fourSampleEdgeStartSamplePos_y = sequenceControlSet->chromaHeight - 4; // Picture wise location //for(horizontalIdx = 1; horizontalIdx <= num4SampleVerticalEdges; ++horizontalIdx) { for (horizontalIdx = (lcuPos_x == 0); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { - fourSampleEdgeStartSamplePos_x = (lcuPos_x >> 1) + (horizontalIdx << 3); // Picture wise location - lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize - 1)) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize - 1)); + fourSampleEdgeStartSamplePos_x = (lcuPos_x >> (colorFormat==EB_YUV444?0:1)) + (horizontalIdx << 3); // Picture wise location + lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize - (colorFormat==EB_YUV420?1:0))) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize - (colorFormat==EB_YUV444?0:1))); lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; // Upper 2 sample edge blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - fourSampleEdgeStartSamplePos_x & (chromaLcuSize - 1), - fourSampleEdgeStartSamplePos_y & (chromaLcuSize - 1), + (fourSampleEdgeStartSamplePos_x & (chromaLcuSizeX - 1)) >> subWidthShfitMinus1, + (fourSampleEdgeStartSamplePos_y & (chromaLcuSizeY - 1)) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = pictureControlSetPtr->verticalEdgeBSArray[lcuIdx][BLK4X4_ADDR_TO_VERTICAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -3687,6 +3825,8 @@ void LCUPictureEdgeDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x, fourSampleEdgeStartSamplePos_y, pictureControlSetPtr->qpArrayStride); @@ -3697,6 +3837,8 @@ void LCUPictureEdgeDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x - 1, fourSampleEdgeStartSamplePos_y, pictureControlSetPtr->qpArrayStride); @@ -3704,13 +3846,13 @@ void LCUPictureEdgeDLFCore( neighbourCuQp = pictureControlSetPtr->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + pictureControlSetPtr->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + pictureControlSetPtr->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -3724,8 +3866,8 @@ void LCUPictureEdgeDLFCore( // Lower 2 sample edge blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - fourSampleEdgeStartSamplePos_x & (chromaLcuSize - 1), - (fourSampleEdgeStartSamplePos_y & (chromaLcuSize - 1)) + 2, + (fourSampleEdgeStartSamplePos_x & (chromaLcuSizeX - 1)) >> subWidthShfitMinus1, + ((fourSampleEdgeStartSamplePos_y & (chromaLcuSizeY - 1)) + 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = pictureControlSetPtr->verticalEdgeBSArray[lcuIdx][BLK4X4_ADDR_TO_VERTICAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -3733,6 +3875,8 @@ void LCUPictureEdgeDLFCore( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x, fourSampleEdgeStartSamplePos_y + 2, pictureControlSetPtr->qpArrayStride); @@ -3743,18 +3887,20 @@ void LCUPictureEdgeDLFCore( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x - 1, fourSampleEdgeStartSamplePos_y + 2, pictureControlSetPtr->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)pictureControlSetPtr->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - pictureControlSetPtr->qpArrayStride; neighbourCuQp = pictureControlSetPtr->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + pictureControlSetPtr->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + pictureControlSetPtr->tcOffset)]; - edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + (fourSampleEdgeStartSamplePos_y + 2) * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; - edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + (fourSampleEdgeStartSamplePos_y + 2) * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCb = reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + (fourSampleEdgeStartSamplePos_y + 2) * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCr = reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + (fourSampleEdgeStartSamplePos_y + 2) * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; Chroma2SampleEdgeDLFCore_Table[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -3800,7 +3946,8 @@ void LCUPictureEdgeDLFCore16bit( EB_U32 fourSampleEdgeStartSamplePos_y; EB_U8 bS; EB_U32 lcuSize; - EB_U32 chromaLcuSize; + EB_U32 chromaLcuSizeX; + EB_U32 chromaLcuSizeY; EB_U8 curCuQp; EB_U16 *edgeStartFilteredSamplePtr; EB_U16 *edgeStartSampleCb; @@ -3816,15 +3963,23 @@ void LCUPictureEdgeDLFCore16bit( EB_U8 crQp; EB_U8 cbTc; EB_U8 crTc; + EB_COLOR_FORMAT colorFormat = reconPic->colorFormat; + const EB_S32 subWidthC = colorFormat==EB_YUV444?1:2; + const EB_S32 subHeightC = colorFormat==EB_YUV420?2:1; + const EB_S32 subWidthCMinus1 = colorFormat==EB_YUV444?0:1; + const EB_S32 subHeightCMinus1 = colorFormat==EB_YUV420?1:0; SequenceControlSet_t *sequenceControlSet = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; LargestCodingUnit_t *lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; pictureWidthInLcu = (sequenceControlSet->lumaWidth + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; pictureHeightInLcu = (sequenceControlSet->lumaHeight + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; - lcuSize = sequenceControlSet->lcuSize; - chromaLcuSize = lcuSize >> 1; - + lcuSize = sequenceControlSet->lcuSize; + chromaLcuSizeX = lcuSize >> (colorFormat==EB_YUV444?0:1); + chromaLcuSizeY = lcuSize >> (colorFormat==EB_YUV420?1:0); + const EB_U32 subWidthShfitMinus1 = colorFormat==EB_YUV444?1:0; + const EB_U32 subHeightShfitMinus1 = colorFormat==EB_YUV420?0:1; + if (lcuPos_x >> lcuPtr->sizeLog2 == pictureWidthInLcu - 1) { /***** picture right-most 4 sample horizontal edges filtering *****/ // luma component filtering @@ -3890,19 +4045,19 @@ void LCUPictureEdgeDLFCore16bit( // chroma component filtering if ((sequenceControlSet->chromaWidth & 7) == 0) { //num4SampleHorizontalEdges = (sequenceControlSet->chromaHeight >> 3) + ((sequenceControlSet->chromaHeight & 7) != 0) - 1; - num4SampleHorizontalEdges = (lcuHeight >> 4) + (((lcuHeight >> 1) & 7) != 0); + num4SampleHorizontalEdges = (lcuHeight >> (colorFormat==EB_YUV420?4:3)) + (((lcuHeight >> (colorFormat==EB_YUV420?1:0)) & (colorFormat==EB_YUV420?7:15)) != 0); fourSampleEdgeStartSamplePos_x = sequenceControlSet->chromaWidth - 4; // Picture wise location //for(verticalIdx = 1; verticalIdx <= num4SampleHorizontalEdges; ++verticalIdx) { for (verticalIdx = (lcuPos_y == 0); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { //fourSampleEdgeStartSamplePos_y = verticalIdx << 3; // Picture wise location - fourSampleEdgeStartSamplePos_y = (lcuPos_y >> 1) + (verticalIdx << 3); // Picture wise location + fourSampleEdgeStartSamplePos_y = (lcuPos_y >> (colorFormat==EB_YUV420?1:0)) + (verticalIdx << 3); // Picture wise location //lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize-1)) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize-1)); //lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; // left 2 sample edge blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - fourSampleEdgeStartSamplePos_x & (chromaLcuSize - 1), - fourSampleEdgeStartSamplePos_y & (chromaLcuSize - 1), + (fourSampleEdgeStartSamplePos_x & (chromaLcuSizeX - 1)) >> subWidthShfitMinus1, + (fourSampleEdgeStartSamplePos_y & (chromaLcuSizeY - 1)) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = pictureControlSetPtr->horizontalEdgeBSArray[lcuIdx][BLK4X4_ADDR_TO_HORIZONTAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -3910,6 +4065,8 @@ void LCUPictureEdgeDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x, fourSampleEdgeStartSamplePos_y, pictureControlSetPtr->qpArrayStride); @@ -3920,22 +4077,24 @@ void LCUPictureEdgeDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x, fourSampleEdgeStartSamplePos_y - 1, pictureControlSetPtr->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)pictureControlSetPtr->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - pictureControlSetPtr->qpArrayStride; neighbourCuQp = pictureControlSetPtr->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + pictureControlSetPtr->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + pictureControlSetPtr->tcOffset)]; cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = ((EB_U16*)reconPic->bufferCb) + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; - edgeStartSampleCr = ((EB_U16*)reconPic->bufferCr) + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCb = ((EB_U16*)reconPic->bufferCb) + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCr = ((EB_U16*)reconPic->bufferCr) + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -3949,8 +4108,8 @@ void LCUPictureEdgeDLFCore16bit( // right 2 sample edge blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - (fourSampleEdgeStartSamplePos_x & (chromaLcuSize - 1)) + 2, - fourSampleEdgeStartSamplePos_y & (chromaLcuSize - 1), + ((fourSampleEdgeStartSamplePos_x & (chromaLcuSizeX - 1)) + 2) >> subWidthShfitMinus1, + (fourSampleEdgeStartSamplePos_y & (chromaLcuSizeY - 1)) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = pictureControlSetPtr->horizontalEdgeBSArray[lcuIdx][BLK4X4_ADDR_TO_HORIZONTAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -3958,6 +4117,8 @@ void LCUPictureEdgeDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x + 2, fourSampleEdgeStartSamplePos_y, pictureControlSetPtr->qpArrayStride); @@ -3968,6 +4129,8 @@ void LCUPictureEdgeDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x + 2, fourSampleEdgeStartSamplePos_y - 1, pictureControlSetPtr->qpArrayStride); @@ -3975,16 +4138,16 @@ void LCUPictureEdgeDLFCore16bit( neighbourCuQp = pictureControlSetPtr->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + pictureControlSetPtr->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + pictureControlSetPtr->tcOffset)]; cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = ((EB_U16*)reconPic->bufferCb) + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + (fourSampleEdgeStartSamplePos_x + 2); - edgeStartSampleCr = ((EB_U16*)reconPic->bufferCr) + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + (fourSampleEdgeStartSamplePos_x + 2); + edgeStartSampleCb = ((EB_U16*)reconPic->bufferCb) + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + (fourSampleEdgeStartSamplePos_x + 2); + edgeStartSampleCr = ((EB_U16*)reconPic->bufferCr) + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + (fourSampleEdgeStartSamplePos_x + 2); chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, @@ -4060,18 +4223,18 @@ void LCUPictureEdgeDLFCore16bit( // chroma component filtering if ((sequenceControlSet->chromaHeight & 7) == 0) { //num4SampleVerticalEdges = (sequenceControlSet->chromaWidth >> 3) + ((sequenceControlSet->chromaWidth & 7) != 0) - 1; - num4SampleVerticalEdges = (lcuWidth >> 4) + (((lcuWidth >> 1) & 7) != 0); + num4SampleVerticalEdges = (lcuWidth >> (3+(colorFormat==EB_YUV444?0:1))) + (((lcuWidth >> (colorFormat==EB_YUV444?0:1)) & (colorFormat==EB_YUV444?15:7)) != 0); fourSampleEdgeStartSamplePos_y = sequenceControlSet->chromaHeight - 4; // Picture wise location //for(horizontalIdx = 1; horizontalIdx <= num4SampleVerticalEdges; ++horizontalIdx) { for (horizontalIdx = (lcuPos_x == 0); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { - fourSampleEdgeStartSamplePos_x = (lcuPos_x >> 1) + (horizontalIdx << 3); // Picture wise location - lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize - 1)) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize - 1)); + fourSampleEdgeStartSamplePos_x = (lcuPos_x >> (colorFormat==EB_YUV444?0:1)) + (horizontalIdx << 3); // Picture wise location + lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize - (colorFormat==EB_YUV420?1:0))) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize - (colorFormat==EB_YUV444?0:1))); lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; // Upper 2 sample edge blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - fourSampleEdgeStartSamplePos_x & (chromaLcuSize - 1), - fourSampleEdgeStartSamplePos_y & (chromaLcuSize - 1), + (fourSampleEdgeStartSamplePos_x & (chromaLcuSizeX - 1)) >> subWidthShfitMinus1, + (fourSampleEdgeStartSamplePos_y & (chromaLcuSizeY - 1)) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = pictureControlSetPtr->verticalEdgeBSArray[lcuIdx][BLK4X4_ADDR_TO_VERTICAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -4079,6 +4242,8 @@ void LCUPictureEdgeDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x, fourSampleEdgeStartSamplePos_y, pictureControlSetPtr->qpArrayStride); @@ -4090,21 +4255,23 @@ void LCUPictureEdgeDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x - 1, fourSampleEdgeStartSamplePos_y, pictureControlSetPtr->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)pictureControlSetPtr->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - pictureControlSetPtr->qpArrayStride; neighbourCuQp = pictureControlSetPtr->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + pictureControlSetPtr->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + pictureControlSetPtr->tcOffset)]; cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + fourSampleEdgeStartSamplePos_y * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + fourSampleEdgeStartSamplePos_y * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -4117,9 +4284,9 @@ void LCUPictureEdgeDLFCore16bit( } // Lower 2 sample edge - blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( - fourSampleEdgeStartSamplePos_x & (chromaLcuSize - 1), - (fourSampleEdgeStartSamplePos_y & (chromaLcuSize - 1)) + 2, + blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( + (fourSampleEdgeStartSamplePos_x & (chromaLcuSizeX - 1)) >> subWidthShfitMinus1, + ((fourSampleEdgeStartSamplePos_y & (chromaLcuSizeY - 1)) + 2) >> subHeightShfitMinus1, logMaxLcuSizeIn4x4blk); bS = pictureControlSetPtr->verticalEdgeBSArray[lcuIdx][BLK4X4_ADDR_TO_VERTICAL_EDGE_BS_ARRAY_IDX(blk2x2Addr)]; @@ -4127,6 +4294,8 @@ void LCUPictureEdgeDLFCore16bit( // Qp for the current CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x, fourSampleEdgeStartSamplePos_y + 2, pictureControlSetPtr->qpArrayStride); @@ -4138,22 +4307,23 @@ void LCUPictureEdgeDLFCore16bit( // Qp for the neighboring CU CUqpIndex = CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( + subWidthC, + subHeightC, fourSampleEdgeStartSamplePos_x - 1, fourSampleEdgeStartSamplePos_y + 2, pictureControlSetPtr->qpArrayStride); //CUqpIndex = ((CUqpIndex - (EB_S32)pictureControlSetPtr->qpArrayStride) < 0) ? CUqpIndex : CUqpIndex - pictureControlSetPtr->qpArrayStride; neighbourCuQp = pictureControlSetPtr->qpArray[CUqpIndex]; - cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset)); - crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset)); + cbQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->cbQpOffset), colorFormat>=EB_YUV422); + crQp = convertToChromaQp((EB_S32)(((curCuQp + neighbourCuQp + 1) >> 1) + pictureControlSetPtr->crQpOffset), colorFormat>=EB_YUV422); //chromaQp = MapChromaQp(Qp); cbTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (cbQp + 2) + pictureControlSetPtr->tcOffset)]; crTc = TcTable_8x8[CLIP3EQ(MIN_QP_VALUE, MAX_QP_VALUE_PLUS_INTRA_TC_OFFSET, (crQp + 2) + pictureControlSetPtr->tcOffset)]; cbTc = cbTc << 2; crTc = crTc << 2; - edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCb + (fourSampleEdgeStartSamplePos_y + 2) * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; - edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> 1) + (reconPic->originY >> 1) * reconPic->strideCr + (fourSampleEdgeStartSamplePos_y + 2) * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; - + edgeStartSampleCb = (EB_U16*)reconPic->bufferCb + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCb + (fourSampleEdgeStartSamplePos_y + 2) * reconPic->strideCb + fourSampleEdgeStartSamplePos_x; + edgeStartSampleCr = (EB_U16*)reconPic->bufferCr + (reconPic->originX >> subWidthCMinus1) + (reconPic->originY >> subHeightCMinus1) * reconPic->strideCr + (fourSampleEdgeStartSamplePos_y + 2) * reconPic->strideCr + fourSampleEdgeStartSamplePos_x; chromaDlf_funcPtrArray16bit[(ASM_TYPES & PREAVX2_MASK) && 1]( edgeStartSampleCb, diff --git a/Source/Lib/Codec/EbDeblockingFilter.h b/Source/Lib/Codec/EbDeblockingFilter.h index 63bcf6e92..ff2431287 100644 --- a/Source/Lib/Codec/EbDeblockingFilter.h +++ b/Source/Lib/Codec/EbDeblockingFilter.h @@ -21,7 +21,7 @@ extern "C" { #define GET_LUMA_4X4BLK_ADDR(lumaLcuWise4x4BlkPos_x, lumaLcuWise4x4BlkPos_y, logMaxLcuSizeIn4x4blk) (((lumaLcuWise4x4BlkPos_x)>>2) + (((lumaLcuWise4x4BlkPos_y)>>2) << (logMaxLcuSizeIn4x4blk))) #define GET_CHROMA_4X4BLK_ADDR(chromaLcuWise2x2BlkPos_x, chromaLcuWise2x2BlkPos_y, logMaxLcuSizeIn4x4blk) (((chromaLcuWise2x2BlkPos_x)>>1) + (((chromaLcuWise2x2BlkPos_y)>>1) << (logMaxLcuSizeIn4x4blk))) #define LUMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX(pos_x, pos_y, qpArrayStride) (((pos_x) >> LOG_MIN_CU_SIZE) + ((pos_y) >> LOG_MIN_CU_SIZE) * (qpArrayStride)) -#define CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX(pos_x, pos_y, qpArrayStride) ((2*(pos_x) >> LOG_MIN_CU_SIZE) + (2*(pos_y) >> LOG_MIN_CU_SIZE) * (qpArrayStride)) +#define CHROMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX(subw, subh, pos_x, pos_y, qpArrayStride) (((subw*(EB_S32)(pos_x)) >> LOG_MIN_CU_SIZE) + ((subh*(EB_S32)(pos_y)) >> LOG_MIN_CU_SIZE) * (qpArrayStride)) #define CHECK_MV_COMPONENT_EQUAL_OR_GREATER_THAN_4(pu1Ptr, pu2Ptr, pu1RefList, pu2RefList) ( \ EB_ABS_DIFF((pu1Ptr)->mv[(pu1RefList)].x, (pu2Ptr)->mv[(pu2RefList)].x) >= 4 || \ EB_ABS_DIFF((pu1Ptr)->mv[(pu1RefList)].y, (pu2Ptr)->mv[(pu2RefList)].y) >= 4 \ diff --git a/Source/Lib/Codec/EbDefinitions.h b/Source/Lib/Codec/EbDefinitions.h index 17c1a5881..063df18e6 100644 --- a/Source/Lib/Codec/EbDefinitions.h +++ b/Source/Lib/Codec/EbDefinitions.h @@ -668,6 +668,28 @@ extern EB_U32 libMutexCount; } \ libMutexCount++; +#define EB_STRDUP(dst, src) \ + EB_MALLOC_(char*, dst, strlen(src)+1, EB_N_PTR); \ + strcpy_ss((char*)dst, strlen(src)+1, src); + +#ifdef _WIN32 +#define EB_SCANF sscanf_s +#else +#define EB_SCANF sscanf +#endif + +#ifdef _MSC_VER +#define FOPEN(f,s,m) fopen_s(&f,s,m) +#else +#define FOPEN(f,s,m) f=fopen(s,m) +#endif + +#ifdef _MSC_VER +#define EB_STRTOK(str,delim,next) strtok_s((char*)str,(const char*)delim,(char**)next) +#else +#define EB_STRTOK(str,delim,next) strtok_r((char*)str,(const char*)delim,(char**)next) +#endif + /** The EB_CTOR type is used to define the svt object constructors. objectPtr is a EB_PTR to the object being constructed. objectInitDataPtr is a EB_PTR to a data structure used to initialize the object. @@ -810,6 +832,8 @@ typedef enum EB_SEI { TEMPORAL_LEVEL0_INDEX = 131, SCALABLE_NESTING = 133, REGION_REFRESH_INFO = 134, + MASTERING_DISPLAY_INFO = 137, + CONTENT_LIGHT_LEVEL_INFO = 144 } EB_SEI; diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 5c8a6a646..b1009257a 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -47,7 +47,8 @@ EB_ERRORTYPE EncDecContextCtor( EbFifo_t *packetizationOutputFifoPtr, EbFifo_t *feedbackFifoPtr, EbFifo_t *pictureDemuxFifoPtr, - EB_BOOL is16bit) + EB_BOOL is16bit, + EB_COLOR_FORMAT colorFormat) { EB_ERRORTYPE return_error = EB_ErrorNone; EncDecContext_t *contextPtr; @@ -55,6 +56,7 @@ EB_ERRORTYPE EncDecContextCtor( *contextDblPtr = contextPtr; contextPtr->is16bit = is16bit; + contextPtr->colorFormat = colorFormat; // Input/Output System Resource Manager FIFOs contextPtr->modeDecisionInputFifoPtr = modeDecisionConfigurationInputFifoPtr; @@ -86,6 +88,7 @@ EB_ERRORTYPE EncDecContextCtor( initData.topPadding = 0; initData.botPadding = 0; initData.splitMode = EB_FALSE; + initData.colorFormat = colorFormat; contextPtr->inputSample16bitBuffer = (EbPictureBufferDesc_t *)EB_NULL; if (is16bit) { @@ -109,6 +112,7 @@ EB_ERRORTYPE EncDecContextCtor( initData.maxWidth = MAX_LCU_SIZE; initData.maxHeight = MAX_LCU_SIZE; initData.bitDepth = EB_16BIT; + initData.colorFormat = colorFormat; initData.leftPadding = 0; initData.rightPadding = 0; initData.topPadding = 0; @@ -132,13 +136,13 @@ EB_ERRORTYPE EncDecContextCtor( } // Intra Reference Samples - return_error = IntraReferenceSamplesCtor(&contextPtr->intraRefPtr); + return_error = IntraReferenceSamplesCtor(&contextPtr->intraRefPtr, colorFormat); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } contextPtr->intraRefPtr16 = (IntraReference16bitSamples_t *)EB_NULL; if (is16bit) { - return_error = IntraReference16bitSamplesCtor(&contextPtr->intraRefPtr16); + return_error = IntraReference16bitSamplesCtor(&contextPtr->intraRefPtr16, colorFormat); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } @@ -229,10 +233,13 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( EB_U8 *temporalBufferLeft; EB_U8 tmp[64], tmpY[64]; EB_U32 i, j; - EB_S8 ReorderedsaoOffset[4 + 1]; + EB_S8 ReorderedsaoOffset[4 + 1 + 3]; EB_S8 OrderedsaoOffsetBo[5]; EB_BOOL FirstColLcu, LastColLcu, FirstRowLcu, LastRowLcu; EncodeContext_t *encodeContextPtr; + const EB_COLOR_FORMAT colorFormat = pictureControlSetPtr->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; encodeContextPtr = ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr; @@ -244,6 +251,9 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( ReorderedsaoOffset[2] = 0; ReorderedsaoOffset[3] = (EB_S8)saoPtr->saoOffset[videoComponent][2]; ReorderedsaoOffset[4] = (EB_S8)saoPtr->saoOffset[videoComponent][3]; + ReorderedsaoOffset[5] = 0; + ReorderedsaoOffset[6] = 0; + ReorderedsaoOffset[7] = 0; OrderedsaoOffsetBo[0] = (EB_S8)saoPtr->saoOffset[videoComponent][0]; OrderedsaoOffsetBo[1] = (EB_S8)saoPtr->saoOffset[videoComponent][1]; @@ -252,7 +262,7 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( OrderedsaoOffsetBo[4] = 0; temporalBufferLeft = contextPtr->saoLeftBuffer[pingpongIdxLeft]; - temporalBufferUpper = contextPtr->saoUpBuffer[pingpongIdxUp] + (tbOriginX >> isChroma); + temporalBufferUpper = contextPtr->saoUpBuffer[pingpongIdxUp] + (tbOriginX >> (isChroma ? subWidthCMinus1:0)); //TODO: get to this function only when lcuPtr->saoTypeIndex[isChroma] is not OFF switch (saoPtr->saoTypeIndex[isChroma]) { @@ -260,7 +270,7 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( case 1: // EO - 0 degrees FirstColLcu = (EB_BOOL)(tbOriginX == 0); - LastColLcu = (EB_BOOL)((tbOriginX >> isChroma) + lcuWidth == pictureWidth); + LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -302,7 +312,7 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( case 2: // EO - 90 degrees FirstRowLcu = (EB_BOOL)(tbOriginY == 0); - LastRowLcu = (EB_BOOL)((tbOriginY >> isChroma) + lcuHeight == pictureHeight); + LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); //save the non filtered first row if this LCU is on the first LCU Row if (FirstRowLcu) { @@ -342,9 +352,9 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( case 3: // EO - 135 degrees FirstColLcu = (EB_BOOL)(tbOriginX == 0); - LastColLcu = (EB_BOOL)((tbOriginX >> isChroma) + lcuWidth == pictureWidth); + LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); FirstRowLcu = (EB_BOOL)(tbOriginY == 0); - LastRowLcu = (EB_BOOL)((tbOriginY >> isChroma) + lcuHeight == pictureHeight); + LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -408,9 +418,9 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( case 4: // EO - 45 degrees FirstColLcu = (EB_BOOL)(tbOriginX == 0); - LastColLcu = (EB_BOOL)((tbOriginX >> isChroma) + lcuWidth == pictureWidth); + LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); FirstRowLcu = (EB_BOOL)(tbOriginY == 0); - LastRowLcu = (EB_BOOL)((tbOriginY >> isChroma) + lcuHeight == pictureHeight); + LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -533,6 +543,9 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( reconPicturePtr = ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->referencePicture; else reconPicturePtr = pictureControlSetPtr->reconPicturePtr; + const EB_COLOR_FORMAT colorFormat = reconPicturePtr->colorFormat; // Chroma format + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; // Apply SAO // Y @@ -610,12 +623,13 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( tbOriginX = lcuParams->originX; tbOriginY = lcuParams->originY; - lcuWidth = lcuParams->width >> 1; - lcuHeight = lcuParams->height >> 1; + lcuWidth = lcuParams->width >> subWidthCMinus1; + lcuHeight = lcuParams->height >> subHeightCMinus1; saoParams = &pictureControlSetPtr->lcuPtrArray[lcuIndex]->saoParams; - reconSampleChromaIndex = ((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCb + reconPicturePtr->originX + tbOriginX) >> 1; + reconSampleChromaIndex = ((reconPicturePtr->originX + tbOriginX) >> subWidthCMinus1) + + (((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCb) >> subHeightCMinus1); if (tbOriginX == 0) { @@ -624,7 +638,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( EB_MEMCPY(contextPtr->saoUpBuffer[pingpongIdxUp], reconSampleCbPtr, sizeof(EB_U8) * sequenceControlSetPtr->chromaWidth); } - lcuHeightPlusOne = (sequenceControlSetPtr->chromaHeight == (tbOriginY >> 1) + lcuHeight) ? lcuHeight : lcuHeight + 1; + lcuHeightPlusOne = (sequenceControlSetPtr->chromaHeight == (tbOriginY >> subHeightCMinus1) + lcuHeight) ? lcuHeight : lcuHeight + 1; //Save last pixel colunm of this LCU for next LCU for (lcuRow = 0; lcuRow < lcuHeightPlusOne; ++lcuRow) { contextPtr->saoLeftBuffer[pingpongIdxLeft][lcuRow] = reconPicturePtr->bufferCb[reconSampleChromaIndex + lcuWidth - 1 + lcuRow*reconPicturePtr->strideCb]; @@ -669,13 +683,13 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( tbOriginX = lcuParams->originX; tbOriginY = lcuParams->originY; - lcuWidth = lcuParams->width >> 1; - lcuHeight = lcuParams->height >> 1; + lcuWidth = lcuParams->width >> subWidthCMinus1; + lcuHeight = lcuParams->height >> subHeightCMinus1; saoParams = &pictureControlSetPtr->lcuPtrArray[lcuIndex]->saoParams; - reconSampleChromaIndex = ((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCr + reconPicturePtr->originX + tbOriginX) >> 1; - + reconSampleChromaIndex = ((reconPicturePtr->originX + tbOriginX) >> subWidthCMinus1) + + (((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCr) >> subHeightCMinus1); if (tbOriginX == 0) { @@ -686,7 +700,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( } - lcuHeightPlusOne = (sequenceControlSetPtr->chromaHeight == (tbOriginY >> 1) + lcuHeight) ? lcuHeight : lcuHeight + 1; + lcuHeightPlusOne = (sequenceControlSetPtr->chromaHeight == (tbOriginY >> subHeightCMinus1) + lcuHeight) ? lcuHeight : lcuHeight + 1; //Save last pixel colunm of this LCU for next LCU for (lcuRow = 0; lcuRow < lcuHeightPlusOne; ++lcuRow) { @@ -757,6 +771,9 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( EB_S8 OrderedsaoOffsetBo[5]; EB_BOOL FirstColLcu, LastColLcu, FirstRowLcu, LastRowLcu; + const EB_COLOR_FORMAT colorFormat = pictureControlSetPtr->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; (void)bitDepth; lcuHeightCount = 0; @@ -773,14 +790,14 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( OrderedsaoOffsetBo[4] = 0; temporalBufferLeft = contextPtr->saoLeftBuffer16[pingpongIdxLeft]; - temporalBufferUpper = contextPtr->saoUpBuffer16[pingpongIdxUp] + (tbOriginX >> isChroma); + temporalBufferUpper = contextPtr->saoUpBuffer16[pingpongIdxUp] + (tbOriginX >> (isChroma ? subWidthCMinus1: 0)); //TODO: get to this function only when lcuPtr->saoTypeIndex[isChroma] is not OFF switch (saoPtr->saoTypeIndex[isChroma]) { case 1: // EO - 0 degrees FirstColLcu = (EB_BOOL)(tbOriginX == 0); - LastColLcu = (EB_BOOL)((tbOriginX >> isChroma) + lcuWidth == pictureWidth); + LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -822,7 +839,7 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( case 2: // EO - 90 degrees FirstRowLcu = (EB_BOOL)((tbOriginY == 0)); - LastRowLcu = (EB_BOOL)(((tbOriginY >> isChroma) + lcuHeight == pictureHeight)); + LastRowLcu = (EB_BOOL)(((tbOriginY >> (isChroma ? subHeightCMinus1 : 0)) + lcuHeight == pictureHeight)); //save the non filtered first row if this LCU is on the first LCU Row if (FirstRowLcu) { @@ -863,9 +880,9 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( case 3: // EO - 135 degrees FirstColLcu = (EB_BOOL)((tbOriginX == 0)); - LastColLcu = (EB_BOOL)(((tbOriginX >> isChroma) + lcuWidth == pictureWidth)); + LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); FirstRowLcu = (EB_BOOL)((tbOriginY == 0)); - LastRowLcu = (EB_BOOL)(((tbOriginY >> isChroma) + lcuHeight == pictureHeight)); + LastRowLcu = (EB_BOOL)(((tbOriginY >> (isChroma ? subHeightCMinus1 : 0)) + lcuHeight == pictureHeight)); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -929,9 +946,9 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( case 4: // EO - 45 degrees FirstColLcu = (EB_BOOL)((tbOriginX == 0)); - LastColLcu = (EB_BOOL)(((tbOriginX >> isChroma) + lcuWidth == pictureWidth)); + LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); FirstRowLcu = (EB_BOOL)((tbOriginY == 0)); - LastRowLcu = (EB_BOOL)(((tbOriginY >> isChroma) + lcuHeight == pictureHeight)); + LastRowLcu = (EB_BOOL)(((tbOriginY >> (isChroma ? subHeightCMinus1 : 0)) + lcuHeight == pictureHeight)); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -1053,6 +1070,9 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture16bit( recBuf16bit = ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->referencePicture16bit; else recBuf16bit = pictureControlSetPtr->reconPicture16bitPtr; + const EB_COLOR_FORMAT colorFormat = recBuf16bit->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; // Apply SAO // Y if (pictureControlSetPtr->saoFlag[0]) { @@ -1133,11 +1153,12 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture16bit( tbOriginX = lcuParams->originX; tbOriginY = lcuParams->originY; - lcuWidth = lcuParams->width >> 1; - lcuHeight = lcuParams->height >> 1; + lcuWidth = lcuParams->width >> subWidthCMinus1; + lcuHeight = lcuParams->height >> subHeightCMinus1; saoParams = &pictureControlSetPtr->lcuPtrArray[lcuIndex]->saoParams; - reconSampleChromaIndex = ((recBuf16bit->originY + tbOriginY) * recBuf16bit->strideCb + recBuf16bit->originX + tbOriginX) >> 1; + reconSampleChromaIndex = ((recBuf16bit->originX + tbOriginX) >> subWidthCMinus1) + + (((recBuf16bit->originY + tbOriginY) * recBuf16bit->strideCb) >> subHeightCMinus1); if (tbOriginX == 0) { @@ -1148,7 +1169,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture16bit( } //Save last pixel colunm of this LCU for next LCU - lcuHeightPlusOne = (sequenceControlSetPtr->chromaHeight == (tbOriginY >> 1) + lcuHeight) ? lcuHeight : lcuHeight + 1; + lcuHeightPlusOne = (sequenceControlSetPtr->chromaHeight == (tbOriginY >> subHeightCMinus1) + lcuHeight) ? lcuHeight : lcuHeight + 1; reconSampleCbPtr = (EB_U16*)(recBuf16bit->bufferCb) + reconSampleChromaIndex + lcuWidth - 1; for (lcuRow = 0; lcuRow < lcuHeightPlusOne; ++lcuRow) { @@ -1195,12 +1216,13 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture16bit( tbOriginX = lcuParams->originX; tbOriginY = lcuParams->originY; - lcuWidth = lcuParams->width >> 1; - lcuHeight = lcuParams->height >> 1; + lcuWidth = lcuParams->width >> subWidthCMinus1; + lcuHeight = lcuParams->height >> subHeightCMinus1; saoParams = &pictureControlSetPtr->lcuPtrArray[lcuIndex]->saoParams; - reconSampleChromaIndex = ((recBuf16bit->originY + tbOriginY) * recBuf16bit->strideCr + recBuf16bit->originX + tbOriginX) >> 1; + reconSampleChromaIndex = ((recBuf16bit->originX + tbOriginX) >> subWidthCMinus1) + + (((recBuf16bit->originY + tbOriginY) * recBuf16bit->strideCr) >> subHeightCMinus1); if (tbOriginX == 0) { @@ -1210,7 +1232,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture16bit( } //Save last pixel colunm of this LCU for next LCU - lcuHeightPlusOne = (sequenceControlSetPtr->chromaHeight == (tbOriginY >> 1) + lcuHeight) ? lcuHeight : lcuHeight + 1; + lcuHeightPlusOne = (sequenceControlSetPtr->chromaHeight == (tbOriginY >> subHeightCMinus1) + lcuHeight) ? lcuHeight : lcuHeight + 1; reconSampleCrPtr = (EB_U16*)(recBuf16bit->bufferCr) + reconSampleChromaIndex + lcuWidth - 1; for (lcuRow = 0; lcuRow < lcuHeightPlusOne; ++lcuRow) { @@ -1617,10 +1639,9 @@ static void ReconOutput( EB_BUFFERHEADERTYPE *outputReconPtr; EncodeContext_t *encodeContextPtr = sequenceControlSetPtr->encodeContextPtr; EB_BOOL is16bit = (sequenceControlSetPtr->staticConfig.encoderBitDepth > EB_8BIT); - // The totalNumberOfReconFrames counter has to be write/read protected as - // it is used to determine the end of the stream. If it is not protected - // the encoder might not properly terminate. - EbBlockOnMutex(encodeContextPtr->terminatingConditionsMutex); + const EB_COLOR_FORMAT colorFormat = (EB_COLOR_FORMAT)sequenceControlSetPtr->chromaFormatIdc; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; // Get Recon Buffer EbGetEmptyObject( @@ -1629,14 +1650,6 @@ static void ReconOutput( outputReconPtr = (EB_BUFFERHEADERTYPE*)outputReconWrapperPtr->objectPtr; outputReconPtr->nFlags = 0; - // START READ/WRITE PROTECTED SECTION - if (encodeContextPtr->totalNumberOfReconFrames == encodeContextPtr->terminatingPictureNumber) - outputReconPtr->nFlags = EB_BUFFERFLAG_EOS; - - encodeContextPtr->totalNumberOfReconFrames++; - - EbReleaseMutex(encodeContextPtr->terminatingConditionsMutex); - // STOP READ/WRITE PROTECTED SECTION outputReconPtr->nFilledLen = 0; @@ -1683,8 +1696,9 @@ static void ReconOutput( outputReconPtr->nFilledLen += sampleTotalCount; // U Recon Samples - sampleTotalCount = ((reconPtr->maxWidth - sequenceControlSetPtr->maxInputPadRight) * (reconPtr->maxHeight - sequenceControlSetPtr->maxInputPadBottom) >> 2) << is16bit; - reconReadPtr = reconPtr->bufferCb + ((reconPtr->originY << is16bit) >> 1) * reconPtr->strideCb + ((reconPtr->originX << is16bit) >> 1); + sampleTotalCount = ((reconPtr->maxWidth - sequenceControlSetPtr->maxInputPadRight) * (reconPtr->maxHeight - sequenceControlSetPtr->maxInputPadBottom) >> (3 - colorFormat)) << is16bit; + reconReadPtr = reconPtr->bufferCb + ((reconPtr->originX << is16bit) >> subWidthCMinus1) + + ((reconPtr->originY << is16bit) >> subHeightCMinus1) * reconPtr->strideCb; reconWritePtr = &(outputReconPtr->pBuffer[outputReconPtr->nFilledLen]); CHECK_REPORT_ERROR( @@ -1697,15 +1711,16 @@ static void ReconOutput( reconReadPtr, reconPtr->strideCb, reconWritePtr, - (reconPtr->maxWidth - sequenceControlSetPtr->maxInputPadRight) >> 1, - (reconPtr->width - sequenceControlSetPtr->padRight) >> 1, - (reconPtr->height - sequenceControlSetPtr->padBottom) >> 1, + (reconPtr->maxWidth - sequenceControlSetPtr->maxInputPadRight) >> subWidthCMinus1, + (reconPtr->width - sequenceControlSetPtr->padRight) >> subWidthCMinus1, + (reconPtr->height - sequenceControlSetPtr->padBottom) >> subHeightCMinus1, 1 << is16bit); outputReconPtr->nFilledLen += sampleTotalCount; // V Recon Samples - sampleTotalCount = ((reconPtr->maxWidth - sequenceControlSetPtr->maxInputPadRight) * (reconPtr->maxHeight - sequenceControlSetPtr->maxInputPadBottom) >> 2) << is16bit; - reconReadPtr = reconPtr->bufferCr + ((reconPtr->originY << is16bit) >> 1) * reconPtr->strideCr + ((reconPtr->originX << is16bit) >> 1); + sampleTotalCount = ((reconPtr->maxWidth - sequenceControlSetPtr->maxInputPadRight) * (reconPtr->maxHeight - sequenceControlSetPtr->maxInputPadBottom) >> (3 - colorFormat)) << is16bit; + reconReadPtr = reconPtr->bufferCr + ((reconPtr->originX << is16bit) >> subWidthCMinus1) + + ((reconPtr->originY << is16bit) >> subHeightCMinus1) * reconPtr->strideCr; reconWritePtr = &(outputReconPtr->pBuffer[outputReconPtr->nFilledLen]); CHECK_REPORT_ERROR( @@ -1719,17 +1734,29 @@ static void ReconOutput( reconReadPtr, reconPtr->strideCr, reconWritePtr, - (reconPtr->maxWidth - sequenceControlSetPtr->maxInputPadRight) >> 1, - (reconPtr->width - sequenceControlSetPtr->padRight) >> 1, - (reconPtr->height - sequenceControlSetPtr->padBottom) >> 1, + (reconPtr->maxWidth - sequenceControlSetPtr->maxInputPadRight) >> subWidthCMinus1, + (reconPtr->width - sequenceControlSetPtr->padRight) >> subWidthCMinus1, + (reconPtr->height - sequenceControlSetPtr->padBottom) >> subHeightCMinus1, 1 << is16bit); outputReconPtr->nFilledLen += sampleTotalCount; outputReconPtr->pts = pictureControlSetPtr->pictureNumber; } + // The totalNumberOfReconFrames counter has to be write/read protected as + // it is used to determine the end of the stream. If it is not protected + // the encoder might not properly terminate. + EbBlockOnMutex(encodeContextPtr->terminatingConditionsMutex); + + // START READ/WRITE PROTECTED SECTION + if (encodeContextPtr->totalNumberOfReconFrames == encodeContextPtr->terminatingPictureNumber) { + outputReconPtr->nFlags = EB_BUFFERFLAG_EOS; + } + encodeContextPtr->totalNumberOfReconFrames++; + // Post the Recon object EbPostFullObject(outputReconWrapperPtr); - + + EbReleaseMutex(encodeContextPtr->terminatingConditionsMutex); } void PadRefAndSetFlags( @@ -1742,6 +1769,9 @@ void PadRefAndSetFlags( EbPictureBufferDesc_t *refPicPtr = (EbPictureBufferDesc_t*)referenceObject->referencePicture; EbPictureBufferDesc_t *refPic16BitPtr = (EbPictureBufferDesc_t*)referenceObject->referencePicture16bit; EB_BOOL is16bit = (sequenceControlSetPtr->staticConfig.encoderBitDepth > EB_8BIT); + EB_COLOR_FORMAT colorFormat = pictureControlSetPtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; if (!is16bit) { // Y samples @@ -1757,19 +1787,19 @@ void PadRefAndSetFlags( GeneratePadding( refPicPtr->bufferCb, refPicPtr->strideCb, - refPicPtr->width >> 1, - refPicPtr->height >> 1, - refPicPtr->originX >> 1, - refPicPtr->originY >> 1); + refPicPtr->width >> subWidthCMinus1, + refPicPtr->height >> subHeightCMinus1, + refPicPtr->originX >> subWidthCMinus1, + refPicPtr->originY >> subHeightCMinus1); // Cr samples GeneratePadding( refPicPtr->bufferCr, refPicPtr->strideCr, - refPicPtr->width >> 1, - refPicPtr->height >> 1, - refPicPtr->originX >> 1, - refPicPtr->originY >> 1); + refPicPtr->width >> subWidthCMinus1, + refPicPtr->height >> subHeightCMinus1, + refPicPtr->originX >> subWidthCMinus1, + refPicPtr->originY >> subHeightCMinus1); } //We need this for MCP @@ -1787,19 +1817,19 @@ void PadRefAndSetFlags( GeneratePadding16Bit( refPic16BitPtr->bufferCb, refPic16BitPtr->strideCb << 1, - refPic16BitPtr->width, - refPic16BitPtr->height >> 1, - refPic16BitPtr->originX, - refPic16BitPtr->originY >> 1); + refPic16BitPtr->width << (1 - subWidthCMinus1), + refPic16BitPtr->height >> subHeightCMinus1, + refPic16BitPtr->originX << (1 - subWidthCMinus1), + refPic16BitPtr->originY >> subHeightCMinus1); // Cr samples GeneratePadding16Bit( refPic16BitPtr->bufferCr, refPic16BitPtr->strideCr << 1, - refPic16BitPtr->width, - refPic16BitPtr->height >> 1, - refPic16BitPtr->originX, - refPic16BitPtr->originY >> 1); + refPic16BitPtr->width << (1 - subWidthCMinus1), + refPic16BitPtr->height >> subHeightCMinus1, + refPic16BitPtr->originX << (1 - subWidthCMinus1), + refPic16BitPtr->originY >> subHeightCMinus1); } // set up TMVP flag for the reference picture @@ -1922,11 +1952,11 @@ EB_ERRORTYPE QpmDeriveWeightsMinAndMax( Input : encoder mode and tune Output : EncDec Kernel signal(s) ******************************************************/ -EB_ERRORTYPE SignalDerivationEncDecKernelSq( +static EB_ERRORTYPE SignalDerivationEncDecKernelSq( SequenceControlSet_t *sequenceControlSetPtr, PictureControlSet_t *pictureControlSetPtr, - EncDecContext_t *contextPtr) { - + EncDecContext_t *contextPtr) +{ EB_ERRORTYPE return_error = EB_ErrorNone; // Set MD Open Loop Flag @@ -2607,11 +2637,11 @@ EB_ERRORTYPE SignalDerivationEncDecKernelSq( Input : encoder mode and tune Output : EncDec Kernel signal(s) ******************************************************/ -EB_ERRORTYPE SignalDerivationEncDecKernelOq( +static EB_ERRORTYPE SignalDerivationEncDecKernelOq( SequenceControlSet_t *sequenceControlSetPtr, PictureControlSet_t *pictureControlSetPtr, - EncDecContext_t *contextPtr) { - + EncDecContext_t *contextPtr) +{ EB_ERRORTYPE return_error = EB_ErrorNone; // Set MD Open Loop Flag @@ -4106,18 +4136,21 @@ void* EncDecKernel(void *inputPtr) pictureControlSetPtr, sequenceControlSetPtr); - if (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag == EB_TRUE && pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr) - { + if (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag == EB_TRUE && + pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr) { EbPictureBufferDesc_t *inputPicturePtr = (EbPictureBufferDesc_t*)pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; + EB_COLOR_FORMAT colorFormat = inputPicturePtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; const EB_U32 SrclumaOffSet = inputPicturePtr->originX + inputPicturePtr->originY *inputPicturePtr->strideY; - const EB_U32 SrccbOffset = (inputPicturePtr->originX >> 1) + (inputPicturePtr->originY >> 1)*inputPicturePtr->strideCb; - const EB_U32 SrccrOffset = (inputPicturePtr->originX >> 1) + (inputPicturePtr->originY >> 1)*inputPicturePtr->strideCr; + const EB_U32 SrccbOffset = (inputPicturePtr->originX >> subWidthCMinus1) + (inputPicturePtr->originY >> subHeightCMinus1) * inputPicturePtr->strideCb; + const EB_U32 SrccrOffset = (inputPicturePtr->originX >> subWidthCMinus1) + (inputPicturePtr->originY >> subHeightCMinus1) * inputPicturePtr->strideCr; EbReferenceObject_t *referenceObject = (EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr; EbPictureBufferDesc_t *refDenPic = referenceObject->refDenSrcPicture; const EB_U32 ReflumaOffSet = refDenPic->originX + refDenPic->originY *refDenPic->strideY; - const EB_U32 RefcbOffset = (refDenPic->originX >> 1) + (refDenPic->originY >> 1)*refDenPic->strideCb; - const EB_U32 RefcrOffset = (refDenPic->originX >> 1) + (refDenPic->originY >> 1)*refDenPic->strideCr; + const EB_U32 RefcbOffset = (refDenPic->originX >> subWidthCMinus1) + (refDenPic->originY >> subHeightCMinus1) * refDenPic->strideCb; + const EB_U32 RefcrOffset = (refDenPic->originX >> subWidthCMinus1) + (refDenPic->originY >> subHeightCMinus1) * refDenPic->strideCr; EB_U16 verticalIdx; @@ -4128,15 +4161,15 @@ void* EncDecKernel(void *inputPtr) inputPicturePtr->width); } - for (verticalIdx = 0; verticalIdx < inputPicturePtr->height / 2; ++verticalIdx) + for (verticalIdx = 0; verticalIdx < inputPicturePtr->height >> subHeightCMinus1; ++verticalIdx) { EB_MEMCPY(refDenPic->bufferCb + RefcbOffset + verticalIdx*refDenPic->strideCb, inputPicturePtr->bufferCb + SrccbOffset + verticalIdx* inputPicturePtr->strideCb, - inputPicturePtr->width / 2); + inputPicturePtr->width >> subWidthCMinus1); EB_MEMCPY(refDenPic->bufferCr + RefcrOffset + verticalIdx*refDenPic->strideCr, inputPicturePtr->bufferCr + SrccrOffset + verticalIdx* inputPicturePtr->strideCr, - inputPicturePtr->width / 2); + inputPicturePtr->width >> subWidthCMinus1 ); } GeneratePadding( @@ -4150,18 +4183,18 @@ void* EncDecKernel(void *inputPtr) GeneratePadding( refDenPic->bufferCb, refDenPic->strideCb, - refDenPic->width >> 1, - refDenPic->height >> 1, - refDenPic->originX >> 1, - refDenPic->originY >> 1); + refDenPic->width >> subWidthCMinus1, + refDenPic->height >> subHeightCMinus1, + refDenPic->originX >> subWidthCMinus1, + refDenPic->originY >> subHeightCMinus1); GeneratePadding( refDenPic->bufferCr, refDenPic->strideCr, - refDenPic->width >> 1, - refDenPic->height >> 1, - refDenPic->originX >> 1, - refDenPic->originY >> 1); + refDenPic->width >> subWidthCMinus1, + refDenPic->height >> subHeightCMinus1, + refDenPic->originX >> subWidthCMinus1, + refDenPic->originY >> subHeightCMinus1); } if (sequenceControlSetPtr->staticConfig.reconEnabled) { diff --git a/Source/Lib/Codec/EbEncDecProcess.h b/Source/Lib/Codec/EbEncDecProcess.h index d8b35fbd2..3f08176e6 100644 --- a/Source/Lib/Codec/EbEncDecProcess.h +++ b/Source/Lib/Codec/EbEncDecProcess.h @@ -106,6 +106,7 @@ typedef struct EncDecContext_s EB_U8 tuItr; EB_BOOL is16bit; //enable 10 bit encode in CL + EB_COLOR_FORMAT colorFormat; // SAO application EB_U8 *saoUpBuffer[2]; EB_U8 *saoLeftBuffer[2]; @@ -155,7 +156,8 @@ extern EB_ERRORTYPE EncDecContextCtor( EbFifo_t *packetizationOutputFifoPtr, EbFifo_t *feedbackFifoPtr, EbFifo_t *pictureDemuxFifoPtr, - EB_BOOL is16bit); + EB_BOOL is16bit, + EB_COLOR_FORMAT colorFormat); diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index 2cdeaedc6..90a9091b1 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -12,6 +12,7 @@ **************************************/ #include #include +#include #include "EbDefinitions.h" #include "EbApi.h" @@ -61,6 +62,8 @@ #if __linux__ #include #include +#include +#include #endif /************************************** @@ -563,15 +566,16 @@ void SwitchToRealTime() int retValue = pthread_setschedparam(pthread_self(), SCHED_FIFO, &schedParam); if (retValue == EPERM) - SVT_LOG("\nSVT [WARNING]: For best speed performance, run with sudo privileges !\n\n"); + SVT_LOG("\nSVT [WARNING] Elevated privileges required to run with real-time policies! Check Linux Best Known Configuration in User Guide to run application in real-time without elevated privileges!\n\n"); #endif } -void EbSetThreadManagementParameters( +EB_ERRORTYPE EbSetThreadManagementParameters( EB_H265_ENC_CONFIGURATION *configPtr) { EB_U32 numLogicProcessors = GetNumProcessors(); + EB_ERRORTYPE return_error = EB_ErrorNone; if (configPtr->switchThreadsToRtPriority == 1) { SwitchToRealTime(); @@ -613,8 +617,11 @@ void EbSetThreadManagementParameters( #else const char* PROCESSORID = "processor"; const char* PHYSICALID = "physical id"; + int processor_id_len = strnlen_ss(PROCESSORID, 128); + int physical_id_len = strnlen_ss(PHYSICALID, 128); + if (processor_id_len < 0 || processor_id_len >= 128) return EB_ErrorInsufficientResources; + if (physical_id_len < 0 || physical_id_len >= 128) return EB_ErrorInsufficientResources; CPU_ZERO(&groupAffinity); - numGroups = 1; typedef struct logicalProcessorGroup { EB_U32 num; EB_U32 group[1024]; @@ -622,29 +629,40 @@ void EbSetThreadManagementParameters( processorGroup lpgroup[16]; memset(lpgroup, 0, 16* sizeof(processorGroup)); - FILE *fin = fopen("/proc/cpuinfo", "rt"); - if (fin) { - int processor_id = 0, socket_id = 0; - while (!feof(fin)) { + int fd = open("/proc/cpuinfo", O_RDONLY | O_NOFOLLOW, "rt"); + struct stat file_stat; + if (fd >= 0) { + if (fstat(fd, &file_stat) != -1 && S_ISREG(file_stat.st_mode) != 0) { + int processor_id = 0, socket_id = 0; char line[128]; - if (!fgets(line, sizeof(line), fin)) break; - if(strncmp(line, PROCESSORID, strnlen_ss(PROCESSORID,128)) == 0) { - char* p = line + strnlen_ss(PROCESSORID,128); - while(*p < '0' || *p > '9') p++; - processor_id = atoi(p); - } - if(strncmp(line, PHYSICALID, strnlen_ss(PHYSICALID,128)) == 0) { - char* p = line + strnlen_ss(PHYSICALID,128); - while(*p < '0' || *p > '9') p++; - socket_id = atoi(p); - if (socket_id + 1 > numGroups) - numGroups = socket_id + 1; - lpgroup[socket_id].group[lpgroup[socket_id].num++] = processor_id; + int bytes = 1; + while (bytes > 0) { + bytes = read(fd, line, 128); + if (bytes > 0) { + if (strncmp(line, PROCESSORID, processor_id_len) == 0) { + char* p = line + processor_id_len; + while (*p < '0' || *p > '9') p++; + processor_id = strtol(p, NULL, 0); + } + if (strncmp(line, PHYSICALID, physical_id_len) == 0) { + char* p = line + physical_id_len; + while (*p < '0' || *p > '9') p++; + socket_id = strtol(p, NULL, 0); + if (socket_id < 0 || socket_id > 15) { + close(fd); + return EB_ErrorInsufficientResources; + } + if (socket_id + 1 > numGroups) + numGroups = socket_id + 1; + lpgroup[socket_id].group[lpgroup[socket_id].num++] = processor_id; + } + lseek(fd, -bytes + 1, SEEK_CUR); + while (line[0] != '\n' && bytes > 0) bytes = read(fd, line, 1); + } } } - fclose(fin); + close(fd); } - if (numGroups == 1) { EB_U32 lps = configPtr->logicalProcessors == 0 ? numLogicProcessors: configPtr->logicalProcessors < numLogicProcessors ? configPtr->logicalProcessors : numLogicProcessors; @@ -683,6 +701,7 @@ void EbSetThreadManagementParameters( } } #endif + return return_error; } /********************************** @@ -746,6 +765,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; inputData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->outputBitdepth; + inputData.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaFormatIdc; inputData.lcuSize = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize; inputData.maxDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxLcuDepth; inputData.is16bit = is16bit; @@ -799,6 +819,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; inputData.bitDepth = EB_8BIT; + inputData.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaFormatIdc; inputData.lcuSize = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize; inputData.maxDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxLcuDepth; inputData.is16bit = is16bit; @@ -850,6 +871,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) referencePictureBufferDescInitData.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth; referencePictureBufferDescInitData.maxHeight = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight; referencePictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; + referencePictureBufferDescInitData.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaFormatIdc; referencePictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_FULL_MASK; referencePictureBufferDescInitData.leftPadding = MAX_LCU_SIZE + MCPXPaddingOffset; referencePictureBufferDescInitData.rightPadding = MAX_LCU_SIZE + MCPXPaddingOffset; @@ -884,6 +906,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) referencePictureBufferDescInitData.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth; referencePictureBufferDescInitData.maxHeight = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight; referencePictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; + referencePictureBufferDescInitData.colorFormat = EB_YUV420; referencePictureBufferDescInitData.bufferEnableMask = 0; referencePictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; referencePictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; @@ -894,6 +917,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) quarterDecimPictureBufferDescInitData.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth >> 1; quarterDecimPictureBufferDescInitData.maxHeight = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight >> 1; quarterDecimPictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; + quarterDecimPictureBufferDescInitData.colorFormat = EB_YUV420; quarterDecimPictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_LUMA_MASK; quarterDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; quarterDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; @@ -904,6 +928,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) sixteenthDecimPictureBufferDescInitData.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth >> 2; sixteenthDecimPictureBufferDescInitData.maxHeight = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight >> 2; sixteenthDecimPictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; + sixteenthDecimPictureBufferDescInitData.colorFormat = EB_YUV420; sixteenthDecimPictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_LUMA_MASK; sixteenthDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; sixteenthDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; @@ -1263,6 +1288,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) pictureBufferDescConf.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaWidth; pictureBufferDescConf.maxHeight = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaHeight; pictureBufferDescConf.bitDepth = EB_8BIT; + pictureBufferDescConf.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->chromaFormatIdc; pictureBufferDescConf.bufferEnableMask = PICTURE_BUFFER_DESC_Y_FLAG; pictureBufferDescConf.leftPadding = 0; pictureBufferDescConf.rightPadding = 0; @@ -1394,7 +1420,8 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) encHandlePtr->encDecResultsProducerFifoPtrArray[processIndex], encHandlePtr->encDecTasksProducerFifoPtrArray[EncDecPortLookup(ENCDEC_INPUT_PORT_ENCDEC, processIndex)], encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[1 + processIndex], // Add port lookup logic here JMJ - is16bit); + is16bit, + (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->chromaFormatIdc); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } @@ -1428,7 +1455,11 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) * Thread Handles ************************************/ EB_H265_ENC_CONFIGURATION *configPtr = &encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->staticConfig; - EbSetThreadManagementParameters(configPtr); + return_error = EbSetThreadManagementParameters(configPtr); + + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } // Resource Coordination EB_CREATETHREAD(EB_HANDLE, encHandlePtr->resourceCoordinationThreadHandle, sizeof(EB_HANDLE), EB_THREAD, ResourceCoordinationKernel, encHandlePtr->resourceCoordinationContextPtr); @@ -1961,7 +1992,7 @@ void SetParamBasedOnInput( sequenceControlSetPtr->maxInputPadRight = MIN_CU_SIZE - (sequenceControlSetPtr->maxInputLumaWidth % MIN_CU_SIZE); sequenceControlSetPtr->maxInputLumaWidth = sequenceControlSetPtr->maxInputLumaWidth + sequenceControlSetPtr->maxInputPadRight; - sequenceControlSetPtr->maxInputChromaWidth = sequenceControlSetPtr->maxInputLumaWidth >> 1; + //sequenceControlSetPtr->maxInputChromaWidth = sequenceControlSetPtr->maxInputLumaWidth >> 1; //TODO: change here } else { @@ -1971,7 +2002,7 @@ void SetParamBasedOnInput( sequenceControlSetPtr->maxInputPadBottom = MIN_CU_SIZE - (sequenceControlSetPtr->maxInputLumaHeight % MIN_CU_SIZE); sequenceControlSetPtr->maxInputLumaHeight = sequenceControlSetPtr->maxInputLumaHeight + sequenceControlSetPtr->maxInputPadBottom; - sequenceControlSetPtr->maxInputChromaHeight = sequenceControlSetPtr->maxInputLumaHeight >> 1; + //sequenceControlSetPtr->maxInputChromaHeight = sequenceControlSetPtr->maxInputLumaHeight >> 1; //ditto } else { sequenceControlSetPtr->maxInputPadBottom = 0; @@ -1996,7 +2027,8 @@ void SetParamBasedOnInput( void CopyApiFromApp( SequenceControlSet_t *sequenceControlSetPtr, EB_H265_ENC_CONFIGURATION* pComponentParameterStructure -) { +) +{ sequenceControlSetPtr->staticConfig.sourceWidth = (EB_U16)((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->sourceWidth; sequenceControlSetPtr->staticConfig.sourceHeight = (EB_U16)((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->sourceHeight; @@ -2082,6 +2114,12 @@ void CopyApiFromApp( // Misc sequenceControlSetPtr->staticConfig.encoderBitDepth = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->encoderBitDepth; + sequenceControlSetPtr->staticConfig.encoderColorFormat = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->encoderColorFormat; + if (sequenceControlSetPtr->staticConfig.encoderColorFormat == EB_YUV400) { + SVT_LOG("SVT [Warning]: Color format EB_YUV400 not supported, set to EB_YUV420\n"); + sequenceControlSetPtr->staticConfig.encoderColorFormat = EB_YUV420; + } + sequenceControlSetPtr->chromaFormatIdc = (EB_U32)(sequenceControlSetPtr->staticConfig.encoderColorFormat); sequenceControlSetPtr->staticConfig.compressedTenBitFormat = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->compressedTenBitFormat; sequenceControlSetPtr->staticConfig.videoUsabilityInfo = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->videoUsabilityInfo; sequenceControlSetPtr->staticConfig.highDynamicRangeInput = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->highDynamicRangeInput; @@ -2100,7 +2138,8 @@ void CopyApiFromApp( sequenceControlSetPtr->staticConfig.injectorFrameRate = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->injectorFrameRate; sequenceControlSetPtr->staticConfig.speedControlFlag = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->speedControlFlag; - sequenceControlSetPtr->staticConfig.latencyMode = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->latencyMode; + //sequenceControlSetPtr->staticConfig.latencyMode = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->latencyMode; + sequenceControlSetPtr->staticConfig.latencyMode = 0; sequenceControlSetPtr->staticConfig.asmType = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->asmType; sequenceControlSetPtr->staticConfig.channelId = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->channelId; sequenceControlSetPtr->staticConfig.activeChannelCount = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->activeChannelCount; @@ -2111,6 +2150,40 @@ void CopyApiFromApp( sequenceControlSetPtr->staticConfig.reconEnabled = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->reconEnabled; sequenceControlSetPtr->staticConfig.hrdFlag = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->hrdFlag; + sequenceControlSetPtr->staticConfig.maxCLL = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxCLL; + sequenceControlSetPtr->staticConfig.maxFALL = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxFALL; + + sequenceControlSetPtr->staticConfig.useMasteringDisplayColorVolume = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->useMasteringDisplayColorVolume; + sequenceControlSetPtr->staticConfig.useNaluFile = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->useNaluFile; + sequenceControlSetPtr->staticConfig.displayPrimaryX[0] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryX[0]; + sequenceControlSetPtr->staticConfig.displayPrimaryX[1] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryX[1]; + sequenceControlSetPtr->staticConfig.displayPrimaryX[2] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryX[2]; + sequenceControlSetPtr->staticConfig.displayPrimaryY[0] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryY[0]; + sequenceControlSetPtr->staticConfig.displayPrimaryY[1] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryY[1]; + sequenceControlSetPtr->staticConfig.displayPrimaryY[2] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryY[2]; + sequenceControlSetPtr->staticConfig.whitePointX = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->whitePointX; + sequenceControlSetPtr->staticConfig.whitePointY = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->whitePointY; + sequenceControlSetPtr->staticConfig.maxDisplayMasteringLuminance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxDisplayMasteringLuminance; + sequenceControlSetPtr->staticConfig.minDisplayMasteringLuminance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->minDisplayMasteringLuminance; + sequenceControlSetPtr->staticConfig.dolbyVisionProfile = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->dolbyVisionProfile; + + // Copying to masteringDisplayColorVolume structure + sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryX[0] = sequenceControlSetPtr->staticConfig.displayPrimaryX[0]; + sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryX[1] = sequenceControlSetPtr->staticConfig.displayPrimaryX[1]; + sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryX[2] = sequenceControlSetPtr->staticConfig.displayPrimaryX[2]; + sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryY[0] = sequenceControlSetPtr->staticConfig.displayPrimaryY[0]; + sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryY[1] = sequenceControlSetPtr->staticConfig.displayPrimaryY[1]; + sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryY[2] = sequenceControlSetPtr->staticConfig.displayPrimaryY[2]; + sequenceControlSetPtr->masteringDisplayColorVolume.whitePointX = sequenceControlSetPtr->staticConfig.whitePointX; + sequenceControlSetPtr->masteringDisplayColorVolume.whitePointY = sequenceControlSetPtr->staticConfig.whitePointY; + sequenceControlSetPtr->masteringDisplayColorVolume.maxDisplayMasteringLuminance = sequenceControlSetPtr->staticConfig.maxDisplayMasteringLuminance; + sequenceControlSetPtr->masteringDisplayColorVolume.minDisplayMasteringLuminance = sequenceControlSetPtr->staticConfig.minDisplayMasteringLuminance; + + // if dolby Profile is set HDR should be set to 1 + if (sequenceControlSetPtr->staticConfig.dolbyVisionProfile == 81) { + sequenceControlSetPtr->staticConfig.highDynamicRangeInput = 1; + } + // if HDR is set videoUsabilityInfo should be set to 1 if (sequenceControlSetPtr->staticConfig.highDynamicRangeInput == 1) { sequenceControlSetPtr->staticConfig.videoUsabilityInfo = 1; @@ -2118,7 +2191,8 @@ void CopyApiFromApp( // Extract frame rate from Numerator and Denominator if not 0 if (sequenceControlSetPtr->staticConfig.frameRateNumerator != 0 && sequenceControlSetPtr->staticConfig.frameRateDenominator != 0) { - sequenceControlSetPtr->frameRate = sequenceControlSetPtr->staticConfig.frameRate = ((sequenceControlSetPtr->staticConfig.frameRateNumerator << 16) / (sequenceControlSetPtr->staticConfig.frameRateDenominator )); + sequenceControlSetPtr->frameRate = sequenceControlSetPtr->staticConfig.frameRate = + (EB_U32)((double)sequenceControlSetPtr->staticConfig.frameRateNumerator/(double)sequenceControlSetPtr->staticConfig.frameRateDenominator)<<16; } // Get Default Intra Period if not specified @@ -2335,7 +2409,7 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - EB_U32 inputSize = sequenceControlSetPtr->maxInputLumaWidth * sequenceControlSetPtr->maxInputLumaHeight; + EB_U32 inputSize = (EB_U32)sequenceControlSetPtr->maxInputLumaWidth * (EB_U32)sequenceControlSetPtr->maxInputLumaHeight; EB_U8 inputResolution = (inputSize < INPUT_SIZE_1080i_TH) ? INPUT_SIZE_576p_RANGE_OR_LOWER : (inputSize < INPUT_SIZE_1080p_TH) ? INPUT_SIZE_1080i_RANGE : @@ -2467,7 +2541,7 @@ static EB_ERRORTYPE VerifySettings(\ if (levelIdx < 13) { // Check if the current input video is conformant with the Level constraint - if(config->level != 0 && ((EB_U64)(sequenceControlSetPtr->maxInputLumaWidth * sequenceControlSetPtr->maxInputLumaHeight) > maxLumaPictureSize[levelIdx])){ + if(config->level != 0 && (((EB_U64)sequenceControlSetPtr->maxInputLumaWidth * (EB_U64)sequenceControlSetPtr->maxInputLumaHeight) > maxLumaPictureSize[levelIdx])){ SVT_LOG("SVT [Error]: Instance %u: The input luma picture size exceeds the maximum luma picture size allowed for level %s\n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } @@ -2496,8 +2570,8 @@ static EB_ERRORTYPE VerifySettings(\ } } - if(config->profile > 3){ - SVT_LOG("SVT [Error]: Instance %u: The maximum allowed Profile number is 3 \n",channelNumber+1); + if(config->profile > 4){ + SVT_LOG("SVT [Error]: Instance %u: The maximum allowed Profile number is 4 or MAINEXT \n",channelNumber+1); return_error = EB_ErrorBadParameter; } @@ -2511,6 +2585,12 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } + if(config->encoderColorFormat >= EB_YUV422 && config->profile != 4) + { + SVT_LOG("SVT [Error]: Instance %u: The input profile is not correct, should be 4 or MainREXT for YUV422 or YUV444 cases\n",channelNumber+1); + return_error = EB_ErrorBadParameter; + } + // Check if the current input video is conformant with the Level constraint if(config->frameRate > (240<<16)){ SVT_LOG("SVT [Error]: Instance %u: The maximum allowed frame rate is 240 fps\n",channelNumber+1); @@ -2617,6 +2697,41 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } + if (config->useMasteringDisplayColorVolume > 1) { + SVT_LOG("SVT [Error]: Instance %u : Invalid useMasterDisplay. useMasterDisplay must be [0 - 1]\n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } + + if (config->useNaluFile > 1) { + SVT_LOG("SVT [Error]: Instance %u : Invalid useNaluFile. useNaluFile must be [0 - 1]\n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } + + if ((config->maxCLL && !config->highDynamicRangeInput) || (config->maxFALL && !config->highDynamicRangeInput)) { + SVT_LOG("Error Instance %u: maxCLL or maxFALL should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); + return_error = EB_ErrorBadParameter; + } + + if (config->useMasteringDisplayColorVolume && !config->highDynamicRangeInput) { + SVT_LOG("Error Instance %u: MasterDisplay should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); + return_error = EB_ErrorBadParameter; + } + + if (config->dolbyVisionProfile != 0 && config->dolbyVisionProfile != 81) { + SVT_LOG("Error Instance %u: Only Dolby Vision Profile 8.1 is supported \n", channelNumber); + return_error = EB_ErrorBadParameter; + } + + if (config->dolbyVisionProfile == 81 && config->encoderBitDepth != 10) { + SVT_LOG("Error Instance %u: Dolby Vision Profile 8.1 work only with main10 input \n", channelNumber); + return_error = EB_ErrorBadParameter; + } + + if (config->dolbyVisionProfile == 81 && !config->useMasteringDisplayColorVolume) { + SVT_LOG("Error Instance %u: Dolby Vision Profile 8.1 requires mastering display color volume information \n", channelNumber); + return_error = EB_ErrorBadParameter; + } + if (config->enableTemporalId > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid TemporalId. TemporalId must be [0 - 1]\n",channelNumber+1); return_error = EB_ErrorBadParameter; @@ -2650,8 +2765,8 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if (config->latencyMode > 1) { - SVT_LOG("SVT [Error]: Instance %u: Invalid Latency Mode flag [0 - 1]\n", channelNumber + 1); + if (config->latencyMode > 0) { + SVT_LOG("SVT [Error]: Instance %u: Latency Mode flag deprecated\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } @@ -2771,6 +2886,25 @@ EB_ERRORTYPE EbH265EncInitParameter( configPtr->channelId = 0; configPtr->activeChannelCount = 1; + //SEI + configPtr->maxCLL = 0; + configPtr->maxFALL = 0; + configPtr->dolbyVisionProfile = 0; + configPtr->useMasteringDisplayColorVolume = EB_FALSE; + configPtr->useNaluFile = EB_FALSE; + + // Master Display + configPtr->displayPrimaryX[0] = 0; + configPtr->displayPrimaryX[1] = 0; + configPtr->displayPrimaryX[2] = 0; + configPtr->displayPrimaryY[0] = 0; + configPtr->displayPrimaryY[1] = 0; + configPtr->displayPrimaryY[2] = 0; + configPtr->whitePointX = 0; + configPtr->whitePointY = 0; + configPtr->maxDisplayMasteringLuminance = 0; + configPtr->minDisplayMasteringLuminance = 0; + // Debug info configPtr->reconEnabled = 0; @@ -2780,10 +2914,12 @@ static void PrintLibParams( EB_H265_ENC_CONFIGURATION* config) { SVT_LOG("------------------------------------------- "); - if (config->profile == 0) + if (config->profile == 1) SVT_LOG("\nSVT [config]: Main Profile\t"); - else + else if (config->profile == 2) SVT_LOG("\nSVT [config]: Main10 Profile\t"); + else + SVT_LOG("\nSVT [config]: MainEXT Profile\t"); if (config->tier != 0 && config->level !=0) SVT_LOG("Tier %d\tLevel %.1f\t", config->tier, (float)(config->level / 10)); @@ -2801,8 +2937,8 @@ static void PrintLibParams( } - SVT_LOG("\nSVT [config]: EncoderMode / LatencyMode / Tune\t\t\t\t\t: %d / %d / %d ", config->encMode, config->latencyMode, config->tune); - SVT_LOG("\nSVT [config]: EncoderBitDepth / CompressedTenBitFormat\t\t\t\t: %d / %d ", config->encoderBitDepth, config->compressedTenBitFormat); + SVT_LOG("\nSVT [config]: EncoderMode / Tune\t\t\t\t\t: %d / %d ", config->encMode, config->tune); + SVT_LOG("\nSVT [config]: EncoderBitDepth / CompressedTenBitFormat / EncoderColorFormat \t: %d / %d / %d", config->encoderBitDepth, config->compressedTenBitFormat, config->encoderColorFormat); SVT_LOG("\nSVT [config]: SourceWidth / SourceHeight\t\t\t\t\t: %d / %d ", config->sourceWidth, config->sourceHeight); if (config->frameRateDenominator != 0 && config->frameRateNumerator != 0) SVT_LOG("\nSVT [config]: Fps_Numerator / Fps_Denominator / Gop Size / IntraRefreshType \t: %d / %d / %d / %d", config->frameRateNumerator > (1<<16) ? config->frameRateNumerator >> 16: config->frameRateNumerator, @@ -2835,9 +2971,15 @@ EB_API EB_ERRORTYPE EbH265EncSetParameter( EB_COMPONENTTYPE *h265EncComponent, EB_H265_ENC_CONFIGURATION *pComponentParameterStructure) { - EB_ERRORTYPE return_error = EB_ErrorNone; - EbEncHandle_t *pEncCompData = (EbEncHandle_t*) h265EncComponent->pComponentPrivate; + EB_ERRORTYPE return_error = EB_ErrorNone; EB_U32 instanceIndex = 0; + EbEncHandle_t *pEncCompData; + + if (h265EncComponent == (EB_COMPONENTTYPE*)EB_NULL) { + return EB_ErrorBadParameter; + } + + pEncCompData = (EbEncHandle_t*)h265EncComponent->pComponentPrivate; // Acquire Config Mutex EbBlockOnMutex(pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->configMutex); @@ -2977,7 +3119,8 @@ EB_API EB_ERRORTYPE EbH265EncStreamHeader( outputStreamBuffer->pBuffer, (EB_U32*) &(outputStreamBuffer->nFilledLen), (EB_U32*) &(outputStreamBuffer->nAllocLen), - encodeContextPtr); + encodeContextPtr, + NAL_UNIT_INVALID); *outputStreamPtr = outputStreamBuffer; @@ -3032,13 +3175,150 @@ EB_API EB_ERRORTYPE EbH265EncEosNal( outputStreamBuffer->pBuffer, (EB_U32*) &(outputStreamBuffer->nFilledLen), (EB_U32*) &(outputStreamBuffer->nAllocLen), - encodeContextPtr); + encodeContextPtr, + NAL_UNIT_INVALID); *outputStreamPtr = outputStreamBuffer; return return_error; } +/* charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" */ +static EB_ERRORTYPE BaseDecodeFunction(EB_U8* encodedString, EB_U32 base64EncodeLength, EB_U8* decodedString ) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + EB_U32 i, j, k = 0; + EB_U32 bitstream = 0; + EB_U32 countBits = 0; + if (encodedString == NULL || decodedString == NULL) { + return_error = EB_ErrorBadParameter; + } + if (return_error == EB_ErrorNone) { + // selects 4 characters from encodedString at a time + for (i = 0; i < base64EncodeLength; i += 4) { + bitstream = 0, countBits = 0; + for (j = 0; j < 4; j++) { + // make space for 6 bits + if (encodedString[i + j] != '=') { + bitstream = bitstream << 6; + countBits += 6; + } + // Finding the position of each encoded character in charSet and storing in bitstream + if (encodedString[i + j] >= 'A' && encodedString[i + j] <= 'Z') + bitstream = bitstream | (encodedString[i + j] - 'A'); + + else if (encodedString[i + j] >= 'a' && encodedString[i + j] <= 'z') + bitstream = bitstream | (encodedString[i + j] - 'a' + 26); + + else if (encodedString[i + j] >= '0' && encodedString[i + j] <= '9') + bitstream = bitstream | (encodedString[i + j] - '0' + 52); + + // '+' occurs in 62nd position in charSet + else if (encodedString[i + j] == '+') + bitstream = bitstream | 62; + + // '/' occurs in 63rd position in charSet + else if (encodedString[i + j] == '/') + bitstream = bitstream | 63; + + else { + bitstream = bitstream >> 2; + countBits -= 2; + } + } + + while (countBits != 0) { + countBits -= 8; + if (k >= sizeof(decodedString)) { + return EB_ErrorBadParameter; + } + decodedString[k++] = (bitstream >> countBits) & 255; + } + } + } + return return_error; +} + +static EB_ERRORTYPE ParseSeiMetaData( + EB_BUFFERHEADERTYPE *dst, + EB_BUFFERHEADERTYPE *src) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + + EbPictureBufferDesc_t *headerPtr = (EbPictureBufferDesc_t*)dst->pBuffer; + EB_U8 *base64Encode; + EB_U32 base64EncodeLength; + EB_U8 *base64Decode; + + if (src->naluFound == EB_FALSE) { + return EB_ErrorBadParameter; + } + + base64Encode = src->naluBase64Encode; + base64EncodeLength = (uint32_t)strlen((char*)base64Encode); + EB_MALLOC(EB_U8*, base64Decode, (base64EncodeLength / 4) * 3, EB_N_PTR); + return_error = BaseDecodeFunction(base64Encode, base64EncodeLength, base64Decode); + + if (return_error != EB_ErrorNone) { + src->naluFound = EB_FALSE; + SVT_LOG("\nSVT [WARNING]: SEI encoded message cannot be decoded \n "); + return EB_ErrorBadParameter; + } + + if (src->naluNalType == NAL_UNIT_PREFIX_SEI && src->naluPrefix == 0) { + EB_U64 currentPOC = src->pts; + if (currentPOC == src->naluPOC) { + headerPtr->userSeiMsg.payloadSize = (base64EncodeLength / 4) * 3; + EB_MALLOC(EB_U8*, headerPtr->userSeiMsg.payload, headerPtr->userSeiMsg.payloadSize, EB_N_PTR); + if (src->naluPayloadType == 4) + headerPtr->userSeiMsg.payloadType = USER_DATA_REGISTERED_ITU_T_T35; + else if (src->naluPayloadType == 5) + headerPtr->userSeiMsg.payloadType = USER_DATA_UNREGISTERED; + else { + src->naluFound = EB_FALSE; + SVT_LOG("\nSVT [WARNING]: Unsupported SEI payload Type for frame %u\n ", src->naluPOC); + return EB_ErrorBadParameter; + } + EB_MEMCPY(headerPtr->userSeiMsg.payload, base64Decode, headerPtr->userSeiMsg.payloadSize); + } + else { + src->naluFound = EB_FALSE; + SVT_LOG("\nSVT [WARNING]: User SEI frame number %u doesn't match input frame number %" PRId64 "\n ", src->naluPOC, currentPOC); + return EB_ErrorBadParameter; + } + } + else { + src->naluFound = EB_FALSE; + SVT_LOG("\nSVT [WARNING]: SEI message for frame %u is not inserted. Will support only PREFIX SEI message \n ", src->naluPOC); + return EB_ErrorBadParameter; + } + return return_error; +} + +static EB_ERRORTYPE CopyUserSei( + SequenceControlSet_t* sequenceControlSetPtr, + EB_BUFFERHEADERTYPE* dst, + EB_BUFFERHEADERTYPE* src) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + EB_H265_ENC_CONFIGURATION *config = &sequenceControlSetPtr->staticConfig; + EbPictureBufferDesc_t *dstPicturePtr = (EbPictureBufferDesc_t*)dst->pBuffer; + + if (config->useNaluFile == EB_TRUE && src->naluFound == EB_FALSE) { + config->useNaluFile = EB_FALSE; + } + + // Copy User SEI metadata from input + if (config->useNaluFile) { + return_error = ParseSeiMetaData(dst, src); + } + else { + dstPicturePtr->userSeiMsg.payloadSize = 0; + dstPicturePtr->userSeiMsg.payload = NULL; + } + return return_error; +} + /*********************************************** **** Copy the input buffer from the **** sample application to the library buffers @@ -3058,17 +3338,21 @@ static EB_ERRORTYPE CopyFrameBuffer( EB_U16 inputRowIndex; EB_BOOL is16BitInput = (EB_BOOL)(config->encoderBitDepth > EB_8BIT); + EB_U16 colorFormat = (EB_U16)(config->encoderColorFormat); + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; // Need to include for Interlacing on the fly with pictureScanType = 1 if (!is16BitInput) { - EB_U32 lumaBufferOffset = (inputPicturePtr->strideY*sequenceControlSetPtr->topPadding + sequenceControlSetPtr->leftPadding) << is16BitInput; - EB_U32 chromaBufferOffset = (inputPicturePtr->strideCr*(sequenceControlSetPtr->topPadding >> 1) + (sequenceControlSetPtr->leftPadding >> 1)) << is16BitInput; - EB_U16 lumaStride = inputPicturePtr->strideY << is16BitInput; - EB_U16 chromaStride = inputPicturePtr->strideCb << is16BitInput; - EB_U16 lumaWidth = (EB_U16)(inputPicturePtr->width - sequenceControlSetPtr->maxInputPadRight) << is16BitInput; - EB_U16 chromaWidth = (lumaWidth >> 1) << is16BitInput; + EB_U32 lumaBufferOffset = inputPicturePtr->strideY*sequenceControlSetPtr->topPadding + sequenceControlSetPtr->leftPadding; + EB_U32 chromaBufferOffset = inputPicturePtr->strideCr*(sequenceControlSetPtr->topPadding >> subHeightCMinus1) + (sequenceControlSetPtr->leftPadding >> subWidthCMinus1); + EB_U16 lumaStride = inputPicturePtr->strideY; + EB_U16 chromaStride = inputPicturePtr->strideCb; + EB_U16 lumaWidth = (EB_U16)(inputPicturePtr->width - sequenceControlSetPtr->maxInputPadRight); + EB_U16 chromaWidth = lumaWidth >> subWidthCMinus1; EB_U16 lumaHeight = (EB_U16)(inputPicturePtr->height - sequenceControlSetPtr->maxInputPadBottom); + EB_U16 chromaHeight = lumaHeight >> subHeightCMinus1; EB_U16 sourceLumaStride = (EB_U16)(inputPtr->yStride); EB_U16 sourceCrStride = (EB_U16)(inputPtr->crStride); @@ -3084,14 +3368,14 @@ static EB_ERRORTYPE CopyFrameBuffer( } // U - for (inputRowIndex = 0; inputRowIndex < lumaHeight >> 1; inputRowIndex++) { + for (inputRowIndex = 0; inputRowIndex < chromaHeight; inputRowIndex++) { EB_MEMCPY((inputPicturePtr->bufferCb + chromaBufferOffset + chromaStride * inputRowIndex), (inputPtr->cb + (sourceCbStride*inputRowIndex)), chromaWidth); } // V - for (inputRowIndex = 0; inputRowIndex < lumaHeight >> 1; inputRowIndex++) { + for (inputRowIndex = 0; inputRowIndex < chromaHeight; inputRowIndex++) { EB_MEMCPY((inputPicturePtr->bufferCr + chromaBufferOffset + chromaStride * inputRowIndex), (inputPtr->cr + (sourceCrStride*inputRowIndex)), chromaWidth); @@ -3102,12 +3386,13 @@ static EB_ERRORTYPE CopyFrameBuffer( { { EB_U32 lumaBufferOffset = (inputPicturePtr->strideY*sequenceControlSetPtr->topPadding + sequenceControlSetPtr->leftPadding); - EB_U32 chromaBufferOffset = (inputPicturePtr->strideCr*(sequenceControlSetPtr->topPadding >> 1) + (sequenceControlSetPtr->leftPadding >> 1)); + EB_U32 chromaBufferOffset = (inputPicturePtr->strideCr*(sequenceControlSetPtr->topPadding >> subHeightCMinus1) + (sequenceControlSetPtr->leftPadding >> subWidthCMinus1)); EB_U16 lumaStride = inputPicturePtr->strideY; EB_U16 chromaStride = inputPicturePtr->strideCb; EB_U16 lumaWidth = (EB_U16)(inputPicturePtr->width - sequenceControlSetPtr->maxInputPadRight); - EB_U16 chromaWidth = (lumaWidth >> 1); + EB_U16 chromaWidth = (lumaWidth >> subWidthCMinus1); EB_U16 lumaHeight = (EB_U16)(inputPicturePtr->height - sequenceControlSetPtr->maxInputPadBottom); + EB_U16 chromaHeight = lumaHeight >> subHeightCMinus1; EB_U16 sourceLumaStride = (EB_U16)(inputPtr->yStride); EB_U16 sourceCrStride = (EB_U16)(inputPtr->crStride); @@ -3123,7 +3408,7 @@ static EB_ERRORTYPE CopyFrameBuffer( } // U 8bit - for (inputRowIndex = 0; inputRowIndex < lumaHeight >> 1; inputRowIndex++) { + for (inputRowIndex = 0; inputRowIndex < chromaHeight; inputRowIndex++) { EB_MEMCPY((inputPicturePtr->bufferCb + chromaBufferOffset + chromaStride * inputRowIndex), (inputPtr->cb + (sourceCbStride*inputRowIndex)), @@ -3132,7 +3417,7 @@ static EB_ERRORTYPE CopyFrameBuffer( } // V 8bit - for (inputRowIndex = 0; inputRowIndex < lumaHeight >> 1; inputRowIndex++) { + for (inputRowIndex = 0; inputRowIndex < chromaHeight; inputRowIndex++) { EB_MEMCPY((inputPicturePtr->bufferCr + chromaBufferOffset + chromaStride * inputRowIndex), (inputPtr->cr + (sourceCrStride*inputRowIndex)), @@ -3152,10 +3437,10 @@ static EB_ERRORTYPE CopyFrameBuffer( for (inputRowIndex = 0; inputRowIndex < lumaHeight; inputRowIndex++) { EB_MEMCPY(inputPicturePtr->bufferBitIncY + luma2BitWidth * inputRowIndex, inputPtr->lumaExt + sourceLuma2BitStride * inputRowIndex, luma2BitWidth); } - for (inputRowIndex = 0; inputRowIndex < lumaHeight >> 1; inputRowIndex++) { + for (inputRowIndex = 0; inputRowIndex < chromaHeight; inputRowIndex++) { EB_MEMCPY(inputPicturePtr->bufferBitIncCb + (luma2BitWidth >> 1)*inputRowIndex, inputPtr->cbExt + sourceChroma2BitStride * inputRowIndex, luma2BitWidth >> 1); } - for (inputRowIndex = 0; inputRowIndex < lumaHeight >> 1; inputRowIndex++) { + for (inputRowIndex = 0; inputRowIndex < chromaHeight; inputRowIndex++) { EB_MEMCPY(inputPicturePtr->bufferBitIncCr + (luma2BitWidth >> 1)*inputRowIndex, inputPtr->crExt + sourceChroma2BitStride * inputRowIndex, luma2BitWidth >> 1); } } @@ -3167,10 +3452,11 @@ static EB_ERRORTYPE CopyFrameBuffer( EB_U32 lumaOffset = 0, chromaOffset = 0; EB_U32 lumaBufferOffset = (inputPicturePtr->strideY*sequenceControlSetPtr->topPadding + sequenceControlSetPtr->leftPadding); - EB_U32 chromaBufferOffset = (inputPicturePtr->strideCr*(sequenceControlSetPtr->topPadding >> 1) + (sequenceControlSetPtr->leftPadding >> 1)); + EB_U32 chromaBufferOffset = (inputPicturePtr->strideCr*(sequenceControlSetPtr->topPadding >> subHeightCMinus1) + (sequenceControlSetPtr->leftPadding >> subWidthCMinus1)); EB_U16 lumaWidth = (EB_U16)(inputPicturePtr->width - sequenceControlSetPtr->maxInputPadRight); - EB_U16 chromaWidth = (lumaWidth >> 1); + EB_U16 chromaWidth = (lumaWidth >> subWidthCMinus1); EB_U16 lumaHeight = (EB_U16)(inputPicturePtr->height - sequenceControlSetPtr->maxInputPadBottom); + EB_U16 chromaHeight = lumaHeight >> subHeightCMinus1; EB_U16 sourceLumaStride = (EB_U16)(inputPtr->yStride); EB_U16 sourceCrStride = (EB_U16)(inputPtr->crStride); @@ -3194,7 +3480,7 @@ static EB_ERRORTYPE CopyFrameBuffer( inputPicturePtr->bufferBitIncCb + chromaBufferOffset, inputPicturePtr->strideBitIncCb, chromaWidth, - (lumaHeight >> 1)); + chromaHeight); UnPack2D( (EB_U16*)(inputPtr->cr + chromaOffset), @@ -3204,8 +3490,20 @@ static EB_ERRORTYPE CopyFrameBuffer( inputPicturePtr->bufferBitIncCr + chromaBufferOffset, inputPicturePtr->strideBitIncCr, chromaWidth, - (lumaHeight >> 1)); + chromaHeight); } + + // Copy Dolby Vision RPU metadata from input + if (inputPtr->dolbyVisionRpu.payloadSize) { + inputPicturePtr->dolbyVisionRpu.payloadSize = inputPtr->dolbyVisionRpu.payloadSize; + EB_MALLOC(EB_U8*, inputPicturePtr->dolbyVisionRpu.payload, inputPtr->dolbyVisionRpu.payloadSize, EB_N_PTR); + EB_MEMCPY(inputPicturePtr->dolbyVisionRpu.payload, inputPtr->dolbyVisionRpu.payload, inputPtr->dolbyVisionRpu.payloadSize); + } + else { + inputPicturePtr->dolbyVisionRpu.payloadSize = 0; + inputPicturePtr->dolbyVisionRpu.payload = NULL; + } + return return_error; } static void CopyInputBuffer( @@ -3227,6 +3525,11 @@ static void CopyInputBuffer( // Copy the picture buffer if(src->pBuffer != NULL) CopyFrameBuffer(sequenceControlSet, dst->pBuffer, src->pBuffer); + + // Copy User SEI + if (src->pBuffer != NULL) + CopyUserSei(sequenceControlSet, dst, src); + } /********************************** @@ -3471,6 +3774,7 @@ EB_ERRORTYPE AllocateFrameBuffer( inputPictureBufferDescInitData.maxWidth = (EB_U16)sequenceControlSetPtr->maxInputLumaWidth; inputPictureBufferDescInitData.maxHeight = (EB_U16)sequenceControlSetPtr->maxInputLumaHeight; inputPictureBufferDescInitData.bitDepth = (EB_BITDEPTH)config->encoderBitDepth; + inputPictureBufferDescInitData.colorFormat = (EB_COLOR_FORMAT)config->encoderColorFormat; if (config->compressedTenBitFormat == 1) { inputPictureBufferDescInitData.bufferEnableMask = 0; @@ -3571,13 +3875,12 @@ EB_ERRORTYPE EbOutputReconBufferHeaderCtor( { EB_BUFFERHEADERTYPE *reconBuffer; SequenceControlSet_t *sequenceControlSetPtr = (SequenceControlSet_t*)objectInitDataPtr; - const EB_U32 lumaSize = - sequenceControlSetPtr->lumaWidth * - sequenceControlSetPtr->lumaHeight; + const EB_COLOR_FORMAT colorFormat = (EB_COLOR_FORMAT)sequenceControlSetPtr->chromaFormatIdc; + const EB_U32 lumaSize = sequenceControlSetPtr->lumaWidth * sequenceControlSetPtr->lumaHeight; // both u and v - const EB_U32 chromaSize = lumaSize >> 1; + const EB_U32 chromaSize = lumaSize >> (3 - colorFormat); const EB_U32 tenBit = (sequenceControlSetPtr->staticConfig.encoderBitDepth > 8); - const EB_U32 frameSize = (lumaSize + chromaSize) << tenBit; + const EB_U32 frameSize = (lumaSize + 2 * chromaSize) << tenBit; EB_MALLOC(EB_BUFFERHEADERTYPE*, reconBuffer, sizeof(EB_BUFFERHEADERTYPE), EB_N_PTR); *objectDblPtr = (EB_PTR)reconBuffer; diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index 8486d4ad6..8d823f243 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -1248,12 +1248,19 @@ void EncodeQuantizedCoefficients_generic( EB_U32 numNonZeroCoeffs = tuPtr->nzCoefCount[ (componentType == COMPONENT_LUMA) ? 0 : (componentType == COMPONENT_CHROMA_CB) ? 1 : 2 - ]; + ]; + numNonZeroCoeffs = (componentType == COMPONENT_CHROMA_CB2) ? tuPtr->nzCoefCount2[0] : + (componentType == COMPONENT_CHROMA_CR2) ? tuPtr->nzCoefCount2[1] : numNonZeroCoeffs; + EB_BOOL secondChroma = componentType == COMPONENT_CHROMA_CB2 || componentType == COMPONENT_CHROMA_CR2; // zerout the buffer to support N2_SHAPE & N4_SHAPE - EB_U32 transCoeffShape = (componentType == COMPONENT_LUMA) ? tuPtr->transCoeffShapeLuma : tuPtr->transCoeffShapeChroma ; + EB_U32 transCoeffShape = ((componentType == COMPONENT_LUMA) ? tuPtr->transCoeffShapeLuma : + secondChroma ? tuPtr->transCoeffShapeChroma2 : tuPtr->transCoeffShapeChroma); + EB_U32 isOnlyDc = (componentType == COMPONENT_LUMA) ? tuPtr->isOnlyDc[0] : + secondChroma ? tuPtr->isOnlyDc2[(componentType == COMPONENT_CHROMA_CB2) ? 0 : 1] : + tuPtr->isOnlyDc[(componentType == COMPONENT_CHROMA_CB) ? 1 : 2]; - if (transCoeffShape && tuPtr->isOnlyDc[(componentType == COMPONENT_LUMA) ? 0 : (componentType == COMPONENT_CHROMA_CB) ? 1 : 2] == EB_FALSE) { + if (transCoeffShape && isOnlyDc == EB_FALSE) { PicZeroOutCoef_funcPtrArray[(EB_ASM_C & PREAVX2_MASK) && 1][(size >> 1) >> 3]( coeffBufferPtr, coeffStride, @@ -1356,13 +1363,15 @@ void EncodeQuantizedCoefficients_generic( //intraLumaMode = candidatePtr->intraLumaMode[0]; //intraChromaMode = candidatePtr->intraChromaMode[0]; - if (((EB_S32)logBlockSize) <= 3 - isChroma) - { + if ((((EB_S32)logBlockSize) <= 3 - isChroma) || + (((EB_S32)logBlockSize) == 3 && cabacEncodeCtxPtr->colorFormat == EB_YUV444)) { EB_U32 tempIntraChromaMode = chromaMappingTable[intraChromaMode]; EB_S32 intraMode = (!isChroma || tempIntraChromaMode == EB_INTRA_CHROMA_DM) ? intraLumaMode : tempIntraChromaMode; + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422 && isChroma && tempIntraChromaMode == EB_INTRA_CHROMA_DM) { + intraMode = intra422PredModeMap[intraLumaMode]; + } - if (ABS(8 - ((intraMode - 2) & 15)) <= 4) - { + if (ABS(8 - ((intraMode - 2) & 15)) <= 4) { scanIndex = (intraMode & 16) ? SCAN_HOR2 : SCAN_VER2; } } @@ -1566,6 +1575,7 @@ void EncodeQuantizedCoefficients_generic( sigMap <<= 16; } + // Jing: change here for 444 if (logBlockSize == 2) { tempOffset = 0; @@ -1573,10 +1583,11 @@ void EncodeQuantizedCoefficients_generic( } else { - tempOffset = (logBlockSize == 3) ? (scanIndex == SCAN_DIAG2 ? 9 : 15) : (!isChroma ? 21 : 12); + tempOffset = (logBlockSize == 3) ? ((scanIndex == SCAN_DIAG2 || isChroma) ? 9 : 15) : (!isChroma ? 21 : 12); tempOffset += (!isChroma && subSetIndex != 0) ? 3 : 0; contextIndexMapPtr = contextIndexMap8[scanIndex != SCAN_DIAG2][significantFlagContextPattern] - subPosition; } + ///////////// // Loop over coefficients do @@ -1728,9 +1739,10 @@ void EncodeQuantizedCoefficients_SSE2( EB_U32 contextOffset2; EB_U32 scanIndex; - EB_U32 numNonZeroCoeffs = tuPtr->nzCoefCount[ (componentType == COMPONENT_LUMA) ? 0 : - (componentType == COMPONENT_CHROMA_CB) ? 1 : 2 - ]; + EB_U32 numNonZeroCoeffs = tuPtr->nzCoefCount[(componentType == COMPONENT_LUMA) ? + 0 : (componentType == COMPONENT_CHROMA_CB) ? 1 : 2]; + numNonZeroCoeffs = (componentType == COMPONENT_CHROMA_CB2) ? tuPtr->nzCoefCount2[0] : + (componentType == COMPONENT_CHROMA_CR2) ? tuPtr->nzCoefCount2[1] : numNonZeroCoeffs; __m128i linearCoeff[MAX_TU_SIZE * MAX_TU_SIZE / (sizeof(__m128i) / sizeof(EB_S16))]; EB_S16 *linearCoeffBufferPtr; @@ -1781,6 +1793,7 @@ void EncodeQuantizedCoefficients_SSE2( EB_S32 index, index2; EB_U32 contextSet; EB_S32 numCoeffWithCodedGt1Flag; // Number of coefficients for which >1 flag is coded + EB_U32 transCoeffShapeChroma = (componentType == COMPONENT_CHROMA_CB2 || componentType == COMPONENT_CHROMA_CR2) ? tuPtr->transCoeffShapeChroma2 : tuPtr->transCoeffShapeChroma; // DC-only fast track @@ -1838,13 +1851,16 @@ void EncodeQuantizedCoefficients_SSE2( //intraLumaMode = candidatePtr->intraLumaMode[0]; //intraChromaMode = candidatePtr->intraChromaMode[0]; - if ((EB_S32)logBlockSize <= 3 - isChroma) - { + if ((((EB_S32)logBlockSize) <= 3 - isChroma) || + (((EB_S32)logBlockSize) == 3 && cabacEncodeCtxPtr->colorFormat == EB_YUV444)) { EB_U32 tempIntraChromaMode = chromaMappingTable[intraChromaMode]; EB_S32 intraMode = (!isChroma || tempIntraChromaMode == EB_INTRA_CHROMA_DM) ? intraLumaMode : tempIntraChromaMode; - if (ABS(8 - ((intraMode - 2) & 15)) <= 4) - { + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422 && isChroma && tempIntraChromaMode == EB_INTRA_CHROMA_DM) { + intraMode = intra422PredModeMap[intraLumaMode]; + } + + if (ABS(8 - ((intraMode - 2) & 15)) <= 4) { scanIndex = (intraMode & 16) ? SCAN_HOR2 : SCAN_VER2; } } @@ -1884,7 +1900,7 @@ void EncodeQuantizedCoefficients_SSE2( if(isChroma==EB_FALSE){ isCGin = ((EB_U32)coeffGroupPositionY < (size >>(tuPtr->transCoeffShapeLuma+2))) && ((EB_U32)coeffGroupPositionX < (size >>(tuPtr->transCoeffShapeLuma+2))); }else{ - isCGin = ((EB_U32)coeffGroupPositionY < (size >>(tuPtr->transCoeffShapeChroma+2))) && ((EB_U32)coeffGroupPositionX < (size >>(tuPtr->transCoeffShapeChroma+2))); + isCGin = ((EB_U32)coeffGroupPositionY < (size >>(transCoeffShapeChroma+2))) && ((EB_U32)coeffGroupPositionX < (size >>(transCoeffShapeChroma+2))); } if(isCGin == EB_FALSE){ @@ -2108,7 +2124,7 @@ void EncodeQuantizedCoefficients_SSE2( } else { - tempOffset = (logBlockSize == 3) ? (scanIndex == SCAN_DIAG2 ? 9 : 15) : (!isChroma ? 21 : 12); + tempOffset = (logBlockSize == 3) ? ((scanIndex == SCAN_DIAG2 || isChroma) ? 9 : 15) : (!isChroma ? 21 : 12); tempOffset += (!isChroma && subSetIndex != 0) ? 3 : 0; contextIndexMapPtr = contextIndexMap8[scanIndex != SCAN_DIAG2][significantFlagContextPattern] - subPosition; } @@ -2346,14 +2362,14 @@ EB_ERRORTYPE RemainingCoeffExponentialGolombCodeTemp( else { numberOfBins = (*golombParamPtr); - //codeWord = codeWord - ( 8 << ((*golombParamPtr))); + //codeWord = codeWord - ( 8 << ((*golombParamPtr))); codeWord = codeWord - (COEF_REMAIN_BIN_REDUCTION << ((*golombParamPtr))); while (codeWord >= (1 << numberOfBins)) { codeWord -= (1 << (numberOfBins++)); } - //*coeffBits += 32768*(8+numberOfBins+1-*golombParamPtr); + //*coeffBits += 32768*(8+numberOfBins+1-*golombParamPtr); *coeffBits += 32768 * (COEF_REMAIN_BIN_REDUCTION + numberOfBins + 1 - *golombParamPtr); *coeffBits += 32768 * numberOfBins; @@ -4038,7 +4054,7 @@ EB_ERRORTYPE CheckAndCodeDeltaQp( EB_ERRORTYPE return_error = EB_ErrorNone; if (isDeltaQpEnable) { - if (tuPtr->lumaCbf || tuPtr->cbCbf || tuPtr->crCbf){ + if (tuPtr->lumaCbf || tuPtr->cbCbf || tuPtr->crCbf || tuPtr->cbCbf2 || tuPtr->crCbf2){ if (*isdeltaQpNotCoded){ EB_S32 deltaQp; deltaQp = cuPtr->qp - cuPtr->refQp; @@ -4056,7 +4072,7 @@ EB_ERRORTYPE CheckAndCodeDeltaQp( } /************************************ -******* EncodeTuCoeff +******* EncodeCoeff **************************************/ static EB_ERRORTYPE EncodeCoeff( CabacEncodeContext_t *cabacEncodeCtxPtr, @@ -4071,12 +4087,13 @@ static EB_ERRORTYPE EncodeCoeff( EB_S16 *coeffBuffer; EB_U32 coeffLocation; - EB_U32 tuChromaSize = tuSize == 4 ? 4 : (tuSize >> 1); + const EB_U16 subWidthCMinus1 = (cabacEncodeCtxPtr->colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (cabacEncodeCtxPtr->colorFormat >= EB_YUV422 ? 1 : 2) - 1; + EB_U32 tuChromaSize = (tuSize == 4) ? 4 : (tuSize >> subWidthCMinus1); coeffLocation = tuOriginX + (tuOriginY * coeffPtr->strideY); coeffBuffer = (EB_S16*)&coeffPtr->bufferY[coeffLocation * sizeof(EB_S16)]; if (tuPtr->lumaCbf) { - EncodeQuantizedCoefficientsFuncArray[(ASM_TYPES & PREAVX2_MASK) && 1]( cabacEncodeCtxPtr, tuSize, @@ -4094,12 +4111,11 @@ static EB_ERRORTYPE EncodeCoeff( //tuOriginY = (cuPtr->size == MIN_CU_SIZE ) || ( (cuPtr->size == 16 ) && ((Log2f(cuPtr->size) - tuSizeLog2) == 2))? tuPtr->tuNode->originY: tuOriginY; // cb - coeffLocation = ((tuOriginX + (tuOriginY * coeffPtr->strideCb)) >> 1); + coeffLocation = (tuOriginX >> subWidthCMinus1) + ((tuOriginY * coeffPtr->strideCb) >> subHeightCMinus1); coeffBuffer = (EB_S16*)&coeffPtr->bufferCb[coeffLocation * sizeof(EB_S16)]; if (tuSize > 4){ if (tuPtr->cbCbf) { - EncodeQuantizedCoefficientsFuncArray[(ASM_TYPES & PREAVX2_MASK) && 1]( cabacEncodeCtxPtr, tuChromaSize, @@ -4110,13 +4126,25 @@ static EB_ERRORTYPE EncodeCoeff( coeffPtr->strideCb, COMPONENT_CHROMA_CB, tuPtr);//tuPtr->nzCoefCount[1]); - } - } - else if (tuPtr->tuIndex - ((tuPtr->tuIndex >> 2) << 2) == 0) { + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422 && tuPtr->cbCbf2) { + coeffLocation = (tuOriginX >> 1) + ((tuOriginY+tuChromaSize) * coeffPtr->strideCb); + coeffBuffer = (EB_S16*)&coeffPtr->bufferCb[coeffLocation * sizeof(EB_S16)]; + EncodeQuantizedCoefficientsFuncArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + cabacEncodeCtxPtr, + tuChromaSize, + (EB_MODETYPE)cuPtr->predictionModeFlag, + (&cuPtr->predictionUnitArray[0])->intraLumaMode, + EB_INTRA_CHROMA_DM, + coeffBuffer,// Jing: check here + coeffPtr->strideCb, + COMPONENT_CHROMA_CB2, + tuPtr);//tuPtr->nzCoefCount[1]); + } + } else if (tuPtr->tuIndex - ((tuPtr->tuIndex >> 2) << 2) == 0) { + // Never be here if (tuPtr->cbCbf) { - EncodeQuantizedCoefficientsFuncArray[(ASM_TYPES & PREAVX2_MASK) && 1]( cabacEncodeCtxPtr, tuChromaSize, @@ -4132,7 +4160,7 @@ static EB_ERRORTYPE EncodeCoeff( } // cr - coeffLocation = ((tuOriginX + tuOriginY * (coeffPtr->strideCr)) >> 1); + coeffLocation = (tuOriginX >> subWidthCMinus1) + ((tuOriginY * coeffPtr->strideCr) >> subHeightCMinus1); coeffBuffer = (EB_S16*)&coeffPtr->bufferCr[coeffLocation * sizeof(EB_S16)]; if (tuSize > 4){ @@ -4146,9 +4174,23 @@ static EB_ERRORTYPE EncodeCoeff( coeffBuffer, coeffPtr->strideCr, COMPONENT_CHROMA_CR, - tuPtr);//tuPtr->nzCoefCount[2]); + tuPtr);//tuPtr->nzCoefCount[2]); } + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422 && tuPtr->crCbf2) { + coeffLocation = (tuOriginX >> 1) + ((tuOriginY+tuChromaSize) * coeffPtr->strideCr); + coeffBuffer = (EB_S16*)&coeffPtr->bufferCr[coeffLocation * sizeof(EB_S16)]; + EncodeQuantizedCoefficientsFuncArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + cabacEncodeCtxPtr, + tuChromaSize, + (EB_MODETYPE)cuPtr->predictionModeFlag, + (&cuPtr->predictionUnitArray[0])->intraLumaMode, + EB_INTRA_CHROMA_DM, + coeffBuffer, + coeffPtr->strideCr, + COMPONENT_CHROMA_CR2, + tuPtr);//tuPtr->nzCoefCount[2]); + } } else if (tuPtr->tuIndex - ((tuPtr->tuIndex >> 2) << 2) == 0) { @@ -4208,17 +4250,18 @@ static EB_ERRORTYPE EncodeTuCoeff( } if (tuPtr->splitFlag) { + // Jing: only comes here for inter 64x64 // Cb CBF EncodeOneBin( &(cabacEncodeCtxPtr->bacEncContext), - tuPtr->cbCbf, + (tuPtr->cbCbf | tuPtr->cbCbf2), &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); // Cr CBF EncodeOneBin( &(cabacEncodeCtxPtr->bacEncContext), - tuPtr->crCbf, + (tuPtr->crCbf | tuPtr->crCbf2), &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); //for(tuIndex = 1; tuIndex < 5; tuIndex++) { @@ -4240,9 +4283,10 @@ static EB_ERRORTYPE EncodeTuCoeff( } if (tuPtr->splitFlag) { + // Jing: seems never comes here for now cbfContext = tuPtr->chromaCbfContext; - if ((cuPtr->transformUnitArray[0].cbCbf) != 0){ + if (cuPtr->transformUnitArray[0].cbCbf | cuPtr->transformUnitArray[0].cbCbf2) { // Cb CBF EncodeOneBin( &(cabacEncodeCtxPtr->bacEncContext), @@ -4250,7 +4294,7 @@ static EB_ERRORTYPE EncodeTuCoeff( &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); } - if ((cuPtr->transformUnitArray[0].crCbf) != 0){ + if (cuPtr->transformUnitArray[0].crCbf | cuPtr->transformUnitArray[0].crCbf2){ // Cr CBF EncodeOneBin( &(cabacEncodeCtxPtr->bacEncContext), @@ -4470,19 +4514,31 @@ static EB_ERRORTYPE EncodeTuCoeff( cbfContext = tuPtr->chromaCbfContext; // Cb CBF - if ((cuPtr->transformUnitArray[0].cbCbf) && (tuSize != 8)){ - EncodeOneBin( - &(cabacEncodeCtxPtr->bacEncContext), - tuPtr->cbCbf, - &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); - } + if (cuPtr->transformUnitArray[0].cbCbf | cuPtr->transformUnitArray[0].cbCbf2) { + EncodeOneBin( + &(cabacEncodeCtxPtr->bacEncContext), + tuPtr->cbCbf, + &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422) { + EncodeOneBin( + &(cabacEncodeCtxPtr->bacEncContext), + tuPtr->cbCbf2, + &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + } + } // Cr CBF - if ((cuPtr->transformUnitArray[0].crCbf) && (tuSize != 8)){ + if (cuPtr->transformUnitArray[0].crCbf | cuPtr->transformUnitArray[0].crCbf2) { EncodeOneBin( &(cabacEncodeCtxPtr->bacEncContext), tuPtr->crCbf, &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422) { + EncodeOneBin( + &(cabacEncodeCtxPtr->bacEncContext), + tuPtr->crCbf2, + &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + } } cbfContext = tuPtr->lumaCbfContext; @@ -4517,8 +4573,6 @@ static EB_ERRORTYPE EncodeTuCoeff( } else { - - tuOriginX = TU_ORIGIN_ADJUST(cuStatsPtr->originX, cuStatsPtr->size, tuStatsPtr->offsetX); tuOriginY = TU_ORIGIN_ADJUST(cuStatsPtr->originY, cuStatsPtr->size, tuStatsPtr->offsetY); @@ -4527,6 +4581,12 @@ static EB_ERRORTYPE EncodeTuCoeff( &(cabacEncodeCtxPtr->bacEncContext), tuPtr->cbCbf, &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422) { + EncodeOneBin( + &(cabacEncodeCtxPtr->bacEncContext), + tuPtr->cbCbf2, + &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + } // Cr CBF EncodeOneBin( @@ -4534,11 +4594,18 @@ static EB_ERRORTYPE EncodeTuCoeff( tuPtr->crCbf, &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422) { + EncodeOneBin( + &(cabacEncodeCtxPtr->bacEncContext), + tuPtr->crCbf2, + &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + } // Luma CBF // In the Inter case, if the RootCbf is 1 and the Chroma Cbfs are 0, then we can infer that the // luma Cbf is true, so there is no need to code it. - if ((cuPtr->predictionModeFlag == INTRA_MODE) || tuPtr->cbCbf || tuPtr->crCbf) { + if ((cuPtr->predictionModeFlag == INTRA_MODE) || tuPtr->cbCbf || tuPtr->crCbf || + (cabacEncodeCtxPtr->colorFormat == EB_YUV422 && (tuPtr->cbCbf2 || tuPtr->crCbf2))) { //cbfContext = ((cuPtr->size == tuPtr->size) || (tuPtr->size == TRANSFORM_MAX_SIZE)); cbfContext = tuPtr->lumaCbfContext; @@ -4606,15 +4673,16 @@ static EB_ERRORTYPE EncodeTuSplitCoeff( (cabacEncodeCtxPtr->bacEncContext.tempBufferedBytesNum << 3); // Root CBF rootCbf = cuPtr->rootCbf; - if (cuPtr->predictionModeFlag != INTRA_MODE && !((&cuPtr->predictionUnitArray[0])->mergeFlag)) { + if (cuPtr->predictionModeFlag != INTRA_MODE && + !((&cuPtr->predictionUnitArray[0])->mergeFlag)) { EncodeOneBin( &(cabacEncodeCtxPtr->bacEncContext), rootCbf, &(cabacEncodeCtxPtr->contextModelEncContext.rootCbfContextModel[0])); } - if ((cuPtr->predictionModeFlag == INTRA_MODE) || ((cuPtr->predictionModeFlag == INTER_MODE) && (rootCbf > 0))) { - + if ((cuPtr->predictionModeFlag == INTRA_MODE) || + ((cuPtr->predictionModeFlag == INTER_MODE) && (rootCbf > 0))) { EncodeTuCoeff( cabacEncodeCtxPtr, cuPtr, @@ -4623,6 +4691,7 @@ static EB_ERRORTYPE EncodeTuSplitCoeff( isDeltaQpEnable, isdeltaQpNotCoded); } + //store the number of written bits after coding quantized coeffs (flush is not called yet): // The total number of bits is // number of written bits @@ -4872,11 +4941,99 @@ static void CodeProfileTier( bitstreamPtr, scsPtr->generalFrameOnlyConstraintFlag); - // "XXX_reserved_zero_44bits[0..15]" - WriteCodeCavlc( - bitstreamPtr, - 0, - 16); + if(scsPtr->profileIdc < 4) + { + // "XXX_reserved_zero_44bits[0..15]" + WriteCodeCavlc( + bitstreamPtr, + 0, + 16); + } else + { + // "general_max_12bit_constraint_flag" + WriteFlagCavlc( + bitstreamPtr, + 1); + + // "general_max_10bit_constraint_flag" + if(scsPtr->encoderBitDepth <= EB_10BIT || scsPtr->staticConfig.constrainedIntra == EB_TRUE) + { + WriteFlagCavlc( + bitstreamPtr, + 1); + } else + { + WriteFlagCavlc( + bitstreamPtr, + 0); + } + + // "general_max_8bit_constraint_flag" + //if(scsPtr->encoderBitDepth == EB_8BIT) + if(scsPtr->encoderBitDepth == EB_8BIT && (scsPtr->chromaFormatIdc == EB_YUV444 || scsPtr->staticConfig.constrainedIntra == EB_TRUE)) + { + WriteFlagCavlc( + bitstreamPtr, + 1); + } else + { + WriteFlagCavlc( + bitstreamPtr, + 0); + } + + // "general_max_422chroma_constraint_flag" + if(scsPtr->chromaFormatIdc == EB_YUV422 || (scsPtr->chromaFormatIdc == EB_YUV420 && scsPtr->staticConfig.constrainedIntra == EB_TRUE)) + { + WriteFlagCavlc( + bitstreamPtr, + 1); + } else + { + WriteFlagCavlc( + bitstreamPtr, + 0); + } + + // "general_max_420chroma_constraint_flag" + if(scsPtr->chromaFormatIdc == EB_YUV420) + { + WriteFlagCavlc( + bitstreamPtr, + 1); + } else + { + WriteFlagCavlc( + bitstreamPtr, + 0); + } + + // "general_max_monochrome_constraint_flag" + WriteFlagCavlc( + bitstreamPtr, + 0); + + // "general_intra_constraint_flag" + WriteFlagCavlc( + bitstreamPtr, + (scsPtr->staticConfig.constrainedIntra == EB_TRUE)); + + // "general_one_picture_only_constraint_flag" + WriteFlagCavlc( + bitstreamPtr, + 0); + + // "general_lower_bit_rate_constraint_flag" + WriteFlagCavlc( + bitstreamPtr, + 1); + + // "XXX_reserved_zero_44bits[9..15]" + WriteCodeCavlc( + bitstreamPtr, + 0, + 7); + } // "XXX_reserved_zero_44bits[16..31]" WriteCodeCavlc( @@ -5329,7 +5486,6 @@ static void CodeVPS( WriteUvlc( bitstreamPtr, 0); - } // "vps_extension_flag" @@ -5814,6 +5970,10 @@ static void CodeSPS( bitstreamPtr, scsPtr->chromaFormatIdc); + if (scsPtr->chromaFormatIdc == EB_YUV444) { + WriteFlagCavlc(bitstreamPtr, 0); //separate_colour_plane_flag=0 + } + // "pic_width_in_luma_samples" WriteUvlc( bitstreamPtr, @@ -6713,7 +6873,11 @@ EB_ERRORTYPE Intra4x4CheckAndCodeDeltaQp( EB_ERRORTYPE return_error = EB_ErrorNone; if (isDeltaQpEnable) { - if (tuPtr->lumaCbf || (&cuPtr->transformUnitArray[1])->cbCbf || (&cuPtr->transformUnitArray[1])->crCbf){ + if (tuPtr->lumaCbf || + (&cuPtr->transformUnitArray[1])->cbCbf || + (&cuPtr->transformUnitArray[1])->crCbf || + (&cuPtr->transformUnitArray[3])->cbCbf || + (&cuPtr->transformUnitArray[3])->crCbf){ if (*isdeltaQpNotCoded){ EB_S32 deltaQp; deltaQp = cuPtr->qp - cuPtr->refQp; @@ -6775,66 +6939,75 @@ static EB_ERRORTYPE Intra4x4EncodeChromaCoeff( EB_U8 intraLumaMode, CabacEncodeContext_t *cabacEncodeCtxPtr, CodingUnit_t *cuPtr, - TransformUnit_t *tuPtr, EB_U32 tuOriginX, EB_U32 tuOriginY, + EB_U32 tuIndex, //For 444 case, 422/420 can ignore this flag EbPictureBufferDesc_t *coeffPtr) { EB_ERRORTYPE return_error = EB_ErrorNone; + TransformUnit_t *tuPtr = NULL; EB_S16 *coeffBuffer; EB_U32 coeffLocation; EB_U32 countNonZeroCoeffs = 0; + const EB_U16 subWidthCMinus1 = (cabacEncodeCtxPtr->colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (cabacEncodeCtxPtr->colorFormat >= EB_YUV422 ? 1 : 2) - 1; // cb - coeffLocation = ((tuOriginX + (tuOriginY * coeffPtr->strideCb)) >> 1); - coeffBuffer = (EB_S16*)&coeffPtr->bufferCb[coeffLocation * sizeof(EB_S16)]; - - if (tuPtr->cbCbf){ - - ComputeNumofSigCoefficients( - coeffBuffer, - coeffPtr->strideCb, - MIN_PU_SIZE, - &countNonZeroCoeffs); - - EncodeQuantizedCoefficientsFuncArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - cabacEncodeCtxPtr, - MIN_PU_SIZE, - (EB_MODETYPE)cuPtr->predictionModeFlag, - intraLumaMode, - EB_INTRA_CHROMA_DM, - coeffBuffer, - coeffPtr->strideCb, - COMPONENT_CHROMA_CB, - tuPtr); + for (int tIdx = 0; tIdx < (cabacEncodeCtxPtr->colorFormat == EB_YUV422 ? 2 : 1); tIdx++) { + // Get the correct TU block for 444, not always the 1st one + tuPtr=&cuPtr->transformUnitArray[tuIndex + 1 + 2 * tIdx]; //1,3 for 422 chroma + coeffLocation = (tuOriginX >> subWidthCMinus1) + + (((tuOriginY + MIN_PU_SIZE * tIdx) * coeffPtr->strideCb) >> subHeightCMinus1); + coeffBuffer = (EB_S16*)&coeffPtr->bufferCb[coeffLocation * sizeof(EB_S16)]; + + if (tuPtr->cbCbf){ + ComputeNumofSigCoefficients( + coeffBuffer, + coeffPtr->strideCb, + MIN_PU_SIZE, + &countNonZeroCoeffs); - } + EncodeQuantizedCoefficientsFuncArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + cabacEncodeCtxPtr, + MIN_PU_SIZE, + (EB_MODETYPE)cuPtr->predictionModeFlag, + intraLumaMode, + EB_INTRA_CHROMA_DM, + coeffBuffer, + coeffPtr->strideCb, + COMPONENT_CHROMA_CB, + tuPtr); + } + } // cr - coeffLocation = ((tuOriginX + (tuOriginY * coeffPtr->strideCr)) >> 1); - coeffBuffer = (EB_S16*)&coeffPtr->bufferCr[coeffLocation * sizeof(EB_S16)]; - - if (tuPtr->crCbf){ - - ComputeNumofSigCoefficients( - coeffBuffer, - coeffPtr->strideCr, - MIN_PU_SIZE, - &countNonZeroCoeffs); - - EncodeQuantizedCoefficientsFuncArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - cabacEncodeCtxPtr, - MIN_PU_SIZE, - (EB_MODETYPE)cuPtr->predictionModeFlag, - intraLumaMode, - EB_INTRA_CHROMA_DM, - coeffBuffer, - coeffPtr->strideCr, - COMPONENT_CHROMA_CR, - tuPtr); + for (int tIdx=0; tIdx<(cabacEncodeCtxPtr->colorFormat==EB_YUV422?2:1); tIdx++) { + tuPtr=&cuPtr->transformUnitArray[tuIndex + 1 + 2 * tIdx]; //1,3 for 422 chroma + //coeffLocation = ((tuOriginX + ((tuOriginY+MIN_PU_SIZE*tIdx) * coeffPtr->strideCb)) >> 1); + coeffLocation = (tuOriginX >> subWidthCMinus1) + + (((tuOriginY + MIN_PU_SIZE * tIdx) * coeffPtr->strideCb) >> subHeightCMinus1); + coeffBuffer = (EB_S16*)&coeffPtr->bufferCr[coeffLocation * sizeof(EB_S16)]; + + if (tuPtr->crCbf){ + ComputeNumofSigCoefficients( + coeffBuffer, + coeffPtr->strideCr, + MIN_PU_SIZE, + &countNonZeroCoeffs); - } + EncodeQuantizedCoefficientsFuncArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + cabacEncodeCtxPtr, + MIN_PU_SIZE, + (EB_MODETYPE)cuPtr->predictionModeFlag, + intraLumaMode, + EB_INTRA_CHROMA_DM, + coeffBuffer, + coeffPtr->strideCr, + COMPONENT_CHROMA_CR, + tuPtr); + } + } return return_error; } @@ -6863,6 +7036,8 @@ static EB_ERRORTYPE Intra4x4EncodeCoeff( //rate Control EB_U32 writtenBitsBeforeQuantizedCoeff; EB_U32 writtenBitsAfterQuantizedCoeff; + EB_BOOL sum_cbCbf; + EB_BOOL sum_crCbf; //store the number of written bits before coding quantized coeffs (flush is not called yet): // The total number of bits is @@ -6877,33 +7052,75 @@ static EB_ERRORTYPE Intra4x4EncodeCoeff( // Get Chroma Cbf context cbfContext = 0; + sum_cbCbf = (cabacEncodeCtxPtr->colorFormat != EB_YUV444) ? + (&cuPtr->transformUnitArray[1])->cbCbf : + ((&cuPtr->transformUnitArray[1])->cbCbf | + (&cuPtr->transformUnitArray[2])->cbCbf | + (&cuPtr->transformUnitArray[3])->cbCbf | + (&cuPtr->transformUnitArray[4])->cbCbf); + + sum_crCbf = (cabacEncodeCtxPtr->colorFormat != EB_YUV444) ? + (&cuPtr->transformUnitArray[1])->crCbf : + ((&cuPtr->transformUnitArray[1])->crCbf | + (&cuPtr->transformUnitArray[2])->crCbf | + (&cuPtr->transformUnitArray[3])->crCbf | + (&cuPtr->transformUnitArray[4])->crCbf); + // Cb CBF EncodeOneBin( &(cabacEncodeCtxPtr->bacEncContext), - (&cuPtr->transformUnitArray[1])->cbCbf, + sum_cbCbf, &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422) { + EncodeOneBin( + &(cabacEncodeCtxPtr->bacEncContext), + (&cuPtr->transformUnitArray[3])->cbCbf, + &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + } // Cr CBF EncodeOneBin( &(cabacEncodeCtxPtr->bacEncContext), - (&cuPtr->transformUnitArray[1])->crCbf, + sum_crCbf, &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + if (cabacEncodeCtxPtr->colorFormat == EB_YUV422) { + EncodeOneBin( + &(cabacEncodeCtxPtr->bacEncContext), + (&cuPtr->transformUnitArray[3])->crCbf, + &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + } // Get Luma Cbf context - cbfContext = 0; // Encode Intra 4x4 data for (puIndex = 0; puIndex < 4; puIndex++) { - tuIndex = puIndex + 1; tuPtr = &cuPtr->transformUnitArray[tuIndex]; tuStatsPtr = GetTransformUnitStats(tuIndex); tuOriginX = TU_ORIGIN_ADJUST(cuStatsPtr->originX, cuStatsPtr->size, tuStatsPtr->offsetX); tuOriginY = TU_ORIGIN_ADJUST(cuStatsPtr->originY, cuStatsPtr->size, tuStatsPtr->offsetY); + if (cabacEncodeCtxPtr->colorFormat == EB_YUV444) { + cbfContext = 1; + if (sum_cbCbf) { + EncodeOneBin( + &(cabacEncodeCtxPtr->bacEncContext), + tuPtr->cbCbf, + &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + } + + if (sum_crCbf) { + EncodeOneBin( + &(cabacEncodeCtxPtr->bacEncContext), + tuPtr->crCbf, + &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext + NUMBER_OF_CBF_CONTEXT_MODELS])); + } + } + + cbfContext = 0; EncodeOneBin( &(cabacEncodeCtxPtr->bacEncContext), - (&cuPtr->transformUnitArray[tuIndex])->lumaCbf, + tuPtr->lumaCbf, &(cabacEncodeCtxPtr->contextModelEncContext.cbfContextModel[cbfContext])); //EncodeDeltaQp @@ -6923,21 +7140,36 @@ static EB_ERRORTYPE Intra4x4EncodeCoeff( tuOriginX, tuOriginY, coeffPtr); + + if (cabacEncodeCtxPtr->colorFormat == EB_YUV444) { + // residual coding for Cb/Cr + Intra4x4EncodeChromaCoeff( + tbPtr->intra4x4Mode[((MD_SCAN_TO_RASTER_SCAN[cuPtr->leafIndex] - 21) << 2) + puIndex], + cabacEncodeCtxPtr, + cuPtr, + tuOriginX, + tuOriginY, + puIndex, + coeffPtr); + } } - // Encode Chroma coeff - tuStatsPtr = GetTransformUnitStats(1); - tuOriginX = TU_ORIGIN_ADJUST(cuStatsPtr->originX, cuStatsPtr->size, tuStatsPtr->offsetX); - tuOriginY = TU_ORIGIN_ADJUST(cuStatsPtr->originY, cuStatsPtr->size, tuStatsPtr->offsetY); + if (cabacEncodeCtxPtr->colorFormat != EB_YUV444) { + // Encode Chroma coeff for non-444 case, + // Jing TODO: see if can move to above loop + tuStatsPtr = GetTransformUnitStats(1); + tuOriginX = TU_ORIGIN_ADJUST(cuStatsPtr->originX, cuStatsPtr->size, tuStatsPtr->offsetX); + tuOriginY = TU_ORIGIN_ADJUST(cuStatsPtr->originY, cuStatsPtr->size, tuStatsPtr->offsetY); - Intra4x4EncodeChromaCoeff( - tbPtr->intra4x4Mode[((MD_SCAN_TO_RASTER_SCAN[cuPtr->leafIndex] - 21) << 2)], - cabacEncodeCtxPtr, - cuPtr, - &cuPtr->transformUnitArray[1], - tuOriginX, - tuOriginY, - coeffPtr); + Intra4x4EncodeChromaCoeff( + tbPtr->intra4x4Mode[((MD_SCAN_TO_RASTER_SCAN[cuPtr->leafIndex] - 21) << 2)], + cabacEncodeCtxPtr, + cuPtr, + tuOriginX, + tuOriginY, + 0, + coeffPtr); + } //store the number of written bits after coding quantized coeffs (flush is not called yet): // The total number of bits is @@ -6989,6 +7221,7 @@ EB_ERRORTYPE EncodeLcu( EB_U32 cuSize; EB_U8 cuDepth; EB_BOOL availableCoeff; + cabacEncodeCtxPtr->colorFormat = pictureControlSetPtr->colorFormat; // PU Varaiables PredictionUnit_t *puPtr; @@ -7037,21 +7270,29 @@ EB_ERRORTYPE EncodeLcu( } if (cuPtr->splitFlag == EB_FALSE){ - if (cuPtr->predictionModeFlag == INTRA_MODE && cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) - + if (cuPtr->predictionModeFlag == INTRA_MODE && + cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) { availableCoeff = ( cuPtr->transformUnitArray[1].lumaCbf || cuPtr->transformUnitArray[2].lumaCbf || cuPtr->transformUnitArray[3].lumaCbf || cuPtr->transformUnitArray[4].lumaCbf || cuPtr->transformUnitArray[1].crCbf || - cuPtr->transformUnitArray[1].cbCbf) ? EB_TRUE : EB_FALSE; - - else + cuPtr->transformUnitArray[1].cbCbf || + cuPtr->transformUnitArray[2].crCbf || + cuPtr->transformUnitArray[2].cbCbf || + cuPtr->transformUnitArray[3].crCbf || + cuPtr->transformUnitArray[3].cbCbf || + cuPtr->transformUnitArray[4].crCbf || // 422 case will use 3rd 4x4 for the 2nd chroma + cuPtr->transformUnitArray[4].cbCbf) ? EB_TRUE : EB_FALSE; + } else { availableCoeff = (cuPtr->predictionModeFlag == INTER_MODE) ? (EB_BOOL)cuPtr->rootCbf : (cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].lumaCbf || cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].crCbf || - cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].cbCbf) ? EB_TRUE : EB_FALSE; + cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].crCbf2 || + cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].cbCbf || + cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].cbCbf2) ? EB_TRUE : EB_FALSE; + } EntropyCodingUpdateQp( cuPtr, @@ -7108,11 +7349,11 @@ EB_ERRORTYPE EncodeLcu( cabacEncodeCtxPtr, cuPtr); } - switch (cuPtr->predictionModeFlag) { + switch (cuPtr->predictionModeFlag) { case INTRA_MODE: - if (cuPtr->predictionModeFlag == INTRA_MODE && cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) { - + if (cuPtr->predictionModeFlag == INTRA_MODE && + cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) { // Code Partition Size EncodeIntra4x4PartitionSize( cabacEncodeCtxPtr, @@ -7187,8 +7428,11 @@ EB_ERRORTYPE EncodeLcu( } // Code Chroma Mode for Intra - EncodeIntraChromaMode( - cabacEncodeCtxPtr); + for (partitionIndex = 0; + partitionIndex < ((cabacEncodeCtxPtr->colorFormat == EB_YUV444) ? 4 : 1); + partitionIndex++) { + EncodeIntraChromaMode(cabacEncodeCtxPtr); + } // Encode Transform Unit Split & CBFs Intra4x4EncodeCoeff( @@ -7202,15 +7446,12 @@ EB_ERRORTYPE EncodeLcu( &deltaQpNotCoded); tbPtr->quantizedCoeffsBits += cuQuantizedCoeffsBits; - - } else - - { + } else { // Code Partition Size EncodePartitionSize( - cabacEncodeCtxPtr, - cuPtr, - pictureControlSetPtr->lcuMaxDepth); + cabacEncodeCtxPtr, + cuPtr, + pictureControlSetPtr->lcuMaxDepth); EB_U8 intraLumaLeftMode; EB_U8 intraLumaTopMode; @@ -7220,64 +7461,63 @@ EB_ERRORTYPE EncodeLcu( puPtr = cuPtr->predictionUnitArray; // Code Luma Mode for Intra First Stage EncodeIntraLumaModeFirstStage( - cabacEncodeCtxPtr, - cuOriginX, - cuOriginY, - lcuSize, - &intraLumaLeftMode, - &intraLumaTopMode, - puPtr->intraLumaMode, - modeTypeNeighborArray, - intraLumaModeNeighborArray); + cabacEncodeCtxPtr, + cuOriginX, + cuOriginY, + lcuSize, + &intraLumaLeftMode, + &intraLumaTopMode, + puPtr->intraLumaMode, + modeTypeNeighborArray, + intraLumaModeNeighborArray); intraLumaMode = (EB_U8)puPtr->intraLumaMode; NeighborArrayUnitModeWrite( - intraLumaModeNeighborArray, - (EB_U8*)&intraLumaMode, - cuOriginX, - cuOriginY, - cuSize, - cuSize, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - { - EB_U8 predictionModeFlag = (EB_U8)cuPtr->predictionModeFlag; - NeighborArrayUnitModeWrite( - modeTypeNeighborArray, - &predictionModeFlag, + intraLumaModeNeighborArray, + (EB_U8*)&intraLumaMode, cuOriginX, cuOriginY, cuSize, cuSize, NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - } - // Get PU Ptr - puPtr = &cuPtr->predictionUnitArray[0]; + { + EB_U8 predictionModeFlag = (EB_U8)cuPtr->predictionModeFlag; + NeighborArrayUnitModeWrite( + modeTypeNeighborArray, + &predictionModeFlag, + cuOriginX, + cuOriginY, + cuSize, + cuSize, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + } - // Code Luma Mode for Intra Second Stage - EncodeIntraLumaModeSecondStage( - cabacEncodeCtxPtr, - intraLumaLeftMode, - intraLumaTopMode, - puPtr->intraLumaMode); + // Get PU Ptr + puPtr = &cuPtr->predictionUnitArray[0]; - // Code Chroma Mode for Intra - EncodeIntraChromaMode( - cabacEncodeCtxPtr); - EncodeTuSplitCoeff( - cabacEncodeCtxPtr, - cuPtr, - cuStatsPtr, - coeffPtr, - &cuQuantizedCoeffsBits, - (EB_BOOL)pictureControlSetPtr->useDeltaQp, - &deltaQpNotCoded); + // Code Luma Mode for Intra Second Stage + EncodeIntraLumaModeSecondStage( + cabacEncodeCtxPtr, + intraLumaLeftMode, + intraLumaTopMode, + puPtr->intraLumaMode); - tbPtr->quantizedCoeffsBits += cuQuantizedCoeffsBits; + // Code Chroma Mode for Intra + EncodeIntraChromaMode( + cabacEncodeCtxPtr); + EncodeTuSplitCoeff( + cabacEncodeCtxPtr, + cuPtr, + cuStatsPtr, + coeffPtr, + &cuQuantizedCoeffsBits, + (EB_BOOL)pictureControlSetPtr->useDeltaQp, + &deltaQpNotCoded); - } + tbPtr->quantizedCoeffsBits += cuQuantizedCoeffsBits; + } break; case INTER_MODE: @@ -8500,7 +8740,7 @@ EB_ERRORTYPE EncodeUnregUserDataSEI( EB_U32 index; unsigned payloadType = UNREG_USER_DATA; - unsigned payloadSize = unregUserDataSeiPtr->userDataSize; + unsigned payloadSize = unregUserDataSeiPtr->userDataSize + 16 ; OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)bitstreamPtr->outputBitstreamPtr; @@ -8677,12 +8917,205 @@ EB_ERRORTYPE CodeEndOfSequenceNalUnit( return return_error; } +EB_ERRORTYPE EncodeContentLightLevelSEI( + Bitstream_t *bitstreamPtr, + AppContentLightLevelSei_t *contentLightLevelPtr) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + unsigned payloadType = CONTENT_LIGHT_LEVEL_INFO; + unsigned payloadSize = GetContentLightLevelSEILength(); + + OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)bitstreamPtr->outputBitstreamPtr; + + CodeNALUnitHeader( + outputBitstreamPtr, + NAL_UNIT_PREFIX_SEI, + 0); + + for (; payloadType >= 0xff; payloadType -= 0xff) { + OutputBitstreamWrite( + outputBitstreamPtr, + 0xff, + 8); + } + return_error = OutputBitstreamWrite( + outputBitstreamPtr, + payloadType, + 8); + + for (; payloadSize >= 0xff; payloadSize -= 0xff) { + OutputBitstreamWrite( + outputBitstreamPtr, + 0xff, + 8); + } + return_error = OutputBitstreamWrite( + outputBitstreamPtr, + payloadSize, + 8); + + //max_content_light_level + WriteCodeCavlc( + outputBitstreamPtr, + contentLightLevelPtr->maxContentLightLevel, + 16); + + // max_pixel_average_light_level + WriteCodeCavlc( + outputBitstreamPtr, + contentLightLevelPtr->maxPicAverageLightLevel, + 16); + + if (outputBitstreamPtr->writtenBitsCount % 8 != 0) { + // bit_equal_to_one + WriteFlagCavlc( + outputBitstreamPtr, + 1); + + while (outputBitstreamPtr->writtenBitsCount % 8 != 0) { + // bit_equal_to_zero + WriteFlagCavlc( + outputBitstreamPtr, + 0); + } + } + + // Byte Align the Bitstream + OutputBitstreamWrite( + outputBitstreamPtr, + 1, + 1); + + OutputBitstreamWriteAlignZero( + outputBitstreamPtr); + + return return_error; +} + +EB_ERRORTYPE EncodeMasteringDisplayColorVolumeSEI( + Bitstream_t *bitstreamPtr, + AppMasteringDisplayColorVolumeSei_t *masterDisplayPtr) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + EB_U32 payloadType = MASTERING_DISPLAY_INFO; + EB_U32 payloadSize = 0; + + OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)bitstreamPtr->outputBitstreamPtr; + + payloadSize = GetMasteringDisplayColorVolumeSEILength(); + + CodeNALUnitHeader( + outputBitstreamPtr, + NAL_UNIT_PREFIX_SEI, + 0); + + for (; payloadType >= 0xff; payloadType -= 0xff) { + OutputBitstreamWrite( + outputBitstreamPtr, + 0xff, + 8); + } + OutputBitstreamWrite( + outputBitstreamPtr, + payloadType, + 8); + + for (; payloadSize >= 0xff; payloadSize -= 0xff) { + OutputBitstreamWrite( + outputBitstreamPtr, + 0xff, + 8); + } + OutputBitstreamWrite( + outputBitstreamPtr, + payloadSize, + 8); + + // R, G, B Primaries + for (int i = 0; i < 3; i++) + { + WriteCodeCavlc( + outputBitstreamPtr, + masterDisplayPtr->displayPrimaryX[i], + 16); + WriteCodeCavlc( + outputBitstreamPtr, + masterDisplayPtr->displayPrimaryY[i], + 16); + } + + // White Point Co-ordinates + WriteCodeCavlc( + outputBitstreamPtr, + masterDisplayPtr->whitePointX, + 16); + WriteCodeCavlc( + outputBitstreamPtr, + masterDisplayPtr->whitePointY, + 16); + + // Min & max Luminance + WriteCodeCavlc( + outputBitstreamPtr, + masterDisplayPtr->maxDisplayMasteringLuminance, + 32); + WriteCodeCavlc( + outputBitstreamPtr, + masterDisplayPtr->minDisplayMasteringLuminance, + 32); + + if (outputBitstreamPtr->writtenBitsCount % 8 != 0) { + // bit_equal_to_one + WriteFlagCavlc( + outputBitstreamPtr, + 1); + + while (outputBitstreamPtr->writtenBitsCount % 8 != 0) { + // bit_equal_to_zero + WriteFlagCavlc( + outputBitstreamPtr, + 0); + } + } + + // Byte Align the Bitstream + OutputBitstreamWrite( + outputBitstreamPtr, + 1, + 1); + + OutputBitstreamWriteAlignZero( + outputBitstreamPtr); + + return return_error; +} + +EB_ERRORTYPE CodeDolbyVisionRpuMetadata( + Bitstream_t *bitstreamPtr, + PictureControlSet_t *pictureControlSetPtr) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)bitstreamPtr->outputBitstreamPtr; + EB_SEI_MESSAGE *rpu = &pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->dolbyVisionRpu; + + CodeNALUnitHeader( + outputBitstreamPtr, + NAL_UNIT_UNSPECIFIED_62, + 0); + + for (EB_U32 i = 0; i < rpu->payloadSize; i++) + WriteCodeCavlc(outputBitstreamPtr, rpu->payload[i], 8); + + return return_error; +} + EB_ERRORTYPE CopyRbspBitstreamToPayload( Bitstream_t *bitstreamPtr, EB_BYTE outputBuffer, EB_U32 *outputBufferIndex, EB_U32 *outputBufferSize, - EncodeContext_t *encodeContextPtr) + EncodeContext_t *encodeContextPtr, + NalUnitType naltype) { EB_ERRORTYPE return_error = EB_ErrorNone; OutputBitstreamUnit_t *outputBitstreamPtr = (OutputBitstreamUnit_t*)bitstreamPtr->outputBitstreamPtr; @@ -8700,7 +9133,8 @@ EB_ERRORTYPE CopyRbspBitstreamToPayload( outputBuffer, outputBufferIndex, outputBufferSize, - 0); + 0, + naltype); return return_error; } diff --git a/Source/Lib/Codec/EbEntropyCoding.h b/Source/Lib/Codec/EbEntropyCoding.h index 465a9d819..e5db618f6 100644 --- a/Source/Lib/Codec/EbEntropyCoding.h +++ b/Source/Lib/Codec/EbEntropyCoding.h @@ -190,6 +190,18 @@ extern EB_ERRORTYPE EncodeRecoveryPointSEI( Bitstream_t *bitstreamPtr, AppRecoveryPoint_t *recoveryPointSeiPtr); +extern EB_ERRORTYPE EncodeContentLightLevelSEI( + Bitstream_t *bitstreamPtr, + AppContentLightLevelSei_t *contentLightLevelPtr); + +EB_ERRORTYPE EncodeMasteringDisplayColorVolumeSEI( + Bitstream_t *bitstreamPtr, + AppMasteringDisplayColorVolumeSei_t *masterDisplayPtr); + +EB_ERRORTYPE CodeDolbyVisionRpuMetadata( + Bitstream_t *bitstreamPtr, + PictureControlSet_t *pictureControlSetPtr); + extern EB_ERRORTYPE CodeEndOfSequenceNalUnit( Bitstream_t *bitstreamPtr); @@ -198,7 +210,8 @@ extern EB_ERRORTYPE CopyRbspBitstreamToPayload( EB_BYTE outputBuffer, EB_U32 *outputBufferIndex, EB_U32 *outputBufferSize, - EncodeContext_t *encodeContextPtr); + EncodeContext_t *encodeContextPtr, + NalUnitType nalType); void EncodeQuantizedCoefficients_SSE2( CabacEncodeContext_t *cabacEncodeCtxPtr, diff --git a/Source/Lib/Codec/EbEntropyCodingUtil.h b/Source/Lib/Codec/EbEntropyCodingUtil.h index 66688bd5a..e14094564 100644 --- a/Source/Lib/Codec/EbEntropyCodingUtil.h +++ b/Source/Lib/Codec/EbEntropyCodingUtil.h @@ -52,7 +52,9 @@ enum COMPONENT_TYPE COMPONENT_CHROMA = 1, // chroma (Cb+Cr) COMPONENT_CHROMA_CB = 2, // chroma Cb COMPONENT_CHROMA_CR = 3, // chroma Cr - COMPONENT_ALL = 4, // Y+Cb+Cr + COMPONENT_CHROMA_CB2 = 4, // chroma 2nd CB for 422 + COMPONENT_CHROMA_CR2 = 5, // chroma 2nd CR for 422 + COMPONENT_ALL = 6, // Y+Cb+Cr COMPONENT_NONE = 15 }; @@ -184,6 +186,7 @@ typedef struct BacEncContext_s { typedef struct CabacEncodeContext_s { BacEncContext_t bacEncContext; ContextModelEncContext_t contextModelEncContext; + EB_COLOR_FORMAT colorFormat; } CabacEncodeContext_t; /************************************** diff --git a/Source/Lib/Codec/EbInitialRateControlProcess.c b/Source/Lib/Codec/EbInitialRateControlProcess.c index 2a4695997..a95fbadf7 100644 --- a/Source/Lib/Codec/EbInitialRateControlProcess.c +++ b/Source/Lib/Codec/EbInitialRateControlProcess.c @@ -342,15 +342,15 @@ void DetectGlobalMotion( pictureControlSetPtr->isPan = EB_FALSE; pictureControlSetPtr->isTilt = EB_FALSE; - - // If more than PAN_LCU_PERCENTAGE % of LCUs are PAN - if ((totalPanLcus * 100 / totalCheckedLcus) > PAN_LCU_PERCENTAGE) { - pictureControlSetPtr->isPan = EB_TRUE; - } - - if ((totalTiltLcus * 100 / totalCheckedLcus) > PAN_LCU_PERCENTAGE) { - pictureControlSetPtr->isTilt = EB_TRUE; - } + if (totalCheckedLcus > 0) { + // If more than PAN_LCU_PERCENTAGE % of LCUs are PAN + if ((totalPanLcus * 100 / totalCheckedLcus) > PAN_LCU_PERCENTAGE) { + pictureControlSetPtr->isPan = EB_TRUE; + } + if ((totalTiltLcus * 100 / totalCheckedLcus) > PAN_LCU_PERCENTAGE) { + pictureControlSetPtr->isTilt = EB_TRUE; + } + } } /************************************************ diff --git a/Source/Lib/Codec/EbInterPrediction.c b/Source/Lib/Codec/EbInterPrediction.c index 11cdf8e42..25364a86b 100644 --- a/Source/Lib/Codec/EbInterPrediction.c +++ b/Source/Lib/Codec/EbInterPrediction.c @@ -612,19 +612,21 @@ EB_ERRORTYPE Inter2Nx2NPuPredictionHevc( } else { - UniPredHevcInterpolationMd( - refPicList0, - refList0PosX, - refList0PosY, - puWidth, - puHeight, - candidateBufferPtr->predictionPtr, - puOriginIndex, - puChromaOriginIndex, - contextPtr->mcpContext->motionCompensationIntermediateResultBuf0, - contextPtr->mcpContext->TwoDInterpolationFirstPassFilterResultBuf, - EB_FALSE, - componentMask); + if (refPicList0) { + UniPredHevcInterpolationMd( + refPicList0, + refList0PosX, + refList0PosY, + puWidth, + puHeight, + candidateBufferPtr->predictionPtr, + puOriginIndex, + puChromaOriginIndex, + contextPtr->mcpContext->motionCompensationIntermediateResultBuf0, + contextPtr->mcpContext->TwoDInterpolationFirstPassFilterResultBuf, + EB_FALSE, + componentMask); + } } break; @@ -658,22 +660,21 @@ EB_ERRORTYPE Inter2Nx2NPuPredictionHevc( } else { - - UniPredHevcInterpolationMd( - - refPicList1, - refList1PosX, - refList1PosY, - puWidth, - puHeight, - candidateBufferPtr->predictionPtr, - puOriginIndex, - puChromaOriginIndex, - contextPtr->mcpContext->motionCompensationIntermediateResultBuf0, - contextPtr->mcpContext->TwoDInterpolationFirstPassFilterResultBuf, - EB_FALSE, - componentMask); - + if (refPicList1) { + UniPredHevcInterpolationMd( + refPicList1, + refList1PosX, + refList1PosY, + puWidth, + puHeight, + candidateBufferPtr->predictionPtr, + puOriginIndex, + puChromaOriginIndex, + contextPtr->mcpContext->motionCompensationIntermediateResultBuf0, + contextPtr->mcpContext->TwoDInterpolationFirstPassFilterResultBuf, + EB_FALSE, + componentMask); + } } break; @@ -722,24 +723,25 @@ EB_ERRORTYPE Inter2Nx2NPuPredictionHevc( } else { - - BiPredHevcInterpolationMd( - refPicList0, - refPicList1, - refList0PosX, - refList0PosY, - refList1PosX, - refList1PosY, - puWidth, - puHeight, - candidateBufferPtr->predictionPtr, - puOriginIndex, - puChromaOriginIndex, - contextPtr->mcpContext->motionCompensationIntermediateResultBuf0, - contextPtr->mcpContext->motionCompensationIntermediateResultBuf1, - contextPtr->mcpContext->TwoDInterpolationFirstPassFilterResultBuf, - EB_FALSE, - componentMask); + if (refPicList0 && refPicList1) { + BiPredHevcInterpolationMd( + refPicList0, + refPicList1, + refList0PosX, + refList0PosY, + refList1PosX, + refList1PosY, + puWidth, + puHeight, + candidateBufferPtr->predictionPtr, + puOriginIndex, + puChromaOriginIndex, + contextPtr->mcpContext->motionCompensationIntermediateResultBuf0, + contextPtr->mcpContext->motionCompensationIntermediateResultBuf1, + contextPtr->mcpContext->TwoDInterpolationFirstPassFilterResultBuf, + EB_FALSE, + componentMask); + } } break; @@ -777,8 +779,12 @@ EB_ERRORTYPE EncodePassInterPrediction( EB_U16 refList1PosX = 0; EB_U16 refList1PosY = 0; + EB_COLOR_FORMAT colorFormat=predictionPtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + EB_U32 puOriginIndex = ((predictionPtr->originY +puOriginY) * predictionPtr->strideY) + (predictionPtr->originX+puOriginX); - EB_U32 puChromaOriginIndex = (((predictionPtr->originY+puOriginY) * predictionPtr->strideCb) + (predictionPtr->originX+puOriginX)) >> 1; + EB_U32 puChromaOriginIndex = (((predictionPtr->originY+puOriginY) * predictionPtr->strideCb) >> subHeightCMinus1) + ((predictionPtr->originX+puOriginX) >> subWidthCMinus1); SequenceControlSet_t *sequenceControlSetPtr = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; EncodeContext_t *encodeContextPtr = sequenceControlSetPtr->encodeContextPtr; @@ -939,16 +945,20 @@ EB_ERRORTYPE EncodePassInterPrediction16bit( EB_U16 refList0PosY = 0; EB_U16 refList1PosX = 0; EB_U16 refList1PosY = 0; + const EB_COLOR_FORMAT colorFormat = predictionPtr->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; - EB_U32 puOriginIndex = ((predictionPtr->originY+puOriginY) * predictionPtr->strideY) + (predictionPtr->originX+puOriginX); - EB_U32 puChromaOriginIndex = (((predictionPtr->originY+puOriginY) * predictionPtr->strideCb) + (predictionPtr->originX+puOriginX)) >> 1; - SequenceControlSet_t *sequenceControlSetPtr = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; - EncodeContext_t *encodeContextPtr = sequenceControlSetPtr->encodeContextPtr; + EB_U32 puOriginIndex = (predictionPtr->originX + puOriginX) + + ((predictionPtr->originY+puOriginY) * predictionPtr->strideY); + EB_U32 puChromaOriginIndex = ((predictionPtr->originX + puOriginX) >> subWidthCMinus1) + + (((predictionPtr->originY+puOriginY) * predictionPtr->strideCb) >> subHeightCMinus1); + SequenceControlSet_t *sequenceControlSetPtr = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; + EncodeContext_t *encodeContextPtr = sequenceControlSetPtr->encodeContextPtr; // Setup List 0 if (mvUnit->predDirection == UNI_PRED_LIST_0 || mvUnit->predDirection == BI_PRED) { - referenceObject = (EbReferenceObject_t*)pictureControlSetPtr->refPicPtrArray[REF_LIST_0]->objectPtr; refPicList0 = (EbPictureBufferDesc_t*)referenceObject->referencePicture16bit; @@ -976,8 +986,8 @@ EB_ERRORTYPE EncodePassInterPrediction16bit( EB_ENC_INTER_INVLD_MCP_ERROR); EB_U32 lumaOffSet = ((refList0PosX >> 2) - 4) * 2 + ((refList0PosY >> 2) - 4) * 2 * refPicList0->strideY; //refPicList0->originX + refPicList0->originY*refPicList0->strideY; // - EB_U32 cbOffset = ((refList0PosX >> 3) - 2) * 2 + ((refList0PosY >> 3) - 2) * 2 * refPicList0->strideCb; - EB_U32 crOffset = ((refList0PosX >> 3) - 2) * 2 + ((refList0PosY >> 3) - 2) * 2 * refPicList0->strideCr; + EB_U32 cbOffset = ((refList0PosX >> (2 + subWidthCMinus1)) - 2) * 2 + ((refList0PosY >> (2 + subHeightCMinus1)) - 2) * 2 * refPicList0->strideCb; //Jing:double check for 444 + EB_U32 crOffset = ((refList0PosX >> (2 + subWidthCMinus1)) - 2) * 2 + ((refList0PosY >> (2 + subHeightCMinus1)) - 2) * 2 * refPicList0->strideCr; //EB_U8 verticalIdx; mcpContext->localReferenceBlockL0->bufferY = refPicList0->bufferY + lumaOffSet; @@ -986,10 +996,8 @@ EB_ERRORTYPE EncodePassInterPrediction16bit( mcpContext->localReferenceBlockL0->strideY = refPicList0->strideY; mcpContext->localReferenceBlockL0->strideCb = refPicList0->strideCb; mcpContext->localReferenceBlockL0->strideCr = refPicList0->strideCr; - } - // Setup List 1 if (mvUnit->predDirection == UNI_PRED_LIST_1 || mvUnit->predDirection == BI_PRED) { @@ -1022,8 +1030,8 @@ EB_ERRORTYPE EncodePassInterPrediction16bit( //mcpContext->localReferenceBlockL1->bufferCb = refPicList1->bufferCb + ((refList1PosX >> 3) - 2) * 2 + ((refList1PosY >> 3) - 2) * 2 * refPicList1->strideCb; //mcpContext->localReferenceBlockL1->bufferCr = refPicList1->bufferCr + ((refList1PosX >> 3) - 2) * 2 + ((refList1PosY >> 3) - 2) * 2 * refPicList1->strideCr; EB_U32 lumaOffSet = ((refList1PosX >> 2) - 4) * 2 + ((refList1PosY >> 2) - 4) * 2 * refPicList1->strideY; //refPicList0->originX + refPicList0->originY*refPicList0->strideY; // - EB_U32 cbOffset = ((refList1PosX >> 3) - 2) * 2 + ((refList1PosY >> 3) - 2) * 2 * refPicList1->strideCb; - EB_U32 crOffset = ((refList1PosX >> 3) - 2) * 2 + ((refList1PosY >> 3) - 2) * 2 * refPicList1->strideCr; + EB_U32 cbOffset = ((refList1PosX >> (2 + subWidthCMinus1)) - 2) * 2 + ((refList1PosY >> (2 + subHeightCMinus1)) - 2) * 2 * refPicList1->strideCb; + EB_U32 crOffset = ((refList1PosX >> (2 + subWidthCMinus1)) - 2) * 2 + ((refList1PosY >> (2 + subHeightCMinus1)) - 2) * 2 * refPicList1->strideCr; //EB_U8 verticalIdx; mcpContext->localReferenceBlockL1->bufferY = refPicList1->bufferY + lumaOffSet; @@ -1035,15 +1043,10 @@ EB_ERRORTYPE EncodePassInterPrediction16bit( mcpContext->localReferenceBlockL1->strideCr = refPicList1->strideCr; } - - - switch(mvUnit->predDirection) { case UNI_PRED_LIST_0: - - //CHKN load the needed FP block from reference frame in a local buffer, we load 4 pixel more in each direction to be used for interpolation. //TODO: the max area needed for interpolation is 4, we could optimize this later on a case by case basis //size of the buffer would be (64+4+4)x(64+4+4) diff --git a/Source/Lib/Codec/EbIntraPrediction.c b/Source/Lib/Codec/EbIntraPrediction.c index 25b3a3ca2..2050bfbca 100644 --- a/Source/Lib/Codec/EbIntraPrediction.c +++ b/Source/Lib/Codec/EbIntraPrediction.c @@ -5,7 +5,6 @@ #include #include - #include "EbIntraPrediction.h" #include "EbUtility.h" #include "EbAvailability.h" @@ -74,8 +73,7 @@ static const EB_S32 intraLumaFilterTable[] = { /********************************************** * Intra Reference Samples Ctor **********************************************/ -EB_ERRORTYPE IntraReferenceSamplesCtor( - IntraReferenceSamples_t **contextDblPtr) +EB_ERRORTYPE IntraReferenceSamplesCtor(IntraReferenceSamples_t **contextDblPtr, EB_COLOR_FORMAT colorFormat) { IntraReferenceSamples_t *contextPtr; EB_MALLOC(IntraReferenceSamples_t*, contextPtr, sizeof(IntraReferenceSamples_t), EB_N_PTR); @@ -97,10 +95,24 @@ EB_ERRORTYPE IntraReferenceSamplesCtor( EB_MALLOC(EB_U8*, contextPtr->crIntraReferenceArrayReverse, sizeof(EB_U8) * (4 * MAX_LCU_SIZE + 2), EB_N_PTR); - contextPtr->yIntraReferenceArrayReverse++; + contextPtr->yIntraReferenceArrayReverse++; //Jing: mem leak? bad here contextPtr->yIntraFilteredReferenceArrayReverse++; - contextPtr->cbIntraReferenceArrayReverse++; - contextPtr->crIntraReferenceArrayReverse++; + contextPtr->cbIntraReferenceArrayReverse++; + contextPtr->crIntraReferenceArrayReverse++; + + if (colorFormat == EB_YUV444) { + EB_MALLOC(EB_U8*, contextPtr->cbIntraFilteredReferenceArray, sizeof(EB_U8) * (4 * MAX_LCU_SIZE + 1), EB_N_PTR); + EB_MALLOC(EB_U8*, contextPtr->crIntraFilteredReferenceArray, sizeof(EB_U8) * (4 * MAX_LCU_SIZE + 1), EB_N_PTR); + EB_MALLOC(EB_U8*, contextPtr->cbIntraFilteredReferenceArrayReverse, sizeof(EB_U8) * (4 * MAX_LCU_SIZE + 2), EB_N_PTR); + EB_MALLOC(EB_U8*, contextPtr->crIntraFilteredReferenceArrayReverse, sizeof(EB_U8) * (4 * MAX_LCU_SIZE + 2), EB_N_PTR); + contextPtr->cbIntraFilteredReferenceArrayReverse++; + contextPtr->crIntraFilteredReferenceArrayReverse++; + } else { + contextPtr->cbIntraFilteredReferenceArray = NULL; + contextPtr->crIntraFilteredReferenceArray = NULL; + contextPtr->cbIntraFilteredReferenceArrayReverse = NULL; + contextPtr->crIntraFilteredReferenceArrayReverse = NULL; + } return EB_ErrorNone; } @@ -109,7 +121,8 @@ EB_ERRORTYPE IntraReferenceSamplesCtor( * Intra Reference Samples Ctor **********************************************/ EB_ERRORTYPE IntraReference16bitSamplesCtor( - IntraReference16bitSamples_t **contextDblPtr) + IntraReference16bitSamples_t **contextDblPtr, + EB_COLOR_FORMAT colorFormat) { IntraReference16bitSamples_t *contextPtr; EB_MALLOC(IntraReference16bitSamples_t*, contextPtr, sizeof(IntraReference16bitSamples_t), EB_N_PTR); @@ -131,10 +144,24 @@ EB_ERRORTYPE IntraReference16bitSamplesCtor( EB_MALLOC(EB_U16*, contextPtr->crIntraReferenceArrayReverse, sizeof(EB_U16) * (4 * MAX_LCU_SIZE + 2), EB_N_PTR); - contextPtr->yIntraReferenceArrayReverse++; + contextPtr->yIntraReferenceArrayReverse++; contextPtr->yIntraFilteredReferenceArrayReverse++; - contextPtr->cbIntraReferenceArrayReverse++; - contextPtr->crIntraReferenceArrayReverse++; + contextPtr->cbIntraReferenceArrayReverse++; + contextPtr->crIntraReferenceArrayReverse++; + + if (colorFormat == EB_YUV444) { + EB_MALLOC(EB_U16*, contextPtr->cbIntraFilteredReferenceArray, sizeof(EB_U16) * (4 * MAX_LCU_SIZE + 1), EB_N_PTR); + EB_MALLOC(EB_U16*, contextPtr->crIntraFilteredReferenceArray, sizeof(EB_U16) * (4 * MAX_LCU_SIZE + 1), EB_N_PTR); + EB_MALLOC(EB_U16*, contextPtr->cbIntraFilteredReferenceArrayReverse, sizeof(EB_U16) * (4 * MAX_LCU_SIZE + 2), EB_N_PTR); + EB_MALLOC(EB_U16*, contextPtr->crIntraFilteredReferenceArrayReverse, sizeof(EB_U16) * (4 * MAX_LCU_SIZE + 2), EB_N_PTR); + contextPtr->cbIntraFilteredReferenceArrayReverse++; + contextPtr->crIntraFilteredReferenceArrayReverse++; + } else { + contextPtr->cbIntraFilteredReferenceArray = NULL; + contextPtr->crIntraFilteredReferenceArray = NULL; + contextPtr->cbIntraFilteredReferenceArrayReverse = NULL; + contextPtr->crIntraFilteredReferenceArrayReverse = NULL; + } return EB_ErrorNone; } @@ -150,12 +177,14 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( EB_U32 originX, EB_U32 originY, EB_U32 size, + EB_U32 lcuSize, EB_U32 cuDepth, NeighborArrayUnit_t *modeTypeNeighborArray, NeighborArrayUnit_t *lumaReconNeighborArray, NeighborArrayUnit_t *cbReconNeighborArray, NeighborArrayUnit_t *crReconNeighborArray, void *refWrapperPtr, + EB_COLOR_FORMAT colorFormat, EB_BOOL pictureLeftBoundary, EB_BOOL pictureTopBoundary, EB_BOOL pictureRightBoundary) @@ -175,19 +204,22 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( const EB_U32 sizeLog2 = Log2f(size); const EB_U32 puChromaSize = size >> 1; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + EB_U32 yLoadCounter; EB_U8 *sampleReadLoc; EB_U8 *sampleWriteLoc; EB_U8 *sampleWriteLocCb; EB_U8 *sampleWriteLocCr; - EB_U32 i; + EB_U32 i; EB_U8 *sampleWriteLocFilt; // This internal LCU availability check will be performed for top right and bottom left neighbors only. // It is always set to true for top, left and top left neighbors EB_BOOL bottomLeftAvailabilityPreCalc; EB_BOOL topRightAvailabilityPreCalc; - const EB_U32 cuIndex = ((originY & 63) >> sizeLog2) * (1 << cuDepth) + ((originX & 63) >> sizeLog2); + const EB_U32 cuIndex = ((originY & (lcuSize-1)) >> sizeLog2) * (1 << cuDepth) + ((originX & (lcuSize-1)) >> sizeLog2); EB_U32 blockIndex; // 4x4 (Min PU size) granularity EB_BOOL neighborAvailable; @@ -206,10 +238,10 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( EB_U8 crPadValue = 0; EB_U8 *lumaWritePtr = yBorder; - EB_U8 *cbWritePtr = cbBorder; - EB_U8 *crWritePtr = crBorder; + EB_U8 *cbWritePtr = cbBorder; + EB_U8 *crWritePtr = crBorder; - EB_U32 writeCountLuma; + EB_U32 writeCountLuma; EB_U32 writeCountChroma; // Neighbor Arrays @@ -225,7 +257,7 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( EB_U8 *leftCbReconNeighborArray = cbReconNeighborArray->leftArray; EB_U8 *topLeftCbReconNeighborArray = cbReconNeighborArray->topLeftArray; EB_U8 *topCrReconNeighborArray = crReconNeighborArray->topArray; - EB_U8 *leftCrReconNeighborArray = crReconNeighborArray->leftArray; + EB_U8 *leftCrReconNeighborArray = crReconNeighborArray->leftArray; EB_U8 *topLeftCrReconNeighborArray = crReconNeighborArray->topLeftArray; // The Generate Intra Reference sample process is a single pass algorithm @@ -280,7 +312,7 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( // Pre-calculate bottom left availability bottomLeftAvailabilityPreCalc = isBottomLeftAvailable( cuDepth, - cuIndex); + cuIndex); // Pre-calculate top right availability topRightAvailabilityPreCalc = isUpperRightAvailable( @@ -314,9 +346,8 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( // Set pad value (end of block) lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; - cbPadValue = leftCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - crPadValue = leftCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - + cbPadValue = leftCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> subHeightCMinus1]; + crPadValue = leftCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> subHeightCMinus1]; } else { ++blockIndex; @@ -343,9 +374,8 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( // Set pad value (end of block) lumaPadValue = topLeftLumaReconNeighborArray[reconArrayIndex]; - cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; - + cbPadValue = topLeftCbReconNeighborArray[((MAX_PICTURE_HEIGHT_SIZE- originY)>>subHeightCMinus1) + (originX>>subWidthCMinus1)]; + crPadValue = topLeftCrReconNeighborArray[((MAX_PICTURE_HEIGHT_SIZE- originY)>>subHeightCMinus1) + (originX>>subWidthCMinus1)]; } else { ++blockIndex; @@ -374,9 +404,8 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( // Set pad value (beginning of block) lumaPadValue = topLumaReconNeighborArray[reconArrayIndex]; - cbPadValue = topCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topCrReconNeighborArray[reconArrayIndex >> 1]; - + cbPadValue = topCbReconNeighborArray[reconArrayIndex >> subWidthCMinus1]; + crPadValue = topCrReconNeighborArray[reconArrayIndex >> subWidthCMinus1]; } else { ++blockIndex; @@ -389,7 +418,7 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( if (blockIndex == topBlockEnd && neighborAvailable == EB_FALSE) { writeCountLuma = 4*size + 1; - writeCountChroma = 2*size + 1; + writeCountChroma = 4*puChromaSize + 1; // Write Midrange EB_MEMSET(lumaWritePtr, MIDRANGE_VALUE_8BIT, writeCountLuma); @@ -445,16 +474,15 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( lumaWritePtr[2] = leftLumaReconNeighborArray[reconArrayIndex + 1]; lumaWritePtr[3] = leftLumaReconNeighborArray[reconArrayIndex + 0]; - cbWritePtr[0] = leftCbReconNeighborArray[(reconArrayIndex >> 1) + 1]; - cbWritePtr[1] = leftCbReconNeighborArray[(reconArrayIndex >> 1) + 0]; - - crWritePtr[0] = leftCrReconNeighborArray[(reconArrayIndex >> 1) + 1]; - crWritePtr[1] = leftCrReconNeighborArray[(reconArrayIndex >> 1) + 0]; + cbWritePtr[0] = leftCbReconNeighborArray[(reconArrayIndex >> subHeightCMinus1) + 1]; + cbWritePtr[1] = leftCbReconNeighborArray[(reconArrayIndex >> subHeightCMinus1) + 0]; + crWritePtr[0] = leftCrReconNeighborArray[(reconArrayIndex >> subHeightCMinus1) + 1]; + crWritePtr[1] = leftCrReconNeighborArray[(reconArrayIndex >> subHeightCMinus1) + 0]; // Set pad value (beginning of block) - lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex]; - cbPadValue = leftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = leftCrReconNeighborArray[reconArrayIndex >> 1]; + lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex]; + cbPadValue = leftCbReconNeighborArray[reconArrayIndex >> subHeightCMinus1]; + crPadValue = leftCrReconNeighborArray[reconArrayIndex >> subHeightCMinus1]; } else { @@ -462,12 +490,11 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( EB_MEMSET(lumaWritePtr, lumaPadValue, MIN_PU_SIZE); EB_MEMSET(cbWritePtr, cbPadValue, MIN_PU_SIZE >> 1); EB_MEMSET(crWritePtr, crPadValue, MIN_PU_SIZE >> 1); - } lumaWritePtr += MIN_PU_SIZE; - cbWritePtr += MIN_PU_SIZE >> 1; - crWritePtr += MIN_PU_SIZE >> 1; + cbWritePtr += MIN_PU_SIZE >> 1;//Jing: works fine for both 420 and 422 + crWritePtr += MIN_PU_SIZE >> 1; ++blockIndex; reconArrayIndex -= MIN_PU_SIZE; @@ -492,25 +519,25 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( // Copy sample *lumaWritePtr = topLeftLumaReconNeighborArray[reconArrayIndex]; - *cbWritePtr = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - *crWritePtr = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; + *cbWritePtr = topLeftCbReconNeighborArray[((MAX_PICTURE_HEIGHT_SIZE- originY)>>subHeightCMinus1) + (originX>>subWidthCMinus1)]; + *crWritePtr = topLeftCrReconNeighborArray[((MAX_PICTURE_HEIGHT_SIZE- originY)>>subHeightCMinus1) + (originX>>subWidthCMinus1)]; // Set Pad Value lumaPadValue = topLeftLumaReconNeighborArray[reconArrayIndex]; - cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; + cbPadValue = topLeftCbReconNeighborArray[((MAX_PICTURE_HEIGHT_SIZE- originY)>>subHeightCMinus1) + (originX>>subWidthCMinus1)]; + crPadValue = topLeftCrReconNeighborArray[((MAX_PICTURE_HEIGHT_SIZE- originY)>>subHeightCMinus1) + (originX>>subWidthCMinus1)]; } else { // Copy pad value *lumaWritePtr = lumaPadValue; - *cbWritePtr = cbPadValue; - *crWritePtr = crPadValue; + *cbWritePtr = cbPadValue; + *crWritePtr = crPadValue; } ++lumaWritePtr; - ++cbWritePtr; - ++crWritePtr; + ++cbWritePtr; + ++crWritePtr; ++blockIndex; } @@ -535,28 +562,26 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( if(neighborAvailable == EB_TRUE) { - // Copy samples in reverse order EB_MEMCPY(lumaWritePtr, &topLumaReconNeighborArray[reconArrayIndex], MIN_PU_SIZE); - EB_MEMCPY(cbWritePtr, &topCbReconNeighborArray[reconArrayIndex >> 1], MIN_PU_SIZE >> 1); - EB_MEMCPY(crWritePtr, &topCrReconNeighborArray[reconArrayIndex >> 1], MIN_PU_SIZE >> 1); + EB_MEMCPY(cbWritePtr, &topCbReconNeighborArray[reconArrayIndex >> subWidthCMinus1], MIN_PU_SIZE >> subWidthCMinus1); + EB_MEMCPY(crWritePtr, &topCrReconNeighborArray[reconArrayIndex >> subWidthCMinus1], MIN_PU_SIZE >> subWidthCMinus1); // Set pad value (end of block) - lumaPadValue = topLumaReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; - cbPadValue = topCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - crPadValue = topCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - + lumaPadValue = topLumaReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; + cbPadValue = topCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> subWidthCMinus1]; + crPadValue = topCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> subWidthCMinus1]; } else { // Copy pad value EB_MEMSET(lumaWritePtr, lumaPadValue, MIN_PU_SIZE); - EB_MEMSET(cbWritePtr, cbPadValue, MIN_PU_SIZE >> 1); - EB_MEMSET(crWritePtr, crPadValue, MIN_PU_SIZE >> 1); + EB_MEMSET(cbWritePtr, cbPadValue, MIN_PU_SIZE >> subWidthCMinus1); + EB_MEMSET(crWritePtr, crPadValue, MIN_PU_SIZE >> subWidthCMinus1); } lumaWritePtr += MIN_PU_SIZE; - cbWritePtr += MIN_PU_SIZE >> 1; - crWritePtr += MIN_PU_SIZE >> 1; + cbWritePtr += MIN_PU_SIZE >> subWidthCMinus1; + crWritePtr += MIN_PU_SIZE >> subWidthCMinus1; ++blockIndex; reconArrayIndex += MIN_PU_SIZE; @@ -575,10 +600,10 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( EB_U32 index; EB_U32 bottomLeftSample = sampleReadLoc[0]; EB_U32 topLeftSample = sampleReadLoc[(size << 1)]; - EB_U32 topRightSample = sampleReadLoc[(size << 2)]; + EB_U32 topRightSample = sampleReadLoc[(size << 2)]; EB_U32 twicePuSize = (size << 1); - EB_BOOL bilinearLeft = (ABS((EB_S32)bottomLeftSample + (EB_S32)topLeftSample - 2 * sampleReadLoc[size]) < SMOOTHING_THRESHOLD) ? EB_TRUE : EB_FALSE; + EB_BOOL bilinearLeft = (ABS((EB_S32)bottomLeftSample + (EB_S32)topLeftSample - 2 * sampleReadLoc[size]) < SMOOTHING_THRESHOLD) ? EB_TRUE : EB_FALSE; EB_BOOL bilinearTop = (ABS((EB_S32)topLeftSample + (EB_S32)topRightSample - 2 * sampleReadLoc[size+(size << 1)]) < SMOOTHING_THRESHOLD) ? EB_TRUE : EB_FALSE; if(size >= STRONG_INTRA_SMOOTHING_BLOCKSIZE && bilinearLeft && bilinearTop) { @@ -588,7 +613,7 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( sampleWriteLoc[(size << 2)] = sampleReadLoc[(size << 2)]; for(index = 1; index < twicePuSize; index++) { - sampleWriteLoc[index] = (EB_U8)(((twicePuSize - index) * bottomLeftSample + index * topLeftSample + size) >> filterShift); + sampleWriteLoc[index] = (EB_U8)(((twicePuSize - index) * bottomLeftSample + index * topLeftSample + size) >> filterShift); sampleWriteLoc[twicePuSize+index] = (EB_U8)(((twicePuSize - index) * topLeftSample + index * topRightSample + size) >> filterShift); } } @@ -653,13 +678,13 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( EB_MEMCPY(yBorderReverse + (size<<1), yBorder + (size<<1), (size<<1)+1); EB_MEMCPY(yBorderFiltReverse + (size<<1), yBorderFilt + (size<<1), (size<<1)+1); - sampleWriteLoc = yBorderReverse + (size<<1) - 1 ; - sampleWriteLocFilt = yBorderFiltReverse + (size<<1) - 1 ; + sampleWriteLoc = yBorderReverse + (size<<1) - 1 ; + sampleWriteLocFilt = yBorderFiltReverse + (size<<1) - 1 ; for(i=0; i<(size<<1) ;i++){ *sampleWriteLoc = yBorder[i]; *sampleWriteLocFilt = yBorderFilt[i] ; - sampleWriteLoc--; + sampleWriteLoc--; sampleWriteLocFilt--; } @@ -668,14 +693,14 @@ EB_ERRORTYPE GenerateIntraReferenceSamplesEncodePass( EB_MEMCPY(crBorderReverse + (puChromaSize<<1), crBorder + (puChromaSize<<1), (puChromaSize<<1)+1); sampleWriteLocCb = cbBorderReverse + (puChromaSize<<1) - 1 ; - sampleWriteLocCr = crBorderReverse + (puChromaSize<<1) - 1 ; + sampleWriteLocCr = crBorderReverse + (puChromaSize<<1) - 1 ; for(i=0; i<(puChromaSize<<1) ;i++){ *sampleWriteLocCb = cbBorder[i]; - *sampleWriteLocCr = crBorder[i]; - sampleWriteLocCb--; - sampleWriteLocCr--; + *sampleWriteLocCr = crBorder[i]; + sampleWriteLocCb--; + sampleWriteLocCr--; } return return_error; @@ -714,7 +739,7 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( EB_U32 yLoadCounter; EB_U8 *sampleReadLoc; EB_U8 *sampleWriteLoc; - EB_U32 i; + EB_U32 i; EB_U8 *sampleWriteLocFilt; // This internal LCU availability check will be performed for top right and bottom left neighbors only. @@ -742,7 +767,7 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( EB_U8 *lumaWritePtr = yBorder; - EB_U32 writeCountLuma; + EB_U32 writeCountLuma; // Neighbor Arrays EB_U32 topModeNeighborArraySize = modeTypeNeighborArray->topArraySize; @@ -809,7 +834,7 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( // Pre-calculate bottom left availability bottomLeftAvailabilityPreCalc = isBottomLeftAvailable( partitionDepth, - cuIndex); + cuIndex); // Pre-calculate top right availability topRightAvailabilityPreCalc = isUpperRightAvailable( @@ -825,7 +850,6 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( reconArrayIndex = originY + 2 * size - MIN_PU_SIZE; neighborAvailable = EB_FALSE; while(blockIndex < leftBlockEnd && neighborAvailable == EB_FALSE) { - modeArrayIndex = GetNeighborArrayUnitLeftIndex( modeTypeNeighborArray, reconArrayIndex); @@ -898,7 +922,7 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( if(neighborAvailable == EB_TRUE) { // Set pad value (beginning of block) - lumaPadValue = topLumaReconNeighborArray[reconArrayIndex]; + lumaPadValue = topLumaReconNeighborArray[reconArrayIndex]; } else { ++blockIndex; @@ -957,7 +981,7 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( lumaWritePtr[3] = leftLumaReconNeighborArray[reconArrayIndex + 0]; // Set pad value (beginning of block) - lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex]; + lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex]; } else { @@ -1058,10 +1082,10 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( EB_U32 index; EB_U32 bottomLeftSample = sampleReadLoc[0]; EB_U32 topLeftSample = sampleReadLoc[(size << 1)]; - EB_U32 topRightSample = sampleReadLoc[(size << 2)]; + EB_U32 topRightSample = sampleReadLoc[(size << 2)]; EB_U32 twicePuSize = (size << 1); - EB_BOOL bilinearLeft = (ABS((EB_S32)bottomLeftSample + (EB_S32)topLeftSample - 2 * sampleReadLoc[size]) < SMOOTHING_THRESHOLD) ? EB_TRUE : EB_FALSE; + EB_BOOL bilinearLeft = (ABS((EB_S32)bottomLeftSample + (EB_S32)topLeftSample - 2 * sampleReadLoc[size]) < SMOOTHING_THRESHOLD) ? EB_TRUE : EB_FALSE; EB_BOOL bilinearTop = (ABS((EB_S32)topLeftSample + (EB_S32)topRightSample - 2 * sampleReadLoc[size+(size << 1)]) < SMOOTHING_THRESHOLD) ? EB_TRUE : EB_FALSE; if(size >= STRONG_INTRA_SMOOTHING_BLOCKSIZE && bilinearLeft && bilinearTop) { @@ -1071,7 +1095,7 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( sampleWriteLoc[(size << 2)] = sampleReadLoc[(size << 2)]; for(index = 1; index < twicePuSize; index++) { - sampleWriteLoc[index] = (EB_U8)(((twicePuSize - index) * bottomLeftSample + index * topLeftSample + size) >> filterShift); + sampleWriteLoc[index] = (EB_U8)(((twicePuSize - index) * bottomLeftSample + index * topLeftSample + size) >> filterShift); sampleWriteLoc[twicePuSize+index] = (EB_U8)(((twicePuSize - index) * topLeftSample + index * topRightSample + size) >> filterShift); } } @@ -1129,13 +1153,13 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( EB_MEMCPY(yBorderReverse + (size<<1), yBorder + (size<<1), (size<<1)+1); EB_MEMCPY(yBorderFiltReverse + (size<<1), yBorderFilt + (size<<1), (size<<1)+1); - sampleWriteLoc = yBorderReverse + (size<<1) - 1 ; - sampleWriteLocFilt = yBorderFiltReverse + (size<<1) - 1 ; + sampleWriteLoc = yBorderReverse + (size<<1) - 1 ; + sampleWriteLocFilt = yBorderFiltReverse + (size<<1) - 1 ; for(i=0; i<(size<<1) ;i++){ *sampleWriteLoc = yBorder[i]; *sampleWriteLocFilt = yBorderFilt[i] ; - sampleWriteLoc--; + sampleWriteLoc--; sampleWriteLocFilt--; } @@ -1148,8 +1172,8 @@ EB_ERRORTYPE GenerateLumaIntraReferenceSamplesEncodePass( EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( EB_BOOL constrainedIntraFlag, //input parameter, indicates if constrained intra is switched on/off EB_BOOL strongIntraSmoothingFlag, - EB_U32 originX, - EB_U32 originY, + EB_U32 cuOriginX, + EB_U32 cuOriginY, EB_U32 size, EB_U32 lcuSize, EB_U32 cuDepth, @@ -1158,53 +1182,68 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( NeighborArrayUnit_t *cbReconNeighborArray, NeighborArrayUnit_t *crReconNeighborArray, void *refWrapperPtr, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, EB_BOOL pictureLeftBoundary, EB_BOOL pictureTopBoundary, EB_BOOL pictureRightBoundary) { - EB_ERRORTYPE return_error = EB_ErrorNone; - IntraReferenceSamples_t *intraRefPtr = (IntraReferenceSamples_t *)refWrapperPtr; - - EB_U8 *cbBorder = intraRefPtr->cbIntraReferenceArray; - EB_U8 *crBorder = intraRefPtr->crIntraReferenceArray; - - EB_U8 *cbBorderReverse = intraRefPtr->cbIntraReferenceArrayReverse; - EB_U8 *crBorderReverse = intraRefPtr->crIntraReferenceArrayReverse; + EB_ERRORTYPE return_error = EB_ErrorNone; + IntraReferenceSamples_t *intraRefPtr = (IntraReferenceSamples_t*)refWrapperPtr; + + EB_U8 *cbBorder = intraRefPtr->cbIntraReferenceArray; + EB_U8 *cbBorderFilt = intraRefPtr->cbIntraFilteredReferenceArray; + EB_U8 *crBorder = intraRefPtr->crIntraReferenceArray; + EB_U8 *crBorderFilt = intraRefPtr->crIntraFilteredReferenceArray; + + EB_U8 *cbBorderReverse = intraRefPtr->cbIntraReferenceArrayReverse; + EB_U8 *cbBorderFiltReverse = intraRefPtr->cbIntraFilteredReferenceArrayReverse; + EB_U8 *crBorderReverse = intraRefPtr->crIntraReferenceArrayReverse; + EB_U8 *crBorderFiltReverse = intraRefPtr->crIntraFilteredReferenceArrayReverse; - const EB_U32 sizeLog2 = Log2f(size); - const EB_U32 puChromaSize = size >> 1; - - EB_U8 *sampleWriteLocCb; - EB_U8 *sampleWriteLocCr; - EB_U32 i; + const EB_U32 sizeLog2 = Log2f(size); + const EB_U32 puChromaSize = size >> ((colorFormat==EB_YUV420 || colorFormat==EB_YUV422) ? 1 : 0); + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + const EB_U32 chromaOffset = (colorFormat == EB_YUV422 && secondChroma) ? puChromaSize : 0; + + EB_U32 i; + EB_U32 yLoadCounter; + EB_U8 *sampleReadLocCb; + EB_U8 *sampleWriteLocCb; + EB_U8 *sampleReadLocCr; + EB_U8 *sampleWriteLocCr; + EB_U8 *sampleWriteLocCbFilt; + EB_U8 *sampleWriteLocCrFilt; // This internal LCU availability check will be performed for top right and bottom left neighbors only. // It is always set to true for top, left and top left neighbors - EB_BOOL bottomLeftAvailabilityPreCalc; - EB_BOOL topRightAvailabilityPreCalc; + EB_BOOL bottomLeftAvailabilityPreCalc; + EB_BOOL topRightAvailabilityPreCalc; - const EB_U32 cuIndex = ((originY & (lcuSize - 1)) >> sizeLog2) * (1 << cuDepth) + ((originX & (lcuSize - 1)) >> sizeLog2); + const EB_U32 partitionDepth = (size == MIN_PU_SIZE) ? cuDepth + 1 : cuDepth; + const EB_U32 cuIndex = ((cuOriginY & (lcuSize - 1)) >> sizeLog2) * (1 << partitionDepth) + ((cuOriginX & (lcuSize - 1)) >> sizeLog2); - EB_U32 blockIndex; // 4x4 (Min PU size) granularity - EB_BOOL neighborAvailable; + EB_U32 blockIndex; // 4x4 (Min PU size) granularity + EB_BOOL neighborAvailable; - const EB_U32 bottomLeftEnd = (size >> LOG_MIN_PU_SIZE); - const EB_U32 leftBlockEnd = 2 * (size >> LOG_MIN_PU_SIZE); - const EB_U32 topLeftBlockEnd = 2 * (size >> LOG_MIN_PU_SIZE) + 1; - const EB_U32 topRightBlockBegin = 3 * (size >> LOG_MIN_PU_SIZE) + 1; - const EB_U32 topBlockEnd = 4 * (size >> LOG_MIN_PU_SIZE) + 1; + EB_U32 bottomLeftEnd = (puChromaSize >> LOG_MIN_PU_SIZE); + EB_U32 leftBlockEnd = 2 * (puChromaSize >> LOG_MIN_PU_SIZE); + EB_U32 topLeftBlockEnd = 2 * (puChromaSize >> LOG_MIN_PU_SIZE) + 1; + EB_U32 topRightBlockBegin = 3 * (puChromaSize >> LOG_MIN_PU_SIZE) + 1; + EB_U32 topBlockEnd = 4 * (puChromaSize >> LOG_MIN_PU_SIZE) + 1; - EB_U32 reconArrayIndex; - EB_U32 modeArrayIndex; + EB_U32 reconArrayIndex; + EB_U32 modeArrayIndex; - EB_U8 cbPadValue = 0; - EB_U8 crPadValue = 0; + EB_U8 cbPadValue = 0; + EB_U8 crPadValue = 0; - EB_U8 *cbWritePtr = cbBorder; - EB_U8 *crWritePtr = crBorder; + EB_U8 *cbWritePtr = cbBorder; + EB_U8 *crWritePtr = crBorder; - EB_U32 writeCountChroma; + EB_U32 writeCountChroma; // Neighbor Arrays EB_U32 topModeNeighborArraySize = modeTypeNeighborArray->topArraySize; @@ -1216,7 +1255,7 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( EB_U8 *leftCbReconNeighborArray = cbReconNeighborArray->leftArray; EB_U8 *topLeftCbReconNeighborArray = cbReconNeighborArray->topLeftArray; EB_U8 *topCrReconNeighborArray = crReconNeighborArray->topArray; - EB_U8 *leftCrReconNeighborArray = crReconNeighborArray->leftArray; + EB_U8 *leftCrReconNeighborArray = crReconNeighborArray->leftArray; EB_U8 *topLeftCrReconNeighborArray = crReconNeighborArray->topLeftArray; (void) strongIntraSmoothingFlag; @@ -1271,29 +1310,42 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( // a. If block is valid, copy recon values & update pad value // b. Else, copy pad value - // Pre-calculate bottom left availability + // Pre-calculate bottom left availability per luma wise bottomLeftAvailabilityPreCalc = isBottomLeftAvailable( - cuDepth, - cuIndex); + partitionDepth, + cuIndex); + // Pre-calculate top right availability topRightAvailabilityPreCalc = isUpperRightAvailable( - cuDepth, + partitionDepth, cuIndex); + if (colorFormat == EB_YUV422 && !secondChroma) { + bottomLeftAvailabilityPreCalc = EB_TRUE; + } + + if (colorFormat == EB_YUV422 && secondChroma) { + topRightAvailabilityPreCalc = EB_FALSE; + } + //************************************************* // Part 1: Initial Invalid Sample Loops //************************************************* // Left Block Loop blockIndex = 0; - reconArrayIndex = originY + 2 * size - MIN_PU_SIZE; + + //chroma pel position + reconArrayIndex = (cuOriginY >> subHeightCMinus1) + chromaOffset + + 2 * puChromaSize - MIN_PU_SIZE; + neighborAvailable = EB_FALSE; while(blockIndex < leftBlockEnd && neighborAvailable == EB_FALSE) { - + //Jing: mode is for luma, reconArrayIndex is pel of chroma, needs to convert to luma axis to get the mode modeArrayIndex = GetNeighborArrayUnitLeftIndex( modeTypeNeighborArray, - reconArrayIndex); + reconArrayIndex << subHeightCMinus1); //mode is stored as luma, so convert to luma axis neighborAvailable = (modeArrayIndex >= leftModeNeighborArraySize) ? EB_FALSE : // array boundary check @@ -1305,11 +1357,9 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check if(neighborAvailable == EB_TRUE) { - // Set pad value (end of block) - cbPadValue = leftCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - crPadValue = leftCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - + cbPadValue = leftCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1)]; + crPadValue = leftCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1)]; } else { ++blockIndex; @@ -1318,26 +1368,24 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( } // Top Left Block - reconArrayIndex = MAX_PICTURE_HEIGHT_SIZE + originX - originY; while(blockIndex < topLeftBlockEnd && neighborAvailable == EB_FALSE) { modeArrayIndex = GetNeighborArrayUnitTopLeftIndex( modeTypeNeighborArray, - originX, - originY); + cuOriginX, + cuOriginY + chromaOffset); neighborAvailable = (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check - (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // left picture boundary check + (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // left picture boundary check (topLeftModeNeighborArray[modeArrayIndex] == INTER_MODE && constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check if(neighborAvailable == EB_TRUE) { - - // Set pad value (end of block) - cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; - + int pos = ((MAX_PICTURE_HEIGHT_SIZE - (cuOriginY + chromaOffset)) >> subHeightCMinus1) + (cuOriginX >> subWidthCMinus1); + cbPadValue = topLeftCbReconNeighborArray[pos]; + crPadValue = topLeftCrReconNeighborArray[pos]; + //crPadValue = topLeftCrReconNeighborArray[MAX_PICTURE_HEIGHT_SIZE + (cuOriginX >> subWidthCMinus1) - (cuOriginY + chromaOffset)]; } else { ++blockIndex; @@ -1345,29 +1393,27 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( } // Top Block Loop - reconArrayIndex = originX; + reconArrayIndex = cuOriginX >> subWidthCMinus1; while(blockIndex < topBlockEnd && neighborAvailable == EB_FALSE) { - modeArrayIndex = GetNeighborArrayUnitTopIndex( modeTypeNeighborArray, - reconArrayIndex); + reconArrayIndex << subWidthCMinus1); neighborAvailable = (modeArrayIndex >= topModeNeighborArraySize) ? EB_FALSE : // array boundary check (topRightAvailabilityPreCalc == EB_FALSE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // internal scan-order check - (topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check + (!secondChroma && topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check (pictureTopBoundary == EB_TRUE) ? EB_FALSE : // top picture boundary check (pictureRightBoundary == EB_TRUE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // right picture boundary check - (topModeNeighborArray[modeArrayIndex] == INTER_MODE && + (!secondChroma && topModeNeighborArray[modeArrayIndex] == INTER_MODE && constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check - + if(neighborAvailable == EB_TRUE) { // Set pad value (beginning of block) - cbPadValue = topCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topCrReconNeighborArray[reconArrayIndex >> 1]; - + cbPadValue = topCbReconNeighborArray[reconArrayIndex]; + crPadValue = topCrReconNeighborArray[reconArrayIndex]; } else { ++blockIndex; @@ -1378,38 +1424,37 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( // Check for no valid border samples if (blockIndex == topBlockEnd && neighborAvailable == EB_FALSE) { - - writeCountChroma = 2*size + 1; + writeCountChroma = 4 * puChromaSize + 1; // Write Midrange EB_MEMSET(cbWritePtr, MIDRANGE_VALUE_8BIT, writeCountChroma); EB_MEMSET(crWritePtr, MIDRANGE_VALUE_8BIT, writeCountChroma); } else { - // Write Pad Value - adjust for the TopLeft block being 1-sample writeCountChroma = (blockIndex >= topLeftBlockEnd) ? - (((blockIndex-1) * MIN_PU_SIZE) >> 1) + 1 : - ((blockIndex * MIN_PU_SIZE) >> 1); + ((blockIndex-1) * MIN_PU_SIZE) + 1 : + (blockIndex * MIN_PU_SIZE); - EB_MEMSET(cbWritePtr, cbPadValue, writeCountChroma); - EB_MEMSET(crWritePtr, crPadValue, writeCountChroma); + EB_MEMSET(cbWritePtr, cbPadValue, writeCountChroma); + EB_MEMSET(crWritePtr, crPadValue, writeCountChroma); } - cbWritePtr += writeCountChroma; - crWritePtr += writeCountChroma; + cbWritePtr += writeCountChroma; + crWritePtr += writeCountChroma; //************************************************* // Part 2: Copy the remainder of the samples //************************************************* // Left Block Loop - reconArrayIndex = originY + (2 * size - MIN_PU_SIZE) - (blockIndex * MIN_PU_SIZE); + reconArrayIndex = (cuOriginY>>subHeightCMinus1) + chromaOffset + (2 * puChromaSize - MIN_PU_SIZE) - (blockIndex * MIN_PU_SIZE); + while(blockIndex < leftBlockEnd) { modeArrayIndex = GetNeighborArrayUnitLeftIndex( modeTypeNeighborArray, - reconArrayIndex); + reconArrayIndex << subHeightCMinus1); neighborAvailable = (modeArrayIndex >= leftModeNeighborArraySize) ? EB_FALSE : // array boundary check @@ -1423,39 +1468,38 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( if(neighborAvailable == EB_TRUE) { // Copy samples (Reverse the order) - cbWritePtr[0] = leftCbReconNeighborArray[(reconArrayIndex >> 1) + 1]; - cbWritePtr[1] = leftCbReconNeighborArray[(reconArrayIndex >> 1) + 0]; - - crWritePtr[0] = leftCrReconNeighborArray[(reconArrayIndex >> 1) + 1]; - crWritePtr[1] = leftCrReconNeighborArray[(reconArrayIndex >> 1) + 0]; - + cbWritePtr[0] = leftCbReconNeighborArray[(reconArrayIndex) + 3]; + cbWritePtr[1] = leftCbReconNeighborArray[(reconArrayIndex) + 2]; + cbWritePtr[2] = leftCbReconNeighborArray[(reconArrayIndex) + 1]; + cbWritePtr[3] = leftCbReconNeighborArray[(reconArrayIndex) + 0]; + + crWritePtr[0] = leftCrReconNeighborArray[(reconArrayIndex) + 3]; + crWritePtr[1] = leftCrReconNeighborArray[(reconArrayIndex) + 2]; + crWritePtr[2] = leftCrReconNeighborArray[(reconArrayIndex) + 1]; + crWritePtr[3] = leftCrReconNeighborArray[(reconArrayIndex) + 0]; + // Set pad value (beginning of block) - cbPadValue = leftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = leftCrReconNeighborArray[reconArrayIndex >> 1]; + cbPadValue = leftCbReconNeighborArray[reconArrayIndex]; + crPadValue = leftCrReconNeighborArray[reconArrayIndex]; } else { - // Copy pad value - EB_MEMSET(cbWritePtr, cbPadValue, MIN_PU_SIZE >> 1); - EB_MEMSET(crWritePtr, crPadValue, MIN_PU_SIZE >> 1); - + EB_MEMSET(cbWritePtr, cbPadValue, MIN_PU_SIZE); + EB_MEMSET(crWritePtr, crPadValue, MIN_PU_SIZE); } - - cbWritePtr += MIN_PU_SIZE >> 1; - crWritePtr += MIN_PU_SIZE >> 1; + cbWritePtr += MIN_PU_SIZE; + crWritePtr += MIN_PU_SIZE; ++blockIndex; reconArrayIndex -= MIN_PU_SIZE; } // Top Left Block - reconArrayIndex = MAX_PICTURE_HEIGHT_SIZE + originX - originY; while(blockIndex < topLeftBlockEnd) { - modeArrayIndex = GetNeighborArrayUnitTopLeftIndex( modeTypeNeighborArray, - originX, - originY); + cuOriginX, + cuOriginY+chromaOffset); neighborAvailable = (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check @@ -1464,72 +1508,88 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check if(neighborAvailable == EB_TRUE) { - // Copy sample - *cbWritePtr = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - *crWritePtr = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; + int pos = ((MAX_PICTURE_HEIGHT_SIZE - (cuOriginY + chromaOffset)) >> subHeightCMinus1) + (cuOriginX >> subWidthCMinus1); + *cbWritePtr = topLeftCbReconNeighborArray[pos]; + *crWritePtr = topLeftCrReconNeighborArray[pos]; // Set Pad Value - cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; - } - else { - + cbPadValue = topLeftCbReconNeighborArray[pos]; + crPadValue = topLeftCrReconNeighborArray[pos]; + } else { // Copy pad value - *cbWritePtr = cbPadValue; - *crWritePtr = crPadValue; + *cbWritePtr = cbPadValue; + *crWritePtr = crPadValue; } - - ++cbWritePtr; - ++crWritePtr; - + ++cbWritePtr; + ++crWritePtr; ++blockIndex; } // Top Block Loop - reconArrayIndex = originX + (blockIndex - topLeftBlockEnd)*MIN_PU_SIZE; + reconArrayIndex = (cuOriginX >> subWidthCMinus1) + (blockIndex - topLeftBlockEnd) * MIN_PU_SIZE; while(blockIndex < topBlockEnd) { - modeArrayIndex = GetNeighborArrayUnitTopIndex( modeTypeNeighborArray, - reconArrayIndex); + reconArrayIndex << subWidthCMinus1); neighborAvailable = (modeArrayIndex >= topModeNeighborArraySize) ? EB_FALSE : // array boundary check (topRightAvailabilityPreCalc == EB_FALSE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // internal scan-order check - (topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check + (!secondChroma && topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check (pictureTopBoundary == EB_TRUE) ? EB_FALSE : // top picture boundary check (pictureRightBoundary == EB_TRUE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // right picture boundary check - (topModeNeighborArray[modeArrayIndex] == INTER_MODE && + (!secondChroma && topModeNeighborArray[modeArrayIndex] == INTER_MODE && constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check - + if(neighborAvailable == EB_TRUE) { - // Copy samples in reverse order - EB_MEMCPY(cbWritePtr, &topCbReconNeighborArray[reconArrayIndex >> 1], MIN_PU_SIZE >> 1); - EB_MEMCPY(crWritePtr, &topCrReconNeighborArray[reconArrayIndex >> 1], MIN_PU_SIZE >> 1); + EB_MEMCPY(cbWritePtr, &topCbReconNeighborArray[reconArrayIndex], MIN_PU_SIZE); + EB_MEMCPY(crWritePtr, &topCrReconNeighborArray[reconArrayIndex], MIN_PU_SIZE); // Set pad value (end of block) - cbPadValue = topCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - crPadValue = topCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - - } - else { - + cbPadValue = topCbReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; + crPadValue = topCrReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; + } else { // Copy pad value - EB_MEMSET(cbWritePtr, cbPadValue, MIN_PU_SIZE >> 1); - EB_MEMSET(crWritePtr, crPadValue, MIN_PU_SIZE >> 1); + EB_MEMSET(cbWritePtr, cbPadValue, MIN_PU_SIZE); + EB_MEMSET(crWritePtr, crPadValue, MIN_PU_SIZE); } - - cbWritePtr += MIN_PU_SIZE >> 1; - crWritePtr += MIN_PU_SIZE >> 1; - + cbWritePtr += MIN_PU_SIZE; + crWritePtr += MIN_PU_SIZE; ++blockIndex; reconArrayIndex += MIN_PU_SIZE; } + if (colorFormat == EB_YUV444) { + //************************************************* + // Part 2.5: Intra Filter Samples for ChromaArrayType==3 + //************************************************* + sampleReadLocCb = cbBorder; + sampleWriteLocCb = cbBorderFilt; + sampleReadLocCr = crBorder; + sampleWriteLocCr = crBorderFilt; + // Loop is only over the non-edge samples (TotalCount - 2) + yLoadCounter = (size << 2) - 1; + + // First Sample + *sampleWriteLocCb++ = *sampleReadLocCb++; + *sampleWriteLocCr++ = *sampleReadLocCr++; + + // Internal Filtered Samples + do { + *sampleWriteLocCb++ = (sampleReadLocCb[-1] + (sampleReadLocCb[0] << 1) + sampleReadLocCb[1] + 2) >> 2; + *sampleWriteLocCr++ = (sampleReadLocCr[-1] + (sampleReadLocCr[0] << 1) + sampleReadLocCr[1] + 2) >> 2; + ++sampleReadLocCb; + ++sampleReadLocCr; + } while(--yLoadCounter); + + // Last Sample + *sampleWriteLocCb = *sampleReadLocCb; + *sampleWriteLocCr = *sampleReadLocCr; + } //************************************************* // Part 3: Create Reversed Reference Samples //************************************************* @@ -1558,15 +1618,27 @@ EB_ERRORTYPE GenerateChromaIntraReferenceSamplesEncodePass( EB_MEMCPY(cbBorderReverse + (puChromaSize<<1), cbBorder + (puChromaSize<<1), (puChromaSize<<1)+1); EB_MEMCPY(crBorderReverse + (puChromaSize<<1), crBorder + (puChromaSize<<1), (puChromaSize<<1)+1); - sampleWriteLocCb = cbBorderReverse + (puChromaSize<<1) - 1 ; - sampleWriteLocCr = crBorderReverse + (puChromaSize<<1) - 1 ; + sampleWriteLocCb = cbBorderReverse + (puChromaSize << 1) - 1 ; + sampleWriteLocCr = crBorderReverse + (puChromaSize << 1) - 1 ; - for(i=0; i<(puChromaSize<<1) ;i++){ - - *sampleWriteLocCb = cbBorder[i]; - *sampleWriteLocCr = crBorder[i]; - sampleWriteLocCb--; - sampleWriteLocCr--; + for(i = 0; i < (puChromaSize << 1) ;i++){ + *sampleWriteLocCb = cbBorder[i]; + *sampleWriteLocCr = crBorder[i]; + sampleWriteLocCb--; + sampleWriteLocCr--; + } + + if (colorFormat == EB_YUV444) { + EB_MEMCPY(cbBorderFiltReverse + (puChromaSize<<1), cbBorderFilt + (puChromaSize<<1), (puChromaSize<<1)+1); + EB_MEMCPY(crBorderFiltReverse + (puChromaSize<<1), crBorderFilt + (puChromaSize<<1), (puChromaSize<<1)+1); + sampleWriteLocCbFilt = cbBorderFiltReverse + (puChromaSize << 1) - 1 ; + sampleWriteLocCrFilt = crBorderFiltReverse + (puChromaSize << 1) - 1 ; + for(i = 0; i < (puChromaSize << 1) ;i++){ + *sampleWriteLocCbFilt = cbBorderFilt[i]; + *sampleWriteLocCrFilt = crBorderFilt[i]; + sampleWriteLocCbFilt--; + sampleWriteLocCrFilt--; + } } return return_error; @@ -1581,12 +1653,14 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( EB_U32 originX, EB_U32 originY, EB_U32 size, + EB_U32 lcuSize, EB_U32 cuDepth, NeighborArrayUnit_t *modeTypeNeighborArray, NeighborArrayUnit_t *lumaReconNeighborArray, NeighborArrayUnit_t *cbReconNeighborArray, NeighborArrayUnit_t *crReconNeighborArray, void *refWrapperPtr, + EB_COLOR_FORMAT colorFormat, EB_BOOL pictureLeftBoundary, EB_BOOL pictureTopBoundary, EB_BOOL pictureRightBoundary) @@ -1604,21 +1678,22 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( EB_U16 *crBorderReverse = intraRefPtr->crIntraReferenceArrayReverse; const EB_U32 sizeLog2 = Log2f(size); - const EB_U32 puChromaSize = size >> 1; + const EB_U32 chromaRatio = (colorFormat==EB_YUV420 || colorFormat==EB_YUV422)?1:0; + const EB_U32 puChromaSize = size >> chromaRatio; EB_U32 yLoadCounter; EB_U16 *sampleReadLoc; EB_U16 *sampleWriteLoc; EB_U16 *sampleWriteLocCb; EB_U16 *sampleWriteLocCr; - EB_U32 i; + EB_U32 i; EB_U16 *sampleWriteLocFilt; // This internal LCU availability check will be performed for top right and bottom left neighbors only. // It is always set to true for top, left and top left neighbors EB_BOOL bottomLeftAvailabilityPreCalc; EB_BOOL topRightAvailabilityPreCalc; - const EB_U32 cuIndex = ((originY & 63) >> sizeLog2) * (1 << cuDepth) + ((originX & 63) >> sizeLog2); + const EB_U32 cuIndex = ((originY & (lcuSize-1)) >> sizeLog2) * (1 << cuDepth) + ((originX & (lcuSize-1)) >> sizeLog2); EB_U32 blockIndex; // 4x4 (Min PU size) granularity EB_BOOL neighborAvailable; @@ -1638,10 +1713,10 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( EB_U16 crPadValue = 0; EB_U16 *lumaWritePtr = yBorder; - EB_U16 *cbWritePtr = cbBorder; - EB_U16 *crWritePtr = crBorder; + EB_U16 *cbWritePtr = cbBorder; + EB_U16 *crWritePtr = crBorder; - EB_U32 writeCountLuma; + EB_U32 writeCountLuma; EB_U32 writeCountChroma; // Neighbor Arrays @@ -1658,7 +1733,7 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( EB_U16 *leftCbReconNeighborArray = (EB_U16*)cbReconNeighborArray->leftArray; EB_U16 *topLeftCbReconNeighborArray = (EB_U16*)cbReconNeighborArray->topLeftArray; EB_U16 *topCrReconNeighborArray = (EB_U16*)crReconNeighborArray->topArray; - EB_U16 *leftCrReconNeighborArray = (EB_U16*)crReconNeighborArray->leftArray; + EB_U16 *leftCrReconNeighborArray = (EB_U16*)crReconNeighborArray->leftArray; EB_U16 *topLeftCrReconNeighborArray = (EB_U16*)crReconNeighborArray->topLeftArray; // The Generate Intra Reference sample process is a single pass algorithm @@ -1713,7 +1788,7 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( // Pre-calculate bottom left availability bottomLeftAvailabilityPreCalc = isBottomLeftAvailable( cuDepth, - cuIndex); + cuIndex); // Pre-calculate top right availability topRightAvailabilityPreCalc = isUpperRightAvailable( @@ -1747,8 +1822,8 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( // Set pad value (end of block) lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; - cbPadValue = leftCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - crPadValue = leftCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; + cbPadValue = leftCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> chromaRatio]; + crPadValue = leftCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> chromaRatio]; } else { @@ -1776,9 +1851,8 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( // Set pad value (end of block) lumaPadValue = topLeftLumaReconNeighborArray[reconArrayIndex]; - cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; - + cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> chromaRatio]; + crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> chromaRatio]; } else { ++blockIndex; @@ -1807,9 +1881,8 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( // Set pad value (beginning of block) lumaPadValue = topLumaReconNeighborArray[reconArrayIndex]; - cbPadValue = topCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topCrReconNeighborArray[reconArrayIndex >> 1]; - + cbPadValue = topCbReconNeighborArray[reconArrayIndex >> chromaRatio]; + crPadValue = topCrReconNeighborArray[reconArrayIndex >> chromaRatio]; } else { ++blockIndex; @@ -1822,7 +1895,7 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( if (blockIndex == topBlockEnd && neighborAvailable == EB_FALSE) { writeCountLuma = 4*size + 1; - writeCountChroma = 2*size + 1; + writeCountChroma = 4*(size>>chromaRatio) + 1; // Write Midrange memset16bit(lumaWritePtr, MIDRANGE_VALUE_10BIT, writeCountLuma); @@ -1837,8 +1910,8 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( blockIndex * MIN_PU_SIZE; writeCountChroma = (blockIndex >= topLeftBlockEnd) ? - (((blockIndex-1) * MIN_PU_SIZE) >> 1) + 1 : - ((blockIndex * MIN_PU_SIZE) >> 1); + (((blockIndex-1) * MIN_PU_SIZE) >> chromaRatio) + 1 : + ((blockIndex * MIN_PU_SIZE) >> chromaRatio); memset16bit(lumaWritePtr, lumaPadValue, writeCountLuma); memset16bit(cbWritePtr, cbPadValue, writeCountChroma); @@ -1878,29 +1951,28 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( lumaWritePtr[2] = leftLumaReconNeighborArray[reconArrayIndex + 1]; lumaWritePtr[3] = leftLumaReconNeighborArray[reconArrayIndex + 0]; - cbWritePtr[0] = leftCbReconNeighborArray[(reconArrayIndex >> 1) + 1]; - cbWritePtr[1] = leftCbReconNeighborArray[(reconArrayIndex >> 1) + 0]; + cbWritePtr[0] = leftCbReconNeighborArray[(reconArrayIndex >> chromaRatio) + 1]; + cbWritePtr[1] = leftCbReconNeighborArray[(reconArrayIndex >> chromaRatio) + 0]; - crWritePtr[0] = leftCrReconNeighborArray[(reconArrayIndex >> 1) + 1]; - crWritePtr[1] = leftCrReconNeighborArray[(reconArrayIndex >> 1) + 0]; + crWritePtr[0] = leftCrReconNeighborArray[(reconArrayIndex >> chromaRatio) + 1]; + crWritePtr[1] = leftCrReconNeighborArray[(reconArrayIndex >> chromaRatio) + 0]; // Set pad value (beginning of block) - lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex]; - cbPadValue = leftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = leftCrReconNeighborArray[reconArrayIndex >> 1]; + lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex]; + cbPadValue = leftCbReconNeighborArray[reconArrayIndex >> chromaRatio]; + crPadValue = leftCrReconNeighborArray[reconArrayIndex >> chromaRatio]; } else { // Copy pad value memset16bit(lumaWritePtr, lumaPadValue, MIN_PU_SIZE); - memset16bit(cbWritePtr, cbPadValue, MIN_PU_SIZE >> 1); - memset16bit(crWritePtr, crPadValue, MIN_PU_SIZE >> 1); - + memset16bit(cbWritePtr, cbPadValue, MIN_PU_SIZE >> chromaRatio); + memset16bit(crWritePtr, crPadValue, MIN_PU_SIZE >> chromaRatio); } lumaWritePtr += MIN_PU_SIZE; - cbWritePtr += MIN_PU_SIZE >> 1; - crWritePtr += MIN_PU_SIZE >> 1; + cbWritePtr += MIN_PU_SIZE >> chromaRatio; + crWritePtr += MIN_PU_SIZE >> chromaRatio; ++blockIndex; reconArrayIndex -= MIN_PU_SIZE; @@ -1925,25 +1997,25 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( // Copy sample *lumaWritePtr = topLeftLumaReconNeighborArray[reconArrayIndex]; - *cbWritePtr = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - *crWritePtr = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; + *cbWritePtr = topLeftCbReconNeighborArray[reconArrayIndex >> chromaRatio]; + *crWritePtr = topLeftCrReconNeighborArray[reconArrayIndex >> chromaRatio]; // Set Pad Value lumaPadValue = topLeftLumaReconNeighborArray[reconArrayIndex]; - cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; + cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> chromaRatio]; + crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> chromaRatio]; } else { // Copy pad value *lumaWritePtr = lumaPadValue; - *cbWritePtr = cbPadValue; - *crWritePtr = crPadValue; + *cbWritePtr = cbPadValue; + *crWritePtr = crPadValue; } ++lumaWritePtr; - ++cbWritePtr; - ++crWritePtr; + ++cbWritePtr; + ++crWritePtr; ++blockIndex; } @@ -1970,26 +2042,25 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( // Copy samples in reverse order memcpy16bit(lumaWritePtr, &topLumaReconNeighborArray[reconArrayIndex], MIN_PU_SIZE); - memcpy16bit(cbWritePtr, &topCbReconNeighborArray[reconArrayIndex >> 1], MIN_PU_SIZE >> 1); - memcpy16bit(crWritePtr, &topCrReconNeighborArray[reconArrayIndex >> 1], MIN_PU_SIZE >> 1); + memcpy16bit(cbWritePtr, &topCbReconNeighborArray[reconArrayIndex >> chromaRatio], MIN_PU_SIZE >> chromaRatio); + memcpy16bit(crWritePtr, &topCrReconNeighborArray[reconArrayIndex >> chromaRatio], MIN_PU_SIZE >> chromaRatio); // Set pad value (end of block) - lumaPadValue = topLumaReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; - cbPadValue = topCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - crPadValue = topCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - + lumaPadValue = topLumaReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; + cbPadValue = topCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> chromaRatio]; + crPadValue = topCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> chromaRatio]; } else { // Copy pad value memset16bit(lumaWritePtr, lumaPadValue, MIN_PU_SIZE); - memset16bit(cbWritePtr, cbPadValue, MIN_PU_SIZE >> 1); - memset16bit(crWritePtr, crPadValue, MIN_PU_SIZE >> 1); + memset16bit(cbWritePtr, cbPadValue, MIN_PU_SIZE >> chromaRatio); + memset16bit(crWritePtr, crPadValue, MIN_PU_SIZE >> chromaRatio); } lumaWritePtr += MIN_PU_SIZE; - cbWritePtr += MIN_PU_SIZE >> 1; - crWritePtr += MIN_PU_SIZE >> 1; + cbWritePtr += MIN_PU_SIZE >> chromaRatio; + crWritePtr += MIN_PU_SIZE >> chromaRatio; ++blockIndex; reconArrayIndex += MIN_PU_SIZE; @@ -2008,10 +2079,10 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( EB_U32 index; EB_U32 bottomLeftSample = sampleReadLoc[0]; EB_U32 topLeftSample = sampleReadLoc[(size << 1)]; - EB_U32 topRightSample = sampleReadLoc[(size << 2)]; + EB_U32 topRightSample = sampleReadLoc[(size << 2)]; EB_U32 twicePuSize = (size << 1); //TODO CLEAN UP THIS MESS, this works only for 32x32 blocks - EB_BOOL bilinearLeft = (ABS((EB_S32)bottomLeftSample + (EB_S32)topLeftSample - 2 * sampleReadLoc[size]) < SMOOTHING_THRESHOLD_10BIT) ? EB_TRUE : EB_FALSE; + EB_BOOL bilinearLeft = (ABS((EB_S32)bottomLeftSample + (EB_S32)topLeftSample - 2 * sampleReadLoc[size]) < SMOOTHING_THRESHOLD_10BIT) ? EB_TRUE : EB_FALSE; EB_BOOL bilinearTop = (ABS((EB_S32)topLeftSample + (EB_S32)topRightSample - 2 * sampleReadLoc[size+(size << 1)]) < SMOOTHING_THRESHOLD_10BIT) ? EB_TRUE : EB_FALSE; if(size >= STRONG_INTRA_SMOOTHING_BLOCKSIZE && bilinearLeft && bilinearTop) { @@ -2021,7 +2092,7 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( sampleWriteLoc[(size << 2)] = sampleReadLoc[(size << 2)]; for(index = 1; index < twicePuSize; index++) { - sampleWriteLoc[index] = (EB_U16)(((twicePuSize - index) * bottomLeftSample + index * topLeftSample + size) >> filterShift); + sampleWriteLoc[index] = (EB_U16)(((twicePuSize - index) * bottomLeftSample + index * topLeftSample + size) >> filterShift); sampleWriteLoc[twicePuSize+index] = (EB_U16)(((twicePuSize - index) * topLeftSample + index * topRightSample + size) >> filterShift); } } @@ -2085,29 +2156,29 @@ EB_ERRORTYPE GenerateIntraReference16bitSamplesEncodePass( memcpy16bit(yBorderReverse + (size<<1), yBorder + (size<<1), (size<<1)+1); memcpy16bit(yBorderFiltReverse + (size<<1), yBorderFilt + (size<<1), (size<<1)+1); - sampleWriteLoc = yBorderReverse + (size<<1) - 1 ; - sampleWriteLocFilt = yBorderFiltReverse + (size<<1) - 1 ; + sampleWriteLoc = yBorderReverse + (size<<1) - 1 ; + sampleWriteLocFilt = yBorderFiltReverse + (size<<1) - 1 ; for(i=0; i<(size<<1) ;i++){ *sampleWriteLoc = yBorder[i]; *sampleWriteLocFilt = yBorderFilt[i] ; - sampleWriteLoc--; + sampleWriteLoc--; sampleWriteLocFilt--; } //Chroma memcpy16bit(cbBorderReverse + (puChromaSize<<1), cbBorder + (puChromaSize<<1), (puChromaSize<<1)+1); - memcpy16bit(crBorderReverse + (puChromaSize<<1), crBorder + (puChromaSize<<1), (puChromaSize<<1)+1); + memcpy16bit(crBorderReverse + (puChromaSize<<1), crBorder + (puChromaSize<<1), (puChromaSize<<1)+1); sampleWriteLocCb = cbBorderReverse + (puChromaSize<<1) - 1 ; - sampleWriteLocCr = crBorderReverse + (puChromaSize<<1) - 1 ; + sampleWriteLocCr = crBorderReverse + (puChromaSize<<1) - 1 ; for(i=0; i<(puChromaSize<<1) ;i++){ *sampleWriteLocCb = cbBorder[i]; - *sampleWriteLocCr = crBorder[i]; - sampleWriteLocCb--; - sampleWriteLocCr--; + *sampleWriteLocCr = crBorder[i]; + sampleWriteLocCb--; + sampleWriteLocCr--; } return return_error; @@ -2133,57 +2204,57 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( EB_BOOL pictureTopBoundary, EB_BOOL pictureRightBoundary) { - EB_ERRORTYPE return_error = EB_ErrorNone; - IntraReference16bitSamples_t *intraRefPtr = (IntraReference16bitSamples_t*)refWrapperPtr; - EB_U16 *yBorder = intraRefPtr->yIntraReferenceArray; - EB_U16 *yBorderFilt = intraRefPtr->yIntraFilteredReferenceArray; + EB_ERRORTYPE return_error = EB_ErrorNone; + IntraReference16bitSamples_t *intraRefPtr = (IntraReference16bitSamples_t*)refWrapperPtr; + EB_U16 *yBorder = intraRefPtr->yIntraReferenceArray; + EB_U16 *yBorderFilt = intraRefPtr->yIntraFilteredReferenceArray; + + + EB_U16 *yBorderReverse = intraRefPtr->yIntraReferenceArrayReverse; + EB_U16 *yBorderFiltReverse = intraRefPtr->yIntraFilteredReferenceArrayReverse; - EB_U16 *yBorderReverse = intraRefPtr->yIntraReferenceArrayReverse; - EB_U16 *yBorderFiltReverse = intraRefPtr->yIntraFilteredReferenceArrayReverse; - const EB_U32 sizeLog2 = Log2f(size); - EB_U32 yLoadCounter; - EB_U16 *sampleReadLoc; - EB_U16 *sampleWriteLoc; - EB_U32 i; - EB_U16 *sampleWriteLocFilt; - + EB_U32 yLoadCounter; + EB_U16 *sampleReadLoc; + EB_U16 *sampleWriteLoc; + EB_U32 i; + EB_U16 *sampleWriteLocFilt; // This internal LCU availability check will be performed for top right and bottom left neighbors only. // It is always set to true for top, left and top left neighbors - EB_BOOL bottomLeftAvailabilityPreCalc; - EB_BOOL topRightAvailabilityPreCalc; - - const EB_U32 cuIndex = ((originY & (lcuSize - 1)) >> sizeLog2) * (1 << (cuDepth + 1)) + ((originX & (lcuSize - 1)) >> sizeLog2); + EB_BOOL bottomLeftAvailabilityPreCalc; + EB_BOOL topRightAvailabilityPreCalc; + + EB_U32 partitionDepth = (size == MIN_PU_SIZE) ? cuDepth + 1 : cuDepth; + const EB_U32 cuIndex = ((originY & (lcuSize - 1)) >> sizeLog2) * (1 << partitionDepth) + ((originX & (lcuSize - 1)) >> sizeLog2); - EB_U32 blockIndex; // 4x4 (Min PU size) granularity - EB_BOOL neighborAvailable; + EB_U32 blockIndex;// 4x4 (Min PU size) granularity + EB_BOOL neighborAvailable; - const EB_U32 bottomLeftEnd = (size >> LOG_MIN_PU_SIZE); - const EB_U32 leftBlockEnd = 2 * (size >> LOG_MIN_PU_SIZE); - const EB_U32 topLeftBlockEnd = 2 * (size >> LOG_MIN_PU_SIZE) + 1; - const EB_U32 topRightBlockBegin = 3 * (size >> LOG_MIN_PU_SIZE) + 1; - const EB_U32 topBlockEnd = 4 * (size >> LOG_MIN_PU_SIZE) + 1; + const EB_U32 bottomLeftEnd = (size >> LOG_MIN_PU_SIZE); + const EB_U32 leftBlockEnd = 2 * (size >> LOG_MIN_PU_SIZE); + const EB_U32 topLeftBlockEnd = 2 * (size >> LOG_MIN_PU_SIZE) + 1; + const EB_U32 topRightBlockBegin = 3 * (size >> LOG_MIN_PU_SIZE) + 1; + const EB_U32 topBlockEnd = 4 * (size >> LOG_MIN_PU_SIZE) + 1; - EB_U32 reconArrayIndex; - EB_U32 modeArrayIndex; + EB_U32 reconArrayIndex; + EB_U32 modeArrayIndex; - EB_U16 lumaPadValue = 0; - - EB_U16 *lumaWritePtr = yBorder; + EB_U16 lumaPadValue = 0; + EB_U16 *lumaWritePtr = yBorder; - EB_U32 writeCountLuma; + EB_U32 writeCountLuma; // Neighbor Arrays - EB_U32 topModeNeighborArraySize = modeTypeNeighborArray->topArraySize; - EB_U8 *topModeNeighborArray = modeTypeNeighborArray->topArray; - EB_U32 leftModeNeighborArraySize = modeTypeNeighborArray->leftArraySize; - EB_U8 *leftModeNeighborArray = modeTypeNeighborArray->leftArray; - EB_U8 *topLeftModeNeighborArray = modeTypeNeighborArray->topLeftArray; - EB_U16 *topLumaReconNeighborArray = (EB_U16*)lumaReconNeighborArray->topArray; - EB_U16 *leftLumaReconNeighborArray = (EB_U16*)lumaReconNeighborArray->leftArray; - EB_U16 *topLeftLumaReconNeighborArray = (EB_U16*)lumaReconNeighborArray->topLeftArray; + EB_U32 topModeNeighborArraySize = modeTypeNeighborArray->topArraySize; + EB_U8 *topModeNeighborArray = modeTypeNeighborArray->topArray; + EB_U32 leftModeNeighborArraySize = modeTypeNeighborArray->leftArraySize; + EB_U8 *leftModeNeighborArray = modeTypeNeighborArray->leftArray; + EB_U8 *topLeftModeNeighborArray = modeTypeNeighborArray->topLeftArray; + EB_U16 *topLumaReconNeighborArray = (EB_U16*)lumaReconNeighborArray->topArray; + EB_U16 *leftLumaReconNeighborArray = (EB_U16*)lumaReconNeighborArray->leftArray; + EB_U16 *topLeftLumaReconNeighborArray = (EB_U16*)lumaReconNeighborArray->topLeftArray; (void) cbReconNeighborArray; (void) crReconNeighborArray; @@ -2239,12 +2310,12 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( // Pre-calculate bottom left availability bottomLeftAvailabilityPreCalc = isBottomLeftAvailable( - cuDepth + 1, - cuIndex); + partitionDepth, + cuIndex); // Pre-calculate top right availability topRightAvailabilityPreCalc = isUpperRightAvailable( - cuDepth + 1, + partitionDepth, cuIndex); //************************************************* @@ -2266,7 +2337,7 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( (bottomLeftAvailabilityPreCalc == EB_FALSE && blockIndex < bottomLeftEnd) ? EB_FALSE : // internal scan-order check (leftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check - (pictureLeftBoundary == EB_TRUE) ? EB_FALSE : // left picture boundary check + (pictureLeftBoundary == EB_TRUE) ? EB_FALSE : // picture boundary check (leftModeNeighborArray[modeArrayIndex] == INTER_MODE && constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check @@ -2292,10 +2363,10 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( originY); neighborAvailable = - (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check - (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // picture boundary check + (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check + (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // picture boundary check (topLeftModeNeighborArray[modeArrayIndex] == INTER_MODE && - constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check + constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check if(neighborAvailable == EB_TRUE) { @@ -2320,7 +2391,7 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( (modeArrayIndex >= topModeNeighborArraySize) ? EB_FALSE : // array boundary check (topRightAvailabilityPreCalc == EB_FALSE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // internal scan-order check - (topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check + (topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check (pictureTopBoundary == EB_TRUE) ? EB_FALSE : // top picture boundary check (pictureRightBoundary == EB_TRUE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // right picture boundary check (topModeNeighborArray[modeArrayIndex] == INTER_MODE && @@ -2343,7 +2414,7 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( writeCountLuma = 4*size + 1; - // Write Midrange + // Write Midrange memset16bit(lumaWritePtr, MIDRANGE_VALUE_10BIT, writeCountLuma); } else { @@ -2352,7 +2423,7 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( writeCountLuma = (blockIndex >= topLeftBlockEnd) ? (blockIndex-1) * MIN_PU_SIZE + 1 : blockIndex * MIN_PU_SIZE; - + memset16bit(lumaWritePtr, lumaPadValue, writeCountLuma); } @@ -2388,7 +2459,7 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( lumaWritePtr[3] = leftLumaReconNeighborArray[reconArrayIndex + 0]; // Set pad value (beginning of block) - lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex]; + lumaPadValue = leftLumaReconNeighborArray[reconArrayIndex]; } else { @@ -2396,7 +2467,7 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( memset16bit(lumaWritePtr, lumaPadValue, MIN_PU_SIZE); } - lumaWritePtr += MIN_PU_SIZE; + lumaWritePtr += MIN_PU_SIZE; ++blockIndex; reconArrayIndex -= MIN_PU_SIZE; @@ -2412,8 +2483,8 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( originY); neighborAvailable = - (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check - (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // picture boundary check + (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check + (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // left picture boundary check (topLeftModeNeighborArray[modeArrayIndex] == INTER_MODE && constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check @@ -2461,13 +2532,12 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( memcpy16bit(lumaWritePtr, &topLumaReconNeighborArray[reconArrayIndex], MIN_PU_SIZE); // Set pad value (end of block) - lumaPadValue = topLumaReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; + lumaPadValue = topLumaReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; } else { - // Copy pad value - memset16bit(lumaWritePtr, lumaPadValue, MIN_PU_SIZE); + memset16bit(lumaWritePtr, lumaPadValue, MIN_PU_SIZE); } lumaWritePtr += MIN_PU_SIZE; @@ -2489,21 +2559,22 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( EB_U32 index; EB_U32 bottomLeftSample = sampleReadLoc[0]; EB_U32 topLeftSample = sampleReadLoc[(size << 1)]; - EB_U32 topRightSample = sampleReadLoc[(size << 2)]; + EB_U32 topRightSample = sampleReadLoc[(size << 2)]; EB_U32 twicePuSize = (size << 1); + //TODO CLEAN UP THIS MESS, this works only for 32x32 blocks - EB_BOOL bilinearLeft = (ABS((EB_S32)bottomLeftSample + (EB_S32)topLeftSample - 2 * sampleReadLoc[size]) < SMOOTHING_THRESHOLD_10BIT) ? EB_TRUE : EB_FALSE; + EB_BOOL bilinearLeft = (ABS((EB_S32)bottomLeftSample + (EB_S32)topLeftSample - 2 * sampleReadLoc[size]) < SMOOTHING_THRESHOLD_10BIT) ? EB_TRUE : EB_FALSE; EB_BOOL bilinearTop = (ABS((EB_S32)topLeftSample + (EB_S32)topRightSample - 2 * sampleReadLoc[size+(size << 1)]) < SMOOTHING_THRESHOLD_10BIT) ? EB_TRUE : EB_FALSE; if(size >= STRONG_INTRA_SMOOTHING_BLOCKSIZE && bilinearLeft && bilinearTop) { filterShift = smoothingFilterShift[Log2f(size)-2]; - sampleWriteLoc[0] = sampleReadLoc[0]; + sampleWriteLoc[0] = sampleReadLoc[0]; sampleWriteLoc[(size << 1)] = sampleReadLoc[(size << 1)]; sampleWriteLoc[(size << 2)] = sampleReadLoc[(size << 2)]; for(index = 1; index < twicePuSize; index++) { - sampleWriteLoc[index] = (EB_U16)(((twicePuSize - index) * bottomLeftSample + index * topLeftSample + size) >> filterShift); - sampleWriteLoc[twicePuSize+index] = (EB_U16)(((twicePuSize - index) * topLeftSample + index * topRightSample + size) >> filterShift); + sampleWriteLoc[index] = (EB_U16)(((twicePuSize - index) * bottomLeftSample + index * topLeftSample + size) >> filterShift); + sampleWriteLoc[twicePuSize+index] = (EB_U16)(((twicePuSize - index) * topLeftSample + index * topRightSample + size) >> filterShift); } } else { @@ -2556,17 +2627,16 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( L7 <-- pointer (Regular Order) L0<-- pointer Reverse Order junk */ - - memcpy16bit(yBorderReverse + (size<<1), yBorder + (size<<1), (size<<1)+1); - memcpy16bit(yBorderFiltReverse + (size<<1), yBorderFilt + (size<<1), (size<<1)+1); - sampleWriteLoc = yBorderReverse + (size<<1) - 1 ; - sampleWriteLocFilt = yBorderFiltReverse + (size<<1) - 1 ; - for(i=0; i<(size<<1) ;i++){ - - *sampleWriteLoc = yBorder[i]; + memcpy16bit(yBorderReverse + (size<<1), yBorder + (size<<1), (size<<1)+1); + memcpy16bit(yBorderFiltReverse + (size<<1), yBorderFilt + (size<<1), (size<<1)+1); + + sampleWriteLoc = yBorderReverse + (size<<1) - 1 ; + sampleWriteLocFilt = yBorderFiltReverse + (size<<1) - 1 ; + for(i=0; i<(size<<1) ;i++) { + *sampleWriteLoc = yBorder[i]; *sampleWriteLocFilt = yBorderFilt[i] ; - sampleWriteLoc--; + sampleWriteLoc--; sampleWriteLocFilt--; } @@ -2579,8 +2649,8 @@ EB_ERRORTYPE GenerateLumaIntraReference16bitSamplesEncodePass( EB_ERRORTYPE GenerateChromaIntraReference16bitSamplesEncodePass( EB_BOOL constrainedIntraFlag, //input parameter, indicates if constrained intra is switched on/off EB_BOOL strongIntraSmoothingFlag, - EB_U32 originX, - EB_U32 originY, + EB_U32 cuOriginX, + EB_U32 cuOriginY, EB_U32 size, EB_U32 lcuSize, EB_U32 cuDepth, @@ -2589,66 +2659,83 @@ EB_ERRORTYPE GenerateChromaIntraReference16bitSamplesEncodePass( NeighborArrayUnit_t *cbReconNeighborArray, NeighborArrayUnit_t *crReconNeighborArray, void *refWrapperPtr, + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, EB_BOOL pictureLeftBoundary, EB_BOOL pictureTopBoundary, EB_BOOL pictureRightBoundary) { EB_ERRORTYPE return_error = EB_ErrorNone; + IntraReference16bitSamples_t *intraRefPtr = (IntraReference16bitSamples_t*)refWrapperPtr; - EB_U16 *cbBorder = intraRefPtr->cbIntraReferenceArray; - EB_U16 *crBorder = intraRefPtr->crIntraReferenceArray; + EB_U16 *cbBorder = intraRefPtr->cbIntraReferenceArray; + EB_U16 *cbBorderFilt = intraRefPtr->cbIntraFilteredReferenceArray; + EB_U16 *crBorder = intraRefPtr->crIntraReferenceArray; + EB_U16 *crBorderFilt = intraRefPtr->crIntraFilteredReferenceArray; - EB_U16 *cbBorderReverse = intraRefPtr->cbIntraReferenceArrayReverse; - EB_U16 *crBorderReverse = intraRefPtr->crIntraReferenceArrayReverse; - - const EB_U32 sizeLog2 = Log2f(size); - const EB_U32 puChromaSize = size >> 1; + EB_U16 *cbBorderReverse = intraRefPtr->cbIntraReferenceArrayReverse; + EB_U16 *cbBorderFiltReverse = intraRefPtr->cbIntraFilteredReferenceArrayReverse; + EB_U16 *crBorderReverse = intraRefPtr->crIntraReferenceArrayReverse; + EB_U16 *crBorderFiltReverse = intraRefPtr->crIntraFilteredReferenceArrayReverse; - EB_U16 *sampleWriteLocCb; - EB_U16 *sampleWriteLocCr; - EB_U32 i; + + const EB_U32 sizeLog2 = Log2f(size); + const EB_U32 puChromaSize = size >> ((colorFormat == EB_YUV420 || colorFormat == EB_YUV422) ? 1 : 0); + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + const EB_U32 chromaOffset = (colorFormat == EB_YUV422 && secondChroma) ? puChromaSize : 0; + + EB_U32 i; + EB_U32 yLoadCounter; + EB_U16 *sampleReadLocCb; + EB_U16 *sampleWriteLocCb; + EB_U16 *sampleReadLocCr; + EB_U16 *sampleWriteLocCr; + EB_U16 *sampleWriteLocCbFilt; + EB_U16 *sampleWriteLocCrFilt; // This internal LCU availability check will be performed for top right and bottom left neighbors only. // It is always set to true for top, left and top left neighbors - EB_BOOL bottomLeftAvailabilityPreCalc; - EB_BOOL topRightAvailabilityPreCalc; + EB_BOOL bottomLeftAvailabilityPreCalc; + EB_BOOL topRightAvailabilityPreCalc; - const EB_U32 cuIndex = ((originY & (lcuSize - 1)) >> sizeLog2) * (1 << cuDepth) + ((originX & (lcuSize - 1)) >> sizeLog2); + const EB_U32 partitionDepth = (size == MIN_PU_SIZE) ? cuDepth + 1 : cuDepth; + const EB_U32 cuIndex = ((cuOriginY & (lcuSize - 1)) >> sizeLog2) * (1 << partitionDepth) + ((cuOriginX & (lcuSize - 1)) >> sizeLog2); - EB_U32 blockIndex; // 4x4 (Min PU size) granularity - EB_BOOL neighborAvailable; + EB_U32 blockIndex; // 4x4 (Min PU size) granularity + EB_BOOL neighborAvailable; - const EB_U32 bottomLeftEnd = (size >> LOG_MIN_PU_SIZE); - const EB_U32 leftBlockEnd = 2 * (size >> LOG_MIN_PU_SIZE); - const EB_U32 topLeftBlockEnd = 2 * (size >> LOG_MIN_PU_SIZE) + 1; - const EB_U32 topRightBlockBegin = 3 * (size >> LOG_MIN_PU_SIZE) + 1; - const EB_U32 topBlockEnd = 4 * (size >> LOG_MIN_PU_SIZE) + 1; + EB_U32 bottomLeftEnd = (puChromaSize >> LOG_MIN_PU_SIZE); + EB_U32 leftBlockEnd = 2 * (puChromaSize >> LOG_MIN_PU_SIZE); + EB_U32 topLeftBlockEnd = 2 * (puChromaSize >> LOG_MIN_PU_SIZE) + 1; + EB_U32 topRightBlockBegin = 3 * (puChromaSize >> LOG_MIN_PU_SIZE) + 1; + EB_U32 topBlockEnd = 4 * (puChromaSize >> LOG_MIN_PU_SIZE) + 1; + EB_U32 reconArrayIndex; + EB_U32 modeArrayIndex; - EB_U32 reconArrayIndex; - EB_U32 modeArrayIndex; - - EB_U16 cbPadValue = 0; - EB_U16 crPadValue = 0; + EB_U16 cbPadValue = 0; + EB_U16 crPadValue = 0; - EB_U16 *cbWritePtr = cbBorder; - EB_U16 *crWritePtr = crBorder; + EB_U16 *cbWritePtr = cbBorder; + EB_U16 *crWritePtr = crBorder; - EB_U32 writeCountChroma; + EB_U32 writeCountChroma; // Neighbor Arrays - EB_U32 topModeNeighborArraySize = modeTypeNeighborArray->topArraySize; - EB_U8 *topModeNeighborArray = modeTypeNeighborArray->topArray; - EB_U32 leftModeNeighborArraySize = modeTypeNeighborArray->leftArraySize; - EB_U8 *leftModeNeighborArray = modeTypeNeighborArray->leftArray; - EB_U8 *topLeftModeNeighborArray = modeTypeNeighborArray->topLeftArray; - EB_U16 *topCbReconNeighborArray = (EB_U16*)cbReconNeighborArray->topArray; - EB_U16 *leftCbReconNeighborArray = (EB_U16*)cbReconNeighborArray->leftArray; - EB_U16 *topLeftCbReconNeighborArray = (EB_U16*)cbReconNeighborArray->topLeftArray; - EB_U16 *topCrReconNeighborArray = (EB_U16*)crReconNeighborArray->topArray; - EB_U16 *leftCrReconNeighborArray = (EB_U16*)crReconNeighborArray->leftArray; - EB_U16 *topLeftCrReconNeighborArray = (EB_U16*)crReconNeighborArray->topLeftArray; + EB_U32 topModeNeighborArraySize = modeTypeNeighborArray->topArraySize; + EB_U8 *topModeNeighborArray = modeTypeNeighborArray->topArray; + EB_U32 leftModeNeighborArraySize = modeTypeNeighborArray->leftArraySize; + EB_U8 *leftModeNeighborArray = modeTypeNeighborArray->leftArray; + EB_U8 *topLeftModeNeighborArray = modeTypeNeighborArray->topLeftArray; + + EB_U16 *topCbReconNeighborArray = (EB_U16*)cbReconNeighborArray->topArray; + EB_U16 *leftCbReconNeighborArray = (EB_U16*)cbReconNeighborArray->leftArray; + EB_U16 *topLeftCbReconNeighborArray = (EB_U16*)cbReconNeighborArray->topLeftArray; + EB_U16 *topCrReconNeighborArray = (EB_U16*)crReconNeighborArray->topArray; + EB_U16 *leftCrReconNeighborArray = (EB_U16*)crReconNeighborArray->leftArray; + EB_U16 *topLeftCrReconNeighborArray = (EB_U16*)crReconNeighborArray->topLeftArray; (void) strongIntraSmoothingFlag; (void) lumaReconNeighborArray; @@ -2702,29 +2789,43 @@ EB_ERRORTYPE GenerateChromaIntraReference16bitSamplesEncodePass( // a. If block is valid, copy recon values & update pad value // b. Else, copy pad value - // Pre-calculate bottom left availability + // Pre-calculate bottom left availability per luma wise bottomLeftAvailabilityPreCalc = isBottomLeftAvailable( - cuDepth, - cuIndex); + partitionDepth, + cuIndex); + // Pre-calculate top right availability topRightAvailabilityPreCalc = isUpperRightAvailable( - cuDepth, + partitionDepth, cuIndex); + if (colorFormat == EB_YUV422 && !secondChroma) { + bottomLeftAvailabilityPreCalc = EB_TRUE; + } + + if (colorFormat == EB_YUV422 && secondChroma) { + topRightAvailabilityPreCalc = EB_FALSE; + } + //************************************************* // Part 1: Initial Invalid Sample Loops //************************************************* // Left Block Loop blockIndex = 0; - reconArrayIndex = originY + 2 * size - MIN_PU_SIZE; + + //chroma pel position + reconArrayIndex = (cuOriginY >> subHeightCMinus1) + chromaOffset + + 2 * puChromaSize - MIN_PU_SIZE; + neighborAvailable = EB_FALSE; while(blockIndex < leftBlockEnd && neighborAvailable == EB_FALSE) { + //Jing: mode is for luma, reconArrayIndex is pel of chroma, needs to convert to luma axis to get the mode modeArrayIndex = GetNeighborArrayUnitLeftIndex( modeTypeNeighborArray, - reconArrayIndex); + reconArrayIndex<= leftModeNeighborArraySize) ? EB_FALSE : // array boundary check @@ -2738,9 +2839,8 @@ EB_ERRORTYPE GenerateChromaIntraReference16bitSamplesEncodePass( if(neighborAvailable == EB_TRUE) { // Set pad value (end of block) - cbPadValue = leftCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - crPadValue = leftCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - + cbPadValue = leftCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1)]; + crPadValue = leftCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1)]; } else { ++blockIndex; @@ -2749,55 +2849,50 @@ EB_ERRORTYPE GenerateChromaIntraReference16bitSamplesEncodePass( } // Top Left Block - reconArrayIndex = MAX_PICTURE_HEIGHT_SIZE + originX - originY; while(blockIndex < topLeftBlockEnd && neighborAvailable == EB_FALSE) { - modeArrayIndex = GetNeighborArrayUnitTopLeftIndex( modeTypeNeighborArray, - originX, - originY); + cuOriginX, + cuOriginY + chromaOffset); neighborAvailable = - (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check - (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // picture boundary check + (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check + (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // left picture boundary check (topLeftModeNeighborArray[modeArrayIndex] == INTER_MODE && - constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check + constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check if(neighborAvailable == EB_TRUE) { - // Set pad value (end of block) - cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; - - } - else { + int pos = ((MAX_PICTURE_HEIGHT_SIZE - (cuOriginY + chromaOffset)) >> subHeightCMinus1) + (cuOriginX >> subWidthCMinus1); + cbPadValue = topLeftCbReconNeighborArray[pos]; + crPadValue = topLeftCrReconNeighborArray[pos]; + } else { ++blockIndex; } } // Top Block Loop - reconArrayIndex = originX; + reconArrayIndex = cuOriginX >> subWidthCMinus1; while(blockIndex < topBlockEnd && neighborAvailable == EB_FALSE) { - modeArrayIndex = GetNeighborArrayUnitTopIndex( modeTypeNeighborArray, - reconArrayIndex); + reconArrayIndex<= topModeNeighborArraySize) ? EB_FALSE : // array boundary check (topRightAvailabilityPreCalc == EB_FALSE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // internal scan-order check - (topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check + (!secondChroma && topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check (pictureTopBoundary == EB_TRUE) ? EB_FALSE : // top picture boundary check (pictureRightBoundary == EB_TRUE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // right picture boundary check - (topModeNeighborArray[modeArrayIndex] == INTER_MODE && + (!secondChroma && topModeNeighborArray[modeArrayIndex] == INTER_MODE && constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check - + if(neighborAvailable == EB_TRUE) { // Set pad value (beginning of block) - cbPadValue = topCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topCrReconNeighborArray[reconArrayIndex >> 1]; + cbPadValue = topCbReconNeighborArray[reconArrayIndex]; + crPadValue = topCrReconNeighborArray[reconArrayIndex]; } else { @@ -2810,21 +2905,21 @@ EB_ERRORTYPE GenerateChromaIntraReference16bitSamplesEncodePass( // Check for no valid border samples if (blockIndex == topBlockEnd && neighborAvailable == EB_FALSE) { - writeCountChroma = 2*size + 1; + writeCountChroma = 4*puChromaSize + 1; - // Write Midrange - memset16bit(cbWritePtr, MIDRANGE_VALUE_10BIT, writeCountChroma); - memset16bit(crWritePtr, MIDRANGE_VALUE_10BIT, writeCountChroma); + // Write Midrange + memset16bit(cbWritePtr, MIDRANGE_VALUE_10BIT, writeCountChroma); + memset16bit(crWritePtr, MIDRANGE_VALUE_10BIT, writeCountChroma); } else { // Write Pad Value - adjust for the TopLeft block being 1-sample writeCountChroma = (blockIndex >= topLeftBlockEnd) ? - (((blockIndex-1) * MIN_PU_SIZE) >> 1) + 1 : - ((blockIndex * MIN_PU_SIZE) >> 1); + ((blockIndex-1) * MIN_PU_SIZE) + 1 : + (blockIndex * MIN_PU_SIZE); - memset16bit(cbWritePtr, cbPadValue, writeCountChroma); - memset16bit(crWritePtr, crPadValue, writeCountChroma); + memset16bit(cbWritePtr, cbPadValue, writeCountChroma); + memset16bit(crWritePtr, crPadValue, writeCountChroma); } cbWritePtr += writeCountChroma; @@ -2835,12 +2930,13 @@ EB_ERRORTYPE GenerateChromaIntraReference16bitSamplesEncodePass( //************************************************* // Left Block Loop - reconArrayIndex = originY + (2 * size - MIN_PU_SIZE) - (blockIndex * MIN_PU_SIZE); - while(blockIndex < leftBlockEnd) { + reconArrayIndex = (cuOriginY >> subHeightCMinus1) + chromaOffset + + (2 * puChromaSize - MIN_PU_SIZE) - (blockIndex * MIN_PU_SIZE); + while(blockIndex < leftBlockEnd) { modeArrayIndex = GetNeighborArrayUnitLeftIndex( modeTypeNeighborArray, - reconArrayIndex); + reconArrayIndex << subHeightCMinus1); neighborAvailable = (modeArrayIndex >= leftModeNeighborArraySize) ? EB_FALSE : // array boundary check @@ -2852,115 +2948,137 @@ EB_ERRORTYPE GenerateChromaIntraReference16bitSamplesEncodePass( constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check if(neighborAvailable == EB_TRUE) { - // Copy samples (Reverse the order) - cbWritePtr[0] = leftCbReconNeighborArray[(reconArrayIndex >> 1) + 1]; - cbWritePtr[1] = leftCbReconNeighborArray[(reconArrayIndex >> 1) + 0]; - - crWritePtr[0] = leftCrReconNeighborArray[(reconArrayIndex >> 1) + 1]; - crWritePtr[1] = leftCrReconNeighborArray[(reconArrayIndex >> 1) + 0]; - - // Set pad value (beginning of block) - cbPadValue = leftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = leftCrReconNeighborArray[reconArrayIndex >> 1]; + cbWritePtr[0] = leftCbReconNeighborArray[(reconArrayIndex) + 3]; + cbWritePtr[1] = leftCbReconNeighborArray[(reconArrayIndex) + 2]; + cbWritePtr[2] = leftCbReconNeighborArray[(reconArrayIndex) + 1]; + cbWritePtr[3] = leftCbReconNeighborArray[(reconArrayIndex) + 0]; + + crWritePtr[0] = leftCrReconNeighborArray[(reconArrayIndex) + 3]; + crWritePtr[1] = leftCrReconNeighborArray[(reconArrayIndex) + 2]; + crWritePtr[2] = leftCrReconNeighborArray[(reconArrayIndex) + 1]; + crWritePtr[3] = leftCrReconNeighborArray[(reconArrayIndex) + 0]; + + // Set pad value (beginning of block) + cbPadValue = leftCbReconNeighborArray[reconArrayIndex]; + crPadValue = leftCrReconNeighborArray[reconArrayIndex]; } else { - // Copy pad value - memset16bit(cbWritePtr, cbPadValue, MIN_PU_SIZE >> 1); - memset16bit(crWritePtr, crPadValue, MIN_PU_SIZE >> 1); - + memset16bit(cbWritePtr, cbPadValue, MIN_PU_SIZE); + memset16bit(crWritePtr, crPadValue, MIN_PU_SIZE); } - cbWritePtr += MIN_PU_SIZE >> 1; - crWritePtr += MIN_PU_SIZE >> 1; + cbWritePtr += MIN_PU_SIZE; + crWritePtr += MIN_PU_SIZE; ++blockIndex; reconArrayIndex -= MIN_PU_SIZE; } // Top Left Block - reconArrayIndex = MAX_PICTURE_HEIGHT_SIZE + originX - originY; while(blockIndex < topLeftBlockEnd) { - modeArrayIndex = GetNeighborArrayUnitTopLeftIndex( modeTypeNeighborArray, - originX, - originY); + cuOriginX, + cuOriginY + chromaOffset); neighborAvailable = - (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check - (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // picture boundary check + (topLeftModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check + (pictureLeftBoundary == EB_TRUE || pictureTopBoundary == EB_TRUE) ? EB_FALSE : // left picture boundary check (topLeftModeNeighborArray[modeArrayIndex] == INTER_MODE && constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check if(neighborAvailable == EB_TRUE) { - // Copy sample - *cbWritePtr = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - *crWritePtr = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; + int pos = ((MAX_PICTURE_HEIGHT_SIZE - (cuOriginY + chromaOffset)) >> subHeightCMinus1) + (cuOriginX >> subWidthCMinus1); + *cbWritePtr = topLeftCbReconNeighborArray[pos]; + *crWritePtr = topLeftCrReconNeighborArray[pos]; // Set Pad Value - cbPadValue = topLeftCbReconNeighborArray[reconArrayIndex >> 1]; - crPadValue = topLeftCrReconNeighborArray[reconArrayIndex >> 1]; - } - else { - + cbPadValue = topLeftCbReconNeighborArray[pos]; + crPadValue = topLeftCrReconNeighborArray[pos]; + } else { // Copy pad value - *cbWritePtr = cbPadValue; - *crWritePtr = crPadValue; + *cbWritePtr = cbPadValue; + *crWritePtr = crPadValue; } - ++cbWritePtr; - ++crWritePtr; - + ++cbWritePtr; + ++crWritePtr; ++blockIndex; } // Top Block Loop - reconArrayIndex = originX + (blockIndex - topLeftBlockEnd)*MIN_PU_SIZE; + reconArrayIndex = (cuOriginX >> subWidthCMinus1) + + (blockIndex - topLeftBlockEnd) * MIN_PU_SIZE; while(blockIndex < topBlockEnd) { - modeArrayIndex = GetNeighborArrayUnitTopIndex( modeTypeNeighborArray, - reconArrayIndex); + reconArrayIndex<= topModeNeighborArraySize) ? EB_FALSE : // array boundary check (topRightAvailabilityPreCalc == EB_FALSE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // internal scan-order check - (topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check + (!secondChroma && topModeNeighborArray[modeArrayIndex] == (EB_U8) INVALID_MODE) ? EB_FALSE : // slice boundary check (pictureTopBoundary == EB_TRUE) ? EB_FALSE : // top picture boundary check (pictureRightBoundary == EB_TRUE && blockIndex >= topRightBlockBegin) ? EB_FALSE : // right picture boundary check - (topModeNeighborArray[modeArrayIndex] == INTER_MODE && + (!secondChroma && topModeNeighborArray[modeArrayIndex] == INTER_MODE && constrainedIntraFlag == EB_TRUE) ? EB_FALSE : EB_TRUE; // contrained intra check - + if(neighborAvailable == EB_TRUE) { - // Copy samples in reverse order - memcpy16bit(cbWritePtr, &topCbReconNeighborArray[reconArrayIndex >> 1], MIN_PU_SIZE >> 1); - memcpy16bit(crWritePtr, &topCrReconNeighborArray[reconArrayIndex >> 1], MIN_PU_SIZE >> 1); + memcpy16bit(cbWritePtr, &topCbReconNeighborArray[reconArrayIndex], MIN_PU_SIZE); + memcpy16bit(crWritePtr, &topCrReconNeighborArray[reconArrayIndex], MIN_PU_SIZE); // Set pad value (end of block) - cbPadValue = topCbReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - crPadValue = topCrReconNeighborArray[(reconArrayIndex + MIN_PU_SIZE - 1) >> 1]; - + cbPadValue = topCbReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; + crPadValue = topCrReconNeighborArray[reconArrayIndex + MIN_PU_SIZE - 1]; } else { // Copy pad value - memset16bit(cbWritePtr, cbPadValue, MIN_PU_SIZE >> 1); - memset16bit(crWritePtr, crPadValue, MIN_PU_SIZE >> 1); + memset16bit(cbWritePtr, cbPadValue, MIN_PU_SIZE); + memset16bit(crWritePtr, crPadValue, MIN_PU_SIZE); } - cbWritePtr += MIN_PU_SIZE >> 1; - crWritePtr += MIN_PU_SIZE >> 1; + cbWritePtr += MIN_PU_SIZE; + crWritePtr += MIN_PU_SIZE; ++blockIndex; reconArrayIndex += MIN_PU_SIZE; } + if (colorFormat == EB_YUV444) { + //************************************************* + // Part 2.5: Intra Filter Samples for ChromaArrayType==3 + //************************************************* + sampleReadLocCb = cbBorder; + sampleWriteLocCb = cbBorderFilt; + sampleReadLocCr = crBorder; + sampleWriteLocCr = crBorderFilt; + // Loop is only over the non-edge samples (TotalCount - 2) + yLoadCounter = (size << 2) - 1; + + // First Sample + *sampleWriteLocCb++ = *sampleReadLocCb++; + *sampleWriteLocCr++ = *sampleReadLocCr++; + + // Internal Filtered Samples + do { + *sampleWriteLocCb++ = (sampleReadLocCb[-1] + (sampleReadLocCb[0] << 1) + sampleReadLocCb[1] + 2) >> 2; + *sampleWriteLocCr++ = (sampleReadLocCr[-1] + (sampleReadLocCr[0] << 1) + sampleReadLocCr[1] + 2) >> 2; + ++sampleReadLocCb; + ++sampleReadLocCr; + } while(--yLoadCounter); + + // Last Sample + *sampleWriteLocCb = *sampleReadLocCb; + *sampleWriteLocCr = *sampleReadLocCr; + } + //************************************************* // Part 3: Create Reversed Reference Samples //************************************************* @@ -2986,20 +3104,37 @@ EB_ERRORTYPE GenerateChromaIntraReference16bitSamplesEncodePass( junk */ - memcpy16bit(cbBorderReverse + (puChromaSize<<1), cbBorder + (puChromaSize<<1), (puChromaSize<<1)+1); - memcpy16bit(crBorderReverse + (puChromaSize<<1), crBorder + (puChromaSize<<1), (puChromaSize<<1)+1); + memcpy16bit(cbBorderReverse + (puChromaSize << 1), cbBorder + (puChromaSize << 1), (puChromaSize << 1) + 1); + memcpy16bit(crBorderReverse + (puChromaSize << 1), crBorder + (puChromaSize << 1), (puChromaSize << 1) + 1); - sampleWriteLocCb = cbBorderReverse + (puChromaSize<<1) - 1 ; - sampleWriteLocCr = crBorderReverse + (puChromaSize<<1) - 1 ; + sampleWriteLocCb = cbBorderReverse + (puChromaSize<<1) - 1 ; + sampleWriteLocCr = crBorderReverse + (puChromaSize<<1) - 1 ; - for(i=0; i<(puChromaSize<<1) ;i++){ - - *sampleWriteLocCb = cbBorder[i]; - *sampleWriteLocCr = crBorder[i]; - sampleWriteLocCb--; - sampleWriteLocCr--; + for(i=0; i<(puChromaSize<<1); i++){ + *sampleWriteLocCb = cbBorder[i]; + *sampleWriteLocCr = crBorder[i]; + sampleWriteLocCb--; + sampleWriteLocCr--; + } + + if (colorFormat == EB_YUV444) { + memcpy16bit(cbBorderFiltReverse + (puChromaSize<<1), + cbBorderFilt + (puChromaSize << 1), (puChromaSize << 1) + 1); + memcpy16bit(crBorderFiltReverse + (puChromaSize<<1), + crBorderFilt + (puChromaSize << 1), (puChromaSize << 1) + 1); + + sampleWriteLocCbFilt = cbBorderFiltReverse + (puChromaSize << 1) - 1 ; + sampleWriteLocCrFilt = crBorderFiltReverse + (puChromaSize << 1) - 1 ; + + for(i = 0; i < (puChromaSize << 1) ;i++){ + *sampleWriteLocCbFilt = cbBorderFilt[i]; + *sampleWriteLocCrFilt = crBorderFilt[i]; + sampleWriteLocCbFilt--; + sampleWriteLocCrFilt--; + } } + return return_error; } @@ -3011,8 +3146,8 @@ static void IntraModeAngular_27To33( EB_U8 *predictionPtr, //output parameter, pointer to the prediction const EB_U32 predictionBufferStride) //input parameter, denotes the stride for the prediction ptr { - EB_U8 *refSampMain; - EB_S32 intraPredAngle = intraModeAngularTable[mode - INTRA_VERTICAL_MODE]; + EB_U8 *refSampMain; + EB_S32 intraPredAngle = intraModeAngularTable[mode - INTRA_VERTICAL_MODE]; refSampMain = refSamples + (size << 1); IntraAngVertical_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( @@ -3021,7 +3156,7 @@ static void IntraModeAngular_27To33( predictionPtr, predictionBufferStride, EB_FALSE, - intraPredAngle); + intraPredAngle); return; } @@ -3032,8 +3167,8 @@ static void IntraModeAngular16bit_27To33( EB_U16 *predictionPtr, //output parameter, pointer to the prediction const EB_U32 predictionBufferStride) //input parameter, denotes the stride for the prediction ptr { - EB_U16 *refSampMain; - EB_S32 intraPredAngle = intraModeAngularTable[mode - INTRA_VERTICAL_MODE]; + EB_U16 *refSampMain; + EB_S32 intraPredAngle = intraModeAngularTable[mode - INTRA_VERTICAL_MODE]; refSampMain = refSamples + (size << 1); IntraAngVertical_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( @@ -3042,7 +3177,7 @@ static void IntraModeAngular16bit_27To33( predictionPtr, predictionBufferStride, EB_FALSE, - intraPredAngle); + intraPredAngle); return; } @@ -3061,7 +3196,7 @@ static void IntraModeAngular_19To25( ) { EB_U8 *refSampMain; - EB_U8 *refSampSide; + EB_U8 *refSampSide; const EB_U32 numberOfSamples = size + 1; EB_S32 signIndex; const EB_U32 refOffset = (size << 1); @@ -3104,7 +3239,7 @@ static void IntraModeAngular_19To25( predictionPtr, predictionBufferStride, EB_FALSE, - intraPredAngle); + intraPredAngle); return; } @@ -3122,7 +3257,7 @@ static void IntraModeAngular16bit_19To25( ) { EB_U16 *refSampMain; - EB_U16 *refSampSide; + EB_U16 *refSampSide; const EB_U32 numberOfSamples = size + 1; EB_S32 signIndex; const EB_U32 refOffset = (size << 1); @@ -3165,7 +3300,7 @@ static void IntraModeAngular16bit_19To25( predictionPtr, predictionBufferStride, EB_FALSE, - intraPredAngle); + intraPredAngle); return; } @@ -3184,11 +3319,11 @@ static void IntraModeAngular_11To17( EB_BOOL *LeftReadyFlag) { EB_U8 *refSampMain; - EB_U8 *refSampSide; + EB_U8 *refSampSide; const EB_U32 numberOfSamples = size + 1; EB_S32 signIndex; const EB_U32 refOffset = (size << 1); - EB_U32 index; + EB_U32 index; EB_S32 intraPredAngle = intraModeAngularTableNegative[ mode - INTRA_HORIZONTAL_MODE]; EB_U32 invAngle = invIntraModeAngularTable[mode - INTRA_HORIZONTAL_MODE]; @@ -3222,7 +3357,7 @@ static void IntraModeAngular_11To17( predictionPtr, predictionBufferStride, EB_FALSE, - intraPredAngle); + intraPredAngle); return; } @@ -3239,11 +3374,11 @@ static void IntraModeAngular16bit_11To17( EB_BOOL *LeftReadyFlag) { EB_U16 *refSampMain; - EB_U16 *refSampSide; + EB_U16 *refSampSide; const EB_U32 numberOfSamples = size + 1; EB_S32 signIndex; const EB_U32 refOffset = (size << 1); - EB_U32 index; + EB_U32 index; EB_S32 intraPredAngle = intraModeAngularTableNegative[ mode - INTRA_HORIZONTAL_MODE]; EB_U32 invAngle = invIntraModeAngularTable[mode - INTRA_HORIZONTAL_MODE]; @@ -3277,7 +3412,7 @@ static void IntraModeAngular16bit_11To17( predictionPtr, predictionBufferStride, EB_FALSE, - intraPredAngle); + intraPredAngle); return; } @@ -3304,7 +3439,7 @@ static void IntraModeAngular_3To9( predictionPtr, predictionBufferStride, EB_FALSE, - intraPredAngle); + intraPredAngle); return; } @@ -3331,7 +3466,7 @@ static void IntraModeAngular16bit_3To9( predictionPtr, predictionBufferStride, EB_FALSE, - intraPredAngle); + intraPredAngle); return; } @@ -3540,8 +3675,8 @@ EB_ERRORTYPE IntraPredictionCl( EB_S32 diffModeB; EB_S32 diffMode; - EB_U8 *yIntraReferenceArray; - EB_U8 *yIntraReferenceArrayReverse; + EB_U8 *yIntraReferenceArray; + EB_U8 *yIntraReferenceArrayReverse; (void) pictureControlSetPtr; @@ -3577,7 +3712,7 @@ EB_ERRORTYPE IntraPredictionCl( case 0: yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : - contextPtr->yIntraReferenceArrayReverse; + contextPtr->yIntraReferenceArrayReverse; IntraPlanar_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( puSize, @@ -3590,7 +3725,7 @@ EB_ERRORTYPE IntraPredictionCl( case 1: - yIntraReferenceArray = contextPtr->yIntraReferenceArrayReverse; + yIntraReferenceArray = contextPtr->yIntraReferenceArrayReverse; IntraDCLuma_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( puSize, @@ -3618,7 +3753,7 @@ EB_ERRORTYPE IntraPredictionCl( case 3: yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : - contextPtr->yIntraReferenceArrayReverse; + contextPtr->yIntraReferenceArrayReverse; IntraHorzLuma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( puSize, @@ -3634,7 +3769,7 @@ EB_ERRORTYPE IntraPredictionCl( yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArray : contextPtr->yIntraReferenceArray; yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : - contextPtr->yIntraReferenceArrayReverse; + contextPtr->yIntraReferenceArrayReverse; IntraModeAngular_all( lumaMode, @@ -3852,8 +3987,8 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( EB_S32 diffModeB; EB_S32 diffMode; - EB_U8 *yIntraReferenceArray; - EB_U8 *yIntraReferenceArrayReverse; + EB_U8 *yIntraReferenceArray; + EB_U8 *yIntraReferenceArrayReverse; (void) puIndex; (void)pictureControlSetPtr; @@ -3882,7 +4017,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( case 0: yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : - contextPtr->yIntraReferenceArrayReverse; + contextPtr->yIntraReferenceArrayReverse; IntraPlanar_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( puSize, @@ -3895,7 +4030,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( case 1: - yIntraReferenceArray = contextPtr->yIntraReferenceArrayReverse; + yIntraReferenceArray = contextPtr->yIntraReferenceArrayReverse; IntraDCLuma_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( puSize, @@ -3923,7 +4058,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( case 3: yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : - contextPtr->yIntraReferenceArrayReverse; + contextPtr->yIntraReferenceArrayReverse; IntraHorzLuma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( puSize, @@ -3939,7 +4074,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArray : contextPtr->yIntraReferenceArray; yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : - contextPtr->yIntraReferenceArrayReverse; + contextPtr->yIntraReferenceArrayReverse; IntraModeAngular_all( lumaMode, @@ -4224,8 +4359,13 @@ EB_ERRORTYPE EncodePassIntraPrediction( EB_U32 originX, EB_U32 originY, EB_U32 puSize, + EB_U32 puChromaSize, EbPictureBufferDesc_t *predictionPtr, - EB_U32 lumaMode) + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 lumaMode, + EB_U32 chromaMode, + EB_U32 componentMask) { EB_ERRORTYPE return_error = EB_ErrorNone; IntraReferenceSamples_t *referenceSamples = (IntraReferenceSamples_t*)refSamples; @@ -4236,32 +4376,40 @@ EB_ERRORTYPE EncodePassIntraPrediction( EB_S32 diffModeB; EB_S32 diffMode; - EB_U8 *yIntraReferenceArray; - EB_U8 *yIntraReferenceArrayReverse; + EB_U8 *yIntraReferenceArray; + EB_U8 *yIntraReferenceArrayReverse; + + EB_U8 *cbIntraReferenceArray; + EB_U8 *cbIntraReferenceArrayReverse; + EB_U8 *crIntraReferenceArray; + EB_U8 *crIntraReferenceArrayReverse; EB_U32 chromaModeAdj; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; //*********************************** // Luma //*********************************** - { - + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { lumaOffset = (originY) * predictionPtr->strideY + (originX); + yIntraReferenceArray = referenceSamples->yIntraReferenceArray; + yIntraReferenceArrayReverse = referenceSamples->yIntraReferenceArrayReverse; diffModeA = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_HORIZONTAL_MODE); diffModeB = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_VERTICAL_MODE); diffMode = MIN(diffModeA, diffModeB); + if (diffMode > intraLumaFilterTable[Log2f(puSize) - 2] && lumaMode != EB_INTRA_DC) { + yIntraReferenceArray = referenceSamples->yIntraFilteredReferenceArray; + yIntraReferenceArrayReverse = referenceSamples->yIntraFilteredReferenceArrayReverse; + } + switch(lumaMode) { case EB_INTRA_PLANAR: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2])? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - IntraPlanar_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( puSize, - yIntraReferenceArray, + yIntraReferenceArrayReverse, predictionPtr->bufferY + lumaOffset, predictionPtr->strideY, EB_FALSE); @@ -4269,12 +4417,9 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; case EB_INTRA_DC: - - yIntraReferenceArray = referenceSamples->yIntraReferenceArrayReverse; - IntraDCLuma_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( puSize, - yIntraReferenceArray, + yIntraReferenceArrayReverse, predictionPtr->bufferY + lumaOffset, predictionPtr->strideY, EB_FALSE); @@ -4282,14 +4427,9 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; case EB_INTRA_VERTICAL: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - IntraVerticalLuma_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( puSize, - yIntraReferenceArray, + yIntraReferenceArrayReverse, predictionPtr->bufferY + lumaOffset, predictionPtr->strideY, EB_FALSE); @@ -4297,14 +4437,9 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; case EB_INTRA_HORIZONTAL: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - IntraHorzLuma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( puSize, - yIntraReferenceArray, + yIntraReferenceArrayReverse, predictionPtr->bufferY + lumaOffset, predictionPtr->strideY, EB_FALSE); @@ -4312,15 +4447,6 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; default: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArray : - referenceSamples->yIntraReferenceArray; - - yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - IntraModeAngular_all( lumaMode, puSize, @@ -4344,28 +4470,57 @@ EB_ERRORTYPE EncodePassIntraPrediction( //*********************************** // Chroma //*********************************** - { + if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { + if (secondChroma && colorFormat == EB_YUV422) { + chromaOffset = (originX >> subWidthCMinus1) + + ((originY + puChromaSize) >> subHeightCMinus1) * predictionPtr->strideCb; + } else { + chromaOffset = (originX >> subWidthCMinus1) + + (originY >> subHeightCMinus1) * predictionPtr->strideCb; + } - chromaOffset = ((originY) * predictionPtr->strideCb + (originX)) >> 1; chromaModeAdj = lumaMode; + chromaModeAdj = + (chromaMode == EB_INTRA_CHROMA_PLANAR) ? EB_INTRA_PLANAR : + (chromaMode == EB_INTRA_CHROMA_VERTICAL) ? EB_INTRA_VERTICAL : + (chromaMode == EB_INTRA_CHROMA_HORIZONTAL) ? EB_INTRA_HORIZONTAL : + (chromaMode == EB_INTRA_CHROMA_DC) ? EB_INTRA_DC : + (chromaMode == EB_INTRA_CHROMA_DM) ? lumaMode : EB_INTRA_MODE_INVALID; - switch(chromaModeAdj) { + cbIntraReferenceArray = referenceSamples->cbIntraReferenceArray; + cbIntraReferenceArrayReverse = referenceSamples->cbIntraReferenceArrayReverse; + crIntraReferenceArray = referenceSamples->crIntraReferenceArray; + crIntraReferenceArrayReverse = referenceSamples->crIntraReferenceArrayReverse; + + if (colorFormat == EB_YUV422) { + chromaModeAdj = intra422PredModeMap[chromaModeAdj]; + } else if (colorFormat == EB_YUV444) { + diffModeA = EB_ABS_DIFF((EB_S32)chromaModeAdj, (EB_S32)INTRA_HORIZONTAL_MODE); + diffModeB = EB_ABS_DIFF((EB_S32)chromaModeAdj, (EB_S32)INTRA_VERTICAL_MODE); + diffMode = MIN(diffModeA, diffModeB); + // Jing: diffMode > intraLumaFilterTable[Log2f(puSize) - 2] guarantee 4x4 case no filter will be done + if (diffMode > intraLumaFilterTable[Log2f(puSize) - 2] && chromaModeAdj != EB_INTRA_DC) { + cbIntraReferenceArray = referenceSamples->cbIntraFilteredReferenceArray; + crIntraReferenceArray = referenceSamples->crIntraFilteredReferenceArray; + cbIntraReferenceArrayReverse = referenceSamples->cbIntraFilteredReferenceArrayReverse; + crIntraReferenceArrayReverse = referenceSamples->crIntraFilteredReferenceArrayReverse; + } + } + switch(chromaModeAdj) { case EB_INTRA_PLANAR: - // Cb Intra Prediction IntraPlanar_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - puSize>>1, - referenceSamples->cbIntraReferenceArrayReverse, + puChromaSize, + cbIntraReferenceArrayReverse, predictionPtr->bufferCb + chromaOffset, predictionPtr->strideCb, EB_FALSE); - // Cr Intra Prediction IntraPlanar_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - puSize>>1, - referenceSamples->crIntraReferenceArrayReverse, + puChromaSize, + crIntraReferenceArrayReverse, predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, EB_FALSE); @@ -4376,16 +4531,16 @@ EB_ERRORTYPE EncodePassIntraPrediction( // Cb Intra Prediction IntraVerticalChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->cbIntraReferenceArray, + puChromaSize, + cbIntraReferenceArrayReverse, predictionPtr->bufferCb + chromaOffset, predictionPtr->strideCb, EB_FALSE); // Cr Intra Prediction IntraVerticalChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->crIntraReferenceArray, + puChromaSize, + crIntraReferenceArrayReverse, predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, EB_FALSE); @@ -4396,8 +4551,8 @@ EB_ERRORTYPE EncodePassIntraPrediction( // Cb Intra Prediction IntraHorzChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->cbIntraReferenceArrayReverse, + puChromaSize, + cbIntraReferenceArrayReverse, predictionPtr->bufferCb + chromaOffset, predictionPtr->strideCb, EB_FALSE); @@ -4405,8 +4560,8 @@ EB_ERRORTYPE EncodePassIntraPrediction( // Cr Intra Prediction IntraHorzChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->crIntraReferenceArrayReverse, + puChromaSize, + crIntraReferenceArrayReverse, predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, EB_FALSE); @@ -4417,8 +4572,8 @@ EB_ERRORTYPE EncodePassIntraPrediction( // Cb Intra Prediction IntraDCChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->cbIntraReferenceArrayReverse, + puChromaSize, + cbIntraReferenceArrayReverse, predictionPtr->bufferCb + chromaOffset, predictionPtr->strideCb, EB_FALSE); @@ -4426,8 +4581,8 @@ EB_ERRORTYPE EncodePassIntraPrediction( // Cr Intra Prediction IntraDCChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->crIntraReferenceArrayReverse, + puChromaSize, + crIntraReferenceArrayReverse, predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, EB_FALSE); @@ -4441,10 +4596,10 @@ EB_ERRORTYPE EncodePassIntraPrediction( // Cb Intra Prediction IntraModeAngular_all( - lumaMode, - puSize>>1, - referenceSamples->cbIntraReferenceArray, - referenceSamples->cbIntraReferenceArrayReverse, + chromaModeAdj, + puChromaSize, + cbIntraReferenceArray, + cbIntraReferenceArrayReverse, predictionPtr->bufferCb + chromaOffset, predictionPtr->strideCb, referenceSamples->ReferenceAboveLineCb, @@ -4454,10 +4609,10 @@ EB_ERRORTYPE EncodePassIntraPrediction( // Cr Intra Prediction IntraModeAngular_all( - lumaMode, - puSize>>1, - referenceSamples->crIntraReferenceArray, - referenceSamples->crIntraReferenceArrayReverse, + chromaModeAdj, + puChromaSize, + crIntraReferenceArray, + crIntraReferenceArrayReverse, predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, referenceSamples->ReferenceAboveLineCr, @@ -4485,10 +4640,15 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( EB_U32 originX, EB_U32 originY, EB_U32 puSize, + EB_U32 puChromaSize, EbPictureBufferDesc_t *predictionPtr, - EB_U32 lumaMode) + EB_COLOR_FORMAT colorFormat, + EB_BOOL secondChroma, + EB_U32 lumaMode, + EB_U32 chromaMode, + EB_U32 componentMask) { - EB_ERRORTYPE return_error = EB_ErrorNone; + EB_ERRORTYPE return_error = EB_ErrorNone; IntraReference16bitSamples_t *referenceSamples = (IntraReference16bitSamples_t *)refSamples; EB_U32 lumaOffset; EB_U32 chromaOffset; @@ -4497,60 +4657,58 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( EB_S32 diffModeB; EB_S32 diffMode; - EB_U16 *yIntraReferenceArray; - EB_U16 *yIntraReferenceArrayReverse; + EB_U16 *yIntraReferenceArray; + EB_U16 *yIntraReferenceArrayReverse; + + EB_U16 *cbIntraReferenceArray; + EB_U16 *cbIntraReferenceArrayReverse; + EB_U16 *crIntraReferenceArray; + EB_U16 *crIntraReferenceArrayReverse; EB_U32 chromaModeAdj; - + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + //*********************************** // Luma //*********************************** - { - - lumaOffset = (originY) * predictionPtr->strideY + (originX); - diffModeA = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_HORIZONTAL_MODE); - diffModeB = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_VERTICAL_MODE); - diffMode = MIN(diffModeA, diffModeB); + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { + lumaOffset = (originY) * predictionPtr->strideY + originX; + yIntraReferenceArray = referenceSamples->yIntraReferenceArray; + yIntraReferenceArrayReverse = referenceSamples->yIntraReferenceArrayReverse; + diffModeA = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_HORIZONTAL_MODE); + diffModeB = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_VERTICAL_MODE); + diffMode = MIN(diffModeA, diffModeB); + + if (diffMode > intraLumaFilterTable[Log2f(puSize) - 2] && lumaMode != EB_INTRA_DC) { + yIntraReferenceArray = referenceSamples->yIntraFilteredReferenceArray; + yIntraReferenceArrayReverse = referenceSamples->yIntraFilteredReferenceArrayReverse; + } switch(lumaMode) { case EB_INTRA_PLANAR: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2])? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - - IntraPlanar_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + IntraPlanar_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( puSize, - yIntraReferenceArray, + yIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferY + lumaOffset, predictionPtr->strideY, EB_FALSE); - break; case EB_INTRA_DC: - - yIntraReferenceArray = referenceSamples->yIntraReferenceArrayReverse; - - IntraDCLuma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + IntraDCLuma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( puSize, - yIntraReferenceArray, + yIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferY + lumaOffset, predictionPtr->strideY, EB_FALSE); - break; case EB_INTRA_VERTICAL: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - IntraVerticalLuma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( puSize, - yIntraReferenceArray, + yIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferY + lumaOffset, predictionPtr->strideY, EB_FALSE); @@ -4558,30 +4716,15 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( break; case EB_INTRA_HORIZONTAL: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - IntraHorzLuma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( puSize, - yIntraReferenceArray, + yIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferY + lumaOffset, predictionPtr->strideY, EB_FALSE); - break; default: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArray : - referenceSamples->yIntraReferenceArray; - - yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - IntraModeAngular16bit_all( lumaMode, puSize, @@ -4601,112 +4744,121 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( } } - + //*********************************** // Chroma //*********************************** - { - - chromaOffset = ((originY) * predictionPtr->strideCb + (originX)) >> 1; - chromaModeAdj = lumaMode; - + if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { + if (secondChroma && colorFormat == EB_YUV422) { + chromaOffset = (originX >> subWidthCMinus1) + + ((originY + puChromaSize) >> subHeightCMinus1) * predictionPtr->strideCb; + } else { + chromaOffset = (originX >> subWidthCMinus1) + + (originY >> subHeightCMinus1) * predictionPtr->strideCb; + } - switch(chromaModeAdj) { + chromaModeAdj = lumaMode; + chromaModeAdj = + (chromaMode == EB_INTRA_CHROMA_PLANAR) ? EB_INTRA_PLANAR : + (chromaMode == EB_INTRA_CHROMA_VERTICAL) ? EB_INTRA_VERTICAL : + (chromaMode == EB_INTRA_CHROMA_HORIZONTAL) ? EB_INTRA_HORIZONTAL : + (chromaMode == EB_INTRA_CHROMA_DC) ? EB_INTRA_DC : + (chromaMode == EB_INTRA_CHROMA_DM) ? lumaMode : EB_INTRA_MODE_INVALID; + + cbIntraReferenceArray = referenceSamples->cbIntraReferenceArray; + cbIntraReferenceArrayReverse = referenceSamples->cbIntraReferenceArrayReverse; + crIntraReferenceArray = referenceSamples->crIntraReferenceArray; + crIntraReferenceArrayReverse = referenceSamples->crIntraReferenceArrayReverse; + + if (colorFormat == EB_YUV422) { + chromaModeAdj = intra422PredModeMap[chromaModeAdj]; + } else if (colorFormat == EB_YUV444) { + diffModeA = EB_ABS_DIFF((EB_S32)chromaModeAdj, (EB_S32)INTRA_HORIZONTAL_MODE); + diffModeB = EB_ABS_DIFF((EB_S32)chromaModeAdj, (EB_S32)INTRA_VERTICAL_MODE); + diffMode = MIN(diffModeA, diffModeB); + // Jing: diffMode > intraLumaFilterTable[Log2f(puSize) - 2] guarantee 4x4 case no filter will be done + if (diffMode > intraLumaFilterTable[Log2f(puSize) - 2] && chromaModeAdj != EB_INTRA_DC) { + cbIntraReferenceArray = referenceSamples->cbIntraFilteredReferenceArray; + crIntraReferenceArray = referenceSamples->crIntraFilteredReferenceArray; + cbIntraReferenceArrayReverse = referenceSamples->cbIntraFilteredReferenceArrayReverse; + crIntraReferenceArrayReverse = referenceSamples->crIntraFilteredReferenceArrayReverse; + } + } + switch(chromaModeAdj) { case EB_INTRA_PLANAR: - - // Cb Intra Prediction - IntraPlanar_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->cbIntraReferenceArrayReverse, + IntraPlanar_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + puChromaSize, + cbIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferCb + chromaOffset, predictionPtr->strideCb, EB_FALSE); - - // Cr Intra Prediction - IntraPlanar_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->crIntraReferenceArrayReverse, + IntraPlanar_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + puChromaSize, + crIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, EB_FALSE); - break; case EB_INTRA_VERTICAL: - - // Cb Intra Prediction - IntraVerticalChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->cbIntraReferenceArray, - (EB_U16*)predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - EB_FALSE); + IntraVerticalChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + puChromaSize, + cbIntraReferenceArrayReverse, + (EB_U16*)predictionPtr->bufferCb + chromaOffset, + predictionPtr->strideCb, + EB_FALSE); - // Cr Intra Prediction - IntraVerticalChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->crIntraReferenceArray, + IntraVerticalChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + puChromaSize, + crIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, EB_FALSE); - break; case EB_INTRA_HORIZONTAL: - - // Cb Intra Prediction - IntraHorzChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->cbIntraReferenceArrayReverse, + IntraHorzChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + puChromaSize, + cbIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferCb + chromaOffset, predictionPtr->strideCb, EB_FALSE); - - // Cr Intra Prediction - IntraHorzChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->crIntraReferenceArrayReverse, + IntraHorzChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + puChromaSize, + crIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, EB_FALSE); - break; case EB_INTRA_DC: - - // Cb Intra Prediction - IntraDCChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->cbIntraReferenceArrayReverse, + IntraDCChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + puChromaSize, + cbIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferCb + chromaOffset, predictionPtr->strideCb, EB_FALSE); - - // Cr Intra Prediction - IntraDCChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize>>1, - referenceSamples->crIntraReferenceArrayReverse, + IntraDCChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( + puChromaSize, + crIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, EB_FALSE); - break; default: // *Note - For Chroma DM mode, use the Luma Angular mode // to generate the prediction. - - // Cb Intra Prediction IntraModeAngular16bit_all( - lumaMode, - puSize>>1, - referenceSamples->cbIntraReferenceArray, - referenceSamples->cbIntraReferenceArrayReverse, + chromaModeAdj, + puChromaSize, + cbIntraReferenceArray, + cbIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferCb + chromaOffset, predictionPtr->strideCb, referenceSamples->ReferenceAboveLineCb, @@ -4714,19 +4866,17 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( referenceSamples->ReferenceLeftLineCb, &referenceSamples->LeftReadyFlagCb); - // Cr Intra Prediction IntraModeAngular16bit_all( - lumaMode, - puSize>>1, - referenceSamples->crIntraReferenceArray, - referenceSamples->crIntraReferenceArrayReverse, + chromaModeAdj, + puChromaSize, + crIntraReferenceArray, + crIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferCr + chromaOffset, predictionPtr->strideCr, referenceSamples->ReferenceAboveLineCr, &referenceSamples->AboveReadyFlagCr, referenceSamples->ReferenceLeftLineCr, &referenceSamples->LeftReadyFlagCr); - break; case EB_INTRA_MODE_INVALID: @@ -4737,561 +4887,22 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( return return_error; } - -/********************************************* - * Encode Pass Intra Prediction - * Calculates a conformant H.265 prediction - * for an Intra Prediction Unit - *********************************************/ -EB_ERRORTYPE EncodePassIntra4x4Prediction( - IntraReferenceSamples_t *referenceSamples, - EB_U32 originX, - EB_U32 originY, - EB_U32 puSize, - EB_U32 chromaPuSize, - EbPictureBufferDesc_t *predictionPtr, - EB_U32 lumaMode, - EB_U32 chromaMode, - EB_U32 componentMask) +/********************************************** + * Intra Reference Samples Ctor + **********************************************/ +EB_ERRORTYPE IntraOpenLoopReferenceSamplesCtor( + IntraReferenceSamplesOpenLoop_t **contextDblPtr) { - EB_ERRORTYPE return_error = EB_ErrorNone; + IntraReferenceSamplesOpenLoop_t *contextPtr; + EB_MALLOC(IntraReferenceSamplesOpenLoop_t*, contextPtr, sizeof(IntraReferenceSamplesOpenLoop_t), EB_N_PTR); - EB_U32 lumaOffset; - EB_U32 chromaOffset; - - EB_S32 diffModeA; - EB_S32 diffModeB; - EB_S32 diffMode; - - EB_U8 *yIntraReferenceArray; - EB_U8 *yIntraReferenceArrayReverse; - - EB_U32 chromaModeAdj; - - //*********************************** - // Luma - //*********************************** - if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { - - lumaOffset = (originY) * predictionPtr->strideY + (originX); - diffModeA = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_HORIZONTAL_MODE); - diffModeB = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_VERTICAL_MODE); - diffMode = MIN(diffModeA, diffModeB); - - switch(lumaMode) { - - case EB_INTRA_PLANAR: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2])? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - - IntraPlanar_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - puSize, - yIntraReferenceArray, - predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - EB_FALSE); - - break; - - case EB_INTRA_DC: - - yIntraReferenceArray = referenceSamples->yIntraReferenceArrayReverse; - - IntraDCLuma_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - puSize, - yIntraReferenceArray, - predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - EB_FALSE); - - break; - - case EB_INTRA_VERTICAL: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - - IntraVerticalLuma_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - puSize, - yIntraReferenceArray, - predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - EB_FALSE); - - break; - - case EB_INTRA_HORIZONTAL: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - - IntraHorzLuma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize, - yIntraReferenceArray, - predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - EB_FALSE); - - break; - - default: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArray : - referenceSamples->yIntraReferenceArray; - - yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - - IntraModeAngular_all( - lumaMode, - puSize, - yIntraReferenceArray, - yIntraReferenceArrayReverse, - predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - referenceSamples->ReferenceAboveLineY, - &referenceSamples->AboveReadyFlagY, - referenceSamples->ReferenceLeftLineY, - &referenceSamples->LeftReadyFlagY); - - break; - - case EB_INTRA_MODE_INVALID: - break; - - } - } - - //*********************************** - // Chroma - //*********************************** - if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { - - chromaOffset = ((originY) * predictionPtr->strideCb + (originX)) >> 1; - chromaModeAdj = - (chromaMode == EB_INTRA_CHROMA_PLANAR) ? EB_INTRA_PLANAR : - (chromaMode == EB_INTRA_CHROMA_VERTICAL) ? EB_INTRA_VERTICAL : - (chromaMode == EB_INTRA_CHROMA_HORIZONTAL) ? EB_INTRA_HORIZONTAL : - (chromaMode == EB_INTRA_CHROMA_DC) ? EB_INTRA_DC : - (chromaMode == EB_INTRA_CHROMA_DM) ? lumaMode : EB_INTRA_MODE_INVALID; - - switch(chromaModeAdj) { - - case EB_INTRA_PLANAR: - - // Cb Intra Prediction - IntraPlanar_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->cbIntraReferenceArrayReverse, - predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - EB_FALSE); - - - // Cr Intra Prediction - IntraPlanar_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->crIntraReferenceArrayReverse, - predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - EB_FALSE); - - break; - - case EB_INTRA_VERTICAL: - - // Cb Intra Prediction - IntraVerticalChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->cbIntraReferenceArray, - predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - EB_FALSE); - - // Cr Intra Prediction - IntraVerticalChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->crIntraReferenceArray, - predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - EB_FALSE); - - break; - - case EB_INTRA_HORIZONTAL: - - // Cb Intra Prediction - IntraHorzChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->cbIntraReferenceArrayReverse, - predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - EB_FALSE); - - - // Cr Intra Prediction - IntraHorzChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->crIntraReferenceArrayReverse, - predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - EB_FALSE); - - break; - - case EB_INTRA_DC: - - // Cb Intra Prediction - IntraDCChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->cbIntraReferenceArrayReverse, - predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - EB_FALSE); - - - // Cr Intra Prediction - IntraDCChroma_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->crIntraReferenceArrayReverse, - predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - EB_FALSE); - - break; - - default: - - // *Note - For Chroma DM mode, use the Luma Angular mode - // to generate the prediction. - - // Cb Intra Prediction - IntraModeAngular_all( - lumaMode, - chromaPuSize, - referenceSamples->cbIntraReferenceArray, - referenceSamples->cbIntraReferenceArrayReverse, - predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - referenceSamples->ReferenceAboveLineCb, - &referenceSamples->AboveReadyFlagCb, - referenceSamples->ReferenceLeftLineCb, - &referenceSamples->LeftReadyFlagCb); - - // Cr Intra Prediction - IntraModeAngular_all( - lumaMode, - chromaPuSize, - referenceSamples->crIntraReferenceArray, - referenceSamples->crIntraReferenceArrayReverse, - predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - referenceSamples->ReferenceAboveLineCr, - &referenceSamples->AboveReadyFlagCr, - referenceSamples->ReferenceLeftLineCr, - &referenceSamples->LeftReadyFlagCr); - - break; - - case EB_INTRA_MODE_INVALID: - break; - } - } - - return return_error; -} - -/********************************************* - * Encode Pass Intra Prediction 16bit - * Calculates a conformant H.265 prediction - * for an Intra Prediction Unit - *********************************************/ -EB_ERRORTYPE EncodePassIntra4x4Prediction16bit( - IntraReference16bitSamples_t *referenceSamples, - EB_U32 originX, - EB_U32 originY, - EB_U32 puSize, - EB_U32 chromaPuSize, - EbPictureBufferDesc_t *predictionPtr, - EB_U32 lumaMode, - EB_U32 chromaMode, - EB_U32 componentMask) -{ - EB_ERRORTYPE return_error = EB_ErrorNone; - - EB_U32 lumaOffset; - EB_U32 chromaOffset; - - EB_S32 diffModeA; - EB_S32 diffModeB; - EB_S32 diffMode; - - EB_U16 *yIntraReferenceArray; - EB_U16 *yIntraReferenceArrayReverse; - - EB_U32 chromaModeAdj; - - //*********************************** - // Luma - //*********************************** - if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { - - lumaOffset = (originY) * predictionPtr->strideY + (originX); - diffModeA = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_HORIZONTAL_MODE); - diffModeB = EB_ABS_DIFF((EB_S32) lumaMode,(EB_S32) INTRA_VERTICAL_MODE); - diffMode = MIN(diffModeA, diffModeB); - - switch(lumaMode) { - - case EB_INTRA_PLANAR: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2])? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - - IntraPlanar_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize, - yIntraReferenceArray, - (EB_U16*)predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - EB_FALSE); - - break; - - case EB_INTRA_DC: - - yIntraReferenceArray = referenceSamples->yIntraReferenceArrayReverse; - - IntraDCLuma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize, - yIntraReferenceArray, - (EB_U16*)predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - EB_FALSE); - - break; - - case EB_INTRA_VERTICAL: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - - IntraVerticalLuma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize, - yIntraReferenceArray, - (EB_U16*)predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - EB_FALSE); - - break; - - case EB_INTRA_HORIZONTAL: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - - IntraHorzLuma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - puSize, - yIntraReferenceArray, - (EB_U16*)predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - EB_FALSE); - - break; - - default: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArray : - referenceSamples->yIntraReferenceArray; - - yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puSize)-2]) ? - referenceSamples->yIntraFilteredReferenceArrayReverse : - referenceSamples->yIntraReferenceArrayReverse; - - IntraModeAngular16bit_all( - lumaMode, - puSize, - yIntraReferenceArray, - yIntraReferenceArrayReverse, - (EB_U16*)predictionPtr->bufferY + lumaOffset, - predictionPtr->strideY, - referenceSamples->ReferenceAboveLineY, - &referenceSamples->AboveReadyFlagY, - referenceSamples->ReferenceLeftLineY, - &referenceSamples->LeftReadyFlagY); - - break; - - case EB_INTRA_MODE_INVALID: - break; - - } - } - - //*********************************** - // Chroma - //*********************************** - if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { - - chromaOffset = ((originY) * predictionPtr->strideCb + (originX)) >> 1; - chromaModeAdj = - (chromaMode == EB_INTRA_CHROMA_PLANAR) ? EB_INTRA_PLANAR : - (chromaMode == EB_INTRA_CHROMA_VERTICAL) ? EB_INTRA_VERTICAL : - (chromaMode == EB_INTRA_CHROMA_HORIZONTAL) ? EB_INTRA_HORIZONTAL : - (chromaMode == EB_INTRA_CHROMA_DC) ? EB_INTRA_DC : - (chromaMode == EB_INTRA_CHROMA_DM) ? lumaMode : EB_INTRA_MODE_INVALID; - - switch(chromaModeAdj) { - - case EB_INTRA_PLANAR: - - // Cb Intra Prediction - IntraPlanar_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->cbIntraReferenceArrayReverse, - (EB_U16*)predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - EB_FALSE); - - - // Cr Intra Prediction - IntraPlanar_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->crIntraReferenceArrayReverse, - (EB_U16*)predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - EB_FALSE); - - break; - - case EB_INTRA_VERTICAL: - - // Cb Intra Prediction - IntraVerticalChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->cbIntraReferenceArray, - (EB_U16*)predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - EB_FALSE); - - // Cr Intra Prediction - IntraVerticalChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->crIntraReferenceArray, - (EB_U16*)predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - EB_FALSE); - - break; - - case EB_INTRA_HORIZONTAL: - - // Cb Intra Prediction - IntraHorzChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->cbIntraReferenceArrayReverse, - (EB_U16*)predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - EB_FALSE); - - - // Cr Intra Prediction - IntraHorzChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->crIntraReferenceArrayReverse, - (EB_U16*)predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - EB_FALSE); - - break; - - case EB_INTRA_DC: - - // Cb Intra Prediction - IntraDCChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->cbIntraReferenceArrayReverse, - (EB_U16*)predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - EB_FALSE); - - - // Cr Intra Prediction - IntraDCChroma_16bit_funcPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1]( - chromaPuSize, - referenceSamples->crIntraReferenceArrayReverse, - (EB_U16*)predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - EB_FALSE); - - break; - - default: - - // *Note - For Chroma DM mode, use the Luma Angular mode - // to generate the prediction. - - // Cb Intra Prediction - IntraModeAngular16bit_all( - lumaMode, - chromaPuSize, - referenceSamples->cbIntraReferenceArray, - referenceSamples->cbIntraReferenceArrayReverse, - (EB_U16*)predictionPtr->bufferCb + chromaOffset, - predictionPtr->strideCb, - referenceSamples->ReferenceAboveLineCb, - &referenceSamples->AboveReadyFlagCb, - referenceSamples->ReferenceLeftLineCb, - &referenceSamples->LeftReadyFlagCb); - - // Cr Intra Prediction - IntraModeAngular16bit_all( - lumaMode, - chromaPuSize, - referenceSamples->crIntraReferenceArray, - referenceSamples->crIntraReferenceArrayReverse, - (EB_U16*)predictionPtr->bufferCr + chromaOffset, - predictionPtr->strideCr, - referenceSamples->ReferenceAboveLineCr, - &referenceSamples->AboveReadyFlagCr, - referenceSamples->ReferenceLeftLineCr, - &referenceSamples->LeftReadyFlagCr); - - break; - - case EB_INTRA_MODE_INVALID: - break; - } - } - - return return_error; -} - -/********************************************** - * Intra Reference Samples Ctor - **********************************************/ -EB_ERRORTYPE IntraOpenLoopReferenceSamplesCtor( - IntraReferenceSamplesOpenLoop_t **contextDblPtr) -{ - IntraReferenceSamplesOpenLoop_t *contextPtr; - EB_MALLOC(IntraReferenceSamplesOpenLoop_t*, contextPtr, sizeof(IntraReferenceSamplesOpenLoop_t), EB_N_PTR); - - *contextDblPtr = contextPtr; + *contextDblPtr = contextPtr; EB_MALLOC(EB_U8*, contextPtr->yIntraReferenceArray, sizeof(EB_U8) * (4 * MAX_LCU_SIZE + 1), EB_N_PTR); EB_MALLOC(EB_U8*, contextPtr->yIntraReferenceArrayReverse, sizeof(EB_U8) * (4 * MAX_LCU_SIZE + 2), EB_N_PTR); - contextPtr->yIntraReferenceArrayReverse++; + contextPtr->yIntraReferenceArrayReverse++; return EB_ErrorNone; } @@ -5332,14 +4943,14 @@ EB_ERRORTYPE UpdateNeighborSamplesArrayOL( dstPtr = yBorderReverse; //Initialise the Luma Intra Reference Array to the mid range value 128 (for CUs at the picture boundaries) - EB_MEMSET(dstPtr, MIDRANGE_VALUE_8BIT, (blockSize << 2) + 1); + EB_MEMSET(dstPtr, MIDRANGE_VALUE_8BIT, (blockSize << 2) + 1); // Get the left-column count = blockSizeHalf; if (pictureLeftBoundary == EB_FALSE) { readPtr = srcPtr - 1; - count = ((srcOriginY + count) > height) ? count - ((srcOriginY + count) - height) : count; + count = ((srcOriginY + count) > height) ? count - ((srcOriginY + count) - height) : count; for(idx = 0; idx < count; ++idx) { @@ -5371,7 +4982,7 @@ EB_ERRORTYPE UpdateNeighborSamplesArrayOL( if (pictureTopBoundary == EB_FALSE) { readPtr = srcPtr - stride; - count = ((srcOriginX + count) > width) ? count - ((srcOriginX + count) - width) : count; + count = ((srcOriginX + count) > width) ? count - ((srcOriginX + count) - width) : count; EB_MEMCPY(dstPtr, readPtr, count); dstPtr += (blockSizeHalf - count); @@ -5400,12 +5011,12 @@ EB_ERRORTYPE UpdateNeighborSamplesArrayOL( */ EB_MEMCPY(yBorder + blockSizeHalf, yBorderReverse + blockSizeHalf, blockSizeHalf + 1); - yBorderLoc = yBorder + blockSizeHalf - 1 ; + yBorderLoc = yBorder + blockSizeHalf - 1 ; for(count = 0; count< blockSizeHalf; count++){ *yBorderLoc = yBorderReverse[count]; - yBorderLoc--; + yBorderLoc--; } return return_error; @@ -5458,7 +5069,7 @@ EB_ERRORTYPE UpdateChromaNeighborSamplesArrayOL( cbDstPtr = cbBorderReverse; crDstPtr = crBorderReverse; //Initialise the Luma Intra Reference Array to the mid range value 128 (for CUs at the picture boundaries) - EB_MEMSET(cbDstPtr, MIDRANGE_VALUE_8BIT, (blockSize << 2) + 1); + EB_MEMSET(cbDstPtr, MIDRANGE_VALUE_8BIT, (blockSize << 2) + 1); EB_MEMSET(crDstPtr, MIDRANGE_VALUE_8BIT, (blockSize << 2) + 1); (void)stride; @@ -5469,7 +5080,7 @@ EB_ERRORTYPE UpdateChromaNeighborSamplesArrayOL( cbReadPtr = cbSrcPtr - 1; crReadPtr = crSrcPtr - 1; - count = ((cuChromaOriginY + count) > height) ? count - ((cuChromaOriginY + count) - height) : count; + count = ((cuChromaOriginY + count) > height) ? count - ((cuChromaOriginY + count) - height) : count; for(idx = 0; idx < count; ++idx) { @@ -5512,7 +5123,7 @@ EB_ERRORTYPE UpdateChromaNeighborSamplesArrayOL( cbReadPtr = cbSrcPtr - cbStride; - count = ((cuChromaOriginX + count) > width) ? count - ((cuChromaOriginX + count) - width) : count; + count = ((cuChromaOriginX + count) > width) ? count - ((cuChromaOriginX + count) - width) : count; EB_MEMCPY(cbDstPtr, cbReadPtr, count); cbDstPtr += (blockSizeHalf - count); @@ -5550,16 +5161,16 @@ EB_ERRORTYPE UpdateChromaNeighborSamplesArrayOL( EB_MEMCPY(cbBorder + blockSizeHalf, cbBorderReverse + blockSizeHalf, blockSizeHalf + 1); EB_MEMCPY(crBorder + blockSizeHalf, crBorderReverse + blockSizeHalf, blockSizeHalf + 1); - cbBorderLoc = cbBorder + blockSizeHalf - 1 ; + cbBorderLoc = cbBorder + blockSizeHalf - 1 ; crBorderLoc = crBorder + blockSizeHalf - 1 ; for(count = 0; count< blockSizeHalf; count++){ *cbBorderLoc = cbBorderReverse[count]; - cbBorderLoc--; + cbBorderLoc--; *crBorderLoc = crBorderReverse[count]; - crBorderLoc--; + crBorderLoc--; } return return_error; @@ -5584,7 +5195,7 @@ EB_ERRORTYPE UpdateNeighborSamplesArrayOpenLoop( EB_U8 *dstPtr; EB_U8 *readPtr; - EB_U32 count; + EB_U32 count; EB_U8 *yBorderReverse = intraRefPtr->yIntraReferenceArrayReverse; EB_U8 *yBorder = intraRefPtr->yIntraReferenceArray; @@ -5601,7 +5212,7 @@ EB_ERRORTYPE UpdateNeighborSamplesArrayOpenLoop( dstPtr = yBorderReverse; //Initialise the Luma Intra Reference Array to the mid range value 128 (for CUs at the picture boundaries) - EB_MEMSET(dstPtr, MIDRANGE_VALUE_8BIT, (blockSize << 2) + 1); + EB_MEMSET(dstPtr, MIDRANGE_VALUE_8BIT, (blockSize << 2) + 1); // Get the left-column count = blockSizeHalf; @@ -5609,7 +5220,7 @@ EB_ERRORTYPE UpdateNeighborSamplesArrayOpenLoop( if (srcOriginX != 0) { readPtr = srcPtr - 1; - count = ((srcOriginY + count) > height) ? count - ((srcOriginY + count) - height) : count; + count = ((srcOriginY + count) > height) ? count - ((srcOriginY + count) - height) : count; for(idx = 0; idx < count; ++idx) { @@ -5642,7 +5253,7 @@ EB_ERRORTYPE UpdateNeighborSamplesArrayOpenLoop( readPtr = srcPtr - stride; - count = ((srcOriginX + count) > width) ? count - ((srcOriginX + count) - width) : count; + count = ((srcOriginX + count) > width) ? count - ((srcOriginX + count) - width) : count; EB_MEMCPY(dstPtr, readPtr, count); dstPtr += (blockSizeHalf - count); @@ -5671,12 +5282,12 @@ EB_ERRORTYPE UpdateNeighborSamplesArrayOpenLoop( */ EB_MEMCPY(yBorder + blockSizeHalf, yBorderReverse + blockSizeHalf, blockSizeHalf + 1); - yBorderLoc = yBorder + blockSizeHalf - 1 ; + yBorderLoc = yBorder + blockSizeHalf - 1 ; for(count = 0; countmotionCompensationIntermediateResultBuf0, sizeof(EB_S16)*(maxCUWidth*maxCUHeight * 3 / 2 + 8), EB_N_PTR); //Y + U + V + // Jing: increase the size for 422/444 + EB_MALLOC(EB_S16*, contextPtr->motionCompensationIntermediateResultBuf0, sizeof(EB_S16)*(maxCUWidth*maxCUHeight * 3 + 8), EB_N_PTR); //Y + U + V - EB_MALLOC(EB_S16*, contextPtr->motionCompensationIntermediateResultBuf1, sizeof(EB_S16)*(maxCUWidth*maxCUHeight * 3 / 2 + 8), EB_N_PTR); //Y + U + V + EB_MALLOC(EB_S16*, contextPtr->motionCompensationIntermediateResultBuf1, sizeof(EB_S16)*(maxCUWidth*maxCUHeight * 3 + 8), EB_N_PTR); //Y + U + V - EB_MALLOC(EB_BYTE, contextPtr->avcStyleMcpIntermediateResultBuf0, sizeof(EB_U8)*maxCUWidth*maxCUHeight * 6 * 3 / 2 + 16, EB_N_PTR); //Y + U + V; + EB_MALLOC(EB_BYTE, contextPtr->avcStyleMcpIntermediateResultBuf0, sizeof(EB_U8)*maxCUWidth*maxCUHeight * 6 * 3 + 16, EB_N_PTR); //Y + U + V; - EB_MALLOC(EB_BYTE, contextPtr->avcStyleMcpIntermediateResultBuf1, sizeof(EB_U8)*maxCUWidth*maxCUHeight * 6 * 3 / 2 + 16, EB_N_PTR); //Y + U + V; + EB_MALLOC(EB_BYTE, contextPtr->avcStyleMcpIntermediateResultBuf1, sizeof(EB_U8)*maxCUWidth*maxCUHeight * 6 * 3 + 16, EB_N_PTR); //Y + U + V; #if !USE_PRE_COMPUTE EB_MALLOC(EB_S16*, contextPtr->TwoDInterpolationFirstPassFilterResultBuf, sizeof(EB_S16)*(maxCUWidth + MaxHorizontalLumaFliterTag - 1)*(maxCUHeight + MaxVerticalLumaFliterTag - 1), EB_N_PTR); // to be modified @@ -58,6 +59,7 @@ EB_ERRORTYPE MotionCompensationPredictionContextCtor( initData.maxWidth = maxCUWidth + 16; // +8 needed for interpolation; the rest to accommodate MCP assembly kernels initData.maxHeight = maxCUHeight + 16; // +8 needed for interpolation; the rest to accommodate MCP assembly kernels initData.bitDepth = EB_16BIT; + initData.colorFormat = EB_YUV420; initData.leftPadding = 0; initData.rightPadding = 0; initData.topPadding = 0; @@ -186,8 +188,12 @@ void EncodeUniPredInterpolation( EB_U32 integPosy; EB_U8 fracPosx; EB_U8 fracPosy; - EB_U32 chromaPuWidth = puWidth >> 1; - EB_U32 chromaPuHeight = puHeight >> 1; + + EB_COLOR_FORMAT colorFormat=dst->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + EB_U32 chromaPuWidth = puWidth >> subWidthCMinus1; + EB_U32 chromaPuHeight = puHeight >> subHeightCMinus1; (void)tempBuf1; @@ -210,10 +216,10 @@ void EncodeUniPredInterpolation( //chroma //compute the chroma fractional position - integPosx = (posX >> 3); - integPosy = (posY >> 3); - fracPosx = posX & 0x07; - fracPosy = posY & 0x07; + integPosx = (posX >> (2 + subWidthCMinus1)); + integPosy = (posY >> (2 + subHeightCMinus1)); + fracPosx = (posX & (0x07 >> (1-subWidthCMinus1))) << (1-subWidthCMinus1); + fracPosy = (posY & (0x07 >> (1-subHeightCMinus1))) << (1-subHeightCMinus1); uniPredChromaIFFunctionPtrArrayNew[(ASM_TYPES & PREAVX2_MASK) && 1][fracPosx + (fracPosy << 3)]( @@ -252,12 +258,15 @@ void UniPredInterpolation16bit( EB_U32 dstChromaIndex, //input parameter, please refer to the detailed explanation above. EB_S16 *tempBuf0) //input parameter, please refer to the detailed explanation above. { + const EB_COLOR_FORMAT colorFormat=dst->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; //EB_U32 integPosx; //EB_U32 integPosy; - EB_U8 fracPosx; - EB_U8 fracPosy; - EB_U32 chromaPuWidth = puWidth >> 1; - EB_U32 chromaPuHeight = puHeight >> 1; + EB_U8 fracPosx; + EB_U8 fracPosy; + EB_U32 chromaPuWidth = puWidth >> subWidthCMinus1; + EB_U32 chromaPuHeight = puHeight >> subHeightCMinus1; //EB_U32 position; (void)refPic; @@ -266,8 +275,8 @@ void UniPredInterpolation16bit( //compute the luma fractional position // integPosx = (posX >> 2); // integPosy = (posY >> 2); - fracPosx = posX & 0x03; - fracPosy = posY & 0x03; + fracPosx = posX & 0x03; + fracPosy = posY & 0x03; uniPredLuma16bitIFFunctionPtrArray[(ASM_TYPES & PREAVX2_MASK) && 1][fracPosx + (fracPosy << 2)]( (EB_U16 *)fullPelBlock->bufferY + 4 + 4 * fullPelBlock->strideY, @@ -282,8 +291,8 @@ void UniPredInterpolation16bit( //compute the chroma fractional position //integPosx = (posX >> 3); //integPosy = (posY >> 3); - fracPosx = posX & 0x07; - fracPosy = posY & 0x07; + fracPosx = (posX & (0x07 >> (1 - subWidthCMinus1))) << (1 - subWidthCMinus1); + fracPosy = (posY & (0x07 >> (1 - subHeightCMinus1))) << (1 - subHeightCMinus1); uniPredChromaIFFunctionPtrArrayNew16bit[(ASM_TYPES & AVX2_MASK) && 1][fracPosx + (fracPosy << 3)]( (EB_U16 *)fullPelBlock->bufferCb + 2 + 2 * fullPelBlock->strideCb, @@ -570,8 +579,13 @@ void EncodeBiPredInterpolation( EB_U32 integPosy; EB_U8 fracPosx; EB_U8 fracPosy; - EB_U32 chromaPuWidth = puWidth >> 1; - EB_U32 chromaPuHeight = puHeight >> 1; + + const EB_COLOR_FORMAT colorFormat = biDst->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + + EB_U32 chromaPuWidth = puWidth >> subWidthCMinus1; + EB_U32 chromaPuHeight = puHeight >> subHeightCMinus1; EB_U32 lumaTempBufSize = puWidth * puHeight; EB_U32 chromaTempBufSize = chromaPuWidth * chromaPuHeight; @@ -657,17 +671,18 @@ void EncodeBiPredInterpolation( //uni-prediction List0 chroma //compute the chroma fractional position - integPosL0x = (refList0PosX >> 3); - integPosL0y = (refList0PosY >> 3); - fracPosL0x = refList0PosX & 0x07; - fracPosL0y = refList0PosY & 0x07; + integPosL0x = (refList0PosX >> (2 + subWidthCMinus1)); + integPosL0y = (refList0PosY >> (2 + subHeightCMinus1)); + fracPosL0x = (refList0PosX & (0x07 >> (1-subWidthCMinus1))) << (1-subWidthCMinus1); + fracPosL0y = (refList0PosY & (0x07 >> (1-subHeightCMinus1))) << (1-subHeightCMinus1); + //uni-prediction List1 chroma //compute the chroma fractional position - integPosL1x = (refList1PosX >> 3); - integPosL1y = (refList1PosY >> 3); - fracPosL1x = refList1PosX & 0x07; - fracPosL1y = refList1PosY & 0x07; + integPosL1x = (refList1PosX >> (2 + subWidthCMinus1)); + integPosL1y = (refList1PosY >> (2 + subHeightCMinus1)); + fracPosL1x = (refList1PosX & (0x07 >> (1-subWidthCMinus1))) << (1-subWidthCMinus1); + fracPosL1y = (refList1PosY & (0x07 >> (1-subHeightCMinus1))) << (1-subHeightCMinus1); if (((fracPosL0x + (fracPosL0y << 3)) == 0) && ((fracPosL1x + (fracPosL1y << 3)) == 0)) { @@ -701,10 +716,10 @@ void EncodeBiPredInterpolation( { //uni-prediction List0 chroma //compute the chroma fractional position - integPosx = (refList0PosX >> 3); - integPosy = (refList0PosY >> 3); - fracPosx = refList0PosX & 0x07; - fracPosy = refList0PosY & 0x07; + integPosx = (refList0PosX >> (2 + subWidthCMinus1)); + integPosy = (refList0PosY >> (2 + subHeightCMinus1)); + fracPosx = (refList0PosX & (0x07 >> (1-subWidthCMinus1))) << (1-subWidthCMinus1); + fracPosy = (refList0PosY & (0x07 >> (1-subHeightCMinus1))) << (1-subHeightCMinus1); //doing the chroma Cb interpolation biPredChromaIFFunctionPtrArrayNew[(ASM_TYPES & PREAVX2_MASK) && 1][fracPosx + (fracPosy << 3)]( @@ -731,10 +746,15 @@ void EncodeBiPredInterpolation( //uni-prediction List1 chroma //compute the chroma fractional position - integPosx = (refList1PosX >> 3); - integPosy = (refList1PosY >> 3); - fracPosx = refList1PosX & 0x07; - fracPosy = refList1PosY & 0x07; + //integPosx = (refList1PosX >> 3); + //integPosy = (refList1PosY >> 3); + //fracPosx = refList1PosX & 0x07; + //fracPosy = refList1PosY & 0x07; + + integPosx = (refList1PosX >> (2 + subWidthCMinus1)); + integPosy = (refList1PosY >> (2 + subHeightCMinus1)); + fracPosx = (refList1PosX & (0x07 >> (1-subWidthCMinus1))) << (1-subWidthCMinus1); + fracPosy = (refList1PosY & (0x07 >> (1-subHeightCMinus1))) << (1-subHeightCMinus1); //doing the chroma Cb interpolation biPredChromaIFFunctionPtrArrayNew[(ASM_TYPES & PREAVX2_MASK) && 1][fracPosx + (fracPosy << 3)]( @@ -801,8 +821,11 @@ void BiPredInterpolation16bit( //EB_U32 integPosy; EB_U8 fracPosx; EB_U8 fracPosy; - EB_U32 chromaPuWidth = puWidth >> 1; - EB_U32 chromaPuHeight = puHeight >> 1; + const EB_COLOR_FORMAT colorFormat = biDst->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + EB_U32 chromaPuWidth = puWidth >> subWidthCMinus1; + EB_U32 chromaPuHeight = puHeight >> subHeightCMinus1; EB_U32 lumaTempBufSize = puWidth * puHeight; EB_U32 chromaTempBufSize = chromaPuWidth * chromaPuHeight; @@ -873,11 +896,11 @@ void BiPredInterpolation16bit( biDst->strideY); } - fracPosL0x = refList0PosX & 0x07; - fracPosL0y = refList0PosY & 0x07; + fracPosL0x = (refList0PosX & (0x07 >> (1 - subWidthCMinus1))) << (1 - subWidthCMinus1); + fracPosL0y = (refList0PosY & (0x07 >> (1 - subHeightCMinus1))) << (1 - subHeightCMinus1); - fracPosL1x = refList1PosX & 0x07; - fracPosL1y = refList1PosY & 0x07; + fracPosL1x = (refList1PosX & (0x07 >> (1 - subWidthCMinus1))) << (1 - subWidthCMinus1); + fracPosL1y = (refList1PosY & (0x07 >> (1 - subHeightCMinus1))) << (1 - subHeightCMinus1); if (((fracPosL0x + (fracPosL0y << 2)) == 0) && ((fracPosL1x + (fracPosL1y << 2)) == 0)) { @@ -910,8 +933,8 @@ void BiPredInterpolation16bit( //List0 chroma //integPosx = (refList0PosX >> 3); //integPosy = (refList0PosY >> 3); - fracPosx = refList0PosX & 0x07; - fracPosy = refList0PosY & 0x07; + fracPosx = (refList0PosX & (0x07 >> (1-subWidthCMinus1))) << (1-subWidthCMinus1); + fracPosy = (refList0PosY & (0x07 >> (1-subHeightCMinus1))) << (1-subHeightCMinus1); biPredChromaIFFunctionPtrArrayNew16bit[(ASM_TYPES & AVX2_MASK) && 1][fracPosx + (fracPosy << 3)]( (EB_U16 *)fullPelBlockL0->bufferCb + 2 + 2 * fullPelBlockL0->strideCb, @@ -941,8 +964,8 @@ void BiPredInterpolation16bit( //List1 chroma //integPosx = (refList1PosX >> 3); //integPosy = (refList1PosY >> 3); - fracPosx = refList1PosX & 0x07; - fracPosy = refList1PosY & 0x07; + fracPosx = (refList1PosX & (0x07 >> (1-subWidthCMinus1))) << (1-subWidthCMinus1); + fracPosy = (refList1PosY & (0x07 >> (1-subHeightCMinus1))) << (1-subHeightCMinus1); biPredChromaIFFunctionPtrArrayNew16bit[(ASM_TYPES & AVX2_MASK) && 1][fracPosx + (fracPosy << 3)]( (EB_U16 *)fullPelBlockL1->bufferCb + 2 + 2 * fullPelBlockL1->strideCb, diff --git a/Source/Lib/Codec/EbModeDecision.c b/Source/Lib/Codec/EbModeDecision.c index 6c616c334..7deaa8d68 100644 --- a/Source/Lib/Codec/EbModeDecision.c +++ b/Source/Lib/Codec/EbModeDecision.c @@ -139,6 +139,7 @@ EB_ERRORTYPE ModeDecisionCandidateBufferCtor( pictureBufferDescInitData.maxHeight = lcuMaxSize; pictureBufferDescInitData.bitDepth = maxBitdepth; pictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_FULL_MASK; + pictureBufferDescInitData.colorFormat = EB_YUV420; pictureBufferDescInitData.leftPadding = 0; pictureBufferDescInitData.rightPadding = 0; pictureBufferDescInitData.topPadding = 0; @@ -149,6 +150,7 @@ EB_ERRORTYPE ModeDecisionCandidateBufferCtor( doubleWidthPictureBufferDescInitData.maxHeight = lcuMaxSize; doubleWidthPictureBufferDescInitData.bitDepth = EB_16BIT; doubleWidthPictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_FULL_MASK; + doubleWidthPictureBufferDescInitData.colorFormat = EB_YUV420; doubleWidthPictureBufferDescInitData.leftPadding = 0; doubleWidthPictureBufferDescInitData.rightPadding = 0; doubleWidthPictureBufferDescInitData.topPadding = 0; @@ -1848,31 +1850,16 @@ EB_U8 ProductFullModeDecision( tuPtr->splitFlag = EB_TRUE; tuPtr->cbCbf = EB_FALSE; tuPtr->crCbf = EB_FALSE; - tuPtr->chromaCbfContext = 0; //at TU level - } - else { - tuTotalCount = 1; - tuIndex = 0; - tuItr = 0; - } - { // Set TU variables - if (cuSize == MAX_LCU_SIZE){ - tuTotalCount = 4; - tuIndex = 1; - tuItr = 0; - tuPtr = &cuPtr->transformUnitArray[0]; - tuPtr->splitFlag = EB_TRUE; - tuPtr->cbCbf = EB_FALSE; - tuPtr->crCbf = EB_FALSE; + tuPtr->cbCbf2 = EB_FALSE; + tuPtr->crCbf2 = EB_FALSE; tuPtr->chromaCbfContext = 0; //at TU level } else { tuTotalCount = 1; tuIndex = 0; tuItr = 0; - } } //cuPtr->forceSmallTu = candidatePtr->forceSmallTu; @@ -1890,6 +1877,8 @@ EB_U8 ProductFullModeDecision( tuPtr->lumaCbf = (EB_BOOL)(((candidatePtr->yCbf) & (1 << tuIndex)) > 0); tuPtr->cbCbf = (EB_BOOL)(((candidatePtr->cbCbf) & (1 << (tuIndex))) > 0); tuPtr->crCbf = (EB_BOOL)(((candidatePtr->crCbf) & (1 << (tuIndex))) > 0); + tuPtr->cbCbf2 = EB_FALSE; + tuPtr->crCbf2 = EB_FALSE; //CHKN tuPtr->chromaCbfContext = (tuIndex == 0 || (cuPtr->partitionMode == SIZE_NxN)) ? 0 : (cuSizeLog2 - Log2f(tuSize)); //at TU level tuPtr->chromaCbfContext = (tuIndex == 0 || (0)) ? 0 : (cuSizeLog2 - Log2f(tuSize)); //at TU level diff --git a/Source/Lib/Codec/EbModeDecisionConfigurationProcess.c b/Source/Lib/Codec/EbModeDecisionConfigurationProcess.c index 693eeba96..13040522e 100644 --- a/Source/Lib/Codec/EbModeDecisionConfigurationProcess.c +++ b/Source/Lib/Codec/EbModeDecisionConfigurationProcess.c @@ -1991,7 +1991,8 @@ void DeriveLcuScore( validCu8x8Count++; } } - distortion = CLIP3(pictureControlSetPtr->ParentPcsPtr->intraComplexityMinPre, pictureControlSetPtr->ParentPcsPtr->intraComplexityMaxPre, (distortion / validCu8x8Count) * 64); + if (validCu8x8Count > 0) + distortion = CLIP3(pictureControlSetPtr->ParentPcsPtr->intraComplexityMinPre, pictureControlSetPtr->ParentPcsPtr->intraComplexityMaxPre, (distortion / validCu8x8Count) * 64); } else { distortion = pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[RASTER_SCAN_CU_INDEX_32x32_0][0].distortion + @@ -2014,7 +2015,8 @@ void DeriveLcuScore( validCu8x8Count++; } } - distortion = CLIP3(pictureControlSetPtr->ParentPcsPtr->interComplexityMinPre, pictureControlSetPtr->ParentPcsPtr->interComplexityMaxPre, (distortion / validCu8x8Count) * 64); + if (validCu8x8Count > 0) + distortion = CLIP3(pictureControlSetPtr->ParentPcsPtr->interComplexityMinPre, pictureControlSetPtr->ParentPcsPtr->interComplexityMaxPre, (distortion / validCu8x8Count) * 64); // Do not perform LCU score manipulation for incomplete LCUs as not valid signals lcuScore = distortion; @@ -2164,9 +2166,11 @@ void PerformOutlierRemoval( // Zero-out the bin if percentage lower than VALID_SLOT_TH for (slot = 0; slot < 10; slot++){ - if ((lcuScoreHistogram[slot] * 100 / processedlcuCount) < VALID_SLOT_TH){ - lcuScoreHistogram[slot] = 0; - } + if (processedlcuCount > 0) { + if ((lcuScoreHistogram[slot] * 100 / processedlcuCount) < VALID_SLOT_TH) { + lcuScoreHistogram[slot] = 0; + } + } } // Ignore null bins diff --git a/Source/Lib/Codec/EbModeDecisionProcess.c b/Source/Lib/Codec/EbModeDecisionProcess.c index 842857d12..b379c7c43 100644 --- a/Source/Lib/Codec/EbModeDecisionProcess.c +++ b/Source/Lib/Codec/EbModeDecisionProcess.c @@ -109,6 +109,7 @@ EB_ERRORTYPE ModeDecisionContextCtor( initData.maxWidth = MAX_LCU_SIZE; initData.maxHeight = MAX_LCU_SIZE; initData.bitDepth = EB_8BIT; + initData.colorFormat = EB_YUV420; initData.leftPadding = 0; initData.rightPadding = 0; initData.topPadding = 0; @@ -125,7 +126,7 @@ EB_ERRORTYPE ModeDecisionContextCtor( } // Intra Reference Samples - return_error = IntraReferenceSamplesCtor(&contextPtr->intraRefPtr); + return_error = IntraReferenceSamplesCtor(&contextPtr->intraRefPtr, EB_YUV420); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } @@ -147,6 +148,7 @@ EB_ERRORTYPE ModeDecisionContextCtor( initData.maxWidth = MAX_LCU_SIZE; initData.maxHeight = MAX_LCU_SIZE; initData.bitDepth = EB_8BIT; + initData.colorFormat = EB_YUV420; initData.leftPadding = 0; initData.rightPadding = 0; initData.topPadding = 0; diff --git a/Source/Lib/Codec/EbMotionEstimation.c b/Source/Lib/Codec/EbMotionEstimation.c index 06650860a..0e5d0c025 100644 --- a/Source/Lib/Codec/EbMotionEstimation.c +++ b/Source/Lib/Codec/EbMotionEstimation.c @@ -1668,16 +1668,6 @@ static void QuarterPelSearch_LCU( buf1, buf1Stride, buf2, buf2Stride); - buf1[0] = buf1[0]; buf2[0] = buf2[0]; - buf1[1] = buf1[1]; buf2[1] = buf2[1]; - buf1[2] = buf1[2]; buf2[2] = buf2[2]; - buf1[3] = buf1[3]; buf2[3] = buf2[3]; - buf1[4] = buf1[4]; buf2[4] = buf2[4]; - buf1[5] = buf1[5]; buf2[5] = buf2[5]; - buf1[6] = buf1[6]; buf2[6] = buf2[6]; - buf1[7] = buf1[7]; buf2[7] = buf2[7]; - - PU_QuarterPelRefinementOnTheFly( contextPtr, contextPtr->pBestSsd64x64, diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 4cccaff93..32c95781e 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -258,6 +258,21 @@ void* PacketizationKernel(void *inputPtr) contextPtr->ppsConfig); } + + if (sequenceControlSetPtr->staticConfig.maxCLL || sequenceControlSetPtr->staticConfig.maxFALL) { + sequenceControlSetPtr->contentLightLevel.maxContentLightLevel = sequenceControlSetPtr->staticConfig.maxCLL; + sequenceControlSetPtr->contentLightLevel.maxPicAverageLightLevel = sequenceControlSetPtr->staticConfig.maxFALL; + EncodeContentLightLevelSEI( + pictureControlSetPtr->bitstreamPtr, + &sequenceControlSetPtr->contentLightLevel); + } + + if (sequenceControlSetPtr->staticConfig.useMasteringDisplayColorVolume) { + EncodeMasteringDisplayColorVolumeSEI( + pictureControlSetPtr->bitstreamPtr, + &sequenceControlSetPtr->masteringDisplayColorVolume); + } + if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) { sequenceControlSetPtr->activeParameterSet.selfContainedCvsFlag = EB_TRUE; @@ -276,8 +291,10 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->pBuffer, (EB_U32*) &(outputStreamPtr->nFilledLen), (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); + encodeContextPtr, + NAL_UNIT_INVALID); } + // Bitstream Written Loop // This loop writes the result of entropy coding into the bitstream { @@ -544,7 +561,8 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->pBuffer, (EB_U32*) &(outputStreamPtr->nFilledLen), (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); + encodeContextPtr, + NAL_UNIT_INVALID); queueEntryPtr->startSplicing = outputStreamPtr->nFilledLen; if (sequenceControlSetPtr->staticConfig.pictureTimingSEI) { if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) @@ -565,7 +583,25 @@ void* PacketizationKernel(void *inputPtr) pictureControlSetPtr->bitstreamPtr, &sequenceControlSetPtr->recoveryPoint); } - + + if (sequenceControlSetPtr->staticConfig.useNaluFile && pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->userSeiMsg.payloadSize) { + if (pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->userSeiMsg.payloadType == USER_DATA_REGISTERED_ITU_T_T35) { + sequenceControlSetPtr->regUserDataSeiPtr.userDataSize = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->userSeiMsg.payloadSize; + sequenceControlSetPtr->regUserDataSeiPtr.userData = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->userSeiMsg.payload; + EncodeRegUserDataSEI( + pictureControlSetPtr->bitstreamPtr, + &sequenceControlSetPtr->regUserDataSeiPtr); + } + if (pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->userSeiMsg.payloadType == USER_DATA_UNREGISTERED) { + sequenceControlSetPtr->unRegUserDataSeiPtr.userDataSize = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->userSeiMsg.payloadSize; + sequenceControlSetPtr->unRegUserDataSeiPtr.userData = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->userSeiMsg.payload; + EncodeUnregUserDataSEI( + pictureControlSetPtr->bitstreamPtr, + &sequenceControlSetPtr->unRegUserDataSeiPtr, + encodeContextPtr); + } + } + EncodeSliceHeader( 0, packetizationQp, @@ -582,7 +618,8 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->pBuffer, (EB_U32*) &(outputStreamPtr->nFilledLen), (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); + encodeContextPtr, + NAL_UNIT_INVALID); // Reset the bitstream ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); @@ -597,7 +634,9 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->pBuffer, (EB_U32*) &(outputStreamPtr->nFilledLen), (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); + encodeContextPtr, + NAL_UNIT_INVALID); + bufferRate = encodeContextPtr->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16); if ((sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) && (sequenceControlSetPtr->staticConfig.vbvMaxrate == sequenceControlSetPtr->staticConfig.targetBitRate)) { @@ -627,7 +666,8 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->pBuffer, (EB_U32*) &(outputStreamPtr->nFilledLen), (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); + encodeContextPtr, + NAL_UNIT_INVALID); for (EB_U32 i = 0; i < fillerBytes; i++) { @@ -641,7 +681,8 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->pBuffer, (EB_U32*) &(outputStreamPtr->nFilledLen), (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); + encodeContextPtr, + NAL_UNIT_INVALID); } ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); // Byte Align the Bitstream: rbsp_trailing_bits @@ -660,12 +701,39 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->pBuffer, (EB_U32*) &(outputStreamPtr->nFilledLen), (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr); + encodeContextPtr, + NAL_UNIT_INVALID); } } + // Send the number of bytes per frame to RC pictureControlSetPtr->ParentPcsPtr->totalNumBits = outputStreamPtr->nFilledLen << 3; + queueEntryPtr->actualBits = pictureControlSetPtr->ParentPcsPtr->totalNumBits; + + // Copy Dolby Vision RPU metadata to the output bitstream + if (sequenceControlSetPtr->staticConfig.dolbyVisionProfile == 81 && pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->dolbyVisionRpu.payloadSize) { + // Reset the bitstream + ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + + CodeDolbyVisionRpuMetadata( + pictureControlSetPtr->bitstreamPtr, + pictureControlSetPtr + ); + + // Flush the Bitstream + FlushBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + + // Copy payload to the Output Bitstream + CopyRbspBitstreamToPayload( + pictureControlSetPtr->bitstreamPtr, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr, + NAL_UNIT_UNSPECIFIED_62); + } + // Code EOS NUT if (outputStreamPtr->nFlags & EB_BUFFERFLAG_EOS && sequenceControlSetPtr->staticConfig.codeEosNal == 1) { @@ -683,7 +751,8 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->pBuffer, (EB_U32*) &(outputStreamPtr->nFilledLen), (EB_U32*) &(outputStreamPtr->nAllocLen), - ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr); + ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr, + NAL_UNIT_INVALID); } //Store the buffer in the Queue @@ -771,7 +840,8 @@ void* PacketizationKernel(void *inputPtr) outputStreamPtr->pBuffer, (EB_U32*) &(queueEntryPtr->startSplicing), (EB_U32*) &(outputStreamPtr->nAllocLen), - sequenceControlSetPtr->encodeContextPtr); + sequenceControlSetPtr->encodeContextPtr, + NAL_UNIT_INVALID); outputStreamPtr->nFilledLen += bufferWrittenBytesCount; } diff --git a/Source/Lib/Codec/EbPictureAnalysisProcess.c b/Source/Lib/Codec/EbPictureAnalysisProcess.c index cd64c607d..ba5a4e049 100644 --- a/Source/Lib/Codec/EbPictureAnalysisProcess.c +++ b/Source/Lib/Codec/EbPictureAnalysisProcess.c @@ -62,6 +62,14 @@ EB_ERRORTYPE PictureAnalysisContextCtor( if (denoiseFlag == EB_TRUE){ //denoised + // If 420/422, re-use luma for chroma + // If 444, re-use luma for Cr + if (inputPictureBufferDescInitData->colorFormat != EB_YUV444) { + inputPictureBufferDescInitData->bufferEnableMask = PICTURE_BUFFER_DESC_Y_FLAG; + } else { + inputPictureBufferDescInitData->bufferEnableMask = PICTURE_BUFFER_DESC_Y_FLAG | PICTURE_BUFFER_DESC_Cb_FLAG; + } + return_error = EbPictureBufferDescCtor( (EB_PTR*)&(contextPtr->denoisedPicturePtr), (EB_PTR)inputPictureBufferDescInitData); @@ -70,9 +78,12 @@ EB_ERRORTYPE PictureAnalysisContextCtor( return EB_ErrorInsufficientResources; } - //luma buffer could re-used to process chroma + if (inputPictureBufferDescInitData->colorFormat != EB_YUV444) { contextPtr->denoisedPicturePtr->bufferCb = contextPtr->denoisedPicturePtr->bufferY; contextPtr->denoisedPicturePtr->bufferCr = contextPtr->denoisedPicturePtr->bufferY + contextPtr->denoisedPicturePtr->chromaSize; + } else { + contextPtr->denoisedPicturePtr->bufferCr = contextPtr->denoisedPicturePtr->bufferY; + } // noise inputPictureBufferDescInitData->maxHeight = MAX_LCU_SIZE; @@ -96,6 +107,66 @@ EB_ERRORTYPE PictureAnalysisContextCtor( return EB_ErrorNone; } +static void DownSampleChroma(EbPictureBufferDesc_t* inputPicturePtr, EbPictureBufferDesc_t* outputPicturePtr) +{ + EB_U32 inputColorFormat = inputPicturePtr->colorFormat; + EB_U16 inputSubWidthCMinus1 = (inputColorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 inputSubHeightCMinus1 = (inputColorFormat >= EB_YUV422 ? 1 : 2) - 1; + + EB_U32 outputColorFormat = outputPicturePtr->colorFormat; + EB_U16 outputSubWidthCMinus1 = (outputColorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 outputSubHeightCMinus1 = (outputColorFormat >= EB_YUV422 ? 1 : 2) - 1; + + EB_U32 strideIn, strideOut; + EB_U32 inputOriginIndex, outputOriginIndex; + + EB_U8 *ptrIn; + EB_U8 *ptrOut; + + EB_U32 ii, jj; + + //Cb + { + strideIn = inputPicturePtr->strideCb; + inputOriginIndex = (inputPicturePtr->originX >> inputSubWidthCMinus1) + + (inputPicturePtr->originY >> inputSubHeightCMinus1) * inputPicturePtr->strideCb; + ptrIn = &(inputPicturePtr->bufferCb[inputOriginIndex]); + + strideOut = outputPicturePtr->strideCb; + outputOriginIndex = (outputPicturePtr->originX >> outputSubWidthCMinus1) + + (outputPicturePtr->originY >> outputSubHeightCMinus1) * outputPicturePtr->strideCb; + ptrOut = &(outputPicturePtr->bufferCb[outputOriginIndex]); + + for (jj = 0; jj < (EB_U32)(outputPicturePtr->height >> outputSubHeightCMinus1); jj++) { + for (ii = 0; ii < (EB_U32)(outputPicturePtr->width >> outputSubWidthCMinus1); ii++) { + ptrOut[ii + jj * strideOut] = + ptrIn[(ii << (1 - inputSubWidthCMinus1)) + + (jj << (1 - inputSubHeightCMinus1)) * strideIn]; + } + } + + } + + //Cr + { + strideIn = inputPicturePtr->strideCr; + inputOriginIndex = (inputPicturePtr->originX >> inputSubWidthCMinus1) + (inputPicturePtr->originY >> inputSubHeightCMinus1) * inputPicturePtr->strideCr; + ptrIn = &(inputPicturePtr->bufferCr[inputOriginIndex]); + + strideOut = outputPicturePtr->strideCr; + outputOriginIndex = (outputPicturePtr->originX >> outputSubWidthCMinus1) + (outputPicturePtr->originY >> outputSubHeightCMinus1) * outputPicturePtr->strideCr; + ptrOut = &(outputPicturePtr->bufferCr[outputOriginIndex]); + + for (jj = 0; jj < (EB_U32)(outputPicturePtr->height >> outputSubHeightCMinus1); jj++) { + for (ii = 0; ii < (EB_U32)(outputPicturePtr->width >> outputSubWidthCMinus1); ii++) { + ptrOut[ii + jj * strideOut] = + ptrIn[(ii << (1 - inputSubWidthCMinus1)) + + (jj << (1 - inputSubHeightCMinus1)) * strideIn]; + } + } + } +} + /************************************************ * Picture Analysis Context Destructor ************************************************/ @@ -1208,17 +1279,22 @@ void noiseExtractChromaStrong( EB_U32 strideOut; EB_U32 idx = (lcuOriginX + MAX_LCU_SIZE > inputPicturePtr->width) ? lcuOriginX : 0; + EB_U32 colorFormat = inputPicturePtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + + //Cb { - picHeight = inputPicturePtr->height / 2; - picWidth = inputPicturePtr->width / 2; - lcuHeight = MIN(MAX_LCU_SIZE / 2, picHeight - lcuOriginY); + picHeight = inputPicturePtr->height >> subHeightCMinus1; + picWidth = inputPicturePtr->width >> subWidthCMinus1; + lcuHeight = MIN(MAX_LCU_SIZE >> subHeightCMinus1, picHeight - lcuOriginY); strideIn = inputPicturePtr->strideCb; - inputOriginIndex = inputPicturePtr->originX / 2 + (inputPicturePtr->originY / 2 + lcuOriginY) * inputPicturePtr->strideCb; + inputOriginIndex = (inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * inputPicturePtr->strideCb; ptrIn = &(inputPicturePtr->bufferCb[inputOriginIndex]); - inputOriginIndexPad = denoisedPicturePtr->originX / 2 + (denoisedPicturePtr->originY / 2 + lcuOriginY) * denoisedPicturePtr->strideCb; + inputOriginIndexPad = (denoisedPicturePtr->originX >> subWidthCMinus1) + ((denoisedPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * denoisedPicturePtr->strideCb; strideOut = denoisedPicturePtr->strideCb; ptrDenoised = &(denoisedPicturePtr->bufferCb[inputOriginIndexPad]); @@ -1240,15 +1316,15 @@ void noiseExtractChromaStrong( //Cr { - picHeight = inputPicturePtr->height / 2; - picWidth = inputPicturePtr->width / 2; - lcuHeight = MIN(MAX_LCU_SIZE / 2, picHeight - lcuOriginY); + picHeight = inputPicturePtr->height >> subHeightCMinus1; + picWidth = inputPicturePtr->width >> subWidthCMinus1; + lcuHeight = MIN(MAX_LCU_SIZE >> subHeightCMinus1, picHeight - lcuOriginY); strideIn = inputPicturePtr->strideCr; - inputOriginIndex = inputPicturePtr->originX / 2 + (inputPicturePtr->originY / 2 + lcuOriginY) * inputPicturePtr->strideCr; + inputOriginIndex = (inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * inputPicturePtr->strideCr; ptrIn = &(inputPicturePtr->bufferCr[inputOriginIndex]); - inputOriginIndexPad = denoisedPicturePtr->originX / 2 + (denoisedPicturePtr->originY / 2 + lcuOriginY) * denoisedPicturePtr->strideCr; + inputOriginIndexPad = (denoisedPicturePtr->originX >> subWidthCMinus1) + ((denoisedPicturePtr->originY >> subHeightCMinus1) + lcuOriginY) * denoisedPicturePtr->strideCr; strideOut = denoisedPicturePtr->strideCr; ptrDenoised = &(denoisedPicturePtr->bufferCr[inputOriginIndexPad]); @@ -1294,18 +1370,23 @@ void noiseExtractChromaWeak( EB_U32 idx = (lcuOriginX + MAX_LCU_SIZE > inputPicturePtr->width) ? lcuOriginX : 0; + EB_U32 colorFormat = inputPicturePtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + + //Cb { - picHeight = inputPicturePtr->height / 2; - picWidth = inputPicturePtr->width / 2; + picHeight = inputPicturePtr->height >> subHeightCMinus1; + picWidth = inputPicturePtr->width >> subWidthCMinus1; - lcuHeight = MIN(MAX_LCU_SIZE / 2, picHeight - lcuOriginY); + lcuHeight = MIN(MAX_LCU_SIZE >> subHeightCMinus1, picHeight - lcuOriginY); strideIn = inputPicturePtr->strideCb; - inputOriginIndex = inputPicturePtr->originX / 2 + (inputPicturePtr->originY / 2 + lcuOriginY)* inputPicturePtr->strideCb; + inputOriginIndex = (inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) + lcuOriginY)* inputPicturePtr->strideCb; ptrIn = &(inputPicturePtr->bufferCb[inputOriginIndex]); - inputOriginIndexPad = denoisedPicturePtr->originX / 2 + (denoisedPicturePtr->originY / 2 + lcuOriginY)* denoisedPicturePtr->strideCb; + inputOriginIndexPad = (denoisedPicturePtr->originX >> subWidthCMinus1) + ((denoisedPicturePtr->originY >> subHeightCMinus1) + lcuOriginY)* denoisedPicturePtr->strideCb; strideOut = denoisedPicturePtr->strideCb; ptrDenoised = &(denoisedPicturePtr->bufferCb[inputOriginIndexPad]); @@ -1327,15 +1408,15 @@ void noiseExtractChromaWeak( //Cr { - picHeight = inputPicturePtr->height / 2; - picWidth = inputPicturePtr->width / 2; - lcuHeight = MIN(MAX_LCU_SIZE / 2, picHeight - lcuOriginY); + picHeight = inputPicturePtr->height >> subHeightCMinus1; + picWidth = inputPicturePtr->width >> subWidthCMinus1; + lcuHeight = MIN(MAX_LCU_SIZE >> subHeightCMinus1, picHeight - lcuOriginY); strideIn = inputPicturePtr->strideCr; - inputOriginIndex = inputPicturePtr->originX / 2 + (inputPicturePtr->originY / 2 + lcuOriginY)* inputPicturePtr->strideCr; + inputOriginIndex = (inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) + lcuOriginY)* inputPicturePtr->strideCr; ptrIn = &(inputPicturePtr->bufferCr[inputOriginIndex]); - inputOriginIndexPad = denoisedPicturePtr->originX / 2 + (denoisedPicturePtr->originY / 2 + lcuOriginY)* denoisedPicturePtr->strideCr; + inputOriginIndexPad = (denoisedPicturePtr->originX >> subWidthCMinus1) + ((denoisedPicturePtr->originY >> subHeightCMinus1) + lcuOriginY)* denoisedPicturePtr->strideCr; strideOut = denoisedPicturePtr->strideCr; ptrDenoised = &(denoisedPicturePtr->bufferCr[inputOriginIndexPad]); @@ -2467,13 +2548,16 @@ EB_ERRORTYPE DenoiseInputPicture( EB_U32 lcuOriginX; EB_U32 lcuOriginY; EB_U16 verticalIdx; + EB_U32 colorFormat = inputPicturePtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; //use denoised input if the source is extremly noisy if (pictureControlSetPtr->picNoiseClass >= PIC_NOISE_CLASS_4){ EB_U32 inLumaOffSet = inputPicturePtr->originX + inputPicturePtr->originY * inputPicturePtr->strideY; - EB_U32 inChromaOffSet = inputPicturePtr->originX / 2 + inputPicturePtr->originY / 2 * inputPicturePtr->strideCb; + EB_U32 inChromaOffSet = (inputPicturePtr->originX >> subWidthCMinus1) + (inputPicturePtr->originY >> subHeightCMinus1) * inputPicturePtr->strideCb; EB_U32 denLumaOffSet = denoisedPicturePtr->originX + denoisedPicturePtr->originY * denoisedPicturePtr->strideY; - EB_U32 denChromaOffSet = denoisedPicturePtr->originX / 2 + denoisedPicturePtr->originY / 2 * denoisedPicturePtr->strideCb; + EB_U32 denChromaOffSet = (denoisedPicturePtr->originX >> subWidthCMinus1) + (denoisedPicturePtr->originY >> subHeightCMinus1) * denoisedPicturePtr->strideCb; //filter Luma for (lcuIndex = 0; lcuIndex < pictureControlSetPtr->lcuTotalCount; ++lcuIndex) { @@ -2521,39 +2605,39 @@ EB_ERRORTYPE DenoiseInputPicture( StrongChromaFilter_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( inputPicturePtr, denoisedPicturePtr, - lcuOriginY / 2, - lcuOriginX / 2); + lcuOriginY >> subHeightCMinus1, + lcuOriginX >> subWidthCMinus1); if (lcuOriginX + MAX_LCU_SIZE > inputPicturePtr->width) { noiseExtractChromaStrong( inputPicturePtr, denoisedPicturePtr, - lcuOriginY / 2, - lcuOriginX / 2); + lcuOriginY >> subHeightCMinus1, + lcuOriginX >> subWidthCMinus1); } } //copy chroma - for (verticalIdx = 0; verticalIdx < inputPicturePtr->height / 2; ++verticalIdx) { + for (verticalIdx = 0; verticalIdx < inputPicturePtr->height >> subHeightCMinus1; ++verticalIdx) { EB_MEMCPY(inputPicturePtr->bufferCb + inChromaOffSet + verticalIdx * inputPicturePtr->strideCb, denoisedPicturePtr->bufferCb + denChromaOffSet + verticalIdx * denoisedPicturePtr->strideCb, - sizeof(EB_U8) * inputPicturePtr->width / 2); + sizeof(EB_U8) * inputPicturePtr->width >> subWidthCMinus1); EB_MEMCPY(inputPicturePtr->bufferCr + inChromaOffSet + verticalIdx * inputPicturePtr->strideCr, denoisedPicturePtr->bufferCr + denChromaOffSet + verticalIdx * denoisedPicturePtr->strideCr, - sizeof(EB_U8) * inputPicturePtr->width / 2); + sizeof(EB_U8) * inputPicturePtr->width >> subWidthCMinus1); } } else if (pictureControlSetPtr->picNoiseClass >= PIC_NOISE_CLASS_3_1){ EB_U32 inLumaOffSet = inputPicturePtr->originX + inputPicturePtr->originY * inputPicturePtr->strideY; - EB_U32 inChromaOffSet = inputPicturePtr->originX / 2 + inputPicturePtr->originY / 2 * inputPicturePtr->strideCb; + EB_U32 inChromaOffSet = (inputPicturePtr->originX >> subWidthCMinus1) + (inputPicturePtr->originY >> subHeightCMinus1) * inputPicturePtr->strideCb; EB_U32 denLumaOffSet = denoisedPicturePtr->originX + denoisedPicturePtr->originY * denoisedPicturePtr->strideY; - EB_U32 denChromaOffSet = denoisedPicturePtr->originX / 2 + denoisedPicturePtr->originY / 2 * denoisedPicturePtr->strideCb; + EB_U32 denChromaOffSet = (denoisedPicturePtr->originX >> subWidthCMinus1) + (denoisedPicturePtr->originY >> subHeightCMinus1) * denoisedPicturePtr->strideCb; for (verticalIdx = 0; verticalIdx < inputPicturePtr->height; ++verticalIdx) { @@ -2574,31 +2658,31 @@ EB_ERRORTYPE DenoiseInputPicture( WeakChromaFilter_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( inputPicturePtr, denoisedPicturePtr, - lcuOriginY / 2, - lcuOriginX / 2); + lcuOriginY >> subHeightCMinus1, + lcuOriginX >> subWidthCMinus1); if (lcuOriginX + MAX_LCU_SIZE > inputPicturePtr->width) { noiseExtractChromaWeak( inputPicturePtr, denoisedPicturePtr, - lcuOriginY / 2, - lcuOriginX / 2); + lcuOriginY >> subHeightCMinus1, + lcuOriginX >> subWidthCMinus1); } } - for (verticalIdx = 0; verticalIdx < inputPicturePtr->height / 2; ++verticalIdx) { + for (verticalIdx = 0; verticalIdx < inputPicturePtr->height >> subHeightCMinus1; ++verticalIdx) { EB_MEMCPY(inputPicturePtr->bufferCb + inChromaOffSet + verticalIdx * inputPicturePtr->strideCb, denoisedPicturePtr->bufferCb + denChromaOffSet + verticalIdx * denoisedPicturePtr->strideCb, - sizeof(EB_U8) * inputPicturePtr->width / 2); + sizeof(EB_U8) * inputPicturePtr->width >> subWidthCMinus1); EB_MEMCPY(inputPicturePtr->bufferCr + inChromaOffSet + verticalIdx * inputPicturePtr->strideCr, denoisedPicturePtr->bufferCr + denChromaOffSet + verticalIdx * denoisedPicturePtr->strideCr, - sizeof(EB_U8) * inputPicturePtr->width / 2); + sizeof(EB_U8) * inputPicturePtr->width >> subWidthCMinus1); } } @@ -2726,10 +2810,11 @@ EB_ERRORTYPE DetectInputPictureNoise( } - contextPtr->picNoiseVarianceFloat = (double)picNoiseVariance / (double)totLcuCount; - - picNoiseVariance = picNoiseVariance / totLcuCount; + if (totLcuCount > 0) { + contextPtr->picNoiseVarianceFloat = (double)picNoiseVariance / (double)totLcuCount; + picNoiseVariance = picNoiseVariance / totLcuCount; + } //the variance of a 64x64 noise area tends to be bigger for small resolutions. if (sequenceControlSetPtr->lumaHeight <= 720) @@ -2824,12 +2909,16 @@ EB_ERRORTYPE SubSampleFilterNoise( EB_U32 lcuOriginX; EB_U32 lcuOriginY; EB_U16 verticalIdx; + EB_U32 colorFormat = inputPicturePtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + if (pictureControlSetPtr->picNoiseClass == PIC_NOISE_CLASS_3_1) { EB_U32 inLumaOffSet = inputPicturePtr->originX + inputPicturePtr->originY * inputPicturePtr->strideY; - EB_U32 inChromaOffSet = inputPicturePtr->originX / 2 + inputPicturePtr->originY / 2 * inputPicturePtr->strideCb; + EB_U32 inChromaOffSet = (inputPicturePtr->originX >> subWidthCMinus1) + (inputPicturePtr->originY >> subHeightCMinus1) * inputPicturePtr->strideCb; EB_U32 denLumaOffSet = denoisedPicturePtr->originX + denoisedPicturePtr->originY * denoisedPicturePtr->strideY; - EB_U32 denChromaOffSet = denoisedPicturePtr->originX / 2 + denoisedPicturePtr->originY / 2 * denoisedPicturePtr->strideCb; + EB_U32 denChromaOffSet = (denoisedPicturePtr->originX >> subWidthCMinus1) + (denoisedPicturePtr->originY >> subHeightCMinus1) * denoisedPicturePtr->strideCb; //filter Luma @@ -2878,34 +2967,33 @@ EB_ERRORTYPE SubSampleFilterNoise( WeakChromaFilter_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( inputPicturePtr, denoisedPicturePtr, - lcuOriginY / 2, - lcuOriginX / 2); + lcuOriginY >> subHeightCMinus1, + lcuOriginX >> subWidthCMinus1); if (lcuOriginX + MAX_LCU_SIZE > inputPicturePtr->width) { noiseExtractChromaWeak( inputPicturePtr, denoisedPicturePtr, - lcuOriginY / 2, - lcuOriginX / 2); + lcuOriginY >> subHeightCMinus1, + lcuOriginX >> subWidthCMinus1); } } //copy chroma - for (verticalIdx = 0; verticalIdx < inputPicturePtr->height / 2; ++verticalIdx) { + for (verticalIdx = 0; verticalIdx < inputPicturePtr->height >> subHeightCMinus1; ++verticalIdx) { EB_MEMCPY(inputPicturePtr->bufferCb + inChromaOffSet + verticalIdx * inputPicturePtr->strideCb, denoisedPicturePtr->bufferCb + denChromaOffSet + verticalIdx * denoisedPicturePtr->strideCb, - sizeof(EB_U8) * inputPicturePtr->width / 2); + sizeof(EB_U8) * inputPicturePtr->width >> subWidthCMinus1); EB_MEMCPY(inputPicturePtr->bufferCr + inChromaOffSet + verticalIdx * inputPicturePtr->strideCr, denoisedPicturePtr->bufferCr + denChromaOffSet + verticalIdx * denoisedPicturePtr->strideCr, - sizeof(EB_U8) * inputPicturePtr->width / 2); + sizeof(EB_U8) * inputPicturePtr->width >> subWidthCMinus1); } - } - else if (pictureControlSetPtr->picNoiseClass == PIC_NOISE_CLASS_2){ + } else if (pictureControlSetPtr->picNoiseClass == PIC_NOISE_CLASS_2){ EB_U32 newTotFN = 0; @@ -3114,11 +3202,11 @@ EB_ERRORTYPE QuarterSampleDetectNoise( } } + if (totLcuCount > 0) { + contextPtr->picNoiseVarianceFloat = (double)picNoiseVariance / (double)totLcuCount; - contextPtr->picNoiseVarianceFloat = (double)picNoiseVariance / (double)totLcuCount; - - picNoiseVariance = picNoiseVariance / totLcuCount; - + picNoiseVariance = picNoiseVariance / totLcuCount; + } //the variance of a 64x64 noise area tends to be bigger for small resolutions. //if (sequenceControlSetPtr->lumaHeight <= 720) @@ -3258,11 +3346,11 @@ EB_ERRORTYPE SubSampleDetectNoise( } } + if (totLcuCount > 0) { + contextPtr->picNoiseVarianceFloat = (double)picNoiseVariance / (double)totLcuCount; - contextPtr->picNoiseVarianceFloat = (double)picNoiseVariance / (double)totLcuCount; - - picNoiseVariance = picNoiseVariance / totLcuCount; - + picNoiseVariance = picNoiseVariance / totLcuCount; + } //the variance of a 64x64 noise area tends to be bigger for small resolutions. if (sequenceControlSetPtr->lumaHeight <= 720) @@ -3959,13 +4047,17 @@ static inline void DetermineHomogeneousRegionInPicture( } } pictureControlSetPtr->veryLowVarPicFlag = EB_FALSE; - if (((veryLowVarCnt * 100) / varLcuCnt) > PIC_LOW_VAR_PERCENTAGE_TH){ - pictureControlSetPtr->veryLowVarPicFlag = EB_TRUE; + if (varLcuCnt > 0) { + if (((veryLowVarCnt * 100) / varLcuCnt) > PIC_LOW_VAR_PERCENTAGE_TH) { + pictureControlSetPtr->veryLowVarPicFlag = EB_TRUE; + } } pictureControlSetPtr->logoPicFlag = EB_FALSE; - if (((veryLowVarCnt * 100) / varLcuCnt) > 80){ - pictureControlSetPtr->logoPicFlag = EB_TRUE; + if (varLcuCnt > 0) { + if (((veryLowVarCnt * 100) / varLcuCnt) > 80) { + pictureControlSetPtr->logoPicFlag = EB_TRUE; + } } return; @@ -4152,6 +4244,9 @@ void PadPictureToMultipleOfMinCuSizeDimensions( EbPictureBufferDesc_t *inputPicturePtr) { EB_BOOL is16BitInput = (EB_BOOL)(sequenceControlSetPtr->staticConfig.encoderBitDepth > EB_8BIT); + EB_U32 colorFormat = inputPicturePtr->colorFormat; + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; // Input Picture Padding PadInputPicture( @@ -4163,23 +4258,22 @@ void PadPictureToMultipleOfMinCuSizeDimensions( sequenceControlSetPtr->padBottom); PadInputPicture( - &inputPicturePtr->bufferCb[(inputPicturePtr->originX >> 1) + ((inputPicturePtr->originY >> 1) * inputPicturePtr->strideCb)], + &inputPicturePtr->bufferCb[(inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) * inputPicturePtr->strideCb)], inputPicturePtr->strideCb, - (inputPicturePtr->width - sequenceControlSetPtr->padRight) >> 1, - (inputPicturePtr->height - sequenceControlSetPtr->padBottom) >> 1, - sequenceControlSetPtr->padRight >> 1, - sequenceControlSetPtr->padBottom >> 1); + (inputPicturePtr->width - sequenceControlSetPtr->padRight) >> subWidthCMinus1, + (inputPicturePtr->height - sequenceControlSetPtr->padBottom) >> subHeightCMinus1, + sequenceControlSetPtr->padRight >> subWidthCMinus1, + sequenceControlSetPtr->padBottom >> subHeightCMinus1); PadInputPicture( - &inputPicturePtr->bufferCr[(inputPicturePtr->originX >> 1) + ((inputPicturePtr->originY >> 1) * inputPicturePtr->strideCr)], + &inputPicturePtr->bufferCr[(inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) * inputPicturePtr->strideCr)], inputPicturePtr->strideCr, - (inputPicturePtr->width - sequenceControlSetPtr->padRight) >> 1, - (inputPicturePtr->height - sequenceControlSetPtr->padBottom) >> 1, - sequenceControlSetPtr->padRight >> 1, - sequenceControlSetPtr->padBottom >> 1); + (inputPicturePtr->width - sequenceControlSetPtr->padRight) >> subWidthCMinus1, + (inputPicturePtr->height - sequenceControlSetPtr->padBottom) >> subHeightCMinus1, + sequenceControlSetPtr->padRight >> subWidthCMinus1, + sequenceControlSetPtr->padBottom >> subHeightCMinus1); - if (is16BitInput) - { + if (is16BitInput) { PadInputPicture( &inputPicturePtr->bufferBitIncY[inputPicturePtr->originX + (inputPicturePtr->originY * inputPicturePtr->strideBitIncY)], inputPicturePtr->strideBitIncY, @@ -4189,20 +4283,20 @@ void PadPictureToMultipleOfMinCuSizeDimensions( sequenceControlSetPtr->padBottom); PadInputPicture( - &inputPicturePtr->bufferBitIncCb[(inputPicturePtr->originX >> 1) + ((inputPicturePtr->originY >> 1) * inputPicturePtr->strideBitIncCb)], + &inputPicturePtr->bufferBitIncCb[(inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) * inputPicturePtr->strideBitIncCb)], inputPicturePtr->strideBitIncCb, - (inputPicturePtr->width - sequenceControlSetPtr->padRight) >> 1, - (inputPicturePtr->height - sequenceControlSetPtr->padBottom) >> 1, - sequenceControlSetPtr->padRight >> 1, - sequenceControlSetPtr->padBottom >> 1); + (inputPicturePtr->width - sequenceControlSetPtr->padRight) >> subWidthCMinus1, + (inputPicturePtr->height - sequenceControlSetPtr->padBottom) >> subHeightCMinus1, + sequenceControlSetPtr->padRight >> subWidthCMinus1, + sequenceControlSetPtr->padBottom >> subHeightCMinus1); PadInputPicture( - &inputPicturePtr->bufferBitIncCr[(inputPicturePtr->originX >> 1) + ((inputPicturePtr->originY >> 1) * inputPicturePtr->strideBitIncCr)], + &inputPicturePtr->bufferBitIncCr[(inputPicturePtr->originX >> subWidthCMinus1) + ((inputPicturePtr->originY >> subHeightCMinus1) * inputPicturePtr->strideBitIncCr)], inputPicturePtr->strideBitIncCr, - (inputPicturePtr->width - sequenceControlSetPtr->padRight) >> 1, - (inputPicturePtr->height - sequenceControlSetPtr->padBottom) >> 1, - sequenceControlSetPtr->padRight >> 1, - sequenceControlSetPtr->padBottom >> 1); + (inputPicturePtr->width - sequenceControlSetPtr->padRight) >> subWidthCMinus1, + (inputPicturePtr->height - sequenceControlSetPtr->padBottom) >> subHeightCMinus1, + sequenceControlSetPtr->padRight >> subWidthCMinus1, + sequenceControlSetPtr->padBottom >> subHeightCMinus1); } @@ -4261,7 +4355,7 @@ void DecimateInputPicture( inputPaddedPicturePtr->strideY, inputPaddedPicturePtr->width , inputPaddedPicturePtr->height, - &quarterDecimatedPicturePtr->bufferY[quarterDecimatedPicturePtr->originX+quarterDecimatedPicturePtr->originX*quarterDecimatedPicturePtr->strideY], + &quarterDecimatedPicturePtr->bufferY[quarterDecimatedPicturePtr->originX+quarterDecimatedPicturePtr->originY*quarterDecimatedPicturePtr->strideY], quarterDecimatedPicturePtr->strideY, 2); @@ -4282,7 +4376,7 @@ void DecimateInputPicture( inputPaddedPicturePtr->strideY, inputPaddedPicturePtr->width , inputPaddedPicturePtr->height , - &sixteenthDecimatedPicturePtr->bufferY[sixteenthDecimatedPicturePtr->originX+sixteenthDecimatedPicturePtr->originX*sixteenthDecimatedPicturePtr->strideY], + &sixteenthDecimatedPicturePtr->bufferY[sixteenthDecimatedPicturePtr->originX+sixteenthDecimatedPicturePtr->originY*sixteenthDecimatedPicturePtr->strideY], sixteenthDecimatedPicturePtr->strideY, 4); @@ -4370,6 +4464,16 @@ void* PictureAnalysisKernel(void *inputPtr) lcuTotalCount, pictureWidthInLcu); + if (inputPicturePtr->colorFormat >= EB_YUV422) { + // Jing: Do the conversion of 422/444=>420 here since it's multi-threaded kernel + // Reuse the Y, only add cb/cr in the newly created buffer desc + // NOTE: since denoise may change the src, so this part is after PicturePreProcessingOperations() + pictureControlSetPtr->chromaDownSamplePicturePtr->bufferY = inputPicturePtr->bufferY; + DownSampleChroma(inputPicturePtr, pictureControlSetPtr->chromaDownSamplePicturePtr); + } else { + pictureControlSetPtr->chromaDownSamplePicturePtr = inputPicturePtr; + } + // Pad input picture to complete border LCUs PadPictureToMultipleOfLcuDimensions( inputPaddedPicturePtr @@ -4388,7 +4492,7 @@ void* PictureAnalysisKernel(void *inputPtr) sequenceControlSetPtr, pictureControlSetPtr, contextPtr, - inputPicturePtr, + pictureControlSetPtr->chromaDownSamplePicturePtr, //420 inputPicturePtr inputPaddedPicturePtr, sixteenthDecimatedPicturePtr, lcuTotalCount); diff --git a/Source/Lib/Codec/EbPictureBufferDesc.c b/Source/Lib/Codec/EbPictureBufferDesc.c index 381544b26..ace1e0cce 100644 --- a/Source/Lib/Codec/EbPictureBufferDesc.c +++ b/Source/Lib/Codec/EbPictureBufferDesc.c @@ -27,20 +27,25 @@ EB_ERRORTYPE EbPictureBufferDescCtor( // Allocate the PictureBufferDesc Object *objectDblPtr = (EB_PTR) pictureBufferDescPtr; + if (pictureBufferDescInitDataPtr->colorFormat < EB_YUV420 || pictureBufferDescInitDataPtr->colorFormat > EB_YUV444) { + pictureBufferDescInitDataPtr->colorFormat = EB_YUV420; + } + EB_U32 subWidthCMinus1 = (pictureBufferDescInitDataPtr->colorFormat == EB_YUV444 ? 1 : 2) - 1; // Set the Picture Buffer Static variables pictureBufferDescPtr->maxWidth = pictureBufferDescInitDataPtr->maxWidth; pictureBufferDescPtr->maxHeight = pictureBufferDescInitDataPtr->maxHeight; pictureBufferDescPtr->width = pictureBufferDescInitDataPtr->maxWidth; pictureBufferDescPtr->height = pictureBufferDescInitDataPtr->maxHeight; pictureBufferDescPtr->bitDepth = pictureBufferDescInitDataPtr->bitDepth; + pictureBufferDescPtr->colorFormat = pictureBufferDescInitDataPtr->colorFormat; pictureBufferDescPtr->strideY = pictureBufferDescInitDataPtr->maxWidth + pictureBufferDescInitDataPtr->leftPadding + pictureBufferDescInitDataPtr->rightPadding; - pictureBufferDescPtr->strideCb = pictureBufferDescPtr->strideCr = pictureBufferDescPtr->strideY >> 1; + pictureBufferDescPtr->strideCb = pictureBufferDescPtr->strideCr = pictureBufferDescPtr->strideY >> subWidthCMinus1; pictureBufferDescPtr->originX = pictureBufferDescInitDataPtr->leftPadding; pictureBufferDescPtr->originY = pictureBufferDescInitDataPtr->topPadding; pictureBufferDescPtr->lumaSize = (pictureBufferDescInitDataPtr->maxWidth + pictureBufferDescInitDataPtr->leftPadding + pictureBufferDescInitDataPtr->rightPadding) * (pictureBufferDescInitDataPtr->maxHeight + pictureBufferDescInitDataPtr->topPadding + pictureBufferDescInitDataPtr->botPadding); - pictureBufferDescPtr->chromaSize = pictureBufferDescPtr->lumaSize >> 2; + pictureBufferDescPtr->chromaSize = pictureBufferDescPtr->lumaSize >> (3 - pictureBufferDescInitDataPtr->colorFormat); pictureBufferDescPtr->packedFlag = EB_FALSE; if(pictureBufferDescInitDataPtr->splitMode == EB_TRUE) { @@ -121,20 +126,22 @@ EB_ERRORTYPE EbReconPictureBufferDescCtor( // Allocate the PictureBufferDesc Object *objectDblPtr = (EB_PTR) pictureBufferDescPtr; + EB_U32 subWidthCMinus1 = (pictureBufferDescInitDataPtr->colorFormat == EB_YUV444 ? 1 : 2) - 1; // Set the Picture Buffer Static variables pictureBufferDescPtr->maxWidth = pictureBufferDescInitDataPtr->maxWidth; pictureBufferDescPtr->maxHeight = pictureBufferDescInitDataPtr->maxHeight; pictureBufferDescPtr->width = pictureBufferDescInitDataPtr->maxWidth; pictureBufferDescPtr->height = pictureBufferDescInitDataPtr->maxHeight; pictureBufferDescPtr->bitDepth = pictureBufferDescInitDataPtr->bitDepth; + pictureBufferDescPtr->colorFormat = pictureBufferDescInitDataPtr->colorFormat; pictureBufferDescPtr->strideY = pictureBufferDescInitDataPtr->maxWidth + pictureBufferDescInitDataPtr->leftPadding + pictureBufferDescInitDataPtr->rightPadding; - pictureBufferDescPtr->strideCb = pictureBufferDescPtr->strideCr = pictureBufferDescPtr->strideY >> 1; + pictureBufferDescPtr->strideCb = pictureBufferDescPtr->strideCr = pictureBufferDescPtr->strideY >> subWidthCMinus1; pictureBufferDescPtr->originX = pictureBufferDescInitDataPtr->leftPadding; pictureBufferDescPtr->originY = pictureBufferDescInitDataPtr->topPadding; pictureBufferDescPtr->lumaSize = (pictureBufferDescInitDataPtr->maxWidth + pictureBufferDescInitDataPtr->leftPadding + pictureBufferDescInitDataPtr->rightPadding) * (pictureBufferDescInitDataPtr->maxHeight + pictureBufferDescInitDataPtr->topPadding + pictureBufferDescInitDataPtr->botPadding); - pictureBufferDescPtr->chromaSize = pictureBufferDescPtr->lumaSize >> 2; + pictureBufferDescPtr->chromaSize = pictureBufferDescPtr->lumaSize >> (3 - pictureBufferDescInitDataPtr->colorFormat); pictureBufferDescPtr->packedFlag = EB_FALSE; pictureBufferDescPtr->strideBitIncY = 0; diff --git a/Source/Lib/Codec/EbPictureBufferDesc.h b/Source/Lib/Codec/EbPictureBufferDesc.h index 6cbcc9a8c..94674da8b 100644 --- a/Source/Lib/Codec/EbPictureBufferDesc.h +++ b/Source/Lib/Codec/EbPictureBufferDesc.h @@ -51,12 +51,16 @@ typedef struct EbPictureBufferDesc_s EB_U16 maxWidth; // Luma picture width EB_U16 maxHeight; // Luma picture height EB_BITDEPTH bitDepth; // Pixel Bit Depth + EB_COLOR_FORMAT colorFormat; // Chroma subsampling format // Buffer Parameters EB_U32 lumaSize; // Size of the luma buffer EB_U32 chromaSize; // Size of the chroma buffers EB_BOOL packedFlag; // Indicates if sample buffers are packed or not + EB_SEI_MESSAGE dolbyVisionRpu; + EB_SEI_MESSAGE userSeiMsg; + } EbPictureBufferDesc_t; /************************************ @@ -67,6 +71,7 @@ typedef struct EbPictureBufferDescInitData_s EB_U16 maxWidth; EB_U16 maxHeight; EB_BITDEPTH bitDepth; + EB_COLOR_FORMAT colorFormat; EB_U32 bufferEnableMask; EB_U16 leftPadding; EB_U16 rightPadding; @@ -90,4 +95,4 @@ extern EB_ERRORTYPE EbReconPictureBufferDescCtor( #ifdef __cplusplus } #endif -#endif // EbPictureBuffer_h \ No newline at end of file +#endif // EbPictureBuffer_h diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index 24af4aea3..4327feb33 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -31,6 +31,8 @@ EB_ERRORTYPE PictureControlSetCtor( EB_ERRORTYPE return_error = EB_ErrorNone; EB_BOOL is16bit = initDataPtr->is16bit; + EB_U16 subWidthCMinus1 = (initDataPtr->colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (initDataPtr->colorFormat >= EB_YUV422 ? 1 : 2) - 1; EB_MALLOC(PictureControlSet_t*, objectPtr, sizeof(PictureControlSet_t), EB_N_PTR); @@ -38,6 +40,7 @@ EB_ERRORTYPE PictureControlSetCtor( inputPictureBufferDescInitData.maxWidth = initDataPtr->pictureWidth; inputPictureBufferDescInitData.maxHeight = initDataPtr->pictureHeight; inputPictureBufferDescInitData.bitDepth = initDataPtr->bitDepth; + inputPictureBufferDescInitData.colorFormat = initDataPtr->colorFormat; inputPictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_FULL_MASK; inputPictureBufferDescInitData.leftPadding = 0; inputPictureBufferDescInitData.rightPadding = 0; @@ -48,6 +51,7 @@ EB_ERRORTYPE PictureControlSetCtor( coeffBufferDescInitData.maxWidth = initDataPtr->pictureWidth; coeffBufferDescInitData.maxHeight = initDataPtr->pictureHeight; coeffBufferDescInitData.bitDepth = EB_16BIT; + coeffBufferDescInitData.colorFormat = initDataPtr->colorFormat; coeffBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_FULL_MASK; coeffBufferDescInitData.leftPadding = 0; coeffBufferDescInitData.rightPadding = 0; @@ -60,6 +64,7 @@ EB_ERRORTYPE PictureControlSetCtor( objectPtr->sequenceControlSetWrapperPtr = (EbObjectWrapper_t *)EB_NULL; + objectPtr->colorFormat = initDataPtr->colorFormat; objectPtr->reconPicture16bitPtr = (EbPictureBufferDesc_t *)EB_NULL; objectPtr->reconPicturePtr = (EbPictureBufferDesc_t *)EB_NULL; // Reconstructed Picture Buffer @@ -383,8 +388,8 @@ EB_ERRORTYPE PictureControlSetCtor( } return_error = NeighborArrayUnitCtor( &objectPtr->epCbReconNeighborArray, - MAX_PICTURE_WIDTH_SIZE >> 1, - MAX_PICTURE_HEIGHT_SIZE >> 1, + MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, + MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, sizeof(EB_U8), SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, @@ -395,8 +400,8 @@ EB_ERRORTYPE PictureControlSetCtor( } return_error = NeighborArrayUnitCtor( &objectPtr->epCrReconNeighborArray, - MAX_PICTURE_WIDTH_SIZE >> 1, - MAX_PICTURE_HEIGHT_SIZE >> 1, + MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, + MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, sizeof(EB_U8), SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, @@ -421,8 +426,8 @@ EB_ERRORTYPE PictureControlSetCtor( } return_error = NeighborArrayUnitCtor( &objectPtr->epCbReconNeighborArray16bit, - MAX_PICTURE_WIDTH_SIZE >> 1, - MAX_PICTURE_HEIGHT_SIZE >> 1, + MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, + MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, sizeof(EB_U16), SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, @@ -433,8 +438,8 @@ EB_ERRORTYPE PictureControlSetCtor( } return_error = NeighborArrayUnitCtor( &objectPtr->epCrReconNeighborArray16bit, - MAX_PICTURE_WIDTH_SIZE >> 1, - MAX_PICTURE_HEIGHT_SIZE >> 1, + MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, + MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, sizeof(EB_U16), SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, @@ -588,6 +593,29 @@ EB_ERRORTYPE PictureParentControlSetCtor( objectPtr->sequenceControlSetWrapperPtr = (EbObjectWrapper_t *)EB_NULL; objectPtr->inputPictureWrapperPtr = (EbObjectWrapper_t *)EB_NULL; objectPtr->referencePictureWrapperPtr = (EbObjectWrapper_t *)EB_NULL; + if (initDataPtr->colorFormat >= EB_YUV422) { + EbPictureBufferDescInitData_t inputPictureBufferDescInitData; + inputPictureBufferDescInitData.maxWidth = initDataPtr->pictureWidth; + inputPictureBufferDescInitData.maxHeight = initDataPtr->pictureHeight; + inputPictureBufferDescInitData.bitDepth = EB_8BIT; // Should be 8bit + inputPictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_CHROMA_MASK; + inputPictureBufferDescInitData.leftPadding = initDataPtr->leftPadding; + inputPictureBufferDescInitData.rightPadding = initDataPtr->rightPadding; + inputPictureBufferDescInitData.topPadding = initDataPtr->topPadding; + inputPictureBufferDescInitData.botPadding = initDataPtr->botPadding; + inputPictureBufferDescInitData.colorFormat = EB_YUV420; + inputPictureBufferDescInitData.splitMode = EB_FALSE; + return_error = EbPictureBufferDescCtor( + (EB_PTR*) &(objectPtr->chromaDownSamplePicturePtr), + (EB_PTR ) &inputPictureBufferDescInitData); + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + } else if(initDataPtr->colorFormat == EB_YUV420) { + objectPtr->chromaDownSamplePicturePtr = NULL; + } else { + return EB_ErrorBadParameter; + } // GOP objectPtr->predStructIndex = 0; diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index 45902c0ba..978a23a90 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -147,13 +147,14 @@ typedef struct PictureControlSet_s EbObjectWrapper_t *refPicPtrArray[MAX_NUM_OF_REF_PIC_LIST]; EB_U8 refPicQpArray[MAX_NUM_OF_REF_PIC_LIST]; - EB_PICTURE refSliceTypeArray[MAX_NUM_OF_REF_PIC_LIST]; + EB_PICTURE refSliceTypeArray[MAX_NUM_OF_REF_PIC_LIST]; // GOP EB_U64 pictureNumber; EB_U8 temporalLayerIndex; EB_U8 temporalId; + EB_COLOR_FORMAT colorFormat; EncDecSegments_t *encDecSegmentCtrl; @@ -346,6 +347,7 @@ typedef struct PictureParentControlSet_s EB_U64 pictureNumber; EbPictureBufferDesc_t *enhancedPicturePtr; + EbPictureBufferDesc_t *chromaDownSamplePicturePtr; EB_PICNOISE_CLASS picNoiseClass; @@ -580,6 +582,7 @@ typedef struct PictureControlSetInitData_s EB_U16 topPadding; EB_U16 botPadding; EB_BITDEPTH bitDepth; + EB_COLOR_FORMAT colorFormat; EB_U32 lcuSize; EB_U32 maxDepth; EB_BOOL is16bit; @@ -610,4 +613,4 @@ extern EB_ERRORTYPE PictureParentControlSetCtor( #ifdef __cplusplus } #endif -#endif // EbPictureControlSet_h \ No newline at end of file +#endif // EbPictureControlSet_h diff --git a/Source/Lib/Codec/EbPictureOperators.h b/Source/Lib/Codec/EbPictureOperators.h index 0549381f9..aa9eb3725 100644 --- a/Source/Lib/Codec/EbPictureOperators.h +++ b/Source/Lib/Codec/EbPictureOperators.h @@ -646,6 +646,8 @@ static EB_SPATIALFULLDIST_TYPE FUNC_TABLE SpatialFullDistortionKernel_funcPtrArr // 16x16 SpatialFullDistortionKernel, // 32x32 + SpatialFullDistortionKernel, + // 64x64 SpatialFullDistortionKernel }, // ASM_AVX2 diff --git a/Source/Lib/Codec/EbProductCodingLoop.c b/Source/Lib/Codec/EbProductCodingLoop.c index 8bae359e0..419f30474 100644 --- a/Source/Lib/Codec/EbProductCodingLoop.c +++ b/Source/Lib/Codec/EbProductCodingLoop.c @@ -1141,10 +1141,7 @@ void SetNmm( if (contextPtr->cuSize == 32) contextPtr->mvMergeSkipModeCount = 3; else - if (contextPtr->cuSize == 32) - contextPtr->mvMergeSkipModeCount = 3; - else - contextPtr->mvMergeSkipModeCount = 2; + contextPtr->mvMergeSkipModeCount = 2; } else { contextPtr->mvMergeSkipModeCount = 2; @@ -2209,6 +2206,8 @@ extern void GenerateIntraChromaReferenceSamplesMd( contextPtr->cbReconNeighborArray, contextPtr->crReconNeighborArray, contextPtr->intraRefPtr, + EB_YUV420, + EB_FALSE, EB_FALSE, EB_FALSE, EB_FALSE); @@ -2593,6 +2592,8 @@ static void Intra4x4InitFastLoop( contextPtr->cbReconNeighborArray, contextPtr->crReconNeighborArray, contextPtr->intraRefPtr, + EB_YUV420, + EB_FALSE, EB_FALSE, EB_FALSE, EB_FALSE); @@ -2698,7 +2699,8 @@ EB_EXTERN EB_ERRORTYPE PerformIntra4x4Search( EbPictureBufferDesc_t *tuTransCoeffTmpPtr; EbPictureBufferDesc_t *tuQuantCoeffTmpPtr; EbPictureBufferDesc_t *transformBuffer; - EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; + EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; + EB_U32 inputOriginIndex; EB_U32 puOriginIndex; @@ -2723,7 +2725,7 @@ EB_EXTERN EB_ERRORTYPE PerformIntra4x4Search( EB_U32 chromaShift; if (contextPtr->coeffCabacUpdate) - memcpy(&(contextPtr->i4x4CoeffCtxModel), &(contextPtr->latestValidCoeffCtxModel), sizeof(CoeffCtxtMdl_t)); + EB_MEMCPY(&(contextPtr->i4x4CoeffCtxModel), &(contextPtr->latestValidCoeffCtxModel), sizeof(CoeffCtxtMdl_t)); for (partitionIndex = 0; partitionIndex < 4; partitionIndex++) { @@ -4692,7 +4694,8 @@ EB_EXTERN EB_ERRORTYPE ModeDecisionLcu( EB_U32 cuIdx; // Input - EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; + EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; + // Mode Decision Candidate Buffers EB_U32 bufferTotalCount; @@ -5173,7 +5176,8 @@ EB_EXTERN EB_ERRORTYPE BdpPillar( EB_U32 cuIdx; // Input - EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; + EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; + // Mode Decision Candidate Buffers EB_U32 bufferTotalCount; @@ -5575,7 +5579,7 @@ EB_EXTERN EB_ERRORTYPE Bdp64x64vs32x32RefinementProcess( EB_ERRORTYPE return_error = EB_ErrorNone; // Input - EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; + EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; // Mode Decision Candidate Buffers EB_U32 bufferTotalCount; @@ -5852,7 +5856,7 @@ EB_EXTERN EB_ERRORTYPE Bdp16x16vs8x8RefinementProcess( EB_ERRORTYPE return_error = EB_ErrorNone; // Input - EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; + EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; // Mode Decision Candidate Buffers EB_U32 bufferTotalCount; @@ -5898,12 +5902,11 @@ EB_EXTERN EB_ERRORTYPE Bdp16x16vs8x8RefinementProcess( while (parentLeafIndex < CU_MAX_COUNT) { - EB_U8 parentDepthOffset = DepthOffset[GetCodedUnitStats(parentLeafIndex)->depth]; - EB_U8 childDepthOffset = DepthOffset[GetCodedUnitStats(parentLeafIndex)->depth + 1]; - if (lcuPtr->codedLeafArrayPtr[parentLeafIndex]->splitFlag == EB_FALSE) { - EB_BOOL cu16x16RefinementFlag; + EB_U8 parentDepthOffset = DepthOffset[GetCodedUnitStats(parentLeafIndex)->depth]; + EB_U8 childDepthOffset = DepthOffset[GetCodedUnitStats(parentLeafIndex)->depth + 1]; + EB_BOOL cu16x16RefinementFlag; if (pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_LIGHT_BDP_DEPTH_MODE || (pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_LCU_SWITCH_DEPTH_MODE && (pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuAddr] == LCU_LIGHT_BDP_DEPTH_MODE))){ @@ -6302,7 +6305,7 @@ EB_EXTERN EB_ERRORTYPE BdpMvMergePass( { EB_ERRORTYPE return_error = EB_ErrorNone; // Input - EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr; + EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; // Mode Decision Candidate Buffers EB_U32 bufferTotalCount; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 945b34d03..241d010d2 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -1968,8 +1968,6 @@ void FrameLevelRcFeedbackPictureMode2( rateControlParamPtr->firstPoc += PARALLEL_GOP_MAX_NUMBER*(EB_U32)(sequenceControlSetPtr->intraPeriodLength + 1); rateControlParamPtr->lastPoc += PARALLEL_GOP_MAX_NUMBER*(EB_U32)(sequenceControlSetPtr->intraPeriodLength + 1); rateControlParamPtr->processedFramesNumber = 0; - rateControlParamPtr->virtualBufferLevel = rateControlParamPtr->virtualBufferLevel; - rateControlParamPtr->previousVirtualBufferLevel = rateControlParamPtr->previousVirtualBufferLevel; rateControlParamPtr->extraApBitRatioI = 0; rateControlParamPtr->inUse = EB_FALSE; rateControlParamPtr->wasUsed = EB_TRUE; @@ -2502,8 +2500,6 @@ void* RateControlKernel(void *inputPtr) pictureControlSetPtr->pictureQp = (EB_U8)CLIP3((EB_S32)sequenceControlSetPtr->staticConfig.minQpAllowed, (EB_S32)sequenceControlSetPtr->staticConfig.maxQpAllowed,pictureControlSetPtr->ParentPcsPtr->pictureQp); } - - pictureControlSetPtr->pictureQp = pictureControlSetPtr->pictureQp; } else{ FrameLevelRcInputPictureMode2( diff --git a/Source/Lib/Codec/EbRateDistortionCost.c b/Source/Lib/Codec/EbRateDistortionCost.c index 978e80292..ff819c7c4 100644 --- a/Source/Lib/Codec/EbRateDistortionCost.c +++ b/Source/Lib/Codec/EbRateDistortionCost.c @@ -2623,7 +2623,7 @@ EB_ERRORTYPE EncodeTuCalcCost( // **Compute distortion - if (componentMask == PICTURE_BUFFER_DESC_LUMA_MASK || componentMask == PICTURE_BUFFER_DESC_FULL_MASK){ + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { // Non Zero Distortion // *Note - As of Oct 2011, the JCT-VC uses the PSNR forumula // PSNR = (LUMA_WEIGHT * PSNRy + PSNRu + PSNRv) / (2+LUMA_WEIGHT) diff --git a/Source/Lib/Codec/EbReferenceObject.c b/Source/Lib/Codec/EbReferenceObject.c index 58c18980e..f098abc20 100644 --- a/Source/Lib/Codec/EbReferenceObject.c +++ b/Source/Lib/Codec/EbReferenceObject.c @@ -200,6 +200,7 @@ EB_ERRORTYPE EbReferenceObjectCtor( bufDesc.topPadding = pictureBufferDescInitDataPtr->topPadding; bufDesc.botPadding = pictureBufferDescInitDataPtr->botPadding; bufDesc.splitMode = 0; + bufDesc.colorFormat = pictureBufferDescInitDataPtr->colorFormat; return_error = EbPictureBufferDescCtor((EB_PTR*)&(referenceObject->refDenSrcPicture), diff --git a/Source/Lib/Codec/EbResourceCoordinationProcess.c b/Source/Lib/Codec/EbResourceCoordinationProcess.c index d95ffe31d..51485bb3a 100644 --- a/Source/Lib/Codec/EbResourceCoordinationProcess.c +++ b/Source/Lib/Codec/EbResourceCoordinationProcess.c @@ -487,6 +487,9 @@ void* ResourceCoordinationKernel(void *inputPtr) EB_U32 inputSize = 0; EbObjectWrapper_t *prevPictureControlSetWrapperPtr = 0; + EB_U32 chromaFormat = EB_YUV420; + EB_U32 subWidthCMinus1 = 1; + EB_U32 subHeightCMinus1 = 1; for(;;) { @@ -504,6 +507,9 @@ void* ResourceCoordinationKernel(void *inputPtr) // Get source video bit depth is16BitInput = (EB_BOOL)(sequenceControlSetPtr->staticConfig.encoderBitDepth > EB_8BIT); + chromaFormat = sequenceControlSetPtr->chromaFormatIdc; + subWidthCMinus1 = (chromaFormat == EB_YUV444 ? 1 : 2) - 1; + subHeightCMinus1 = (chromaFormat >= EB_YUV422 ? 1 : 2) - 1; // If config changes occured since the last picture began encoding, then // prepare a new sequenceControlSetPtr containing the new changes and update the state // of the previous Active SequenceControlSet @@ -516,8 +522,8 @@ void* ResourceCoordinationKernel(void *inputPtr) { contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lumaWidth = contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth; contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lumaHeight = contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight; - contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaWidth = (contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth >> 1); - contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaHeight = (contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight >> 1); + contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaWidth = (contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth >> subWidthCMinus1); + contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaHeight = (contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight >> subHeightCMinus1); contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->padRight = contextPtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputPadRight; @@ -627,8 +633,8 @@ void* ResourceCoordinationKernel(void *inputPtr) // Copy data from the buffer to the input frame // *Note - Assumes 4:2:0 planar - inputPictureWrapperPtr = 0; - inputPicturePtr = 0; + inputPictureWrapperPtr = NULL; + inputPicturePtr = NULL; // assign the input picture pictureControlSetPtr->enhancedPicturePtr = (EbPictureBufferDesc_t*)ebInputPtr->pBuffer; @@ -651,12 +657,12 @@ void* ResourceCoordinationKernel(void *inputPtr) inputPicturePtr->lumaSize = ((sequenceControlSetPtr->maxInputLumaWidth - sequenceControlSetPtr->maxInputPadRight) + sequenceControlSetPtr->leftPadding + sequenceControlSetPtr->rightPadding) * ((sequenceControlSetPtr->maxInputLumaHeight - sequenceControlSetPtr->maxInputPadBottom) + sequenceControlSetPtr->topPadding + sequenceControlSetPtr->botPadding); - inputPicturePtr->chromaSize = inputPicturePtr->lumaSize >> 2; + inputPicturePtr->chromaSize = inputPicturePtr->lumaSize >> (3 - chromaFormat); inputPicturePtr->width = sequenceControlSetPtr->lumaWidth; inputPicturePtr->height = sequenceControlSetPtr->lumaHeight; inputPicturePtr->strideY = sequenceControlSetPtr->lumaWidth + sequenceControlSetPtr->leftPadding + sequenceControlSetPtr->rightPadding; - inputPicturePtr->strideCb = inputPicturePtr->strideCr = inputPicturePtr->strideY >> 1; + inputPicturePtr->strideCb = inputPicturePtr->strideCr = inputPicturePtr->strideY >> subWidthCMinus1; inputPicturePtr->strideBitIncY = inputPicturePtr->strideY; inputPicturePtr->strideBitIncCb = inputPicturePtr->strideCb; diff --git a/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c b/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c index ae5a5c254..46916c785 100644 --- a/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c +++ b/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c @@ -677,6 +677,11 @@ EB_ERRORTYPE SaoGenerationDecision( reconPicturePtr = ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->referencePicture; else reconPicturePtr = pictureControlSetPtr->reconPicturePtr; + + const EB_COLOR_FORMAT colorFormat = reconPicturePtr->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + // Intialise SAO Parameters saoParams->saoTypeIndex[SAO_COMPONENT_LUMA] = 0; saoParams->saoTypeIndex[SAO_COMPONENT_CHROMA] = 0; @@ -707,12 +712,12 @@ EB_ERRORTYPE SaoGenerationDecision( // U SaoGatherFunctionTableLossy[(ASM_TYPES & PREAVX2_MASK) && 1]( - &(inputPicturePtr->bufferCb[((inputPicturePtr->originY + tbOriginY) * inputPicturePtr->strideCb + inputPicturePtr->originX + tbOriginX) >> 1]), + &(inputPicturePtr->bufferCb[(((inputPicturePtr->originY + tbOriginY) * inputPicturePtr->strideCb) >> subHeightCMinus1) + ((inputPicturePtr->originX + tbOriginX) >> subWidthCMinus1)]), inputPicturePtr->strideCb, - &(reconPicturePtr->bufferCb[((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCb + reconPicturePtr->originX + tbOriginX) >> 1]), + &(reconPicturePtr->bufferCb[(((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCb) >> subHeightCMinus1) + ((reconPicturePtr->originX + tbOriginX) >> subWidthCMinus1)]), reconPicturePtr->strideCb, - lcuWidth >> 1, - lcuHeight >> 1, + lcuWidth >> subWidthCMinus1, + lcuHeight >> subHeightCMinus1, saoStats->boDiff[1], saoStats->boCount[1], saoStats->eoDiff[1], @@ -720,12 +725,12 @@ EB_ERRORTYPE SaoGenerationDecision( // V SaoGatherFunctionTableLossy[(ASM_TYPES & PREAVX2_MASK) && 1]( - &(inputPicturePtr->bufferCr[((inputPicturePtr->originY + tbOriginY) * inputPicturePtr->strideCr + inputPicturePtr->originX + tbOriginX) >> 1]), + &(inputPicturePtr->bufferCr[(((inputPicturePtr->originY + tbOriginY) * inputPicturePtr->strideCr) >> subHeightCMinus1) + ((inputPicturePtr->originX + tbOriginX) >> subWidthCMinus1)]), inputPicturePtr->strideCr, - &(reconPicturePtr->bufferCr[((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCr + reconPicturePtr->originX + tbOriginX) >> 1]), + &(reconPicturePtr->bufferCr[(((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCr) >> subHeightCMinus1) + ((reconPicturePtr->originX + tbOriginX) >> subHeightCMinus1)]), reconPicturePtr->strideCr, - lcuWidth >> 1, - lcuHeight >> 1, + lcuWidth >> subWidthCMinus1, + lcuHeight >> subHeightCMinus1, saoStats->boDiff[2], saoStats->boCount[2], saoStats->eoDiff[2], @@ -958,6 +963,10 @@ EB_ERRORTYPE SaoGenerationDecision16bit( recon16 = ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->referencePicture16bit; else recon16 = pictureControlSetPtr->reconPicture16bitPtr; + + const EB_COLOR_FORMAT colorFormat = recon16->colorFormat; + const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; // Intialise SAO Parameters saoParams->saoTypeIndex[SAO_COMPONENT_LUMA] = 0; @@ -974,8 +983,8 @@ EB_ERRORTYPE SaoGenerationDecision16bit( if (mmSao) { - EB_U32 lcuChromaWidth = lcuWidth >> 1; - EB_U32 lcuChromaHeight = lcuHeight >> 1; + EB_U32 lcuChromaWidth = lcuWidth >> subWidthCMinus1; + EB_U32 lcuChromaHeight = lcuHeight >> subHeightCMinus1; // Y // Requirement: lcuWidth = 28, 56 or lcuWidth % 16 = 0 @@ -998,7 +1007,7 @@ EB_ERRORTYPE SaoGenerationDecision16bit( SaoGatherFunctionTabl_16bit[((lcuChromaWidth & 15) == 0) || (lcuChromaWidth == 28) || (lcuChromaWidth == 56)]( (EB_U16*)inputLcuPtr->bufferCb, inputLcuPtr->strideCb, - (EB_U16*)(recon16->bufferCb) + (((recon16->originY + tbOriginY) * recon16->strideCb + (recon16->originX + tbOriginX)) >> 1), + (EB_U16*)(recon16->bufferCb) + ((((recon16->originY + tbOriginY) * recon16->strideCb) >> subHeightCMinus1) + ((recon16->originX + tbOriginX) >> subWidthCMinus1)), recon16->strideCb, lcuChromaWidth, lcuChromaHeight, @@ -1011,7 +1020,7 @@ EB_ERRORTYPE SaoGenerationDecision16bit( SaoGatherFunctionTabl_16bit[((lcuChromaWidth & 15) == 0) || (lcuChromaWidth == 28) || (lcuChromaWidth == 56)]( (EB_U16*)inputLcuPtr->bufferCr, inputLcuPtr->strideCr, - (EB_U16*)(recon16->bufferCr) + (((recon16->originY + tbOriginY) * recon16->strideCb + (recon16->originX + tbOriginX)) >> 1), + (EB_U16*)(recon16->bufferCr) + ((((recon16->originY + tbOriginY) * recon16->strideCb) >> subHeightCMinus1) + ((recon16->originX + tbOriginX) >> subWidthCMinus1)), recon16->strideCr, lcuChromaWidth, lcuChromaHeight, diff --git a/Source/Lib/Codec/EbSei.c b/Source/Lib/Codec/EbSei.c index 2c2586fde..688cad7d1 100644 --- a/Source/Lib/Codec/EbSei.c +++ b/Source/Lib/Codec/EbSei.c @@ -288,6 +288,51 @@ void EbRecoveryPointSeiCtor( return; } +void EbContentLightLevelCtor( + AppContentLightLevelSei_t *contentLightLevelPtr) +{ + contentLightLevelPtr->maxContentLightLevel = 0; + contentLightLevelPtr->maxPicAverageLightLevel = 0; +} + +void EbMasteringDisplayColorVolumeCtor( + AppMasteringDisplayColorVolumeSei_t *masteringDisplayPtr) +{ + + EB_MEMSET( + masteringDisplayPtr->displayPrimaryX, + 0, + sizeof(EB_U16) * 3); + EB_MEMSET( + masteringDisplayPtr->displayPrimaryY, + 0, + sizeof(EB_U16) * 3); + + masteringDisplayPtr->whitePointX = 0; + masteringDisplayPtr->whitePointY= 0 ; + masteringDisplayPtr->maxDisplayMasteringLuminance = 0; + masteringDisplayPtr->minDisplayMasteringLuminance = 0; +} + +void EbRegUserDataSEICtor( + RegistedUserData_t* regUserDataSeiPtr) { + + regUserDataSeiPtr->userData = NULL; + regUserDataSeiPtr->userDataSize = 0; +} + +void EbUnRegUserDataSEICtor( + UnregistedUserData_t* UnRegUserDataPtr) { + + UnRegUserDataPtr->userData = NULL; + UnRegUserDataPtr->userDataSize = 0; + EB_MEMSET( + UnRegUserDataPtr->uuidIsoIec_11578, + 0, + sizeof(EB_U8) * 16); +} + + /************************************************** * GetUvlcCodeLength **************************************************/ @@ -490,5 +535,38 @@ EB_U32 GetRecoveryPointSEILength( seiLength = (seiLength + 7) >> 3; + return seiLength; +} + +EB_U32 GetContentLightLevelSEILength() +{ + EB_U32 seiLength = 0; + + // max_content_light_level + seiLength += 16; + + // max_pixel_average_light_level + seiLength += 16; + + seiLength = (seiLength + 7) >> 3; + + return seiLength; +} + +EB_U32 GetMasteringDisplayColorVolumeSEILength() +{ + EB_U32 seiLength = 0; + + // R, G, B Primaries + seiLength += 2 * 16 + 2 * 16 + 2 * 16; + + // White Point Co-Ordinates + seiLength += 2 * 16; + + // min & max luminance values + seiLength += 2 * 32; + + seiLength = (seiLength + 7) >> 3; + return seiLength; } \ No newline at end of file diff --git a/Source/Lib/Codec/EbSei.h b/Source/Lib/Codec/EbSei.h index 22d9e396f..b9d25a0fc 100644 --- a/Source/Lib/Codec/EbSei.h +++ b/Source/Lib/Codec/EbSei.h @@ -203,6 +203,23 @@ extern "C" { // + typedef struct AppContentLightLevelSei_s { + + EB_U16 maxContentLightLevel; + EB_U16 maxPicAverageLightLevel; + + }AppContentLightLevelSei_t; + + typedef struct AppMasteringDisplayColorVolumeSei_s { + + EB_U16 displayPrimaryX[3]; + EB_U16 displayPrimaryY[3]; + EB_U16 whitePointX, whitePointY; + EB_U32 maxDisplayMasteringLuminance; + EB_U32 minDisplayMasteringLuminance; + + }AppMasteringDisplayColorVolumeSei_t; + typedef struct EB_FRAME_RATE_CFG { @@ -310,6 +327,18 @@ extern void EbActiveParameterSetSeiCtor( extern void EbRecoveryPointSeiCtor( AppRecoveryPoint_t *recoveryPointSeiPtr); +extern void EbContentLightLevelCtor( + AppContentLightLevelSei_t *contentLightLevelPtr); + +extern void EbMasteringDisplayColorVolumeCtor( + AppMasteringDisplayColorVolumeSei_t *masteringDisplayPtr); + +extern void EbRegUserDataSEICtor( + RegistedUserData_t *regUserDataSeiPtr); + +extern void EbUnRegUserDataSEICtor( + UnregistedUserData_t *UnRegUserDataPtr); + extern EB_U32 GetPictureTimingSEILength( AppPictureTimingSei_t *picTimingSeiPtr, AppVideoUsabilityInfo_t *vuiPtr); @@ -323,6 +352,10 @@ extern EB_U32 GetActiveParameterSetSEILength( extern EB_U32 GetRecoveryPointSEILength( AppRecoveryPoint_t *recoveryPointSeiPtr); + +extern EB_U32 GetContentLightLevelSEILength(); + +extern EB_U32 GetMasteringDisplayColorVolumeSEILength(); #ifdef __cplusplus } #endif diff --git a/Source/Lib/Codec/EbSequenceControlSet.c b/Source/Lib/Codec/EbSequenceControlSet.c index 7cfa79134..278375488 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.c +++ b/Source/Lib/Codec/EbSequenceControlSet.c @@ -72,7 +72,7 @@ EB_ERRORTYPE EbSequenceControlSetCtor( sequenceControlSetPtr->profileIdc = 0; sequenceControlSetPtr->levelIdc = 0; sequenceControlSetPtr->tierIdc = 0; - sequenceControlSetPtr->chromaFormatIdc = 1; // EB_YUV420 + sequenceControlSetPtr->chromaFormatIdc = EB_YUV420; sequenceControlSetPtr->maxTemporalLayers = 1; sequenceControlSetPtr->bitsForPictureOrderCount = 16; @@ -149,9 +149,25 @@ EB_ERRORTYPE EbSequenceControlSetCtor( EbRecoveryPointSeiCtor( &sequenceControlSetPtr->recoveryPoint); - // Initialize LCU params - LcuParamsCtor( - sequenceControlSetPtr); + // Initialize Content Light Level SEI + EbContentLightLevelCtor( + &sequenceControlSetPtr->contentLightLevel); + + // Initialize Mastering Color Volume SEI + EbMasteringDisplayColorVolumeCtor( + &sequenceControlSetPtr->masteringDisplayColorVolume); + + // Initialize Registered User Data SEI + EbRegUserDataSEICtor( + &sequenceControlSetPtr->regUserDataSeiPtr); + + // Initialize Un-Registered User Data SEI + EbUnRegUserDataSEICtor( + &sequenceControlSetPtr->unRegUserDataSeiPtr); + + // Initialize LCU params + LcuParamsCtor( + sequenceControlSetPtr); sequenceControlSetPtr->maxDpbSize = 0; @@ -182,8 +198,6 @@ EB_ERRORTYPE CopySequenceControlSet( dst->bitsForPictureOrderCount = src->bitsForPictureOrderCount; writeCount += sizeof(EB_U32); dst->maxInputLumaWidth = src->maxInputLumaWidth; writeCount += sizeof(EB_U32); dst->maxInputLumaHeight = src->maxInputLumaHeight; writeCount += sizeof(EB_U32); - dst->maxInputChromaHeight = src->maxInputChromaHeight; writeCount += sizeof(EB_U32); - dst->maxInputChromaWidth = src->maxInputChromaWidth; writeCount += sizeof(EB_U32); dst->maxInputPadRight = src->maxInputPadRight; writeCount += sizeof(EB_U32); dst->maxInputPadBottom = src->maxInputPadBottom; writeCount += sizeof(EB_U32); dst->lumaWidth = src->lumaWidth; writeCount += sizeof(EB_U32); @@ -254,6 +268,20 @@ EB_ERRORTYPE CopySequenceControlSet( writeCount += sizeof(AppRecoveryPoint_t); + EB_MEMCPY( + &dst->contentLightLevel, + &src->contentLightLevel, + sizeof(AppContentLightLevelSei_t)); + + writeCount += sizeof(AppContentLightLevelSei_t); + + EB_MEMCPY( + &dst->masteringDisplayColorVolume, + &src->masteringDisplayColorVolume, + sizeof(AppMasteringDisplayColorVolumeSei_t)); + + writeCount += sizeof(AppMasteringDisplayColorVolumeSei_t); + EB_MEMCPY( &dst->picTimingSei, &src->picTimingSei, @@ -261,6 +289,20 @@ EB_ERRORTYPE CopySequenceControlSet( writeCount += sizeof(AppPictureTimingSei_t); + EB_MEMCPY( + &dst->regUserDataSeiPtr, + &src->regUserDataSeiPtr, + sizeof(RegistedUserData_t)); + + writeCount += sizeof(RegistedUserData_t); + + EB_MEMCPY( + &dst->unRegUserDataSeiPtr, + &src->unRegUserDataSeiPtr, + sizeof(UnregistedUserData_t)); + + writeCount += sizeof(UnregistedUserData_t); + EbVideoUsabilityInfoCopy( dst->videoUsabilityInfoPtr, src->videoUsabilityInfoPtr); diff --git a/Source/Lib/Codec/EbSequenceControlSet.h b/Source/Lib/Codec/EbSequenceControlSet.h index e2d05a210..7850dd1f4 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.h +++ b/Source/Lib/Codec/EbSequenceControlSet.h @@ -39,8 +39,8 @@ typedef struct SequenceControlSet_s // Picture deminsions EB_U16 maxInputLumaWidth; EB_U16 maxInputLumaHeight; - EB_U16 maxInputChromaWidth; - EB_U16 maxInputChromaHeight; + //EB_U16 maxInputChromaWidth; + //EB_U16 maxInputChromaHeight; EB_U16 maxInputPadRight; EB_U16 maxInputPadBottom; EB_U16 lumaWidth; @@ -121,6 +121,19 @@ typedef struct SequenceControlSet_s // Recovery point AppRecoveryPoint_t recoveryPoint; + + // Content Light Level sei + AppContentLightLevelSei_t contentLightLevel; + + // Mastering Display Color Volume Sei + AppMasteringDisplayColorVolumeSei_t masteringDisplayColorVolume; + + // Registered User data Sei + RegistedUserData_t regUserDataSeiPtr; + + // Un Registered User data Sei + UnregistedUserData_t unRegUserDataSeiPtr; + // Maximum Decoded Picture Buffer size. EB_U32 maxDpbSize; @@ -226,4 +239,4 @@ extern EB_ERRORTYPE DeriveInputResolution( #ifdef __cplusplus } #endif -#endif // EbSequenceControlSet_h \ No newline at end of file +#endif // EbSequenceControlSet_h diff --git a/Source/Lib/Codec/EbSourceBasedOperationsProcess.c b/Source/Lib/Codec/EbSourceBasedOperationsProcess.c index abc720d33..a4388d7d6 100644 --- a/Source/Lib/Codec/EbSourceBasedOperationsProcess.c +++ b/Source/Lib/Codec/EbSourceBasedOperationsProcess.c @@ -110,8 +110,10 @@ void DerivePictureActivityStatistics( } - pictureControlSetPtr->nonMovingIndexAverage = (EB_U16)(nonMovingIndexSum / completeLcuCount); - pictureControlSetPtr->zzCostAverage = zzSum / completeLcuCount; + if (completeLcuCount > 0) { + pictureControlSetPtr->nonMovingIndexAverage = (EB_U16)(nonMovingIndexSum / completeLcuCount); + pictureControlSetPtr->zzCostAverage = zzSum / completeLcuCount; + } pictureControlSetPtr->lowMotionContentFlag = pictureControlSetPtr->zzCostAverage == 0 ? EB_TRUE : EB_FALSE; return; diff --git a/Source/Lib/Codec/EbThreads.c b/Source/Lib/Codec/EbThreads.c index 63f7eea97..d2d543732 100644 --- a/Source/Lib/Codec/EbThreads.c +++ b/Source/Lib/Codec/EbThreads.c @@ -74,28 +74,31 @@ EB_HANDLE EbCreateThread( pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); threadHandle = (pthread_t*) malloc(sizeof(pthread_t)); - int ret = pthread_create( - (pthread_t*)threadHandle, // Thread handle - &attr, // attributes - threadFunction, // function to be run by new thread - threadContext); - - if (ret != 0) - if (ret == EPERM) { - - pthread_cancel(*((pthread_t*)threadHandle)); - pthread_join(*((pthread_t*)threadHandle), NULL); - free(threadHandle); - - threadHandle = (pthread_t*)malloc(sizeof(pthread_t)); - - pthread_create( - (pthread_t*)threadHandle, // Thread handle - (const pthread_attr_t*)EB_NULL, // attributes - threadFunction, // function to be run by new thread - threadContext); - } - + if (threadHandle != NULL) { + int ret = pthread_create( + (pthread_t*)threadHandle, // Thread handle + &attr, // attributes + threadFunction, // function to be run by new thread + threadContext); + + if (ret != 0) { + if (ret == EPERM) { + + pthread_cancel(*((pthread_t*)threadHandle)); + free(threadHandle); + + threadHandle = (pthread_t*)malloc(sizeof(pthread_t)); + if (threadHandle != NULL) { + pthread_create( + (pthread_t*)threadHandle, // Thread handle + (const pthread_attr_t*)EB_NULL, // attributes + threadFunction, // function to be run by new thread + threadContext); + } + } + } + } + pthread_attr_destroy(&attr); #endif // _WIN32 return threadHandle; @@ -210,19 +213,19 @@ EB_HANDLE EbCreateMutex( EB_HANDLE mutexHandle = NULL; #ifdef _WIN32 - mutexHandle = (EB_HANDLE) CreateMutex( - NULL, // default security attributes - FALSE, // FALSE := not initially owned - NULL); // mutex is not named + mutexHandle = (EB_HANDLE)CreateMutex( + NULL, // default security attributes + FALSE, // FALSE := not initially owned + NULL); // mutex is not named #elif __linux__ - mutexHandle = (EB_HANDLE) malloc(sizeof(pthread_mutex_t)); - - pthread_mutex_init( - (pthread_mutex_t*) mutexHandle, - NULL); // default attributes - + mutexHandle = (EB_HANDLE)malloc(sizeof(pthread_mutex_t)); + if (mutexHandle != NULL) { + pthread_mutex_init( + (pthread_mutex_t*)mutexHandle, + NULL); // default attributes + } #endif // _WIN32 return mutexHandle; diff --git a/Source/Lib/Codec/EbTransQuantBuffers.c b/Source/Lib/Codec/EbTransQuantBuffers.c index b178fa20a..740d03305 100644 --- a/Source/Lib/Codec/EbTransQuantBuffers.c +++ b/Source/Lib/Codec/EbTransQuantBuffers.c @@ -18,6 +18,7 @@ EB_ERRORTYPE EbTransQuantBuffersCtor( transCoeffInitArray.maxWidth = MAX_LCU_SIZE; transCoeffInitArray.maxHeight = MAX_LCU_SIZE; transCoeffInitArray.bitDepth = EB_16BIT; + transCoeffInitArray.colorFormat = EB_YUV420; transCoeffInitArray.bufferEnableMask = PICTURE_BUFFER_DESC_FULL_MASK; transCoeffInitArray.leftPadding = 0; transCoeffInitArray.rightPadding = 0; diff --git a/Source/Lib/Codec/EbTransformUnit.h b/Source/Lib/Codec/EbTransformUnit.h index 5a0bbe472..ab5abcc1a 100644 --- a/Source/Lib/Codec/EbTransformUnit.h +++ b/Source/Lib/Codec/EbTransformUnit.h @@ -25,13 +25,18 @@ typedef struct TransformUnit_s { unsigned lumaCbf : 1; unsigned transCoeffShapeLuma : 2; unsigned transCoeffShapeChroma : 2; + unsigned transCoeffShapeChroma2 : 2; - EB_U16 nzCoefCount[3]; + unsigned cbCbf2 : 1; + unsigned crCbf2 : 1; + EB_U16 nzCoefCount[3]; EB_BOOL isOnlyDc[3]; + EB_U16 nzCoefCount2[2]; + EB_BOOL isOnlyDc2[3]; } TransformUnit_t; #pragma pack(pop) #ifdef __cplusplus } #endif -#endif // EbTransformUnit_h \ No newline at end of file +#endif // EbTransformUnit_h diff --git a/Source/Lib/Codec/EbTransforms.c b/Source/Lib/Codec/EbTransforms.c index 5a015d6e9..a826859df 100644 --- a/Source/Lib/Codec/EbTransforms.c +++ b/Source/Lib/Codec/EbTransforms.c @@ -3233,21 +3233,22 @@ void UnifiedQuantizeInvQuantize( } } } - - UpdateQiQCoef( - quantCoeff, - reconCoeff, - coeffStride, - shiftedFFunc, - iq_offset, - shiftNum, - activeAreaSize, - &(*yCountNonZeroCoeffs), - componentType, - sliceType, - temporalLayerIndex, - enableCbflag, - enableContouringQCUpdateFlag); + if (yCountNonZeroCoeffs != (EB_U32*)EB_NULL) { + UpdateQiQCoef( + quantCoeff, + reconCoeff, + coeffStride, + shiftedFFunc, + iq_offset, + shiftNum, + activeAreaSize, + &(*yCountNonZeroCoeffs), + componentType, + sliceType, + temporalLayerIndex, + enableCbflag, + enableContouringQCUpdateFlag); + } } } diff --git a/Source/SimpleApp/EbSimpleAppContext.c b/Source/SimpleApp/EbSimpleAppContext.c index 28e57c30c..756e511f8 100644 --- a/Source/SimpleApp/EbSimpleAppContext.c +++ b/Source/SimpleApp/EbSimpleAppContext.c @@ -9,22 +9,17 @@ #define INPUT_SIZE_1080p_TH 0x1AB3F0 // 1.75 Million #define INPUT_SIZE_4K_TH 0x29F630 // 2.75 Million #define EB_OUTPUTSTREAMBUFFERSIZE_MACRO(ResolutionSize) ((ResolutionSize) < (INPUT_SIZE_1080i_TH) ? 0x1E8480 : (ResolutionSize) < (INPUT_SIZE_1080p_TH) ? 0x2DC6C0 : (ResolutionSize) < (INPUT_SIZE_4K_TH) ? 0x2DC6C0 : 0x2DC6C0 ) -EB_ERRORTYPE AllocateFrameBuffer( - EbConfig_t *config, - uint8_t *pBuffer) + +static EB_ERRORTYPE AllocateFrameBuffer(EbConfig_t *config, uint8_t *pBuffer) { EB_ERRORTYPE return_error = EB_ErrorNone; const int32_t tenBitPackedMode = (config->encoderBitDepth > 8) && (config->compressedTenBitFormat == 0) ? 1 : 0; // Determine size of each plane - const size_t luma8bitSize = - config->inputPaddedWidth * - config->inputPaddedHeight * - (1 << tenBitPackedMode); - - const size_t chroma8bitSize = luma8bitSize >> 2; - const size_t luma10bitSize = (config->encoderBitDepth > 8 && tenBitPackedMode == 0) ? luma8bitSize : 0; - const size_t chroma10bitSize = (config->encoderBitDepth > 8 && tenBitPackedMode == 0) ? chroma8bitSize : 0; + const uint32_t luma8bitSize = config->inputPaddedWidth * config->inputPaddedHeight * (1 << tenBitPackedMode); + const uint32_t chroma8bitSize = luma8bitSize >> (3- config->encoderColorFormat); + const uint32_t luma10bitSize = (config->encoderBitDepth > 8 && tenBitPackedMode == 0) ? luma8bitSize : 0; + const uint32_t chroma10bitSize = (config->encoderBitDepth > 8 && tenBitPackedMode == 0) ? chroma8bitSize : 0; // Determine EB_H265_ENC_INPUT* inputPtr = (EB_H265_ENC_INPUT*)pBuffer; @@ -73,12 +68,11 @@ EB_ERRORTYPE AllocateFrameBuffer( } return return_error; } + /*********************************** * AppContext Constructor ***********************************/ -EB_ERRORTYPE EbAppContextCtor( - EbAppContext_t *contextPtr, - EbConfig_t *config) +EB_ERRORTYPE EbAppContextCtor(EbAppContext_t *contextPtr, EbConfig_t *config) { EB_ERRORTYPE return_error = EB_ErrorInsufficientResources; @@ -113,13 +107,11 @@ EB_ERRORTYPE EbAppContextCtor( if (config->reconFile) { contextPtr->reconBuffer = (EB_BUFFERHEADERTYPE*)malloc(sizeof(EB_BUFFERHEADERTYPE)); if (!contextPtr->reconBuffer) return return_error; - const size_t lumaSize = - config->inputPaddedWidth * - config->inputPaddedHeight; + const uint32_t lumaSize = config->inputPaddedWidth * config->inputPaddedHeight; // both u and v - const size_t chromaSize = lumaSize >> 1; - const size_t tenBit = (config->encoderBitDepth > 8); - const size_t frameSize = (lumaSize + chromaSize) << tenBit; + const uint32_t chromaSize = lumaSize >> (3 - config->encoderColorFormat); + const uint32_t tenBit = (config->encoderBitDepth > 8); + const uint32_t frameSize = (lumaSize + 2 * chromaSize) << tenBit; // Initialize Header contextPtr->reconBuffer->nSize = sizeof(EB_BUFFERHEADERTYPE); @@ -129,17 +121,16 @@ EB_ERRORTYPE EbAppContextCtor( contextPtr->reconBuffer->nAllocLen = (uint32_t)frameSize; contextPtr->reconBuffer->pAppPrivate = NULL; - } - else + } else { contextPtr->reconBuffer = NULL; + } return EB_ErrorNone; } /*********************************** * AppContext Destructor ***********************************/ -void EbAppContextDtor( - EbAppContext_t *contextPtr) +void EbAppContextDtor(EbAppContext_t *contextPtr) { EB_H265_ENC_INPUT *inputPtr = NULL; if (contextPtr->inputPictureBuffer) { @@ -156,8 +147,9 @@ void EbAppContextDtor( free(contextPtr->outputStreamBuffer->pBuffer); free(contextPtr->inputPictureBuffer); free(contextPtr->outputStreamBuffer); - if(contextPtr->reconBuffer) + if(contextPtr->reconBuffer) { free(contextPtr->reconBuffer); + } } /*********************************************** @@ -165,7 +157,7 @@ void EbAppContextDtor( * The config structure, to the * callback structure to send to the library ***********************************************/ -EB_ERRORTYPE CopyConfigurationParameters( +static EB_ERRORTYPE CopyConfigurationParameters( EbConfig_t *config, EbAppContext_t *callbackData, uint32_t instanceIdx) @@ -179,6 +171,12 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.sourceWidth = config->sourceWidth; callbackData->ebEncParameters.sourceHeight = config->sourceHeight; callbackData->ebEncParameters.encoderBitDepth = config->encoderBitDepth; + callbackData->ebEncParameters.encoderColorFormat = config->encoderColorFormat; + if(config->encoderColorFormat >= EB_YUV422 && config->profile != 4) + { + printf("\nWarning: force profile to be MainREXT for YUV422 or YUV44 cases\n"); + callbackData->ebEncParameters.profile = 4; + } callbackData->ebEncParameters.codeVpsSpsPps = 0; callbackData->ebEncParameters.codeEosNal = 1; callbackData->ebEncParameters.reconEnabled = config->reconFile ? 1 : 0; @@ -228,9 +226,7 @@ EB_ERRORTYPE InitEncoder( /*********************************** * Deinit Components ***********************************/ -EB_ERRORTYPE DeInitEncoder( - EbAppContext_t *callbackDataPtr, - uint32_t instanceIndex) +EB_ERRORTYPE DeInitEncoder(EbAppContext_t *callbackDataPtr, uint32_t instanceIndex) { (void)instanceIndex; EB_ERRORTYPE return_error = EB_ErrorNone; @@ -240,7 +236,9 @@ EB_ERRORTYPE DeInitEncoder( } // Destruct the buffer memory pool - if (return_error != EB_ErrorNone) { return return_error; } + if (return_error != EB_ErrorNone) { + return return_error; + } // Destruct the component EbDeinitHandle(callbackDataPtr->svtEncoderHandle); diff --git a/Source/SimpleApp/EbSimpleAppContext.h b/Source/SimpleApp/EbSimpleAppContext.h index 6f5a05a54..9a4b67238 100644 --- a/Source/SimpleApp/EbSimpleAppContext.h +++ b/Source/SimpleApp/EbSimpleAppContext.h @@ -32,6 +32,7 @@ typedef struct EbConfig_s uint32_t injector; uint32_t speedControlFlag; uint32_t encoderBitDepth; + uint32_t encoderColorFormat; uint32_t compressedTenBitFormat; uint32_t sourceWidth; uint32_t sourceHeight; diff --git a/Source/SimpleApp/EbSimpleAppMain.c b/Source/SimpleApp/EbSimpleAppMain.c index 5e904cfe8..41dc6a8a6 100644 --- a/Source/SimpleApp/EbSimpleAppMain.c +++ b/Source/SimpleApp/EbSimpleAppMain.c @@ -40,17 +40,6 @@ typedef enum APPEXITCONDITIONTYPE { APP_ExitConditionError } APPEXITCONDITIONTYPE; -/**************************************** -* Padding -****************************************/ -#define LEFT_INPUT_PADDING 0 -#define RIGHT_INPUT_PADDING 0 -#define TOP_INPUT_PADDING 0 -#define BOTTOM_INPUT_PADDING 0 - - /********************************** - * Constructor - **********************************/ static void EbConfigCtor(EbConfig_t *configPtr) { configPtr->inputFile = NULL; @@ -72,7 +61,6 @@ static void EbConfigCtor(EbConfig_t *configPtr) **********************************/ static void EbConfigDtor(EbConfig_t *configPtr) { - if (configPtr->inputFile) { fclose(configPtr->inputFile); configPtr->inputFile = (FILE *)NULL; @@ -91,7 +79,7 @@ static void EbConfigDtor(EbConfig_t *configPtr) return; } -APPEXITCONDITIONTYPE ProcessOutputReconBuffer( +static APPEXITCONDITIONTYPE ProcessOutputReconBuffer( EbConfig_t *config, EbAppContext_t *appCallBack) { @@ -128,7 +116,8 @@ APPEXITCONDITIONTYPE ProcessOutputReconBuffer( } return return_value; } -APPEXITCONDITIONTYPE ProcessOutputStreamBuffer( + +static APPEXITCONDITIONTYPE ProcessOutputStreamBuffer( EbConfig_t *config, EbAppContext_t *appCallback, uint8_t picSendDone @@ -163,40 +152,38 @@ APPEXITCONDITIONTYPE ProcessOutputStreamBuffer( return return_value; } -#define SIZE_OF_ONE_FRAME_IN_BYTES(width, height,is16bit) ( ( ((width)*(height)*3)>>1 )<inputPaddedWidth; uint32_t inputPaddedHeight = config->inputPaddedHeight; FILE *inputFile = config->inputFile; uint8_t *ebInputPtr; EB_H265_ENC_INPUT* inputPtr = (EB_H265_ENC_INPUT*)headerPtr->pBuffer; + uint32_t colorFormat = (uint32_t)(config->encoderColorFormat); + uint32_t subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; inputPtr->yStride = inputPaddedWidth; - inputPtr->cbStride = inputPaddedWidth >> 1; - inputPtr->crStride = inputPaddedWidth >> 1; + inputPtr->cbStride = inputPaddedWidth >> subWidthCMinus1; + inputPtr->crStride = inputPaddedWidth >> subWidthCMinus1; { if (is16bit == 0 || (is16bit == 1 && config->compressedTenBitFormat == 0)) { - readSize = (uint64_t)SIZE_OF_ONE_FRAME_IN_BYTES(inputPaddedWidth, inputPaddedHeight, is16bit); + uint64_t lumaReadSize = (uint64_t)inputPaddedWidth*inputPaddedHeight << is16bit; + uint64_t chromaReadSize = lumaReadSize >> (3 - colorFormat); + readSize = lumaReadSize + (chromaReadSize << 1); headerPtr->nFilledLen = 0; { - uint64_t lumaReadSize = (uint64_t)inputPaddedWidth*inputPaddedHeight << is16bit; ebInputPtr = inputPtr->luma; headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize, inputFile); ebInputPtr = inputPtr->cb; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); + headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, chromaReadSize, inputFile); ebInputPtr = inputPtr->cr; - headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - inputPtr->luma = inputPtr->luma + ((config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING) << is16bit); - inputPtr->cb = inputPtr->cb + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1)) << is16bit); - inputPtr->cr = inputPtr->cr + (((config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1)) << is16bit); + headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, chromaReadSize, inputFile); if (readSize != headerPtr->nFilledLen) { config->stopEncoder = 1; @@ -219,11 +206,6 @@ void ReadInputFrames( ebInputPtr = inputPtr->cr; headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, lumaReadSize >> 2, inputFile); - inputPtr->luma = inputPtr->luma + config->inputPaddedWidth*TOP_INPUT_PADDING + LEFT_INPUT_PADDING; - inputPtr->cb = inputPtr->cb + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); - inputPtr->cr = inputPtr->cr + (config->inputPaddedWidth >> 1)*(TOP_INPUT_PADDING >> 1) + (LEFT_INPUT_PADDING >> 1); - - ebInputPtr = inputPtr->lumaExt; headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, nbitlumaReadSize, inputFile); ebInputPtr = inputPtr->cbExt; @@ -231,10 +213,6 @@ void ReadInputFrames( ebInputPtr = inputPtr->crExt; headerPtr->nFilledLen += (uint32_t)fread(ebInputPtr, 1, nbitlumaReadSize >> 2, inputFile); - inputPtr->lumaExt = inputPtr->lumaExt + ((config->inputPaddedWidth >> 2)*TOP_INPUT_PADDING + (LEFT_INPUT_PADDING >> 2)); - inputPtr->cbExt = inputPtr->cbExt + (((config->inputPaddedWidth >> 1) >> 2)*(TOP_INPUT_PADDING >> 1) + ((LEFT_INPUT_PADDING >> 1) >> 2)); - inputPtr->crExt = inputPtr->crExt + (((config->inputPaddedWidth >> 1) >> 2)*(TOP_INPUT_PADDING >> 1) + ((LEFT_INPUT_PADDING >> 1) >> 2)); - readSize = ((lumaReadSize * 3) >> 1) + ((nbitlumaReadSize * 3) >> 1); if (readSize != headerPtr->nFilledLen) { @@ -251,8 +229,9 @@ void ReadInputFrames( return; } + #define TEST_IDR 0 -APPEXITCONDITIONTYPE ProcessInputBuffer( +static APPEXITCONDITIONTYPE ProcessInputBuffer( EbConfig_t *config, EbAppContext_t *appCallBack) { @@ -299,6 +278,14 @@ APPEXITCONDITIONTYPE ProcessInputBuffer( return return_value; } +static char* checkFileInput(char* input) +{ + if (!input) { + return NULL; + } + return input; +} + /*************************************** * Encoder App Main ***************************************/ @@ -334,13 +321,17 @@ int32_t main(int32_t argc, char* argv[]) return_error = EB_ErrorInsufficientResources; } else { EbConfigCtor(config); - if (argc != 6 && argc != 7) { - printf("Usage: ./HevcEncoderSimpleApp in.yuv out.265 width height bitdepth recon.yuv(optional)\n"); + if (argc != 7 && argc != 8) { + printf("Usage: %s in.yuv out.265 width height bitdepth colorFormat recon.yuv(optional)\n", argv[0]); return_error = EB_ErrorBadParameter; } else if (return_error == EB_ErrorNone) { // Get info for config FILE *fin = NULL; - FOPEN(fin, argv[1], "rb"); + char* input_file = argv[1]; + input_file = checkFileInput(input_file); + if (input_file) { + FOPEN(fin, input_file, "rb"); + } if (!fin) { printf("Invalid input file \n"); return_error = EB_ErrorBadParameter; @@ -349,9 +340,13 @@ int32_t main(int32_t argc, char* argv[]) } FILE *fout = NULL; - FOPEN(fout, argv[2], "wb"); + char* output_file = argv[2]; + output_file = checkFileInput(output_file); + if (output_file) { + FOPEN(fout, output_file, "wb"); + } if (!fout) { - printf("Invalid input file \n"); + printf("Invalid output file \n"); return_error = EB_ErrorBadParameter; } else { config->bitstreamFile = fout; @@ -361,7 +356,7 @@ int32_t main(int32_t argc, char* argv[]) width = strtoul(argv[3], NULL, 0); height = strtoul(argv[4], NULL, 0); - if ((width&&height) == 0) { + if (width <= 0 || width > 8192 || height <= 0 || height > 4320) { printf("Invalid video dimensions\n"); return_error = EB_ErrorBadParameter; } @@ -376,9 +371,19 @@ int32_t main(int32_t argc, char* argv[]) } config->encoderBitDepth = bdepth; - if (argc == 7) { - FILE * frec; - FOPEN(frec, argv[6], "wb"); + uint32_t chromaIdx = strtoul(argv[6], NULL, 0); + if (chromaIdx < EB_YUV420 || chromaIdx > EB_YUV444) { + printf("Invalid chromaIdx value %d. 1: 420, 2:422, 3:444(not supported)\n", chromaIdx); + return_error = EB_ErrorBadParameter; + } + config->encoderColorFormat = chromaIdx; + if (argc == 8) { + FILE * frec = NULL; + char* recon_file = argv[7]; + recon_file = checkFileInput(recon_file); + if (recon_file) { + FOPEN(frec, recon_file, "wb"); + } if (!frec) { printf("Invalid recon file \n"); return_error = EB_ErrorBadParameter; @@ -427,7 +432,6 @@ int32_t main(int32_t argc, char* argv[]) // DeInit Encoder return_error = DeInitEncoder(appCallback, 0); - } else { printf("Error in configuration, could not begin encoding! ... \n"); diff --git a/Tests/README.txt b/Tests/README.txt new file mode 100644 index 000000000..d3a6b9d4c --- /dev/null +++ b/Tests/README.txt @@ -0,0 +1,14 @@ +HOW TO USE: +1. Build SVT encoder and place the executable in the folder specified under "ENC_PATH" +2. Download the reference decoder from https://hevc.hhi.fraunhofer.de/ +3. Build the decoder and place it in the folder specified under "TOOLS_PATH" +4. Run script + +Test input options: +- python SVT-HEVC_FunctionalTests.py Fast +- python SVT-HEVC_FunctionalTests.py Nightly +- python SVT-HEVC_FunctionalTests.py Full +* Fast test takes around 2 hours +* Nightly test takes around 18 hours +* Full test takes around 2 days +* Estimated time is based on tests ran on Intel Xeon Gold 6140 CPU w/ 94.7 GB memory \ No newline at end of file diff --git a/Tests/SVT_FunctionalTests.py b/Tests/SVT-HEVC_FunctionalTests.py similarity index 87% rename from Tests/SVT_FunctionalTests.py rename to Tests/SVT-HEVC_FunctionalTests.py index 90958ea9b..ff1eb6160 100644 --- a/Tests/SVT_FunctionalTests.py +++ b/Tests/SVT-HEVC_FunctionalTests.py @@ -12,6 +12,7 @@ import filecmp import time import glob +import sys LINUX_PLATFORM_STR = "Linux" WINDOWS_PLATFORM_STR = "Windows" @@ -23,26 +24,47 @@ subprocess_flags = 0x8000000 slash = '\\' exe_name = 'SvtHevcEncApp.exe' + dec_exe = 'TAppDecoder.exe' else: slash = '/' exe_name = 'SvtHevcEncApp' + dec_exe = 'TAppDecoder' +def get_test_mode(mode): + if len(mode) <= 1: + print("Running default mode: Fast") + return 0 + test_mode = mode[1] + if test_mode == "fast" or test_mode == "Fast": + print("Running mode: Fast") + return 0 + elif test_mode == "nightly" or test_mode == "Nightly": + print("Running mode: Nightly") + return 1 + elif test_mode == "full" or test_mode == "Full": + print("Running mode: Full") + return 2 + else: + print("Mode not recognized") + print("Running default mode: Fast") + return 0 + DEBUG_MODE = 0 # For debugging purposes ##--------------------- TEST SETTINGS --------------------## ENC_PATH = "encoders" BIN_PATH = "bitstreams" YUV_PATH = "yuvs" +TOOLS_PATH = "tools" TEST_CONFIGURATION = 0 # 0 - Validation Test, 1 - Speed Test (Refer to Validation/Speed Test specific configurations) -SQ_OQ_MODE = 0 # 0 - Both OQ and SQ, 1 - SQ Only, 2 - OQ Only +SQ_OQ_MODE = 0 # 0 - SQ, OQ and VMAF, 1 - SQ Only, 2 - OQ Only #------------- Validation Test Specific -------------# -VALIDATION_TEST_MODE = 0 # 0 - Fast Test, 1 - Overnight Test, 2- Full Test +VALIDATION_TEST_MODE = get_test_mode(sys.argv) # 0 - Fast Test, 1 - Overnight Test, 2- Full Test QP_VBR_MODE = 0 # 0 - Both QP and VBR, 1 - QP Only, 2 - VBR Only VALIDATION_TEST_SEQUENCES = [ -'Netflix_FoodMarket2_4096x2160_10bit_60Hz_P420', 'Netflix_Crosswalk_3840x2160_10bit_60Hz_P420', 'Fallout4_1920x1080_8bit_60Hz_P420', 'DucksTakeOff_1280x720_8bit_50Hz_P420', @@ -55,32 +77,57 @@ # Speed Test Sequences, use 'qp' and 'tbr' to specify to run in Fixed QP or VBR Mode SPEED_TEST_SEQUENCES = [ -{'name': 'Netflix_FoodMarket2_4096x2160_10bit_60Hz_P420_2bitspacked', 'tbr': 10000}, -{'name': 'Netflix_Crosswalk_3840x2160_10bit_60Hz_P420_2bitspacked', 'tbr': 10000}, -{'name': 'Netflix_FoodMarket2_4096x2160_10bit_60Hz_P420_2bitspacked', 'qp': 34}, -{'name': 'Netflix_Crosswalk_3840x2160_10bit_60Hz_P420_2bitspacked', 'qp': 34}, -{'name': 'Fallout4_1920x1080_8bit_60Hz_P420', 'qp': 34}, -{'name': 'DucksTakeOff_1280x720_8bit_50Hz_P420', 'qp': 34}, -{'name': 'ParkJoy_864x480_10bit_50Hz_P420_2bitspacked', 'qp': 34} +{'name': 'Netflix_Boat_4096x2160_10bit_60Hz_P420', 'tbr': 13000}, +{'name': 'Netflix_FoodMarket2_4096x2160_10bit_60Hz_P420_2bitspack', 'tbr': 13000}, +{'name': 'Netflix_Narrator_4096x2160_10bit_60Hz_P420', 'tbr': 13000}, +{'name': 'Netflix_RitualDance_4096x2160_10bit_60Hz_P420', 'tbr': 13000}, +{'name': 'Netflix_Tango_4096x2160_10bit_60Hz_P420_2bitspack', 'tbr': 13000}, +{'name': 'EuroTruckSimulator2_003_1920x1080_8bit_60Hz_P420', 'tbr': 7500}, +{'name': 'Fallout4_003_1920x1080_8bit_60Hz_P420', 'tbr': 7500}, +{'name': 'GTAV_005_1920x1080_8bit_60Hz_P420', 'tbr': 7500}, +{'name': 'RUST_004_1920x1080_8bit_60Hz_P420', 'tbr': 7500}, +{'name': 'WITCHER3_007_1920x1080_8bit_60Hz_P420', 'tbr': 7500}, +{'name': 'EuroTruckSimulator2_003_1280x720_8bit_60Hz_P420', 'tbr': 4500}, +{'name': 'Fallout4_003_1280x720_8bit_60Hz_P420', 'tbr': 4500}, +{'name': 'GTAV_005_1280x720_8bit_60Hz_P420', 'tbr': 4500}, +{'name': 'RUST_004_1280x720_8bit_60Hz_P420', 'tbr': 4500}, +{'name': 'WITCHER3_007_1280x720_8bit_60Hz_P420', 'tbr': 4500}, +{'name': 'ParkJoy_864x480_10bit_50Hz_P420', 'tbr': 2500}, +{'name': 'DucksTakeOff_864x480_8bit_50Hz_P420', 'tbr': 2500}, +{'name': 'CrowdRun_864x480_8bit_50Hz_P420', 'tbr': 2500}, +{'name': 'InToTree_864x480_8bit_50Hz_P420', 'tbr': 2500}, +{'name': 'OldTownCross_864x480_8bit_50Hz_P420', 'tbr': 2500}, ] ##--------------------------------------------------------## ##-------------- TEST MODE SPECIFIC SETTINGS -------------## if VALIDATION_TEST_MODE == 0: - ENC_MODES = [0,3,6,9,11] + ENC_MODES = [1,4,8,12] NUM_FRAMES = 20 QP_ITERATIONS = 1 VBR_ITERATIONS = 1 + SA_ITER = 1 # Search Area WidthxHeight per test + LAD_ITER = 1 # LAD per test + INTRA_PERIOD_ITER = 1 # Intra Period per test + WH_ITER = 1 # WidthxHeight per test elif VALIDATION_TEST_MODE == 1: ENC_MODES = [0,1,2,3,4,5,6,7,8,9,10,11,12] - NUM_FRAMES = 80 + NUM_FRAMES = 40 QP_ITERATIONS = 1 VBR_ITERATIONS = 1 + SA_ITER = 1 # Search Area WidthxHeight per test + LAD_ITER = 1 # LAD per test + INTRA_PERIOD_ITER = 1 # Intra Period per test + WH_ITER = 1 # WidthxHeight per test elif VALIDATION_TEST_MODE == 2: ENC_MODES = [0,1,2,3,4,5,6,7,8,9,10,11,12] - NUM_FRAMES = 100 - QP_ITERATIONS = 5 - VBR_ITERATIONS = 5 + NUM_FRAMES = 40 + QP_ITERATIONS = 2 + VBR_ITERATIONS = 2 + SA_ITER = 2 # Search Area WidthxHeight per test + LAD_ITER = 2 # LAD per test + INTRA_PERIOD_ITER = 2 # Intra Period per test + WH_ITER = 2 # WidthxHeight per test ##--------------------------------------------------------## ##-------------------- Global Defines --------------------## @@ -92,10 +139,6 @@ MAX_QP = 51 MIN_BR = 1000 MAX_BR = 10000000 -SA_ITER = 3 # Search Area WidthxHeight per test -LAD_ITER = 3 # LAD per test -INTRA_PERIOD_ITER = 3 # Intra Period per test -WH_ITER = 3 # WidthxHeight per test ##--------------------------------------------------------## if QP_VBR_MODE == 0: @@ -119,11 +162,13 @@ class EB_Test(object): def __init__(self, encoder_path, bitstream_path, - yuv_path): + yuv_path, + tools_path): self.yuv_path = yuv_path self.encoder_path = encoder_path self.bitstream_path = bitstream_path + self.tools_path = tools_path if not os.path.exists(bitstream_path): os.mkdir(bitstream_path) @@ -150,6 +195,7 @@ def get_default_params(self): default_enc = { 'encoder_dir' : self.encoder_path, 'bitstream_dir' : self.bitstream_path, 'yuv_dir' : self.yuv_path, + 'tools_dir' : self.tools_path, 'encoder_bit_depth' : encoder_bit_depth, 'width' : width, 'height' : height, @@ -415,6 +461,8 @@ def check_seq_support(self, test_name, seq, enc_params): base_sw_mode = enc_params['BaseLayerSwitchMode'] if base_sw_mode == 1 and pred_struct != 2: return -1 + if width*height >= 1920*1080: + return -1 if test_name == 'qp_file_test': if 'rc' in enc_params: rc = enc_params['rc'] @@ -507,6 +555,10 @@ def run_test(self, test_name, test_params, enc_params, OQ, VBR, COMPARE): print(enc_cmd, file=open(test_name + '.txt', 'a')) if DEBUG_MODE == 0: exit_code = subprocess.call(enc_cmd, shell = True) + if exit_code == 0 and test_name == 'decode_test': + dec_cmd = enc_params['tools_dir'] + slash + dec_exe + " -b " + enc_params['bitstream_dir'] + slash + bitstream_name + '.265 > NUL' + print(dec_cmd, file=open(test_name + '.txt', 'a')) + exit_code = subprocess.call(dec_cmd, shell = True) else: continue if COMPARE == 0: @@ -600,8 +652,8 @@ def run_to_run_test(self, seq_list): total_passed = 0 for seq in seq_list: test_params = [ - [seq, {'frame_to_be_encoded': 300}], - [seq, {'frame_to_be_encoded': 300}] + [seq, {'frame_to_be_encoded': 150}], + [seq, {'frame_to_be_encoded': 150}] ] # Run test for OQ in SQ_OQ_COMBINATION: @@ -695,7 +747,7 @@ def intra_period_test(self,seq_list): intra.append(random.randint(intra_min + int(bin*count), intra_min + int(bin*(count+1)))) combination_test_params = { 'intra_period' : intra, 'IntraRefreshType' : [1, 2], - 'frame_to_be_encoded': [300] + 'frame_to_be_encoded': [260] } # Run tests return self.run_functional_tests(seq_list, test_name, combination_test_params) @@ -755,7 +807,7 @@ def scene_change_test(self,seq_list): lad.append(random.randint(lad_min + int(bin*count), lad_min + int(bin*(count+1)))) combination_test_params = { 'SceneChangeDetection' : [0, 1], 'LookAheadDistance' : lad, - 'frame_to_be_encoded' : [300] + 'frame_to_be_encoded' : [260] } # Run tests return self.run_functional_tests(seq_list, test_name, combination_test_params) @@ -778,7 +830,34 @@ def me_hme_test(self,seq_list): } # Run tests return self.run_functional_tests(seq_list, test_name, combination_test_params) +## ------------------------------------------- ## +## --------------- DECODE TEST --------------- ## +# The decode test ensure encoded streams are conformant to standard decoders + def decode_test(self,seq_list): + # Test specific parameters: + test_name = 'decode_test' + # Get default encoding params + enc_params = self.get_default_params().copy() + if os.path.exists(test_name + '.txt'): + return 0, 0 + print ("Running Test: " + test_name) + print ("---------------------------------------", file=open(test_name + '.txt', 'w')) + print ("Test Name: " + test_name, file=open(test_name + '.txt', 'a')) + total_test = 0 + total_passed = 0 + for seq in seq_list: + for VBR in QP_VBR_COMBINATION: + for OQ in SQ_OQ_COMBINATION: + test_params = [ + [seq, {'frame_to_be_encoded': 20}], + ] + num_tests, num_passed = self.run_test(test_name, test_params, enc_params, OQ, VBR, 0) + total_test = total_test + num_tests + total_passed = total_passed + num_passed + return total_test, total_passed + + ## ------------------------------------------- ## def get_time(self, total_seconds): @@ -807,6 +886,10 @@ def error_check(self, seq_list): print ("Cannot find encoder executable. Please make sure " + exe_name + " can be found in the folder \"" + self.encoder_path + "\"") exit_code = -1 return exit_code + if not os.path.exists(self.tools_path + slash + dec_exe): + print ("Cannot find decoder executable. Please make sure " + dec_exe + " can be found in the folder \"" + self.tools_path + "\"") + exit_code = -1 + return exit_code for seq in seq_list: if not os.path.exists(self.yuv_path + slash + seq + '.yuv'): print ("Cannot find " + seq + ".yuv in yuv_path") @@ -873,6 +956,9 @@ def run_validation_test(self, seq_list): num_tests, num_passed = self.me_hme_test(seq_list) total_tests = total_tests + num_tests total_passed = total_passed + num_passed + num_tests, num_passed = self.decode_test(seq_list) + total_tests = total_tests + num_tests + total_passed = total_passed + num_passed finish_time = time.time() if total_tests == 0 and total_passed == 0: print ("No tests were ran.. Exiting...", file=open(file_name + '.txt', 'a')) @@ -889,7 +975,7 @@ def show_speed_test_instructions(self): print ("1. Run shell script (Run in \"sudo\" mode in Linux)", file=open('Running_Speed_Test.txt', 'a')) print ("Note: Running it from a shell script minimizes use of CPU cycles from Python", file=open('Running_Speed_Test.txt', 'a')) - # Set number of frames for speed test + # Set number of channels for speed test def get_num_channels(self, enc_mode, enc_params): if enc_params['width']*enc_params['height'] > 1920*1080: if enc_mode >= 0 and enc_mode <= 3: @@ -915,6 +1001,16 @@ def get_num_channels(self, enc_mode, enc_params): else: num_channels = 6 return num_channels + + # Set number of frames for speed test + def get_num_frames(self, enc_mode): + if enc_mode >= 0 and enc_mode <= 1: + num_frames = 500 + elif enc_mode >= 2 and enc_mode <= 4: + num_frames = 2000 + elif enc_mode >= 5: + num_frames = 4000 + return num_frames def run_speed_test(self, seq_dict): seq_list = [] @@ -951,15 +1047,18 @@ def run_speed_test(self, seq_dict): continue for enc_mode in SPEED_ENC_MODES: num_channels = self.get_num_channels(enc_mode, enc_params) + num_frames = self.get_num_frames(enc_mode) if OQ == 0: quality_mode = '_SQ' - else: + elif OQ == 1: quality_mode = '_OQ' + else: + quality_mode = '_VMAF' if VBR == 0: - enc_params.update({'enc_mode': enc_mode, 'qp': iter_list, 'rc': 0, 'tune': OQ}) + enc_params.update({'enc_mode': enc_mode, 'qp': iter_list, 'rc': 0, 'tune': OQ, 'frame_to_be_encoded': num_frames}) cmd = self.get_enc_cmd(enc_params, seq['name'], 'Speed_Test_M' + str(enc_mode) + '_' + seq['name'] + quality_mode + '_Q' + str(iter_list), num_channels) else: - enc_params.update({'enc_mode': enc_mode, 'tbr': iter_list, 'rc': 1, 'tune': OQ}) + enc_params.update({'enc_mode': enc_mode, 'tbr': iter_list, 'rc': 1, 'tune': OQ, 'frame_to_be_encoded': num_frames}) cmd = self.get_enc_cmd(enc_params, seq['name'], 'Speed_Test_M' + str(enc_mode) + '_' + seq['name'] + quality_mode + '_TBR' + str(iter_list), num_channels) print (cmd) if platform == WINDOWS_PLATFORM_STR: @@ -968,7 +1067,7 @@ def run_speed_test(self, seq_dict): print(cmd, file=open('speed_script.sh', 'a')) ##----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------## -small_test = EB_Test(ENC_PATH, BIN_PATH, YUV_PATH) +small_test = EB_Test(ENC_PATH, BIN_PATH, YUV_PATH, TOOLS_PATH) if TEST_CONFIGURATION == 0: small_test.run_validation_test(VALIDATION_TEST_SEQUENCES) elif TEST_CONFIGURATION == 1: diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..244b34282 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,24 @@ +image: Visual Studio 2017 +configuration: Release + +install: + - git submodule update --init + - appveyor DownloadFile http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win64.exe -FileName yasm.exe + - set PATH=%PATH%;%APPVEYOR_BUILD_FOLDER% + - cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_ASM_NASM_COMPILER="yasm.exe" + +build: + project: svt-hevc.sln + +artifacts: + - path: bin\Release\*.* + name: $(APPVEYOR_PROJECT_NAME) + +deploy: + - provider: GitHub + artifact: $(APPVEYOR_PROJECT_NAME) + auth_token: + secure: 'hY5Mk6KOwgQ97TzEBsM7Woqr1ZIm5QTvHg8EvxMV1x8j3wk/3mNBMqWFFbEIBK0i' + prerelease: true + on: + appveyor_repo_tag: true diff --git a/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch b/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch index a70dbb258..20b14db9d 100644 --- a/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch +++ b/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch @@ -1,10 +1,18 @@ -From ed9da7f1f574f9282b80b5ada89e0b434efc91ea Mon Sep 17 00:00:00 2001 -From: Jun Zhao +From ddca0355175f9cae73ee29548d78de489da7f3f3 Mon Sep 17 00:00:00 2001 +From: Jing Sun Date: Wed, 21 Nov 2018 11:33:04 +0800 Subject: [PATCH 1/2] lavc/svt_hevc: add libsvt hevc encoder wrapper. base on patch by Huang, Zhengxu from https://github.com/intel/SVT-HEVC +V4: - Fix the build error with new API in PR#52 + - Fix the encoding hang issue by API change in PR#52 + - Fix the last frame dropping issue + - Fix the invalid parameter causing segmentation fault issue + - Add the support to svt hevc and av1 plugins coexistance + - Add the VMAF optimized mode to "-tune" + - Add the "-hdr" parameter + V3: - Fix the build error with new API V2: - Change the config options (didn't need to enable-gpl for BSD+Patent, @@ -18,22 +26,24 @@ V2: - Change the config options (didn't need to enable-gpl for BSD+Patent, V1: - base on patch by Huang, Zhengxu, then refine some code. +Change-Id: If0dcc5044ab9effd6847a8f48797b985d02b0816 Signed-off-by: Huang, Zhengxu Signed-off-by: hassene Signed-off-by: Jun Zhao +Signed-off-by: Jing Sun --- configure | 4 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + - libavcodec/libsvt_hevc.c | 527 +++++++++++++++++++++++++++++++++++++++ - 4 files changed, 533 insertions(+) + libavcodec/libsvt_hevc.c | 546 +++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 552 insertions(+) create mode 100644 libavcodec/libsvt_hevc.c diff --git a/configure b/configure -index 2205751402..5e14df98b3 100755 +index a70c5f9..06d5e8e 100755 --- a/configure +++ b/configure -@@ -263,6 +263,7 @@ External library support: +@@ -262,6 +262,7 @@ External library support: --enable-libspeex enable Speex de/encoding via libspeex [no] --enable-libsrt enable Haivision SRT protocol via libsrt [no] --enable-libssh enable SFTP protocol via libssh [no] @@ -41,7 +51,7 @@ index 2205751402..5e14df98b3 100755 --enable-libtensorflow enable TensorFlow as a DNN module backend for DNN based filters like sr [no] --enable-libtesseract enable Tesseract, needed for ocr filter [no] -@@ -1745,6 +1746,7 @@ EXTERNAL_LIBRARY_LIST=" +@@ -1743,6 +1744,7 @@ EXTERNAL_LIBRARY_LIST=" libspeex libsrt libssh @@ -49,7 +59,7 @@ index 2205751402..5e14df98b3 100755 libtensorflow libtesseract libtheora -@@ -3131,6 +3133,7 @@ libshine_encoder_select="audio_frame_queue" +@@ -3125,6 +3127,7 @@ libshine_encoder_select="audio_frame_queue" libspeex_decoder_deps="libspeex" libspeex_encoder_deps="libspeex" libspeex_encoder_select="audio_frame_queue" @@ -57,19 +67,19 @@ index 2205751402..5e14df98b3 100755 libtheora_encoder_deps="libtheora" libtwolame_encoder_deps="libtwolame" libvo_amrwbenc_encoder_deps="libvo_amrwbenc" -@@ -6154,6 +6157,7 @@ enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr +@@ -6135,6 +6138,7 @@ enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr enabled libssh && require_pkg_config libssh libssh libssh/sftp.h sftp_init enabled libspeex && require_pkg_config libspeex speex speex/speex.h speex_decoder_init enabled libsrt && require_pkg_config libsrt "srt >= 1.3.0" srt/srt.h srt_socket -+enabled libsvthevc && require_pkg_config libsvthevc SvtHevcEnc EbApi.h EbInitHandle ++enabled libsvthevc && require_pkg_config libsvthevc SvtHevcEnc svt-hevc/EbApi.h EbInitHandle enabled libtensorflow && require libtensorflow tensorflow/c/c_api.h TF_Version -ltensorflow enabled libtesseract && require_pkg_config libtesseract tesseract tesseract/capi.h TessBaseAPICreate enabled libtheora && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg diff --git a/libavcodec/Makefile b/libavcodec/Makefile -index d53b8ff330..bd46866075 100644 +index 3e41497..728503b 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile -@@ -983,6 +983,7 @@ OBJS-$(CONFIG_LIBOPUS_ENCODER) += libopusenc.o libopus.o \ +@@ -981,6 +981,7 @@ OBJS-$(CONFIG_LIBOPUS_ENCODER) += libopusenc.o libopus.o \ OBJS-$(CONFIG_LIBSHINE_ENCODER) += libshine.o OBJS-$(CONFIG_LIBSPEEX_DECODER) += libspeexdec.o OBJS-$(CONFIG_LIBSPEEX_ENCODER) += libspeexenc.o @@ -78,10 +88,10 @@ index d53b8ff330..bd46866075 100644 OBJS-$(CONFIG_LIBTWOLAME_ENCODER) += libtwolame.o OBJS-$(CONFIG_LIBVO_AMRWBENC_ENCODER) += libvo-amrwbenc.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c -index d70646e91a..094ee3cd7f 100644 +index 1b8144a..5739d53 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c -@@ -699,6 +699,7 @@ extern AVCodec ff_librsvg_decoder; +@@ -697,6 +697,7 @@ extern AVCodec ff_librsvg_decoder; extern AVCodec ff_libshine_encoder; extern AVCodec ff_libspeex_encoder; extern AVCodec ff_libspeex_decoder; @@ -91,10 +101,10 @@ index d70646e91a..094ee3cd7f 100644 extern AVCodec ff_libvo_amrwbenc_encoder; diff --git a/libavcodec/libsvt_hevc.c b/libavcodec/libsvt_hevc.c new file mode 100644 -index 0000000000..aa51830788 +index 0000000..533d308 --- /dev/null +++ b/libavcodec/libsvt_hevc.c -@@ -0,0 +1,527 @@ +@@ -0,0 +1,546 @@ +/* +* Scalable Video Technology for HEVC encoder library plugin +* @@ -117,9 +127,9 @@ index 0000000000..aa51830788 +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + -+#include "EbErrorCodes.h" -+#include "EbTime.h" -+#include "EbApi.h" ++#include "svt-hevc/EbErrorCodes.h" ++#include "svt-hevc/EbTime.h" ++#include "svt-hevc/EbApi.h" + +#include "libavutil/common.h" +#include "libavutil/frame.h" @@ -128,6 +138,12 @@ index 0000000000..aa51830788 +#include "internal.h" +#include "avcodec.h" + ++typedef enum eos_status { ++ EOS_NOT_REACHED = 0, ++ EOS_REACHED, ++ EOS_TOTRIGGER ++}EOS_STATUS; ++ +typedef struct SvtContext { + AVClass *class; + @@ -135,10 +151,9 @@ index 0000000000..aa51830788 + EB_COMPONENTTYPE *svt_handle; + + EB_BUFFERHEADERTYPE *in_buf; -+ EB_BUFFERHEADERTYPE *out_buf; + int raw_size; + -+ int eos_flag; ++ EOS_STATUS eos_flag; + + // User options. + int vui_info; @@ -149,6 +164,7 @@ index 0000000000..aa51830788 + int scd; + int tune; + int qp; ++ int hdr; + + int forced_idr; + @@ -206,7 +222,6 @@ index 0000000000..aa51830788 + av_freep(&in_data); + av_freep(&svt_enc->in_buf); + } -+ av_freep(&svt_enc->out_buf); +} + +static int alloc_buffer(EB_H265_ENC_CONFIGURATION *config, SvtContext *svt_enc) @@ -224,8 +239,7 @@ index 0000000000..aa51830788 + + // allocate buffer for in and out + svt_enc->in_buf = av_mallocz(sizeof(*svt_enc->in_buf)); -+ svt_enc->out_buf = av_mallocz(sizeof(*svt_enc->out_buf)); -+ if (!svt_enc->in_buf || !svt_enc->out_buf) ++ if (!svt_enc->in_buf) + goto failed; + + in_data = av_mallocz(sizeof(*in_data)); @@ -235,9 +249,6 @@ index 0000000000..aa51830788 + + svt_enc->in_buf->nSize = sizeof(*svt_enc->in_buf); + svt_enc->in_buf->pAppPrivate = NULL; -+ svt_enc->out_buf->nSize = sizeof(*svt_enc->out_buf); -+ svt_enc->out_buf->nAllocLen = svt_enc->raw_size; -+ svt_enc->out_buf->pAppPrivate = NULL; + + return 0; + @@ -308,6 +319,13 @@ index 0000000000..aa51830788 + else + param->codeVpsSpsPps = 1; + ++ param->codeEosNal = 1; ++ ++ if (svt_enc->hdr) { ++ svt_enc->vui_info = 1; ++ param->highDynamicRangeInput = svt_enc->hdr; ++ } ++ + if (svt_enc->vui_info) + param->videoUsabilityInfo = svt_enc->vui_info; + @@ -350,7 +368,7 @@ index 0000000000..aa51830788 + SvtContext *svt_enc = avctx->priv_data; + EB_ERRORTYPE svt_ret; + -+ svt_enc->eos_flag = 0; ++ svt_enc->eos_flag = EOS_NOT_REACHED; + + svt_ret = EbInitHandle(&svt_enc->svt_handle, svt_enc, &svt_enc->enc_params); + if (svt_ret != EB_ErrorNone) { @@ -377,38 +395,23 @@ index 0000000000..aa51830788 + } + + if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { -+ EB_BUFFERHEADERTYPE headerPtr; -+ headerPtr.nSize = sizeof(headerPtr); -+ headerPtr.nFilledLen = 0; /* in/out */ -+ headerPtr.pBuffer = av_malloc(10 * 1024 * 1024); -+ headerPtr.nAllocLen = (10 * 1024 * 1024); -+ -+ if (!headerPtr.pBuffer) { -+ av_log(avctx, AV_LOG_ERROR, -+ "Cannot allocate buffer size %d.\n", headerPtr.nAllocLen); -+ svt_ret = EB_ErrorInsufficientResources; -+ goto failed_init_enc; -+ } ++ EB_BUFFERHEADERTYPE *headerPtr = NULL; + + svt_ret = EbH265EncStreamHeader(svt_enc->svt_handle, &headerPtr); + if (svt_ret != EB_ErrorNone) { + av_log(avctx, AV_LOG_ERROR, "Error when build stream header.\n"); -+ av_freep(&headerPtr.pBuffer); + goto failed_init_enc; + } + -+ avctx->extradata_size = headerPtr.nFilledLen; ++ avctx->extradata_size = headerPtr->nFilledLen; + avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) { + av_log(avctx, AV_LOG_ERROR, + "Cannot allocate HEVC header of size %d.\n", avctx->extradata_size); -+ av_freep(&headerPtr.pBuffer); + svt_ret = EB_ErrorInsufficientResources; + goto failed_init_enc; + } -+ memcpy(avctx->extradata, headerPtr.pBuffer, avctx->extradata_size); -+ -+ av_freep(&headerPtr.pBuffer); ++ memcpy(avctx->extradata, headerPtr->pBuffer, avctx->extradata_size); + } + + return 0; @@ -419,6 +422,8 @@ index 0000000000..aa51830788 + EbDeinitHandle(svt_enc->svt_handle); +failed: + free_buffer(svt_enc); ++ svt_enc->svt_handle = NULL; ++ svt_enc = NULL; + return error_mapping(svt_ret); +} + @@ -437,7 +442,7 @@ index 0000000000..aa51830788 + headerPtrLast.nFlags = EB_BUFFERFLAG_EOS; + + EbH265EncSendPicture(svt_enc->svt_handle, &headerPtrLast); -+ svt_enc->eos_flag = 1; ++ svt_enc->eos_flag = EOS_REACHED; + av_log(avctx, AV_LOG_DEBUG, "Finish sending frames!!!\n"); + return 0; + } @@ -449,16 +454,16 @@ index 0000000000..aa51830788 + headerPtr->pts = frame->pts; + switch (frame->pict_type) { + case AV_PICTURE_TYPE_I: -+ headerPtr->sliceType = svt_enc->forced_idr > 0 ? EB_IDR_SLICE : EB_I_SLICE; ++ headerPtr->sliceType = svt_enc->forced_idr > 0 ? EB_IDR_PICTURE : EB_I_PICTURE; + break; + case AV_PICTURE_TYPE_P: -+ headerPtr->sliceType = EB_P_SLICE; ++ headerPtr->sliceType = EB_P_PICTURE; + break; + case AV_PICTURE_TYPE_B: -+ headerPtr->sliceType = EB_B_SLICE; ++ headerPtr->sliceType = EB_B_PICTURE; + break; + default: -+ headerPtr->sliceType = EB_INVALID_SLICE; ++ headerPtr->sliceType = EB_INVALID_PICTURE; + break; + } + EbH265EncSendPicture(svt_enc->svt_handle, headerPtr); @@ -469,39 +474,57 @@ index 0000000000..aa51830788 +static int eb_receive_packet(AVCodecContext *avctx, AVPacket *pkt) +{ + SvtContext *svt_enc = avctx->priv_data; -+ EB_BUFFERHEADERTYPE *headerPtr = svt_enc->out_buf; ++ EB_BUFFERHEADERTYPE *headerPtr = NULL; + EB_ERRORTYPE svt_ret; -+ int ret; ++ int ret = 0; + -+ if ((ret = ff_alloc_packet2(avctx, pkt, svt_enc->raw_size, 0)) < 0) { -+ av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n"); -+ return ret; ++ if (EOS_TOTRIGGER == svt_enc->eos_flag) { ++ pkt = NULL; ++ return AVERROR_EOF; + } -+ headerPtr->pBuffer = pkt->data; -+ svt_ret = EbH265GetPacket(svt_enc->svt_handle, headerPtr, svt_enc->eos_flag); ++ ++ svt_ret = EbH265GetPacket(svt_enc->svt_handle, &headerPtr, svt_enc->eos_flag); + if (svt_ret == EB_NoErrorEmptyQueue) + return AVERROR(EAGAIN); + ++ if ((ret = ff_alloc_packet2(avctx, pkt, headerPtr->nFilledLen, 0)) < 0) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n"); ++ EbH265ReleaseOutBuffer(&headerPtr); ++ return ret; ++ } ++ ++ memcpy(pkt->data, headerPtr->pBuffer, headerPtr->nFilledLen); ++ + pkt->size = headerPtr->nFilledLen; + pkt->pts = headerPtr->pts; + pkt->dts = headerPtr->dts; -+ if (headerPtr->sliceType == EB_IDR_SLICE) ++ if (headerPtr->sliceType == EB_IDR_PICTURE) + pkt->flags |= AV_PKT_FLAG_KEY; -+ if (headerPtr->sliceType == EB_NON_REF_SLICE) ++ if (headerPtr->sliceType == EB_NON_REF_PICTURE) + pkt->flags |= AV_PKT_FLAG_DISPOSABLE; + -+ ret = (headerPtr->nFlags & EB_BUFFERFLAG_EOS) ? AVERROR_EOF : 0; -+ return ret; ++ EbH265ReleaseOutBuffer(&headerPtr); ++ ++ if (EB_BUFFERFLAG_EOS == headerPtr->nFlags) ++ svt_enc->eos_flag = EOS_TOTRIGGER; ++ ++ return 0; +} + +static av_cold int eb_enc_close(AVCodecContext *avctx) +{ + SvtContext *svt_enc = avctx->priv_data; + -+ EbDeinitEncoder(svt_enc->svt_handle); -+ EbDeinitHandle(svt_enc->svt_handle); ++ if (svt_enc->svt_handle) { ++ EbDeinitEncoder(svt_enc->svt_handle); ++ EbDeinitHandle(svt_enc->svt_handle); ++ svt_enc->svt_handle = NULL; ++ } + -+ free_buffer(svt_enc); ++ if (svt_enc) { ++ free_buffer(svt_enc); ++ svt_enc = NULL; ++ } + + return 0; +} @@ -571,13 +594,15 @@ index 0000000000..aa51830788 + AV_OPT_TYPE_INT, { .i64 = 32 }, 0, 51, VE }, + + { "sc_detection", "Scene change detection", OFFSET(scd), -+ AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, ++ AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, + -+ { "tune", "Quality tuning mode", OFFSET(tune), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE, "tune" }, -+ { "subjective", "Subjective quality mode", 0, ++ { "tune", "Quality tuning mode", OFFSET(tune), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 2, VE, "tune" }, ++ { "sq", "Visually optimized mode", 0, + AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "tune" }, -+ { "objective", "Objective quality mode for PSNR / SSIM / VMAF benchmarking", 0, ++ { "oq", "PSNR / SSIM optimized mode", 0, + AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "tune" }, ++ { "vmaf", "VMAF optimized mode", 0, ++ AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, "tune" }, + + { "bl_mode", "Random Access Prediction Structure type setting", OFFSET(base_layer_switch_mode), + AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, @@ -585,6 +610,9 @@ index 0000000000..aa51830788 + { "forced-idr", "If forcing keyframes, force them as IDR frames.", OFFSET(forced_idr), + AV_OPT_TYPE_BOOL, { .i64 = 0 }, -1, 1, VE }, + ++ { "hdr", "High dynamic range input", OFFSET(hdr), ++ AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, ++ + {NULL}, +}; + @@ -600,6 +628,7 @@ index 0000000000..aa51830788 + { "flags", "+cgop" }, + { "qmin", "10" }, + { "qmax", "48" }, ++ { "g", "-2" }, + { NULL }, +}; + @@ -623,5 +652,5 @@ index 0000000000..aa51830788 + .wrapper_name = "libsvt_hevc", +}; -- -2.17.1 +1.8.3.1 diff --git a/ffmpeg_plugin/libsvt_hevc.c b/ffmpeg_plugin/libsvt_hevc.c deleted file mode 100644 index 6ec9e1645..000000000 --- a/ffmpeg_plugin/libsvt_hevc.c +++ /dev/null @@ -1,440 +0,0 @@ -/* -* Scalable Video Technology for HEVC encoder library plugin -* -* Copyright (c) 2018 Intel Corporation -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; either -* version 2.1 of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "EbErrorCodes.h" -#include "EbTime.h" -#include "EbApi.h" - -#include "libavutil/common.h" -#include "libavutil/frame.h" -#include "libavutil/opt.h" - -#include "internal.h" -#include "avcodec.h" - -typedef struct SvtEncoder { - EB_H265_ENC_CONFIGURATION enc_params; - EB_COMPONENTTYPE *svt_handle; - EB_BUFFERHEADERTYPE *in_buf; - EB_BUFFERHEADERTYPE *out_buf; - int raw_size; -} SvtEncoder; - -typedef struct SvtParams { - int vui_info; - int hierarchical_level; - int la_depth; - int intra_ref_type; - int enc_mode; - int rc_mode; - int scd; - int tune; - int qp; - int profile; - int base_layer_switch_mode; -}SvtParams; - -typedef struct SvtContext { - AVClass *class; - SvtEncoder *svt_enc; - SvtParams svt_param; - int eos_flag; -} SvtContext; - -static void free_buffer(SvtEncoder *svt_enc) -{ - if (svt_enc->in_buf) { - EB_H265_ENC_INPUT *in_data = (EB_H265_ENC_INPUT *)svt_enc->in_buf->pBuffer; - av_freep(&in_data); - av_freep(&svt_enc->in_buf); - } - av_freep(&svt_enc->out_buf); -} - -static EB_ERRORTYPE alloc_buffer(EB_H265_ENC_CONFIGURATION *config, SvtEncoder *svt_enc) -{ - EB_ERRORTYPE ret = EB_ErrorNone; - - const int pack_mode_10bit = - (config->encoderBitDepth > 8) && (config->compressedTenBitFormat == 0) ? 1 : 0; - const size_t luma_size_8bit = - config->sourceWidth * config->sourceHeight * (1 << pack_mode_10bit); - const size_t luma_size_10bit = - (config->encoderBitDepth > 8 && pack_mode_10bit == 0) ? luma_size_8bit : 0; - - svt_enc->raw_size = (luma_size_8bit + luma_size_10bit) * 3 / 2; - - // allocate buffer for in and out - svt_enc->in_buf = av_mallocz(sizeof(EB_BUFFERHEADERTYPE)); - svt_enc->out_buf = av_mallocz(sizeof(EB_BUFFERHEADERTYPE)); - if (!svt_enc->in_buf || !svt_enc->out_buf) - goto failed; - - svt_enc->in_buf->pBuffer = av_mallocz(sizeof(EB_H265_ENC_INPUT)); - if (!svt_enc->in_buf->pBuffer) - goto failed; - - svt_enc->in_buf->nSize = sizeof(EB_BUFFERHEADERTYPE); - svt_enc->in_buf->pAppPrivate = NULL; - svt_enc->out_buf->nSize = sizeof(EB_BUFFERHEADERTYPE); - svt_enc->out_buf->nAllocLen = svt_enc->raw_size; - svt_enc->out_buf->pAppPrivate = NULL; - - return ret; - -failed: - free_buffer(svt_enc); - return EB_ErrorInsufficientResources; -} - -static int error_mapping(int val) -{ - int err; - - switch (val) { - case EB_ErrorInsufficientResources: - err = AVERROR(ENOMEM); - break; - - case EB_ErrorUndefined: - case EB_ErrorInvalidComponent: - case EB_ErrorBadParameter: - err = AVERROR(EINVAL); - break; - - case EB_NoErrorEmptyQueue: - err = AVERROR(EAGAIN); - break; - - default: - err = AVERROR_EXTERNAL; - } - - return err; -} - -static EB_ERRORTYPE config_enc_params(EB_H265_ENC_CONFIGURATION *param, - AVCodecContext *avctx) -{ - SvtContext *q = avctx->priv_data; - SvtEncoder *svt_enc = q->svt_enc; - EB_ERRORTYPE ret = EB_ErrorNone; - int ten_bits = 0; - - param->sourceWidth = avctx->width; - param->sourceHeight = avctx->height; - - if (avctx->pix_fmt == AV_PIX_FMT_YUV420P10LE) { - av_log(avctx, AV_LOG_DEBUG , "Encoder 10 bits depth input\n"); - param->compressedTenBitFormat = 0; - ten_bits = 1; - } - - // Update param from options - param->hierarchicalLevels = q->svt_param.hierarchical_level; - param->encMode = q->svt_param.enc_mode; - param->intraRefreshType = q->svt_param.intra_ref_type; - param->profile = q->svt_param.profile; - param->rateControlMode = q->svt_param.rc_mode; - param->sceneChangeDetection = q->svt_param.scd; - param->tune = q->svt_param.tune; - param->baseLayerSwitchMode = q->svt_param.base_layer_switch_mode; - param->qp = q->svt_param.qp; - - param->targetBitRate = avctx->bit_rate; - param->intraPeriodLength = avctx->gop_size-1; - param->frameRateNumerator = avctx->time_base.den; - param->frameRateDenominator = avctx->time_base.num * avctx->ticks_per_frame; - - param->codeVpsSpsPps = 0; - - if (q->svt_param.vui_info) - param->videoUsabilityInfo = q->svt_param.vui_info; - - if (q->svt_param.la_depth != -1) - param->lookAheadDistance = q->svt_param.la_depth; - - if (ten_bits) { - param->encoderBitDepth = 10; - param->profile = 2; - } - - ret = alloc_buffer(param, svt_enc); - - return ret; -} - -static void read_in_data(EB_H265_ENC_CONFIGURATION *config, - const AVFrame *frame, - EB_BUFFERHEADERTYPE *headerPtr) -{ - unsigned int is16bit = config->encoderBitDepth > 8; - unsigned long long luma_size = - (unsigned long long)config->sourceWidth * config->sourceHeight<< is16bit; - EB_H265_ENC_INPUT *in_data = (EB_H265_ENC_INPUT*)headerPtr->pBuffer; - - // support yuv420p and yuv420p010 - in_data->luma = frame->data[0]; - in_data->cb = frame->data[1]; - in_data->cr = frame->data[2]; - - // stride info - in_data->yStride = frame->linesize[0] >> is16bit; - in_data->cbStride = frame->linesize[1] >> is16bit; - in_data->crStride = frame->linesize[2] >> is16bit; - - headerPtr->nFilledLen += luma_size * 3/2u; -} - -static av_cold int eb_enc_init(AVCodecContext *avctx) -{ - SvtContext *q = avctx->priv_data; - SvtEncoder *svt_enc = NULL; - EB_ERRORTYPE ret = EB_ErrorNone; - - q->svt_enc = av_mallocz(sizeof(*q->svt_enc)); - if (!q->svt_enc) - return AVERROR(ENOMEM); - - svt_enc = q->svt_enc; - - q->eos_flag = 0; - - ret = EbInitHandle(&svt_enc->svt_handle, q, &svt_enc->enc_params); - if (ret != EB_ErrorNone) { - av_log(avctx, AV_LOG_ERROR, "Error init encoder handle\n"); - goto failed; - } - - ret = config_enc_params(&svt_enc->enc_params, avctx); - if (ret != EB_ErrorNone) { - av_log(avctx, AV_LOG_ERROR, "Error configure encoder parameters\n"); - goto failed_init_handle; - } - - ret = EbH265EncSetParameter(svt_enc->svt_handle, &svt_enc->enc_params); - if (ret != EB_ErrorNone) { - av_log(avctx, AV_LOG_ERROR, "Error setting encoder parameters\n"); - goto failed_init_handle; - } - - ret = EbInitEncoder(svt_enc->svt_handle); - if (ret != EB_ErrorNone) { - av_log(avctx, AV_LOG_ERROR, "Error init encoder\n"); - goto failed_init_handle; - } - - if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { - EB_BUFFERHEADERTYPE headerPtr; - headerPtr.nSize = sizeof(EB_BUFFERHEADERTYPE); - headerPtr.nFilledLen = 0; - headerPtr.pBuffer = av_malloc(10 * 1024 * 1024); - headerPtr.nAllocLen = (10 * 1024 * 1024); - - if (!headerPtr.pBuffer) { - av_log(avctx, AV_LOG_ERROR, - "Cannot allocate buffer size %d.\n", headerPtr.nAllocLen); - ret = EB_ErrorInsufficientResources; - goto failed_init_enc; - } - - ret = EbH265EncStreamHeader(svt_enc->svt_handle, &headerPtr); - if (ret != EB_ErrorNone) { - av_log(avctx, AV_LOG_ERROR, "Error when build stream header.\n"); - av_freep(&headerPtr.pBuffer); - goto failed_init_enc; - } - - avctx->extradata_size = headerPtr.nFilledLen; - avctx->extradata = av_malloc(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!avctx->extradata) { - av_log(avctx, AV_LOG_ERROR, - "Cannot allocate HEVC header of size %d.\n", avctx->extradata_size); - av_freep(&headerPtr.pBuffer); - ret = EB_ErrorInsufficientResources; - goto failed_init_enc; - } - memcpy(avctx->extradata, headerPtr.pBuffer, avctx->extradata_size); - - av_freep(&headerPtr.pBuffer); - } - - return 0; - -failed_init_enc: - EbDeinitEncoder(svt_enc->svt_handle); -failed_init_handle: - EbDeinitHandle(svt_enc->svt_handle); -failed: - return error_mapping(ret); -} - -static int eb_send_frame(AVCodecContext *avctx, const AVFrame *frame) -{ - SvtContext *q = avctx->priv_data; - SvtEncoder *svt_enc = q->svt_enc; - EB_BUFFERHEADERTYPE *headerPtr = svt_enc->in_buf; - int ret = 0; - - if (!frame) { - EB_BUFFERHEADERTYPE headerPtrLast; - headerPtrLast.nAllocLen = 0; - headerPtrLast.nFilledLen = 0; - headerPtrLast.nTickCount = 0; - headerPtrLast.pAppPrivate = NULL; - headerPtrLast.nOffset = 0; - headerPtrLast.pBuffer = NULL; - headerPtrLast.nFlags = EB_BUFFERFLAG_EOS; - - EbH265EncSendPicture(svt_enc->svt_handle, &headerPtrLast); - q->eos_flag = 1; - av_log(avctx, AV_LOG_DEBUG, "Finish sending frames!!!\n"); - return ret; - } - - read_in_data(&svt_enc->enc_params, frame, headerPtr); - - headerPtr->nOffset = 0; - headerPtr->nFlags = 0; - headerPtr->pAppPrivate = NULL; - headerPtr->pts = frame->pts; - headerPtr->sliceType = INVALID_SLICE; - EbH265EncSendPicture(svt_enc->svt_handle, headerPtr); - - return ret; -} - -static int eb_receive_packet(AVCodecContext *avctx, AVPacket *pkt) -{ - SvtContext *q = avctx->priv_data; - SvtEncoder *svt_enc = q->svt_enc; - EB_BUFFERHEADERTYPE *headerPtr = svt_enc->out_buf; - EB_ERRORTYPE stream_status = EB_ErrorNone; - int ret = 0; - - if ((ret = ff_alloc_packet2(avctx, pkt, svt_enc->raw_size, 0)) < 0) { - av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n"); - return ret; - } - headerPtr->pBuffer = pkt->data; - stream_status = EbH265GetPacket(svt_enc->svt_handle, headerPtr, q->eos_flag); - if (stream_status == EB_NoErrorEmptyQueue) - return AVERROR(EAGAIN); - - pkt->size = headerPtr->nFilledLen; - pkt->pts = headerPtr->pts; - pkt->dts = headerPtr->dts; - if (headerPtr->sliceType == IDR_SLICE) - pkt->flags |= AV_PKT_FLAG_KEY; - if (headerPtr->sliceType == NON_REF_SLICE) - pkt->flags |= AV_PKT_FLAG_DISPOSABLE; - - ret = (headerPtr->nFlags & EB_BUFFERFLAG_EOS) ? AVERROR_EOF : 0; - return ret; -} - -static av_cold int eb_enc_close(AVCodecContext *avctx) -{ - SvtContext *q = avctx->priv_data; - SvtEncoder *svt_enc = q->svt_enc; - - EbDeinitEncoder(svt_enc->svt_handle); - EbDeinitHandle(svt_enc->svt_handle); - - free_buffer(svt_enc); - av_freep(&svt_enc); - - return 0; -} - -#define OFFSET(x) offsetof(SvtContext, x) -#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM -static const AVOption options[] = { - {"vui", "Enable vui info", OFFSET(svt_param.vui_info), - AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, - {"hielevel", "Hierarchical prediction levels setting", OFFSET(svt_param.hierarchical_level), - AV_OPT_TYPE_INT, { .i64 = 3 }, 0, 3, VE , "hielevel"}, - { "flat", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "hielevel" }, - { "2level", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "hielevel" }, - { "3level", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, "hielevel" }, - { "4level", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, INT_MIN, INT_MAX, VE, "hielevel" }, - {"la_depth", "Look ahead distance [0, 256]", OFFSET(svt_param.la_depth), - AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 256, VE }, - {"intra_ref_type", "Intra refresh type", OFFSET(svt_param.intra_ref_type), - AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 2, VE , "intra_ref_type"}, - { "none", "No intra refresh", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "intra_ref_type" }, - { "cra", "CRA (Open GOP)", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "intra_ref_type" }, - { "idr", "IDR", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, "intra_ref_type" }, - {"perset", "Encoding preset [0, 12] (e,g, for subjective quality tuning mode and >=4k resolution), [0, 10] (for >= 1080p resolution), [0, 9] (for all resolution and modes)", - OFFSET(svt_param.enc_mode), AV_OPT_TYPE_INT, { .i64 = 9 }, 0, 12, VE }, - {"profile", "Profile setting, Main Still Picture Profile not supported", OFFSET(svt_param.profile), - AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 2, VE, "profile"}, - { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "profile" }, - { "main10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "profile" }, - {"rc", "Bit rate control mode", OFFSET(svt_param.rc_mode), - AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE , "rc"}, - { "cqp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "rc" }, - { "vbr", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "rc" }, - {"qp", "QP value for intra frames", OFFSET(svt_param.qp), - AV_OPT_TYPE_INT, { .i64 = 32 }, 0, 51, VE }, - {"sc_detection", "Scene change detection", OFFSET(svt_param.scd), - AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, - {"tune", "Tune mode", OFFSET(svt_param.tune), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, "tune" }, - { "sq", "subjective quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "tune" }, - { "oq", "objective quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "tune" }, - {"bl_mode", "Random Access Prediction Structure type setting", OFFSET(svt_param.base_layer_switch_mode), - AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, - {NULL}, -}; - -static const AVClass class = { - .class_name = "libsvt_hevc", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, -}; - -static const AVCodecDefault eb_enc_defaults[] = { - { "b", "7M" }, - { "refs", "0" }, - { "g", "64" }, - { "flags", "+cgop" }, - { NULL }, -}; - -AVCodec ff_libsvt_hevc_encoder = { - .name = "libsvt_hevc", - .long_name = NULL_IF_CONFIG_SMALL("SVT-HEVC(Scalable Video Technology for HEVC) encoder"), - .priv_data_size = sizeof(SvtContext), - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .init = eb_enc_init, - .send_frame = eb_send_frame, - .receive_packet = eb_receive_packet, - .close = eb_enc_close, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS, - .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, - AV_PIX_FMT_YUV420P10, - AV_PIX_FMT_NONE }, - .priv_class = &class, - .defaults = eb_enc_defaults, - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - .wrapper_name = "libsvt_hevc", -}; From f268574c95fa776efd2814c74c1069ac6a9647b5 Mon Sep 17 00:00:00 2001 From: kirithika Date: Thu, 21 Mar 2019 10:36:59 +0530 Subject: [PATCH 30/93] Update the filler bit error in the packetization feedback to rate control --- Source/Lib/Codec/EbEncodeContext.c | 3 +- Source/Lib/Codec/EbEncodeContext.h | 2 + Source/Lib/Codec/EbPacketizationProcess.c | 138 ++++++++++-------- .../Lib/Codec/EbPacketizationReorderQueue.h | 2 + Source/Lib/Codec/EbRateControlProcess.c | 6 +- 5 files changed, 87 insertions(+), 64 deletions(-) diff --git a/Source/Lib/Codec/EbEncodeContext.c b/Source/Lib/Codec/EbEncodeContext.c index ee0b5be08..0ed5be04d 100644 --- a/Source/Lib/Codec/EbEncodeContext.c +++ b/Source/Lib/Codec/EbEncodeContext.c @@ -209,6 +209,7 @@ EB_ERRORTYPE EncodeContextCtor( encodeContextPtr->bufferFill = 0; encodeContextPtr->vbvBufsize = 0; encodeContextPtr->vbvMaxrate = 0; + encodeContextPtr->fillerBitError = 0; // Rate Control Bit Tables EB_MALLOC(RateControlTables_t*, encodeContextPtr->rateControlTablesArray, sizeof(RateControlTables_t) * TOTAL_NUMBER_OF_INITIAL_RC_TABLES_ENTRY, EB_N_PTR); @@ -229,7 +230,7 @@ EB_ERRORTYPE EncodeContextCtor( encodeContextPtr->encMode = SPEED_CONTROL_INIT_MOD; EB_CREATEMUTEX(EB_HANDLE, encodeContextPtr->bufferFillMutex, sizeof(EB_HANDLE), EB_MUTEX); - + EB_CREATEMUTEX(EB_HANDLE,encodeContextPtr->fillerBitMutex,sizeof(EB_HANDLE),EB_MUTEX) encodeContextPtr->previousSelectedRefQp = 32; encodeContextPtr->maxCodedPoc = 0; encodeContextPtr->maxCodedPocSelectedRefQp = 32; diff --git a/Source/Lib/Codec/EbEncodeContext.h b/Source/Lib/Codec/EbEncodeContext.h index 14f8df8d4..c56556c4e 100644 --- a/Source/Lib/Codec/EbEncodeContext.h +++ b/Source/Lib/Codec/EbEncodeContext.h @@ -147,6 +147,8 @@ typedef struct EncodeContext_s EB_U32 vbvMaxrate; EB_U32 vbvBufsize; EB_U64 bufferFill; + EB_S64 fillerBitError; + EB_HANDLE fillerBitMutex; EB_HANDLE bufferFillMutex; EB_U32 previousSelectedRefQp; diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 5d34be48a..caadeb49e 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -639,8 +639,9 @@ void* PacketizationKernel(void *inputPtr) NAL_UNIT_INVALID); bufferRate = encodeContextPtr->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16); + queueEntryPtr->fillerBitsSent = 0; if ((sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) && (sequenceControlSetPtr->staticConfig.vbvMaxrate == sequenceControlSetPtr->staticConfig.targetBitRate)) - { + { pictureControlSetPtr->ParentPcsPtr->totalNumBits = outputStreamPtr->nFilledLen << 3; EB_S64 buffer = (EB_S64)(encodeContextPtr->bufferFill); @@ -651,59 +652,7 @@ void* PacketizationKernel(void *inputPtr) if ((EB_U64)buffer > encodeContextPtr->vbvBufsize) { filler = (EB_U64)buffer - encodeContextPtr->vbvBufsize; - fillerBytes = ((EB_U32)(filler >> 3)) - FILLER_DATA_OVERHEAD; - // Reset the bitstream - ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - - EncodeFillerData(pictureControlSetPtr->bitstreamPtr, fillerBytes, pictureControlSetPtr->temporalId); - - // Flush the Bitstream - FlushBitstream( - pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - - // Copy Filler Bits to the Output Bitstream - CopyRbspBitstreamToPayload( - pictureControlSetPtr->bitstreamPtr, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr, - NAL_UNIT_INVALID); - - for (EB_U32 i = 0; i < fillerBytes; i++) - { - ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - OutputBitstreamWrite(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr, 0xff, 8); - // Flush the Bitstream - FlushBitstream( - pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - CopyRbspBitstreamToPayload( - pictureControlSetPtr->bitstreamPtr, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr, - NAL_UNIT_INVALID); - } - ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - // Byte Align the Bitstream: rbsp_trailing_bits - OutputBitstreamWrite( - pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr, - 1, - 1); - - OutputBitstreamWriteAlignZero( - pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - // Flush the Bitstream - FlushBitstream( - pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - CopyRbspBitstreamToPayload( - pictureControlSetPtr->bitstreamPtr, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr, - NAL_UNIT_INVALID); + queueEntryPtr->fillerBitsSent = filler; } } @@ -711,7 +660,7 @@ void* PacketizationKernel(void *inputPtr) pictureControlSetPtr->ParentPcsPtr->totalNumBits = outputStreamPtr->nFilledLen << 3; queueEntryPtr->actualBits = pictureControlSetPtr->ParentPcsPtr->totalNumBits; - + pictureControlSetPtr->ParentPcsPtr->totalNumBits += queueEntryPtr->fillerBitsSent; // Copy Dolby Vision RPU metadata to the output bitstream if (sequenceControlSetPtr->staticConfig.dolbyVisionProfile == 81 && pictureControlSetPtr->ParentPcsPtr->enhancedPicturePtr->dolbyVisionRpu.payloadSize) { // Reset the bitstream @@ -850,22 +799,89 @@ void* PacketizationKernel(void *inputPtr) { refDecOrder = queueEntryPtr->pictureNumber; } - EbPostFullObject(outputStreamWrapperPtr); /* update VBV plan */ EbBlockOnMutex(encodeContextPtr->bufferFillMutex); if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) { EB_S64 bufferfill_temp = (EB_S64)(encodeContextPtr->bufferFill); - bufferfill_temp -= queueEntryPtr->actualBits; bufferfill_temp = MAX(bufferfill_temp, 0); - bufferfill_temp = (EB_S64)(bufferfill_temp + (encodeContextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); + queueEntryPtr->fillerBitsFinal = 0; + EB_U64 buffer=bufferfill_temp = (EB_S64)(bufferfill_temp + (encodeContextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); + //Block to write filler data to prevent cpb overflow + if ((sequenceControlSetPtr->staticConfig.vbvMaxrate == sequenceControlSetPtr->staticConfig.targetBitRate)&& !(outputStreamPtr->nFlags & EB_BUFFERFLAG_EOS)) + { + if (buffer > encodeContextPtr->vbvBufsize) + { + filler = buffer - encodeContextPtr->vbvBufsize; + queueEntryPtr->fillerBitsFinal = filler; + fillerBytes = ((EB_U32)(filler >> 3)) - FILLER_DATA_OVERHEAD; + // Reset the bitstream + ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + + EncodeFillerData(queueEntryPtr->bitStreamPtr2, fillerBytes, queueEntryPtr->picTimingEntry->temporalId); + + // Flush the Bitstream + FlushBitstream( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + + // Copy filler bits to the Output Bitstream + CopyRbspBitstreamToPayload( + queueEntryPtr->bitStreamPtr2, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr, NAL_UNIT_INVALID); + + for (EB_U32 i = 0; i < fillerBytes; i++) + { + ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + OutputBitstreamWrite(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr, 0xff, 8); + FlushBitstream( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + CopyRbspBitstreamToPayload( + queueEntryPtr->bitStreamPtr2, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr, NAL_UNIT_INVALID); + } + ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + // Byte Align the Bitstream: rbsp_trailing_bits + OutputBitstreamWrite( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr, + 1, + 1); + FlushBitstream( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + CopyRbspBitstreamToPayload( + queueEntryPtr->bitStreamPtr2, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr, NAL_UNIT_INVALID); + ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + OutputBitstreamWriteAlignZero( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + FlushBitstream( + queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); + CopyRbspBitstreamToPayload( + queueEntryPtr->bitStreamPtr2, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr, NAL_UNIT_INVALID); + } + } + bufferfill_temp -= queueEntryPtr->fillerBitsFinal; bufferfill_temp = MIN(bufferfill_temp, encodeContextPtr->vbvBufsize); encodeContextPtr->bufferFill = (EB_U64)(bufferfill_temp); - + EbBlockOnMutex(encodeContextPtr->fillerBitMutex); + encodeContextPtr->fillerBitError = (EB_S64)(queueEntryPtr->fillerBitsFinal - queueEntryPtr->fillerBitsSent); + EbReleaseMutex(encodeContextPtr->fillerBitMutex); + EbReleaseMutex(encodeContextPtr->bufferFillMutex); } - EbReleaseMutex(encodeContextPtr->bufferFillMutex); - + EbPostFullObject(outputStreamWrapperPtr); // Reset the Reorder Queue Entry queueEntryPtr->pictureNumber += PACKETIZATION_REORDER_QUEUE_MAX_DEPTH; queueEntryPtr->outputStreamWrapperPtr = (EbObjectWrapper_t *)EB_NULL; diff --git a/Source/Lib/Codec/EbPacketizationReorderQueue.h b/Source/Lib/Codec/EbPacketizationReorderQueue.h index 6a349a193..656cc2f67 100644 --- a/Source/Lib/Codec/EbPacketizationReorderQueue.h +++ b/Source/Lib/Codec/EbPacketizationReorderQueue.h @@ -35,6 +35,8 @@ typedef struct PacketizationReorderEntry_s { Bitstream_t *bitStreamPtr2; Bitstream_t *bitStreamPtr3; EB_U32 startSplicing; + EB_U32 fillerBitsSent; + EB_U32 fillerBitsFinal; } PacketizationReorderEntry_t; extern EB_ERRORTYPE PacketizationReorderEntryCtor( diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 241d010d2..111522c3b 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -1919,10 +1919,12 @@ void FrameLevelRcFeedbackPictureMode2( (EB_S64)rateControlParamPtr->previousVirtualBufferLevel; } else{ + EbBlockOnMutex(sequenceControlSetPtr->encodeContextPtr->fillerBitMutex); rateControlParamPtr->virtualBufferLevel = (EB_S64)rateControlParamPtr->previousVirtualBufferLevel + - (EB_S64)previousFrameBitActual - (EB_S64)rateControlLayerPtr->channelBitRate; - contextPtr->extraBitsGen -= (EB_S64)previousFrameBitActual - (EB_S64)rateControlLayerPtr->channelBitRate; + ((EB_S64)previousFrameBitActual + sequenceControlSetPtr->encodeContextPtr->fillerBitError) - (EB_S64)rateControlLayerPtr->channelBitRate; + contextPtr->extraBitsGen -= ((EB_S64)previousFrameBitActual + sequenceControlSetPtr->encodeContextPtr->fillerBitError) - (EB_S64)rateControlLayerPtr->channelBitRate; + EbReleaseMutex(sequenceControlSetPtr->encodeContextPtr->fillerBitMutex); } if (parentPictureControlSetPtr->hierarchicalLevels > 1 && rateControlLayerPtr->frameSameSADMinQpCount > 10){ From f1cd4c53e18fd25ffad9b5d8a5e871e70cb38e18 Mon Sep 17 00:00:00 2001 From: anaghdin Date: Tue, 26 Mar 2019 11:36:25 -0700 Subject: [PATCH 31/93] Added feedback from packetization to picturemanager to address a deadlock. Deadlock was due to base layer pictures waiting for the previous one. Once testing is done, I will removed the macros --- Source/Lib/Codec/EbDefinitions.h | 2 +- Source/Lib/Codec/EbEncHandle.c | 10 ++++- Source/Lib/Codec/EbPacketizationProcess.c | 43 +++++++++++++++++-- Source/Lib/Codec/EbPacketizationProcess.h | 9 +++- Source/Lib/Codec/EbPictureDemuxResults.h | 3 ++ Source/Lib/Codec/EbPictureManagerProcess.c | 36 ++++++++++++++-- Source/Lib/Codec/EbRateControlProcess.c | 4 +- .../Lib/Codec/EbResourceCoordinationProcess.c | 18 +++++++- Source/Lib/Codec/EbSequenceControlSet.c | 1 - Source/Lib/Codec/EbSequenceControlSet.h | 1 - 10 files changed, 112 insertions(+), 15 deletions(-) diff --git a/Source/Lib/Codec/EbDefinitions.h b/Source/Lib/Codec/EbDefinitions.h index 063df18e6..2dc214e5e 100644 --- a/Source/Lib/Codec/EbDefinitions.h +++ b/Source/Lib/Codec/EbDefinitions.h @@ -10,7 +10,7 @@ #ifdef __cplusplus extern "C" { #endif - +#define PACK_FEEDBACK 1 // Internal Marcos #define NON_AVX512_SUPPORT diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index fda142874..09300ab95 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -1127,7 +1127,11 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) return_error = EbSystemResourceCtor( &encHandlePtr->pictureDemuxResultsResourcePtr, encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->pictureDemuxFifoInitCount, +#if PACK_FEEDBACK + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->encDecProcessInitCount + 1, // 1 for packetization +#else encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->encDecProcessInitCount, +#endif EB_PictureManagerProcessInitCount, &encHandlePtr->pictureDemuxResultsProducerFifoPtrArray, &encHandlePtr->pictureDemuxResultsConsumerFifoPtrArray, @@ -1446,7 +1450,11 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) return_error = PacketizationContextCtor( (PacketizationContext_t**) &encHandlePtr->packetizationContextPtr, encHandlePtr->entropyCodingResultsConsumerFifoPtrArray[0], - encHandlePtr->rateControlTasksProducerFifoPtrArray[RateControlPortLookup(RATE_CONTROL_INPUT_PORT_PACKETIZATION, 0)]); + encHandlePtr->rateControlTasksProducerFifoPtrArray[RateControlPortLookup(RATE_CONTROL_INPUT_PORT_PACKETIZATION, 0)] +#if PACK_FEEDBACK + ,encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->encDecProcessInitCount] // Add port lookup logic here JMJ +#endif + ); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index caadeb49e..06e9971c8 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -17,6 +17,9 @@ #include "EbRateControlTasks.h" #include "EbRateControlProcess.h" #include "EbTime.h" +#if PACK_FEEDBACK +#include "EbPictureDemuxResults.h" +#endif void HrdFullness(SequenceControlSet_t *sequenceControlSetPtr, PictureControlSet_t *pictureControlSetptr, AppBufferingPeriodSei_t *seiBP) { @@ -95,7 +98,11 @@ void InitHRD(SequenceControlSet_t *scsPtr) EB_ERRORTYPE PacketizationContextCtor( PacketizationContext_t **contextDblPtr, EbFifo_t *entropyCodingInputFifoPtr, - EbFifo_t *rateControlTasksOutputFifoPtr) + EbFifo_t *rateControlTasksOutputFifoPtr +#if PACK_FEEDBACK + ,EbFifo_t *pictureManagerOutputFifoPtr +#endif +) { PacketizationContext_t *contextPtr; EB_MALLOC(PacketizationContext_t*, contextPtr, sizeof(PacketizationContext_t), EB_N_PTR); @@ -103,6 +110,9 @@ EB_ERRORTYPE PacketizationContextCtor( contextPtr->entropyCodingInputFifoPtr = entropyCodingInputFifoPtr; contextPtr->rateControlTasksOutputFifoPtr = rateControlTasksOutputFifoPtr; +#if PACK_FEEDBACK + contextPtr->pictureManagerOutputFifoPtr = pictureManagerOutputFifoPtr; +#endif EB_MALLOC(EbPPSConfig_t*, contextPtr->ppsConfig, sizeof(EbPPSConfig_t), EB_N_PTR); @@ -131,7 +141,10 @@ void* PacketizationKernel(void *inputPtr) EB_BUFFERHEADERTYPE *outputStreamPtr; EbObjectWrapper_t *rateControlTasksWrapperPtr; RateControlTasks_t *rateControlTasksPtr; - +#if PACK_FEEDBACK + EbObjectWrapper_t *pictureManagerResultsWrapperPtr; + PictureDemuxResults_t *pictureManagerResultPtr; +#endif // Bitstream copy to output buffer Bitstream_t bitstream; @@ -197,7 +210,25 @@ void* PacketizationKernel(void *inputPtr) rateControlTasksPtr = (RateControlTasks_t*) rateControlTasksWrapperPtr->objectPtr; rateControlTasksPtr->pictureControlSetWrapperPtr = pictureControlSetPtr->PictureParentControlSetWrapperPtr; rateControlTasksPtr->taskType = RC_PACKETIZATION_FEEDBACK_RESULT; - + +#if PACK_FEEDBACK + if (sequenceControlSetPtr->staticConfig.rateControlMode) { + // Get Empty Results Object + EbGetEmptyObject( + contextPtr->pictureManagerOutputFifoPtr, + &pictureManagerResultsWrapperPtr); + + pictureManagerResultPtr = (PictureDemuxResults_t*)pictureManagerResultsWrapperPtr->objectPtr; + pictureManagerResultPtr->pictureNumber = pictureControlSetPtr->pictureNumber; + pictureManagerResultPtr->pictureType = EB_PIC_FEEDBACK; + pictureManagerResultPtr->sequenceControlSetWrapperPtr = pictureControlSetPtr->sequenceControlSetWrapperPtr; + } + else { + pictureManagerResultsWrapperPtr = EB_NULL; + (void) pictureManagerResultPtr; + (void)pictureManagerResultsWrapperPtr; + } +#endif sliceType = pictureControlSetPtr->sliceType; if(pictureControlSetPtr->pictureNumber == 0 && sequenceControlSetPtr->staticConfig.codeVpsSpsPps == 1) { @@ -718,6 +749,12 @@ void* PacketizationKernel(void *inputPtr) // Post Rate Control Taks EbPostFullObject(rateControlTasksWrapperPtr); +#if PACK_FEEDBACK + if (sequenceControlSetPtr->staticConfig.rateControlMode) { + // Post the Full Results Object + EbPostFullObject(pictureManagerResultsWrapperPtr); + } +#endif //Release the Parent PCS then the Child PCS EbReleaseObject(entropyCodingResultsPtr->pictureControlSetWrapperPtr);//Child diff --git a/Source/Lib/Codec/EbPacketizationProcess.h b/Source/Lib/Codec/EbPacketizationProcess.h index 0f2c6c3b4..e8c25e230 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.h +++ b/Source/Lib/Codec/EbPacketizationProcess.h @@ -30,6 +30,9 @@ typedef struct PacketizationContext_s EbFifo_t *entropyCodingInputFifoPtr; EbFifo_t *rateControlTasksOutputFifoPtr; EbPPSConfig_t *ppsConfig; +#if PACK_FEEDBACK + EbFifo_t *pictureManagerOutputFifoPtr; // to picture-manager +#endif } PacketizationContext_t; @@ -39,7 +42,11 @@ typedef struct PacketizationContext_s extern EB_ERRORTYPE PacketizationContextCtor( PacketizationContext_t **contextDblPtr, EbFifo_t *entropyCodingInputFifoPtr, - EbFifo_t *rateControlTasksOutputFifoPtr); + EbFifo_t *rateControlTasksOutputFifoPtr +#if PACK_FEEDBACK + , EbFifo_t *pictureManagerOutputFifoPtr +#endif +); extern void* PacketizationKernel(void *inputPtr); diff --git a/Source/Lib/Codec/EbPictureDemuxResults.h b/Source/Lib/Codec/EbPictureDemuxResults.h index 4d77bc8ce..a387c99d8 100644 --- a/Source/Lib/Codec/EbPictureDemuxResults.h +++ b/Source/Lib/Codec/EbPictureDemuxResults.h @@ -15,6 +15,9 @@ typedef enum EB_PIC_TYPE { EB_PIC_INVALID = 0, EB_PIC_INPUT = 1, EB_PIC_REFERENCE = 2 +#if PACK_FEEDBACK + ,EB_PIC_FEEDBACK = 3 +#endif } EB_PIC_TYPE; /************************************** diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index bba7acd5f..303bc4f4a 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -526,7 +526,35 @@ void* PictureManagerKernel(void *inputPtr) EbReleaseObject(inputPictureDemuxPtr->sequenceControlSetWrapperPtr); break; - +#if PACK_FEEDBACK + case EB_PIC_FEEDBACK: + + sequenceControlSetPtr = (SequenceControlSet_t*)inputPictureDemuxPtr->sequenceControlSetWrapperPtr->objectPtr; + encodeContextPtr = sequenceControlSetPtr->encodeContextPtr; + + referenceQueueIndex = encodeContextPtr->referencePictureQueueHeadIndex; + // Find the Reference in the Reference Queue + do { + + referenceEntryPtr = encodeContextPtr->referencePictureQueue[referenceQueueIndex]; + + if (referenceEntryPtr->pictureNumber == inputPictureDemuxPtr->pictureNumber) { + + // Set the feedback arrived + referenceEntryPtr->feedbackArrived = EB_TRUE; + } + + // Increment the referenceQueueIndex Iterator + referenceQueueIndex = (referenceQueueIndex == REFERENCE_QUEUE_MAX_DEPTH - 1) ? 0 : referenceQueueIndex + 1; + + } while ((referenceQueueIndex != encodeContextPtr->referencePictureQueueTailIndex) && (referenceEntryPtr->pictureNumber != inputPictureDemuxPtr->pictureNumber)); + + //keep the relase of SCS here because we still need the encodeContext strucutre here + // Release the Reference's SequenceControlSet + EbReleaseObject(inputPictureDemuxPtr->sequenceControlSetWrapperPtr); + + break; +#endif default: sequenceControlSetPtr = (SequenceControlSet_t*) inputPictureDemuxPtr->sequenceControlSetWrapperPtr->objectPtr; @@ -585,7 +613,7 @@ void* PictureManagerKernel(void *inputPtr) availabilityFlag = (availabilityFlag == EB_FALSE) ? EB_FALSE : // Don't update if already False (refPoc > currentInputPoc) ? EB_FALSE : // The Reference has not been received as an Input Picture yet, then its availability is false - (!encodeContextPtr->terminatingSequenceFlagReceived && (sequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived)) ? EB_FALSE : + (!encodeContextPtr->terminatingSequenceFlagReceived && (entrySequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived)) ? EB_FALSE : (referenceEntryPtr->referenceAvailable) ? EB_TRUE : // The Reference has been completed EB_FALSE; // The Reference has not been completed } @@ -622,7 +650,7 @@ void* PictureManagerKernel(void *inputPtr) availabilityFlag = (availabilityFlag == EB_FALSE) ? EB_FALSE : // Don't update if already False (refPoc > currentInputPoc) ? EB_FALSE : // The Reference has not been received as an Input Picture yet, then its availability is false - (!encodeContextPtr->terminatingSequenceFlagReceived && (sequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived)) ? EB_FALSE : + (!encodeContextPtr->terminatingSequenceFlagReceived && (entrySequenceControlSetPtr->staticConfig.rateControlMode && entryPictureControlSetPtr->sliceType != EB_I_PICTURE && entryPictureControlSetPtr->temporalLayerIndex == 0 && !referenceEntryPtr->feedbackArrived)) ? EB_FALSE : (referenceEntryPtr->referenceAvailable) ? EB_TRUE : // The Reference has been completed EB_FALSE; // The Reference has not been completed } @@ -718,7 +746,7 @@ void* PictureManagerKernel(void *inputPtr) ChildPictureControlSetPtr->useDeltaQp = (EB_U8)(entrySequenceControlSetPtr->staticConfig.improveSharpness || entrySequenceControlSetPtr->staticConfig.bitRateReduction); // Check resolution - if (sequenceControlSetPtr->inputResolution < INPUT_SIZE_1080p_RANGE) + if (entrySequenceControlSetPtr->inputResolution < INPUT_SIZE_1080p_RANGE) ChildPictureControlSetPtr->difCuDeltaQpDepth = 2; else ChildPictureControlSetPtr->difCuDeltaQpDepth = 3; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 111522c3b..2cfa924d6 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2676,7 +2676,7 @@ void* RateControlKernel(void *inputPtr) // Update feedback arrived in referencepictureQueue // printf("RC FEEDBACK ARRIVED %d\n", (int)parentPictureControlSetPtr->pictureNumber); - +#if !PACK_FEEDBACK ReferenceQueueEntry_t *referenceEntryPtr; EB_U32 referenceQueueIndex; encodeContextPtr = sequenceControlSetPtr->encodeContextPtr; @@ -2696,7 +2696,7 @@ void* RateControlKernel(void *inputPtr) referenceQueueIndex = (referenceQueueIndex == REFERENCE_QUEUE_MAX_DEPTH - 1) ? 0 : referenceQueueIndex + 1; } while ((referenceQueueIndex != encodeContextPtr->referencePictureQueueTailIndex) && (referenceEntryPtr->pictureNumber != parentPictureControlSetPtr->pictureNumber)); - +#endif // Frame level RC if (sequenceControlSetPtr->intraPeriodLength == -1 || sequenceControlSetPtr->staticConfig.rateControlMode == 0){ rateControlParamPtr = contextPtr->rateControlParamQueue[0]; diff --git a/Source/Lib/Codec/EbResourceCoordinationProcess.c b/Source/Lib/Codec/EbResourceCoordinationProcess.c index 51485bb3a..8e08f4422 100644 --- a/Source/Lib/Codec/EbResourceCoordinationProcess.c +++ b/Source/Lib/Codec/EbResourceCoordinationProcess.c @@ -588,13 +588,29 @@ void* ResourceCoordinationKernel(void *inputPtr) } EbReleaseMutex(contextPtr->sequenceControlSetInstanceArray[instanceIndex]->configMutex); +#if PACK_FEEDBACK + if (sequenceControlSetPtr->staticConfig.rateControlMode) { + // Sequence Control Set is released by Rate Control after passing through MDC->MD->ENCDEC->Packetization->RateControl + // ,in the PictureManager after receiving the reference and in PictureManager after receiving the feedback + EbObjectIncLiveCount( + contextPtr->sequenceControlSetActiveArray[instanceIndex], + 3); + } + else { + // Sequence Control Set is released by Rate Control after passing through MDC->MD->ENCDEC->Packetization->RateControl + // and in the PictureManager + EbObjectIncLiveCount( + contextPtr->sequenceControlSetActiveArray[instanceIndex], + 2); + } +#else // Sequence Control Set is released by Rate Control after passing through MDC->MD->ENCDEC->Packetization->RateControl // and in the PictureManager EbObjectIncLiveCount( contextPtr->sequenceControlSetActiveArray[instanceIndex], 2); - +#endif // Set the current SequenceControlSet sequenceControlSetPtr = (SequenceControlSet_t*) contextPtr->sequenceControlSetActiveArray[instanceIndex]->objectPtr; diff --git a/Source/Lib/Codec/EbSequenceControlSet.c b/Source/Lib/Codec/EbSequenceControlSet.c index 278375488..24af73434 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.c +++ b/Source/Lib/Codec/EbSequenceControlSet.c @@ -113,7 +113,6 @@ EB_ERRORTYPE EbSequenceControlSetCtor( sequenceControlSetPtr->enableStrongIntraSmoothing = EB_TRUE; // Rate Control - sequenceControlSetPtr->rateControlMode = 0; sequenceControlSetPtr->targetBitrate = 0x1000; sequenceControlSetPtr->availableBandwidth = 0x1000; diff --git a/Source/Lib/Codec/EbSequenceControlSet.h b/Source/Lib/Codec/EbSequenceControlSet.h index 7850dd1f4..d2c7aeabe 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.h +++ b/Source/Lib/Codec/EbSequenceControlSet.h @@ -90,7 +90,6 @@ typedef struct SequenceControlSet_s EB_U32 generalFrameOnlyConstraintFlag; // Rate Control - EB_U32 rateControlMode; EB_U32 targetBitrate; EB_U32 availableBandwidth; From 0b1103a6a4b8f1dd361d85f1758970246ff6e879 Mon Sep 17 00:00:00 2001 From: anaghdin Date: Tue, 26 Mar 2019 17:36:05 -0700 Subject: [PATCH 32/93] Fix the demux port --- Source/Lib/Codec/EbEncHandle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index 09300ab95..f50e85d81 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -1423,7 +1423,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) encHandlePtr->encDecTasksConsumerFifoPtrArray[processIndex], encHandlePtr->encDecResultsProducerFifoPtrArray[processIndex], encHandlePtr->encDecTasksProducerFifoPtrArray[EncDecPortLookup(ENCDEC_INPUT_PORT_ENCDEC, processIndex)], - encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[1 + processIndex], // Add port lookup logic here JMJ + encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[3 + processIndex], // Add port lookup logic here JMJ is16bit, (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->chromaFormatIdc); if (return_error == EB_ErrorInsufficientResources){ From 494cb249d9be222d00d09c519f26eb9b0143e13f Mon Sep 17 00:00:00 2001 From: anaghdin Date: Tue, 26 Mar 2019 17:54:51 -0700 Subject: [PATCH 33/93] update the pictureDemuxResultsProducerFifoPtrArray ports --- Source/Lib/Codec/EbEncHandle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index f50e85d81..9f93a24bf 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -1423,7 +1423,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) encHandlePtr->encDecTasksConsumerFifoPtrArray[processIndex], encHandlePtr->encDecResultsProducerFifoPtrArray[processIndex], encHandlePtr->encDecTasksProducerFifoPtrArray[EncDecPortLookup(ENCDEC_INPUT_PORT_ENCDEC, processIndex)], - encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[3 + processIndex], // Add port lookup logic here JMJ + encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount + processIndex], // Add port lookup logic here JMJ is16bit, (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->chromaFormatIdc); if (return_error == EB_ErrorInsufficientResources){ From 7b70491a73ca33755da45ac3641dc13422bba0ed Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 27 Mar 2019 12:21:32 +0530 Subject: [PATCH 34/93] Revert the feedback of filler bits error to GOP's virtual Buffer level This commit ensures feeding filler bits error only to contextPtr->extraBitsGen and not to virtual Buffer level pertaining to each GOP. --- Source/Lib/Codec/EbRateControlProcess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 2cfa924d6..28fd82672 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -1922,7 +1922,7 @@ void FrameLevelRcFeedbackPictureMode2( EbBlockOnMutex(sequenceControlSetPtr->encodeContextPtr->fillerBitMutex); rateControlParamPtr->virtualBufferLevel = (EB_S64)rateControlParamPtr->previousVirtualBufferLevel + - ((EB_S64)previousFrameBitActual + sequenceControlSetPtr->encodeContextPtr->fillerBitError) - (EB_S64)rateControlLayerPtr->channelBitRate; + (EB_S64)previousFrameBitActual - (EB_S64)rateControlLayerPtr->channelBitRate; contextPtr->extraBitsGen -= ((EB_S64)previousFrameBitActual + sequenceControlSetPtr->encodeContextPtr->fillerBitError) - (EB_S64)rateControlLayerPtr->channelBitRate; EbReleaseMutex(sequenceControlSetPtr->encodeContextPtr->fillerBitMutex); } From 07c3947940828301d10056257acf93001409e768 Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 29 Mar 2019 18:03:09 +0530 Subject: [PATCH 35/93] Fix for deadlock when vbv is not enabled --- Source/Lib/Codec/EbPacketizationProcess.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 06e9971c8..1fe86f68c 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -837,9 +837,9 @@ void* PacketizationKernel(void *inputPtr) refDecOrder = queueEntryPtr->pictureNumber; } /* update VBV plan */ - EbBlockOnMutex(encodeContextPtr->bufferFillMutex); if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) - { + { + EbBlockOnMutex(encodeContextPtr->bufferFillMutex); EB_S64 bufferfill_temp = (EB_S64)(encodeContextPtr->bufferFill); bufferfill_temp -= queueEntryPtr->actualBits; bufferfill_temp = MAX(bufferfill_temp, 0); From 3df55f3851492b151f5ab82d1f8986db521a5312 Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 29 Mar 2019 18:04:01 +0530 Subject: [PATCH 36/93] Fix compiler warnings --- Source/Lib/Codec/EbEntropyCoding.c | 1 - Source/Lib/Codec/EbEntropyCoding.h | 1 - Source/Lib/Codec/EbPacketizationProcess.c | 2 +- Source/Lib/Codec/EbPacketizationReorderQueue.c | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index 5516fc3b5..5aa743dc4 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -8647,7 +8647,6 @@ EB_ERRORTYPE EncodeActiveParameterSetsSEI( EB_ERRORTYPE EncodeFillerData( Bitstream_t *bitstreamPtr, - EB_U32 fillerBytes, EB_U8 temporalId) { EB_ERRORTYPE return_error = EB_ErrorNone; diff --git a/Source/Lib/Codec/EbEntropyCoding.h b/Source/Lib/Codec/EbEntropyCoding.h index e5db618f6..694713d19 100644 --- a/Source/Lib/Codec/EbEntropyCoding.h +++ b/Source/Lib/Codec/EbEntropyCoding.h @@ -174,7 +174,6 @@ extern EB_ERRORTYPE EncodeActiveParameterSetsSEI( extern EB_ERRORTYPE EncodeFillerData( Bitstream_t *bitstreamPtr, - EB_U32 fillerBytes, EB_U8 temporalId); extern EB_ERRORTYPE EncodeRegUserDataSEI( diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 1fe86f68c..2135ae7b1 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -856,7 +856,7 @@ void* PacketizationKernel(void *inputPtr) // Reset the bitstream ResetBitstream(queueEntryPtr->bitStreamPtr2->outputBitstreamPtr); - EncodeFillerData(queueEntryPtr->bitStreamPtr2, fillerBytes, queueEntryPtr->picTimingEntry->temporalId); + EncodeFillerData(queueEntryPtr->bitStreamPtr2, queueEntryPtr->picTimingEntry->temporalId); // Flush the Bitstream FlushBitstream( diff --git a/Source/Lib/Codec/EbPacketizationReorderQueue.c b/Source/Lib/Codec/EbPacketizationReorderQueue.c index 7a3748fe2..0980e281e 100644 --- a/Source/Lib/Codec/EbPacketizationReorderQueue.c +++ b/Source/Lib/Codec/EbPacketizationReorderQueue.c @@ -33,6 +33,6 @@ EB_ERRORTYPE PacketizationReorderEntryCtor( (*entryDblPtr)->pictureNumber = pictureNumber; (*entryDblPtr)->outputStreamWrapperPtr = (EbObjectWrapper_t *)EB_NULL; (*entryDblPtr)->startSplicing = 0; - return EB_ErrorNone; + return return_error; } From e3b304dc6dffbc14e360141fab98bdaf867c8513 Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Thu, 4 Apr 2019 09:32:47 +0530 Subject: [PATCH 37/93] Fix for Final Bitrate is greater than TBR in Strict CBR --- Source/Lib/Codec/EbRateControlProcess.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 28fd82672..c4c67aa58 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2295,9 +2295,8 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet bufferFillCur -= curBits; queueEntryIndexTemp++; } - /* Try to get the buffer at least 50% filled, but don't set an impossible goal. */ - double finalDur = 1; - targetFill = MIN(encodeContextPtr->bufferFill + totalDuration * encodeContextPtr->vbvMaxrate * 0.5, encodeContextPtr->vbvBufsize * (1 - 0.5 * finalDur)); + + targetFill = MIN(encodeContextPtr->bufferFill + totalDuration * encodeContextPtr->vbvMaxrate * 0.5, encodeContextPtr->vbvBufsize * (1 - 0.5)); if (bufferFillCur < targetFill) { q++; @@ -2308,8 +2307,8 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet loopTerminate |= 1; continue; } - /* Try to get the buffer not more than 80% filled, but don't set an impossible goal. */ - targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.2 * finalDur), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); + + targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.05), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); if ((bitrateFlag) && (bufferFillCur > targetFill)) { q--; From 603afa8e460106e7a57fdb98aff5e51b175940aa Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 15 Apr 2019 10:17:13 +0530 Subject: [PATCH 38/93] Fix:Remove the duplicated use of current frame bits for lookahead based VBV QP decisions --- Source/Lib/Codec/EbRateControlProcess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index c4c67aa58..8a4743f4d 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2275,7 +2275,7 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet double fps = 1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION); double totalDuration = fps; queueEntryIndexTemp = currentInd; - + queueEntryIndexTemp++; /* Loop over the planned future frames. */ for (EB_S32 j = 0; bufferFillCur >= 0; j++) { From aa3d3f6ec9f689be56b336913391c9ef04d7c3fe Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 5 Apr 2019 10:54:57 +0530 Subject: [PATCH 39/93] Add support for vbv-end and vbv-end-fr-adj vbv-end specifies Buffer Fullness at the end in %. Range[0-100],Default = 0 vbv-end-fr-adj specifies the frame from which vbv-end could be adjusted in % Range[0-100],Default = 0 --- Source/API/EbApi.h | 3 +- Source/App/EbAppConfig.c | 9 +++ Source/App/EbAppConfig.h | 3 +- Source/App/EbAppContext.c | 2 + Source/Lib/Codec/EbEncHandle.c | 10 +++ Source/Lib/Codec/EbEncodeContext.c | 1 + Source/Lib/Codec/EbEncodeContext.h | 1 + Source/Lib/Codec/EbRateControlProcess.c | 84 +++++++++++++++++-------- 8 files changed, 85 insertions(+), 28 deletions(-) diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index d9a71372f..8e985536b 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -511,7 +511,8 @@ typedef struct EB_H265_ENC_CONFIGURATION uint32_t vbvBufsize; uint32_t hrdFlag; uint64_t vbvBufInit; - + uint64_t vbvBufEnd; + uint64_t vbvEndFrameAdjust; /* ID assigned to each channel when multiple instances are running within the * same application. */ uint32_t channelId; diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index 30de03bcb..d3fcfee56 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -83,6 +83,8 @@ #define VBV_MAX_RATE_TOKEN "-vbv-maxrate" #define VBV_BUFFER_SIZE_TOKEN "-vbv-bufsize" #define VBV_BUFFER_INIT_TOKEN "-vbv-init" +#define VBV_BUFFER_END_TOKEN "-vbv-end" +#define VBV_END_FRAME_ADJUST_TOKEN "-vbv-end-fr-adj" #define HRD_TOKEN "-hrd" #define MAX_QP_TOKEN "-max-qp" #define MIN_QP_TOKEN "-min-qp" @@ -202,6 +204,8 @@ static void SetImproveSharpness (const char *value, EbConfig_t * static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0); }; static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0); }; static void SetVbvBufInit (const char *value, EbConfig_t *cfg) { cfg->vbvBufInit = strtoul(value, NULL, 0); }; +static void SetVbvEndFrameAdjust (const char *value, EbConfig_t *cfg) { cfg->vbvEndFrameAdjust = strtoul(value, NULL, 0); }; +static void SetVbvBufEnd (const char *value, EbConfig_t *cfg) { cfg->vbvBufEnd = strtoul(value, NULL, 0); }; static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0); }; static void SetVideoUsabilityInfo (const char *value, EbConfig_t *cfg) {cfg->videoUsabilityInfo = strtol(value, NULL, 0);}; static void SetHighDynamicRangeInput (const char *value, EbConfig_t *cfg) {cfg->highDynamicRangeInput = strtol(value, NULL, 0);}; @@ -314,6 +318,9 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, VBV_BUFFER_SIZE_TOKEN, "vbvBufsize", SetVbvBufsize }, { SINGLE_INPUT, HRD_TOKEN, "hrd", SetHrdFlag }, { SINGLE_INPUT, VBV_BUFFER_INIT_TOKEN, "vbvBufInit", SetVbvBufInit}, + { SINGLE_INPUT, VBV_BUFFER_END_TOKEN, "vbvBufEnd", SetVbvBufEnd}, + { SINGLE_INPUT, VBV_END_FRAME_ADJUST_TOKEN, "vbvEndFrameAdjustToken", SetVbvEndFrameAdjust}, + // DLF { SINGLE_INPUT, LOOP_FILTER_DISABLE_TOKEN, "LoopFilterDisable", SetDisableDlfFlag }, @@ -425,6 +432,8 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->vbvMaxRate = 0; configPtr->vbvBufsize = 0; configPtr->vbvBufInit = 90; + configPtr->vbvBufEnd = 0; + configPtr->vbvEndFrameAdjust = 0; configPtr->hrdFlag = 0; configPtr->intraPeriod = -2; configPtr->intraRefreshType = 1; diff --git a/Source/App/EbAppConfig.h b/Source/App/EbAppConfig.h index e2b41583d..c15b325a5 100644 --- a/Source/App/EbAppConfig.h +++ b/Source/App/EbAppConfig.h @@ -322,7 +322,8 @@ typedef struct EbConfig_s uint32_t vbvMaxRate; uint32_t vbvBufsize; uint64_t vbvBufInit; - + uint64_t vbvBufEnd; + uint64_t vbvEndFrameAdjust; /**************************************** * TUNE ****************************************/ diff --git a/Source/App/EbAppContext.c b/Source/App/EbAppContext.c index f0e2aded4..7bb2825ae 100644 --- a/Source/App/EbAppContext.c +++ b/Source/App/EbAppContext.c @@ -183,6 +183,8 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.vbvMaxrate = config->vbvMaxRate; callbackData->ebEncParameters.vbvBufsize = config->vbvBufsize; callbackData->ebEncParameters.vbvBufInit = config->vbvBufInit; + callbackData->ebEncParameters.vbvBufEnd = config->vbvBufEnd; + callbackData->ebEncParameters.vbvEndFrameAdjust = config->vbvEndFrameAdjust; callbackData->ebEncParameters.useQpFile = (EB_BOOL)config->useQpFile; callbackData->ebEncParameters.disableDlfFlag = (EB_BOOL)config->disableDlfFlag; callbackData->ebEncParameters.enableSaoFlag = (EB_BOOL)config->enableSaoFlag; diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index 9f93a24bf..a69c44353 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2102,6 +2102,8 @@ void CopyApiFromApp( sequenceControlSetPtr->staticConfig.vbvMaxrate = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvMaxrate; sequenceControlSetPtr->staticConfig.vbvBufsize = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufsize; sequenceControlSetPtr->staticConfig.vbvBufInit = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufInit; + sequenceControlSetPtr->staticConfig.vbvBufEnd = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufEnd; + sequenceControlSetPtr->staticConfig.vbvEndFrameAdjust = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvEndFrameAdjust; sequenceControlSetPtr->staticConfig.lookAheadDistance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->lookAheadDistance; sequenceControlSetPtr->staticConfig.framesToBeEncoded = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->framesToBeEncoded; @@ -2836,6 +2838,14 @@ static EB_ERRORTYPE VerifySettings(\ printf("Error instance %u: Invalid vbvBufInit [0 - 100]\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } + if (config->vbvBufEnd > 100) { + printf("Error instance %u: Invalid vbvBufEnd [0 - 100]\n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } + if (config->vbvEndFrameAdjust > 100) { + printf("Error instance %u: Invalid vbvEndFrameAdjust [0 - 100]\n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } return return_error; } diff --git a/Source/Lib/Codec/EbEncodeContext.c b/Source/Lib/Codec/EbEncodeContext.c index 0ed5be04d..66734358e 100644 --- a/Source/Lib/Codec/EbEncodeContext.c +++ b/Source/Lib/Codec/EbEncodeContext.c @@ -210,6 +210,7 @@ EB_ERRORTYPE EncodeContextCtor( encodeContextPtr->vbvBufsize = 0; encodeContextPtr->vbvMaxrate = 0; encodeContextPtr->fillerBitError = 0; + encodeContextPtr->vbvEndEmptiness = 0; // Rate Control Bit Tables EB_MALLOC(RateControlTables_t*, encodeContextPtr->rateControlTablesArray, sizeof(RateControlTables_t) * TOTAL_NUMBER_OF_INITIAL_RC_TABLES_ENTRY, EB_N_PTR); diff --git a/Source/Lib/Codec/EbEncodeContext.h b/Source/Lib/Codec/EbEncodeContext.h index c56556c4e..d347e1261 100644 --- a/Source/Lib/Codec/EbEncodeContext.h +++ b/Source/Lib/Codec/EbEncodeContext.h @@ -147,6 +147,7 @@ typedef struct EncodeContext_s EB_U32 vbvMaxrate; EB_U32 vbvBufsize; EB_U64 bufferFill; + EB_U64 vbvEndEmptiness; EB_S64 fillerBitError; EB_HANDLE fillerBitMutex; EB_HANDLE bufferFillMutex; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 8a4743f4d..2b7533e01 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2296,32 +2296,63 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet queueEntryIndexTemp++; } - targetFill = MIN(encodeContextPtr->bufferFill + totalDuration * encodeContextPtr->vbvMaxrate * 0.5, encodeContextPtr->vbvBufsize * (1 - 0.5)); - if (bufferFillCur < targetFill) - { - q++; - q = CLIP3( - sequenceControlSetPtr->staticConfig.minQpAllowed, - sequenceControlSetPtr->staticConfig.maxQpAllowed, - q); - loopTerminate |= 1; - continue; - } - - targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.05), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); - if ((bitrateFlag) && (bufferFillCur > targetFill)) - { - q--; - q = CLIP3( - sequenceControlSetPtr->staticConfig.minQpAllowed, - sequenceControlSetPtr->staticConfig.maxQpAllowed, - q); - loopTerminate |= 2; - continue; - } - break; - } - q = MAX(q0 / 2, q); + if (encodeContextPtr->vbvEndEmptiness && pictureControlSetPtr->ParentPcsPtr->decodeOrder >= (EB_U32)((sequenceControlSetPtr->staticConfig.vbvEndFrameAdjust / 100.0) * sequenceControlSetPtr->staticConfig.framesToBeEncoded)) + { + EB_BOOL loopBreak = EB_FALSE; + double bufferDiff = (encodeContextPtr->vbvEndEmptiness / 100.0) - ((double)encodeContextPtr->bufferFill / (double)encodeContextPtr->vbvBufsize); + targetFill = encodeContextPtr->bufferFill + encodeContextPtr->vbvBufsize * (bufferDiff / (sequenceControlSetPtr->staticConfig.framesToBeEncoded - pictureControlSetPtr->ParentPcsPtr->decodeOrder)); + if (bufferFillCur < targetFill) + { + q++; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 1; + loopBreak = EB_TRUE; + } + if (bufferFillCur > ((encodeContextPtr->vbvEndEmptiness / 100.0) * encodeContextPtr->vbvBufsize)) + { + q--; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 2; + loopBreak = EB_TRUE; + } + if (!loopBreak) + break; + } + + else { + targetFill = MIN(encodeContextPtr->bufferFill + totalDuration * encodeContextPtr->vbvMaxrate * 0.5, encodeContextPtr->vbvBufsize * (1 - 0.5)); + if (bufferFillCur < targetFill) + { + q++; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 1; + continue; + } + + targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.05), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); + if ((bitrateFlag) && (bufferFillCur > targetFill)) + { + q--; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 2; + continue; + } + break; + } + } + q = MAX(q0 / 2, q); return (EB_U8)q; } @@ -2393,6 +2424,7 @@ void* RateControlKernel(void *inputPtr) contextPtr->highLevelRateControlPtr->channelBitRatePerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerFrame * (sequenceControlSetPtr->staticConfig.lookAheadDistance + 1); contextPtr->highLevelRateControlPtr->bitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; encodeContextPtr->bufferFill = (EB_U64)(sequenceControlSetPtr->staticConfig.vbvBufsize * sequenceControlSetPtr->staticConfig.vbvBufInit / 100); + encodeContextPtr->vbvEndEmptiness = sequenceControlSetPtr->staticConfig.vbvBufEnd; #if RC_UPDATE_TARGET_RATE contextPtr->highLevelRateControlPtr->previousUpdatedBitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; #endif From c82520c8204a91d2e18b7d57c633797f9b3caac3 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 6 Feb 2019 16:41:09 +0530 Subject: [PATCH 40/93] Add proxy entropy coding call into Encdec process to support block level vbv tuning --- Source/Lib/Codec/EbEncDecProcess.c | 35 ++++++++++++++++ Source/Lib/Codec/EbPictureControlSet.c | 57 +++++++++++++++++++++++++- Source/Lib/Codec/EbPictureControlSet.h | 7 ++++ 3 files changed, 97 insertions(+), 2 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 36e8c9838..3f846a608 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -1378,6 +1378,11 @@ static void ResetEncDec( entropyCodingQp, pictureControlSetPtr->sliceType); + ResetEntropyCoder( + sequenceControlSetPtr->encodeContextPtr, + pictureControlSetPtr->tempEntropyCoderPtr, + entropyCodingQp, + pictureControlSetPtr->sliceType); ResetEncodePassNeighborArrays(pictureControlSetPtr); //this fucntion could be optimized by removed chroma, and unessary TU sizes. @@ -3829,6 +3834,11 @@ void* EncDecKernel(void *inputPtr) EB_U32 segmentBandSize; EncDecSegments_t *segmentsPtr; + //Proxy entropy coding + EbPictureBufferDesc_t * tempCoeffPicturePtr; + EB_U32 totalbits = 0; + EB_U32 tempWrittenBitsBeforeQuantizedCoeff; + EB_U32 tempWrittenBitsAfterQuantizedCoeff; for (;;) { @@ -3945,6 +3955,8 @@ void* EncDecKernel(void *inputPtr) lcuRowIndexCount = (xLcuIndex == pictureWidthInLcu - 1) ? lcuRowIndexCount + 1 : lcuRowIndexCount; mdcPtr = &pictureControlSetPtr->mdcLcuArray[lcuIndex]; contextPtr->lcuIndex = lcuIndex; + + tempCoeffPicturePtr = lcuPtr->quantizedCoeff; // Derive cuUseRefSrcFlag Flag contextPtr->mdContext->cuUseRefSrcFlag = (pictureControlSetPtr->ParentPcsPtr->useSrcRef) && (pictureControlSetPtr->ParentPcsPtr->edgeResultsPtr[lcuIndex].edgeBlockNum == EB_FALSE || pictureControlSetPtr->ParentPcsPtr->lcuFlatNoiseArray[lcuIndex]) ? EB_TRUE : EB_FALSE; @@ -4084,6 +4096,29 @@ void* EncDecKernel(void *inputPtr) enableSaoFlag, contextPtr); + /*Entropy Estimation for LCU*/ + tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + EncodeLcu( + lcuPtr, + lcuOriginX, + lcuOriginY, + pictureControlSetPtr, + sequenceControlSetPtr->lcuSize, + pictureControlSetPtr->tempEntropyCoderPtr, + tempCoeffPicturePtr, + pictureControlSetPtr->tempModeTypeNeighborArray, + pictureControlSetPtr->tempLeafDepthNeighborArray, + pictureControlSetPtr->tempIntraLumaModeNeighborArray, + pictureControlSetPtr->tempSkipFlagNeighborArray, + 0, + 0); + tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + totalbits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; + if (pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr != NULL){ ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->intraCodedAreaLCU[lcuIndex] = (EB_U8)((100 * contextPtr->intraCodedAreaLCU[lcuIndex]) / (64 * 64)); } diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index 4327feb33..6e1595dbd 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -92,7 +92,13 @@ EB_ERRORTYPE PictureControlSetCtor( if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } - + //Proxy Entropy Coder to be used in Encdec + return_error = EntropyCoderCtor( + &objectPtr->tempEntropyCoderPtr, + SEGMENT_ENTROPY_BUFFER_SIZE); + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } // Cabaccost EB_MALLOC(CabacCost_t*, objectPtr->cabacCost, sizeof(CabacCost_t), EB_N_PTR); @@ -503,6 +509,19 @@ EB_ERRORTYPE PictureControlSetCtor( if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } + // Proxy Entropy Coding Neighbor Arrays to be used in Encdec + return_error = NeighborArrayUnitCtor( + &objectPtr->tempModeTypeNeighborArray, + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } return_error = NeighborArrayUnitCtor( &objectPtr->leafDepthNeighborArray, MAX_PICTURE_WIDTH_SIZE, @@ -515,6 +534,18 @@ EB_ERRORTYPE PictureControlSetCtor( if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } + return_error = NeighborArrayUnitCtor( + &objectPtr->tempLeafDepthNeighborArray, + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } return_error = NeighborArrayUnitCtor( &objectPtr->skipFlagNeighborArray, MAX_PICTURE_WIDTH_SIZE, @@ -527,7 +558,18 @@ EB_ERRORTYPE PictureControlSetCtor( if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } - + return_error = NeighborArrayUnitCtor( + &objectPtr->tempSkipFlagNeighborArray, + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } return_error = NeighborArrayUnitCtor( &objectPtr->intraLumaModeNeighborArray, MAX_PICTURE_WIDTH_SIZE, @@ -539,6 +581,17 @@ EB_ERRORTYPE PictureControlSetCtor( if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } + return_error = NeighborArrayUnitCtor( + &objectPtr->tempIntraLumaModeNeighborArray, + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } // Note - non-zero offsets are not supported (to be fixed later in DLF chroma filtering) objectPtr->cbQpOffset = 0; diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index 978a23a90..fe5bf59d2 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -140,6 +140,8 @@ typedef struct PictureControlSet_s EntropyCoder_t *entropyCoderPtr; + EntropyCoder_t *tempEntropyCoderPtr; + // Packetization (used to encode SPS, PPS, etc) Bitstream_t *bitstreamPtr; @@ -249,6 +251,11 @@ typedef struct PictureControlSet_s NeighborArrayUnit_t *intraLumaModeNeighborArray; NeighborArrayUnit_t *skipFlagNeighborArray; + NeighborArrayUnit_t *tempModeTypeNeighborArray; + NeighborArrayUnit_t *tempLeafDepthNeighborArray; + NeighborArrayUnit_t *tempIntraLumaModeNeighborArray; + NeighborArrayUnit_t *tempSkipFlagNeighborArray; + EB_REFLIST colocatedPuRefList; EB_BOOL isLowDelay; From 1397357ed3be27b534865ef417d05030401a68d1 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 11 Feb 2019 15:10:52 +0530 Subject: [PATCH 41/93] Create and initialize row stats structure --- Source/Lib/Codec/EbCodingUnit.c | 24 ++++++++++++++++++++++++ Source/Lib/Codec/EbCodingUnit.h | 20 ++++++++++++++++++++ Source/Lib/Codec/EbPictureControlSet.c | 11 +++++++++++ Source/Lib/Codec/EbPictureControlSet.h | 3 +++ 4 files changed, 58 insertions(+) diff --git a/Source/Lib/Codec/EbCodingUnit.c b/Source/Lib/Codec/EbCodingUnit.c index 751a41ac1..878f3b7d6 100644 --- a/Source/Lib/Codec/EbCodingUnit.c +++ b/Source/Lib/Codec/EbCodingUnit.c @@ -19,6 +19,30 @@ Tasks & Questions -Need a ReconPicture for each candidate. -I don't see a way around doing the copies in temp memory and then copying it in... */ +EB_ERRORTYPE RCStatRowCtor( + RCStatRow_t **rcStatRowDblPtr, + EB_U16 rowIndex) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + RCStatRow_t *rcStatRowPtr; + EB_MALLOC(RCStatRow_t*, rcStatRowPtr, sizeof(RCStatRow_t), EB_N_PTR); + *rcStatRowDblPtr = rcStatRowPtr; + rcStatRowPtr->rowIndex = rowIndex; + rcStatRowPtr->numEncodedCUs = 0; + rcStatRowPtr->distortionBitsForVbv = 0; + rcStatRowPtr->encodedBits = 0; + rcStatRowPtr->intradistortionBitsForVbv = 0; + rcStatRowPtr->rowDistortionBits = 0; + rcStatRowPtr->rowIntraDistortionBits = 0; + rcStatRowPtr->rowQp = 0; + rcStatRowPtr->sumQpRc = 0; + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } + + return EB_ErrorNone; +} + EB_ERRORTYPE LargestCodingUnitCtor( LargestCodingUnit_t **largetCodingUnitDblPtr, EB_U8 lcuSize, diff --git a/Source/Lib/Codec/EbCodingUnit.h b/Source/Lib/Codec/EbCodingUnit.h index 2d284b29e..3f040c23f 100644 --- a/Source/Lib/Codec/EbCodingUnit.h +++ b/Source/Lib/Codec/EbCodingUnit.h @@ -201,6 +201,26 @@ typedef struct LargestCodingUnit_s { +/************************************** + * Row level vbv + **************************************/ +typedef struct RCStatRow_s +{ + EB_U16 rowIndex; + EB_U32 numEncodedCUs; /* Addr of last encoded LCU in row */ + EB_U32 encodedBits; /* sum of 'totalBits' of encoded LCUs */ + EB_U32 distortionBitsForVbv; /* sum of lowres (estimated) costs for entire row */ + EB_U32 intradistortionBitsForVbv; /* sum of lowres (estimated) intra costs for entire row */ + EB_U32 rowDistortionBits; + EB_U32 rowIntraDistortionBits; + EB_U32 rowQp; + EB_U32 sumQpRc; + +}RCStatRow_t; + + +extern EB_ERRORTYPE RCStatRowCtor( +RCStatRow_t **rcStatRowDblPtr, EB_U16 rowIndex); extern EB_ERRORTYPE LargestCodingUnitCtor( diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index 6e1595dbd..9e01f580d 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -26,6 +26,7 @@ EB_ERRORTYPE PictureControlSetCtor( const EB_U16 pictureLcuWidth = (EB_U16)((initDataPtr->pictureWidth + initDataPtr->lcuSize - 1) / initDataPtr->lcuSize); const EB_U16 pictureLcuHeight = (EB_U16)((initDataPtr->pictureHeight + initDataPtr->lcuSize - 1) / initDataPtr->lcuSize); EB_U16 lcuIndex; + EB_U16 rowIndex; EB_U16 lcuOriginX; EB_U16 lcuOriginY; EB_ERRORTYPE return_error = EB_ErrorNone; @@ -150,6 +151,16 @@ EB_ERRORTYPE PictureControlSetCtor( lcuOriginX = (lcuOriginX == pictureLcuWidth - 1) ? 0 : lcuOriginX + 1; } + //Row stats Array + EB_MALLOC(RCStatRow_t**, objectPtr->rowStats, sizeof(RCStatRow_t*) * pictureLcuHeight, EB_N_PTR); + for (rowIndex = 0; rowIndex < pictureLcuHeight; ++rowIndex) + { + return_error = RCStatRowCtor( + &(objectPtr->rowStats[rowIndex]), (EB_U16)rowIndex); + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } + } // Mode Decision Control config EB_MALLOC(MdcLcuData_t*, objectPtr->mdcLcuArray, objectPtr->lcuTotalCount * sizeof(MdcLcuData_t), EB_N_PTR); objectPtr->qpArrayStride = (EB_U16)((initDataPtr->pictureWidth + MIN_CU_SIZE - 1) / MIN_CU_SIZE); diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index fe5bf59d2..e25cb13dd 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -285,6 +285,9 @@ typedef struct PictureControlSet_s EB_BOOL bdpPresentFlag; EB_BOOL mdPresentFlag; + //Row level vbv data + RCStatRow_t **rowStats; + } PictureControlSet_t; From 742a38eadfa3c64876c5ec2a9a7b5aecb2092a23 Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 12 Feb 2019 11:31:48 +0530 Subject: [PATCH 42/93] Add code to assign baseqp for each LCU --- Source/Lib/Codec/EbEncDecProcess.c | 21 +++++++++++++++++++++ Source/Lib/Codec/EbPictureControlSet.h | 1 + 2 files changed, 22 insertions(+) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 3f846a608..6729bf90d 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3805,6 +3805,7 @@ void* EncDecKernel(void *inputPtr) // LCU Loop variables LargestCodingUnit_t *lcuPtr; EB_U16 lcuIndex; + EB_U16 rowIndex; EB_U8 lcuSize; EB_U8 lcuSizeLog2; EB_U32 xLcuIndex; @@ -3816,7 +3817,11 @@ void* EncDecKernel(void *inputPtr) EB_U32 lcuRowIndexStart; EB_U32 lcuRowIndexCount; EB_U32 pictureWidthInLcu; + EB_U32 pictureHeightInLcu; MdcLcuData_t *mdcPtr; + + //Row level vbv controls + RCStatRow_t *rowPtr; // Variables EB_BOOL enableSaoFlag = EB_TRUE; EB_BOOL is16bit; @@ -3862,6 +3867,7 @@ void* EncDecKernel(void *inputPtr) lcuSizeLog2 = (EB_U8)Log2f(lcuSize); contextPtr->lcuSize = lcuSize; pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + lcuSize - 1) >> lcuSizeLog2; + pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + lcuSize - 1) >> lcuSizeLog2; endOfRowFlag = EB_FALSE; lcuRowIndexStart = lcuRowIndexCount = 0; contextPtr->totIntraCodedArea = 0; @@ -3947,6 +3953,9 @@ void* EncDecKernel(void *inputPtr) lcuIndex = (EB_U16)(yLcuIndex * pictureWidthInLcu + xLcuIndex); lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIndex]; + rowIndex = (EB_U16)(yLcuIndex / pictureHeightInLcu); + rowPtr = pictureControlSetPtr->rowStats[rowIndex]; + pictureControlSetPtr->firstRowOfPicture = (xLcuIndex <= pictureWidthInLcu) ? EB_TRUE : EB_FALSE; lcuOriginX = xLcuIndex << lcuSizeLog2; lcuOriginY = yLcuIndex << lcuSizeLog2; lastLcuFlag = (lcuIndex == pictureControlSetPtr->lcuTotalCount - 1) ? EB_TRUE : EB_FALSE; @@ -3963,6 +3972,18 @@ void* EncDecKernel(void *inputPtr) // Derive restrictIntraGlobalMotion Flag contextPtr->mdContext->restrictIntraGlobalMotion = ((pictureControlSetPtr->ParentPcsPtr->isPan || pictureControlSetPtr->ParentPcsPtr->isTilt) && pictureControlSetPtr->ParentPcsPtr->nonMovingIndexArray[lcuIndex] < INTRA_GLOBAL_MOTION_NON_MOVING_INDEX_TH && pictureControlSetPtr->ParentPcsPtr->yMean[lcuIndex][RASTER_SCAN_CU_INDEX_64x64] < INTRA_GLOBAL_MOTION_DARK_LCU_TH); + //Block level vbv tuning starts here + if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) + { + if (pictureControlSetPtr->firstRowOfPicture) + pictureControlSetPtr->rowStats[rowIndex]->rowQp=pictureControlSetPtr->pictureQp; + + //Assign the base qp for the LCU + if (xLcuIndex <= yLcuIndex && yLcuIndex) + lcuPtr->qp = pictureControlSetPtr->lcuPtrArray[lcuIndex - pictureWidthInLcu]->qp; + else + lcuPtr->qp = pictureControlSetPtr->rowStats[rowPtr->rowIndex]->rowQp; + } // Configure the LCU ModeDecisionConfigureLcu( // HT done contextPtr->mdContext, diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index e25cb13dd..efa8277fe 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -287,6 +287,7 @@ typedef struct PictureControlSet_s //Row level vbv data RCStatRow_t **rowStats; + EB_BOOL firstRowOfPicture; } PictureControlSet_t; From 6926341dc50569d6e66a8ad54be16acf256dd0ab Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 12 Feb 2019 13:00:08 +0530 Subject: [PATCH 43/93] Update rowstats based on the CU history --- Source/Lib/Codec/EbCodingUnit.h | 2 ++ Source/Lib/Codec/EbEncDecProcess.c | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Source/Lib/Codec/EbCodingUnit.h b/Source/Lib/Codec/EbCodingUnit.h index 3f040c23f..6fa581243 100644 --- a/Source/Lib/Codec/EbCodingUnit.h +++ b/Source/Lib/Codec/EbCodingUnit.h @@ -185,6 +185,8 @@ typedef struct LargestCodingUnit_s { //Bits only used for quantized coeffs EB_U32 quantizedCoeffsBits; EB_U32 totalBits; + EB_U32 proxytotalBits; + EB_U32 rowInd; // Quantized Coefficients EbPictureBufferDesc_t *quantizedCoeff; diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 6729bf90d..98cb54931 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3844,6 +3844,7 @@ void* EncDecKernel(void *inputPtr) EB_U32 totalbits = 0; EB_U32 tempWrittenBitsBeforeQuantizedCoeff; EB_U32 tempWrittenBitsAfterQuantizedCoeff; + EB_U32 bestOisCuIndex = 0; for (;;) { @@ -3983,6 +3984,25 @@ void* EncDecKernel(void *inputPtr) lcuPtr->qp = pictureControlSetPtr->lcuPtrArray[lcuIndex - pictureWidthInLcu]->qp; else lcuPtr->qp = pictureControlSetPtr->rowStats[rowPtr->rowIndex]->rowQp; + + //Update CU Stats for row level vbv control + rowPtr->rowDistortionBits += pictureControlSetPtr->ParentPcsPtr->rcMEdistortion[lcuIndex]; + rowPtr->rowIntraDistortionBits += pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[1][bestOisCuIndex].distortion + + + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[2][bestOisCuIndex].distortion + + + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[3][bestOisCuIndex].distortion + + + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[4][bestOisCuIndex].distortion; + + rowPtr->encodedBits += lcuPtr->proxytotalBits; + + // If current block is at row diagonal checkpoint, call vbv ratecontrol. + if (yLcuIndex == xLcuIndex && !pictureControlSetPtr->firstRowOfPicture) + { + + } + } // Configure the LCU ModeDecisionConfigureLcu( // HT done @@ -4138,8 +4158,8 @@ void* EncDecKernel(void *inputPtr) tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); - totalbits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; - + lcuPtr->proxytotalBits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; + rowPtr->numEncodedCUs = lcuPtr->rowInd; if (pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr != NULL){ ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->intraCodedAreaLCU[lcuIndex] = (EB_U8)((100 * contextPtr->intraCodedAreaLCU[lcuIndex]) / (64 * 64)); } From 1d6bce712d18d8a615114d35d2210b3cc80c249c Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 26 Feb 2019 23:29:50 +0530 Subject: [PATCH 44/93] Add code for Low level RC --- Source/Lib/Codec/EbEncDecProcess.c | 83 +++++++++++++++++++++++++ Source/Lib/Codec/EbPictureControlSet.h | 4 ++ Source/Lib/Codec/EbRateControlProcess.c | 15 +++++ 3 files changed, 102 insertions(+) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 98cb54931..980723874 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3782,6 +3782,86 @@ EB_ERRORTYPE SignalDerivationEncDecKernelVmaf( return return_error; } +EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, + SequenceControlSet_t *sequenceControlSetPtr, + RCStatRow_t *rowPtr, + EncodeContext_t *rcData, + EB_U8 qpVbv) +{ + /* tweak quality based on difference from predicted size */ + EB_U8 prevRowQp= qpVbv; + EB_U8 qpAbsoluteMax = sequenceControlSetPtr->staticConfig.maxQpAllowed; + EB_U8 qpAbsoluteMin = sequenceControlSetPtr->staticConfig.minQpAllowed; + EB_U8 lcuSizeLog2 = (EB_U8)Log2f(sequenceControlSetPtr->lcuSize); + EB_U8 pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; + EB_U8 pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; + + EB_U8 qpMax = MIN(prevRowQp + 4, qpAbsoluteMax); + EB_U8 qpMin = MAX(prevRowQp - 4, qpAbsoluteMin); + EB_U64 bufferLeftPlanned = rcData->bufferFill - pictureControlSetPtr->frameSizePlanned; + double maxFrameError = MAX(0.05, 1.0 / pictureHeightInLcu); + if (rowPtr->rowIndexframeSizePlanned)) + qpMax = qpAbsoluteMax = prevRowQp; + + if (pictureControlSetPtr->sliceType!= EB_I_PICTURE) + rcTol *= 0; + + qpMin = MAX(qpMin, pictureControlSetPtr->qpNoVbv); + + while (qpVbv < qpMax + && (((accFrameBits > pictureControlSetPtr->frameSizePlanned + rcTol) || + (rcData->bufferFill - accFrameBits < (EB_U64)(bufferLeftPlanned * 0.5)) || + (accFrameBits > pictureControlSetPtr->frameSizePlanned && qpVbv < pictureControlSetPtr->qpNoVbv) + ))) + { + qpVbv += 1; + accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, encodedBitsSoFar); + } + + while (qpVbv > qpMin + && (qpVbv > pictureControlSetPtr->rowStats[0]->rowQp ) + && (((accFrameBits < (EB_U64)(pictureControlSetPtr->frameSizePlanned * 0.8f) && qpVbv <= prevRowQp) + || accFrameBits < (EB_U64)((rcData->bufferFill - rcData->vbvBufsize + rcData->vbvMaxrate/pictureControlSetPtr->ParentPcsPtr->frameRate) * 1.1)) + )) + { + qpVbv -= 1; + accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, encodedBitsSoFar); + } + + /* avoid VBV underflow */ + while ((qpVbv < qpAbsoluteMax) + && (rcData->bufferFill - accFrameBits < (EB_U64)((rcData->vbvMaxrate / pictureControlSetPtr->ParentPcsPtr->frameRate) * maxFrameError)) + ) + { + qpVbv += 1; + accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, encodedBitsSoFar); + } + + pictureControlSetPtr->frameSizeEstimated = accFrameBits; + + /* If the current row was large enough to cause a large QP jump */ + if (qpVbv > qpMax && prevRowQp < qpMax) + { + /* Bump QP to halfway in between... close enough. */ + qpVbv = CLIP3(prevRowQp, qpMax, (EB_U8)((prevRowQp + qpVbv) * 0.5)); + } + + } + + return qpVbv; +} + /****************************************************** * EncDec Kernel ******************************************************/ @@ -3845,6 +3925,7 @@ void* EncDecKernel(void *inputPtr) EB_U32 tempWrittenBitsBeforeQuantizedCoeff; EB_U32 tempWrittenBitsAfterQuantizedCoeff; EB_U32 bestOisCuIndex = 0; + EB_U8 baseQp; for (;;) { @@ -4000,6 +4081,8 @@ void* EncDecKernel(void *inputPtr) // If current block is at row diagonal checkpoint, call vbv ratecontrol. if (yLcuIndex == xLcuIndex && !pictureControlSetPtr->firstRowOfPicture) { + baseQp=RowVbvRateControl(pictureControlSetPtr,sequenceControlSetPtr, rowPtr, sequenceControlSetPtr->encodeContextPtr, lcuPtr->qp); + lcuPtr->qp = CLIP3(sequenceControlSetPtr->staticConfig.minQpAllowed,sequenceControlSetPtr->staticConfig.maxQpAllowed, baseQp); } diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index efa8277fe..e2d05a498 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -289,6 +289,10 @@ typedef struct PictureControlSet_s RCStatRow_t **rowStats; EB_BOOL firstRowOfPicture; + EB_U64 frameSizePlanned; + EB_U64 frameSizeEstimated; + EB_U8 qpNoVbv; + } PictureControlSet_t; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 2b7533e01..b685f51d1 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2391,6 +2391,10 @@ void* RateControlKernel(void *inputPtr) EB_U32 bestOisCuIndex = 0; RATE_CONTROL_TASKTYPES taskType; + EB_U32 queueEntryIndexTemp2; + EB_U32 queueEntryIndexHeadTemp; + HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp; + for (;;) { @@ -2640,8 +2644,19 @@ void* RateControlKernel(void *inputPtr) if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) { EbBlockOnMutex(encodeContextPtr->bufferFillMutex); + pictureControlSetPtr->qpNoVbv = pictureControlSetPtr->pictureQp; pictureControlSetPtr->pictureQp = (EB_U8)Vbv_Buf_Calc(pictureControlSetPtr, sequenceControlSetPtr, encodeContextPtr); + queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - encodeContextPtr->hlRateControlHistorgramQueue[encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); + queueEntryIndexHeadTemp += encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; + queueEntryIndexHeadTemp = (queueEntryIndexHeadTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? + queueEntryIndexHeadTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : + queueEntryIndexHeadTemp; + + queueEntryIndexTemp2 = queueEntryIndexHeadTemp; + EB_U32 currentInd = (queueEntryIndexTemp2 > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp2 - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp2; + hlRateControlHistogramPtrTemp = encodeContextPtr->hlRateControlHistorgramQueue[currentInd]; + pictureControlSetPtr->frameSizePlanned = predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, pictureControlSetPtr->pictureQp); EbReleaseMutex(encodeContextPtr->bufferFillMutex); } pictureControlSetPtr->ParentPcsPtr->pictureQp = pictureControlSetPtr->pictureQp; From c97722032e48fac4869eb9f10f4c2c26f6c06e83 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 13 Feb 2019 17:29:19 +0530 Subject: [PATCH 45/93] Add code to estimate frame size at vbv checkpoints --- Source/Lib/Codec/EbCodingUnit.c | 8 +- Source/Lib/Codec/EbCodingUnit.h | 11 ++- Source/Lib/Codec/EbEncDecProcess.c | 153 +++++++++++++++++++++++++---- 3 files changed, 147 insertions(+), 25 deletions(-) diff --git a/Source/Lib/Codec/EbCodingUnit.c b/Source/Lib/Codec/EbCodingUnit.c index 878f3b7d6..cba910195 100644 --- a/Source/Lib/Codec/EbCodingUnit.c +++ b/Source/Lib/Codec/EbCodingUnit.c @@ -29,11 +29,11 @@ EB_ERRORTYPE RCStatRowCtor( *rcStatRowDblPtr = rcStatRowPtr; rcStatRowPtr->rowIndex = rowIndex; rcStatRowPtr->numEncodedCUs = 0; - rcStatRowPtr->distortionBitsForVbv = 0; + rcStatRowPtr->distortionDataForVbv = 0; rcStatRowPtr->encodedBits = 0; - rcStatRowPtr->intradistortionBitsForVbv = 0; - rcStatRowPtr->rowDistortionBits = 0; - rcStatRowPtr->rowIntraDistortionBits = 0; + rcStatRowPtr->intradistortionDataForVbv = 0; + rcStatRowPtr->rowDistortion = 0; + rcStatRowPtr->rowIntraDistortion = 0; rcStatRowPtr->rowQp = 0; rcStatRowPtr->sumQpRc = 0; if (return_error == EB_ErrorInsufficientResources) { diff --git a/Source/Lib/Codec/EbCodingUnit.h b/Source/Lib/Codec/EbCodingUnit.h index 6fa581243..a5acf5d98 100644 --- a/Source/Lib/Codec/EbCodingUnit.h +++ b/Source/Lib/Codec/EbCodingUnit.h @@ -187,6 +187,9 @@ typedef struct LargestCodingUnit_s { EB_U32 totalBits; EB_U32 proxytotalBits; EB_U32 rowInd; + EB_U32 intraSadBits; + EB_U32 interSadBits; + EB_U32 sadVbv; // Quantized Coefficients EbPictureBufferDesc_t *quantizedCoeff; @@ -211,10 +214,10 @@ typedef struct RCStatRow_s EB_U16 rowIndex; EB_U32 numEncodedCUs; /* Addr of last encoded LCU in row */ EB_U32 encodedBits; /* sum of 'totalBits' of encoded LCUs */ - EB_U32 distortionBitsForVbv; /* sum of lowres (estimated) costs for entire row */ - EB_U32 intradistortionBitsForVbv; /* sum of lowres (estimated) intra costs for entire row */ - EB_U32 rowDistortionBits; - EB_U32 rowIntraDistortionBits; + EB_U32 distortionDataForVbv; /* sum of lowres (estimated) costs for entire row */ + EB_U32 intradistortionDataForVbv; /* sum of lowres (estimated) intra costs for entire row */ + EB_U32 rowDistortion; + EB_U32 rowIntraDistortion; EB_U32 rowQp; EB_U32 sumQpRc; diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 980723874..45324b44b 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3782,7 +3782,133 @@ EB_ERRORTYPE SignalDerivationEncDecKernelVmaf( return return_error; } -EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, +EB_U64 predBits(PictureControlSet_t* pictureControlSetPtr, SequenceControlSet_t* sequenceControlSetPtr, EB_U64 distortion, EB_U8 qpVbv, EB_BOOL useIntraSadFlag) +{ + EB_U16 sadIntervalIndex = 0; + EB_U64 sadBits = 0; + sadIntervalIndex = (EB_U16)(distortion >> (12 - SAD_PRECISION_INTERVAL));//change 12 to 2*log2(64) + + sadIntervalIndex = (EB_U16)(sadIntervalIndex >> 2); + if (sadIntervalIndex > (NUMBER_OF_SAD_INTERVALS >> 1) - 1) { + EB_U16 sadIntervalIndexTemp = sadIntervalIndex - ((NUMBER_OF_SAD_INTERVALS >> 1) - 1); + + sadIntervalIndex = ((NUMBER_OF_SAD_INTERVALS >> 1) - 1) + (sadIntervalIndexTemp >> 3); + + } + RateControlTables_t *rateControlTablesPtr = &(sequenceControlSetPtr->encodeContextPtr)->rateControlTablesArray[qpVbv]; + EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[pictureControlSetPtr->temporalLayerIndex]; + EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; + + if (useIntraSadFlag) + sadBits = intraSadBitsArrayPtr[sadIntervalIndex]; + else + sadBits = sadBitsArrayPtr[sadIntervalIndex]; + + return sadBits; +} + +EB_U64 predictBitsDup(SequenceControlSet_t *sequenceControlSetPtr, PictureControlSet_t* pictureControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp,EB_U32 qp) +{ + EB_U64 totalBits = 0; + RateControlTables_t *rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qp]; + EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[hlRateControlHistogramPtrTemp->temporalLayerIndex]; + EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; + EB_U32 predBitsRefQp = 0; + EB_U32 numOfFullLcus = 0; + EB_U32 areaInPixel = sequenceControlSetPtr->lumaWidth * sequenceControlSetPtr->lumaHeight; + + if (hlRateControlHistogramPtrTemp->sliceType == EB_I_PICTURE) { + // Loop over block in the frame and calculated the predicted bits at reg QP + EB_U32 i; + EB_U32 accum = 0; + for (i = 0; i < NUMBER_OF_INTRA_SAD_INTERVALS; ++i) + { + accum += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); + } + + predBitsRefQp = accum; + numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; + totalBits += predBitsRefQp; + } + else { + EB_U32 i; + EB_U32 accum = 0; + EB_U32 accumIntra = 0; + for (i = 0; i < NUMBER_OF_SAD_INTERVALS; ++i) + { + accum += (EB_U32)(hlRateControlHistogramPtrTemp->meDistortionHistogram[i] * sadBitsArrayPtr[i]); + accumIntra += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); + + } + if (accum > accumIntra * 3) + predBitsRefQp = accumIntra; + else + predBitsRefQp = accum; + numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; + totalBits += predBitsRefQp; + } + + // Scale for in complete LCSs + // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries + totalBits = totalBits * (EB_U64)areaInPixel / (numOfFullLcus << 12); + return totalBits; +} + +EB_U64 predictRowsSizeSum(PictureControlSet_t* pictureControlSetPtr, SequenceControlSet_t* sequenceControlSetPtr, EB_U8 qpVbv, EB_U64 *encodedBitsSoFar) +{ + EB_U64 predictedBitsForFrame = 0; + EB_U64 predictedBitsDoneSoFar = 0; + EB_U64 distortionBitsSoFar = 0; + *encodedBitsSoFar = 0; + EB_U64 distortionSofar = 0; + EB_U64 distortionSofarIntra = 0; + EB_U64 distortionSofarInter = 0; + EB_U32 queueEntryIndexTemp; + EB_U32 queueEntryIndexHeadTemp; + HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp; + EB_BOOL useIntraSadFlag = EB_TRUE; + EB_U8 lcuSizeLog2 = (EB_U8)Log2f(sequenceControlSetPtr->lcuSize); + EB_U8 pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; + EB_U8 pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; + queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueue[sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); + queueEntryIndexHeadTemp += sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; + queueEntryIndexHeadTemp = (queueEntryIndexHeadTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? + queueEntryIndexHeadTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : + queueEntryIndexHeadTemp; + + queueEntryIndexTemp = queueEntryIndexHeadTemp; + + + EB_U32 currentInd = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; + hlRateControlHistogramPtrTemp = (sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueue[currentInd]); + for (EB_U8 row = 0; row < pictureHeightInLcu; row++) + { + *encodedBitsSoFar += pictureControlSetPtr->rowStats[row]->encodedBits; + if (pictureControlSetPtr->sliceType == EB_I_PICTURE) + distortionSofarIntra += pictureControlSetPtr->rowStats[row]->rowIntraDistortion; + else + { + distortionSofarIntra += pictureControlSetPtr->rowStats[row]->rowIntraDistortion; + distortionSofarInter += pictureControlSetPtr->rowStats[row]->rowDistortion; + } + } + if ((pictureControlSetPtr->sliceType != EB_I_PICTURE) + && distortionSofarInter <= distortionSofarIntra * 3) + { + distortionSofar = distortionSofarInter; + useIntraSadFlag = EB_FALSE; + } + else + { + distortionSofar = distortionSofarIntra; + } + predictedBitsDoneSoFar = predBits(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, distortionSofar, useIntraSadFlag); + predictedBitsForFrame = predictBitsDup(sequenceControlSetPtr, pictureControlSetPtr, sequenceControlSetPtr->encodeContextPtr, hlRateControlHistogramPtrTemp, qpVbv); + EB_U64 framesizeEstimated = (predictedBitsForFrame - predictedBitsDoneSoFar) + (*encodedBitsSoFar); + return framesizeEstimated; +} + +EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,///mutex to be added since Buffer fill is read SequenceControlSet_t *sequenceControlSetPtr, RCStatRow_t *rowPtr, EncodeContext_t *rcData, @@ -3803,9 +3929,10 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, if (rowPtr->rowIndexframeNumThreads * m_rateTolerance; EB_U64 encodedBitsSoFar = 0; - EB_U64 accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, encodedBitsSoFar); + EB_U64 accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); /* * Don't increase the row QPs until a sufficent amount of the bits of @@ -3826,7 +3953,7 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, ))) { qpVbv += 1; - accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, encodedBitsSoFar); + accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); } while (qpVbv > qpMin @@ -3836,16 +3963,7 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, )) { qpVbv -= 1; - accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, encodedBitsSoFar); - } - - /* avoid VBV underflow */ - while ((qpVbv < qpAbsoluteMax) - && (rcData->bufferFill - accFrameBits < (EB_U64)((rcData->vbvMaxrate / pictureControlSetPtr->ParentPcsPtr->frameRate) * maxFrameError)) - ) - { - qpVbv += 1; - accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, encodedBitsSoFar); + accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); } pictureControlSetPtr->frameSizeEstimated = accFrameBits; @@ -4067,15 +4185,16 @@ void* EncDecKernel(void *inputPtr) lcuPtr->qp = pictureControlSetPtr->rowStats[rowPtr->rowIndex]->rowQp; //Update CU Stats for row level vbv control - rowPtr->rowDistortionBits += pictureControlSetPtr->ParentPcsPtr->rcMEdistortion[lcuIndex]; - rowPtr->rowIntraDistortionBits += pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[1][bestOisCuIndex].distortion + + //predBitsLCU(pictureControlSetPtr, lcuIndex); + rowPtr->rowIntraDistortion += pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[1][bestOisCuIndex].distortion + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[2][bestOisCuIndex].distortion + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[3][bestOisCuIndex].distortion + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[4][bestOisCuIndex].distortion; - + if (pictureControlSetPtr->sliceType != EB_I_PICTURE) + rowPtr->rowDistortion += pictureControlSetPtr->ParentPcsPtr->rcMEdistortion[lcuIndex]; rowPtr->encodedBits += lcuPtr->proxytotalBits; // If current block is at row diagonal checkpoint, call vbv ratecontrol. From 9234146944c56ba9c6e9b28ef54ca4b972d9bad7 Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Fri, 1 Mar 2019 10:55:41 +0530 Subject: [PATCH 46/93] Code clean up & removed warnings --- Source/Lib/Codec/EbCodingUnit.c | 1 + Source/Lib/Codec/EbEncDecProcess.c | 56 ++++++++++++++++-------------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/Source/Lib/Codec/EbCodingUnit.c b/Source/Lib/Codec/EbCodingUnit.c index cba910195..38386e3cc 100644 --- a/Source/Lib/Codec/EbCodingUnit.c +++ b/Source/Lib/Codec/EbCodingUnit.c @@ -9,6 +9,7 @@ #include "EbUtility.h" #include "EbTransformUnit.h" #include "EbPictureControlSet.h" +#include "EbThreads.h" /* Tasks & Questions diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 45324b44b..0830725a0 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3782,7 +3782,7 @@ EB_ERRORTYPE SignalDerivationEncDecKernelVmaf( return return_error; } -EB_U64 predBits(PictureControlSet_t* pictureControlSetPtr, SequenceControlSet_t* sequenceControlSetPtr, EB_U64 distortion, EB_U8 qpVbv, EB_BOOL useIntraSadFlag) +EB_U64 predBits(PictureControlSet_t* pictureControlSetPtr, SequenceControlSet_t* sequenceControlSetPtr, EB_U8 qpVbv, EB_U64 distortion, EB_BOOL useIntraSadFlag) { EB_U16 sadIntervalIndex = 0; EB_U64 sadBits = 0; @@ -4202,7 +4202,7 @@ void* EncDecKernel(void *inputPtr) { baseQp=RowVbvRateControl(pictureControlSetPtr,sequenceControlSetPtr, rowPtr, sequenceControlSetPtr->encodeContextPtr, lcuPtr->qp); lcuPtr->qp = CLIP3(sequenceControlSetPtr->staticConfig.minQpAllowed,sequenceControlSetPtr->staticConfig.maxQpAllowed, baseQp); - + rowPtr->rowQp = lcuPtr->qp; } } @@ -4338,30 +4338,34 @@ void* EncDecKernel(void *inputPtr) lcuPtr->qp, enableSaoFlag, contextPtr); - - /*Entropy Estimation for LCU*/ - tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); - EncodeLcu( - lcuPtr, - lcuOriginX, - lcuOriginY, - pictureControlSetPtr, - sequenceControlSetPtr->lcuSize, - pictureControlSetPtr->tempEntropyCoderPtr, - tempCoeffPicturePtr, - pictureControlSetPtr->tempModeTypeNeighborArray, - pictureControlSetPtr->tempLeafDepthNeighborArray, - pictureControlSetPtr->tempIntraLumaModeNeighborArray, - pictureControlSetPtr->tempSkipFlagNeighborArray, - 0, - 0); - tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); - lcuPtr->proxytotalBits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; - rowPtr->numEncodedCUs = lcuPtr->rowInd; + if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) + { + /*Entropy Estimation for LCU*/ + tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + EncodeLcu( + lcuPtr, + lcuOriginX, + lcuOriginY, + pictureControlSetPtr, + sequenceControlSetPtr->lcuSize, + pictureControlSetPtr->tempEntropyCoderPtr, + tempCoeffPicturePtr, + pictureControlSetPtr->tempModeTypeNeighborArray, + pictureControlSetPtr->tempLeafDepthNeighborArray, + pictureControlSetPtr->tempIntraLumaModeNeighborArray, + pictureControlSetPtr->tempSkipFlagNeighborArray, + 0, + 0); + + tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + lcuPtr->proxytotalBits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; + //Update CU Stats for row level vbv control + rowPtr->numEncodedCUs = lcuPtr->rowInd; + } if (pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr != NULL){ ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->intraCodedAreaLCU[lcuIndex] = (EB_U8)((100 * contextPtr->intraCodedAreaLCU[lcuIndex]) / (64 * 64)); } From 0560e9cf4d444499c286892f0db1ec2f3ef4a44c Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 11 Mar 2019 16:32:07 +0530 Subject: [PATCH 47/93] Add code to predict bits for each LCU --- Source/Lib/Codec/EbCodingUnit.c | 11 ++- Source/Lib/Codec/EbCodingUnit.h | 14 ++-- Source/Lib/Codec/EbEncDecProcess.c | 109 +++++++++++++++++------------ 3 files changed, 73 insertions(+), 61 deletions(-) diff --git a/Source/Lib/Codec/EbCodingUnit.c b/Source/Lib/Codec/EbCodingUnit.c index 38386e3cc..d176cb906 100644 --- a/Source/Lib/Codec/EbCodingUnit.c +++ b/Source/Lib/Codec/EbCodingUnit.c @@ -30,13 +30,11 @@ EB_ERRORTYPE RCStatRowCtor( *rcStatRowDblPtr = rcStatRowPtr; rcStatRowPtr->rowIndex = rowIndex; rcStatRowPtr->numEncodedCUs = 0; - rcStatRowPtr->distortionDataForVbv = 0; + rcStatRowPtr->predictedBits = 0; rcStatRowPtr->encodedBits = 0; - rcStatRowPtr->intradistortionDataForVbv = 0; - rcStatRowPtr->rowDistortion = 0; - rcStatRowPtr->rowIntraDistortion = 0; rcStatRowPtr->rowQp = 0; - rcStatRowPtr->sumQpRc = 0; + rcStatRowPtr->totalCUEncoded = 0; + EB_CREATEMUTEX(EB_HANDLE, rcStatRowPtr->rowUpdateMutex, sizeof(EB_HANDLE), EB_MUTEX); if (return_error == EB_ErrorInsufficientResources) { return EB_ErrorInsufficientResources; } @@ -89,7 +87,8 @@ EB_ERRORTYPE LargestCodingUnitCtor( largestCodingUnitPtr->originY = lcuOriginY; largestCodingUnitPtr->index = lcuIndex; - + largestCodingUnitPtr->proxytotalBits = 0; + largestCodingUnitPtr->rowInd = 0; EB_MALLOC(CodingUnit_t**, largestCodingUnitPtr->codedLeafArrayPtr, sizeof(CodingUnit_t*) * CU_MAX_COUNT, EB_N_PTR); for(codedLeafIndex=0; codedLeafIndex < CU_MAX_COUNT; ++codedLeafIndex) { EB_MALLOC(CodingUnit_t*, largestCodingUnitPtr->codedLeafArrayPtr[codedLeafIndex], sizeof(CodingUnit_t) , EB_N_PTR); diff --git a/Source/Lib/Codec/EbCodingUnit.h b/Source/Lib/Codec/EbCodingUnit.h index a5acf5d98..89e40745e 100644 --- a/Source/Lib/Codec/EbCodingUnit.h +++ b/Source/Lib/Codec/EbCodingUnit.h @@ -187,9 +187,8 @@ typedef struct LargestCodingUnit_s { EB_U32 totalBits; EB_U32 proxytotalBits; EB_U32 rowInd; - EB_U32 intraSadBits; - EB_U32 interSadBits; - EB_U32 sadVbv; + EB_U32 intraDistortion; + EB_U32 interDistortion; // Quantized Coefficients EbPictureBufferDesc_t *quantizedCoeff; @@ -214,13 +213,10 @@ typedef struct RCStatRow_s EB_U16 rowIndex; EB_U32 numEncodedCUs; /* Addr of last encoded LCU in row */ EB_U32 encodedBits; /* sum of 'totalBits' of encoded LCUs */ - EB_U32 distortionDataForVbv; /* sum of lowres (estimated) costs for entire row */ - EB_U32 intradistortionDataForVbv; /* sum of lowres (estimated) intra costs for entire row */ - EB_U32 rowDistortion; - EB_U32 rowIntraDistortion; + EB_U32 predictedBits; EB_U32 rowQp; - EB_U32 sumQpRc; - + EB_U32 totalCUEncoded; /*Tracks number of LCUs encoded in each row*/ + EB_HANDLE rowUpdateMutex; }RCStatRow_t; diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 0830725a0..ccd34574e 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3782,28 +3782,54 @@ EB_ERRORTYPE SignalDerivationEncDecKernelVmaf( return return_error; } -EB_U64 predBits(PictureControlSet_t* pictureControlSetPtr, SequenceControlSet_t* sequenceControlSetPtr, EB_U8 qpVbv, EB_U64 distortion, EB_BOOL useIntraSadFlag) +EB_U32 predBitsPerLcu(PictureControlSet_t* pictureControlSetPtr, EncodeContext_t* encodeContextPtr, LargestCodingUnit_t* lcuPtr, EB_U8 qpVbv) { EB_U16 sadIntervalIndex = 0; - EB_U64 sadBits = 0; - sadIntervalIndex = (EB_U16)(distortion >> (12 - SAD_PRECISION_INTERVAL));//change 12 to 2*log2(64) + EB_U16 intraSadIntervalIndex = 0; + EB_U32 sadBits; + EB_U32 intraSadBits; + EB_U32 interSadBits; + RateControlTables_t *rateControlTablesPtr; + EB_Bit_Number *sadBitsArrayPtr; + EB_Bit_Number *intraSadBitsArrayPtr; + intraSadIntervalIndex = (EB_U16)(lcuPtr->intraDistortion >> (12 - SAD_PRECISION_INTERVAL));//change 12 to 2*log2(64) + + intraSadIntervalIndex = (EB_U16)(intraSadIntervalIndex >> 2); + if (intraSadIntervalIndex > (NUMBER_OF_SAD_INTERVALS >> 1) - 1) { + EB_U16 intraSadIntervalIndexTemp = intraSadIntervalIndex - ((NUMBER_OF_SAD_INTERVALS >> 1) - 1); + + intraSadIntervalIndex = ((NUMBER_OF_SAD_INTERVALS >> 1) - 1) + (intraSadIntervalIndexTemp >> 3); + } + if (pictureControlSetPtr->sliceType != EB_I_PICTURE) + { + sadIntervalIndex = (EB_U16)(lcuPtr->interDistortion >> (12 - SAD_PRECISION_INTERVAL));//change 12 to 2*log2(64) - sadIntervalIndex = (EB_U16)(sadIntervalIndex >> 2); - if (sadIntervalIndex > (NUMBER_OF_SAD_INTERVALS >> 1) - 1) { - EB_U16 sadIntervalIndexTemp = sadIntervalIndex - ((NUMBER_OF_SAD_INTERVALS >> 1) - 1); + sadIntervalIndex = (EB_U16)(sadIntervalIndex >> 2); + if (sadIntervalIndex > (NUMBER_OF_SAD_INTERVALS >> 1) - 1) { + EB_U16 sadIntervalIndexTemp = sadIntervalIndex - ((NUMBER_OF_SAD_INTERVALS >> 1) - 1); - sadIntervalIndex = ((NUMBER_OF_SAD_INTERVALS >> 1) - 1) + (sadIntervalIndexTemp >> 3); + sadIntervalIndex = ((NUMBER_OF_SAD_INTERVALS >> 1) - 1) + (sadIntervalIndexTemp >> 3); - } - RateControlTables_t *rateControlTablesPtr = &(sequenceControlSetPtr->encodeContextPtr)->rateControlTablesArray[qpVbv]; - EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[pictureControlSetPtr->temporalLayerIndex]; - EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; + } + } + rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qpVbv]; + sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[pictureControlSetPtr->temporalLayerIndex]; + intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; - if (useIntraSadFlag) - sadBits = intraSadBitsArrayPtr[sadIntervalIndex]; + if (pictureControlSetPtr->sliceType == EB_I_PICTURE) + { + intraSadBits = intraSadBitsArrayPtr[intraSadIntervalIndex]; + interSadBits = 0; + sadBits = intraSadBits; + } else - sadBits = sadBitsArrayPtr[sadIntervalIndex]; - + { + intraSadBits = intraSadBitsArrayPtr[intraSadIntervalIndex]; + interSadBits = sadBitsArrayPtr[intraSadIntervalIndex]; + sadBits = interSadBits; + if (interSadBits > (intraSadBits * 3)) + sadBits = intraSadBits; + } return sadBits; } @@ -3883,26 +3909,14 @@ EB_U64 predictRowsSizeSum(PictureControlSet_t* pictureControlSetPtr, SequenceCon hlRateControlHistogramPtrTemp = (sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueue[currentInd]); for (EB_U8 row = 0; row < pictureHeightInLcu; row++) { - *encodedBitsSoFar += pictureControlSetPtr->rowStats[row]->encodedBits; - if (pictureControlSetPtr->sliceType == EB_I_PICTURE) - distortionSofarIntra += pictureControlSetPtr->rowStats[row]->rowIntraDistortion; - else + pictureControlSetPtr->rowStats[row]->predictedBits = 0; + for (EB_U8 col = 0; col < pictureControlSetPtr->rowStats[row]->totalCUEncoded; col++) { - distortionSofarIntra += pictureControlSetPtr->rowStats[row]->rowIntraDistortion; - distortionSofarInter += pictureControlSetPtr->rowStats[row]->rowDistortion; + pictureControlSetPtr->rowStats[row]->predictedBits += predBitsPerLcu(pictureControlSetPtr, sequenceControlSetPtr->encodeContextPtr,pictureControlSetPtr->lcuPtrArray[row*pictureWidthInLcu+col],qpVbv); } + *encodedBitsSoFar += pictureControlSetPtr->rowStats[row]->encodedBits; + predictedBitsDoneSoFar += pictureControlSetPtr->rowStats[row]->predictedBits; } - if ((pictureControlSetPtr->sliceType != EB_I_PICTURE) - && distortionSofarInter <= distortionSofarIntra * 3) - { - distortionSofar = distortionSofarInter; - useIntraSadFlag = EB_FALSE; - } - else - { - distortionSofar = distortionSofarIntra; - } - predictedBitsDoneSoFar = predBits(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, distortionSofar, useIntraSadFlag); predictedBitsForFrame = predictBitsDup(sequenceControlSetPtr, pictureControlSetPtr, sequenceControlSetPtr->encodeContextPtr, hlRateControlHistogramPtrTemp, qpVbv); EB_U64 framesizeEstimated = (predictedBitsForFrame - predictedBitsDoneSoFar) + (*encodedBitsSoFar); return framesizeEstimated; @@ -4149,6 +4163,9 @@ void* EncDecKernel(void *inputPtr) } for (yLcuIndex = yLcuStartIndex, lcuSegmentIndex = lcuStartIndex; lcuSegmentIndex < lcuStartIndex + lcuSegmentCount; ++yLcuIndex) { + pictureControlSetPtr->rowStats[yLcuIndex]->totalCUEncoded=0; + pictureControlSetPtr->rowStats[yLcuIndex]->encodedBits = 0; + pictureControlSetPtr->rowStats[yLcuIndex]->numEncodedCUs = 0; for (xLcuIndex = xLcuStartIndex; xLcuIndex < pictureWidthInLcu && (xLcuIndex + yLcuIndex < segmentBandSize) && lcuSegmentIndex < lcuStartIndex + lcuSegmentCount; ++xLcuIndex, ++lcuSegmentIndex) { lcuIndex = (EB_U16)(yLcuIndex * pictureWidthInLcu + xLcuIndex); @@ -4184,19 +4201,6 @@ void* EncDecKernel(void *inputPtr) else lcuPtr->qp = pictureControlSetPtr->rowStats[rowPtr->rowIndex]->rowQp; - //Update CU Stats for row level vbv control - //predBitsLCU(pictureControlSetPtr, lcuIndex); - rowPtr->rowIntraDistortion += pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[1][bestOisCuIndex].distortion + - - pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[2][bestOisCuIndex].distortion + - - pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[3][bestOisCuIndex].distortion + - - pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[4][bestOisCuIndex].distortion; - if (pictureControlSetPtr->sliceType != EB_I_PICTURE) - rowPtr->rowDistortion += pictureControlSetPtr->ParentPcsPtr->rcMEdistortion[lcuIndex]; - rowPtr->encodedBits += lcuPtr->proxytotalBits; - // If current block is at row diagonal checkpoint, call vbv ratecontrol. if (yLcuIndex == xLcuIndex && !pictureControlSetPtr->firstRowOfPicture) { @@ -4364,7 +4368,20 @@ void* EncDecKernel(void *inputPtr) (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); lcuPtr->proxytotalBits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; //Update CU Stats for row level vbv control - rowPtr->numEncodedCUs = lcuPtr->rowInd; + EbBlockOnMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); + lcuPtr->intraDistortion = pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[1][bestOisCuIndex].distortion + + + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[2][bestOisCuIndex].distortion + + + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[3][bestOisCuIndex].distortion + + + pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[4][bestOisCuIndex].distortion; + if (pictureControlSetPtr->sliceType != EB_I_PICTURE) + lcuPtr->interDistortion = pictureControlSetPtr->ParentPcsPtr->rcMEdistortion[lcuIndex]; + pictureControlSetPtr->rowStats[yLcuIndex]->encodedBits += lcuPtr->proxytotalBits; + pictureControlSetPtr->rowStats[yLcuIndex]->totalCUEncoded++; + pictureControlSetPtr->rowStats[yLcuIndex]->numEncodedCUs = lcuPtr->index; + EbReleaseMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); } if (pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr != NULL){ ((EbReferenceObject_t*)pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr->objectPtr)->intraCodedAreaLCU[lcuIndex] = (EB_U8)((100 * contextPtr->intraCodedAreaLCU[lcuIndex]) / (64 * 64)); From 970311804f7b722f21498412b00e67dab78dfc49 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 22 Apr 2019 16:32:05 +0530 Subject: [PATCH 48/93] Fix Buffer Fill based on the lookahead buffer Fill for the current frame --- Source/Lib/Codec/EbEncDecProcess.c | 6 +++--- Source/Lib/Codec/EbPictureControlSet.h | 1 + Source/Lib/Codec/EbRateControlProcess.c | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index ccd34574e..4f52ab024 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3938,7 +3938,7 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,///mutex to EB_U8 qpMax = MIN(prevRowQp + 4, qpAbsoluteMax); EB_U8 qpMin = MAX(prevRowQp - 4, qpAbsoluteMin); - EB_U64 bufferLeftPlanned = rcData->bufferFill - pictureControlSetPtr->frameSizePlanned; + EB_U64 bufferLeftPlanned = pictureControlSetPtr->bufferFillPerFrame - pictureControlSetPtr->frameSizePlanned; double maxFrameError = MAX(0.05, 1.0 / pictureHeightInLcu); if (rowPtr->rowIndex pictureControlSetPtr->frameSizePlanned + rcTol) || - (rcData->bufferFill - accFrameBits < (EB_U64)(bufferLeftPlanned * 0.5)) || + (pictureControlSetPtr->bufferFillPerFrame - accFrameBits < (EB_U64)(bufferLeftPlanned * 0.5)) || (accFrameBits > pictureControlSetPtr->frameSizePlanned && qpVbv < pictureControlSetPtr->qpNoVbv) ))) { @@ -3973,7 +3973,7 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,///mutex to while (qpVbv > qpMin && (qpVbv > pictureControlSetPtr->rowStats[0]->rowQp ) && (((accFrameBits < (EB_U64)(pictureControlSetPtr->frameSizePlanned * 0.8f) && qpVbv <= prevRowQp) - || accFrameBits < (EB_U64)((rcData->bufferFill - rcData->vbvBufsize + rcData->vbvMaxrate/pictureControlSetPtr->ParentPcsPtr->frameRate) * 1.1)) + || accFrameBits < (EB_U64)((pictureControlSetPtr->bufferFillPerFrame - rcData->vbvBufsize + rcData->vbvMaxrate/pictureControlSetPtr->ParentPcsPtr->frameRate) * 1.1)) )) { qpVbv -= 1; diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index e2d05a498..c899ef804 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -291,6 +291,7 @@ typedef struct PictureControlSet_s EB_U64 frameSizePlanned; EB_U64 frameSizeEstimated; + EB_U64 bufferFillPerFrame; EB_U8 qpNoVbv; } PictureControlSet_t; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index b685f51d1..fe281e5c0 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2645,6 +2645,7 @@ void* RateControlKernel(void *inputPtr) { EbBlockOnMutex(encodeContextPtr->bufferFillMutex); pictureControlSetPtr->qpNoVbv = pictureControlSetPtr->pictureQp; + pictureControlSetPtr->bufferFillPerFrame = encodeContextPtr->bufferFill; pictureControlSetPtr->pictureQp = (EB_U8)Vbv_Buf_Calc(pictureControlSetPtr, sequenceControlSetPtr, encodeContextPtr); queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - encodeContextPtr->hlRateControlHistorgramQueue[encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); queueEntryIndexHeadTemp += encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; From 01d52a409f9bda7a7c02f2645e1abe061fb89189 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 22 Apr 2019 18:30:47 +0530 Subject: [PATCH 49/93] Fix exception due to null frame rate --- Source/Lib/Codec/EbEncDecProcess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 4f52ab024..b075e2008 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3973,7 +3973,7 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,///mutex to while (qpVbv > qpMin && (qpVbv > pictureControlSetPtr->rowStats[0]->rowQp ) && (((accFrameBits < (EB_U64)(pictureControlSetPtr->frameSizePlanned * 0.8f) && qpVbv <= prevRowQp) - || accFrameBits < (EB_U64)((pictureControlSetPtr->bufferFillPerFrame - rcData->vbvBufsize + rcData->vbvMaxrate/pictureControlSetPtr->ParentPcsPtr->frameRate) * 1.1)) + || accFrameBits < (EB_U64)((pictureControlSetPtr->bufferFillPerFrame - rcData->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16)) * 1.1)) )) { qpVbv -= 1; From 501e5a4399ab25bc9a094730d0d26d9f39a4a229 Mon Sep 17 00:00:00 2001 From: anaghdin Date: Tue, 12 Mar 2019 16:36:58 -0700 Subject: [PATCH 50/93] Move the low-level QP calculation after MD Fix the confromant part for signaling QPs Create EstimateLcu and used in instead of EncodeLcu in VBV calculation ( // the function was overwriting the values of pictureControlSetPtr->prevCodedQp which are used in EC) --- Source/Lib/Codec/EbCodingLoop.c | 35 +- Source/Lib/Codec/EbEncDecProcess.c | 48 ++- Source/Lib/Codec/EbEntropyCoding.c | 461 ++++++++++++++++++++- Source/Lib/Codec/EbEntropyCoding.h | 15 + Source/Lib/Codec/EbPictureManagerProcess.c | 2 +- 5 files changed, 520 insertions(+), 41 deletions(-) diff --git a/Source/Lib/Codec/EbCodingLoop.c b/Source/Lib/Codec/EbCodingLoop.c index d998a4a2d..c6cfc4bc5 100644 --- a/Source/Lib/Codec/EbCodingLoop.c +++ b/Source/Lib/Codec/EbCodingLoop.c @@ -2954,13 +2954,16 @@ EB_EXTERN void EncodePass( } } } - - QpmDeriveBeaAndSkipQpmFlagLcu( - sequenceControlSetPtr, - pictureControlSetPtr, - lcuPtr, - tbAddr, - contextPtr); + if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) { + contextPtr->skipQpmFlag = EB_TRUE; + } + else + QpmDeriveBeaAndSkipQpmFlagLcu( + sequenceControlSetPtr, + pictureControlSetPtr, + lcuPtr, + tbAddr, + contextPtr); encodeContextPtr = ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr; @@ -2983,11 +2986,11 @@ EB_EXTERN void EncodePass( } - EB_BOOL useDeltaQp = (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction); + EB_BOOL useDeltaQp = (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction ||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)); EB_BOOL singleSegment = (sequenceControlSetPtr->encDecSegmentColCountArray[pictureControlSetPtr->temporalLayerIndex] == 1) && (sequenceControlSetPtr->encDecSegmentRowCountArray[pictureControlSetPtr->temporalLayerIndex] == 1); - EB_BOOL useDeltaQpSegments = singleSegment ? 0 : (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction); + EB_BOOL useDeltaQpSegments = singleSegment ? 0 : (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)); if (is16bit) { EncodePassPackLcu( @@ -3029,8 +3032,10 @@ EB_EXTERN void EncodePass( cuPtr->deltaQp = 0; - cuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) ? contextPtr->qpmQp : pictureControlSetPtr->pictureQp; - lcuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) ? contextPtr->qpmQp : pictureControlSetPtr->pictureQp; + cuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) ? + contextPtr->qpmQp : lcuPtr->qp; + lcuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) ? + contextPtr->qpmQp : lcuPtr->qp; cuPtr->orgDeltaQp = cuPtr->deltaQp; if (!contextPtr->skipQpmFlag && @@ -3496,7 +3501,8 @@ EB_EXTERN void EncodePass( } // Encode Transform Unit -INTRA- - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? + + contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)) ? EB_FALSE : lcuPtr->pictureLeftEdgeFlag && ((contextPtr->cuOriginX & (63)) == 0) && (contextPtr->cuOriginY == lcuOriginY); @@ -3783,7 +3789,7 @@ EB_EXTERN void EncodePass( } //TU LOOP for MV mode + Luma CBF decision. - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? + contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)) ? EB_FALSE : (tuOriginX == 0) && (tuOriginY == lcuOriginY); @@ -3972,7 +3978,7 @@ EB_EXTERN void EncodePass( cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf2 = EB_FALSE; cuPtr->transformUnitArray[contextPtr->tuItr].crCbf2 = EB_FALSE; } else if (cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) { - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? + contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)) ? EB_FALSE : (tuOriginX == 0) && (tuOriginY == lcuOriginY); @@ -4070,7 +4076,6 @@ EB_EXTERN void EncodePass( cuPtr->transformUnitArray[0].cbCbf2 = EB_TRUE; } - if (cuPtr->transformUnitArray[contextPtr->tuItr].crCbf) { cuPtr->transformUnitArray[0].crCbf = EB_TRUE; } diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index b075e2008..561ae58bd 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -4189,27 +4189,6 @@ void* EncDecKernel(void *inputPtr) // Derive restrictIntraGlobalMotion Flag contextPtr->mdContext->restrictIntraGlobalMotion = ((pictureControlSetPtr->ParentPcsPtr->isPan || pictureControlSetPtr->ParentPcsPtr->isTilt) && pictureControlSetPtr->ParentPcsPtr->nonMovingIndexArray[lcuIndex] < INTRA_GLOBAL_MOTION_NON_MOVING_INDEX_TH && pictureControlSetPtr->ParentPcsPtr->yMean[lcuIndex][RASTER_SCAN_CU_INDEX_64x64] < INTRA_GLOBAL_MOTION_DARK_LCU_TH); - //Block level vbv tuning starts here - if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) - { - if (pictureControlSetPtr->firstRowOfPicture) - pictureControlSetPtr->rowStats[rowIndex]->rowQp=pictureControlSetPtr->pictureQp; - - //Assign the base qp for the LCU - if (xLcuIndex <= yLcuIndex && yLcuIndex) - lcuPtr->qp = pictureControlSetPtr->lcuPtrArray[lcuIndex - pictureWidthInLcu]->qp; - else - lcuPtr->qp = pictureControlSetPtr->rowStats[rowPtr->rowIndex]->rowQp; - - // If current block is at row diagonal checkpoint, call vbv ratecontrol. - if (yLcuIndex == xLcuIndex && !pictureControlSetPtr->firstRowOfPicture) - { - baseQp=RowVbvRateControl(pictureControlSetPtr,sequenceControlSetPtr, rowPtr, sequenceControlSetPtr->encodeContextPtr, lcuPtr->qp); - lcuPtr->qp = CLIP3(sequenceControlSetPtr->staticConfig.minQpAllowed,sequenceControlSetPtr->staticConfig.maxQpAllowed, baseQp); - rowPtr->rowQp = lcuPtr->qp; - } - - } // Configure the LCU ModeDecisionConfigureLcu( // HT done contextPtr->mdContext, @@ -4320,6 +4299,31 @@ void* EncDecKernel(void *inputPtr) } } + //Block level vbv tuning starts here + if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) + { + EbBlockOnMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); + rowPtr = pictureControlSetPtr->rowStats[yLcuIndex]; + rowPtr->rowIndex = yLcuIndex; + lcuPtr->rowInd = yLcuIndex; + if (!yLcuIndex) + pictureControlSetPtr->rowStats[rowPtr->rowIndex]->rowQp = pictureControlSetPtr->pictureQp; + + //Assign the base qp for the LCU + if (xLcuIndex <= yLcuIndex && yLcuIndex) + lcuPtr->qp = pictureControlSetPtr->lcuPtrArray[lcuIndex - pictureWidthInLcu]->qp; + else + lcuPtr->qp = pictureControlSetPtr->rowStats[rowPtr->rowIndex]->rowQp; + + // If current block is at row diagonal checkpoint, call vbv ratecontrol. + if (xLcuIndex == yLcuIndex && yLcuIndex) + { + baseQp = RowVbvRateControl(pictureControlSetPtr, sequenceControlSetPtr, rowPtr, sequenceControlSetPtr->encodeContextPtr, lcuPtr->qp); + lcuPtr->qp = CLIP3(sequenceControlSetPtr->staticConfig.minQpAllowed, sequenceControlSetPtr->staticConfig.maxQpAllowed, baseQp); + rowPtr->rowQp = lcuPtr->qp; + } + EbReleaseMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); + } // Configure the LCU EncDecConfigureLcu( // HT done @@ -4348,7 +4352,7 @@ void* EncDecKernel(void *inputPtr) tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); - EncodeLcu( + EstimateLcu( lcuPtr, lcuOriginX, lcuOriginY, diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index 5aa743dc4..948b8a670 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -6248,9 +6248,9 @@ static void CodePPS( // "cu_qp_delta_enabled_flag" WriteFlagCavlc( bitstreamPtr, - scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction);// pcsPtr->useDeltaQp); + scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction ||(scsPtr->staticConfig.vbvBufsize && scsPtr->staticConfig.vbvMaxrate));// pcsPtr->useDeltaQp); - if (scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction) { //pcsPtr->useDeltaQp) { + if (scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction|| (scsPtr->staticConfig.vbvBufsize && scsPtr->staticConfig.vbvMaxrate)) { //pcsPtr->useDeltaQp) { // "diff_cu_qp_delta_depth" WriteUvlc( bitstreamPtr, @@ -7188,6 +7188,461 @@ static EB_ERRORTYPE Intra4x4EncodeCoeff( return return_error; } +/********************************************** +* Estimate Lcu +**********************************************/ +EB_ERRORTYPE EstimateLcu( + LargestCodingUnit_t *tbPtr, + EB_U32 lcuOriginX, + EB_U32 lcuOriginY, + PictureControlSet_t *pictureControlSetPtr, + EB_U32 lcuSize, + EntropyCoder_t *entropyCoderPtr, + EbPictureBufferDesc_t *coeffPtr, + NeighborArrayUnit_t *modeTypeNeighborArray, + NeighborArrayUnit_t *leafDepthNeighborArray, + NeighborArrayUnit_t *intraLumaModeNeighborArray, + NeighborArrayUnit_t *skipFlagNeighborArray, + EB_U32 pictureOriginX, + EB_U32 pictureOriginY) +{ + EB_ERRORTYPE return_error = EB_ErrorNone; + + CabacEncodeContext_t *cabacEncodeCtxPtr = (CabacEncodeContext_t*)entropyCoderPtr->cabacEncodeContextPtr; + SequenceControlSet_t *sequenceControlSetPtr = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; + EncodeContext_t *encodeContextPtr = ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr; + // CU Varaiables + const CodedUnitStats_t *cuStatsPtr; + CodingUnit_t *cuPtr; + EB_U32 cuIndex = 0; + EB_REFLIST refList; + EB_U32 maxRefList; + EB_U32 cuOriginX; + EB_U32 cuOriginY; + EB_U32 cuSize; + EB_U8 cuDepth; + EB_BOOL availableCoeff; + + // PU Varaiables + PredictionUnit_t *puPtr; + EB_U32 cuQuantizedCoeffsBits; + EB_U32 log2MinCuQpDeltaSize = Log2f(MAX_LCU_SIZE) - pictureControlSetPtr->difCuDeltaQpDepth; + + tbPtr->quantizedCoeffsBits = 0; + EB_BOOL entropyDeltaQpNotCoded = EB_TRUE; + EB_BOOL deltaQpNotCoded = EB_TRUE; + EB_BOOL checkCuOutOfBound = EB_FALSE; + LcuParams_t * lcuParam = &sequenceControlSetPtr->lcuParamsArray[tbPtr->index]; + if (!(lcuParam->isCompleteLcu)) { + checkCuOutOfBound = EB_TRUE; + } + do { + EB_BOOL codeCuCond = EB_TRUE; // Code cu only if it is inside the picture + cuPtr = tbPtr->codedLeafArrayPtr[cuIndex]; + if (checkCuOutOfBound) + codeCuCond = (EB_BOOL)lcuParam->rasterScanCuValidity[MD_SCAN_TO_RASTER_SCAN[cuIndex]]; // check if cu is inside the picture + + if (codeCuCond) { + // CU Stats + cuStatsPtr = GetCodedUnitStats(cuIndex); + cuSize = cuStatsPtr->size; + cuOriginX = lcuOriginX + cuStatsPtr->originX; + cuOriginY = lcuOriginY + cuStatsPtr->originY; + cuDepth = (EB_U8)cuStatsPtr->depth; + + // Code Split Flag + EncodeSplitFlag( + cabacEncodeCtxPtr, + cuDepth, + pictureControlSetPtr->lcuMaxDepth, + (EB_BOOL)cuPtr->splitFlag, + cuOriginX, + cuOriginY, + modeTypeNeighborArray, + leafDepthNeighborArray); + if (cuStatsPtr->sizeLog2 >= log2MinCuQpDeltaSize) { + deltaQpNotCoded = EB_TRUE; + } + if (((cuStatsPtr->originY & ((1 << log2MinCuQpDeltaSize) - 1)) == 0) && + ((cuStatsPtr->originX & ((1 << log2MinCuQpDeltaSize) - 1)) == 0)) { + deltaQpNotCoded = EB_TRUE; + } + + if (cuPtr->splitFlag == EB_FALSE) { + if (cuPtr->predictionModeFlag == INTRA_MODE && cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) + + availableCoeff = ( + cuPtr->transformUnitArray[1].lumaCbf || + cuPtr->transformUnitArray[2].lumaCbf || + cuPtr->transformUnitArray[3].lumaCbf || + cuPtr->transformUnitArray[4].lumaCbf || + cuPtr->transformUnitArray[1].crCbf || + cuPtr->transformUnitArray[1].cbCbf) ? EB_TRUE : EB_FALSE; + + else + availableCoeff = (cuPtr->predictionModeFlag == INTER_MODE) ? (EB_BOOL)cuPtr->rootCbf : + (cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].lumaCbf || + cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].crCbf || + cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].cbCbf) ? EB_TRUE : EB_FALSE; + // Estimate function is overwritting the values of pictureControlSetPtr->prevCodedQp which are used in EC + //EntropyCodingUpdateQp( + // cuPtr, + // availableCoeff, + // cuOriginX, + // cuOriginY, + // cuSize, + // sequenceControlSetPtr->lcuSize, + // sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) ? EB_TRUE : EB_FALSE, + // &entropyDeltaQpNotCoded, + // pictureControlSetPtr->difCuDeltaQpDepth, + // &pictureControlSetPtr->prevCodedQp, + // &pictureControlSetPtr->prevQuantGroupCodedQp, + // tbPtr->qp, + // pictureControlSetPtr, + // pictureOriginX, + // pictureOriginY); + + // Assign DLF QP + entropySetQpArrayBasedOnCU( + pictureControlSetPtr, + cuOriginX, + cuOriginY, + cuSize, + cuSize, + cuPtr->qp); + + // Code the skip flag + if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) + { + EncodeSkipFlag( + cabacEncodeCtxPtr, + (EB_BOOL)cuPtr->skipFlag, + cuOriginX, + cuOriginY, + modeTypeNeighborArray, + skipFlagNeighborArray); + } + + if (cuPtr->skipFlag) + { + // Merge Index + EncodeMergeIndex( + cabacEncodeCtxPtr, + &cuPtr->predictionUnitArray[0]); + } + else + { + // Code CU pred mode (I, P, B, etc.) + // (not needed for Intra Slice) + if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) + { + EncodePredictionMode( + cabacEncodeCtxPtr, + cuPtr); + } + switch (cuPtr->predictionModeFlag) { + + case INTRA_MODE: + if (cuPtr->predictionModeFlag == INTRA_MODE && cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) { + + // Code Partition Size + EncodeIntra4x4PartitionSize( + cabacEncodeCtxPtr, + cuPtr, + pictureControlSetPtr->lcuMaxDepth); + + // Get the PU Ptr + puPtr = cuPtr->predictionUnitArray; + + + EB_U8 partitionIndex; + + EB_U8 intraLumaLeftModeArray[4]; + EB_U8 intraLumaTopModeArray[4]; + + EB_U8 intraLumaLeftMode; + EB_U8 intraLumaTopMode; + + // Partition Loop + for (partitionIndex = 0; partitionIndex < 4; partitionIndex++) { + + EB_U8 intraLumaMode = tbPtr->intra4x4Mode[((MD_SCAN_TO_RASTER_SCAN[cuIndex] - 21) << 2) + partitionIndex]; + EB_U8 predictionModeFlag = INTRA_MODE; + + EB_U32 partitionOriginX = cuOriginX + INTRA_4x4_OFFSET_X[partitionIndex]; + EB_U32 partitionOriginY = cuOriginY + INTRA_4x4_OFFSET_Y[partitionIndex]; + + // Code Luma Mode for Intra First Stage + EncodeIntraLumaModeFirstStage( + cabacEncodeCtxPtr, + partitionOriginX, + partitionOriginY, + lcuSize, + &intraLumaLeftMode, + &intraLumaTopMode, + intraLumaMode, + modeTypeNeighborArray, + intraLumaModeNeighborArray); + + intraLumaLeftModeArray[partitionIndex] = intraLumaLeftMode; + intraLumaTopModeArray[partitionIndex] = intraLumaTopMode; + + NeighborArrayUnitModeWrite( + intraLumaModeNeighborArray, + (EB_U8*)&intraLumaMode, + partitionOriginX, + partitionOriginY, + MIN_PU_SIZE, + MIN_PU_SIZE, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + NeighborArrayUnitModeWrite( + modeTypeNeighborArray, + &predictionModeFlag, + partitionOriginX, + partitionOriginY, + MIN_PU_SIZE, + MIN_PU_SIZE, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + } + + for (partitionIndex = 0; partitionIndex < 4; partitionIndex++) { + EB_U8 intraLumaMode = tbPtr->intra4x4Mode[((MD_SCAN_TO_RASTER_SCAN[cuIndex] - 21) << 2) + partitionIndex]; + + // Code Luma Mode for Intra Second Stage + EncodeIntraLumaModeSecondStage( + cabacEncodeCtxPtr, + intraLumaLeftModeArray[partitionIndex], + intraLumaTopModeArray[partitionIndex], + intraLumaMode); + } + + // Code Chroma Mode for Intra + EncodeIntraChromaMode( + cabacEncodeCtxPtr); + + // Encode Transform Unit Split & CBFs + Intra4x4EncodeCoeff( + tbPtr, + cabacEncodeCtxPtr, + cuPtr, + cuStatsPtr, + coeffPtr, + &cuQuantizedCoeffsBits, + (EB_BOOL)pictureControlSetPtr->useDeltaQp, + &deltaQpNotCoded); + + tbPtr->quantizedCoeffsBits += cuQuantizedCoeffsBits; + + } + else + + { + // Code Partition Size + EncodePartitionSize( + cabacEncodeCtxPtr, + cuPtr, + pictureControlSetPtr->lcuMaxDepth); + + EB_U8 intraLumaLeftMode; + EB_U8 intraLumaTopMode; + EB_U8 intraLumaMode; + + // Get the PU Ptr + puPtr = cuPtr->predictionUnitArray; + // Code Luma Mode for Intra First Stage + EncodeIntraLumaModeFirstStage( + cabacEncodeCtxPtr, + cuOriginX, + cuOriginY, + lcuSize, + &intraLumaLeftMode, + &intraLumaTopMode, + puPtr->intraLumaMode, + modeTypeNeighborArray, + intraLumaModeNeighborArray); + + intraLumaMode = (EB_U8)puPtr->intraLumaMode; + + NeighborArrayUnitModeWrite( + intraLumaModeNeighborArray, + (EB_U8*)&intraLumaMode, + cuOriginX, + cuOriginY, + cuSize, + cuSize, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + { + EB_U8 predictionModeFlag = (EB_U8)cuPtr->predictionModeFlag; + NeighborArrayUnitModeWrite( + modeTypeNeighborArray, + &predictionModeFlag, + cuOriginX, + cuOriginY, + cuSize, + cuSize, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + } + + // Get PU Ptr + puPtr = &cuPtr->predictionUnitArray[0]; + + // Code Luma Mode for Intra Second Stage + EncodeIntraLumaModeSecondStage( + cabacEncodeCtxPtr, + intraLumaLeftMode, + intraLumaTopMode, + puPtr->intraLumaMode); + + // Code Chroma Mode for Intra + EncodeIntraChromaMode( + cabacEncodeCtxPtr); + EncodeTuSplitCoeff( + cabacEncodeCtxPtr, + cuPtr, + cuStatsPtr, + coeffPtr, + &cuQuantizedCoeffsBits, + (EB_BOOL)pictureControlSetPtr->useDeltaQp, + &deltaQpNotCoded); + + tbPtr->quantizedCoeffsBits += cuQuantizedCoeffsBits; + + } + break; + + case INTER_MODE: + { + + // Code Partition Size + EncodePartitionSize( + cabacEncodeCtxPtr, + cuPtr, + pictureControlSetPtr->lcuMaxDepth); + + puPtr = cuPtr->predictionUnitArray; + // mv merge Flag + EncodeMergeFlag( + cabacEncodeCtxPtr, + puPtr); + + if (puPtr->mergeFlag) { + // mv merge index + EncodeMergeIndex( + cabacEncodeCtxPtr, + cuPtr->predictionUnitArray); + } + else { + // Inter Prediction Direction + if (pictureControlSetPtr->sliceType == EB_B_PICTURE) { + EncodePredictionDirection( + cabacEncodeCtxPtr, + puPtr, + cuPtr); + } + + refList = puPtr->interPredDirectionIndex == UNI_PRED_LIST_1 ? REF_LIST_1 : REF_LIST_0; + maxRefList = (EB_U32)refList + (puPtr->interPredDirectionIndex == BI_PRED ? 2 : 1); + { + EB_U32 refIndex = refList; + for (; (EB_U32)refIndex < maxRefList; ++refIndex) { + // Reference Index + refList = (EB_REFLIST)refIndex; + // Reference Index + EncodeReferencePictureIndex( + cabacEncodeCtxPtr, + refList, + pictureControlSetPtr); + + // Motion Vector Difference + EncodeMvd( + cabacEncodeCtxPtr, + puPtr, + refList); + + // Motion Vector Prediction Index + EncodeMvpIndex( + cabacEncodeCtxPtr, + puPtr, + refList); + } + } + } + } + + // Encode Transform Unit Split & CBFs + EncodeTuSplitCoeff( + cabacEncodeCtxPtr, + cuPtr, + cuStatsPtr, + coeffPtr, + &cuQuantizedCoeffsBits, + (EB_BOOL)pictureControlSetPtr->useDeltaQp, + &deltaQpNotCoded); + + tbPtr->quantizedCoeffsBits += cuQuantizedCoeffsBits; + + break; + default: + CHECK_REPORT_ERROR_NC( + encodeContextPtr->appCallbackPtr, + EB_ENC_EC_ERROR3); + break; + } + } + + // Update the Leaf Depth Neighbor Array + NeighborArrayUnitModeWrite( + leafDepthNeighborArray, + &cuDepth, + cuOriginX, + cuOriginY, + cuSize, + cuSize, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + // Update the Mode Type Neighbor Array + { + EB_U8 predictionModeFlag = (EB_U8)cuPtr->predictionModeFlag; + NeighborArrayUnitModeWrite( + modeTypeNeighborArray, + &predictionModeFlag, + cuOriginX, + cuOriginY, + cuSize, + cuSize, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + } + + // Update the Skip Flag Neighbor Array + { + EB_U8 skipFlag = (EB_U8)cuPtr->skipFlag; + NeighborArrayUnitModeWrite( + skipFlagNeighborArray, + (EB_U8*)&skipFlag, + cuOriginX, + cuOriginY, + cuSize, + cuSize, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + } + + } + if (cuPtr->splitFlag == EB_FALSE) + cuIndex += DepthOffset[cuDepth]; + else + ++cuIndex; + } + else + ++cuIndex; + + } while (cuIndex < CU_MAX_COUNT); + + + return return_error; +} /********************************************** * Encode Lcu @@ -7304,7 +7759,7 @@ EB_ERRORTYPE EncodeLcu( cuOriginY, cuSize, sequenceControlSetPtr->lcuSize, - sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction ? EB_TRUE : EB_FALSE, + sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) ? EB_TRUE : EB_FALSE, &entropyDeltaQpNotCoded, pictureControlSetPtr->difCuDeltaQpDepth, &pictureControlSetPtr->prevCodedQp, diff --git a/Source/Lib/Codec/EbEntropyCoding.h b/Source/Lib/Codec/EbEntropyCoding.h index 694713d19..b8210348e 100644 --- a/Source/Lib/Codec/EbEntropyCoding.h +++ b/Source/Lib/Codec/EbEntropyCoding.h @@ -44,6 +44,21 @@ extern EB_ERRORTYPE EncodeLcu( EB_U32 pictureOriginX, EB_U32 pictureOriginY); +extern EB_ERRORTYPE EstimateLcu( + LargestCodingUnit_t *tbPtr, + EB_U32 lcuOriginX, + EB_U32 lcuOriginY, + PictureControlSet_t *pictureControlSetPtr, + EB_U32 lcuSize, + EntropyCoder_t *entropyCoderPtr, + EbPictureBufferDesc_t *coeffPtr, + NeighborArrayUnit_t *modeTypeNeighborArray, + NeighborArrayUnit_t *leafDepthNeighborArray, + NeighborArrayUnit_t *intraLumaModeNeighborArray, + NeighborArrayUnit_t *skipFlagNeighborArray, + EB_U32 pictureOriginX, + EB_U32 pictureOriginY); + extern EB_ERRORTYPE EncodeLcuSaoParameters( LargestCodingUnit_t *tbPtr, EntropyCoder_t *entropyCoderPtr, diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index 303bc4f4a..510c8ba77 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -743,7 +743,7 @@ void* PictureManagerKernel(void *inputPtr) // Rate Control - ChildPictureControlSetPtr->useDeltaQp = (EB_U8)(entrySequenceControlSetPtr->staticConfig.improveSharpness || entrySequenceControlSetPtr->staticConfig.bitRateReduction); + ChildPictureControlSetPtr->useDeltaQp = (EB_U8)(entrySequenceControlSetPtr->staticConfig.improveSharpness || entrySequenceControlSetPtr->staticConfig.bitRateReduction ||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)); // Check resolution if (entrySequenceControlSetPtr->inputResolution < INPUT_SIZE_1080p_RANGE) From 037d7c242fe0e289bae9432c8f8e3a3d240e2283 Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 19 Mar 2019 17:49:38 +0530 Subject: [PATCH 51/93] Add cli support to enable/disable low level vbv CLI:-low-level-vbv Default-disabled --- Source/API/EbApi.h | 2 +- Source/App/EbAppConfig.c | 10 +++++++--- Source/App/EbAppConfig.h | 1 + Source/App/EbAppContext.c | 1 + Source/Lib/Codec/EbEncHandle.c | 6 ++++++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index 8e985536b..80ef2bf87 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -519,7 +519,7 @@ typedef struct EB_H265_ENC_CONFIGURATION /* Active channel count. */ uint32_t activeChannelCount; - + uint8_t lowLevelVbv; // Threads management diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index d3fcfee56..5e4158a27 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -85,6 +85,7 @@ #define VBV_BUFFER_INIT_TOKEN "-vbv-init" #define VBV_BUFFER_END_TOKEN "-vbv-end" #define VBV_END_FRAME_ADJUST_TOKEN "-vbv-end-fr-adj" +#define ENABLE_LOW_LEVEL_VBV_TOKEN "-low-level-vbv" #define HRD_TOKEN "-hrd" #define MAX_QP_TOKEN "-max-qp" #define MIN_QP_TOKEN "-min-qp" @@ -206,6 +207,7 @@ static void SetVbvBufsize (const char *value, EbConfig_t * static void SetVbvBufInit (const char *value, EbConfig_t *cfg) { cfg->vbvBufInit = strtoul(value, NULL, 0); }; static void SetVbvEndFrameAdjust (const char *value, EbConfig_t *cfg) { cfg->vbvEndFrameAdjust = strtoul(value, NULL, 0); }; static void SetVbvBufEnd (const char *value, EbConfig_t *cfg) { cfg->vbvBufEnd = strtoul(value, NULL, 0); }; +static void SetLowLevelVbv (const char *value, EbConfig_t *cfg) { cfg->lowLevelVbv = (EB_BOOL)strtol(value, NULL, 0); }; static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0); }; static void SetVideoUsabilityInfo (const char *value, EbConfig_t *cfg) {cfg->videoUsabilityInfo = strtol(value, NULL, 0);}; static void SetHighDynamicRangeInput (const char *value, EbConfig_t *cfg) {cfg->highDynamicRangeInput = strtol(value, NULL, 0);}; @@ -297,10 +299,10 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, FRAME_RATE_DENOMINATOR_TOKEN, "FrameRateDenominator", SetFrameRateDenominator }, { SINGLE_INPUT, ENCODER_BIT_DEPTH, "EncoderBitDepth", SetEncoderBitDepth }, { SINGLE_INPUT, ENCODER_COLOR_FORMAT, "EncoderColorFormat", SetEncoderColorFormat}, - { SINGLE_INPUT, INPUT_COMPRESSED_TEN_BIT_FORMAT, "CompressedTenBitFormat", SetcompressedTenBitFormat }, - { SINGLE_INPUT, HIERARCHICAL_LEVELS_TOKEN, "HierarchicalLevels", SetHierarchicalLevels }, + { SINGLE_INPUT, INPUT_COMPRESSED_TEN_BIT_FORMAT, "CompressedTenBitFormat", SetcompressedTenBitFormat }, + { SINGLE_INPUT, HIERARCHICAL_LEVELS_TOKEN, "HierarchicalLevels", SetHierarchicalLevels }, - { SINGLE_INPUT, PRED_STRUCT_TOKEN, "PredStructure", SetCfgPredStructure }, + { SINGLE_INPUT, PRED_STRUCT_TOKEN, "PredStructure", SetCfgPredStructure }, // Rate Control @@ -321,6 +323,7 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, VBV_BUFFER_END_TOKEN, "vbvBufEnd", SetVbvBufEnd}, { SINGLE_INPUT, VBV_END_FRAME_ADJUST_TOKEN, "vbvEndFrameAdjustToken", SetVbvEndFrameAdjust}, + { SINGLE_INPUT, ENABLE_LOW_LEVEL_VBV_TOKEN,"lowLevelVbv",SetLowLevelVbv}, // DLF { SINGLE_INPUT, LOOP_FILTER_DISABLE_TOKEN, "LoopFilterDisable", SetDisableDlfFlag }, @@ -435,6 +438,7 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->vbvBufEnd = 0; configPtr->vbvEndFrameAdjust = 0; configPtr->hrdFlag = 0; + configPtr->lowLevelVbv = 0; configPtr->intraPeriod = -2; configPtr->intraRefreshType = 1; configPtr->hierarchicalLevels = 3; diff --git a/Source/App/EbAppConfig.h b/Source/App/EbAppConfig.h index c15b325a5..9197abbd6 100644 --- a/Source/App/EbAppConfig.h +++ b/Source/App/EbAppConfig.h @@ -347,6 +347,7 @@ typedef struct EbConfig_s EB_BOOL switchThreadsToRtPriority; EB_BOOL fpsInVps; uint32_t hrdFlag; + EB_BOOL lowLevelVbv; /**************************************** * Annex A Parameters diff --git a/Source/App/EbAppContext.c b/Source/App/EbAppContext.c index 7bb2825ae..43b3f82aa 100644 --- a/Source/App/EbAppContext.c +++ b/Source/App/EbAppContext.c @@ -185,6 +185,7 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.vbvBufInit = config->vbvBufInit; callbackData->ebEncParameters.vbvBufEnd = config->vbvBufEnd; callbackData->ebEncParameters.vbvEndFrameAdjust = config->vbvEndFrameAdjust; + callbackData->ebEncParameters.lowLevelVbv = config->lowLevelVbv; callbackData->ebEncParameters.useQpFile = (EB_BOOL)config->useQpFile; callbackData->ebEncParameters.disableDlfFlag = (EB_BOOL)config->disableDlfFlag; callbackData->ebEncParameters.enableSaoFlag = (EB_BOOL)config->enableSaoFlag; diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index a69c44353..9ffee23b8 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2104,6 +2104,7 @@ void CopyApiFromApp( sequenceControlSetPtr->staticConfig.vbvBufInit = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufInit; sequenceControlSetPtr->staticConfig.vbvBufEnd = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufEnd; sequenceControlSetPtr->staticConfig.vbvEndFrameAdjust = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvEndFrameAdjust; + sequenceControlSetPtr->staticConfig.lowLevelVbv= ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->lowLevelVbv; sequenceControlSetPtr->staticConfig.lookAheadDistance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->lookAheadDistance; sequenceControlSetPtr->staticConfig.framesToBeEncoded = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->framesToBeEncoded; @@ -2846,6 +2847,11 @@ static EB_ERRORTYPE VerifySettings(\ printf("Error instance %u: Invalid vbvEndFrameAdjust [0 - 100]\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } + + if (config->lowLevelVbv > 1) { + SVT_LOG("SVT [Error]: Instance %u : Invalid lowLevelVbv flag [0 - 1]\n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } return return_error; } From d1b7deaea916c69c87fb75427b2941d661ab804a Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 19 Mar 2019 18:01:46 +0530 Subject: [PATCH 52/93] Add additional checks for -low-level-vbv introduced --- Source/Lib/Codec/EbCodingLoop.c | 15 +++++++-------- Source/Lib/Codec/EbEncDecProcess.c | 4 ++-- Source/Lib/Codec/EbEntropyCoding.c | 8 ++++---- Source/Lib/Codec/EbPictureManagerProcess.c | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Source/Lib/Codec/EbCodingLoop.c b/Source/Lib/Codec/EbCodingLoop.c index c6cfc4bc5..e4031f9ff 100644 --- a/Source/Lib/Codec/EbCodingLoop.c +++ b/Source/Lib/Codec/EbCodingLoop.c @@ -2954,7 +2954,7 @@ EB_EXTERN void EncodePass( } } } - if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) { + if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) { contextPtr->skipQpmFlag = EB_TRUE; } else @@ -2986,11 +2986,11 @@ EB_EXTERN void EncodePass( } - EB_BOOL useDeltaQp = (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction ||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)); + EB_BOOL useDeltaQp = (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction ||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)); EB_BOOL singleSegment = (sequenceControlSetPtr->encDecSegmentColCountArray[pictureControlSetPtr->temporalLayerIndex] == 1) && (sequenceControlSetPtr->encDecSegmentRowCountArray[pictureControlSetPtr->temporalLayerIndex] == 1); - EB_BOOL useDeltaQpSegments = singleSegment ? 0 : (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)); + EB_BOOL useDeltaQpSegments = singleSegment ? 0 : (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)); if (is16bit) { EncodePassPackLcu( @@ -3032,9 +3032,9 @@ EB_EXTERN void EncodePass( cuPtr->deltaQp = 0; - cuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) ? + cuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? contextPtr->qpmQp : lcuPtr->qp; - lcuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) ? + lcuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? contextPtr->qpmQp : lcuPtr->qp; cuPtr->orgDeltaQp = cuPtr->deltaQp; @@ -3502,7 +3502,7 @@ EB_EXTERN void EncodePass( // Encode Transform Unit -INTRA- - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)) ? + contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)) ? EB_FALSE : lcuPtr->pictureLeftEdgeFlag && ((contextPtr->cuOriginX & (63)) == 0) && (contextPtr->cuOriginY == lcuOriginY); @@ -3978,7 +3978,7 @@ EB_EXTERN void EncodePass( cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf2 = EB_FALSE; cuPtr->transformUnitArray[contextPtr->tuItr].crCbf2 = EB_FALSE; } else if (cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) { - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)) ? + contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)) ? EB_FALSE : (tuOriginX == 0) && (tuOriginY == lcuOriginY); @@ -3988,7 +3988,6 @@ EB_EXTERN void EncodePass( tbAddr, lcuStatPtr->stationaryEdgeOverTimeFlag, pictureControlSetPtr->temporalLayerIndex > 0 ? lcuStatPtr->pmStationaryEdgeOverTimeFlag : lcuStatPtr->stationaryEdgeOverTimeFlag); - // Set Fast El coef shaping method contextPtr->transCoeffShapeLuma = DEFAULT_SHAPE; contextPtr->transCoeffShapeChroma = DEFAULT_SHAPE; diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 561ae58bd..d16478fe0 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -4300,7 +4300,7 @@ void* EncDecKernel(void *inputPtr) } //Block level vbv tuning starts here - if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) + if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) { EbBlockOnMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); rowPtr = pictureControlSetPtr->rowStats[yLcuIndex]; @@ -4346,7 +4346,7 @@ void* EncDecKernel(void *inputPtr) lcuPtr->qp, enableSaoFlag, contextPtr); - if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) + if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) { /*Entropy Estimation for LCU*/ tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index 948b8a670..a205934ab 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -6248,9 +6248,9 @@ static void CodePPS( // "cu_qp_delta_enabled_flag" WriteFlagCavlc( bitstreamPtr, - scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction ||(scsPtr->staticConfig.vbvBufsize && scsPtr->staticConfig.vbvMaxrate));// pcsPtr->useDeltaQp); + scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction ||(scsPtr->staticConfig.vbvBufsize && scsPtr->staticConfig.vbvMaxrate && scsPtr->staticConfig.lowLevelVbv));// pcsPtr->useDeltaQp); - if (scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction|| (scsPtr->staticConfig.vbvBufsize && scsPtr->staticConfig.vbvMaxrate)) { //pcsPtr->useDeltaQp) { + if (scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction|| (scsPtr->staticConfig.vbvBufsize && scsPtr->staticConfig.vbvMaxrate && scsPtr->staticConfig.lowLevelVbv)) { //pcsPtr->useDeltaQp) { // "diff_cu_qp_delta_depth" WriteUvlc( bitstreamPtr, @@ -7292,7 +7292,7 @@ EB_ERRORTYPE EstimateLcu( // cuOriginY, // cuSize, // sequenceControlSetPtr->lcuSize, - // sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) ? EB_TRUE : EB_FALSE, + // sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? EB_TRUE : EB_FALSE, // &entropyDeltaQpNotCoded, // pictureControlSetPtr->difCuDeltaQpDepth, // &pictureControlSetPtr->prevCodedQp, @@ -7759,7 +7759,7 @@ EB_ERRORTYPE EncodeLcu( cuOriginY, cuSize, sequenceControlSetPtr->lcuSize, - sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) ? EB_TRUE : EB_FALSE, + sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? EB_TRUE : EB_FALSE, &entropyDeltaQpNotCoded, pictureControlSetPtr->difCuDeltaQpDepth, &pictureControlSetPtr->prevCodedQp, diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index 510c8ba77..330e373e1 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -743,7 +743,7 @@ void* PictureManagerKernel(void *inputPtr) // Rate Control - ChildPictureControlSetPtr->useDeltaQp = (EB_U8)(entrySequenceControlSetPtr->staticConfig.improveSharpness || entrySequenceControlSetPtr->staticConfig.bitRateReduction ||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)); + ChildPictureControlSetPtr->useDeltaQp = (EB_U8)(entrySequenceControlSetPtr->staticConfig.improveSharpness || entrySequenceControlSetPtr->staticConfig.bitRateReduction ||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)); // Check resolution if (entrySequenceControlSetPtr->inputResolution < INPUT_SIZE_1080p_RANGE) From 5a8dc5300cb10cb6d11812d04eafe3bd11a228ee Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 29 Apr 2019 11:41:20 +0530 Subject: [PATCH 53/93] Fix the run time error "No RC interval found" when CRF+intraperiod=0 is enabled --- Source/Lib/Codec/EbRateControlProcess.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 305622fb6..a0279a104 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2393,7 +2393,7 @@ void* RateControlKernel(void *inputPtr) } // Frame level RC - if (sequenceControlSetPtr->intraPeriodLength == -1 || sequenceControlSetPtr->staticConfig.rateControlMode == 0){ + if (sequenceControlSetPtr->intraPeriodLength == -1 || sequenceControlSetPtr->staticConfig.rateControlMode == 0 || sequenceControlSetPtr->staticConfig.rateControlMode == 2){ rateControlParamPtr = contextPtr->rateControlParamQueue[0]; prevGopRateControlParamPtr = contextPtr->rateControlParamQueue[0]; nextGopRateControlParamPtr = contextPtr->rateControlParamQueue[0]; @@ -2628,7 +2628,7 @@ void* RateControlKernel(void *inputPtr) parentPictureControlSetPtr = (PictureParentControlSet_t*)rateControlTasksPtr->pictureControlSetWrapperPtr->objectPtr; sequenceControlSetPtr = (SequenceControlSet_t*)parentPictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; // Frame level RC - if (sequenceControlSetPtr->intraPeriodLength == -1 || sequenceControlSetPtr->staticConfig.rateControlMode == 0){ + if (sequenceControlSetPtr->intraPeriodLength == -1 || sequenceControlSetPtr->staticConfig.rateControlMode == 0 || sequenceControlSetPtr->staticConfig.rateControlMode == 2){ rateControlParamPtr = contextPtr->rateControlParamQueue[0]; prevGopRateControlParamPtr = contextPtr->rateControlParamQueue[0]; if (parentPictureControlSetPtr->sliceType == EB_I_PICTURE){ From d55a18b6d68d7feea336c02c2fe5a7d9cbf7680a Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 29 Apr 2019 12:27:58 +0530 Subject: [PATCH 54/93] Fix: Reset encodedBitsSoFar to 0 before recomputing frame size --- Source/Lib/Codec/EbEncDecProcess.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index d16478fe0..8076dddf8 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3967,27 +3967,29 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,///mutex to ))) { qpVbv += 1; + encodedBitsSoFar = 0; accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); } while (qpVbv > qpMin && (qpVbv > pictureControlSetPtr->rowStats[0]->rowQp ) && (((accFrameBits < (EB_U64)(pictureControlSetPtr->frameSizePlanned * 0.8f) && qpVbv <= prevRowQp) - || accFrameBits < (EB_U64)((pictureControlSetPtr->bufferFillPerFrame - rcData->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16)) * 1.1)) + || accFrameBits < (EB_U64)((pictureControlSetPtr->bufferFillPerFrame - rcData->vbvBufsize + rcData->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16)) * 1.1)) )) { qpVbv -= 1; + encodedBitsSoFar = 0; accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); } pictureControlSetPtr->frameSizeEstimated = accFrameBits; - /* If the current row was large enough to cause a large QP jump */ - if (qpVbv > qpMax && prevRowQp < qpMax) - { - /* Bump QP to halfway in between... close enough. */ - qpVbv = CLIP3(prevRowQp, qpMax, (EB_U8)((prevRowQp + qpVbv) * 0.5)); - } + ///* If the current row was large enough to cause a large QP jump */ + //if (qpVbv > qpMax && prevRowQp < qpMax) + //{ + // /* Bump QP to halfway in between... close enough. */ + // qpVbv = CLIP3(prevRowQp, qpMax, (EB_U8)((prevRowQp + qpVbv) * 0.5)); + //} } From f7ebfc35028fb6e2d927b595e653ed7165aa704c Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Thu, 30 May 2019 13:54:29 +0530 Subject: [PATCH 55/93] -Modified code as per review comments and coding guidelines --- Config/Sample.cfg | 7 + Docs/svt-hevc_encoder_user_guide.md | 22 + Source/API/EbApi.h | 39 +- Source/App/EbAppConfig.c | 29 +- Source/Lib/Codec/EbEncHandle.c | 459 +++++++++------------ Source/Lib/Codec/EbEntropyCoding.c | 24 +- Source/Lib/Codec/EbIntraPrediction.c | 80 +--- Source/Lib/Codec/EbPictureManagerProcess.c | 6 +- 8 files changed, 301 insertions(+), 365 deletions(-) diff --git a/Config/Sample.cfg b/Config/Sample.cfg index 727e62d4c..492eb093f 100644 --- a/Config/Sample.cfg +++ b/Config/Sample.cfg @@ -78,6 +78,13 @@ ConstrainedIntra : 0 # Enable the use of Cons # ====================== Rate Control =============================== RateControlMode : 0 # Rate control mode (0: OFF(CQP), 1: VBR) TargetBitRate : 7000000 # Target Bit Rate (in bits per second) +lowLevelVbv : 0 # Enables loeLevelVBV Algorithm (0: OFF, 1: ON) +vbvMaxrate : 0 # VBV MaxRate (in bits per second) +vbvBufsize : 0 # VBV Bufsize (in bits per second) +vbvBufInit : 90 # Sets how full the VBV buffer to be - [0 - 100] +vbvBufEnd : 0 # VBV BufEnd [0 - 100] +vbvEndFrameAdjust : 0 # VBV EndFrameAdjust [0 - 100] +hrdFlag : 0 # hrdflag (When hrdFlag is set to 1 it requires vbvMaxrate and vbvBufsize to be greater than 0)(0: disable , 1: enable) MaxQpAllowed : 48 # maximum allowed QP when rate control is on - [0-51] MinQpAllowed : 10 # minimum allowed QP when rate control is on - [0-51] LookAheadDistance : 17 # Enable Look Ahead [0-250] diff --git a/Docs/svt-hevc_encoder_user_guide.md b/Docs/svt-hevc_encoder_user_guide.md index 03a727c40..41b317f09 100644 --- a/Docs/svt-hevc_encoder_user_guide.md +++ b/Docs/svt-hevc_encoder_user_guide.md @@ -283,6 +283,13 @@ The encoder parameters present in the Sample.cfg file are listed in this table b | **ConstrainedIntra** | -constrd-intra | [0,1] | 0 | Allow the use of Constrained Intra, when enabled, this features yields to sending two PPSs in the HEVC Elementary streams
0 = OFF, 1 = ON | | **RateControlMode** | -rc | [0,1] | 0 | 0 : CQP , 1 : VBR | | **TargetBitRate** | -tbr | Any Number | 7000000 | Target bitrate in bits / second. Only used when RateControlMode is set to 1 | +| **lowLevelVbv** | -low-level-vbv | [0,1] | 0 | Enable lowLevelVBV algorithm. 0 = OFF, 1 = ON | +| **vbvMaxrate** | -vbv-maxrate | Any Number | 0 | VBVMaxrate in bits / second. Only used when RateControlMode is set to 1 | +| **vbvBufsize** | -vbv-bufsize | Any Number | 0 | VBV BufferSize in bits / second. Only used when RateControlMode is set to 1 | +| **vbvBufInit** | -vbv-init | [0 - 100] | 90 | Sets how full the VBV buffer to be| +| **vbvBufEnd** | -vbv-end | [0 - 100] | 0 | Sets how VBV Buffer ends| +| **vbvEndFrameAdjust** | -vbv-end-fr-adj | [0 - 100] | 0 | Sets vbvEndFrameAdjust| +| **hrdFlag** | -hrd | [0,1] | 0 | HRD Flag, 0 = OFF, 1 = ON |When hrdFlag is set to 1 it requires vbvMaxrate and vbvBufsize to be greater than 0 | | **MaxQpAllowed** | -max-qp | [0 - 51] | 48 | Maximum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be >= MinQpAllowed | | **MinQpAllowed** | -min-qp | [0 - 50] | 10 | Minimum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be < MaxQpAllowed | | **LookAheadDistance** | -lad | [0 - 250] | Depending on BRC mode | When RateControlMode is set to 1 it's best to set this parameter to be equal to the Intra period value (such is the default set by the encoder), When CQP is chosen, then a (2 \* minigopsize +1) look ahead is recommended. | @@ -414,6 +421,21 @@ Similarly, in order to run a 2-stream 8kp50 simultaneous encode on a Xeon Platin >taskset 0xFFFFFFF0000000FFFFFFF0000000./SvtHevcEncApp -encMode 12 -tune 0 -w 7680 -h 4320 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 20000000 -fps 50 -b out1.bin -n 5000 –nb 500 & +
+Similarly, in order to enable VBV and run a 2-stream 8kp50 simultaneous encode on a Xeon Platinum 8180 system the following command lines should be used: + +#### *Running Windows\* Server 2016:* + +>start /node 0 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -vbv-maxrate 10000000 -vbv-bufsize 10000000 -fps 50 -b out1.bin -n 5000 –nb 500 + +>start /node 1 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -vbv-maxrate 10000000 -vbv-bufsize 10000000 -fps 50 -b out3.bin -n 5000 –nb 500 + +#### *Running Ubuntu\* 18.04:* + +>taskset 0x0000000FFFFFFF0000000FFFFFFF ./SvtHevcEncApp -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -vbv-maxrate 10000000 -vbv-bufsize 10000000 -fps 50 -b out3.bin -n 5000 –nb 500 & + +>taskset 0xFFFFFFF0000000FFFFFFF0000000 ./SvtHevcEncApp -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -vbv-maxrate 10000000 -vbv-bufsize 10000000 -fps 50 -b out3.bin -n 5000 –nb 500 & +
Where 0x0000000FFFFFFF0000000FFFFFFF and 0xFFFFFFF0000000FFFFFFF0000000 are masks for sockets 0 and 1 respectively on a dual 8180 system. diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index 4b29e8ef1..c70107721 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -511,19 +511,50 @@ typedef struct EB_H265_ENC_CONFIGURATION // VBV Parameters - + /* Sets the maximum rate the VBV buffer should be assumed to refill at + * + * Default is 0. */ uint32_t vbvMaxrate; + + /* Sets the size of the VBV buffer in bits. + * + * Default is 0. */ uint32_t vbvBufsize; + /* Sets how full the VBV buffer must be before playback starts. If picture + * number is 0, then the initial fill is vbv-init * vbvBufferSize. + * Otherwise, it is interpreted as the initial fill in bits. + * + * Default is 90. */ + uint64_t vbvBufInit; + /* Sets how full the VBV buffer must be end. + * + * Default is 0. */ + uint64_t vbvBufEnd; + /* Sets vbvEndFrameAdjust. + * + * Default is 0. */ + uint64_t vbvEndFrameAdjust; + /* Enables the buffering period SEI and picture timing SEI to signal the HRD + * parameters. + * + * 0 = disable. + * 1 = enable. + * + * Default is 0. */ uint32_t hrdFlag; - uint64_t vbvBufInit; - uint64_t vbvBufEnd; - uint64_t vbvEndFrameAdjust; + /* ID assigned to each channel when multiple instances are running within the * same application. */ uint32_t channelId; /* Active channel count. */ uint32_t activeChannelCount; + /* Enables lowLevelVBV Algorithm + * + * 0 = disable. + * 1 = enable. + * + * Default is 0. */ uint8_t lowLevelVbv; // Threads management diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index 187296c10..9da56253a 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -213,13 +213,13 @@ static void SetEnableConstrainedIntra (const char *value, EbConfig_t * static void SetCfgTune (const char *value, EbConfig_t *cfg) {cfg->tune = (uint8_t)strtoul(value, NULL, 0); }; static void SetBitRateReduction (const char *value, EbConfig_t *cfg) {cfg->bitRateReduction = (EB_BOOL)strtol(value, NULL, 0); }; static void SetImproveSharpness (const char *value, EbConfig_t *cfg) {cfg->improveSharpness = (EB_BOOL)strtol(value, NULL, 0);}; -static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0); }; -static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0); }; -static void SetVbvBufInit (const char *value, EbConfig_t *cfg) { cfg->vbvBufInit = strtoul(value, NULL, 0); }; -static void SetVbvEndFrameAdjust (const char *value, EbConfig_t *cfg) { cfg->vbvEndFrameAdjust = strtoul(value, NULL, 0); }; -static void SetVbvBufEnd (const char *value, EbConfig_t *cfg) { cfg->vbvBufEnd = strtoul(value, NULL, 0); }; -static void SetLowLevelVbv (const char *value, EbConfig_t *cfg) { cfg->lowLevelVbv = (EB_BOOL)strtol(value, NULL, 0); }; -static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0); }; +static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0); }; +static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0); }; +static void SetVbvBufInit (const char *value, EbConfig_t *cfg) { cfg->vbvBufInit = strtoul(value, NULL, 0); }; +static void SetVbvEndFrameAdjust (const char *value, EbConfig_t *cfg) { cfg->vbvEndFrameAdjust = strtoul(value, NULL, 0); }; +static void SetVbvBufEnd (const char *value, EbConfig_t *cfg) { cfg->vbvBufEnd = strtoul(value, NULL, 0); }; +static void SetLowLevelVbv (const char *value, EbConfig_t *cfg) { cfg->lowLevelVbv = (EB_BOOL)strtol(value, NULL, 0); }; +static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0); }; static void SetVideoUsabilityInfo (const char *value, EbConfig_t *cfg) {cfg->videoUsabilityInfo = strtol(value, NULL, 0);}; static void SetHighDynamicRangeInput (const char *value, EbConfig_t *cfg) {cfg->highDynamicRangeInput = strtol(value, NULL, 0);}; static void SetAccessUnitDelimiter (const char *value, EbConfig_t *cfg) {cfg->accessUnitDelimiter = strtol(value, NULL, 0);}; @@ -465,7 +465,6 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->maxQpAllowed = 48; configPtr->minQpAllowed = 10; configPtr->baseLayerSwitchMode = 0; - configPtr->encMode = 9; configPtr->vbvMaxRate = 0; configPtr->vbvBufsize = 0; configPtr->vbvBufInit = 90; @@ -475,8 +474,8 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->lowLevelVbv = 0; configPtr->intraPeriod = -2; configPtr->intraRefreshType = 1; - configPtr->hierarchicalLevels = 3; - configPtr->predStructure = 2; + configPtr->hierarchicalLevels = 3; + configPtr->predStructure = 2; configPtr->disableDlfFlag = EB_FALSE; configPtr->enableSaoFlag = EB_TRUE; configPtr->useDefaultMeHme = EB_TRUE; @@ -492,7 +491,7 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->bufferingPeriodSEI = 0; configPtr->pictureTimingSEI = 0; - configPtr->bitRateReduction = EB_TRUE; + configPtr->bitRateReduction = EB_TRUE; configPtr->improveSharpness = EB_TRUE; configPtr->registeredUserDataSeiFlag = EB_FALSE; configPtr->unregisteredUserDataSeiFlag = EB_FALSE; @@ -523,7 +522,7 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->unrestrictedMotionVector = EB_TRUE; // Encoding Presets - configPtr->encMode = 9; + configPtr->encMode = 9; //configPtr->latencyMode = 0; // Deprecated configPtr->speedControlFlag = 0; @@ -555,9 +554,9 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->separateFields = EB_FALSE; // Coding Structure - configPtr->hierarchicalLevels = 3; + configPtr->hierarchicalLevels = 3; configPtr->baseLayerSwitchMode = 0; - configPtr->predStructure = 2; + configPtr->predStructure = 2; configPtr->intraPeriod = -2; configPtr->intraRefreshType = 1; @@ -590,7 +589,7 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->tune = 1; // Adaptive QP Params - configPtr->bitRateReduction = EB_TRUE; + configPtr->bitRateReduction = EB_TRUE; configPtr->improveSharpness = EB_TRUE; // Optional Features diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index fc3e0f84d..1df1e295f 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -485,7 +485,7 @@ static EB_ERRORTYPE EbEncHandleCtor( encHandlePtr->pictureDecisionThreadHandle = (EB_HANDLE) EB_NULL; encHandlePtr->motionEstimationThreadHandleArray = (EB_HANDLE*) EB_NULL; encHandlePtr->initialRateControlThreadHandle = (EB_HANDLE) EB_NULL; - encHandlePtr->sourceBasedOperationsThreadHandleArray = (EB_HANDLE*)EB_NULL; + encHandlePtr->sourceBasedOperationsThreadHandleArray = (EB_HANDLE*)EB_NULL; encHandlePtr->pictureManagerThreadHandle = (EB_HANDLE) EB_NULL; encHandlePtr->rateControlThreadHandle = (EB_HANDLE) EB_NULL; encHandlePtr->modeDecisionConfigurationThreadHandleArray = (EB_HANDLE*) EB_NULL; @@ -499,7 +499,7 @@ static EB_ERRORTYPE EbEncHandleCtor( encHandlePtr->pictureDecisionContextPtr = (EB_PTR) EB_NULL; encHandlePtr->motionEstimationContextPtrArray = (EB_PTR*) EB_NULL; encHandlePtr->initialRateControlContextPtr = (EB_PTR) EB_NULL; - encHandlePtr->sourceBasedOperationsContextPtrArray = (EB_PTR*)EB_NULL; + encHandlePtr->sourceBasedOperationsContextPtrArray = (EB_PTR*)EB_NULL; encHandlePtr->pictureManagerContextPtr = (EB_PTR) EB_NULL; encHandlePtr->rateControlContextPtr = (EB_PTR) EB_NULL; encHandlePtr->modeDecisionConfigurationContextPtrArray = (EB_PTR*) EB_NULL; @@ -514,7 +514,7 @@ static EB_ERRORTYPE EbEncHandleCtor( encHandlePtr->pictureAnalysisResultsResourcePtr = (EbSystemResource_t*) EB_NULL; encHandlePtr->pictureDecisionResultsResourcePtr = (EbSystemResource_t*) EB_NULL; encHandlePtr->motionEstimationResultsResourcePtr = (EbSystemResource_t*) EB_NULL; - encHandlePtr->initialRateControlResultsResourcePtr = (EbSystemResource_t*)EB_NULL; + encHandlePtr->initialRateControlResultsResourcePtr = (EbSystemResource_t*)EB_NULL; encHandlePtr->pictureDemuxResultsResourcePtr = (EbSystemResource_t*) EB_NULL; encHandlePtr->rateControlTasksResourcePtr = (EbSystemResource_t*) EB_NULL; encHandlePtr->rateControlResultsResourcePtr = (EbSystemResource_t*) EB_NULL; @@ -755,21 +755,21 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) inputData.pictureWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth; inputData.pictureHeight = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight; - inputData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->leftPadding; - inputData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->rightPadding; - inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; - inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; + inputData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->leftPadding; + inputData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->rightPadding; + inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; + inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; inputData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->outputBitdepth; inputData.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaFormatIdc; inputData.lcuSize = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize; inputData.maxDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxLcuDepth; inputData.is16bit = is16bit; - inputData.compressedTenBitFormat = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.compressedTenBitFormat; + inputData.compressedTenBitFormat = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.compressedTenBitFormat; - inputData.encMode = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.encMode; - inputData.speedControl = (EB_U8)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.speedControlFlag; + inputData.encMode = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.encMode; + inputData.speedControl = (EB_U8)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.speedControlFlag; inputData.tune = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.tune; - return_error = EbSystemResourceCtor( + return_error = EbSystemResourceCtor( &(encHandlePtr->pictureParentControlSetPoolPtrArray[instanceIndex]), encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->pictureControlSetPoolInitCount,//encHandlePtr->pictureControlSetPoolTotalCount, 1, @@ -809,10 +809,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) inputData.pictureWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth; inputData.pictureHeight = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight; - inputData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->leftPadding; - inputData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->rightPadding; - inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; - inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; + inputData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->leftPadding; + inputData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->rightPadding; + inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; + inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; inputData.bitDepth = EB_8BIT; inputData.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaFormatIdc; inputData.lcuSize = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize; @@ -868,10 +868,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) referencePictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; referencePictureBufferDescInitData.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaFormatIdc; referencePictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_FULL_MASK; - referencePictureBufferDescInitData.leftPadding = MAX_LCU_SIZE + MCPXPaddingOffset; - referencePictureBufferDescInitData.rightPadding = MAX_LCU_SIZE + MCPXPaddingOffset; - referencePictureBufferDescInitData.topPadding = MAX_LCU_SIZE + MCPYPaddingOffset; - referencePictureBufferDescInitData.botPadding = MAX_LCU_SIZE + MCPYPaddingOffset; + referencePictureBufferDescInitData.leftPadding = MAX_LCU_SIZE + MCPXPaddingOffset; + referencePictureBufferDescInitData.rightPadding = MAX_LCU_SIZE + MCPXPaddingOffset; + referencePictureBufferDescInitData.topPadding = MAX_LCU_SIZE + MCPYPaddingOffset; + referencePictureBufferDescInitData.botPadding = MAX_LCU_SIZE + MCPYPaddingOffset; referencePictureBufferDescInitData.splitMode = EB_FALSE; if (is16bit){ @@ -903,10 +903,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) referencePictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; referencePictureBufferDescInitData.colorFormat = EB_YUV420; referencePictureBufferDescInitData.bufferEnableMask = 0; - referencePictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; - referencePictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; - referencePictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; - referencePictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; + referencePictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; + referencePictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; + referencePictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; + referencePictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; referencePictureBufferDescInitData.splitMode = EB_FALSE; quarterDecimPictureBufferDescInitData.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth >> 1; @@ -914,10 +914,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) quarterDecimPictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; quarterDecimPictureBufferDescInitData.colorFormat = EB_YUV420; quarterDecimPictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_LUMA_MASK; - quarterDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; - quarterDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; - quarterDecimPictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; - quarterDecimPictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; + quarterDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; + quarterDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; + quarterDecimPictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; + quarterDecimPictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; quarterDecimPictureBufferDescInitData.splitMode = EB_FALSE; sixteenthDecimPictureBufferDescInitData.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth >> 2; @@ -925,10 +925,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) sixteenthDecimPictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; sixteenthDecimPictureBufferDescInitData.colorFormat = EB_YUV420; sixteenthDecimPictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_LUMA_MASK; - sixteenthDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; - sixteenthDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; - sixteenthDecimPictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; - sixteenthDecimPictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; + sixteenthDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; + sixteenthDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; + sixteenthDecimPictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; + sixteenthDecimPictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; sixteenthDecimPictureBufferDescInitData.splitMode = EB_FALSE; EbPaReferenceObjectDescInitDataStructure.referencePictureDescInitData = referencePictureBufferDescInitData; @@ -1252,7 +1252,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) // Output Buffer Fifo Ptrs for(instanceIndex=0; instanceIndex < encHandlePtr->encodeInstanceTotalCount; ++instanceIndex) { - encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->streamOutputFifoPtr = (encHandlePtr->outputStreamBufferProducerFifoPtrDblArray[instanceIndex])[0]; + encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->streamOutputFifoPtr = (encHandlePtr->outputStreamBufferProducerFifoPtrDblArray[instanceIndex])[0]; if (encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->staticConfig.reconEnabled) { encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->reconOutputFifoPtr = (encHandlePtr->outputReconBufferProducerFifoPtrDblArray[instanceIndex])[0]; } @@ -1283,19 +1283,19 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) for(processIndex=0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->pictureAnalysisProcessInitCount; ++processIndex) { - EbPictureBufferDescInitData_t pictureBufferDescConf; - pictureBufferDescConf.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaWidth; - pictureBufferDescConf.maxHeight = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaHeight; - pictureBufferDescConf.bitDepth = EB_8BIT; + EbPictureBufferDescInitData_t pictureBufferDescConf; + pictureBufferDescConf.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaWidth; + pictureBufferDescConf.maxHeight = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaHeight; + pictureBufferDescConf.bitDepth = EB_8BIT; pictureBufferDescConf.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->chromaFormatIdc; - pictureBufferDescConf.bufferEnableMask = PICTURE_BUFFER_DESC_Y_FLAG; - pictureBufferDescConf.leftPadding = 0; - pictureBufferDescConf.rightPadding = 0; - pictureBufferDescConf.topPadding = 0; - pictureBufferDescConf.botPadding = 0; - pictureBufferDescConf.splitMode = EB_FALSE; - - return_error = PictureAnalysisContextCtor( + pictureBufferDescConf.bufferEnableMask = PICTURE_BUFFER_DESC_Y_FLAG; + pictureBufferDescConf.leftPadding = 0; + pictureBufferDescConf.rightPadding = 0; + pictureBufferDescConf.topPadding = 0; + pictureBufferDescConf.botPadding = 0; + pictureBufferDescConf.splitMode = EB_FALSE; + + return_error = PictureAnalysisContextCtor( &pictureBufferDescConf, EB_TRUE, (PictureAnalysisContext_t**) &encHandlePtr->pictureAnalysisContextPtrArray[processIndex], @@ -1305,8 +1305,8 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) ((encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaHeight + MAX_LCU_SIZE - 1) / MAX_LCU_SIZE)); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; } } @@ -1318,8 +1318,8 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) (PictureDecisionContext_t**) &encHandlePtr->pictureDecisionContextPtr, encHandlePtr->pictureAnalysisResultsConsumerFifoPtrArray[0], encHandlePtr->pictureDecisionResultsProducerFifoPtrArray[0]); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; } } @@ -1328,10 +1328,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) for(processIndex=0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->motionEstimationProcessInitCount; ++processIndex) { - return_error = MotionEstimationContextCtor( - (MotionEstimationContext_t**) &encHandlePtr->motionEstimationContextPtrArray[processIndex], - encHandlePtr->pictureDecisionResultsConsumerFifoPtrArray[processIndex], - encHandlePtr->motionEstimationResultsProducerFifoPtrArray[processIndex]); + return_error = MotionEstimationContextCtor( + (MotionEstimationContext_t**) &encHandlePtr->motionEstimationContextPtrArray[processIndex], + encHandlePtr->pictureDecisionResultsConsumerFifoPtrArray[processIndex], + encHandlePtr->motionEstimationResultsProducerFifoPtrArray[processIndex]); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; @@ -1342,21 +1342,21 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) return_error = InitialRateControlContextCtor( (InitialRateControlContext_t**) &encHandlePtr->initialRateControlContextPtr, encHandlePtr->motionEstimationResultsConsumerFifoPtrArray[0], - encHandlePtr->initialRateControlResultsProducerFifoPtrArray[0]); + encHandlePtr->initialRateControlResultsProducerFifoPtrArray[0]); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } // Source Based Operations Context - EB_MALLOC(EB_PTR*, encHandlePtr->sourceBasedOperationsContextPtrArray, sizeof(EB_PTR) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount, EB_N_PTR); + EB_MALLOC(EB_PTR*, encHandlePtr->sourceBasedOperationsContextPtrArray, sizeof(EB_PTR) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount, EB_N_PTR); - for (processIndex = 0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount; ++processIndex) { + for (processIndex = 0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount; ++processIndex) { return_error = SourceBasedOperationsContextCtor( (SourceBasedOperationsContext_t**)&encHandlePtr->sourceBasedOperationsContextPtrArray[processIndex], encHandlePtr->initialRateControlResultsConsumerFifoPtrArray[processIndex], encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[processIndex]); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; } } @@ -1375,8 +1375,8 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) (RateControlContext_t**) &encHandlePtr->rateControlContextPtr, encHandlePtr->rateControlTasksConsumerFifoPtrArray[0], encHandlePtr->rateControlResultsProducerFifoPtrArray[0], - encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->intraPeriodLength); - if (return_error == EB_ErrorInsufficientResources){ + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->intraPeriodLength); + if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } @@ -1387,7 +1387,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) EB_MALLOC(EB_PTR*, encHandlePtr->modeDecisionConfigurationContextPtrArray, sizeof(EB_PTR) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->modeDecisionConfigurationProcessInitCount, EB_N_PTR); for(processIndex=0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->modeDecisionConfigurationProcessInitCount; ++processIndex) { - return_error = ModeDecisionConfigurationContextCtor( + return_error = ModeDecisionConfigurationContextCtor( (ModeDecisionConfigurationContext_t**) &encHandlePtr->modeDecisionConfigurationContextPtrArray[processIndex], encHandlePtr->rateControlResultsConsumerFifoPtrArray[processIndex], @@ -1395,10 +1395,9 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) ((encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaWidth + MAX_LCU_SIZE - 1) / MAX_LCU_SIZE) * ((encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaHeight + MAX_LCU_SIZE - 1) / MAX_LCU_SIZE) ); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } } } @@ -1485,11 +1484,11 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) EB_CREATETHREAD(EB_HANDLE, encHandlePtr->initialRateControlThreadHandle, sizeof(EB_HANDLE), EB_THREAD, InitialRateControlKernel, encHandlePtr->initialRateControlContextPtr); // Source Based Oprations - EB_MALLOC(EB_HANDLE*, encHandlePtr->sourceBasedOperationsThreadHandleArray, sizeof(EB_HANDLE) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount, EB_N_PTR); + EB_MALLOC(EB_HANDLE*, encHandlePtr->sourceBasedOperationsThreadHandleArray, sizeof(EB_HANDLE) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount, EB_N_PTR); - for (processIndex = 0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount; ++processIndex) { - EB_CREATETHREAD(EB_HANDLE, encHandlePtr->sourceBasedOperationsThreadHandleArray[processIndex], sizeof(EB_HANDLE), EB_THREAD, SourceBasedOperationsKernel, encHandlePtr->sourceBasedOperationsContextPtrArray[processIndex]); - } + for (processIndex = 0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount; ++processIndex) { + EB_CREATETHREAD(EB_HANDLE, encHandlePtr->sourceBasedOperationsThreadHandleArray[processIndex], sizeof(EB_HANDLE), EB_THREAD, SourceBasedOperationsKernel, encHandlePtr->sourceBasedOperationsContextPtrArray[processIndex]); + } // Picture Manager EB_CREATETHREAD(EB_HANDLE, encHandlePtr->pictureManagerThreadHandle, sizeof(EB_HANDLE), EB_THREAD, PictureManagerKernel, encHandlePtr->pictureManagerContextPtr); @@ -2062,7 +2061,7 @@ void CopyApiFromApp( // Coding Structure sequenceControlSetPtr->staticConfig.intraPeriodLength = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->intraPeriodLength; sequenceControlSetPtr->staticConfig.intraRefreshType = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->intraRefreshType; - sequenceControlSetPtr->staticConfig.predStructure = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->predStructure; + sequenceControlSetPtr->staticConfig.predStructure = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->predStructure; sequenceControlSetPtr->staticConfig.baseLayerSwitchMode = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->baseLayerSwitchMode; sequenceControlSetPtr->staticConfig.hierarchicalLevels = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->hierarchicalLevels; @@ -2151,7 +2150,7 @@ void CopyApiFromApp( } sequenceControlSetPtr->chromaFormatIdc = (EB_U32)(sequenceControlSetPtr->staticConfig.encoderColorFormat); sequenceControlSetPtr->staticConfig.compressedTenBitFormat = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->compressedTenBitFormat; - sequenceControlSetPtr->staticConfig.videoUsabilityInfo = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->videoUsabilityInfo; + sequenceControlSetPtr->staticConfig.videoUsabilityInfo = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->videoUsabilityInfo; sequenceControlSetPtr->staticConfig.highDynamicRangeInput = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->highDynamicRangeInput; sequenceControlSetPtr->staticConfig.accessUnitDelimiter = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->accessUnitDelimiter; sequenceControlSetPtr->staticConfig.bufferingPeriodSEI = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->bufferingPeriodSEI; @@ -2178,46 +2177,9 @@ void CopyApiFromApp( sequenceControlSetPtr->staticConfig.frameRateDenominator = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->frameRateDenominator; sequenceControlSetPtr->staticConfig.frameRateNumerator = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->frameRateNumerator; sequenceControlSetPtr->staticConfig.reconEnabled = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->reconEnabled; - sequenceControlSetPtr->staticConfig.hrdFlag = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->hrdFlag; - - sequenceControlSetPtr->staticConfig.maxCLL = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxCLL; - sequenceControlSetPtr->staticConfig.maxFALL = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxFALL; - - sequenceControlSetPtr->staticConfig.useMasteringDisplayColorVolume = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->useMasteringDisplayColorVolume; - sequenceControlSetPtr->staticConfig.useNaluFile = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->useNaluFile; - sequenceControlSetPtr->staticConfig.displayPrimaryX[0] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryX[0]; - sequenceControlSetPtr->staticConfig.displayPrimaryX[1] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryX[1]; - sequenceControlSetPtr->staticConfig.displayPrimaryX[2] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryX[2]; - sequenceControlSetPtr->staticConfig.displayPrimaryY[0] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryY[0]; - sequenceControlSetPtr->staticConfig.displayPrimaryY[1] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryY[1]; - sequenceControlSetPtr->staticConfig.displayPrimaryY[2] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryY[2]; - sequenceControlSetPtr->staticConfig.whitePointX = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->whitePointX; - sequenceControlSetPtr->staticConfig.whitePointY = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->whitePointY; - sequenceControlSetPtr->staticConfig.maxDisplayMasteringLuminance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxDisplayMasteringLuminance; - sequenceControlSetPtr->staticConfig.minDisplayMasteringLuminance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->minDisplayMasteringLuminance; - sequenceControlSetPtr->staticConfig.dolbyVisionProfile = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->dolbyVisionProfile; - - // Copying to masteringDisplayColorVolume structure - sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryX[0] = sequenceControlSetPtr->staticConfig.displayPrimaryX[0]; - sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryX[1] = sequenceControlSetPtr->staticConfig.displayPrimaryX[1]; - sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryX[2] = sequenceControlSetPtr->staticConfig.displayPrimaryX[2]; - sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryY[0] = sequenceControlSetPtr->staticConfig.displayPrimaryY[0]; - sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryY[1] = sequenceControlSetPtr->staticConfig.displayPrimaryY[1]; - sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryY[2] = sequenceControlSetPtr->staticConfig.displayPrimaryY[2]; - sequenceControlSetPtr->masteringDisplayColorVolume.whitePointX = sequenceControlSetPtr->staticConfig.whitePointX; - sequenceControlSetPtr->masteringDisplayColorVolume.whitePointY = sequenceControlSetPtr->staticConfig.whitePointY; - sequenceControlSetPtr->masteringDisplayColorVolume.maxDisplayMasteringLuminance = sequenceControlSetPtr->staticConfig.maxDisplayMasteringLuminance; - sequenceControlSetPtr->masteringDisplayColorVolume.minDisplayMasteringLuminance = sequenceControlSetPtr->staticConfig.minDisplayMasteringLuminance; - - // if dolby Profile is set HDR should be set to 1 - if (sequenceControlSetPtr->staticConfig.dolbyVisionProfile == 81) { - sequenceControlSetPtr->staticConfig.highDynamicRangeInput = 1; - } - sequenceControlSetPtr->staticConfig.unrestrictedMotionVector = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->unrestrictedMotionVector; sequenceControlSetPtr->enableTmvpSps = sequenceControlSetPtr->staticConfig.unrestrictedMotionVector; - sequenceControlSetPtr->staticConfig.maxCLL = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxCLL; sequenceControlSetPtr->staticConfig.maxFALL = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxFALL; @@ -2273,8 +2235,7 @@ void CopyApiFromApp( } //Set required flags to signal vbv status when hrd is enabled - if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) - { + if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) { sequenceControlSetPtr->staticConfig.videoUsabilityInfo = 1; sequenceControlSetPtr->videoUsabilityInfoPtr->vuiHrdParametersPresentFlag = 1; sequenceControlSetPtr->staticConfig.bufferingPeriodSEI = 1; @@ -2282,10 +2243,6 @@ void CopyApiFromApp( sequenceControlSetPtr->videoUsabilityInfoPtr->hrdParametersPtr->nalHrdParametersPresentFlag = 1; sequenceControlSetPtr->videoUsabilityInfoPtr->hrdParametersPtr->cpbDpbDelaysPresentFlag = 1; } - - - - return; } @@ -2297,7 +2254,7 @@ void CopyApiFromApp( static int VerifyHmeDimention(unsigned int index,unsigned int HmeLevel0SearchAreaInWidth, EB_U32 NumberHmeSearchRegionInWidth[EB_HME_SEARCH_AREA_ROW_MAX_COUNT], unsigned int numberHmeSearchRegionInWidth ) { int return_error = 0; - EB_U32 i; + EB_U32 i; EB_U32 totalSearchWidth = 0; for (i=0 ; i < numberHmeSearchRegionInWidth; i++){ @@ -2305,11 +2262,11 @@ static int VerifyHmeDimention(unsigned int index,unsigned int HmeLevel0SearchAre } if ((totalSearchWidth) != (HmeLevel0SearchAreaInWidth)) { SVT_LOG("SVT [Error]: Instance %u: Invalid HME Total Search Area. \n", index); - return_error = -1; - return return_error; + return_error = -1; + return return_error; } - return return_error; + return return_error; } static int VerifyHmeDimentionL1L2(unsigned int index, EB_U32 NumberHmeSearchRegionInWidth[EB_HME_SEARCH_AREA_ROW_MAX_COUNT], unsigned int numberHmeSearchRegionInWidth) @@ -2342,7 +2299,7 @@ static EB_ERRORTYPE VerifySettings(\ if ( config->tier > 1 ) { SVT_LOG("SVT [Error]: Instance %u: Tier must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } // For levels below level 4 (exclusive), only the main tier is allowed @@ -2431,14 +2388,14 @@ static EB_ERRORTYPE VerifySettings(\ break; } - if(levelIdx > TOTAL_LEVEL_COUNT){ + if(levelIdx > TOTAL_LEVEL_COUNT){ SVT_LOG("SVT [Error]: Instance %u: Unsupported level\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (sequenceControlSetPtr->maxInputLumaWidth < 64) { SVT_LOG("SVT [Error]: Instance %u: Source Width must be at least 64\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (sequenceControlSetPtr->maxInputLumaHeight < 64) { SVT_LOG("SVT [Error]: Instance %u: Source Width must be at least 64\n", channelNumber + 1); @@ -2459,7 +2416,7 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if (sequenceControlSetPtr->maxInputLumaWidth % 2) { + if (sequenceControlSetPtr->maxInputLumaWidth % 2) { SVT_LOG("SVT [Error]: Instance %u: Source Width must be even for YUV_420 colorspace\n",channelNumber+1); return_error = EB_ErrorBadParameter; } @@ -2470,19 +2427,19 @@ static EB_ERRORTYPE VerifySettings(\ } if (sequenceControlSetPtr->maxInputLumaWidth > 8192) { SVT_LOG("SVT [Error]: Instance %u: Source Width must be less than 8192\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (sequenceControlSetPtr->maxInputLumaHeight > 4320) { SVT_LOG("SVT [Error]: Instance %u: Source Height must be less than 4320\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - EB_U32 inputSize = (EB_U32)sequenceControlSetPtr->maxInputLumaWidth * (EB_U32)sequenceControlSetPtr->maxInputLumaHeight; + EB_U32 inputSize = (EB_U32)sequenceControlSetPtr->maxInputLumaWidth * (EB_U32)sequenceControlSetPtr->maxInputLumaHeight; - EB_U8 inputResolution = (inputSize < INPUT_SIZE_1080i_TH) ? INPUT_SIZE_576p_RANGE_OR_LOWER : - (inputSize < INPUT_SIZE_1080p_TH) ? INPUT_SIZE_1080i_RANGE : - (inputSize < INPUT_SIZE_4K_TH) ? INPUT_SIZE_1080p_RANGE : + EB_U8 inputResolution = (inputSize < INPUT_SIZE_1080i_TH) ? INPUT_SIZE_576p_RANGE_OR_LOWER : + (inputSize < INPUT_SIZE_1080p_TH) ? INPUT_SIZE_1080i_RANGE : + (inputSize < INPUT_SIZE_4K_TH) ? INPUT_SIZE_1080p_RANGE : INPUT_SIZE_4K_RANGE; if (inputResolution <= INPUT_SIZE_1080i_RANGE) { @@ -2516,14 +2473,14 @@ static EB_ERRORTYPE VerifySettings(\ sequenceControlSetPtr->maxEncMode = MAX_SUPPORTED_MODES_SUB1080P - 1; if (config->encMode > MAX_SUPPORTED_MODES_SUB1080P -1) { SVT_LOG("SVT [Error]: Instance %u: encMode must be [0 - %d]\n", channelNumber + 1, MAX_SUPPORTED_MODES_SUB1080P-1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } }else if (inputResolution == INPUT_SIZE_1080p_RANGE){ sequenceControlSetPtr->maxEncMode = MAX_SUPPORTED_MODES_1080P - 1; if (config->encMode > MAX_SUPPORTED_MODES_1080P - 1) { SVT_LOG("SVT [Error]: Instance %u: encMode must be [0 - %d]\n", channelNumber + 1, MAX_SUPPORTED_MODES_1080P - 1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } }else { if (config->tune == 0) sequenceControlSetPtr->maxEncMode = MAX_SUPPORTED_MODES_4K_SQ - 1; @@ -2532,29 +2489,29 @@ static EB_ERRORTYPE VerifySettings(\ if (config->encMode > MAX_SUPPORTED_MODES_4K_SQ - 1 && config->tune == 0) { SVT_LOG("SVT [Error]: Instance %u: encMode must be [0 - %d]\n", channelNumber + 1, MAX_SUPPORTED_MODES_4K_SQ-1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; }else if (config->encMode > MAX_SUPPORTED_MODES_4K_OQ - 1 && config->tune >= 1) { SVT_LOG("SVT [Error]: Instance %u: encMode must be [0 - %d]\n", channelNumber + 1, MAX_SUPPORTED_MODES_4K_OQ-1); - return_error = EB_ErrorBadParameter; - } - } + return_error = EB_ErrorBadParameter; + } + } if(config->qp > 51) { SVT_LOG("SVT [Error]: Instance %u: QP must be [0 - 51]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (config->hierarchicalLevels > 3) { SVT_LOG("SVT [Error]: Instance %u: Hierarchical Levels supported [0-3]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (config->intraPeriodLength < -2 || config->intraPeriodLength > 255) { SVT_LOG("SVT [Error]: Instance %u: The intra period must be [-2 - 255] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if( config->intraRefreshType > 2 || config->intraRefreshType < 1) { + if( config->intraRefreshType > 2 || config->intraRefreshType < 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid intra Refresh Type [1-2]\n",channelNumber+1); return_error = EB_ErrorBadParameter; } @@ -2567,45 +2524,41 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if ( config->disableDlfFlag > 1) { + if ( config->disableDlfFlag > 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid LoopFilterDisable. LoopFilterDisable must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->hrdFlag > 1) - { - printf("Error Instance %u: hrdFlag must be [0 - 1]\n", channelNumber + 1); + if (config->hrdFlag > 1) { + SVT_LOG("SVT [Error]: Instance %u: hrdFlag must be [0 - 1]\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->hrdFlag == 1 && ((config->vbvBufsize <= 0) || (config->vbvMaxrate <= 0))) - { - printf("Error instance %u: hrd requires vbv max rate and vbv bufsize to be greater than 0 ", channelNumber + 1); + if (config->hrdFlag == 1 && ((config->vbvBufsize <= 0) || (config->vbvMaxrate <= 0))) { + SVT_LOG("SVT [Error]: Instance %u: hrd requires vbv max rate and vbv bufsize to be greater than 0 ", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if ( config->enableSaoFlag > 1) { + if ( config->enableSaoFlag > 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid SAO. SAO range must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if ( config->useDefaultMeHme > 1 ){ + if ( config->useDefaultMeHme > 1 ){ SVT_LOG("SVT [Error]: Instance %u: invalid useDefaultMeHme. useDefaultMeHme must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if ( config->enableHmeFlag > 1 ){ SVT_LOG("SVT [Error]: Instance %u: invalid HME. HME must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } - if ((config->searchAreaWidth > 256) || (config->searchAreaWidth == 0)){ + return_error = EB_ErrorBadParameter; + } + if ((config->searchAreaWidth > 256) || (config->searchAreaWidth == 0)){ SVT_LOG("SVT [Error]: Instance %u: Invalid SearchAreaWidth. SearchAreaWidth must be [1 - 256]\n",channelNumber+1); return_error = EB_ErrorBadParameter; - } - if((config->searchAreaHeight > 256) || (config->searchAreaHeight == 0)) { + if((config->searchAreaHeight > 256) || (config->searchAreaHeight == 0)) { SVT_LOG("SVT [Error]: Instance %u: Invalid SearchAreaHeight. SearchAreaHeight must be [1 - 256]\n",channelNumber+1); return_error = EB_ErrorBadParameter; - } if (levelIdx < 13) { @@ -2621,47 +2574,47 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->rateControlMode) && (config->tier == 0) && ((config->targetBitRate*2) > mainTierMaxBitRate[levelIdx])){ + if ((config->level != 0) && (config->rateControlMode) && (config->tier == 0) && ((config->targetBitRate*2) > mainTierMaxBitRate[levelIdx])){ SVT_LOG("SVT [Error]: Instance %u: Allowed MaxBitRate exceeded for level %s and tier 0 \n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->rateControlMode) && (config->tier == 1) && ((config->targetBitRate*2) > highTierMaxBitRate[levelIdx])){ + if ((config->level != 0) && (config->rateControlMode) && (config->tier == 1) && ((config->targetBitRate*2) > highTierMaxBitRate[levelIdx])){ SVT_LOG("SVT [Error]: Instance %u: Allowed MaxBitRate exceeded for level %s and tier 1 \n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->rateControlMode) && (config->tier == 0) && ((config->targetBitRate * 3) > mainTierCPB[levelIdx])) { + if ((config->level != 0) && (config->rateControlMode) && (config->tier == 0) && ((config->targetBitRate * 3) > mainTierCPB[levelIdx])) { SVT_LOG("SVT [Error]: Instance %u: Out of bound maxBufferSize for level %s and tier 0 \n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->rateControlMode) && (config->tier == 1) && ((config->targetBitRate * 3) > highTierCPB[levelIdx])) { + if ((config->level != 0) && (config->rateControlMode) && (config->tier == 1) && ((config->targetBitRate * 3) > highTierCPB[levelIdx])) { SVT_LOG("SVT [Error]: Instance %u: Out of bound maxBufferSize for level %s and tier 1 \n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } // Table A.6 General tier and level limits - if ((config->level != 0) && (config->tileColumnCount > maxTileColumn[levelIdx])) { + if ((config->level != 0) && (config->tileColumnCount > maxTileColumn[levelIdx])) { SVT_LOG("SVT [Error]: Instance %u: Out of bound maxTileColumn for level %s\n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->tileRowCount > maxTileRow[levelIdx])) { + if ((config->level != 0) && (config->tileRowCount > maxTileRow[levelIdx])) { SVT_LOG("SVT [Error]: Instance %u: Out of bound maxTileRow for level %s\n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } } - if(config->profile > 4){ + if(config->profile > 4){ SVT_LOG("SVT [Error]: Instance %u: The maximum allowed Profile number is 4 or MAINEXT \n",channelNumber+1); return_error = EB_ErrorBadParameter; } - if (config->profile == 0){ + if (config->profile == 0){ SVT_LOG("SVT [Error]: Instance %u: The minimum allowed Profile number is 1 \n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } - if(config->profile == 3) { + if(config->profile == 3) { SVT_LOG("SVT [Error]: Instance %u: The Main Still Picture Profile is not supported \n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if(config->encoderColorFormat >= EB_YUV422 && config->profile != 4) { @@ -2683,14 +2636,14 @@ static EB_ERRORTYPE VerifySettings(\ SVT_LOG("SVT [Error]: Instance %u: The intra period must be [-2 - 255] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->constrainedIntra > 1) { + if (config->constrainedIntra > 1) { SVT_LOG("SVT [Error]: Instance %u: The constrained intra must be [0 - 1] \n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } - if (config->rateControlMode > 1) { + return_error = EB_ErrorBadParameter; + } + if (config->rateControlMode > 1) { SVT_LOG("SVT [Error]: Instance %u: The rate control mode must be [0 - 1] \n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (config->tune > 0 && config->bitRateReduction == 1){ SVT_LOG("SVT [Error]: Instance %u: Bit Rate Reduction is not supported for OQ mode (Tune = 1 ) and VMAF mode (Tune = 2)\n", channelNumber + 1); @@ -2706,73 +2659,73 @@ static EB_ERRORTYPE VerifySettings(\ SVT_LOG("SVT [Error]: Instance %u: The lookahead distance must be [0 - 250] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->sceneChangeDetection > 1) { + if (config->sceneChangeDetection > 1) { SVT_LOG("SVT [Error]: Instance %u: The scene change detection must be [0 - 1] \n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } - if ( config->maxQpAllowed > 51) { + return_error = EB_ErrorBadParameter; + } + if ( config->maxQpAllowed > 51) { SVT_LOG("SVT [Error]: Instance %u: MaxQpAllowed must be [0 - 51]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } - else if ( config->minQpAllowed > 50 ) { + return_error = EB_ErrorBadParameter; + } + else if ( config->minQpAllowed > 50 ) { SVT_LOG("SVT [Error]: Instance %u: MinQpAllowed must be [0 - 50]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } - else if ( (config->minQpAllowed) > (config->maxQpAllowed)) { + return_error = EB_ErrorBadParameter; + } + else if ( (config->minQpAllowed) > (config->maxQpAllowed)) { SVT_LOG("SVT [Error]: Instance %u: MinQpAllowed must be smaller than MaxQpAllowed\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } - if (config->videoUsabilityInfo > 1) { + if (config->videoUsabilityInfo > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid VideoUsabilityInfo. VideoUsabilityInfo must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (config->tune > 2) { SVT_LOG("SVT [Error]: Instance %u : Invalid Tune. Tune must be [0 - 2]\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->bitRateReduction > 1) { + if (config->bitRateReduction > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid BitRateReduction. BitRateReduction must be [0 - 1]\n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (config->improveSharpness > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid ImproveSharpness. ImproveSharpness must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (config->highDynamicRangeInput > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid HighDynamicRangeInput. HighDynamicRangeInput must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->accessUnitDelimiter > 1) { + if (config->accessUnitDelimiter > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid AccessUnitDelimiter. AccessUnitDelimiter must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->bufferingPeriodSEI > 1) { + if (config->bufferingPeriodSEI > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid BufferingPeriod. BufferingPeriod must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->pictureTimingSEI > 1) { + if (config->pictureTimingSEI > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid PictureTiming. PictureTiming must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->registeredUserDataSeiFlag > 1) { + if (config->registeredUserDataSeiFlag > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid RegisteredUserData. RegisteredUserData must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->unregisteredUserDataSeiFlag > 1) { + if (config->unregisteredUserDataSeiFlag > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid UnregisteredUserData. UnregisteredUserData must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->recoveryPointSeiFlag > 1) { + if (config->recoveryPointSeiFlag > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid RecoveryPoint. RecoveryPoint must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (config->useMasteringDisplayColorVolume > 1) { @@ -2785,34 +2738,34 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if ((config->maxCLL && !config->highDynamicRangeInput) || (config->maxFALL && !config->highDynamicRangeInput)) { - SVT_LOG("Error Instance %u: maxCLL or maxFALL should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if ((config->maxCLL && !config->highDynamicRangeInput) || (config->maxFALL && !config->highDynamicRangeInput)) { + SVT_LOG("SVT [Error]: Instance %u: maxCLL or maxFALL should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->useMasteringDisplayColorVolume && !config->highDynamicRangeInput) { - SVT_LOG("Error Instance %u: MasterDisplay should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if (config->useMasteringDisplayColorVolume && !config->highDynamicRangeInput) { + SVT_LOG("SVT [Error]: Instance %u: MasterDisplay should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->dolbyVisionProfile != 0 && config->dolbyVisionProfile != 81) { - SVT_LOG("Error Instance %u: Only Dolby Vision Profile 8.1 is supported \n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if (config->dolbyVisionProfile != 0 && config->dolbyVisionProfile != 81) { + SVT_LOG("SVT [Error]: Instance %u: Only Dolby Vision Profile 8.1 is supported \n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->dolbyVisionProfile == 81 && config->encoderBitDepth != 10) { - SVT_LOG("Error Instance %u: Dolby Vision Profile 8.1 work only with main10 input \n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if (config->dolbyVisionProfile == 81 && config->encoderBitDepth != 10) { + SVT_LOG("SVT [Error]: Instance %u: Dolby Vision Profile 8.1 work only with main10 input \n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->dolbyVisionProfile == 81 && !config->useMasteringDisplayColorVolume) { - SVT_LOG("Error Instance %u: Dolby Vision Profile 8.1 requires mastering display color volume information \n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if (config->dolbyVisionProfile == 81 && !config->useMasteringDisplayColorVolume) { + SVT_LOG("SVT [Error]: Instance %u: Dolby Vision Profile 8.1 requires mastering display color volume information \n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->enableTemporalId > 1) { + if (config->enableTemporalId > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid TemporalId. TemporalId must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (config->pictureTimingSEI && !config->videoUsabilityInfo){ @@ -2820,23 +2773,22 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if ( (config->encoderBitDepth !=8 ) && - (config->encoderBitDepth !=10 ) - ) { + if ( (config->encoderBitDepth !=8 ) && + (config->encoderBitDepth !=10 ) + ) { SVT_LOG("SVT [Error]: Instance %u: Encoder Bit Depth shall be only 8 or 10 \n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } // Check if the EncoderBitDepth is conformant with the Profile constraint - if(config->profile == 1 && config->encoderBitDepth == 10) { + if(config->profile == 1 && config->encoderBitDepth == 10) { SVT_LOG("SVT [Error]: Instance %u: The encoder bit depth shall be equal to 8 for Main Profile\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } - if (config->compressedTenBitFormat > 1) - { + if (config->compressedTenBitFormat > 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid Compressed Ten Bit Format shall be only [0 - 1] \n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (config->speedControlFlag > 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid Speed Control flag [0 - 1]\n", channelNumber + 1); @@ -2869,15 +2821,15 @@ static EB_ERRORTYPE VerifySettings(\ } if (config->vbvBufInit > 100) { - printf("Error instance %u: Invalid vbvBufInit [0 - 100]\n", channelNumber + 1); + SVT_LOG("SVT [Error]: Instance %u: Invalid vbvBufInit [0 - 100]\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } if (config->vbvBufEnd > 100) { - printf("Error instance %u: Invalid vbvBufEnd [0 - 100]\n", channelNumber + 1); + SVT_LOG("SVT [Error]: Instance %u: Invalid vbvBufEnd [0 - 100]\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } if (config->vbvEndFrameAdjust > 100) { - printf("Error instance %u: Invalid vbvEndFrameAdjust [0 - 100]\n", channelNumber + 1); + SVT_LOG("SVT [Error]: Instance %u: Invalid vbvEndFrameAdjust [0 - 100]\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } @@ -2933,7 +2885,6 @@ static EB_ERRORTYPE VerifySettings(\ } } #endif - return return_error; } @@ -3158,8 +3109,8 @@ EB_API EB_ERRORTYPE EbH265EncSetParameter( // Set the Prediction Structure pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->predStructPtr = GetPredictionStructure( - pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->predictionStructureGroupPtr, - pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.predStructure, + pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->predictionStructureGroupPtr, + pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.predStructure, pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxRefCount, pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxTemporalLayers); @@ -3266,7 +3217,7 @@ EB_API EB_ERRORTYPE EbH265EncStreamHeader( (EB_U32*) &(outputStreamBuffer->nFilledLen), (EB_U32*) &(outputStreamBuffer->nAllocLen), encodeContextPtr, - NAL_UNIT_INVALID); + NAL_UNIT_INVALID); *outputStreamPtr = outputStreamBuffer; @@ -3322,7 +3273,7 @@ EB_API EB_ERRORTYPE EbH265EncEosNal( (EB_U32*) &(outputStreamBuffer->nFilledLen), (EB_U32*) &(outputStreamBuffer->nAllocLen), encodeContextPtr, - NAL_UNIT_INVALID); + NAL_UNIT_INVALID); *outputStreamPtr = outputStreamBuffer; @@ -4007,7 +3958,7 @@ EB_ERRORTYPE EbOutputBufferHeaderCtor( outBufPtr->nAllocLen = nStride; outBufPtr->pAppPrivate = NULL; - (void)objectInitDataPtr; + (void)objectInitDataPtr; return EB_ErrorNone; } diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index fa4789896..e74374981 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -4096,7 +4096,6 @@ static EB_ERRORTYPE EncodeCoeff( coeffBuffer = (EB_S16*)&coeffPtr->bufferY[coeffLocation * sizeof(EB_S16)]; if (tuPtr->lumaCbf) { - EncodeQuantizedCoefficientsFuncArray[!!(ASM_TYPES & PREAVX2_MASK)]( cabacEncodeCtxPtr, tuSize, @@ -4134,7 +4133,6 @@ static EB_ERRORTYPE EncodeCoeff( if (cabacEncodeCtxPtr->colorFormat == EB_YUV422 && tuPtr->cbCbf2) { coeffLocation = (tuOriginX >> 1) + ((tuOriginY+tuChromaSize) * coeffPtr->strideCb); coeffBuffer = (EB_S16*)&coeffPtr->bufferCb[coeffLocation * sizeof(EB_S16)]; - EncodeQuantizedCoefficientsFuncArray[!!(ASM_TYPES & PREAVX2_MASK)]( cabacEncodeCtxPtr, tuChromaSize, @@ -4184,7 +4182,6 @@ static EB_ERRORTYPE EncodeCoeff( if (cabacEncodeCtxPtr->colorFormat == EB_YUV422 && tuPtr->crCbf2) { coeffLocation = (tuOriginX >> 1) + ((tuOriginY+tuChromaSize) * coeffPtr->strideCr); coeffBuffer = (EB_S16*)&coeffPtr->bufferCr[coeffLocation * sizeof(EB_S16)]; - EncodeQuantizedCoefficientsFuncArray[!!(ASM_TYPES & PREAVX2_MASK)]( cabacEncodeCtxPtr, tuChromaSize, @@ -5463,8 +5460,7 @@ static void CodeVPS( WriteFlagCavlc( bitstreamPtr, scsPtr->staticConfig.fpsInVps == 1 ? EB_TRUE : EB_FALSE); - if (scsPtr->staticConfig.fpsInVps == 1) - { + if (scsPtr->staticConfig.fpsInVps == 1) { if (scsPtr->staticConfig.frameRateDenominator != 0 && scsPtr->staticConfig.frameRateNumerator != 0) { @@ -7086,7 +7082,6 @@ static EB_ERRORTYPE Intra4x4EncodeChromaCoeff( MIN_PU_SIZE, &countNonZeroCoeffs); - EncodeQuantizedCoefficientsFuncArray[!!(ASM_TYPES & PREAVX2_MASK)]( cabacEncodeCtxPtr, MIN_PU_SIZE, @@ -7115,7 +7110,6 @@ static EB_ERRORTYPE Intra4x4EncodeChromaCoeff( MIN_PU_SIZE, &countNonZeroCoeffs); - EncodeQuantizedCoefficientsFuncArray[!!(ASM_TYPES & PREAVX2_MASK)]( cabacEncodeCtxPtr, MIN_PU_SIZE, @@ -7429,8 +7423,7 @@ EB_ERRORTYPE EstimateLcu( cuPtr->qp); // Code the skip flag - if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) - { + if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) { EncodeSkipFlag( cabacEncodeCtxPtr, (EB_BOOL)cuPtr->skipFlag, @@ -7440,19 +7433,16 @@ EB_ERRORTYPE EstimateLcu( skipFlagNeighborArray); } - if (cuPtr->skipFlag) - { + if (cuPtr->skipFlag) { // Merge Index EncodeMergeIndex( cabacEncodeCtxPtr, &cuPtr->predictionUnitArray[0]); } - else - { + else { // Code CU pred mode (I, P, B, etc.) // (not needed for Intra Slice) - if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) - { + if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) { EncodePredictionMode( cabacEncodeCtxPtr, cuPtr); @@ -7553,9 +7543,7 @@ EB_ERRORTYPE EstimateLcu( tbPtr->quantizedCoeffsBits += cuQuantizedCoeffsBits; } - else - - { + else { // Code Partition Size EncodePartitionSize( cabacEncodeCtxPtr, diff --git a/Source/Lib/Codec/EbIntraPrediction.c b/Source/Lib/Codec/EbIntraPrediction.c index 3e2569c94..5eeb89ac7 100644 --- a/Source/Lib/Codec/EbIntraPrediction.c +++ b/Source/Lib/Codec/EbIntraPrediction.c @@ -3574,7 +3574,6 @@ static inline void IntraModeAngular16bit_all( switch(mode){ case 34: - IntraAng34_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, refSamples, @@ -3710,7 +3709,6 @@ EB_ERRORTYPE IntraPredictionCl( switch(funcIndex) { case 0: - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -3724,7 +3722,6 @@ EB_ERRORTYPE IntraPredictionCl( break; case 1: - yIntraReferenceArray = contextPtr->yIntraReferenceArrayReverse; IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( @@ -3737,8 +3734,7 @@ EB_ERRORTYPE IntraPredictionCl( break; case 2: - - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : + yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -3751,7 +3747,6 @@ EB_ERRORTYPE IntraPredictionCl( break; case 3: - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -3765,7 +3760,6 @@ EB_ERRORTYPE IntraPredictionCl( break; case 4: - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArray : contextPtr->yIntraReferenceArray; yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : @@ -3815,7 +3809,6 @@ EB_ERRORTYPE IntraPredictionCl( switch(funcIndex) { case 0: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( @@ -3839,7 +3832,6 @@ EB_ERRORTYPE IntraPredictionCl( break; case 2: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraVerticalChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -3863,7 +3855,6 @@ EB_ERRORTYPE IntraPredictionCl( break; case 3: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraHorzChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -3887,7 +3878,6 @@ EB_ERRORTYPE IntraPredictionCl( break; case 1: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraDCChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -3911,7 +3901,6 @@ EB_ERRORTYPE IntraPredictionCl( break; case 4: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraModeAngular_all( @@ -4015,7 +4004,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( switch(funcIndex) { case 0: - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -4029,7 +4017,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 1: - yIntraReferenceArray = contextPtr->yIntraReferenceArrayReverse; IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( @@ -4042,7 +4029,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 2: - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -4056,7 +4042,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 3: - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -4070,7 +4055,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 4: - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArray : contextPtr->yIntraReferenceArray; yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : @@ -4111,8 +4095,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( switch(funcIndex) { case 0: - - // Cb Intra Prediction + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( chromaPuSize, @@ -4135,7 +4118,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 2: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraVerticalChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -4159,7 +4141,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 3: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraHorzChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -4183,7 +4164,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 1: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraDCChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -4207,7 +4187,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 4: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraModeAngular_all( @@ -4282,7 +4261,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( switch(funcIndex) { case 0: - IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -4293,7 +4271,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( break; case 1: - IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -4304,7 +4281,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( break; case 2: - IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -4315,8 +4291,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( break; case 3: - - IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( + IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, &(candidateBufferPtr->predictionPtr->bufferY[puOriginIndex]), @@ -4326,7 +4301,6 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( break; case 4: - IntraModeAngular_all( openLoopIntraCandidateIndex, puSize, @@ -4407,8 +4381,7 @@ EB_ERRORTYPE EncodePassIntraPrediction( switch(lumaMode) { case EB_INTRA_PLANAR: - - IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( + IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, yIntraReferenceArrayReverse, predictionPtr->bufferY + lumaOffset, @@ -4530,7 +4503,6 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; case EB_INTRA_VERTICAL: - // Cb Intra Prediction IntraVerticalChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -4552,8 +4524,7 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; case EB_INTRA_HORIZONTAL: - - // Cb Intra Prediction + // Cb Intra Prediction IntraHorzChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, @@ -4575,7 +4546,6 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; case EB_INTRA_DC: - // Cb Intra Prediction IntraDCChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -4696,8 +4666,7 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( switch(lumaMode) { case EB_INTRA_PLANAR: - - IntraPlanar_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( + IntraPlanar_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, yIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferY + lumaOffset, @@ -4706,7 +4675,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( break; case EB_INTRA_DC: - IntraDCLuma_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, yIntraReferenceArrayReverse, @@ -4716,7 +4684,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( break; case EB_INTRA_VERTICAL: - IntraVerticalLuma_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, yIntraReferenceArrayReverse, @@ -4727,7 +4694,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( break; case EB_INTRA_HORIZONTAL: - IntraHorzLuma_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, yIntraReferenceArrayReverse, @@ -4799,7 +4765,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( switch(chromaModeAdj) { case EB_INTRA_PLANAR: - IntraPlanar_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, cbIntraReferenceArrayReverse, @@ -4817,7 +4782,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( break; case EB_INTRA_VERTICAL: - IntraVerticalChroma_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, cbIntraReferenceArrayReverse, @@ -4835,7 +4799,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( break; case EB_INTRA_HORIZONTAL: - IntraHorzChroma_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, cbIntraReferenceArrayReverse, @@ -4853,7 +4816,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( break; case EB_INTRA_DC: - IntraDCChroma_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, cbIntraReferenceArrayReverse, @@ -5334,8 +5296,7 @@ EB_ERRORTYPE IntraPredictionOpenLoop( switch(funcIndex) { case 0: - - IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( + IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( cuSize, contextPtr->intraRefPtr->yIntraReferenceArrayReverse, (&(contextPtr->meContextPtr->lcuBuffer[0])), @@ -5345,8 +5306,7 @@ EB_ERRORTYPE IntraPredictionOpenLoop( break; case 1: - - IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( + IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( cuSize, contextPtr->intraRefPtr->yIntraReferenceArrayReverse, (&(contextPtr->meContextPtr->lcuBuffer[0])), @@ -5356,8 +5316,7 @@ EB_ERRORTYPE IntraPredictionOpenLoop( break; case 2: - - IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( + IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( cuSize, contextPtr->intraRefPtr->yIntraReferenceArrayReverse, (&(contextPtr->meContextPtr->lcuBuffer[0])), @@ -5367,8 +5326,7 @@ EB_ERRORTYPE IntraPredictionOpenLoop( break; case 3: - - IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( + IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( cuSize, contextPtr->intraRefPtr->yIntraReferenceArrayReverse, (&(contextPtr->meContextPtr->lcuBuffer[0])), @@ -5378,8 +5336,7 @@ EB_ERRORTYPE IntraPredictionOpenLoop( break; case 4: - - IntraModeAngular_all( + IntraModeAngular_all( openLoopIntraCandidateIndex, cuSize, contextPtr->intraRefPtr->yIntraReferenceArray, @@ -5452,7 +5409,6 @@ EB_ERRORTYPE IntraPredictionOl( switch (funcIndex) { case 0: - IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5463,7 +5419,6 @@ EB_ERRORTYPE IntraPredictionOl( break; case 1: - IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5474,7 +5429,6 @@ EB_ERRORTYPE IntraPredictionOl( break; case 2: - IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5485,7 +5439,6 @@ EB_ERRORTYPE IntraPredictionOl( break; case 3: - IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5496,7 +5449,6 @@ EB_ERRORTYPE IntraPredictionOl( break; case 4: - IntraModeAngular_all( openLoopIntraCandidateIndex, puSize, @@ -5543,7 +5495,6 @@ EB_ERRORTYPE IntraPredictionOl( switch (funcIndex) { case 0: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( @@ -5567,7 +5518,6 @@ EB_ERRORTYPE IntraPredictionOl( break; case 2: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraVerticalChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -5591,7 +5541,6 @@ EB_ERRORTYPE IntraPredictionOl( break; case 3: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraHorzChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -5615,7 +5564,6 @@ EB_ERRORTYPE IntraPredictionOl( break; case 1: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraDCChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -5639,7 +5587,6 @@ EB_ERRORTYPE IntraPredictionOl( break; case 4: - // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraModeAngular_all( @@ -5730,7 +5677,6 @@ EB_ERRORTYPE IntraPredOnSrc( switch (funcIndex) { case 0: - IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5741,7 +5687,6 @@ EB_ERRORTYPE IntraPredOnSrc( break; case 1: - IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5752,7 +5697,6 @@ EB_ERRORTYPE IntraPredOnSrc( break; case 2: - IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5763,7 +5707,6 @@ EB_ERRORTYPE IntraPredOnSrc( break; case 3: - IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5774,7 +5717,6 @@ EB_ERRORTYPE IntraPredOnSrc( break; case 4: - IntraModeAngular_all( openLoopIntraCandidateIndex, puSize, diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index 10293c912..4b5e61ba3 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -434,7 +434,7 @@ void* PictureManagerKernel(void *inputPtr) referenceEntryPtr->releaseEnable = EB_TRUE; referenceEntryPtr->referenceAvailable = EB_FALSE; referenceEntryPtr->isUsedAsReferenceFlag = pictureControlSetPtr->isUsedAsReferenceFlag; - referenceEntryPtr->feedbackArrived = EB_FALSE; + referenceEntryPtr->feedbackArrived = EB_FALSE; encodeContextPtr->referencePictureQueueTailIndex = (encodeContextPtr->referencePictureQueueTailIndex == REFERENCE_QUEUE_MAX_DEPTH - 1) ? 0 : encodeContextPtr->referencePictureQueueTailIndex + 1; @@ -535,18 +535,14 @@ void* PictureManagerKernel(void *inputPtr) referenceQueueIndex = encodeContextPtr->referencePictureQueueHeadIndex; // Find the Reference in the Reference Queue do { - referenceEntryPtr = encodeContextPtr->referencePictureQueue[referenceQueueIndex]; - if (referenceEntryPtr->pictureNumber == inputPictureDemuxPtr->pictureNumber) { // Set the feedback arrived referenceEntryPtr->feedbackArrived = EB_TRUE; } - // Increment the referenceQueueIndex Iterator referenceQueueIndex = (referenceQueueIndex == REFERENCE_QUEUE_MAX_DEPTH - 1) ? 0 : referenceQueueIndex + 1; - } while ((referenceQueueIndex != encodeContextPtr->referencePictureQueueTailIndex) && (referenceEntryPtr->pictureNumber != inputPictureDemuxPtr->pictureNumber)); //keep the relase of SCS here because we still need the encodeContext strucutre here From c5cdcfef4cfc7f961d30608bbf4acd6fdd8ab62b Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Fri, 31 May 2019 14:01:03 +0530 Subject: [PATCH 56/93] Fix for Deadlock --- Source/Lib/Codec/EbPacketizationProcess.c | 2 -- Source/Lib/Codec/EbRateControlProcess.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 2135ae7b1..463245120 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -913,9 +913,7 @@ void* PacketizationKernel(void *inputPtr) bufferfill_temp -= queueEntryPtr->fillerBitsFinal; bufferfill_temp = MIN(bufferfill_temp, encodeContextPtr->vbvBufsize); encodeContextPtr->bufferFill = (EB_U64)(bufferfill_temp); - EbBlockOnMutex(encodeContextPtr->fillerBitMutex); encodeContextPtr->fillerBitError = (EB_S64)(queueEntryPtr->fillerBitsFinal - queueEntryPtr->fillerBitsSent); - EbReleaseMutex(encodeContextPtr->fillerBitMutex); EbReleaseMutex(encodeContextPtr->bufferFillMutex); } EbPostFullObject(outputStreamWrapperPtr); diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index fe281e5c0..81024335b 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -1919,12 +1919,10 @@ void FrameLevelRcFeedbackPictureMode2( (EB_S64)rateControlParamPtr->previousVirtualBufferLevel; } else{ - EbBlockOnMutex(sequenceControlSetPtr->encodeContextPtr->fillerBitMutex); rateControlParamPtr->virtualBufferLevel = (EB_S64)rateControlParamPtr->previousVirtualBufferLevel + (EB_S64)previousFrameBitActual - (EB_S64)rateControlLayerPtr->channelBitRate; contextPtr->extraBitsGen -= ((EB_S64)previousFrameBitActual + sequenceControlSetPtr->encodeContextPtr->fillerBitError) - (EB_S64)rateControlLayerPtr->channelBitRate; - EbReleaseMutex(sequenceControlSetPtr->encodeContextPtr->fillerBitMutex); } if (parentPictureControlSetPtr->hierarchicalLevels > 1 && rateControlLayerPtr->frameSameSADMinQpCount > 10){ From beda1ecbb4d5b0e4a535599adb91f5463b1472b1 Mon Sep 17 00:00:00 2001 From: anaghdin Date: Wed, 27 Mar 2019 15:40:54 -0700 Subject: [PATCH 57/93] Remove the macro PACK_FEEDBACK --- Source/Lib/Codec/EbDefinitions.h | 1 - Source/Lib/Codec/EbEncHandle.c | 10 ++------ Source/Lib/Codec/EbPacketizationProcess.c | 16 ++----------- Source/Lib/Codec/EbPacketizationProcess.h | 8 ++----- Source/Lib/Codec/EbPictureDemuxResults.h | 6 ++--- Source/Lib/Codec/EbPictureManagerProcess.c | 2 -- Source/Lib/Codec/EbRateControlProcess.c | 23 ------------------- .../Lib/Codec/EbResourceCoordinationProcess.c | 8 ------- 8 files changed, 8 insertions(+), 66 deletions(-) diff --git a/Source/Lib/Codec/EbDefinitions.h b/Source/Lib/Codec/EbDefinitions.h index c3d2b8100..ee3305bc8 100644 --- a/Source/Lib/Codec/EbDefinitions.h +++ b/Source/Lib/Codec/EbDefinitions.h @@ -10,7 +10,6 @@ #ifdef __cplusplus extern "C" { #endif -#define PACK_FEEDBACK 1 // Internal Marcos #define NON_AVX512_SUPPORT diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index 1df1e295f..3078568d1 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -1122,11 +1122,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) return_error = EbSystemResourceCtor( &encHandlePtr->pictureDemuxResultsResourcePtr, encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->pictureDemuxFifoInitCount, -#if PACK_FEEDBACK encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->encDecProcessInitCount + 1, // 1 for packetization -#else - encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->encDecProcessInitCount, -#endif EB_PictureManagerProcessInitCount, &encHandlePtr->pictureDemuxResultsProducerFifoPtrArray, &encHandlePtr->pictureDemuxResultsConsumerFifoPtrArray, @@ -1444,10 +1440,8 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) return_error = PacketizationContextCtor( (PacketizationContext_t**) &encHandlePtr->packetizationContextPtr, encHandlePtr->entropyCodingResultsConsumerFifoPtrArray[0], - encHandlePtr->rateControlTasksProducerFifoPtrArray[RateControlPortLookup(RATE_CONTROL_INPUT_PORT_PACKETIZATION, 0)] -#if PACK_FEEDBACK - ,encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->encDecProcessInitCount] // Add port lookup logic here JMJ -#endif + encHandlePtr->rateControlTasksProducerFifoPtrArray[RateControlPortLookup(RATE_CONTROL_INPUT_PORT_PACKETIZATION, 0)], + encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->encDecProcessInitCount] // Add port lookup logic here JMJ ); if (return_error == EB_ErrorInsufficientResources){ diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 463245120..c8e420a0d 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -17,9 +17,7 @@ #include "EbRateControlTasks.h" #include "EbRateControlProcess.h" #include "EbTime.h" -#if PACK_FEEDBACK #include "EbPictureDemuxResults.h" -#endif void HrdFullness(SequenceControlSet_t *sequenceControlSetPtr, PictureControlSet_t *pictureControlSetptr, AppBufferingPeriodSei_t *seiBP) { @@ -98,10 +96,8 @@ void InitHRD(SequenceControlSet_t *scsPtr) EB_ERRORTYPE PacketizationContextCtor( PacketizationContext_t **contextDblPtr, EbFifo_t *entropyCodingInputFifoPtr, - EbFifo_t *rateControlTasksOutputFifoPtr -#if PACK_FEEDBACK - ,EbFifo_t *pictureManagerOutputFifoPtr -#endif + EbFifo_t *rateControlTasksOutputFifoPtr, + EbFifo_t *pictureManagerOutputFifoPtr ) { PacketizationContext_t *contextPtr; @@ -110,9 +106,7 @@ EB_ERRORTYPE PacketizationContextCtor( contextPtr->entropyCodingInputFifoPtr = entropyCodingInputFifoPtr; contextPtr->rateControlTasksOutputFifoPtr = rateControlTasksOutputFifoPtr; -#if PACK_FEEDBACK contextPtr->pictureManagerOutputFifoPtr = pictureManagerOutputFifoPtr; -#endif EB_MALLOC(EbPPSConfig_t*, contextPtr->ppsConfig, sizeof(EbPPSConfig_t), EB_N_PTR); @@ -141,10 +135,8 @@ void* PacketizationKernel(void *inputPtr) EB_BUFFERHEADERTYPE *outputStreamPtr; EbObjectWrapper_t *rateControlTasksWrapperPtr; RateControlTasks_t *rateControlTasksPtr; -#if PACK_FEEDBACK EbObjectWrapper_t *pictureManagerResultsWrapperPtr; PictureDemuxResults_t *pictureManagerResultPtr; -#endif // Bitstream copy to output buffer Bitstream_t bitstream; @@ -211,7 +203,6 @@ void* PacketizationKernel(void *inputPtr) rateControlTasksPtr->pictureControlSetWrapperPtr = pictureControlSetPtr->PictureParentControlSetWrapperPtr; rateControlTasksPtr->taskType = RC_PACKETIZATION_FEEDBACK_RESULT; -#if PACK_FEEDBACK if (sequenceControlSetPtr->staticConfig.rateControlMode) { // Get Empty Results Object EbGetEmptyObject( @@ -228,7 +219,6 @@ void* PacketizationKernel(void *inputPtr) (void) pictureManagerResultPtr; (void)pictureManagerResultsWrapperPtr; } -#endif sliceType = pictureControlSetPtr->sliceType; if(pictureControlSetPtr->pictureNumber == 0 && sequenceControlSetPtr->staticConfig.codeVpsSpsPps == 1) { @@ -749,12 +739,10 @@ void* PacketizationKernel(void *inputPtr) // Post Rate Control Taks EbPostFullObject(rateControlTasksWrapperPtr); -#if PACK_FEEDBACK if (sequenceControlSetPtr->staticConfig.rateControlMode) { // Post the Full Results Object EbPostFullObject(pictureManagerResultsWrapperPtr); } -#endif //Release the Parent PCS then the Child PCS EbReleaseObject(entropyCodingResultsPtr->pictureControlSetWrapperPtr);//Child diff --git a/Source/Lib/Codec/EbPacketizationProcess.h b/Source/Lib/Codec/EbPacketizationProcess.h index e8c25e230..ced44a311 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.h +++ b/Source/Lib/Codec/EbPacketizationProcess.h @@ -30,9 +30,7 @@ typedef struct PacketizationContext_s EbFifo_t *entropyCodingInputFifoPtr; EbFifo_t *rateControlTasksOutputFifoPtr; EbPPSConfig_t *ppsConfig; -#if PACK_FEEDBACK EbFifo_t *pictureManagerOutputFifoPtr; // to picture-manager -#endif } PacketizationContext_t; @@ -42,10 +40,8 @@ typedef struct PacketizationContext_s extern EB_ERRORTYPE PacketizationContextCtor( PacketizationContext_t **contextDblPtr, EbFifo_t *entropyCodingInputFifoPtr, - EbFifo_t *rateControlTasksOutputFifoPtr -#if PACK_FEEDBACK - , EbFifo_t *pictureManagerOutputFifoPtr -#endif + EbFifo_t *rateControlTasksOutputFifoPtr, + EbFifo_t *pictureManagerOutputFifoPtr ); diff --git a/Source/Lib/Codec/EbPictureDemuxResults.h b/Source/Lib/Codec/EbPictureDemuxResults.h index a387c99d8..39beb9dd9 100644 --- a/Source/Lib/Codec/EbPictureDemuxResults.h +++ b/Source/Lib/Codec/EbPictureDemuxResults.h @@ -14,10 +14,8 @@ typedef enum EB_PIC_TYPE { EB_PIC_INVALID = 0, EB_PIC_INPUT = 1, - EB_PIC_REFERENCE = 2 -#if PACK_FEEDBACK - ,EB_PIC_FEEDBACK = 3 -#endif + EB_PIC_REFERENCE = 2, + EB_PIC_FEEDBACK = 3 } EB_PIC_TYPE; /************************************** diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index 4b5e61ba3..364e7324d 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -526,7 +526,6 @@ void* PictureManagerKernel(void *inputPtr) EbReleaseObject(inputPictureDemuxPtr->sequenceControlSetWrapperPtr); break; -#if PACK_FEEDBACK case EB_PIC_FEEDBACK: sequenceControlSetPtr = (SequenceControlSet_t*)inputPictureDemuxPtr->sequenceControlSetWrapperPtr->objectPtr; @@ -550,7 +549,6 @@ void* PictureManagerKernel(void *inputPtr) EbReleaseObject(inputPictureDemuxPtr->sequenceControlSetWrapperPtr); break; -#endif default: sequenceControlSetPtr = (SequenceControlSet_t*) inputPictureDemuxPtr->sequenceControlSetWrapperPtr->objectPtr; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 81024335b..82b0543aa 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2719,29 +2719,6 @@ void* RateControlKernel(void *inputPtr) parentPictureControlSetPtr = (PictureParentControlSet_t*)rateControlTasksPtr->pictureControlSetWrapperPtr->objectPtr; sequenceControlSetPtr = (SequenceControlSet_t*)parentPictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; - // Update feedback arrived in referencepictureQueue - // printf("RC FEEDBACK ARRIVED %d\n", (int)parentPictureControlSetPtr->pictureNumber); -#if !PACK_FEEDBACK - ReferenceQueueEntry_t *referenceEntryPtr; - EB_U32 referenceQueueIndex; - encodeContextPtr = sequenceControlSetPtr->encodeContextPtr; - referenceQueueIndex = encodeContextPtr->referencePictureQueueHeadIndex; - // Find the Reference in the Reference Queue - do { - - referenceEntryPtr = encodeContextPtr->referencePictureQueue[referenceQueueIndex]; - - if (referenceEntryPtr->pictureNumber == parentPictureControlSetPtr->pictureNumber) { - - // Set the feedback arrived - referenceEntryPtr->feedbackArrived = EB_TRUE; - } - - // Increment the referenceQueueIndex Iterator - referenceQueueIndex = (referenceQueueIndex == REFERENCE_QUEUE_MAX_DEPTH - 1) ? 0 : referenceQueueIndex + 1; - - } while ((referenceQueueIndex != encodeContextPtr->referencePictureQueueTailIndex) && (referenceEntryPtr->pictureNumber != parentPictureControlSetPtr->pictureNumber)); -#endif // Frame level RC if (sequenceControlSetPtr->intraPeriodLength == -1 || sequenceControlSetPtr->staticConfig.rateControlMode == 0){ rateControlParamPtr = contextPtr->rateControlParamQueue[0]; diff --git a/Source/Lib/Codec/EbResourceCoordinationProcess.c b/Source/Lib/Codec/EbResourceCoordinationProcess.c index b2bf2441b..b07f0e26f 100644 --- a/Source/Lib/Codec/EbResourceCoordinationProcess.c +++ b/Source/Lib/Codec/EbResourceCoordinationProcess.c @@ -588,7 +588,6 @@ void* ResourceCoordinationKernel(void *inputPtr) } EbReleaseMutex(contextPtr->sequenceControlSetInstanceArray[instanceIndex]->configMutex); -#if PACK_FEEDBACK if (sequenceControlSetPtr->staticConfig.rateControlMode) { // Sequence Control Set is released by Rate Control after passing through MDC->MD->ENCDEC->Packetization->RateControl // ,in the PictureManager after receiving the reference and in PictureManager after receiving the feedback @@ -604,13 +603,6 @@ void* ResourceCoordinationKernel(void *inputPtr) 2); } -#else - // Sequence Control Set is released by Rate Control after passing through MDC->MD->ENCDEC->Packetization->RateControl - // and in the PictureManager - EbObjectIncLiveCount( - contextPtr->sequenceControlSetActiveArray[instanceIndex], - 2); -#endif // Set the current SequenceControlSet sequenceControlSetPtr = (SequenceControlSet_t*) contextPtr->sequenceControlSetActiveArray[instanceIndex]->objectPtr; From d1386a6a26418c26bdc76b1bb618985ddf33df31 Mon Sep 17 00:00:00 2001 From: zhoubo Date: Thu, 6 Jun 2019 13:39:50 +0800 Subject: [PATCH 58/93] Implement 16x16 SATD C and AVX2 functions. --- Source/Lib/ASM_AVX2/CMakeLists.txt | 5 + .../Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm | 10120 ++++++++++++++++ .../Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h | 23 + Source/Lib/ASM_AVX2/const-a.asm | 159 + Source/Lib/ASM_AVX2/x86inc.asm | 1664 +++ Source/Lib/ASM_AVX2/x86util.asm | 911 ++ Source/Lib/C_DEFAULT/CMakeLists.txt | 4 +- Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c | 80 + Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.h | 23 + Source/Lib/Codec/CMakeLists.txt | 1 + Source/Lib/Codec/EbMeSatdCalculation.h | 42 + Source/Lib/Codec/EbMotionEstimation.c | 21 +- Source/Lib/Codec/EbPictureControlSet.c | 3 +- Source/Lib/Codec/EbPictureControlSet.h | 1 + Source/Lib/Codec/EbRateControlProcess.c | 4 +- 15 files changed, 13047 insertions(+), 14 deletions(-) create mode 100644 Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm create mode 100644 Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h create mode 100644 Source/Lib/ASM_AVX2/const-a.asm create mode 100644 Source/Lib/ASM_AVX2/x86inc.asm create mode 100644 Source/Lib/ASM_AVX2/x86util.asm create mode 100644 Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c create mode 100644 Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.h create mode 100644 Source/Lib/Codec/EbMeSatdCalculation.h diff --git a/Source/Lib/ASM_AVX2/CMakeLists.txt b/Source/Lib/ASM_AVX2/CMakeLists.txt index dbb5a06ca..b428fe45b 100644 --- a/Source/Lib/ASM_AVX2/CMakeLists.txt +++ b/Source/Lib/ASM_AVX2/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(ASM_AVX2 STATIC EbComputeSAD_SadLoopKernel_AVX512.h EbIntraPrediction_AVX2.h EbMcp_AVX2.h + EbMeSatdCalculation_AVX2.h EbNoiseExtractAVX2.h EbPackUnPack_Intrinsic_AVX2.h EbPictureOperators_AVX2.h @@ -48,5 +49,9 @@ add_library(ASM_AVX2 STATIC EbPackUnPack_Intrinsic_AVX2.c EbPictureOperators_Intrinsic_AVX2.c EbTransforms_Intrinsic_AVX2.c + EbMeSatdCalculation_AVX2.asm + const-a.asm + x86inc.asm + x86util.asm ) diff --git a/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm b/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm new file mode 100644 index 000000000..4e795dc21 --- /dev/null +++ b/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm @@ -0,0 +1,10120 @@ +;***************************************************************************** +;* pixel.asm: x86 pixel metrics +;***************************************************************************** +;* Copyright (C) 2003-2013 x264 project +;* Copyright (C) 2013-2017 MulticoreWare, Inc +;* +;* Authors: Loren Merritt +;* Holger Lubitz +;* Laurent Aimar +;* Alex Izvorski +;* Fiona Glaser +;* Oskar Arvidsson +;* Min Chen +;* +;* This program is free software; you can redistribute it and/or modify +;* it under the terms of the GNU General Public License as published by +;* the Free Software Foundation; either version 2 of the License, or +;* (at your option) any later version. +;* +;* This program is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;* GNU General Public License for more details. +;* +;* You should have received a copy of the GNU General Public License +;* along with this program; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. +;* +;* This program is also available under a commercial proprietary license. +;* For more information, contact us at license @ x265.com. +;***************************************************************************** + +%include "x86inc.asm" +%include "x86util.asm" + +SECTION_RODATA 32 +hmul_8p: times 8 db 1 + times 4 db 1, -1 + times 8 db 1 + times 4 db 1, -1 +hmul_4p: times 4 db 1, 1, 1, 1, 1, -1, 1, -1 +mask_10: times 4 dw 0, -1 +mask_1100: times 2 dd 0, -1 +hmul_8w: times 4 dw 1 + times 2 dw 1, -1 + times 4 dw 1 + times 2 dw 1, -1 +psy_pp_shuff1: dq 0, 1, 8, 9, 4, 5, 12, 13 +psy_pp_shuff2: dq 2, 3, 10, 11, 6, 7, 14, 15 +psy_pp_shuff3: dq 0, 0, 8, 8, 1, 1, 9, 9 + +ALIGN 32 +transd_shuf1: SHUFFLE_MASK_W 0, 8, 2, 10, 4, 12, 6, 14 +transd_shuf2: SHUFFLE_MASK_W 1, 9, 3, 11, 5, 13, 7, 15 + +SECTION .text + +cextern pb_0 +cextern pb_1 +cextern pw_1 +cextern pw_8 +cextern pw_16 +cextern pw_32 +cextern pw_00ff +cextern pw_ppppmmmm +cextern pw_ppmmppmm +cextern pw_pmpmpmpm +cextern pw_pmmpzzzz +cextern pd_1 +cextern pd_2 +cextern hmul_16p +cextern pb_movemask +cextern pb_movemask_32 +cextern pw_pixel_max + + +;============================================================================= +; SATD +;============================================================================= + +%macro JDUP 2 +%if cpuflag(sse4) + ; just use shufps on anything post conroe + shufps %1, %2, 0 +%elif cpuflag(ssse3) && notcpuflag(atom) + ; join 2x 32 bit and duplicate them + ; emulating shufps is faster on conroe + punpcklqdq %1, %2 + movsldup %1, %1 +%else + ; doesn't need to dup. sse2 does things by zero extending to words and full h_2d + punpckldq %1, %2 +%endif +%endmacro + +%macro HSUMSUB 5 + pmaddubsw m%2, m%5 + pmaddubsw m%1, m%5 + pmaddubsw m%4, m%5 + pmaddubsw m%3, m%5 +%endmacro + +%macro DIFF_UNPACK_SSE2 5 + punpcklbw m%1, m%5 + punpcklbw m%2, m%5 + punpcklbw m%3, m%5 + punpcklbw m%4, m%5 + psubw m%1, m%2 + psubw m%3, m%4 +%endmacro + +%macro DIFF_SUMSUB_SSSE3 5 + HSUMSUB %1, %2, %3, %4, %5 + psubw m%1, m%2 + psubw m%3, m%4 +%endmacro + +%macro LOAD_DUP_2x4P 4 ; dst, tmp, 2* pointer + movd %1, %3 + movd %2, %4 + JDUP %1, %2 +%endmacro + +%macro LOAD_DUP_4x8P_CONROE 8 ; 4*dst, 4*pointer + movddup m%3, %6 + movddup m%4, %8 + movddup m%1, %5 + movddup m%2, %7 +%endmacro + +%macro LOAD_DUP_4x8P_PENRYN 8 + ; penryn and nehalem run punpcklqdq and movddup in different units + movh m%3, %6 + movh m%4, %8 + punpcklqdq m%3, m%3 + movddup m%1, %5 + punpcklqdq m%4, m%4 + movddup m%2, %7 +%endmacro + +%macro LOAD_SUMSUB_8x2P 9 + LOAD_DUP_4x8P %1, %2, %3, %4, %6, %7, %8, %9 + DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %5 +%endmacro + +%macro LOAD_SUMSUB_8x4P_SSSE3 7-11 r0, r2, 0, 0 +; 4x dest, 2x tmp, 1x mul, [2* ptr], [increment?] + LOAD_SUMSUB_8x2P %1, %2, %5, %6, %7, [%8], [%9], [%8+r1], [%9+r3] + LOAD_SUMSUB_8x2P %3, %4, %5, %6, %7, [%8+2*r1], [%9+2*r3], [%8+r4], [%9+r5] +%if %10 + lea %8, [%8+4*r1] + lea %9, [%9+4*r3] +%endif +%endmacro + +%macro LOAD_SUMSUB_16P_SSSE3 7 ; 2*dst, 2*tmp, mul, 2*ptr + movddup m%1, [%7] + movddup m%2, [%7+8] + mova m%4, [%6] + movddup m%3, m%4 + punpckhqdq m%4, m%4 + DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %5 +%endmacro + +%macro LOAD_SUMSUB_16P_SSE2 7 ; 2*dst, 2*tmp, mask, 2*ptr + movu m%4, [%7] + mova m%2, [%6] + DEINTB %1, %2, %3, %4, %5 + psubw m%1, m%3 + psubw m%2, m%4 + SUMSUB_BA w, %1, %2, %3 +%endmacro + +%macro LOAD_SUMSUB_16x4P 10-13 r0, r2, none +; 8x dest, 1x tmp, 1x mul, [2* ptr] [2nd tmp] + LOAD_SUMSUB_16P %1, %5, %2, %3, %10, %11, %12 + LOAD_SUMSUB_16P %2, %6, %3, %4, %10, %11+r1, %12+r3 + LOAD_SUMSUB_16P %3, %7, %4, %9, %10, %11+2*r1, %12+2*r3 + LOAD_SUMSUB_16P %4, %8, %13, %9, %10, %11+r4, %12+r5 +%endmacro + +%macro LOAD_SUMSUB_16x2P_AVX2 9 +; 2*dst, 2*tmp, mul, 4*ptr + vbroadcasti128 m%1, [%6] + vbroadcasti128 m%3, [%7] + vbroadcasti128 m%2, [%8] + vbroadcasti128 m%4, [%9] + DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %5 +%endmacro + +%macro LOAD_SUMSUB_16x4P_AVX2 7-11 r0, r2, 0, 0 +; 4x dest, 2x tmp, 1x mul, [2* ptr], [increment?] + LOAD_SUMSUB_16x2P_AVX2 %1, %2, %5, %6, %7, %8, %9, %8+r1, %9+r3 + LOAD_SUMSUB_16x2P_AVX2 %3, %4, %5, %6, %7, %8+2*r1, %9+2*r3, %8+r4, %9+r5 +%if %10 + lea %8, [%8+4*r1] + lea %9, [%9+4*r3] +%endif +%endmacro + +%macro LOAD_DUP_4x16P_AVX2 8 ; 4*dst, 4*pointer + mova xm%3, %6 + mova xm%4, %8 + mova xm%1, %5 + mova xm%2, %7 + vpermq m%3, m%3, q0011 + vpermq m%4, m%4, q0011 + vpermq m%1, m%1, q0011 + vpermq m%2, m%2, q0011 +%endmacro + +%macro LOAD_SUMSUB8_16x2P_AVX2 9 +; 2*dst, 2*tmp, mul, 4*ptr + LOAD_DUP_4x16P_AVX2 %1, %2, %3, %4, %6, %7, %8, %9 + DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %5 +%endmacro + +%macro LOAD_SUMSUB8_16x4P_AVX2 7-11 r0, r2, 0, 0 +; 4x dest, 2x tmp, 1x mul, [2* ptr], [increment?] + LOAD_SUMSUB8_16x2P_AVX2 %1, %2, %5, %6, %7, [%8], [%9], [%8+r1], [%9+r3] + LOAD_SUMSUB8_16x2P_AVX2 %3, %4, %5, %6, %7, [%8+2*r1], [%9+2*r3], [%8+r4], [%9+r5] +%if %10 + lea %8, [%8+4*r1] + lea %9, [%9+4*r3] +%endif +%endmacro + +; in: r4=3*stride1, r5=3*stride2 +; in: %2 = horizontal offset +; in: %3 = whether we need to increment pix1 and pix2 +; clobber: m3..m7 +; out: %1 = satd +%macro SATD_4x4_MMX 3 + %xdefine %%n nn%1 + %assign offset %2*SIZEOF_PIXEL + LOAD_DIFF m4, m3, none, [r0+ offset], [r2+ offset] + LOAD_DIFF m5, m3, none, [r0+ r1+offset], [r2+ r3+offset] + LOAD_DIFF m6, m3, none, [r0+2*r1+offset], [r2+2*r3+offset] + LOAD_DIFF m7, m3, none, [r0+ r4+offset], [r2+ r5+offset] +%if %3 + lea r0, [r0+4*r1] + lea r2, [r2+4*r3] +%endif + HADAMARD4_2D 4, 5, 6, 7, 3, %%n + paddw m4, m6 +;%if HIGH_BIT_DEPTH && (BIT_DEPTH == 12) +; pxor m5, m5 +; punpcklwd m6, m4, m5 +; punpckhwd m4, m5 +; paddd m4, m6 +;%endif + SWAP %%n, 4 +%endmacro + +; in: %1 = horizontal if 0, vertical if 1 +%macro SATD_8x4_SSE 8-9 +%if %1 + HADAMARD4_2D_SSE %2, %3, %4, %5, %6, amax +%else + HADAMARD4_V %2, %3, %4, %5, %6 + ; doing the abs first is a slight advantage + ABSW2 m%2, m%4, m%2, m%4, m%6, m%7 + ABSW2 m%3, m%5, m%3, m%5, m%6, m%7 + HADAMARD 1, max, %2, %4, %6, %7 +%endif +%ifnidn %9, swap + %if (BIT_DEPTH == 12) + pxor m%6, m%6 + punpcklwd m%7, m%2, m%6 + punpckhwd m%2, m%6 + paddd m%8, m%7 + paddd m%8, m%2 + %else + paddw m%8, m%2 + %endif +%else + SWAP %8, %2 + %if (BIT_DEPTH == 12) + pxor m%6, m%6 + punpcklwd m%7, m%8, m%6 + punpckhwd m%8, m%6 + paddd m%8, m%7 + %endif +%endif +%if %1 + %if (BIT_DEPTH == 12) + pxor m%6, m%6 + punpcklwd m%7, m%4, m%6 + punpckhwd m%4, m%6 + paddd m%8, m%7 + paddd m%8, m%4 + %else + paddw m%8, m%4 + %endif +%else + HADAMARD 1, max, %3, %5, %6, %7 + %if (BIT_DEPTH == 12) + pxor m%6, m%6 + punpcklwd m%7, m%3, m%6 + punpckhwd m%3, m%6 + paddd m%8, m%7 + paddd m%8, m%3 + %else + paddw m%8, m%3 + %endif +%endif +%endmacro + +%macro SATD_8x4_1_SSE 10 +%if %1 + HADAMARD4_2D_SSE %2, %3, %4, %5, %6, amax +%else + HADAMARD4_V %2, %3, %4, %5, %6 + ; doing the abs first is a slight advantage + ABSW2 m%2, m%4, m%2, m%4, m%6, m%7 + ABSW2 m%3, m%5, m%3, m%5, m%6, m%7 + HADAMARD 1, max, %2, %4, %6, %7 +%endif + + pxor m%10, m%10 + punpcklwd m%9, m%2, m%10 + paddd m%8, m%9 + punpckhwd m%9, m%2, m%10 + paddd m%8, m%9 + +%if %1 + pxor m%10, m%10 + punpcklwd m%9, m%4, m%10 + paddd m%8, m%9 + punpckhwd m%9, m%4, m%10 + paddd m%8, m%9 +%else + HADAMARD 1, max, %3, %5, %6, %7 + pxor m%10, m%10 + punpcklwd m%9, m%3, m%10 + paddd m%8, m%9 + punpckhwd m%9, m%3, m%10 + paddd m%8, m%9 +%endif +%endmacro + +%macro SATD_START_MMX 0 + FIX_STRIDES r1, r3 + lea r4, [3*r1] ; 3*stride1 + lea r5, [3*r3] ; 3*stride2 +%endmacro + +%macro SATD_END_MMX 0 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 + movd eax, m0 +%else ; !HIGH_BIT_DEPTH + pshufw m1, m0, q1032 + paddw m0, m1 + pshufw m1, m0, q2301 + paddw m0, m1 + movd eax, m0 + and eax, 0xffff +%endif ; HIGH_BIT_DEPTH + EMMS + RET +%endmacro + +; FIXME avoid the spilling of regs to hold 3*stride. +; for small blocks on x86_32, modify pixel pointer instead. + +;----------------------------------------------------------------------------- +; int pixel_satd_16x16( uint8_t *, intptr_t, uint8_t *, intptr_t ) +;----------------------------------------------------------------------------- +INIT_MMX mmx2 +cglobal pixel_satd_4x4, 4,6 + SATD_START_MMX + SATD_4x4_MMX m0, 0, 0 + SATD_END_MMX + +%macro SATD_START_SSE2 2-3 0 + FIX_STRIDES r1, r3 +%if HIGH_BIT_DEPTH && %3 + pxor %2, %2 +%elif cpuflag(ssse3) && notcpuflag(atom) +%if mmsize==32 + mova %2, [hmul_16p] +%else + mova %2, [hmul_8p] +%endif +%endif + lea r4, [3*r1] + lea r5, [3*r3] + pxor %1, %1 +%endmacro + +%macro SATD_END_SSE2 1-2 +%if HIGH_BIT_DEPTH + %if BIT_DEPTH == 12 + HADDD %1, xm0 + %else ; BIT_DEPTH == 12 + HADDUW %1, xm0 + %endif ; BIT_DEPTH == 12 + %if %0 == 2 + paddd %1, %2 + %endif +%else + HADDW %1, xm7 +%endif + movd eax, %1 + RET +%endmacro + +%macro SATD_ACCUM 3 +%if HIGH_BIT_DEPTH + HADDUW %1, %2 + paddd %3, %1 + pxor %1, %1 +%endif +%endmacro + +%macro BACKUP_POINTERS 0 +%if ARCH_X86_64 +%if WIN64 + PUSH r7 +%endif + mov r6, r0 + mov r7, r2 +%endif +%endmacro + +%macro RESTORE_AND_INC_POINTERS 0 +%if ARCH_X86_64 + lea r0, [r6+8*SIZEOF_PIXEL] + lea r2, [r7+8*SIZEOF_PIXEL] +%if WIN64 + POP r7 +%endif +%else + mov r0, r0mp + mov r2, r2mp + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL +%endif +%endmacro + +%macro SATD_4x8_SSE 3-4 +%if HIGH_BIT_DEPTH + movh m0, [r0+0*r1] + movh m4, [r2+0*r3] + movh m1, [r0+1*r1] + movh m5, [r2+1*r3] + movhps m0, [r0+4*r1] + movhps m4, [r2+4*r3] + movh m2, [r0+2*r1] + movh m6, [r2+2*r3] + psubw m0, m4 + movh m3, [r0+r4] + movh m4, [r2+r5] + lea r0, [r0+4*r1] + lea r2, [r2+4*r3] + movhps m1, [r0+1*r1] + movhps m5, [r2+1*r3] + movhps m2, [r0+2*r1] + movhps m6, [r2+2*r3] + psubw m1, m5 + movhps m3, [r0+r4] + movhps m4, [r2+r5] + psubw m2, m6 + psubw m3, m4 +%else ; !HIGH_BIT_DEPTH + movd m4, [r2] + movd m5, [r2+r3] + movd m6, [r2+2*r3] + add r2, r5 + movd m0, [r0] + movd m1, [r0+r1] + movd m2, [r0+2*r1] + add r0, r4 + movd m3, [r2+r3] + JDUP m4, m3 + movd m3, [r0+r1] + JDUP m0, m3 + movd m3, [r2+2*r3] + JDUP m5, m3 + movd m3, [r0+2*r1] + JDUP m1, m3 +%if %1==0 && %2==1 + mova m3, [hmul_4p] + DIFFOP 0, 4, 1, 5, 3 +%else + DIFFOP 0, 4, 1, 5, 7 +%endif + movd m5, [r2] + add r2, r5 + movd m3, [r0] + add r0, r4 + movd m4, [r2] + JDUP m6, m4 + movd m4, [r0] + JDUP m2, m4 + movd m4, [r2+r3] + JDUP m5, m4 + movd m4, [r0+r1] + JDUP m3, m4 +%if %1==0 && %2==1 + mova m4, [hmul_4p] + DIFFOP 2, 6, 3, 5, 4 +%else + DIFFOP 2, 6, 3, 5, 7 +%endif +%endif ; HIGH_BIT_DEPTH +%if %0 == 4 + SATD_8x4_1_SSE %1, 0, 1, 2, 3, 4, 5, 7, %3, %4 +%else + SATD_8x4_SSE %1, 0, 1, 2, 3, 4, 5, 7, %3 +%endif +%endmacro + +;----------------------------------------------------------------------------- +; int pixel_satd_8x4( uint8_t *, intptr_t, uint8_t *, intptr_t ) +;----------------------------------------------------------------------------- +%macro SATDS_SSE2 0 +%define vertical ((notcpuflag(ssse3) || cpuflag(atom)) || HIGH_BIT_DEPTH) + +%if cpuflag(ssse3) && (vertical==0 || HIGH_BIT_DEPTH) +cglobal pixel_satd_4x4, 4, 6, 6 + SATD_START_MMX + mova m4, [hmul_4p] + LOAD_DUP_2x4P m2, m5, [r2], [r2+r3] + LOAD_DUP_2x4P m3, m5, [r2+2*r3], [r2+r5] + LOAD_DUP_2x4P m0, m5, [r0], [r0+r1] + LOAD_DUP_2x4P m1, m5, [r0+2*r1], [r0+r4] + DIFF_SUMSUB_SSSE3 0, 2, 1, 3, 4 + HADAMARD 0, sumsub, 0, 1, 2, 3 + HADAMARD 4, sumsub, 0, 1, 2, 3 + HADAMARD 1, amax, 0, 1, 2, 3 + HADDW m0, m1 + movd eax, m0 + RET +%endif + +cglobal pixel_satd_4x8, 4, 6, 8 + SATD_START_MMX +%if vertical==0 + mova m7, [hmul_4p] +%endif + SATD_4x8_SSE vertical, 0, swap +%if BIT_DEPTH == 12 + HADDD m7, m1 +%else + HADDUW m7, m1 +%endif + movd eax, m7 + RET + +cglobal pixel_satd_4x16, 4, 6, 8 + SATD_START_MMX +%if vertical==0 + mova m7, [hmul_4p] +%endif + SATD_4x8_SSE vertical, 0, swap + lea r0, [r0+r1*2*SIZEOF_PIXEL] + lea r2, [r2+r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add +%if BIT_DEPTH == 12 + HADDD m7, m1 +%else + HADDUW m7, m1 +%endif + movd eax, m7 + RET + +cglobal pixel_satd_8x8_internal + LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 + SATD_8x4_SSE vertical, 0, 1, 2, 3, 4, 5, 6 +%%pixel_satd_8x4_internal: + LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 + SATD_8x4_SSE vertical, 0, 1, 2, 3, 4, 5, 6 + ret + +cglobal pixel_satd_8x8_internal2 +%if WIN64 + LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 + SATD_8x4_1_SSE vertical, 0, 1, 2, 3, 4, 5, 6, 12, 13 +%%pixel_satd_8x4_internal2: + LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 + SATD_8x4_1_SSE vertical, 0, 1, 2, 3, 4, 5, 6, 12, 13 +%else + LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 + SATD_8x4_1_SSE vertical, 0, 1, 2, 3, 4, 5, 6, 4, 5 +%%pixel_satd_8x4_internal2: + LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 + SATD_8x4_1_SSE vertical, 0, 1, 2, 3, 4, 5, 6, 4, 5 +%endif + ret + +; 16x8 regresses on phenom win64, 16x16 is almost the same (too many spilled registers) +; These aren't any faster on AVX systems with fast movddup (Bulldozer, Sandy Bridge) +%if HIGH_BIT_DEPTH == 0 && (WIN64 || UNIX64) && notcpuflag(avx) + +cglobal pixel_satd_16x4_internal2 + LOAD_SUMSUB_16x4P 0, 1, 2, 3, 4, 8, 5, 9, 6, 7, r0, r2, 11 + lea r2, [r2+4*r3] + lea r0, [r0+4*r1] + SATD_8x4_1_SSE 0, 0, 1, 2, 3, 6, 11, 10, 12, 13 + SATD_8x4_1_SSE 0, 4, 8, 5, 9, 6, 3, 10, 12, 13 + ret + +cglobal pixel_satd_16x4, 4,6,14 + SATD_START_SSE2 m10, m7 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_16x8, 4,6,14 + SATD_START_SSE2 m10, m7 +%if vertical + mova m7, [pw_00ff] +%endif + jmp %%pixel_satd_16x8_internal + +cglobal pixel_satd_16x12, 4,6,14 + SATD_START_SSE2 m10, m7 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + jmp %%pixel_satd_16x8_internal + +cglobal pixel_satd_16x32, 4,6,14 + SATD_START_SSE2 m10, m7 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + jmp %%pixel_satd_16x8_internal + +cglobal pixel_satd_16x64, 4,6,14 + SATD_START_SSE2 m10, m7 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + jmp %%pixel_satd_16x8_internal + +cglobal pixel_satd_16x16, 4,6,14 + SATD_START_SSE2 m10, m7 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 +%%pixel_satd_16x8_internal: + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_32x8, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_32x16, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_32x24, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_32x32, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_32x64, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_48x64, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_64x16, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_64x32, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_64x48, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + + HADDD m10, m0 + movd eax, m10 + RET + +cglobal pixel_satd_64x64, 4,8,14 ;if WIN64 && notcpuflag(avx) + SATD_START_SSE2 m10, m7 + mov r6, r0 + mov r7, r2 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + call pixel_satd_16x4_internal2 + + HADDD m10, m0 + movd eax, m10 + RET + +%else +%if WIN64 +cglobal pixel_satd_16x24, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_16x24, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif +%if WIN64 +cglobal pixel_satd_32x48, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_32x48, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_24x64, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_24x64, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_8x64, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_8x64, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_8x12, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call %%pixel_satd_8x4_internal2 + pxor m7, m7 + movhlps m7, m6 + paddd m6, m7 + pshufd m7, m6, 1 + paddd m6, m7 + movd eax, m6 + RET +%else +cglobal pixel_satd_8x12, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call %%pixel_satd_8x4_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if HIGH_BIT_DEPTH +%if WIN64 +cglobal pixel_satd_12x32, 4,8,8 ;if WIN64 && cpuflag(avx) + SATD_START_MMX + mov r6, r0 + mov r7, r2 + pxor m7, m7 + SATD_4x8_SSE vertical, 0, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r6 + 4*SIZEOF_PIXEL] + lea r2, [r7 + 4*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + HADDD m7, m0 + movd eax, m7 + RET +%else +cglobal pixel_satd_12x32, 4,7,8,0-gprsize + SATD_START_MMX + mov r6, r0 + mov [rsp], r2 + pxor m7, m7 + SATD_4x8_SSE vertical, 0, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r6 + 4*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 4*SIZEOF_PIXEL + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + HADDD m7, m0 + movd eax, m7 + RET +%endif +%else ;HIGH_BIT_DEPTH +%if WIN64 +cglobal pixel_satd_12x32, 4,8,8 ;if WIN64 && cpuflag(avx) + SATD_START_MMX + mov r6, r0 + mov r7, r2 +%if vertical==0 + mova m7, [hmul_4p] +%endif + SATD_4x8_SSE vertical, 0, swap + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r6 + 4*SIZEOF_PIXEL] + lea r2, [r7 + 4*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + HADDW m7, m1 + movd eax, m7 + RET +%else +cglobal pixel_satd_12x32, 4,7,8,0-gprsize + SATD_START_MMX + mov r6, r0 + mov [rsp], r2 +%if vertical==0 + mova m7, [hmul_4p] +%endif + SATD_4x8_SSE vertical, 0, swap + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r6 + 4*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 4*SIZEOF_PIXEL + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + HADDW m7, m1 + movd eax, m7 + RET +%endif +%endif + +%if HIGH_BIT_DEPTH +%if WIN64 +cglobal pixel_satd_4x32, 4,8,8 ;if WIN64 && cpuflag(avx) + SATD_START_MMX + mov r6, r0 + mov r7, r2 + pxor m7, m7 + SATD_4x8_SSE vertical, 0, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + HADDD m7, m0 + movd eax, m7 + RET +%else +cglobal pixel_satd_4x32, 4,7,8,0-gprsize + SATD_START_MMX + mov r6, r0 + mov [rsp], r2 + pxor m7, m7 + SATD_4x8_SSE vertical, 0, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + pxor m1, m1 + movhlps m1, m7 + paddd m7, m1 + pshufd m1, m7, 1 + paddd m7, m1 + movd eax, m7 + RET +%endif +%else +%if WIN64 +cglobal pixel_satd_4x32, 4,8,8 ;if WIN64 && cpuflag(avx) + SATD_START_MMX + mov r6, r0 + mov r7, r2 +%if vertical==0 + mova m7, [hmul_4p] +%endif + SATD_4x8_SSE vertical, 0, swap + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + HADDW m7, m1 + movd eax, m7 + RET +%else +cglobal pixel_satd_4x32, 4,7,8,0-gprsize + SATD_START_MMX + mov r6, r0 + mov [rsp], r2 +%if vertical==0 + mova m7, [hmul_4p] +%endif + SATD_4x8_SSE vertical, 0, swap + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + HADDW m7, m1 + movd eax, m7 + RET +%endif +%endif + +%if WIN64 +cglobal pixel_satd_32x8, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_32x8, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_32x16, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_32x16, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_32x24, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_32x24, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_32x32, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_32x32, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_32x64, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_32x64, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_48x64, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + lea r2, [r7 + 32*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + lea r2, [r7 + 40*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_48x64, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,32*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,40*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + + +%if WIN64 +cglobal pixel_satd_64x16, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + lea r2, [r7 + 32*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + lea r2, [r7 + 40*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 48*SIZEOF_PIXEL] + lea r2, [r7 + 48*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 56*SIZEOF_PIXEL] + lea r2, [r7 + 56*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_64x16, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,32*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,40*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 48*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,48*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 56*SIZEOF_PIXEL] + mov r2, [rsp] + add r2,56*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_64x32, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + lea r2, [r7 + 32*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + lea r2, [r7 + 40*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 48*SIZEOF_PIXEL] + lea r2, [r7 + 48*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 56*SIZEOF_PIXEL] + lea r2, [r7 + 56*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_64x32, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 32*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 40*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 48*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 48*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 56*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 56*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_64x48, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + lea r2, [r7 + 32*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + lea r2, [r7 + 40*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 48*SIZEOF_PIXEL] + lea r2, [r7 + 48*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 56*SIZEOF_PIXEL] + lea r2, [r7 + 56*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_64x48, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 32*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 40*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 48*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 48*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 56*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 56*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_64x64, 4,8,14 ;if WIN64 && cpuflag(avx) + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + lea r2, [r7 + 24*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + lea r2, [r7 + 32*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + lea r2, [r7 + 40*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 48*SIZEOF_PIXEL] + lea r2, [r7 + 48*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 56*SIZEOF_PIXEL] + lea r2, [r7 + 56*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_64x64, 4,7,8,0-gprsize ;if !WIN64 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 24*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 24*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 32*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 32*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 40*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 40*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 48*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 48*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 56*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 56*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if WIN64 +cglobal pixel_satd_16x4, 4,6,14 +%else +cglobal pixel_satd_16x4, 4,6,8 +%endif + SATD_START_SSE2 m6, m7 + BACKUP_POINTERS + call %%pixel_satd_8x4_internal2 + RESTORE_AND_INC_POINTERS + call %%pixel_satd_8x4_internal2 + HADDD m6, m0 + movd eax, m6 + RET + +%if WIN64 +cglobal pixel_satd_16x8, 4,6,14 +%else +cglobal pixel_satd_16x8, 4,6,8 +%endif + SATD_START_SSE2 m6, m7 + BACKUP_POINTERS + call pixel_satd_8x8_internal2 + RESTORE_AND_INC_POINTERS + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET + +%if WIN64 +cglobal pixel_satd_16x12, 4,6,14 +%else +cglobal pixel_satd_16x12, 4,6,8 +%endif + SATD_START_SSE2 m6, m7, 1 + BACKUP_POINTERS + call pixel_satd_8x8_internal2 + call %%pixel_satd_8x4_internal2 + RESTORE_AND_INC_POINTERS + call pixel_satd_8x8_internal2 + call %%pixel_satd_8x4_internal2 + HADDD m6, m0 + movd eax, m6 + RET + +%if WIN64 +cglobal pixel_satd_16x16, 4,6,14 +%else +cglobal pixel_satd_16x16, 4,6,8 +%endif + SATD_START_SSE2 m6, m7, 1 + BACKUP_POINTERS + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + RESTORE_AND_INC_POINTERS + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET + +%if WIN64 +cglobal pixel_satd_16x32, 4,6,14 +%else +cglobal pixel_satd_16x32, 4,6,8 +%endif + SATD_START_SSE2 m6, m7, 1 + BACKUP_POINTERS + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + RESTORE_AND_INC_POINTERS + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET + +%if WIN64 +cglobal pixel_satd_16x64, 4,6,14 +%else +cglobal pixel_satd_16x64, 4,6,8 +%endif + SATD_START_SSE2 m6, m7, 1 + BACKUP_POINTERS + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + RESTORE_AND_INC_POINTERS + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif + +%if HIGH_BIT_DEPTH +%if WIN64 +cglobal pixel_satd_12x16, 4,8,8 + SATD_START_MMX + mov r6, r0 + mov r7, r2 + pxor m7, m7 + SATD_4x8_SSE vertical, 0, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r6 + 4*SIZEOF_PIXEL] + lea r2, [r7 + 4*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + HADDD m7, m0 + movd eax, m7 + RET +%else +cglobal pixel_satd_12x16, 4,7,8,0-gprsize + SATD_START_MMX + mov r6, r0 + mov [rsp], r2 + pxor m7, m7 + SATD_4x8_SSE vertical, 0, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r6 + 4*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 4*SIZEOF_PIXEL + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + SATD_4x8_SSE vertical, 1, 4, 5 + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, 4, 5 + HADDD m7, m0 + movd eax, m7 + RET +%endif +%else ;HIGH_BIT_DEPTH +%if WIN64 +cglobal pixel_satd_12x16, 4,8,8 + SATD_START_MMX + mov r6, r0 + mov r7, r2 +%if vertical==0 + mova m7, [hmul_4p] +%endif + SATD_4x8_SSE vertical, 0, swap + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r6 + 4*SIZEOF_PIXEL] + lea r2, [r7 + 4*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + HADDW m7, m1 + movd eax, m7 + RET +%else +cglobal pixel_satd_12x16, 4,7,8,0-gprsize + SATD_START_MMX + mov r6, r0 + mov [rsp], r2 +%if vertical==0 + mova m7, [hmul_4p] +%endif + SATD_4x8_SSE vertical, 0, swap + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r6 + 4*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 4*SIZEOF_PIXEL + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + SATD_4x8_SSE vertical, 1, add + lea r0, [r0 + r1*2*SIZEOF_PIXEL] + lea r2, [r2 + r3*2*SIZEOF_PIXEL] + SATD_4x8_SSE vertical, 1, add + HADDW m7, m1 + movd eax, m7 + RET +%endif +%endif + +%if WIN64 +cglobal pixel_satd_24x32, 4,8,14 + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov r7, r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + lea r2, [r7 + 8*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + lea r2, [r7 + 16*SIZEOF_PIXEL] + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%else +cglobal pixel_satd_24x32, 4,7,8,0-gprsize + SATD_START_SSE2 m6, m7 + mov r6, r0 + mov [rsp], r2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 8*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 8*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + lea r0, [r6 + 16*SIZEOF_PIXEL] + mov r2, [rsp] + add r2, 16*SIZEOF_PIXEL + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET +%endif ;WIN64 + +%if WIN64 +cglobal pixel_satd_8x32, 4,6,14 +%else +cglobal pixel_satd_8x32, 4,6,8 +%endif + SATD_START_SSE2 m6, m7 +%if vertical + mova m7, [pw_00ff] +%endif + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET + +%if WIN64 +cglobal pixel_satd_8x16, 4,6,14 +%else +cglobal pixel_satd_8x16, 4,6,8 +%endif + SATD_START_SSE2 m6, m7 + call pixel_satd_8x8_internal2 + call pixel_satd_8x8_internal2 + HADDD m6, m0 + movd eax, m6 + RET + +cglobal pixel_satd_8x8, 4,6,8 + SATD_START_SSE2 m6, m7 + call pixel_satd_8x8_internal + SATD_END_SSE2 m6 + +%if WIN64 +cglobal pixel_satd_8x4, 4,6,14 +%else +cglobal pixel_satd_8x4, 4,6,8 +%endif + SATD_START_SSE2 m6, m7 + call %%pixel_satd_8x4_internal2 + SATD_END_SSE2 m6 +%endmacro ; SATDS_SSE2 + + +;============================================================================= +; SA8D +;============================================================================= + +%macro SA8D_INTER 0 +%if ARCH_X86_64 + %define lh m10 + %define rh m0 +%else + %define lh m0 + %define rh [esp+48] +%endif +%if HIGH_BIT_DEPTH + HADDUW m0, m1 + paddd lh, rh +%else + paddusw lh, rh +%endif ; HIGH_BIT_DEPTH +%endmacro + +%macro SA8D_8x8 0 + call pixel_sa8d_8x8_internal +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%else + HADDW m0, m1 +%endif ; HIGH_BIT_DEPTH + paddd m0, [pd_1] + psrld m0, 1 + paddd m12, m0 +%endmacro + +%macro SA8D_16x16 0 + call pixel_sa8d_8x8_internal ; pix[0] + add r2, 8*SIZEOF_PIXEL + add r0, 8*SIZEOF_PIXEL +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova m10, m0 + call pixel_sa8d_8x8_internal ; pix[8] + lea r2, [r2+8*r3] + lea r0, [r0+8*r1] + SA8D_INTER + call pixel_sa8d_8x8_internal ; pix[8*stride+8] + sub r2, 8*SIZEOF_PIXEL + sub r0, 8*SIZEOF_PIXEL + SA8D_INTER + call pixel_sa8d_8x8_internal ; pix[8*stride] + SA8D_INTER + SWAP 0, 10 +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + paddd m0, [pd_1] + psrld m0, 1 + paddd m12, m0 +%endmacro + +%macro AVG_16x16 0 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d +%endmacro + +%macro SA8D 0 +; sse2 doesn't seem to like the horizontal way of doing things +%define vertical ((notcpuflag(ssse3) || cpuflag(atom)) || HIGH_BIT_DEPTH) + +%if ARCH_X86_64 +;----------------------------------------------------------------------------- +; int pixel_sa8d_8x8( uint8_t *, intptr_t, uint8_t *, intptr_t ) +;----------------------------------------------------------------------------- +cglobal pixel_sa8d_8x8_internal + lea r6, [r0+4*r1] + lea r7, [r2+4*r3] + LOAD_SUMSUB_8x4P 0, 1, 2, 8, 5, 6, 7, r0, r2 + LOAD_SUMSUB_8x4P 4, 5, 3, 9, 11, 6, 7, r6, r7 +%if vertical + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax +%else ; non-sse2 + HADAMARD8_2D_HMUL 0, 1, 2, 8, 4, 5, 3, 9, 6, 11 +%endif + paddw m0, m1 + paddw m0, m2 + paddw m0, m8 + SAVE_MM_PERMUTATION + ret + +cglobal pixel_sa8d_8x8, 4,8,12 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] +%if vertical == 0 + mova m7, [hmul_8p] +%endif + call pixel_sa8d_8x8_internal +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%else + HADDW m0, m1 +%endif ; HIGH_BIT_DEPTH + movd eax, m0 + add eax, 1 + shr eax, 1 + RET + +cglobal pixel_sa8d_16x16, 4,8,12 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] +%if vertical == 0 + mova m7, [hmul_8p] +%endif + call pixel_sa8d_8x8_internal ; pix[0] + add r2, 8*SIZEOF_PIXEL + add r0, 8*SIZEOF_PIXEL +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova m10, m0 + call pixel_sa8d_8x8_internal ; pix[8] + lea r2, [r2+8*r3] + lea r0, [r0+8*r1] + SA8D_INTER + call pixel_sa8d_8x8_internal ; pix[8*stride+8] + sub r2, 8*SIZEOF_PIXEL + sub r0, 8*SIZEOF_PIXEL + SA8D_INTER + call pixel_sa8d_8x8_internal ; pix[8*stride] + SA8D_INTER + SWAP 0, 10 +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd eax, m0 + add eax, 1 + shr eax, 1 + RET + +cglobal pixel_sa8d_8x16, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_8x8 + lea r0, [r0 + 8*r1] + lea r2, [r2 + 8*r3] + SA8D_8x8 + movd eax, m12 + RET + +cglobal pixel_sa8d_8x32, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_8x8 + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + SA8D_8x8 + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + SA8D_8x8 + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + SA8D_8x8 + movd eax, m12 + RET + +cglobal pixel_sa8d_16x8, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + movd eax, m12 + RET + +cglobal pixel_sa8d_16x32, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + movd eax, m12 + RET + +cglobal pixel_sa8d_16x64, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + movd eax, m12 + RET + +cglobal pixel_sa8d_24x32, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + SA8D_8x8 + sub r0, 8*SIZEOF_PIXEL + sub r2, 8*SIZEOF_PIXEL + SA8D_8x8 + sub r0, 8*SIZEOF_PIXEL + sub r2, 8*SIZEOF_PIXEL + SA8D_8x8 + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + SA8D_8x8 + sub r0, 8*SIZEOF_PIXEL + sub r2, 8*SIZEOF_PIXEL + SA8D_8x8 + sub r0, 8*SIZEOF_PIXEL + sub r2, 8*SIZEOF_PIXEL + SA8D_8x8 + movd eax, m12 + RET + +cglobal pixel_sa8d_32x8, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + movd eax, m12 + RET + +cglobal pixel_sa8d_32x16, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + movd eax, m12 + RET + +cglobal pixel_sa8d_32x24, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + SA8D_8x8 + sub r0, 8*SIZEOF_PIXEL + sub r2, 8*SIZEOF_PIXEL + SA8D_8x8 + sub r0, 8*SIZEOF_PIXEL + sub r2, 8*SIZEOF_PIXEL + SA8D_8x8 + sub r0, 8*SIZEOF_PIXEL + sub r2, 8*SIZEOF_PIXEL + SA8D_8x8 + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_8x8 + movd eax, m12 + RET + +cglobal pixel_sa8d_32x32, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + movd eax, m12 + RET + +cglobal pixel_sa8d_32x64, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + movd eax, m12 + RET + +cglobal pixel_sa8d_48x64, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + movd eax, m12 + RET + +cglobal pixel_sa8d_64x16, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + movd eax, m12 + RET + +cglobal pixel_sa8d_64x32, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + movd eax, m12 + RET + +cglobal pixel_sa8d_64x48, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + movd eax, m12 + RET + +cglobal pixel_sa8d_64x64, 4,8,13 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + pxor m12, m12 +%if vertical == 0 + mova m7, [hmul_8p] +%endif + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + add r2, 16*SIZEOF_PIXEL + add r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + lea r4, [8*r1] + lea r5, [8*r3] + sub r0, r4 + sub r2, r5 + sub r2, 16*SIZEOF_PIXEL + sub r0, 16*SIZEOF_PIXEL + lea r4, [3*r1] + lea r5, [3*r3] + SA8D_16x16 + movd eax, m12 + RET + +%else ; ARCH_X86_32 +%if mmsize == 16 +cglobal pixel_sa8d_8x8_internal + %define spill0 [esp+4] + %define spill1 [esp+20] + %define spill2 [esp+36] +%if vertical + LOAD_DIFF_8x4P 0, 1, 2, 3, 4, 5, 6, r0, r2, 1 + HADAMARD4_2D 0, 1, 2, 3, 4 + movdqa spill0, m3 + LOAD_DIFF_8x4P 4, 5, 6, 7, 3, 3, 2, r0, r2, 1 + HADAMARD4_2D 4, 5, 6, 7, 3 + HADAMARD2_2D 0, 4, 1, 5, 3, qdq, amax + movdqa m3, spill0 + paddw m0, m1 + HADAMARD2_2D 2, 6, 3, 7, 5, qdq, amax +%else ; mmsize == 8 + mova m7, [hmul_8p] + LOAD_SUMSUB_8x4P 0, 1, 2, 3, 5, 6, 7, r0, r2, 1 + ; could do first HADAMARD4_V here to save spilling later + ; surprisingly, not a win on conroe or even p4 + mova spill0, m2 + mova spill1, m3 + mova spill2, m1 + SWAP 1, 7 + LOAD_SUMSUB_8x4P 4, 5, 6, 7, 2, 3, 1, r0, r2, 1 + HADAMARD4_V 4, 5, 6, 7, 3 + mova m1, spill2 + mova m2, spill0 + mova m3, spill1 + mova spill0, m6 + mova spill1, m7 + HADAMARD4_V 0, 1, 2, 3, 7 + SUMSUB_BADC w, 0, 4, 1, 5, 7 + HADAMARD 2, sumsub, 0, 4, 7, 6 + HADAMARD 2, sumsub, 1, 5, 7, 6 + HADAMARD 1, amax, 0, 4, 7, 6 + HADAMARD 1, amax, 1, 5, 7, 6 + mova m6, spill0 + mova m7, spill1 + paddw m0, m1 + SUMSUB_BADC w, 2, 6, 3, 7, 4 + HADAMARD 2, sumsub, 2, 6, 4, 5 + HADAMARD 2, sumsub, 3, 7, 4, 5 + HADAMARD 1, amax, 2, 6, 4, 5 + HADAMARD 1, amax, 3, 7, 4, 5 +%endif ; sse2/non-sse2 + paddw m0, m2 + paddw m0, m3 + SAVE_MM_PERMUTATION + ret +%endif ; ifndef mmx2 + +cglobal pixel_sa8d_8x8_internal2 + %define spill0 [esp+4] + LOAD_DIFF_8x4P 0, 1, 2, 3, 4, 5, 6, r0, r2, 1 + HADAMARD4_2D 0, 1, 2, 3, 4 + movdqa spill0, m3 + LOAD_DIFF_8x4P 4, 5, 6, 7, 3, 3, 2, r0, r2, 1 + HADAMARD4_2D 4, 5, 6, 7, 3 + HADAMARD2_2D 0, 4, 1, 5, 3, qdq, amax + movdqa m3, spill0 + paddw m0, m1 + HADAMARD2_2D 2, 6, 3, 7, 5, qdq, amax + paddw m0, m2 + paddw m0, m3 + SAVE_MM_PERMUTATION + ret + +cglobal pixel_sa8d_8x8, 4,7 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 48 + lea r4, [3*r1] + lea r5, [3*r3] + call pixel_sa8d_8x8_internal +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%else + HADDW m0, m1 +%endif ; HIGH_BIT_DEPTH + movd eax, m0 + add eax, 1 + shr eax, 1 + mov esp, r6 + RET + +cglobal pixel_sa8d_16x16, 4,7 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + lea r4, [3*r1] + lea r5, [3*r3] + call pixel_sa8d_8x8_internal +%if mmsize == 8 + lea r0, [r0+4*r1] + lea r2, [r2+4*r3] +%endif +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal +%if mmsize == 8 + lea r0, [r0+4*r1] + lea r2, [r2+4*r3] +%else + SA8D_INTER +%endif + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal +%if HIGH_BIT_DEPTH + SA8D_INTER +%else ; !HIGH_BIT_DEPTH + paddusw m0, [esp+64-mmsize] +%if mmsize == 16 + HADDUW m0, m1 +%else + mova m2, [esp+48] + pxor m7, m7 + mova m1, m0 + mova m3, m2 + punpcklwd m0, m7 + punpckhwd m1, m7 + punpcklwd m2, m7 + punpckhwd m3, m7 + paddd m0, m1 + paddd m2, m3 + paddd m0, m2 + HADDD m0, m1 +%endif +%endif ; HIGH_BIT_DEPTH + movd eax, m0 + add eax, 1 + shr eax, 1 + mov esp, r6 + RET + +cglobal pixel_sa8d_8x16, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_8x32, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_16x8, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_16x32, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_16x64, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_24x32, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_32x8, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_32x16, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_32x24, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 + HADDUW m0, m1 + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_32x32, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_32x64, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_48x64, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_64x16, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_64x32, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_64x48, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET + +cglobal pixel_sa8d_64x64, 4,7,8 + FIX_STRIDES r1, r3 + mov r6, esp + and esp, ~15 + sub esp, 64 + + lea r4, [r1 + 2*r1] + lea r5, [r3 + 2*r3] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [rsp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + mov dword [esp+36], r4d + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + lea r0, [r0 + r1*8] + lea r2, [r2 + r3*8] + mov [r6+20], r0 + mov [r6+28], r2 + + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 8*SIZEOF_PIXEL + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 16*SIZEOF_PIXEL + add r2, 16*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 24*SIZEOF_PIXEL + add r2, 24*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 32*SIZEOF_PIXEL + add r2, 32*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 40*SIZEOF_PIXEL + add r2, 40*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + AVG_16x16 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 48*SIZEOF_PIXEL + add r2, 48*SIZEOF_PIXEL + lea r4, [r1 + 2*r1] + call pixel_sa8d_8x8_internal2 +%if HIGH_BIT_DEPTH + HADDUW m0, m1 +%endif + mova [esp+48], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+48], m0 + + mov r0, [r6+20] + mov r2, [r6+28] + add r0, 56*SIZEOF_PIXEL + add r2, 56*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal2 + SA8D_INTER + mova [esp+64-mmsize], m0 + call pixel_sa8d_8x8_internal2 + SA8D_INTER +%if HIGH_BIT_DEPTH == 0 + HADDUW m0, m1 +%endif + movd r4d, m0 + add r4d, 1 + shr r4d, 1 + add r4d, dword [esp+36] + mov eax, r4d + mov esp, r6 + RET +%endif ; !ARCH_X86_64 +%endmacro ; SA8D + + +%if ARCH_X86_64 == 1 && BIT_DEPTH == 12 +INIT_YMM avx2 +cglobal sa8d_8x8_12bit + pmovzxwd m0, [r0] + pmovzxwd m9, [r2] + psubd m0, m9 + + pmovzxwd m1, [r0 + r1] + pmovzxwd m9, [r2 + r3] + psubd m1, m9 + + pmovzxwd m2, [r0 + r1 * 2] + pmovzxwd m9, [r2 + r3 * 2] + psubd m2, m9 + + pmovzxwd m8, [r0 + r4] + pmovzxwd m9, [r2 + r5] + psubd m8, m9 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + + pmovzxwd m4, [r0] + pmovzxwd m9, [r2] + psubd m4, m9 + + pmovzxwd m5, [r0 + r1] + pmovzxwd m9, [r2 + r3] + psubd m5, m9 + + pmovzxwd m3, [r0 + r1 * 2] + pmovzxwd m9, [r2 + r3 * 2] + psubd m3, m9 + + pmovzxwd m7, [r0 + r4] + pmovzxwd m9, [r2 + r5] + psubd m7, m9 + + mova m6, m0 + paddd m0, m1 + psubd m1, m6 + mova m6, m2 + paddd m2, m8 + psubd m8, m6 + mova m6, m0 + + punpckldq m0, m1 + punpckhdq m6, m1 + + mova m1, m0 + paddd m0, m6 + psubd m6, m1 + mova m1, m2 + + punpckldq m2, m8 + punpckhdq m1, m8 + + mova m8, m2 + paddd m2, m1 + psubd m1, m8 + mova m8, m4 + paddd m4, m5 + psubd m5, m8 + mova m8, m3 + paddd m3, m7 + psubd m7, m8 + mova m8, m4 + + punpckldq m4, m5 + punpckhdq m8, m5 + + mova m5, m4 + paddd m4, m8 + psubd m8, m5 + mova m5, m3 + punpckldq m3, m7 + punpckhdq m5, m7 + + mova m7, m3 + paddd m3, m5 + psubd m5, m7 + mova m7, m0 + paddd m0, m2 + psubd m2, m7 + mova m7, m6 + paddd m6, m1 + psubd m1, m7 + mova m7, m0 + + punpcklqdq m0, m2 + punpckhqdq m7, m2 + + mova m2, m0 + paddd m0, m7 + psubd m7, m2 + mova m2, m6 + + punpcklqdq m6, m1 + punpckhqdq m2, m1 + + mova m1, m6 + paddd m6, m2 + psubd m2, m1 + mova m1, m4 + paddd m4, m3 + psubd m3, m1 + mova m1, m8 + paddd m8, m5 + psubd m5, m1 + mova m1, m4 + + punpcklqdq m4, m3 + punpckhqdq m1, m3 + + mova m3, m4 + paddd m4, m1 + psubd m1, m3 + mova m3, m8 + + punpcklqdq m8, m5 + punpckhqdq m3, m5 + + mova m5, m8 + paddd m8, m3 + psubd m3, m5 + mova m5, m0 + paddd m0, m4 + psubd m4, m5 + mova m5, m7 + paddd m7, m1 + psubd m1, m5 + mova m5, m0 + + vinserti128 m0, m0, xm4, 1 + vperm2i128 m5, m5, m4, 00110001b + + pxor m4, m4 + psubd m4, m0 + pmaxsd m0, m4 + pxor m4, m4 + psubd m4, m5 + pmaxsd m5, m4 + pmaxsd m0, m5 + mova m4, m7 + + vinserti128 m7, m7, xm1, 1 + vperm2i128 m4, m4, m1, 00110001b + + pxor m1, m1 + psubd m1, m7 + pmaxsd m7, m1 + pxor m1, m1 + psubd m1, m4 + pmaxsd m4, m1 + pmaxsd m7, m4 + mova m1, m6 + paddd m6, m8 + psubd m8, m1 + mova m1, m2 + paddd m2, m3 + psubd m3, m1 + mova m1, m6 + + vinserti128 m6, m6, xm8, 1 + vperm2i128 m1, m1, m8, 00110001b + + pxor m8, m8 + psubd m8, m6 + pmaxsd m6, m8 + pxor m8, m8 + psubd m8, m1 + pmaxsd m1, m8 + pmaxsd m6, m1 + mova m8, m2 + + vinserti128 m2, m2, xm3, 1 + vperm2i128 m8, m8, m3, 00110001b + + pxor m3, m3 + psubd m3, m2 + pmaxsd m2, m3 + pxor m3, m3 + psubd m3, m8 + pmaxsd m8, m3 + pmaxsd m2, m8 + paddd m0, m6 + paddd m0, m7 + paddd m0, m2 + ret + +cglobal pixel_sa8d_8x8, 4,6,10 + add r1d, r1d + add r3d, r3d + lea r4, [r1 + r1 * 2] + lea r5, [r3 + r3 * 2] + + call sa8d_8x8_12bit + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + movd eax, xm0 + add eax, 1 + shr eax, 1 + RET + +cglobal pixel_sa8d_8x16, 4,7,11 + add r1d, r1d + add r3d, r3d + lea r4, [r1 + r1 * 2] + lea r5, [r3 + r3 * 2] + pxor m10, m10 + + call sa8d_8x8_12bit + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm10, xm0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm0, xm10 + movd eax, xm0 + RET + +cglobal pixel_sa8d_16x16, 4,8,11 + add r1d, r1d + add r3d, r3d + lea r4, [r1 + r1 * 2] + lea r5, [r3 + r3 * 2] + mov r6, r0 + mov r7, r2 + pxor m10, m10 + + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + movd eax, xm0 + add eax, 1 + shr eax, 1 + RET + +cglobal pixel_sa8d_16x32, 4,8,12 + add r1d, r1d + add r3d, r3d + lea r4, [r1 + r1 * 2] + lea r5, [r3 + r3 * 2] + mov r6, r0 + mov r7, r2 + pxor m10, m10 + pxor m11, m11 + + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + lea r6, [r6 + r1 * 8] + lea r6, [r6 + r1 * 8] + lea r7, [r7 + r3 * 8] + lea r7, [r7 + r3 * 8] + pxor m10, m10 + mov r0, r6 + mov r2, r7 + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + movd eax, xm11 + RET + +cglobal pixel_sa8d_32x32, 4,8,12 + add r1d, r1d + add r3d, r3d + lea r4, [r1 + r1 * 2] + lea r5, [r3 + r3 * 2] + mov r6, r0 + mov r7, r2 + pxor m10, m10 + pxor m11, m11 + + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + lea r6, [r6 + r1 * 8] + lea r6, [r6 + r1 * 8] + lea r7, [r7 + r3 * 8] + lea r7, [r7 + r3 * 8] + pxor m10, m10 + mov r0, r6 + mov r2, r7 + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + movd eax, xm11 + RET + +cglobal pixel_sa8d_32x64, 4,8,12 + add r1d, r1d + add r3d, r3d + lea r4, [r1 + r1 * 2] + lea r5, [r3 + r3 * 2] + mov r6, r0 + mov r7, r2 + pxor m10, m10 + pxor m11, m11 + + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + lea r6, [r6 + r1 * 8] + lea r6, [r6 + r1 * 8] + lea r7, [r7 + r3 * 8] + lea r7, [r7 + r3 * 8] + pxor m10, m10 + mov r0, r6 + mov r2, r7 + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + lea r6, [r6 + r1 * 8] + lea r6, [r6 + r1 * 8] + lea r7, [r7 + r3 * 8] + lea r7, [r7 + r3 * 8] + pxor m10, m10 + mov r0, r6 + mov r2, r7 + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + lea r6, [r6 + r1 * 8] + lea r6, [r6 + r1 * 8] + lea r7, [r7 + r3 * 8] + lea r7, [r7 + r3 * 8] + pxor m10, m10 + mov r0, r6 + mov r2, r7 + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + movd eax, xm11 + RET + +cglobal pixel_sa8d_64x64, 4,8,12 + add r1d, r1d + add r3d, r3d + lea r4, [r1 + r1 * 2] + lea r5, [r3 + r3 * 2] + mov r6, r0 + mov r7, r2 + pxor m10, m10 + pxor m11, m11 + + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 64] + lea r2, [r7 + 64] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 80] + lea r2, [r7 + 80] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 96] + lea r2, [r7 + 96] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 112] + lea r2, [r7 + 112] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + lea r6, [r6 + r1 * 8] + lea r6, [r6 + r1 * 8] + lea r7, [r7 + r3 * 8] + lea r7, [r7 + r3 * 8] + pxor m10, m10 + mov r0, r6 + mov r2, r7 + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 64] + lea r2, [r7 + 64] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 80] + lea r2, [r7 + 80] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 96] + lea r2, [r7 + 96] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 112] + lea r2, [r7 + 112] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + lea r6, [r6 + r1 * 8] + lea r6, [r6 + r1 * 8] + lea r7, [r7 + r3 * 8] + lea r7, [r7 + r3 * 8] + pxor m10, m10 + mov r0, r6 + mov r2, r7 + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 64] + lea r2, [r7 + 64] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 80] + lea r2, [r7 + 80] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 96] + lea r2, [r7 + 96] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 112] + lea r2, [r7 + 112] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + lea r6, [r6 + r1 * 8] + lea r6, [r6 + r1 * 8] + lea r7, [r7 + r3 * 8] + lea r7, [r7 + r3 * 8] + pxor m10, m10 + mov r0, r6 + mov r2, r7 + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 64] + lea r2, [r7 + 64] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 80] + lea r2, [r7 + 80] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + + pxor m10, m10 + lea r0, [r6 + 96] + lea r2, [r7 + 96] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r6 + 112] + lea r2, [r7 + 112] + call sa8d_8x8_12bit + paddd m10, m0 + + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + call sa8d_8x8_12bit + paddd m0, m10 + + vextracti128 xm6, m0, 1 + paddd xm0, xm6 + + movhlps xm6, xm0 + paddd xm0, xm6 + + pshuflw xm6, xm0, 0Eh + paddd xm0, xm6 + paddd xm0, [pd_1] + psrld xm0, 1 + paddd xm11, xm0 + movd eax, xm11 + RET +%endif + + +;============================================================================= +; INTRA SATD +;============================================================================= +%define TRANS TRANS_SSE2 +%define DIFFOP DIFF_UNPACK_SSE2 +%define LOAD_SUMSUB_8x4P LOAD_DIFF_8x4P +%define LOAD_SUMSUB_16P LOAD_SUMSUB_16P_SSE2 +%define movdqa movaps ; doesn't hurt pre-nehalem, might as well save size +%define movdqu movups +%define punpcklqdq movlhps +INIT_XMM sse2 +%if BIT_DEPTH <= 10 +SA8D +%endif +SATDS_SSE2 + +%if HIGH_BIT_DEPTH == 0 +INIT_XMM ssse3,atom +SATDS_SSE2 +SA8D +%endif + +%define DIFFOP DIFF_SUMSUB_SSSE3 +%define LOAD_DUP_4x8P LOAD_DUP_4x8P_CONROE +%if HIGH_BIT_DEPTH == 0 +%define LOAD_SUMSUB_8x4P LOAD_SUMSUB_8x4P_SSSE3 +%define LOAD_SUMSUB_16P LOAD_SUMSUB_16P_SSSE3 +%endif +INIT_XMM ssse3 +%if BIT_DEPTH <= 10 +SA8D +%endif +SATDS_SSE2 +%undef movdqa ; nehalem doesn't like movaps +%undef movdqu ; movups +%undef punpcklqdq ; or movlhps + +%define TRANS TRANS_SSE4 +%define LOAD_DUP_4x8P LOAD_DUP_4x8P_PENRYN +INIT_XMM sse4 +%if BIT_DEPTH <= 10 +SA8D +%endif +SATDS_SSE2 + +; Sandy/Ivy Bridge and Bulldozer do movddup in the load unit, so +; it's effectively free. +%define LOAD_DUP_4x8P LOAD_DUP_4x8P_CONROE +INIT_XMM avx +SA8D +SATDS_SSE2 + +%define TRANS TRANS_XOP +INIT_XMM xop +%if BIT_DEPTH <= 10 +SA8D +%endif +SATDS_SSE2 + +%if HIGH_BIT_DEPTH == 0 +%define LOAD_SUMSUB_8x4P LOAD_SUMSUB8_16x4P_AVX2 +%define LOAD_DUP_4x8P LOAD_DUP_4x16P_AVX2 +%define TRANS TRANS_SSE4 + +%macro LOAD_SUMSUB_8x8P_AVX2 7 ; 4*dst, 2*tmp, mul] + movddup xm%1, [r0] + movddup xm%3, [r2] + movddup xm%2, [r0+4*r1] + movddup xm%5, [r2+4*r3] + vinserti128 m%1, m%1, xm%2, 1 + vinserti128 m%3, m%3, xm%5, 1 + + movddup xm%2, [r0+r1] + movddup xm%4, [r2+r3] + movddup xm%5, [r0+r4] + movddup xm%6, [r2+r5] + vinserti128 m%2, m%2, xm%5, 1 + vinserti128 m%4, m%4, xm%6, 1 + + DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %7 + lea r0, [r0+2*r1] + lea r2, [r2+2*r3] + + movddup xm%3, [r0] + movddup xm%5, [r0+4*r1] + vinserti128 m%3, m%3, xm%5, 1 + + movddup xm%5, [r2] + movddup xm%4, [r2+4*r3] + vinserti128 m%5, m%5, xm%4, 1 + + movddup xm%4, [r0+r1] + movddup xm%6, [r0+r4] + vinserti128 m%4, m%4, xm%6, 1 + + movq xm%6, [r2+r3] + movhps xm%6, [r2+r5] + vpermq m%6, m%6, q1100 + DIFF_SUMSUB_SSSE3 %3, %5, %4, %6, %7 +%endmacro + +%macro SATD_START_AVX2 2-3 0 + FIX_STRIDES r1, r3 +%if %3 + mova %2, [hmul_8p] + lea r4, [5*r1] + lea r5, [5*r3] +%else + mova %2, [hmul_16p] + lea r4, [3*r1] + lea r5, [3*r3] +%endif + pxor %1, %1 +%endmacro + +%define TRANS TRANS_SSE4 +INIT_YMM avx2 +cglobal pixel_satd_16x8_internal + LOAD_SUMSUB_16x4P_AVX2 0, 1, 2, 3, 4, 5, 7, r0, r2, 1 + SATD_8x4_SSE 0, 0, 1, 2, 3, 4, 5, 6 + LOAD_SUMSUB_16x4P_AVX2 0, 1, 2, 3, 4, 5, 7, r0, r2, 0 + SATD_8x4_SSE 0, 0, 1, 2, 3, 4, 5, 6 + ret + +cglobal SatdCalculation_16x16, 4,6,8 + SATD_START_AVX2 m6, m7 + call pixel_satd_16x8_internal + lea r0, [r0+4*r1] + lea r2, [r2+4*r3] +pixel_satd_16x8_internal: + call pixel_satd_16x8_internal + vextracti128 xm0, m6, 1 + paddw xm0, xm6 + SATD_END_SSE2 xm0 + RET + +cglobal pixel_satd_16x8, 4,6,8 + SATD_START_AVX2 m6, m7 + jmp pixel_satd_16x8_internal + +cglobal pixel_satd_8x8_internal + LOAD_SUMSUB_8x8P_AVX2 0, 1, 2, 3, 4, 5, 7 + SATD_8x4_SSE 0, 0, 1, 2, 3, 4, 5, 6 + ret + +cglobal pixel_satd_8x16, 4,6,8 + SATD_START_AVX2 m6, m7, 1 + call pixel_satd_8x8_internal + lea r0, [r0+2*r1] + lea r2, [r2+2*r3] + lea r0, [r0+4*r1] + lea r2, [r2+4*r3] + call pixel_satd_8x8_internal + vextracti128 xm0, m6, 1 + paddw xm0, xm6 + SATD_END_SSE2 xm0 + RET + +cglobal pixel_satd_8x8, 4,6,8 + SATD_START_AVX2 m6, m7, 1 + call pixel_satd_8x8_internal + vextracti128 xm0, m6, 1 + paddw xm0, xm6 + SATD_END_SSE2 xm0 + RET + +cglobal pixel_sa8d_8x8_internal + LOAD_SUMSUB_8x8P_AVX2 0, 1, 2, 3, 4, 5, 7 + HADAMARD4_V 0, 1, 2, 3, 4 + HADAMARD 8, sumsub, 0, 1, 4, 5 + HADAMARD 8, sumsub, 2, 3, 4, 5 + HADAMARD 2, sumsub, 0, 1, 4, 5 + HADAMARD 2, sumsub, 2, 3, 4, 5 + HADAMARD 1, amax, 0, 1, 4, 5 + HADAMARD 1, amax, 2, 3, 4, 5 + paddw m6, m0 + paddw m6, m2 + ret + +cglobal pixel_sa8d_8x8, 4,6,8 + SATD_START_AVX2 m6, m7, 1 + call pixel_sa8d_8x8_internal + vextracti128 xm1, m6, 1 + paddw xm6, xm1 + HADDW xm6, xm1 + movd eax, xm6 + add eax, 1 + shr eax, 1 + RET + +cglobal pixel_sa8d_16x16, 4,6,8 + SATD_START_AVX2 m6, m7, 1 + + call pixel_sa8d_8x8_internal ; pix[0] + + sub r0, r1 + sub r0, r1 + add r0, 8*SIZEOF_PIXEL + sub r2, r3 + sub r2, r3 + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal ; pix[8] + + add r0, r4 + add r0, r1 + add r2, r5 + add r2, r3 + call pixel_sa8d_8x8_internal ; pix[8*stride+8] + + sub r0, r1 + sub r0, r1 + sub r0, 8*SIZEOF_PIXEL + sub r2, r3 + sub r2, r3 + sub r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal ; pix[8*stride] + + ; TODO: analyze Dynamic Range + vextracti128 xm0, m6, 1 + paddusw xm6, xm0 + HADDUW xm6, xm0 + movd eax, xm6 + add eax, 1 + shr eax, 1 + RET + +cglobal pixel_sa8d_16x16_internal + call pixel_sa8d_8x8_internal ; pix[0] + + sub r0, r1 + sub r0, r1 + add r0, 8*SIZEOF_PIXEL + sub r2, r3 + sub r2, r3 + add r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal ; pix[8] + + add r0, r4 + add r0, r1 + add r2, r5 + add r2, r3 + call pixel_sa8d_8x8_internal ; pix[8*stride+8] + + sub r0, r1 + sub r0, r1 + sub r0, 8*SIZEOF_PIXEL + sub r2, r3 + sub r2, r3 + sub r2, 8*SIZEOF_PIXEL + call pixel_sa8d_8x8_internal ; pix[8*stride] + + ; TODO: analyze Dynamic Range + vextracti128 xm0, m6, 1 + paddusw xm6, xm0 + HADDUW xm6, xm0 + movd eax, xm6 + add eax, 1 + shr eax, 1 + ret + +%if ARCH_X86_64 +cglobal pixel_sa8d_32x32, 4,8,8 + ; TODO: R6 is RAX on x64 platform, so we use it directly + + SATD_START_AVX2 m6, m7, 1 + xor r7d, r7d + + call pixel_sa8d_16x16_internal ; [0] + pxor m6, m6 + add r7d, eax + + add r0, r4 + add r0, r1 + add r2, r5 + add r2, r3 + call pixel_sa8d_16x16_internal ; [2] + pxor m6, m6 + add r7d, eax + + lea eax, [r4 * 5 - 16] + sub r0, rax + sub r0, r1 + lea eax, [r5 * 5 - 16] + sub r2, rax + sub r2, r3 + call pixel_sa8d_16x16_internal ; [1] + pxor m6, m6 + add r7d, eax + + add r0, r4 + add r0, r1 + add r2, r5 + add r2, r3 + call pixel_sa8d_16x16_internal ; [3] + add eax, r7d + RET +%endif ; ARCH_X86_64=1 +%endif ; HIGH_BIT_DEPTH + +%macro HMAXABSW2 4 ; a, b, tmp1, tmp2 + pabsw m%1, m%1 + pabsw m%2, m%2 + psrldq m%3, m%1, 2 + psrld m%4, m%2, 16 + pmaxsw m%1, m%3 + pmaxsw m%2, m%4 +%endmacro + +;;--------------------------------------------------------------- +;; SATD AVX2 +;; int pixel_satd(const pixel*, intptr_t, const pixel*, intptr_t) +;;--------------------------------------------------------------- +;; r0 - pix0 +;; r1 - pix0Stride +;; r2 - pix1 +;; r3 - pix1Stride + +%if ARCH_X86_64 == 1 && HIGH_BIT_DEPTH == 0 +INIT_YMM avx2 +cglobal calc_satd_16x8 ; function to compute satd cost for 16 columns, 8 rows + pxor m6, m6 + vbroadcasti128 m0, [r0] + vbroadcasti128 m4, [r2] + vbroadcasti128 m1, [r0 + r1] + vbroadcasti128 m5, [r2 + r3] + pmaddubsw m4, m7 + pmaddubsw m0, m7 + pmaddubsw m5, m7 + pmaddubsw m1, m7 + psubw m0, m4 + psubw m1, m5 + vbroadcasti128 m2, [r0 + r1 * 2] + vbroadcasti128 m4, [r2 + r3 * 2] + vbroadcasti128 m3, [r0 + r4] + vbroadcasti128 m5, [r2 + r5] + pmaddubsw m4, m7 + pmaddubsw m2, m7 + pmaddubsw m5, m7 + pmaddubsw m3, m7 + psubw m2, m4 + psubw m3, m5 + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + paddw m4, m0, m1 + psubw m1, m1, m0 + paddw m0, m2, m3 + psubw m3, m2 + paddw m2, m4, m0 + psubw m0, m4 + paddw m4, m1, m3 + psubw m3, m1 + pabsw m2, m2 + pabsw m0, m0 + pabsw m4, m4 + pabsw m3, m3 + pblendw m1, m2, m0, 10101010b + pslld m0, 16 + psrld m2, 16 + por m0, m2 + pmaxsw m1, m0 + paddw m6, m1 + pblendw m2, m4, m3, 10101010b + pslld m3, 16 + psrld m4, 16 + por m3, m4 + pmaxsw m2, m3 + paddw m6, m2 + vbroadcasti128 m1, [r0] + vbroadcasti128 m4, [r2] + vbroadcasti128 m2, [r0 + r1] + vbroadcasti128 m5, [r2 + r3] + pmaddubsw m4, m7 + pmaddubsw m1, m7 + pmaddubsw m5, m7 + pmaddubsw m2, m7 + psubw m1, m4 + psubw m2, m5 + vbroadcasti128 m0, [r0 + r1 * 2] + vbroadcasti128 m4, [r2 + r3 * 2] + vbroadcasti128 m3, [r0 + r4] + vbroadcasti128 m5, [r2 + r5] + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + pmaddubsw m4, m7 + pmaddubsw m0, m7 + pmaddubsw m5, m7 + pmaddubsw m3, m7 + psubw m0, m4 + psubw m3, m5 + paddw m4, m1, m2 + psubw m2, m1 + paddw m1, m0, m3 + psubw m3, m0 + paddw m0, m4, m1 + psubw m1, m4 + paddw m4, m2, m3 + psubw m3, m2 + pabsw m0, m0 + pabsw m1, m1 + pabsw m4, m4 + pabsw m3, m3 + pblendw m2, m0, m1, 10101010b + pslld m1, 16 + psrld m0, 16 + por m1, m0 + pmaxsw m2, m1 + paddw m6, m2 + pblendw m0, m4, m3, 10101010b + pslld m3, 16 + psrld m4, 16 + por m3, m4 + pmaxsw m0, m3 + paddw m6, m0 + vextracti128 xm0, m6, 1 + pmovzxwd m6, xm6 + pmovzxwd m0, xm0 + paddd m8, m6 + paddd m9, m0 + ret + +cglobal calc_satd_16x4 ; function to compute satd cost for 16 columns, 4 rows + pxor m6, m6 + vbroadcasti128 m0, [r0] + vbroadcasti128 m4, [r2] + vbroadcasti128 m1, [r0 + r1] + vbroadcasti128 m5, [r2 + r3] + pmaddubsw m4, m7 + pmaddubsw m0, m7 + pmaddubsw m5, m7 + pmaddubsw m1, m7 + psubw m0, m4 + psubw m1, m5 + vbroadcasti128 m2, [r0 + r1 * 2] + vbroadcasti128 m4, [r2 + r3 * 2] + vbroadcasti128 m3, [r0 + r4] + vbroadcasti128 m5, [r2 + r5] + pmaddubsw m4, m7 + pmaddubsw m2, m7 + pmaddubsw m5, m7 + pmaddubsw m3, m7 + psubw m2, m4 + psubw m3, m5 + paddw m4, m0, m1 + psubw m1, m1, m0 + paddw m0, m2, m3 + psubw m3, m2 + paddw m2, m4, m0 + psubw m0, m4 + paddw m4, m1, m3 + psubw m3, m1 + pabsw m2, m2 + pabsw m0, m0 + pabsw m4, m4 + pabsw m3, m3 + pblendw m1, m2, m0, 10101010b + pslld m0, 16 + psrld m2, 16 + por m0, m2 + pmaxsw m1, m0 + paddw m6, m1 + pblendw m2, m4, m3, 10101010b + pslld m3, 16 + psrld m4, 16 + por m3, m4 + pmaxsw m2, m3 + paddw m6, m2 + vextracti128 xm0, m6, 1 + pmovzxwd m6, xm6 + pmovzxwd m0, xm0 + paddd m8, m6 + paddd m9, m0 + ret + +cglobal pixel_satd_16x4, 4,6,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + + call calc_satd_16x4 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_16x12, 4,6,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + + call calc_satd_16x8 + call calc_satd_16x4 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_16x32, 4,6,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_16x64, 4,6,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_32x8, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_32x16, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_32x24, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_32x32, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_32x64, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 16] + lea r2, [r7 + 16] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_48x64, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_64x16, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_64x32, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_64x48, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +cglobal pixel_satd_64x64, 4,8,10 ; if WIN64 && cpuflag(avx2) + mova m7, [hmul_16p] + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m8, m8 + pxor m9, m9 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 16] + lea r2, [r7 + 16] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 32] + lea r2, [r7 + 32] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + lea r0, [r6 + 48] + lea r2, [r7 + 48] + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + paddd m8, m9 + vextracti128 xm0, m8, 1 + paddd xm0, xm8 + movhlps xm1, xm0 + paddd xm0, xm1 + pshuflw xm1, xm0, q0032 + paddd xm0, xm1 + movd eax, xm0 + RET + +%endif ; ARCH_X86_64 == 1 && HIGH_BIT_DEPTH == 0 +%if ARCH_X86_64 == 1 && HIGH_BIT_DEPTH == 1 +INIT_YMM avx2 +cglobal calc_satd_16x8 ; function to compute satd cost for 16 columns, 8 rows + ; rows 0-3 + movu m0, [r0] + movu m4, [r2] + psubw m0, m4 + movu m1, [r0 + r1] + movu m5, [r2 + r3] + psubw m1, m5 + movu m2, [r0 + r1 * 2] + movu m4, [r2 + r3 * 2] + psubw m2, m4 + movu m3, [r0 + r4] + movu m5, [r2 + r5] + psubw m3, m5 + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + paddw m4, m0, m1 + psubw m1, m0 + paddw m0, m2, m3 + psubw m3, m2 + punpckhwd m2, m4, m1 + punpcklwd m4, m1 + punpckhwd m1, m0, m3 + punpcklwd m0, m3 + paddw m3, m4, m0 + psubw m0, m4 + paddw m4, m2, m1 + psubw m1, m2 + punpckhdq m2, m3, m0 + punpckldq m3, m0 + paddw m0, m3, m2 + psubw m2, m3 + punpckhdq m3, m4, m1 + punpckldq m4, m1 + paddw m1, m4, m3 + psubw m3, m4 + punpckhqdq m4, m0, m1 + punpcklqdq m0, m1 + pabsw m0, m0 + pabsw m4, m4 + pmaxsw m0, m0, m4 + punpckhqdq m1, m2, m3 + punpcklqdq m2, m3 + pabsw m2, m2 + pabsw m1, m1 + pmaxsw m2, m1 + pxor m7, m7 + mova m1, m0 + punpcklwd m1, m7 + paddd m6, m1 + mova m1, m0 + punpckhwd m1, m7 + paddd m6, m1 + pxor m7, m7 + mova m1, m2 + punpcklwd m1, m7 + paddd m6, m1 + mova m1, m2 + punpckhwd m1, m7 + paddd m6, m1 + ; rows 4-7 + movu m0, [r0] + movu m4, [r2] + psubw m0, m4 + movu m1, [r0 + r1] + movu m5, [r2 + r3] + psubw m1, m5 + movu m2, [r0 + r1 * 2] + movu m4, [r2 + r3 * 2] + psubw m2, m4 + movu m3, [r0 + r4] + movu m5, [r2 + r5] + psubw m3, m5 + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + paddw m4, m0, m1 + psubw m1, m0 + paddw m0, m2, m3 + psubw m3, m2 + punpckhwd m2, m4, m1 + punpcklwd m4, m1 + punpckhwd m1, m0, m3 + punpcklwd m0, m3 + paddw m3, m4, m0 + psubw m0, m4 + paddw m4, m2, m1 + psubw m1, m2 + punpckhdq m2, m3, m0 + punpckldq m3, m0 + paddw m0, m3, m2 + psubw m2, m3 + punpckhdq m3, m4, m1 + punpckldq m4, m1 + paddw m1, m4, m3 + psubw m3, m4 + punpckhqdq m4, m0, m1 + punpcklqdq m0, m1 + pabsw m0, m0 + pabsw m4, m4 + pmaxsw m0, m0, m4 + punpckhqdq m1, m2, m3 + punpcklqdq m2, m3 + pabsw m2, m2 + pabsw m1, m1 + pmaxsw m2, m1 + pxor m7, m7 + mova m1, m0 + punpcklwd m1, m7 + paddd m6, m1 + mova m1, m0 + punpckhwd m1, m7 + paddd m6, m1 + pxor m7, m7 + mova m1, m2 + punpcklwd m1, m7 + paddd m6, m1 + mova m1, m2 + punpckhwd m1, m7 + paddd m6, m1 + ret + +cglobal calc_satd_16x4 ; function to compute satd cost for 16 columns, 4 rows + ; rows 0-3 + movu m0, [r0] + movu m4, [r2] + psubw m0, m4 + movu m1, [r0 + r1] + movu m5, [r2 + r3] + psubw m1, m5 + movu m2, [r0 + r1 * 2] + movu m4, [r2 + r3 * 2] + psubw m2, m4 + movu m3, [r0 + r4] + movu m5, [r2 + r5] + psubw m3, m5 + lea r0, [r0 + r1 * 4] + lea r2, [r2 + r3 * 4] + paddw m4, m0, m1 + psubw m1, m0 + paddw m0, m2, m3 + psubw m3, m2 + punpckhwd m2, m4, m1 + punpcklwd m4, m1 + punpckhwd m1, m0, m3 + punpcklwd m0, m3 + paddw m3, m4, m0 + psubw m0, m4 + paddw m4, m2, m1 + psubw m1, m2 + punpckhdq m2, m3, m0 + punpckldq m3, m0 + paddw m0, m3, m2 + psubw m2, m3 + punpckhdq m3, m4, m1 + punpckldq m4, m1 + paddw m1, m4, m3 + psubw m3, m4 + punpckhqdq m4, m0, m1 + punpcklqdq m0, m1 + pabsw m0, m0 + pabsw m4, m4 + pmaxsw m0, m0, m4 + punpckhqdq m1, m2, m3 + punpcklqdq m2, m3 + pabsw m2, m2 + pabsw m1, m1 + pmaxsw m2, m1 + pxor m7, m7 + mova m1, m0 + punpcklwd m1, m7 + paddd m6, m1 + mova m1, m0 + punpckhwd m1, m7 + paddd m6, m1 + pxor m7, m7 + mova m1, m2 + punpcklwd m1, m7 + paddd m6, m1 + mova m1, m2 + punpckhwd m1, m7 + paddd m6, m1 + ret + +cglobal pixel_satd_16x4, 4,6,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + + call calc_satd_16x4 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_16x8, 4,6,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_16x12, 4,6,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + + call calc_satd_16x8 + call calc_satd_16x4 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_16x16, 4,6,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_16x32, 4,6,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_16x64, 4,6,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_32x8, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_32x16, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_32x24, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_32x32, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_32x64, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_48x64, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 64] + lea r2, [r7 + 64] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_64x16, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 64] + lea r2, [r7 + 64] + + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 96] + lea r2, [r7 + 96] + + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_64x32, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 64] + lea r2, [r7 + 64] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 96] + lea r2, [r7 + 96] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_64x48, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 64] + lea r2, [r7 + 64] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 96] + lea r2, [r7 + 96] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +cglobal pixel_satd_64x64, 4,8,8 + add r1d, r1d + add r3d, r3d + lea r4, [3 * r1] + lea r5, [3 * r3] + pxor m6, m6 + mov r6, r0 + mov r7, r2 + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 32] + lea r2, [r7 + 32] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 64] + lea r2, [r7 + 64] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + lea r0, [r6 + 96] + lea r2, [r7 + 96] + + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + call calc_satd_16x8 + + vextracti128 xm7, m6, 1 + paddd xm6, xm7 + pxor xm7, xm7 + movhlps xm7, xm6 + paddd xm6, xm7 + pshufd xm7, xm6, 1 + paddd xm6, xm7 + movd eax, xm6 + RET + +%endif ; ARCH_X86_64 == 1 && HIGH_BIT_DEPTH == 1 + + +;------------------------------------------------------------------------------------------------------------------------------------- +; pixel planeClipAndMax(pixel *src, intptr_t stride, int width, int height, uint64_t *outsum, const pixel minPix, const pixel maxPix) +;------------------------------------------------------------------------------------------------------------------------------------- + +%if HIGH_BIT_DEPTH == 1 && BIT_DEPTH == 10 +%macro LOAD_DIFF_AVX2 4 + movu %1, %3 + movu %2, %4 + psubw %1, %2 +%endmacro + +%macro LOAD_DIFF_8x4P_AVX2 6-8 r0,r2 ; 4x dest, 2x temp, 2x pointer + LOAD_DIFF_AVX2 xm%1, xm%5, [%7], [%8] + LOAD_DIFF_AVX2 xm%2, xm%6, [%7+r1], [%8+r3] + LOAD_DIFF_AVX2 xm%3, xm%5, [%7+2*r1], [%8+2*r3] + LOAD_DIFF_AVX2 xm%4, xm%6, [%7+r4], [%8+r5] + + ;lea %7, [%7+4*r1] + ;lea %8, [%8+4*r3] +%endmacro + +%if ARCH_X86_64 +INIT_YMM avx2 +cglobal pixel_satd_8x8, 4,4,7 + + FIX_STRIDES r1, r3 + pxor xm6, xm6 + + ; load_diff 0 & 4 + movu xm0, [r0] + movu xm1, [r2] + vinserti128 m0, m0, [r0 + r1 * 4], 1 + vinserti128 m1, m1, [r2 + r3 * 4], 1 + psubw m0, m1 + add r0, r1 + add r2, r3 + + ; load_diff 1 & 5 + movu xm1, [r0] + movu xm2, [r2] + vinserti128 m1, m1, [r0 + r1 * 4], 1 + vinserti128 m2, m2, [r2 + r3 * 4], 1 + psubw m1, m2 + add r0, r1 + add r2, r3 + + ; load_diff 2 & 6 + movu xm2, [r0] + movu xm3, [r2] + vinserti128 m2, m2, [r0 + r1 * 4], 1 + vinserti128 m3, m3, [r2 + r3 * 4], 1 + psubw m2, m3 + add r0, r1 + add r2, r3 + + ; load_diff 3 & 7 + movu xm3, [r0] + movu xm4, [r2] + vinserti128 m3, m3, [r0 + r1 * 4], 1 + vinserti128 m4, m4, [r2 + r3 * 4], 1 + psubw m3, m4 + + SATD_8x4_SSE vertical, 0, 1, 2, 3, 4, 5, 6 + + vextracti128 xm0, m6, 1 + paddw xm6, xm0 + HADDUW xm6, xm0 + movd eax, xm6 + RET + +INIT_XMM avx2 +cglobal pixel_sa8d_8x8_internal + lea r6, [r0+4*r1] + lea r7, [r2+4*r3] + LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + ;HADAMARD2_2D 0, 1, 2, 8, 6, wd + ;HADAMARD2_2D 4, 5, 3, 9, 6, wd + ;HADAMARD2_2D 0, 2, 1, 8, 6, dq + ;HADAMARD2_2D 4, 3, 5, 9, 6, dq + ;HADAMARD2_2D 0, 4, 2, 3, 6, qdq, amax + ;HADAMARD2_2D 1, 5, 8, 9, 6, qdq, amax + + paddw m0, m1 + paddw m0, m2 + paddw m0, m8 + SAVE_MM_PERMUTATION + ret + + +INIT_XMM avx2 +cglobal pixel_sa8d_8x8, 4,8,12 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + call pixel_sa8d_8x8_internal + HADDUW m0, m1 + movd eax, m0 + add eax, 1 + shr eax, 1 + RET + + +INIT_YMM avx2 +cglobal pixel_sa8d_16x16, 4,8,12 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + lea r6, [r0+4*r1] + lea r7, [r2+4*r3] + vbroadcasti128 m7, [pw_1] + + ; Top 16x8 + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] ; 10 bits + movu m5, [r2] + psubw m0, m5 ; 11 bits + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax ; 16 bits + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m10, m0, m2 + + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + lea r6, [r6+8*r1] + lea r7, [r7+8*r3] + + ; Bottom 16x8 + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] + movu m5, [r2] + psubw m0, m5 + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m10, m0 + paddd m10, m2 + + HADDD m10, m0 + + movd eax, xm10 + add eax, 1 + shr eax, 1 + RET + + +; TODO: optimize me, need more 2 of YMM registers because C model get partial result every 16x16 block +INIT_YMM avx2 +cglobal pixel_sa8d_32x32, 4,8,14 + FIX_STRIDES r1, r3 + lea r4, [3*r1] + lea r5, [3*r3] + lea r6, [r0+4*r1] + lea r7, [r2+4*r3] + vbroadcasti128 m7, [pw_1] + + + ;SA8D[16x8] ; pix[0] + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] + movu m5, [r2] + psubw m0, m5 + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m10, m0, m2 + + + ; SA8D[16x8] ; pix[16] + add r0, mmsize + add r2, mmsize + add r6, mmsize + add r7, mmsize + + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] + movu m5, [r2] + psubw m0, m5 + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m12, m0, m2 + + + ; SA8D[16x8] ; pix[8*stride+16] + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + lea r6, [r6+8*r1] + lea r7, [r7+8*r3] + + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] + movu m5, [r2] + psubw m0, m5 + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m12, m0 + paddd m12, m2 + + ; sum[1] + HADDD m12, m0 + + + ; SA8D[16x8] ; pix[8*stride] + sub r0, mmsize + sub r2, mmsize + sub r6, mmsize + sub r7, mmsize + + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] + movu m5, [r2] + psubw m0, m5 + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m10, m0 + paddd m10, m2 + + ; sum[0] + HADDD m10, m0 + punpckldq xm10, xm12 + + + ;SA8D[16x8] ; pix[16*stridr] + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + lea r6, [r6+8*r1] + lea r7, [r7+8*r3] + + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] + movu m5, [r2] + psubw m0, m5 + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m12, m0, m2 + + + ; SA8D[16x8] ; pix[16*stride+16] + add r0, mmsize + add r2, mmsize + add r6, mmsize + add r7, mmsize + + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] + movu m5, [r2] + psubw m0, m5 + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m13, m0, m2 + + + ; SA8D[16x8] ; pix[24*stride+16] + lea r0, [r0+8*r1] + lea r2, [r2+8*r3] + lea r6, [r6+8*r1] + lea r7, [r7+8*r3] + + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] + movu m5, [r2] + psubw m0, m5 + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m13, m0 + paddd m13, m2 + + ; sum[3] + HADDD m13, m0 + + + ; SA8D[16x8] ; pix[24*stride] + sub r0, mmsize + sub r2, mmsize + sub r6, mmsize + sub r7, mmsize + + ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 + movu m0, [r0] + movu m5, [r2] + psubw m0, m5 + movu m1, [r0 + r1] + movu m6, [r2 + r3] + psubw m1, m6 + movu m2, [r0 + r1 * 2] + movu m5, [r2 + r3 * 2] + psubw m2, m5 + movu m8, [r0 + r4] + movu m6, [r2 + r5] + psubw m8, m6 + + ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 + movu m4, [r6] + movu m11, [r7] + psubw m4, m11 + movu m5, [r6 + r1] + movu m6, [r7 + r3] + psubw m5, m6 + movu m3, [r6 + r1 * 2] + movu m11, [r7 + r3 * 2] + psubw m3, m11 + movu m9, [r6 + r4] + movu m6, [r7 + r5] + psubw m9, m6 + + HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax + pmaddwd m0, m7 + pmaddwd m1, m7 + pmaddwd m2, m7 + pmaddwd m8, m7 + paddd m0, m1 + paddd m2, m8 + paddd m12, m0 + paddd m12, m2 + + ; sum[2] + HADDD m12, m0 + punpckldq xm12, xm13 + + ; SA8D + punpcklqdq xm0, xm10, xm12 + paddd xm0, [pd_1] + psrld xm0, 1 + HADDD xm0, xm1 + + movd eax, xm0 + RET +%endif +%endif ; HIGH_BIT_DEPTH == 1 && BIT_DEPTH == 10 diff --git a/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h b/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h new file mode 100644 index 000000000..6fbf6388c --- /dev/null +++ b/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h @@ -0,0 +1,23 @@ +/* +* Copyright(c) 2018 Intel Corporation +* SPDX - License - Identifier: BSD - 2 - Clause - Patent +*/ + +#ifndef EbMeSatdCalculation_AVX2_h +#define EbMeSatdCalculation_AVX2_h + +#include "EbDefinitions.h" +#ifdef __cplusplus +extern "C" { +#endif + +EB_U32 SatdCalculation_16x16_avx2( + EB_U8 *src, + EB_U32 srcStride, + EB_U8 *ref, + EB_U32 refStride); + +#ifdef __cplusplus +} +#endif +#endif // EbMeSadCalculation_C_h \ No newline at end of file diff --git a/Source/Lib/ASM_AVX2/const-a.asm b/Source/Lib/ASM_AVX2/const-a.asm new file mode 100644 index 000000000..4553a21b1 --- /dev/null +++ b/Source/Lib/ASM_AVX2/const-a.asm @@ -0,0 +1,159 @@ +;***************************************************************************** +;* const-a.asm: x86 global constants +;***************************************************************************** +;* Copyright (C) 2003-2013 x264 project +;* Copyright (C) 2013-2017 MulticoreWare, Inc +;* +;* Authors: Loren Merritt +;* Fiona Glaser +;* Min Chen +;* Praveen Kumar Tiwari +;* This program is free software; you can redistribute it and/or modify +;* it under the terms of the GNU General Public License as published by +;* the Free Software Foundation; either version 2 of the License, or +;* (at your option) any later version. +;* +;* This program is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;* GNU General Public License for more details. +;* +;* You should have received a copy of the GNU General Public License +;* along with this program; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. +;* +;* This program is also available under a commercial proprietary license. +;* For more information, contact us at license @ x265.com. +;***************************************************************************** + +%include "x86inc.asm" + +SECTION_RODATA 64 + +;; 8-bit constants + +const pb_0, times 32 db 0 +const pb_1, times 32 db 1 +const pb_2, times 32 db 2 +const pb_3, times 32 db 3 +const pb_4, times 32 db 4 +const pb_8, times 32 db 8 +const pb_15, times 32 db 15 +const pb_16, times 32 db 16 +const pb_31, times 32 db 31 +const pb_32, times 32 db 32 +const pb_64, times 32 db 64 +const pb_124, times 32 db 124 +const pb_128, times 32 db 128 +const pb_a1, times 16 db 0xa1 + +const pb_01, times 8 db 0, 1 +const pb_0123, times 4 db 0, 1 + times 4 db 2, 3 +const hsub_mul, times 16 db 1, -1 +const pw_swap, times 2 db 6, 7, 4, 5, 2, 3, 0, 1 +const pb_unpackbd1, times 2 db 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 +const pb_unpackbd2, times 2 db 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7 +const pb_unpackwq1, times 1 db 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3 +const pb_unpackwq2, times 1 db 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7 +const pb_shuf8x8c, times 1 db 0, 0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6 +const pb_movemask, times 16 db 0x00 + times 16 db 0xFF + +const pb_movemask_32, times 32 db 0x00 + times 32 db 0xFF + times 32 db 0x00 + +const pb_0000000000000F0F, times 2 db 0xff, 0x00 + times 12 db 0x00 +const pb_000000000000000F, db 0xff + times 15 db 0x00 +const pb_shuf_off4, times 2 db 0, 4, 1, 5, 2, 6, 3, 7 +const pw_shuf_off4, times 1 db 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 + +;; 16-bit constants + +const pw_n1, times 16 dw -1 +const pw_1, times 16 dw 1 +const pw_2, times 16 dw 2 +const pw_3, times 16 dw 3 +const pw_7, times 16 dw 7 +const pw_m2, times 8 dw -2 +const pw_4, times 8 dw 4 +const pw_8, times 8 dw 8 +const pw_16, times 16 dw 16 +const pw_15, times 16 dw 15 +const pw_31, times 16 dw 31 +const pw_32, times 16 dw 32 +const pw_64, times 8 dw 64 +const pw_128, times 16 dw 128 +const pw_256, times 16 dw 256 +const pw_257, times 16 dw 257 +const pw_512, times 16 dw 512 +const pw_1023, times 16 dw 1023 +const pw_1024, times 16 dw 1024 +const pw_2048, times 16 dw 2048 +const pw_4096, times 16 dw 4096 +const pw_8192, times 8 dw 8192 +const pw_00ff, times 16 dw 0x00ff +const pw_ff00, times 8 dw 0xff00 +const pw_2000, times 16 dw 0x2000 +const pw_8000, times 8 dw 0x8000 +const pw_3fff, times 16 dw 0x3fff +const pw_32_0, times 4 dw 32, + times 4 dw 0 +const pw_pixel_max, times 16 dw ((1 << BIT_DEPTH)-1) + +const pw_0_7, times 2 dw 0, 1, 2, 3, 4, 5, 6, 7 +const pw_ppppmmmm, times 1 dw 1, 1, 1, 1, -1, -1, -1, -1 +const pw_ppmmppmm, times 1 dw 1, 1, -1, -1, 1, 1, -1, -1 +const pw_pmpmpmpm, times 16 dw 1, -1, 1, -1, 1, -1, 1, -1 +const pw_pmmpzzzz, times 1 dw 1, -1, -1, 1, 0, 0, 0, 0 +const multi_2Row, times 1 dw 1, 2, 3, 4, 1, 2, 3, 4 +const multiH, times 1 dw 9, 10, 11, 12, 13, 14, 15, 16 +const multiH3, times 1 dw 25, 26, 27, 28, 29, 30, 31, 32 +const multiL, times 1 dw 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +const multiH2, times 1 dw 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 +const pw_planar16_mul, times 1 dw 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +const pw_planar32_mul, times 1 dw 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16 +const pw_FFFFFFFFFFFFFFF0, dw 0x00 + times 7 dw 0xff +const hmul_16p, times 16 db 1 + times 8 db 1, -1 +const pw_exp2_0_15, dw 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 +const pw_1_ffff, times 4 dw 1 + times 4 dw 0xFFFF + + +;; 32-bit constants + +const pd_1, times 8 dd 1 +const pd_2, times 8 dd 2 +const pd_4, times 4 dd 4 +const pd_8, times 4 dd 8 +const pd_15, times 8 dd 15 +const pd_16, times 8 dd 16 +const pd_31, times 8 dd 31 +const pd_32, times 8 dd 32 +const pd_64, times 4 dd 64 +const pd_128, times 4 dd 128 +const pd_256, times 4 dd 256 +const pd_512, times 4 dd 512 +const pd_1024, times 4 dd 1024 +const pd_2048, times 4 dd 2048 +const pd_ffff, times 4 dd 0xffff +const pd_32767, times 4 dd 32767 +const pd_524416, times 4 dd 524416 +const pd_n32768, times 8 dd 0xffff8000 +const pd_n131072, times 4 dd 0xfffe0000 +const pd_0000ffff, times 8 dd 0x0000FFFF +const pd_planar16_mul0, times 1 dd 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +const pd_planar16_mul1, times 1 dd 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +const pd_planar32_mul1, times 1 dd 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16 +const pd_planar32_mul2, times 1 dd 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 +const pd_planar16_mul2, times 1 dd 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +const trans8_shuf, times 1 dd 0, 4, 1, 5, 2, 6, 3, 7 + +;; 64-bit constants + +const pq_1, times 1 dq 1 diff --git a/Source/Lib/ASM_AVX2/x86inc.asm b/Source/Lib/ASM_AVX2/x86inc.asm new file mode 100644 index 000000000..5c2d8af0d --- /dev/null +++ b/Source/Lib/ASM_AVX2/x86inc.asm @@ -0,0 +1,1664 @@ +;***************************************************************************** +;* x86inc.asm: x264asm abstraction layer +;***************************************************************************** +;* Copyright (C) 2003-2013 x264 project +;* Copyright (C) 2013-2017 MulticoreWarae, Inc +;* +;* Authors: Loren Merritt +;* Anton Mitrofanov +;* Fiona Glaser +;* Henrik Gramner +;* Min Chen +;* +;* Permission to use, copy, modify, and/or distribute this software for any +;* purpose with or without fee is hereby granted, provided that the above +;* copyright notice and this permission notice appear in all copies. +;* +;* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +;* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +;* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +;* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +;* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +;* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +;***************************************************************************** + +; This is a header file for the x264ASM assembly language, which uses +; NASM/YASM syntax combined with a large number of macros to provide easy +; abstraction between different calling conventions (x86_32, win64, linux64). +; It also has various other useful features to simplify writing the kind of +; DSP functions that are most often used in x264. + +; Unlike the rest of x264, this file is available under an ISC license, as it +; has significant usefulness outside of x264 and we want it to be available +; to the largest audience possible. Of course, if you modify it for your own +; purposes to add a new feature, we strongly encourage contributing a patch +; as this feature might be useful for others as well. Send patches or ideas +; to x264-devel@videolan.org . + +%define ARCH_X86_64 1 +%define HIGH_BIT_DEPTH 0 +%define BIT_DEPTH 8 + +; %ifndef private_prefix +; %define private_prefix Eb +; %endif + +; %ifndef public_prefix +; %define public_prefix private_prefix +; %endif + +%ifndef STACK_ALIGNMENT + %if ARCH_X86_64 + %define STACK_ALIGNMENT 16 + %else + %define STACK_ALIGNMENT 4 + %endif +%endif + +%define WIN64 0 +%define UNIX64 0 +%if ARCH_X86_64 + %ifidn __OUTPUT_FORMAT__,win32 + %define WIN64 1 + %elifidn __OUTPUT_FORMAT__,win64 + %define WIN64 1 + %elifidn __OUTPUT_FORMAT__,x64 + %define WIN64 1 + %else + %define UNIX64 1 + %endif +%endif + +%define FORMAT_ELF 0 +%ifidn __OUTPUT_FORMAT__,elf + %define FORMAT_ELF 1 +%elifidn __OUTPUT_FORMAT__,elf32 + %define FORMAT_ELF 1 +%elifidn __OUTPUT_FORMAT__,elf64 + %define FORMAT_ELF 1 +%endif + +%ifdef PREFIX + %define mangle(x) _ %+ x +%else + %define mangle(x) x +%endif + +%macro SECTION_RODATA 0-1 32 + %ifidn __OUTPUT_FORMAT__,win32 + SECTION .rdata align=%1 + %elif WIN64 + SECTION .rdata align=%1 + %else + SECTION .rodata align=%1 + %endif +%endmacro + +%if WIN64 + %define PIC +%elif ARCH_X86_64 == 0 +; x86_32 doesn't require PIC. +; Some distros prefer shared objects to be PIC, but nothing breaks if +; the code contains a few textrels, so we'll skip that complexity. + %undef PIC +%endif +%ifdef PIC + default rel +%endif + +%ifdef __NASM_VER__ + %use smartalign +%endif + +; Macros to eliminate most code duplication between x86_32 and x86_64: +; Currently this works only for leaf functions which load all their arguments +; into registers at the start, and make no other use of the stack. Luckily that +; covers most of x264's asm. + +; PROLOGUE: +; %1 = number of arguments. loads them from stack if needed. +; %2 = number of registers used. pushes callee-saved regs if needed. +; %3 = number of xmm registers used. pushes callee-saved xmm regs if needed. +; %4 = (optional) stack size to be allocated. The stack will be aligned before +; allocating the specified stack size. If the required stack alignment is +; larger than the known stack alignment the stack will be manually aligned +; and an extra register will be allocated to hold the original stack +; pointer (to not invalidate r0m etc.). To prevent the use of an extra +; register as stack pointer, request a negative stack size. +; %4+/%5+ = list of names to define to registers +; PROLOGUE can also be invoked by adding the same options to cglobal + +; e.g. +; cglobal foo, 2,3,7,0x40, dst, src, tmp +; declares a function (foo) that automatically loads two arguments (dst and +; src) into registers, uses one additional register (tmp) plus 7 vector +; registers (m0-m6) and allocates 0x40 bytes of stack space. + +; TODO Some functions can use some args directly from the stack. If they're the +; last args then you can just not declare them, but if they're in the middle +; we need more flexible macro. + +; RET: +; Pops anything that was pushed by PROLOGUE, and returns. + +; REP_RET: +; Use this instead of RET if it's a branch target. + +; registers: +; rN and rNq are the native-size register holding function argument N +; rNd, rNw, rNb are dword, word, and byte size +; rNh is the high 8 bits of the word size +; rNm is the original location of arg N (a register or on the stack), dword +; rNmp is native size + +%macro DECLARE_REG 2-3 + %define r%1q %2 + %define r%1d %2d + %define r%1w %2w + %define r%1b %2b + %define r%1h %2h + %define %2q %2 + %if %0 == 2 + %define r%1m %2d + %define r%1mp %2 + %elif ARCH_X86_64 ; memory + %define r%1m [rstk + stack_offset + %3] + %define r%1mp qword r %+ %1 %+ m + %else + %define r%1m [rstk + stack_offset + %3] + %define r%1mp dword r %+ %1 %+ m + %endif + %define r%1 %2 +%endmacro + +%macro DECLARE_REG_SIZE 3 + %define r%1q r%1 + %define e%1q r%1 + %define r%1d e%1 + %define e%1d e%1 + %define r%1w %1 + %define e%1w %1 + %define r%1h %3 + %define e%1h %3 + %define r%1b %2 + %define e%1b %2 + %if ARCH_X86_64 == 0 + %define r%1 e%1 + %endif +%endmacro + +DECLARE_REG_SIZE ax, al, ah +DECLARE_REG_SIZE bx, bl, bh +DECLARE_REG_SIZE cx, cl, ch +DECLARE_REG_SIZE dx, dl, dh +DECLARE_REG_SIZE si, sil, null +DECLARE_REG_SIZE di, dil, null +DECLARE_REG_SIZE bp, bpl, null + +; t# defines for when per-arch register allocation is more complex than just function arguments + +%macro DECLARE_REG_TMP 1-* + %assign %%i 0 + %rep %0 + CAT_XDEFINE t, %%i, r%1 + %assign %%i %%i+1 + %rotate 1 + %endrep +%endmacro + +%macro DECLARE_REG_TMP_SIZE 0-* + %rep %0 + %define t%1q t%1 %+ q + %define t%1d t%1 %+ d + %define t%1w t%1 %+ w + %define t%1h t%1 %+ h + %define t%1b t%1 %+ b + %rotate 1 + %endrep +%endmacro + +DECLARE_REG_TMP_SIZE 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + +%if ARCH_X86_64 + %define gprsize 8 +%else + %define gprsize 4 +%endif + +%macro PUSH 1 + push %1 + %ifidn rstk, rsp + %assign stack_offset stack_offset+gprsize + %endif +%endmacro + +%macro POP 1 + pop %1 + %ifidn rstk, rsp + %assign stack_offset stack_offset-gprsize + %endif +%endmacro + +%macro PUSH_IF_USED 1-* + %rep %0 + %if %1 < regs_used + PUSH r%1 + %endif + %rotate 1 + %endrep +%endmacro + +%macro POP_IF_USED 1-* + %rep %0 + %if %1 < regs_used + pop r%1 + %endif + %rotate 1 + %endrep +%endmacro + +%macro LOAD_IF_USED 1-* + %rep %0 + %if %1 < num_args + mov r%1, r %+ %1 %+ mp + %endif + %rotate 1 + %endrep +%endmacro + +%macro SUB 2 + sub %1, %2 + %ifidn %1, rstk + %assign stack_offset stack_offset+(%2) + %endif +%endmacro + +%macro ADD 2 + add %1, %2 + %ifidn %1, rstk + %assign stack_offset stack_offset-(%2) + %endif +%endmacro + +%macro movifnidn 2 + %ifnidn %1, %2 + mov %1, %2 + %endif +%endmacro + +%macro movsxdifnidn 2 + %ifnidn %1, %2 + movsxd %1, %2 + %endif +%endmacro + +%macro ASSERT 1 + %if (%1) == 0 + %error assertion ``%1'' failed + %endif +%endmacro + +%macro DEFINE_ARGS 0-* + %ifdef n_arg_names + %assign %%i 0 + %rep n_arg_names + CAT_UNDEF arg_name %+ %%i, q + CAT_UNDEF arg_name %+ %%i, d + CAT_UNDEF arg_name %+ %%i, w + CAT_UNDEF arg_name %+ %%i, h + CAT_UNDEF arg_name %+ %%i, b + CAT_UNDEF arg_name %+ %%i, m + CAT_UNDEF arg_name %+ %%i, mp + CAT_UNDEF arg_name, %%i + %assign %%i %%i+1 + %endrep + %endif + + %xdefine %%stack_offset stack_offset + %undef stack_offset ; so that the current value of stack_offset doesn't get baked in by xdefine + %assign %%i 0 + %rep %0 + %xdefine %1q r %+ %%i %+ q + %xdefine %1d r %+ %%i %+ d + %xdefine %1w r %+ %%i %+ w + %xdefine %1h r %+ %%i %+ h + %xdefine %1b r %+ %%i %+ b + %xdefine %1m r %+ %%i %+ m + %xdefine %1mp r %+ %%i %+ mp + CAT_XDEFINE arg_name, %%i, %1 + %assign %%i %%i+1 + %rotate 1 + %endrep + %xdefine stack_offset %%stack_offset + %assign n_arg_names %0 +%endmacro + +%define required_stack_alignment ((mmsize + 15) & ~15) +%define vzeroupper_required (mmsize > 16 && (ARCH_X86_64 == 0 || xmm_regs_used > 16 || notcpuflag(avx512))) +%define high_mm_regs (16*cpuflag(avx512)) + +%macro ALLOC_STACK 1-2 0 ; stack_size, n_xmm_regs (for win64 only) + %ifnum %1 + %if %1 != 0 + %assign %%pad 0 + %assign stack_size %1 + %if stack_size < 0 + %assign stack_size -stack_size + %endif + %if WIN64 + %assign %%pad %%pad + 32 ; shadow space + %if mmsize != 8 + %assign xmm_regs_used %2 + %if xmm_regs_used > 8 + %assign %%pad %%pad + (xmm_regs_used-8)*16 ; callee-saved xmm registers + %endif + %endif + %endif + %if required_stack_alignment <= STACK_ALIGNMENT + ; maintain the current stack alignment + %assign stack_size_padded stack_size + %%pad + ((-%%pad-stack_offset-gprsize) & (STACK_ALIGNMENT-1)) + SUB rsp, stack_size_padded + %else + %assign %%reg_num (regs_used - 1) + %xdefine rstk r %+ %%reg_num + ; align stack, and save original stack location directly above + ; it, i.e. in [rsp+stack_size_padded], so we can restore the + ; stack in a single instruction (i.e. mov rsp, rstk or mov + ; rsp, [rsp+stack_size_padded]) + %if %1 < 0 ; need to store rsp on stack + %xdefine rstkm [rsp + stack_size + %%pad] + %assign %%pad %%pad + gprsize + %else ; can keep rsp in rstk during whole function + %xdefine rstkm rstk + %endif + %assign stack_size_padded stack_size + ((%%pad + required_stack_alignment-1) & ~(required_stack_alignment-1)) + mov rstk, rsp + and rsp, ~(required_stack_alignment-1) + sub rsp, stack_size_padded + movifnidn rstkm, rstk + %endif + WIN64_PUSH_XMM + %endif + %endif +%endmacro + +%macro SETUP_STACK_POINTER 1 + %ifnum %1 + %if %1 != 0 && required_stack_alignment > STACK_ALIGNMENT + %if %1 > 0 + ; Reserve an additional register for storing the original stack pointer, but avoid using + ; eax/rax for this purpose since it can potentially get overwritten as a return value. + %assign regs_used (regs_used + 1) + %if ARCH_X86_64 && regs_used == 7 + %assign regs_used 8 + %elif ARCH_X86_64 == 0 && regs_used == 1 + %assign regs_used 2 + %endif + %endif + %if ARCH_X86_64 && regs_used < 5 + UNIX64 * 3 + ; Ensure that we don't clobber any registers containing arguments. For UNIX64 we also preserve r6 (rax) + ; since it's used as a hidden argument in vararg functions to specify the number of vector registers used. + %assign regs_used 5 + UNIX64 * 3 + %endif + %endif + %endif +%endmacro + +%macro DEFINE_ARGS_INTERNAL 3+ + %ifnum %2 + DEFINE_ARGS %3 + %elif %1 == 4 + DEFINE_ARGS %2 + %elif %1 > 4 + DEFINE_ARGS %2, %3 + %endif +%endmacro + +%if WIN64 ; Windows x64 ;================================================= + +DECLARE_REG 0, rcx +DECLARE_REG 1, rdx +DECLARE_REG 2, R8 +DECLARE_REG 3, R9 +DECLARE_REG 4, R10, 40 +DECLARE_REG 5, R11, 48 +DECLARE_REG 6, rax, 56 +DECLARE_REG 7, rdi, 64 +DECLARE_REG 8, rsi, 72 +DECLARE_REG 9, rbx, 80 +DECLARE_REG 10, rbp, 88 +DECLARE_REG 11, R14, 96 +DECLARE_REG 12, R15, 104 +DECLARE_REG 13, R12, 112 +DECLARE_REG 14, R13, 120 + +%macro PROLOGUE 2-5+ 0 ; #args, #regs, #xmm_regs, [stack_size,] arg_names... + %assign num_args %1 + %assign regs_used %2 + ASSERT regs_used >= num_args + SETUP_STACK_POINTER %4 + ASSERT regs_used <= 15 + PUSH_IF_USED 7, 8, 9, 10, 11, 12, 13, 14 + ALLOC_STACK %4, %3 + %if mmsize != 8 && stack_size == 0 + WIN64_SPILL_XMM %3 + %endif + LOAD_IF_USED 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 + DEFINE_ARGS_INTERNAL %0, %4, %5 +%endmacro + +%macro WIN64_PUSH_XMM 0 + ; Use the shadow space to store XMM6 and XMM7, the rest needs stack space allocated. + %if xmm_regs_used > 6 + high_mm_regs + movaps [rstk + stack_offset + 8], xmm6 + %endif + %if xmm_regs_used > 7 + high_mm_regs + movaps [rstk + stack_offset + 24], xmm7 + %endif + %assign %%xmm_regs_on_stack xmm_regs_used - high_mm_regs - 8 + %if %%xmm_regs_on_stack > 0 + %assign %%i 8 + %rep %%xmm_regs_on_stack + movaps [rsp + (%%i-8)*16 + stack_size + 32], xmm %+ %%i + %assign %%i %%i+1 + %endrep + %endif +%endmacro + +%macro WIN64_SPILL_XMM 1 + %assign xmm_regs_used %1 + ASSERT xmm_regs_used <= 16 + high_mm_regs + %assign %%xmm_regs_on_stack xmm_regs_used - high_mm_regs - 8 + %if %%xmm_regs_on_stack > 0 + ; Allocate stack space for callee-saved xmm registers plus shadow space and align the stack. + %assign %%pad (xmm_regs_used-8)*16 + 32 + %assign stack_size_padded %%pad + ((-%%pad-stack_offset-gprsize) & (STACK_ALIGNMENT-1)) + SUB rsp, stack_size_padded + %endif + WIN64_PUSH_XMM +%endmacro + +%macro WIN64_RESTORE_XMM_INTERNAL 0 + %assign %%pad_size 0 + %assign %%xmm_regs_on_stack xmm_regs_used - high_mm_regs - 8 + %if %%xmm_regs_on_stack > 0 + %assign %%i xmm_regs_used - high_mm_regs + %rep %%xmm_regs_on_stack + %assign %%i %%i-1 + movaps xmm %+ %%i, [rsp + (%%i-8)*16 + stack_size + 32] + %endrep + %endif + %if stack_size_padded > 0 + %if stack_size > 0 && required_stack_alignment > STACK_ALIGNMENT + mov rsp, rstkm + %else + add rsp, stack_size_padded + %assign %%pad_size stack_size_padded + %endif + %endif + %if xmm_regs_used > 7 + high_mm_regs + movaps xmm7, [rsp + stack_offset - %%pad_size + 24] + %endif + %if xmm_regs_used > 6 + high_mm_regs + movaps xmm6, [rsp + stack_offset - %%pad_size + 8] + %endif +%endmacro + +%macro WIN64_RESTORE_XMM 0 + WIN64_RESTORE_XMM_INTERNAL + %assign stack_offset (stack_offset-stack_size_padded) + %assign stack_size_padded 0 + %assign xmm_regs_used 0 +%endmacro + +%define has_epilogue regs_used > 7 || stack_size > 0 || vzeroupper_required || xmm_regs_used > 6 + high_mm_regs + +%macro RET 0 + WIN64_RESTORE_XMM_INTERNAL + POP_IF_USED 14, 13, 12, 11, 10, 9, 8, 7 + %if vzeroupper_required + vzeroupper + %endif + AUTO_REP_RET +%endmacro + +%elif ARCH_X86_64 ; *nix x64 ;============================================= + +DECLARE_REG 0, rdi +DECLARE_REG 1, rsi +DECLARE_REG 2, rdx +DECLARE_REG 3, rcx +DECLARE_REG 4, R8 +DECLARE_REG 5, R9 +DECLARE_REG 6, rax, 8 +DECLARE_REG 7, R10, 16 +DECLARE_REG 8, R11, 24 +DECLARE_REG 9, rbx, 32 +DECLARE_REG 10, rbp, 40 +DECLARE_REG 11, R14, 48 +DECLARE_REG 12, R15, 56 +DECLARE_REG 13, R12, 64 +DECLARE_REG 14, R13, 72 + +%macro PROLOGUE 2-5+ 0; #args, #regs, #xmm_regs, [stack_size,] arg_names... + %assign num_args %1 + %assign regs_used %2 + %assign xmm_regs_used %3 + ASSERT regs_used >= num_args + SETUP_STACK_POINTER %4 + ASSERT regs_used <= 15 + PUSH_IF_USED 9, 10, 11, 12, 13, 14 + ALLOC_STACK %4 + LOAD_IF_USED 6, 7, 8, 9, 10, 11, 12, 13, 14 + DEFINE_ARGS_INTERNAL %0, %4, %5 +%endmacro + +%define has_epilogue regs_used > 9 || stack_size > 0 || vzeroupper_required + +%macro RET 0 + %if stack_size_padded > 0 + %if required_stack_alignment > STACK_ALIGNMENT + mov rsp, rstkm + %else + add rsp, stack_size_padded + %endif + %endif + POP_IF_USED 14, 13, 12, 11, 10, 9 + %if vzeroupper_required + vzeroupper + %endif + AUTO_REP_RET +%endmacro + +%else ; X86_32 ;============================================================== + +DECLARE_REG 0, eax, 4 +DECLARE_REG 1, ecx, 8 +DECLARE_REG 2, edx, 12 +DECLARE_REG 3, ebx, 16 +DECLARE_REG 4, esi, 20 +DECLARE_REG 5, edi, 24 +DECLARE_REG 6, ebp, 28 +%define rsp esp + +%macro DECLARE_ARG 1-* + %rep %0 + %define r%1m [rstk + stack_offset + 4*%1 + 4] + %define r%1mp dword r%1m + %rotate 1 + %endrep +%endmacro + +DECLARE_ARG 7, 8, 9, 10, 11, 12, 13, 14 + +%macro PROLOGUE 2-5+ ; #args, #regs, #xmm_regs, [stack_size,] arg_names... + %assign num_args %1 + %assign regs_used %2 + ASSERT regs_used >= num_args + %if num_args > 7 + %assign num_args 7 + %endif + %if regs_used > 7 + %assign regs_used 7 + %endif + SETUP_STACK_POINTER %4 + ASSERT regs_used <= 7 + PUSH_IF_USED 3, 4, 5, 6 + ALLOC_STACK %4 + LOAD_IF_USED 0, 1, 2, 3, 4, 5, 6 + DEFINE_ARGS_INTERNAL %0, %4, %5 +%endmacro + +%define has_epilogue regs_used > 3 || stack_size > 0 || vzeroupper_required + +%macro RET 0 + %if stack_size_padded > 0 + %if required_stack_alignment > STACK_ALIGNMENT + mov rsp, rstkm + %else + add rsp, stack_size_padded + %endif + %endif + POP_IF_USED 6, 5, 4, 3 + %if vzeroupper_required + vzeroupper + %endif + AUTO_REP_RET +%endmacro + +%endif ;====================================================================== + +%if WIN64 == 0 + %macro WIN64_SPILL_XMM 1 + %endmacro + %macro WIN64_RESTORE_XMM 0 + %endmacro + %macro WIN64_PUSH_XMM 0 + %endmacro +%endif + +; On AMD cpus <=K10, an ordinary ret is slow if it immediately follows either +; a branch or a branch target. So switch to a 2-byte form of ret in that case. +; We can automatically detect "follows a branch", but not a branch target. +; (SSSE3 is a sufficient condition to know that your cpu doesn't have this problem.) +%macro REP_RET 0 + %if has_epilogue + RET + %else + rep ret + %endif +%endmacro + +%define last_branch_adr $$ +%macro AUTO_REP_RET 0 + %if notcpuflag(ssse3) + times ((last_branch_adr-$)>>31)+1 rep ; times 1 iff $ == last_branch_adr. + %endif + ret +%endmacro + +%macro BRANCH_INSTR 0-* + %rep %0 + %macro %1 1-2 %1 + %2 %1 + %if notcpuflag(ssse3) + %%branch_instr equ $ + %xdefine last_branch_adr %%branch_instr + %endif + %endmacro + %rotate 1 + %endrep +%endmacro + +BRANCH_INSTR jz, je, jnz, jne, jl, jle, jnl, jnle, jg, jge, jng, jnge, ja, jae, jna, jnae, jb, jbe, jnb, jnbe, jc, jnc, js, jns, jo, jno, jp, jnp + +%macro TAIL_CALL 2 ; callee, is_nonadjacent + %if has_epilogue + call %1 + RET + %elif %2 + jmp %1 + %endif +%endmacro + +;============================================================================= +; arch-independent part +;============================================================================= + +%assign function_align 16 + +; Begin a function. +; Applies any symbol mangling needed for C linkage, and sets up a define such that +; subsequent uses of the function name automatically refer to the mangled version. +; Appends cpuflags to the function name if cpuflags has been specified. +; The "" empty default parameter is a workaround for nasm, which fails if SUFFIX +; is empty and we call cglobal_internal with just %1 %+ SUFFIX (without %2). +%macro cglobal 1-2+ "" ; name, [PROLOGUE args] + cglobal_internal 1, %1 %+ SUFFIX, %2 +%endmacro +%macro cvisible 1-2+ "" ; name, [PROLOGUE args] + cglobal_internal 0, %1 %+ SUFFIX, %2 +%endmacro +%macro cglobal_internal 2-3+ + %if %1 + ; %xdefine %%FUNCTION_PREFIX private_prefix + %xdefine %%VISIBILITY hidden + %else + ; %xdefine %%FUNCTION_PREFIX public_prefix + %xdefine %%VISIBILITY + %endif + %ifndef cglobaled_%2 + ; %xdefine %2 mangle(%%FUNCTION_PREFIX %+ _ %+ %2) + %xdefine %2.skip_prologue %2 %+ .skip_prologue + CAT_XDEFINE cglobaled_, %2, 1 + %endif + %xdefine current_function %2 + %if FORMAT_ELF + global %2:function %%VISIBILITY + %else + global %2 + %endif + align function_align + %2: + RESET_MM_PERMUTATION ; needed for x86-64, also makes disassembly somewhat nicer + %xdefine rstk rsp ; copy of the original stack pointer, used when greater alignment than the known stack alignment is required + %assign stack_offset 0 ; stack pointer offset relative to the return address + %assign stack_size 0 ; amount of stack space that can be freely used inside a function + %assign stack_size_padded 0 ; total amount of allocated stack space, including space for callee-saved xmm registers on WIN64 and alignment padding + %assign xmm_regs_used 0 ; number of XMM registers requested, used for dealing with callee-saved registers on WIN64 and vzeroupper + %ifnidn %3, "" + PROLOGUE %3 + %endif +%endmacro + +; Create a global symbol from a local label with the correct name mangling and type +%macro cglobal_label 1 + %if FORMAT_ELF + global current_function %+ %1:function hidden + %else + global current_function %+ %1 + %endif + %1: +%endmacro + +%macro cextern 1 + %xdefine %1 mangle(private_prefix %+ _ %+ %1) + CAT_XDEFINE cglobaled_, %1, 1 + extern %1 +%endmacro + +; like cextern, but without the prefix +%macro cextern_naked 1 + %ifdef PREFIX + %xdefine %1 mangle(%1) + %endif + CAT_XDEFINE cglobaled_, %1, 1 + extern %1 +%endmacro + +%macro const 1-2+ + %xdefine %1 mangle(private_prefix %+ _ %+ %1) + %if FORMAT_ELF + global %1:data hidden + %else + global %1 + %endif + ALIGN 32 + %1: %2 +%endmacro + +; This is needed for ELF, otherwise the GNU linker assumes the stack is executable by default. +%if FORMAT_ELF + [SECTION .note.GNU-stack noalloc noexec nowrite progbits] +%endif + +; cpuflags + +%assign cpuflags_mmx (1<<0) +%assign cpuflags_mmx2 (1<<1) | cpuflags_mmx +%assign cpuflags_3dnow (1<<2) | cpuflags_mmx +%assign cpuflags_3dnowext (1<<3) | cpuflags_3dnow +%assign cpuflags_sse (1<<4) | cpuflags_mmx2 +%assign cpuflags_sse2 (1<<5) | cpuflags_sse +%assign cpuflags_sse2slow (1<<6) | cpuflags_sse2 +%assign cpuflags_lzcnt (1<<7) | cpuflags_sse2 +%assign cpuflags_sse3 (1<<8) | cpuflags_sse2 +%assign cpuflags_ssse3 (1<<9) | cpuflags_sse3 +%assign cpuflags_sse4 (1<<10)| cpuflags_ssse3 +%assign cpuflags_sse42 (1<<11)| cpuflags_sse4 +%assign cpuflags_avx (1<<12)| cpuflags_sse42 +%assign cpuflags_xop (1<<13)| cpuflags_avx +%assign cpuflags_fma4 (1<<14)| cpuflags_avx +%assign cpuflags_fma3 (1<<15)| cpuflags_avx +%assign cpuflags_bmi1 (1<<16)| cpuflags_avx | cpuflags_lzcnt +%assign cpuflags_bmi2 (1<<17)| cpuflags_bmi1 +%assign cpuflags_avx2 (1<<18)| cpuflags_fma3 | cpuflags_bmi2 +%assign cpuflags_avx512 (1<<19)| cpuflags_avx2 ; F, CD, BW, DQ, VL + +%assign cpuflags_cache32 (1<<20) +%assign cpuflags_cache64 (1<<21) +%assign cpuflags_aligned (1<<22) ; not a cpu feature, but a function variant +%assign cpuflags_atom (1<<23) + +; Returns a boolean value expressing whether or not the specified cpuflag is enabled. +%define cpuflag(x) (((((cpuflags & (cpuflags_ %+ x)) ^ (cpuflags_ %+ x)) - 1) >> 31) & 1) +%define notcpuflag(x) (cpuflag(x) ^ 1) + +; Takes an arbitrary number of cpuflags from the above list. +; All subsequent functions (up to the next INIT_CPUFLAGS) is built for the specified cpu. +; You shouldn't need to invoke this macro directly, it's a subroutine for INIT_MMX &co. +%macro INIT_CPUFLAGS 0-* + %xdefine SUFFIX + %undef cpuname + %assign cpuflags 0 + + %if %0 >= 1 + %rep %0 + %ifdef cpuname + %xdefine cpuname cpuname %+ _%1 + %else + %xdefine cpuname %1 + %endif + %assign cpuflags cpuflags | cpuflags_%1 + %rotate 1 + %endrep + %xdefine SUFFIX _ %+ cpuname + + %if cpuflag(avx) + %assign avx_enabled 1 + %endif + %if (mmsize == 16 && notcpuflag(sse2)) || (mmsize == 32 && notcpuflag(avx2)) + %define mova movaps + %define movu movups + %define movnta movntps + %endif + %if cpuflag(aligned) + %define movu mova + %elif cpuflag(sse3) && notcpuflag(ssse3) + %define movu lddqu + %endif + %endif + + %if ARCH_X86_64 || cpuflag(sse2) + %ifdef __NASM_VER__ + ALIGNMODE p6 + %else + CPU amdnop + %endif + %else + %ifdef __NASM_VER__ + ALIGNMODE nop + %else + CPU basicnop + %endif + %endif +%endmacro + +; Merge mmx and sse*, and avx* +; m# is a simd register of the currently selected size +; xm# is the corresponding xmm register if mmsize >= 16, otherwise the same as m# +; ym# is the corresponding ymm register if mmsize >= 32, otherwise the same as m# +; zm# is the corresponding zmm register if mmsize >= 64, otherwise the same as m# +; (All 4 remain in sync through SWAP.) + +%macro CAT_XDEFINE 3 + %xdefine %1%2 %3 +%endmacro + +%macro CAT_UNDEF 2 + %undef %1%2 +%endmacro + +%macro DEFINE_MMREGS 1 ; mmtype + %assign %%prev_mmregs 0 + %ifdef num_mmregs + %assign %%prev_mmregs num_mmregs + %endif + + %assign num_mmregs 8 + %if ARCH_X86_64 && mmsize >= 16 + %assign num_mmregs 16 + %if cpuflag(avx512) || mmsize == 64 + %assign num_mmregs 32 + %endif + %endif + + %assign %%i 0 + %rep num_mmregs + CAT_XDEFINE m, %%i, %1 %+ %%i + CAT_XDEFINE nn%1, %%i, %%i + %assign %%i %%i+1 + %endrep + %if %%prev_mmregs > num_mmregs + %rep %%prev_mmregs - num_mmregs + CAT_UNDEF m, %%i + CAT_UNDEF nn %+ mmtype, %%i + %assign %%i %%i+1 + %endrep + %endif + %xdefine mmtype %1 +%endmacro + +; Prefer registers 16-31 over 0-15 to avoid having to use vzeroupper +%macro AVX512_MM_PERMUTATION 0-1 0 ; start_reg + %if ARCH_X86_64 && cpuflag(avx512) + %assign %%i %1 + %rep 16-%1 + %assign %%i_high %%i+16 + SWAP %%i, %%i_high + %assign %%i %%i+1 + %endrep + %endif +%endmacro + +%macro INIT_MMX 0-1+ + %assign avx_enabled 0 + %define RESET_MM_PERMUTATION INIT_MMX %1 + %define mmsize 8 + %define mova movq + %define movu movq + %define movh movd + %define movnta movntq + INIT_CPUFLAGS %1 + DEFINE_MMREGS mm +%endmacro + +%macro INIT_XMM 0-1+ + %assign avx_enabled 0 + %define RESET_MM_PERMUTATION INIT_XMM %1 + %define mmsize 16 + %define mova movdqa + %define movu movdqu + %define movh movq + %define movnta movntdq + INIT_CPUFLAGS %1 + DEFINE_MMREGS xmm + %if WIN64 + ; Swap callee-saved registers with volatile registers + AVX512_MM_PERMUTATION 6 + %endif +%endmacro + +%macro INIT_YMM 0-1+ + %assign avx_enabled 1 + %define RESET_MM_PERMUTATION INIT_YMM %1 + %define mmsize 32 + %define mova movdqa + %define movu movdqu + %undef movh + %define movnta movntdq + INIT_CPUFLAGS %1 + DEFINE_MMREGS ymm + AVX512_MM_PERMUTATION +%endmacro + +%macro INIT_ZMM 0-1+ + %assign avx_enabled 1 + %define RESET_MM_PERMUTATION INIT_ZMM %1 + %define mmsize 64 + %define mova movdqa + %define movu movdqu + %undef movh + %define movnta movntdq + INIT_CPUFLAGS %1 + DEFINE_MMREGS zmm + AVX512_MM_PERMUTATION +%endmacro + +INIT_XMM + +%macro DECLARE_MMCAST 1 + %define mmmm%1 mm%1 + %define mmxmm%1 mm%1 + %define mmymm%1 mm%1 + %define mmzmm%1 mm%1 + %define xmmmm%1 mm%1 + %define xmmxmm%1 xmm%1 + %define xmmymm%1 xmm%1 + %define xmmzmm%1 xmm%1 + %define ymmmm%1 mm%1 + %define ymmxmm%1 xmm%1 + %define ymmymm%1 ymm%1 + %define ymmzmm%1 ymm%1 + %define zmmmm%1 mm%1 + %define zmmxmm%1 xmm%1 + %define zmmymm%1 ymm%1 + %define zmmzmm%1 zmm%1 + %define xm%1 xmm %+ m%1 + %define ym%1 ymm %+ m%1 + %define zm%1 zmm %+ m%1 +%endmacro + +%assign i 0 +%rep 32 + DECLARE_MMCAST i + %assign i i+1 +%endrep + +; I often want to use macros that permute their arguments. e.g. there's no +; efficient way to implement butterfly or transpose or dct without swapping some +; arguments. +; +; I would like to not have to manually keep track of the permutations: +; If I insert a permutation in the middle of a function, it should automatically +; change everything that follows. For more complex macros I may also have multiple +; implementations, e.g. the SSE2 and SSSE3 versions may have different permutations. +; +; Hence these macros. Insert a PERMUTE or some SWAPs at the end of a macro that +; permutes its arguments. It's equivalent to exchanging the contents of the +; registers, except that this way you exchange the register names instead, so it +; doesn't cost any cycles. + +%macro PERMUTE 2-* ; takes a list of pairs to swap + %rep %0/2 + %xdefine %%tmp%2 m%2 + %rotate 2 + %endrep + %rep %0/2 + %xdefine m%1 %%tmp%2 + CAT_XDEFINE nn, m%1, %1 + %rotate 2 + %endrep +%endmacro + +%macro SWAP 2+ ; swaps a single chain (sometimes more concise than pairs) + %ifnum %1 ; SWAP 0, 1, ... + SWAP_INTERNAL_NUM %1, %2 + %else ; SWAP m0, m1, ... + SWAP_INTERNAL_NAME %1, %2 + %endif +%endmacro + +%macro SWAP_INTERNAL_NUM 2-* + %rep %0-1 + %xdefine %%tmp m%1 + %xdefine m%1 m%2 + %xdefine m%2 %%tmp + CAT_XDEFINE nn, m%1, %1 + CAT_XDEFINE nn, m%2, %2 + %rotate 1 + %endrep +%endmacro + +%macro SWAP_INTERNAL_NAME 2-* + %xdefine %%args nn %+ %1 + %rep %0-1 + %xdefine %%args %%args, nn %+ %2 + %rotate 1 + %endrep + SWAP_INTERNAL_NUM %%args +%endmacro + +; If SAVE_MM_PERMUTATION is placed at the end of a function, then any later +; calls to that function will automatically load the permutation, so values can +; be returned in mmregs. +%macro SAVE_MM_PERMUTATION 0-1 + %if %0 + %xdefine %%f %1_m + %else + %xdefine %%f current_function %+ _m + %endif + %assign %%i 0 + %rep num_mmregs + CAT_XDEFINE %%f, %%i, m %+ %%i + %assign %%i %%i+1 + %endrep +%endmacro + +%macro LOAD_MM_PERMUTATION 1 ; name to load from + %ifdef %1_m0 + %assign %%i 0 + %rep num_mmregs + CAT_XDEFINE m, %%i, %1_m %+ %%i + CAT_XDEFINE nn, m %+ %%i, %%i + %assign %%i %%i+1 + %endrep + %endif +%endmacro + +; Append cpuflags to the callee's name iff the appended name is known and the plain name isn't +%macro call 1 + %ifid %1 + call_internal %1 %+ SUFFIX, %1 + %else + call %1 + %endif +%endmacro +%macro call_internal 2 + %xdefine %%i %2 + %ifndef cglobaled_%2 + %ifdef cglobaled_%1 + %xdefine %%i %1 + %endif + %endif + call %%i + LOAD_MM_PERMUTATION %%i +%endmacro + +; Substitutions that reduce instruction size but are functionally equivalent +%macro add 2 + %ifnum %2 + %if %2==128 + sub %1, -128 + %else + add %1, %2 + %endif + %else + add %1, %2 + %endif +%endmacro + +%macro sub 2 + %ifnum %2 + %if %2==128 + add %1, -128 + %else + sub %1, %2 + %endif + %else + sub %1, %2 + %endif +%endmacro + +;============================================================================= +; AVX abstraction layer +;============================================================================= + +%assign i 0 +%rep 32 + %if i < 8 + CAT_XDEFINE sizeofmm, i, 8 + CAT_XDEFINE regnumofmm, i, i + %endif + CAT_XDEFINE sizeofxmm, i, 16 + CAT_XDEFINE sizeofymm, i, 32 + CAT_XDEFINE sizeofzmm, i, 64 + CAT_XDEFINE regnumofxmm, i, i + CAT_XDEFINE regnumofymm, i, i + CAT_XDEFINE regnumofzmm, i, i + %assign i i+1 +%endrep +%undef i + +%macro CHECK_AVX_INSTR_EMU 3-* + %xdefine %%opcode %1 + %xdefine %%dst %2 + %rep %0-2 + %ifidn %%dst, %3 + %error non-avx emulation of ``%%opcode'' is not supported + %endif + %rotate 1 + %endrep +%endmacro + +;%1 == instruction +;%2 == minimal instruction set +;%3 == 1 if float, 0 if int +;%4 == 1 if 4-operand emulation, 0 if 3-operand emulation, 255 otherwise (no emulation) +;%5 == 1 if commutative (i.e. doesn't matter which src arg is which), 0 if not +;%6+: operands +%macro RUN_AVX_INSTR 6-9+ + %ifnum sizeof%7 + %assign __sizeofreg sizeof%7 + %elifnum sizeof%6 + %assign __sizeofreg sizeof%6 + %else + %assign __sizeofreg mmsize + %endif + %assign __emulate_avx 0 + %if avx_enabled && __sizeofreg >= 16 + %xdefine __instr v%1 + %else + %xdefine __instr %1 + %if %0 >= 8+%4 + %assign __emulate_avx 1 + %endif + %endif + %ifnidn %2, fnord + %ifdef cpuname + %if notcpuflag(%2) + %error use of ``%1'' %2 instruction in cpuname function: current_function + %elif cpuflags_%2 < cpuflags_sse && notcpuflag(sse2) && __sizeofreg > 8 + %error use of ``%1'' sse2 instruction in cpuname function: current_function + %endif + %endif + %endif + + %if __emulate_avx + %xdefine __src1 %7 + %xdefine __src2 %8 + %if %5 && %4 == 0 + %ifnidn %6, %7 + %ifidn %6, %8 + %xdefine __src1 %8 + %xdefine __src2 %7 + %elifnnum sizeof%8 + ; 3-operand AVX instructions with a memory arg can only have it in src2, + ; whereas SSE emulation prefers to have it in src1 (i.e. the mov). + ; So, if the instruction is commutative with a memory arg, swap them. + %xdefine __src1 %8 + %xdefine __src2 %7 + %endif + %endif + %endif + %ifnidn %6, __src1 + %if %0 >= 9 + CHECK_AVX_INSTR_EMU {%1 %6, %7, %8, %9}, %6, __src2, %9 + %else + CHECK_AVX_INSTR_EMU {%1 %6, %7, %8}, %6, __src2 + %endif + %if __sizeofreg == 8 + MOVQ %6, __src1 + %elif %3 + MOVAPS %6, __src1 + %else + MOVDQA %6, __src1 + %endif + %endif + %if %0 >= 9 + %1 %6, __src2, %9 + %else + %1 %6, __src2 + %endif + %elif %0 >= 9 + __instr %6, %7, %8, %9 + %elif %0 == 8 + __instr %6, %7, %8 + %elif %0 == 7 + __instr %6, %7 + %else + __instr %6 + %endif +%endmacro + +;%1 == instruction +;%2 == minimal instruction set +;%3 == 1 if float, 0 if int +;%4 == 1 if 4-operand emulation, 0 if 3-operand emulation, 255 otherwise (no emulation) +;%5 == 1 if commutative (i.e. doesn't matter which src arg is which), 0 if not +%macro AVX_INSTR 1-5 fnord, 0, 255, 0 + %macro %1 1-10 fnord, fnord, fnord, fnord, %1, %2, %3, %4, %5 + %ifidn %2, fnord + RUN_AVX_INSTR %6, %7, %8, %9, %10, %1 + %elifidn %3, fnord + RUN_AVX_INSTR %6, %7, %8, %9, %10, %1, %2 + %elifidn %4, fnord + RUN_AVX_INSTR %6, %7, %8, %9, %10, %1, %2, %3 + %elifidn %5, fnord + RUN_AVX_INSTR %6, %7, %8, %9, %10, %1, %2, %3, %4 + %else + RUN_AVX_INSTR %6, %7, %8, %9, %10, %1, %2, %3, %4, %5 + %endif + %endmacro +%endmacro + +; Instructions with both VEX/EVEX and legacy encodings +; Non-destructive instructions are written without parameters +AVX_INSTR addpd, sse2, 1, 0, 1 +AVX_INSTR addps, sse, 1, 0, 1 +AVX_INSTR addsd, sse2, 1, 0, 0 +AVX_INSTR addss, sse, 1, 0, 0 +AVX_INSTR addsubpd, sse3, 1, 0, 0 +AVX_INSTR addsubps, sse3, 1, 0, 0 +AVX_INSTR aesdec, aesni, 0, 0, 0 +AVX_INSTR aesdeclast, aesni, 0, 0, 0 +AVX_INSTR aesenc, aesni, 0, 0, 0 +AVX_INSTR aesenclast, aesni, 0, 0, 0 +AVX_INSTR aesimc, aesni +AVX_INSTR aeskeygenassist, aesni +AVX_INSTR andnpd, sse2, 1, 0, 0 +AVX_INSTR andnps, sse, 1, 0, 0 +AVX_INSTR andpd, sse2, 1, 0, 1 +AVX_INSTR andps, sse, 1, 0, 1 +AVX_INSTR blendpd, sse4, 1, 1, 0 +AVX_INSTR blendps, sse4, 1, 1, 0 +AVX_INSTR blendvpd, sse4 ; can't be emulated +AVX_INSTR blendvps, sse4 ; can't be emulated +AVX_INSTR cmpeqpd, sse2, 1, 0, 1 +AVX_INSTR cmpeqps, sse, 1, 0, 1 +AVX_INSTR cmpeqsd, sse2, 1, 0, 0 +AVX_INSTR cmpeqss, sse, 1, 0, 0 +AVX_INSTR cmplepd, sse2, 1, 0, 0 +AVX_INSTR cmpleps, sse, 1, 0, 0 +AVX_INSTR cmplesd, sse2, 1, 0, 0 +AVX_INSTR cmpless, sse, 1, 0, 0 +AVX_INSTR cmpltpd, sse2, 1, 0, 0 +AVX_INSTR cmpltps, sse, 1, 0, 0 +AVX_INSTR cmpltsd, sse2, 1, 0, 0 +AVX_INSTR cmpltss, sse, 1, 0, 0 +AVX_INSTR cmpneqpd, sse2, 1, 0, 1 +AVX_INSTR cmpneqps, sse, 1, 0, 1 +AVX_INSTR cmpneqsd, sse2, 1, 0, 0 +AVX_INSTR cmpneqss, sse, 1, 0, 0 +AVX_INSTR cmpnlepd, sse2, 1, 0, 0 +AVX_INSTR cmpnleps, sse, 1, 0, 0 +AVX_INSTR cmpnlesd, sse2, 1, 0, 0 +AVX_INSTR cmpnless, sse, 1, 0, 0 +AVX_INSTR cmpnltpd, sse2, 1, 0, 0 +AVX_INSTR cmpnltps, sse, 1, 0, 0 +AVX_INSTR cmpnltsd, sse2, 1, 0, 0 +AVX_INSTR cmpnltss, sse, 1, 0, 0 +AVX_INSTR cmpordpd, sse2 1, 0, 1 +AVX_INSTR cmpordps, sse 1, 0, 1 +AVX_INSTR cmpordsd, sse2 1, 0, 0 +AVX_INSTR cmpordss, sse 1, 0, 0 +AVX_INSTR cmppd, sse2, 1, 1, 0 +AVX_INSTR cmpps, sse, 1, 1, 0 +AVX_INSTR cmpsd, sse2, 1, 1, 0 +AVX_INSTR cmpss, sse, 1, 1, 0 +AVX_INSTR cmpunordpd, sse2, 1, 0, 1 +AVX_INSTR cmpunordps, sse, 1, 0, 1 +AVX_INSTR cmpunordsd, sse2, 1, 0, 0 +AVX_INSTR cmpunordss, sse, 1, 0, 0 +AVX_INSTR comisd, sse2 +AVX_INSTR comiss, sse +AVX_INSTR cvtdq2pd, sse2 +AVX_INSTR cvtdq2ps, sse2 +AVX_INSTR cvtpd2dq, sse2 +AVX_INSTR cvtpd2ps, sse2 +AVX_INSTR cvtps2dq, sse2 +AVX_INSTR cvtps2pd, sse2 +AVX_INSTR cvtsd2si, sse2 +AVX_INSTR cvtsd2ss, sse2, 1, 0, 0 +AVX_INSTR cvtsi2sd, sse2, 1, 0, 0 +AVX_INSTR cvtsi2ss, sse, 1, 0, 0 +AVX_INSTR cvtss2sd, sse2, 1, 0, 0 +AVX_INSTR cvtss2si, sse +AVX_INSTR cvttpd2dq, sse2 +AVX_INSTR cvttps2dq, sse2 +AVX_INSTR cvttsd2si, sse2 +AVX_INSTR cvttss2si, sse +AVX_INSTR divpd, sse2, 1, 0, 0 +AVX_INSTR divps, sse, 1, 0, 0 +AVX_INSTR divsd, sse2, 1, 0, 0 +AVX_INSTR divss, sse, 1, 0, 0 +AVX_INSTR dppd, sse4, 1, 1, 0 +AVX_INSTR dpps, sse4, 1, 1, 0 +AVX_INSTR extractps, sse4 +AVX_INSTR haddpd, sse3, 1, 0, 0 +AVX_INSTR haddps, sse3, 1, 0, 0 +AVX_INSTR hsubpd, sse3, 1, 0, 0 +AVX_INSTR hsubps, sse3, 1, 0, 0 +AVX_INSTR insertps, sse4, 1, 1, 0 +AVX_INSTR lddqu, sse3 +AVX_INSTR ldmxcsr, sse +AVX_INSTR maskmovdqu, sse2 +AVX_INSTR maxpd, sse2, 1, 0, 1 +AVX_INSTR maxps, sse, 1, 0, 1 +AVX_INSTR maxsd, sse2, 1, 0, 0 +AVX_INSTR maxss, sse, 1, 0, 0 +AVX_INSTR minpd, sse2, 1, 0, 1 +AVX_INSTR minps, sse, 1, 0, 1 +AVX_INSTR minsd, sse2, 1, 0, 0 +AVX_INSTR minss, sse, 1, 0, 0 +AVX_INSTR movapd, sse2 +AVX_INSTR movaps, sse +AVX_INSTR movd, mmx +AVX_INSTR movddup, sse3 +AVX_INSTR movdqa, sse2 +AVX_INSTR movdqu, sse2 +AVX_INSTR movhlps, sse, 1, 0, 0 +AVX_INSTR movhpd, sse2, 1, 0, 0 +AVX_INSTR movhps, sse, 1, 0, 0 +AVX_INSTR movlhps, sse, 1, 0, 0 +AVX_INSTR movlpd, sse2, 1, 0, 0 +AVX_INSTR movlps, sse, 1, 0, 0 +AVX_INSTR movmskpd, sse2 +AVX_INSTR movmskps, sse +AVX_INSTR movntdq, sse2 +AVX_INSTR movntdqa, sse4 +AVX_INSTR movntpd, sse2 +AVX_INSTR movntps, sse +AVX_INSTR movq, mmx +AVX_INSTR movsd, sse2, 1, 0, 0 +AVX_INSTR movshdup, sse3 +AVX_INSTR movsldup, sse3 +AVX_INSTR movss, sse, 1, 0, 0 +AVX_INSTR movupd, sse2 +AVX_INSTR movups, sse +AVX_INSTR mpsadbw, sse4, 0, 1, 0 +AVX_INSTR mulpd, sse2, 1, 0, 1 +AVX_INSTR mulps, sse, 1, 0, 1 +AVX_INSTR mulsd, sse2, 1, 0, 0 +AVX_INSTR mulss, sse, 1, 0, 0 +AVX_INSTR orpd, sse2, 1, 0, 1 +AVX_INSTR orps, sse, 1, 0, 1 +AVX_INSTR pabsb, ssse3 +AVX_INSTR pabsd, ssse3 +AVX_INSTR pabsw, ssse3 +AVX_INSTR packsswb, mmx, 0, 0, 0 +AVX_INSTR packssdw, mmx, 0, 0, 0 +AVX_INSTR packuswb, mmx, 0, 0, 0 +AVX_INSTR packusdw, sse4, 0, 0, 0 +AVX_INSTR paddb, mmx, 0, 0, 1 +AVX_INSTR paddw, mmx, 0, 0, 1 +AVX_INSTR paddd, mmx, 0, 0, 1 +AVX_INSTR paddq, sse2, 0, 0, 1 +AVX_INSTR paddsb, mmx, 0, 0, 1 +AVX_INSTR paddsw, mmx, 0, 0, 1 +AVX_INSTR paddusb, mmx, 0, 0, 1 +AVX_INSTR paddusw, mmx, 0, 0, 1 +AVX_INSTR palignr, ssse3, 0, 1, 0 +AVX_INSTR pand, mmx, 0, 0, 1 +AVX_INSTR pandn, mmx, 0, 0, 0 +AVX_INSTR pavgb, mmx2, 0, 0, 1 +AVX_INSTR pavgw, mmx2, 0, 0, 1 +AVX_INSTR pblendvb, sse4 ; can't be emulated +AVX_INSTR pblendw, sse4, 0, 1, 0 +AVX_INSTR pclmulqdq, fnord, 0, 1, 0 +AVX_INSTR pclmulhqhqdq, fnord, 0, 0, 0 +AVX_INSTR pclmulhqlqdq, fnord, 0, 0, 0 +AVX_INSTR pclmullqhqdq, fnord, 0, 0, 0 +AVX_INSTR pclmullqlqdq, fnord, 0, 0, 0 +AVX_INSTR pcmpestri, sse42 +AVX_INSTR pcmpestrm, sse42 +AVX_INSTR pcmpistri, sse42 +AVX_INSTR pcmpistrm, sse42 +AVX_INSTR pcmpeqb, mmx, 0, 0, 1 +AVX_INSTR pcmpeqw, mmx, 0, 0, 1 +AVX_INSTR pcmpeqd, mmx, 0, 0, 1 +AVX_INSTR pcmpeqq, sse4, 0, 0, 1 +AVX_INSTR pcmpgtb, mmx, 0, 0, 0 +AVX_INSTR pcmpgtw, mmx, 0, 0, 0 +AVX_INSTR pcmpgtd, mmx, 0, 0, 0 +AVX_INSTR pcmpgtq, sse42, 0, 0, 0 +AVX_INSTR pextrb, sse4 +AVX_INSTR pextrd, sse4 +AVX_INSTR pextrq, sse4 +AVX_INSTR pextrw, mmx2 +AVX_INSTR phaddw, ssse3, 0, 0, 0 +AVX_INSTR phaddd, ssse3, 0, 0, 0 +AVX_INSTR phaddsw, ssse3, 0, 0, 0 +AVX_INSTR phminposuw, sse4 +AVX_INSTR phsubw, ssse3, 0, 0, 0 +AVX_INSTR phsubd, ssse3, 0, 0, 0 +AVX_INSTR phsubsw, ssse3, 0, 0, 0 +AVX_INSTR pinsrb, sse4, 0, 1, 0 +AVX_INSTR pinsrd, sse4, 0, 1, 0 +AVX_INSTR pinsrq, sse4, 0, 1, 0 +AVX_INSTR pinsrw, mmx2, 0, 1, 0 +AVX_INSTR pmaddwd, mmx, 0, 0, 1 +AVX_INSTR pmaddubsw, ssse3, 0, 0, 0 +AVX_INSTR pmaxsb, sse4, 0, 0, 1 +AVX_INSTR pmaxsw, mmx2, 0, 0, 1 +AVX_INSTR pmaxsd, sse4, 0, 0, 1 +AVX_INSTR pmaxub, mmx2, 0, 0, 1 +AVX_INSTR pmaxuw, sse4, 0, 0, 1 +AVX_INSTR pmaxud, sse4, 0, 0, 1 +AVX_INSTR pminsb, sse4, 0, 0, 1 +AVX_INSTR pminsw, mmx2, 0, 0, 1 +AVX_INSTR pminsd, sse4, 0, 0, 1 +AVX_INSTR pminub, mmx2, 0, 0, 1 +AVX_INSTR pminuw, sse4, 0, 0, 1 +AVX_INSTR pminud, sse4, 0, 0, 1 +AVX_INSTR pmovmskb, mmx2 +AVX_INSTR pmovsxbw, sse4 +AVX_INSTR pmovsxbd, sse4 +AVX_INSTR pmovsxbq, sse4 +AVX_INSTR pmovsxwd, sse4 +AVX_INSTR pmovsxwq, sse4 +AVX_INSTR pmovsxdq, sse4 +AVX_INSTR pmovzxbw, sse4 +AVX_INSTR pmovzxbd, sse4 +AVX_INSTR pmovzxbq, sse4 +AVX_INSTR pmovzxwd, sse4 +AVX_INSTR pmovzxwq, sse4 +AVX_INSTR pmovzxdq, sse4 +AVX_INSTR pmuldq, sse4, 0, 0, 1 +AVX_INSTR pmulhrsw, ssse3, 0, 0, 1 +AVX_INSTR pmulhuw, mmx2, 0, 0, 1 +AVX_INSTR pmulhw, mmx, 0, 0, 1 +AVX_INSTR pmullw, mmx, 0, 0, 1 +AVX_INSTR pmulld, sse4, 0, 0, 1 +AVX_INSTR pmuludq, sse2, 0, 0, 1 +AVX_INSTR por, mmx, 0, 0, 1 +AVX_INSTR psadbw, mmx2, 0, 0, 1 +AVX_INSTR pshufb, ssse3, 0, 0, 0 +AVX_INSTR pshufd, sse2 +AVX_INSTR pshufhw, sse2 +AVX_INSTR pshuflw, sse2 +AVX_INSTR psignb, ssse3, 0, 0, 0 +AVX_INSTR psignw, ssse3, 0, 0, 0 +AVX_INSTR psignd, ssse3, 0, 0, 0 +AVX_INSTR psllw, mmx, 0, 0, 0 +AVX_INSTR pslld, mmx, 0, 0, 0 +AVX_INSTR psllq, mmx, 0, 0, 0 +AVX_INSTR pslldq, sse2, 0, 0, 0 +AVX_INSTR psraw, mmx, 0, 0, 0 +AVX_INSTR psrad, mmx, 0, 0, 0 +AVX_INSTR psrlw, mmx, 0, 0, 0 +AVX_INSTR psrld, mmx, 0, 0, 0 +AVX_INSTR psrlq, mmx, 0, 0, 0 +AVX_INSTR psrldq, sse2, 0, 0, 0 +AVX_INSTR psubb, mmx, 0, 0, 0 +AVX_INSTR psubw, mmx, 0, 0, 0 +AVX_INSTR psubd, mmx, 0, 0, 0 +AVX_INSTR psubq, sse2, 0, 0, 0 +AVX_INSTR psubsb, mmx, 0, 0, 0 +AVX_INSTR psubsw, mmx, 0, 0, 0 +AVX_INSTR psubusb, mmx, 0, 0, 0 +AVX_INSTR psubusw, mmx, 0, 0, 0 +AVX_INSTR ptest, sse4 +AVX_INSTR punpckhbw, mmx, 0, 0, 0 +AVX_INSTR punpckhwd, mmx, 0, 0, 0 +AVX_INSTR punpckhdq, mmx, 0, 0, 0 +AVX_INSTR punpckhqdq, sse2, 0, 0, 0 +AVX_INSTR punpcklbw, mmx, 0, 0, 0 +AVX_INSTR punpcklwd, mmx, 0, 0, 0 +AVX_INSTR punpckldq, mmx, 0, 0, 0 +AVX_INSTR punpcklqdq, sse2, 0, 0, 0 +AVX_INSTR pxor, mmx, 0, 0, 1 +AVX_INSTR rcpps, sse +AVX_INSTR rcpss, sse, 1, 0, 0 +AVX_INSTR roundpd, sse4 +AVX_INSTR roundps, sse4 +AVX_INSTR roundsd, sse4, 1, 1, 0 +AVX_INSTR roundss, sse4, 1, 1, 0 +AVX_INSTR rsqrtps, sse +AVX_INSTR rsqrtss, sse, 1, 0, 0 +AVX_INSTR shufpd, sse2, 1, 1, 0 +AVX_INSTR shufps, sse, 1, 1, 0 +AVX_INSTR sqrtpd, sse2 +AVX_INSTR sqrtps, sse +AVX_INSTR sqrtsd, sse2, 1, 0, 0 +AVX_INSTR sqrtss, sse, 1, 0, 0 +AVX_INSTR stmxcsr, sse +AVX_INSTR subpd, sse2, 1, 0, 0 +AVX_INSTR subps, sse, 1, 0, 0 +AVX_INSTR subsd, sse2, 1, 0, 0 +AVX_INSTR subss, sse, 1, 0, 0 +AVX_INSTR ucomisd, sse2 +AVX_INSTR ucomiss, sse +AVX_INSTR unpckhpd, sse2, 1, 0, 0 +AVX_INSTR unpckhps, sse, 1, 0, 0 +AVX_INSTR unpcklpd, sse2, 1, 0, 0 +AVX_INSTR unpcklps, sse, 1, 0, 0 +AVX_INSTR xorpd, sse2, 1, 0, 1 +AVX_INSTR xorps, sse, 1, 0, 1 + +; 3DNow instructions, for sharing code between AVX, SSE and 3DN +AVX_INSTR pfadd, 3dnow, 1, 0, 1 +AVX_INSTR pfsub, 3dnow, 1, 0, 0 +AVX_INSTR pfmul, 3dnow, 1, 0, 1 + +; base-4 constants for shuffles +%assign i 0 +%rep 256 + %assign j ((i>>6)&3)*1000 + ((i>>4)&3)*100 + ((i>>2)&3)*10 + (i&3) + %if j < 10 + CAT_XDEFINE q000, j, i + %elif j < 100 + CAT_XDEFINE q00, j, i + %elif j < 1000 + CAT_XDEFINE q0, j, i + %else + CAT_XDEFINE q, j, i + %endif + %assign i i+1 +%endrep +%undef i +%undef j + +%macro FMA_INSTR 3 + %macro %1 4-7 %1, %2, %3 + %if cpuflag(xop) + v%5 %1, %2, %3, %4 + %elifnidn %1, %4 + %6 %1, %2, %3 + %7 %1, %4 + %else + %error non-xop emulation of ``%5 %1, %2, %3, %4'' is not supported + %endif + %endmacro +%endmacro + +FMA_INSTR pmacsww, pmullw, paddw +FMA_INSTR pmacsdd, pmulld, paddd ; sse4 emulation +FMA_INSTR pmacsdql, pmuldq, paddq ; sse4 emulation +FMA_INSTR pmadcswd, pmaddwd, paddd + +; Macros for consolidating FMA3 and FMA4 using 4-operand (dst, src1, src2, src3) syntax. +; FMA3 is only possible if dst is the same as one of the src registers. +; Either src2 or src3 can be a memory operand. +%macro FMA4_INSTR 2-* + %push fma4_instr + %xdefine %$prefix %1 + %rep %0 - 1 + %macro %$prefix%2 4-6 %$prefix, %2 + %if notcpuflag(fma3) && notcpuflag(fma4) + %error use of ``%5%6'' fma instruction in cpuname function: current_function + %elif cpuflag(fma4) + v%5%6 %1, %2, %3, %4 + %elifidn %1, %2 + ; If %3 or %4 is a memory operand it needs to be encoded as the last operand. + %ifnum sizeof%3 + v%{5}213%6 %2, %3, %4 + %else + v%{5}132%6 %2, %4, %3 + %endif + %elifidn %1, %3 + v%{5}213%6 %3, %2, %4 + %elifidn %1, %4 + v%{5}231%6 %4, %2, %3 + %else + %error fma3 emulation of ``%5%6 %1, %2, %3, %4'' is not supported + %endif + %endmacro + %rotate 1 + %endrep + %pop +%endmacro + +FMA4_INSTR fmadd, pd, ps, sd, ss +FMA4_INSTR fmaddsub, pd, ps +FMA4_INSTR fmsub, pd, ps, sd, ss +FMA4_INSTR fmsubadd, pd, ps +FMA4_INSTR fnmadd, pd, ps, sd, ss +FMA4_INSTR fnmsub, pd, ps, sd, ss + +; Macros for converting VEX instructions to equivalent EVEX ones. +%macro EVEX_INSTR 2-3 0 ; vex, evex, prefer_evex + %macro %1 2-7 fnord, fnord, %1, %2, %3 + %ifidn %3, fnord + %define %%args %1, %2 + %elifidn %4, fnord + %define %%args %1, %2, %3 + %else + %define %%args %1, %2, %3, %4 + %endif + %assign %%evex_required cpuflag(avx512) & %7 + %ifnum regnumof%1 + %if regnumof%1 >= 16 || sizeof%1 > 32 + %assign %%evex_required 1 + %endif + %endif + %ifnum regnumof%2 + %if regnumof%2 >= 16 || sizeof%2 > 32 + %assign %%evex_required 1 + %endif + %endif + %if %%evex_required + %6 %%args + %else + %5 %%args ; Prefer VEX over EVEX due to shorter instruction length + %endif + %endmacro +%endmacro + +EVEX_INSTR vbroadcastf128, vbroadcastf32x4 +EVEX_INSTR vbroadcasti128, vbroadcasti32x4 +EVEX_INSTR vextractf128, vextractf32x4 +EVEX_INSTR vextracti128, vextracti32x4 +EVEX_INSTR vinsertf128, vinsertf32x4 +EVEX_INSTR vinserti128, vinserti32x4 +EVEX_INSTR vmovdqa, vmovdqa32 +EVEX_INSTR vmovdqu, vmovdqu32 +EVEX_INSTR vpand, vpandd +EVEX_INSTR vpandn, vpandnd +EVEX_INSTR vpor, vpord +EVEX_INSTR vpxor, vpxord +EVEX_INSTR vrcpps, vrcp14ps, 1 ; EVEX versions have higher precision +EVEX_INSTR vrcpss, vrcp14ss, 1 +EVEX_INSTR vrsqrtps, vrsqrt14ps, 1 +EVEX_INSTR vrsqrtss, vrsqrt14ss, 1 diff --git a/Source/Lib/ASM_AVX2/x86util.asm b/Source/Lib/ASM_AVX2/x86util.asm new file mode 100644 index 000000000..36bd3b0e0 --- /dev/null +++ b/Source/Lib/ASM_AVX2/x86util.asm @@ -0,0 +1,911 @@ +;***************************************************************************** +;* x86util.asm: x86 utility macros +;***************************************************************************** +;* Copyright (C) 2003-2013 x264 project +;* Copyright (C) 2013-2017 MulticoreWare, Inc +;* +;* Authors: Holger Lubitz +;* Loren Merritt +;* Min Chen +;* +;* This program is free software; you can redistribute it and/or modify +;* it under the terms of the GNU General Public License as published by +;* the Free Software Foundation; either version 2 of the License, or +;* (at your option) any later version. +;* +;* This program is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;* GNU General Public License for more details. +;* +;* You should have received a copy of the GNU General Public License +;* along with this program; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. +;* +;* This program is also available under a commercial proprietary license. +;* For more information, contact us at license @ x265.com. +;***************************************************************************** + +%define ARCH_X86_64 1 +%define HIGH_BIT_DEPTH 0 +%define BIT_DEPTH 8 + +%assign FENC_STRIDE 64 +%assign FDEC_STRIDE 32 + +%assign SIZEOF_PIXEL 1 +%assign SIZEOF_DCTCOEF 2 +%define pixel byte +%define vpbroadcastdct vpbroadcastw +%define vpbroadcastpix vpbroadcastb +%if HIGH_BIT_DEPTH + %assign SIZEOF_PIXEL 2 + %assign SIZEOF_DCTCOEF 4 + %define pixel word + %define vpbroadcastdct vpbroadcastd + %define vpbroadcastpix vpbroadcastw +%endif + +%assign FENC_STRIDEB SIZEOF_PIXEL*FENC_STRIDE +%assign FDEC_STRIDEB SIZEOF_PIXEL*FDEC_STRIDE + +%assign PIXEL_MAX ((1 << BIT_DEPTH)-1) + +%macro FIX_STRIDES 1-* +%if HIGH_BIT_DEPTH +%rep %0 + add %1, %1 + %rotate 1 +%endrep +%endif +%endmacro + + +%macro SBUTTERFLY 4 +%ifidn %1, dqqq + vperm2i128 m%4, m%2, m%3, q0301 ; punpckh + vinserti128 m%2, m%2, xm%3, 1 ; punpckl +%elif avx_enabled && mmsize >= 16 + punpckh%1 m%4, m%2, m%3 + punpckl%1 m%2, m%3 +%else + mova m%4, m%2 + punpckl%1 m%2, m%3 + punpckh%1 m%4, m%3 +%endif + SWAP %3, %4 +%endmacro + +%macro SBUTTERFLY2 4 + punpckl%1 m%4, m%2, m%3 + punpckh%1 m%2, m%2, m%3 + SWAP %2, %4, %3 +%endmacro + +%macro TRANSPOSE4x4W 5 + SBUTTERFLY wd, %1, %2, %5 + SBUTTERFLY wd, %3, %4, %5 + SBUTTERFLY dq, %1, %3, %5 + SBUTTERFLY dq, %2, %4, %5 + SWAP %2, %3 +%endmacro + +%macro TRANSPOSE2x4x4W 5 + SBUTTERFLY wd, %1, %2, %5 + SBUTTERFLY wd, %3, %4, %5 + SBUTTERFLY dq, %1, %3, %5 + SBUTTERFLY dq, %2, %4, %5 + SBUTTERFLY qdq, %1, %2, %5 + SBUTTERFLY qdq, %3, %4, %5 +%endmacro + +%macro TRANSPOSE4x4D 5 + SBUTTERFLY dq, %1, %2, %5 + SBUTTERFLY dq, %3, %4, %5 + SBUTTERFLY qdq, %1, %3, %5 + SBUTTERFLY qdq, %2, %4, %5 + SWAP %2, %3 +%endmacro + +%macro TRANSPOSE8x8W 9-11 +%if ARCH_X86_64 + SBUTTERFLY wd, %1, %2, %9 + SBUTTERFLY wd, %3, %4, %9 + SBUTTERFLY wd, %5, %6, %9 + SBUTTERFLY wd, %7, %8, %9 + SBUTTERFLY dq, %1, %3, %9 + SBUTTERFLY dq, %2, %4, %9 + SBUTTERFLY dq, %5, %7, %9 + SBUTTERFLY dq, %6, %8, %9 + SBUTTERFLY qdq, %1, %5, %9 + SBUTTERFLY qdq, %2, %6, %9 + SBUTTERFLY qdq, %3, %7, %9 + SBUTTERFLY qdq, %4, %8, %9 + SWAP %2, %5 + SWAP %4, %7 +%else +; in: m0..m7, unless %11 in which case m6 is in %9 +; out: m0..m7, unless %11 in which case m4 is in %10 +; spills into %9 and %10 +%if %0<11 + movdqa %9, m%7 +%endif + SBUTTERFLY wd, %1, %2, %7 + movdqa %10, m%2 + movdqa m%7, %9 + SBUTTERFLY wd, %3, %4, %2 + SBUTTERFLY wd, %5, %6, %2 + SBUTTERFLY wd, %7, %8, %2 + SBUTTERFLY dq, %1, %3, %2 + movdqa %9, m%3 + movdqa m%2, %10 + SBUTTERFLY dq, %2, %4, %3 + SBUTTERFLY dq, %5, %7, %3 + SBUTTERFLY dq, %6, %8, %3 + SBUTTERFLY qdq, %1, %5, %3 + SBUTTERFLY qdq, %2, %6, %3 + movdqa %10, m%2 + movdqa m%3, %9 + SBUTTERFLY qdq, %3, %7, %2 + SBUTTERFLY qdq, %4, %8, %2 + SWAP %2, %5 + SWAP %4, %7 +%if %0<11 + movdqa m%5, %10 +%endif +%endif +%endmacro + +%macro WIDEN_SXWD 2 + punpckhwd m%2, m%1 + psrad m%2, 16 +%if cpuflag(sse4) + pmovsxwd m%1, m%1 +%else + punpcklwd m%1, m%1 + psrad m%1, 16 +%endif +%endmacro + +%macro ABSW 2-3 ; dst, src, tmp (tmp used only if dst==src) +%if cpuflag(ssse3) + pabsw %1, %2 +%elifidn %3, sign ; version for pairing with PSIGNW: modifies src + pxor %1, %1 + pcmpgtw %1, %2 + pxor %2, %1 + psubw %2, %1 + SWAP %1, %2 +%elifidn %1, %2 + pxor %3, %3 + psubw %3, %1 + pmaxsw %1, %3 +%elifid %2 + pxor %1, %1 + psubw %1, %2 + pmaxsw %1, %2 +%elif %0 == 2 + pxor %1, %1 + psubw %1, %2 + pmaxsw %1, %2 +%else + mova %1, %2 + pxor %3, %3 + psubw %3, %1 + pmaxsw %1, %3 +%endif +%endmacro + +%macro ABSW2 6 ; dst1, dst2, src1, src2, tmp, tmp +%if cpuflag(ssse3) + pabsw %1, %3 + pabsw %2, %4 +%elifidn %1, %3 + pxor %5, %5 + pxor %6, %6 + psubw %5, %1 + psubw %6, %2 + pmaxsw %1, %5 + pmaxsw %2, %6 +%else + pxor %1, %1 + pxor %2, %2 + psubw %1, %3 + psubw %2, %4 + pmaxsw %1, %3 + pmaxsw %2, %4 +%endif +%endmacro + +%macro ABSB 2 +%if cpuflag(ssse3) + pabsb %1, %1 +%else + pxor %2, %2 + psubb %2, %1 + pminub %1, %2 +%endif +%endmacro + +%macro ABSD 2-3 +%if cpuflag(ssse3) + pabsd %1, %2 +%else + %define %%s %2 +%if %0 == 3 + mova %3, %2 + %define %%s %3 +%endif + pxor %1, %1 + pcmpgtd %1, %%s + pxor %%s, %1 + psubd %%s, %1 + SWAP %1, %%s +%endif +%endmacro + +%macro PSIGN 3-4 +%if cpuflag(ssse3) && %0 == 4 + psign%1 %2, %3, %4 +%elif cpuflag(ssse3) + psign%1 %2, %3 +%elif %0 == 4 + pxor %2, %3, %4 + psub%1 %2, %4 +%else + pxor %2, %3 + psub%1 %2, %3 +%endif +%endmacro + +%define PSIGNW PSIGN w, +%define PSIGND PSIGN d, + +%macro SPLATB_LOAD 3 +%if cpuflag(ssse3) + movd %1, [%2-3] + pshufb %1, %3 +%else + movd %1, [%2-3] ;to avoid crossing a cacheline + punpcklbw %1, %1 + SPLATW %1, %1, 3 +%endif +%endmacro + +%imacro SPLATW 2-3 0 +%if cpuflag(avx2) && %3 == 0 + vpbroadcastw %1, %2 +%else + PSHUFLW %1, %2, (%3)*q1111 +%if mmsize == 16 + punpcklqdq %1, %1 +%endif +%endif +%endmacro + +%imacro SPLATD 2-3 0 +%if mmsize == 16 + pshufd %1, %2, (%3)*q1111 +%else + pshufw %1, %2, (%3)*q0101 + ((%3)+1)*q1010 +%endif +%endmacro + +%macro CLIPW 3 ;(dst, min, max) + pmaxsw %1, %2 + pminsw %1, %3 +%endmacro + +%macro CLIPW2 4 ;(dst0, dst1, min, max) + pmaxsw %1, %3 + pmaxsw %2, %3 + pminsw %1, %4 + pminsw %2, %4 +%endmacro + +%macro MOVHL 2 ; dst, src +%ifidn %1, %2 + punpckhqdq %1, %2 +%elif cpuflag(avx) + punpckhqdq %1, %2, %2 +%elif cpuflag(sse4) + pshufd %1, %2, q3232 ; pshufd is slow on some older CPUs, so only use it on more modern ones +%else + movhlps %1, %2 ; may cause an int/float domain transition and has a dependency on dst +%endif +%endmacro + +%macro HADDD 2 ; sum junk +%if sizeof%1 >= 64 + vextracti32x8 ymm%2, zmm%1, 1 + paddd ymm%1, ymm%2 +%endif +%if sizeof%1 >= 32 + vextracti128 xmm%2, ymm%1, 1 + paddd xmm%1, xmm%2 +%endif +%if sizeof%1 >= 16 + MOVHL xmm%2, xmm%1 + paddd xmm%1, xmm%2 +%endif +%if cpuflag(xop) && sizeof%1 == 16 + vphadddq xmm%1, xmm%1 +%endif +%if notcpuflag(xop) + PSHUFLW xmm%2, xmm%1, q1032 + paddd xmm%1, xmm%2 +%endif +%endmacro + +%macro HADDW 2 ; reg, tmp +%if cpuflag(xop) && sizeof%1 == 16 + vphaddwq %1, %1 + MOVHL %2, %1 + paddd %1, %2 +%else + pmaddwd %1, [pw_1] + HADDD %1, %2 +%endif +%endmacro + +%macro HADDUWD 2 +%if cpuflag(xop) && sizeof%1 == 16 + vphadduwd %1, %1 +%else + psrld %2, %1, 16 + pslld %1, 16 + psrld %1, 16 + paddd %1, %2 +%endif +%endmacro + +%macro HADDUW 2 +%if cpuflag(xop) && sizeof%1 == 16 + vphadduwq %1, %1 + MOVHL %2, %1 + paddd %1, %2 +%else + HADDUWD %1, %2 + HADDD %1, %2 +%endif +%endmacro + +%macro PALIGNR 4-5 ; [dst,] src1, src2, imm, tmp +; AVX2 version uses a precalculated extra input that +; can be re-used across calls +%if sizeof%1==32 + ; %3 = abcdefgh ijklmnop (lower address) + ; %2 = ABCDEFGH IJKLMNOP (higher address) + vperm2i128 %4, %1, %2, q0003 ; %4 = ijklmnop ABCDEFGH +%if %3 < 16 + palignr %1, %4, %2, %3 ; %1 = bcdefghi jklmnopA +%else + palignr %1, %2, %4, %3-16 ; %1 = pABCDEFG HIJKLMNO +%endif +%elif cpuflag(ssse3) + %if %0==5 + palignr %1, %2, %3, %4 + %else + palignr %1, %2, %3 + %endif +%else + %define %%dst %1 + %if %0==5 + %ifnidn %1, %2 + mova %%dst, %2 + %endif + %rotate 1 + %endif + %ifnidn %4, %2 + mova %4, %2 + %endif + %if mmsize==8 + psllq %%dst, (8-%3)*8 + psrlq %4, %3*8 + %else + pslldq %%dst, 16-%3 + psrldq %4, %3 + %endif + por %%dst, %4 +%endif +%endmacro + +%macro PSHUFLW 1+ + %if mmsize == 8 + pshufw %1 + %else + pshuflw %1 + %endif +%endmacro + +; shift a mmxreg by n bytes, or a xmmreg by 2*n bytes +; values shifted in are undefined +; faster if dst==src +%define PSLLPIX PSXLPIX l, -1, ;dst, src, shift +%define PSRLPIX PSXLPIX r, 1, ;dst, src, shift +%macro PSXLPIX 5 + %if mmsize == 8 + %if %5&1 + ps%1lq %3, %4, %5*8 + %else + pshufw %3, %4, (q3210<<8>>(8+%2*%5))&0xff + %endif + %else + ps%1ldq %3, %4, %5*2 + %endif +%endmacro + +%macro DEINTB 5 ; mask, reg1, mask, reg2, optional src to fill masks from +%ifnum %5 + pand m%3, m%5, m%4 ; src .. y6 .. y4 + pand m%1, m%5, m%2 ; dst .. y6 .. y4 +%else + mova m%1, %5 + pand m%3, m%1, m%4 ; src .. y6 .. y4 + pand m%1, m%1, m%2 ; dst .. y6 .. y4 +%endif + psrlw m%2, 8 ; dst .. y7 .. y5 + psrlw m%4, 8 ; src .. y7 .. y5 +%endmacro + +%macro SUMSUB_BA 3-4 +%if %0==3 + padd%1 m%2, m%3 + padd%1 m%3, m%3 + psub%1 m%3, m%2 +%elif avx_enabled + padd%1 m%4, m%2, m%3 + psub%1 m%3, m%2 + SWAP %2, %4 +%else + mova m%4, m%2 + padd%1 m%2, m%3 + psub%1 m%3, m%4 +%endif +%endmacro + +%macro SUMSUB_BADC 5-6 +%if %0==6 + SUMSUB_BA %1, %2, %3, %6 + SUMSUB_BA %1, %4, %5, %6 +%else + padd%1 m%2, m%3 + padd%1 m%4, m%5 + padd%1 m%3, m%3 + padd%1 m%5, m%5 + psub%1 m%3, m%2 + psub%1 m%5, m%4 +%endif +%endmacro + +%macro HADAMARD4_V 4+ + SUMSUB_BADC w, %1, %2, %3, %4 + SUMSUB_BADC w, %1, %3, %2, %4 +%endmacro + +%macro HADAMARD8_V 8+ + SUMSUB_BADC w, %1, %2, %3, %4 + SUMSUB_BADC w, %5, %6, %7, %8 + SUMSUB_BADC w, %1, %3, %2, %4 + SUMSUB_BADC w, %5, %7, %6, %8 + SUMSUB_BADC w, %1, %5, %2, %6 + SUMSUB_BADC w, %3, %7, %4, %8 +%endmacro + +%macro TRANS_SSE2 5-6 +; TRANSPOSE2x2 +; %1: transpose width (d/q) - use SBUTTERFLY qdq for dq +; %2: ord/unord (for compat with sse4, unused) +; %3/%4: source regs +; %5/%6: tmp regs +%ifidn %1, d +%define mask [mask_10] +%define shift 16 +%elifidn %1, q +%define mask [mask_1100] +%define shift 32 +%endif +%if %0==6 ; less dependency if we have two tmp + mova m%5, mask ; ff00 + mova m%6, m%4 ; x5x4 + psll%1 m%4, shift ; x4.. + pand m%6, m%5 ; x5.. + pandn m%5, m%3 ; ..x0 + psrl%1 m%3, shift ; ..x1 + por m%4, m%5 ; x4x0 + por m%3, m%6 ; x5x1 +%else ; more dependency, one insn less. sometimes faster, sometimes not + mova m%5, m%4 ; x5x4 + psll%1 m%4, shift ; x4.. + pxor m%4, m%3 ; (x4^x1)x0 + pand m%4, mask ; (x4^x1).. + pxor m%3, m%4 ; x4x0 + psrl%1 m%4, shift ; ..(x1^x4) + pxor m%5, m%4 ; x5x1 + SWAP %4, %3, %5 +%endif +%endmacro + +%macro TRANS_SSE4 5-6 ; see above +%ifidn %1, d +%ifidn %2, ord + psrl%1 m%5, m%3, 16 + pblendw m%5, m%4, q2222 + psll%1 m%4, 16 + pblendw m%4, m%3, q1111 + SWAP %3, %5 +%else +%if avx_enabled + pblendw m%5, m%3, m%4, q2222 + SWAP %3, %5 +%else + mova m%5, m%3 + pblendw m%3, m%4, q2222 +%endif + psll%1 m%4, 16 + psrl%1 m%5, 16 + por m%4, m%5 +%endif +%elifidn %1, q + shufps m%5, m%3, m%4, q3131 + shufps m%3, m%3, m%4, q2020 + SWAP %4, %5 +%endif +%endmacro + +%macro TRANS_XOP 5-6 +%ifidn %1, d + vpperm m%5, m%3, m%4, [transd_shuf1] + vpperm m%3, m%3, m%4, [transd_shuf2] +%elifidn %1, q + shufps m%5, m%3, m%4, q3131 + shufps m%3, m%4, q2020 +%endif + SWAP %4, %5 +%endmacro + +%macro HADAMARD 5-6 +; %1=distance in words (0 for vertical pass, 1/2/4 for horizontal passes) +; %2=sumsub/max/amax (sum and diff / maximum / maximum of absolutes) +; %3/%4: regs +; %5(%6): tmpregs +%if %1!=0 ; have to reorder stuff for horizontal op + %ifidn %2, sumsub + %define ORDER ord + ; sumsub needs order because a-b != b-a unless a=b + %else + %define ORDER unord + ; if we just max, order doesn't matter (allows pblendw+or in sse4) + %endif + %if %1==1 + TRANS d, ORDER, %3, %4, %5, %6 + %elif %1==2 + %if mmsize==8 + SBUTTERFLY dq, %3, %4, %5 + %else + TRANS q, ORDER, %3, %4, %5, %6 + %endif + %elif %1==4 + SBUTTERFLY qdq, %3, %4, %5 + %elif %1==8 + SBUTTERFLY dqqq, %3, %4, %5 + %endif +%endif +%ifidn %2, sumsub + SUMSUB_BA w, %3, %4, %5 +%else + %ifidn %2, amax + %if %0==6 + ABSW2 m%3, m%4, m%3, m%4, m%5, m%6 + %else + ABSW m%3, m%3, m%5 + ABSW m%4, m%4, m%5 + %endif + %endif + pmaxsw m%3, m%4 +%endif +%endmacro + + +%macro HADAMARD2_2D 6-7 sumsub + HADAMARD 0, sumsub, %1, %2, %5 + HADAMARD 0, sumsub, %3, %4, %5 + SBUTTERFLY %6, %1, %2, %5 +%ifnum %7 + HADAMARD 0, amax, %1, %2, %5, %7 +%else + HADAMARD 0, %7, %1, %2, %5 +%endif + SBUTTERFLY %6, %3, %4, %5 +%ifnum %7 + HADAMARD 0, amax, %3, %4, %5, %7 +%else + HADAMARD 0, %7, %3, %4, %5 +%endif +%endmacro + +%macro HADAMARD4_2D 5-6 sumsub + HADAMARD2_2D %1, %2, %3, %4, %5, wd + HADAMARD2_2D %1, %3, %2, %4, %5, dq, %6 + SWAP %2, %3 +%endmacro + +%macro HADAMARD4_2D_SSE 5-6 sumsub + HADAMARD 0, sumsub, %1, %2, %5 ; 1st V row 0 + 1 + HADAMARD 0, sumsub, %3, %4, %5 ; 1st V row 2 + 3 + SBUTTERFLY wd, %1, %2, %5 ; %1: m0 1+0 %2: m1 1+0 + SBUTTERFLY wd, %3, %4, %5 ; %3: m0 3+2 %4: m1 3+2 + HADAMARD2_2D %1, %3, %2, %4, %5, dq + SBUTTERFLY qdq, %1, %2, %5 + HADAMARD 0, %6, %1, %2, %5 ; 2nd H m1/m0 row 0+1 + SBUTTERFLY qdq, %3, %4, %5 + HADAMARD 0, %6, %3, %4, %5 ; 2nd H m1/m0 row 2+3 +%endmacro + +%macro HADAMARD8_2D 9-10 sumsub + HADAMARD2_2D %1, %2, %3, %4, %9, wd + HADAMARD2_2D %5, %6, %7, %8, %9, wd + HADAMARD2_2D %1, %3, %2, %4, %9, dq + HADAMARD2_2D %5, %7, %6, %8, %9, dq + HADAMARD2_2D %1, %5, %3, %7, %9, qdq, %10 + HADAMARD2_2D %2, %6, %4, %8, %9, qdq, %10 +%ifnidn %10, amax + SWAP %2, %5 + SWAP %4, %7 +%endif +%endmacro + +; doesn't include the "pmaddubsw hmul_8p" pass +%macro HADAMARD8_2D_HMUL 10 + HADAMARD4_V %1, %2, %3, %4, %9 + HADAMARD4_V %5, %6, %7, %8, %9 + SUMSUB_BADC w, %1, %5, %2, %6, %9 + HADAMARD 2, sumsub, %1, %5, %9, %10 + HADAMARD 2, sumsub, %2, %6, %9, %10 + SUMSUB_BADC w, %3, %7, %4, %8, %9 + HADAMARD 2, sumsub, %3, %7, %9, %10 + HADAMARD 2, sumsub, %4, %8, %9, %10 + HADAMARD 1, amax, %1, %5, %9, %10 + HADAMARD 1, amax, %2, %6, %9, %5 + HADAMARD 1, amax, %3, %7, %9, %5 + HADAMARD 1, amax, %4, %8, %9, %5 +%endmacro + +%macro SUMSUB2_AB 4 +%if cpuflag(xop) + pmacs%1%1 m%4, m%3, [p%1_m2], m%2 + pmacs%1%1 m%2, m%2, [p%1_2], m%3 +%elifnum %3 + psub%1 m%4, m%2, m%3 + psub%1 m%4, m%3 + padd%1 m%2, m%2 + padd%1 m%2, m%3 +%else + mova m%4, m%2 + padd%1 m%2, m%2 + padd%1 m%2, %3 + psub%1 m%4, %3 + psub%1 m%4, %3 +%endif +%endmacro + +%macro SUMSUBD2_AB 5 +%ifnum %4 + psra%1 m%5, m%2, 1 ; %3: %3>>1 + psra%1 m%4, m%3, 1 ; %2: %2>>1 + padd%1 m%4, m%2 ; %3: %3>>1+%2 + psub%1 m%5, m%3 ; %2: %2>>1-%3 + SWAP %2, %5 + SWAP %3, %4 +%else + mova %5, m%2 + mova %4, m%3 + psra%1 m%3, 1 ; %3: %3>>1 + psra%1 m%2, 1 ; %2: %2>>1 + padd%1 m%3, %5 ; %3: %3>>1+%2 + psub%1 m%2, %4 ; %2: %2>>1-%3 +%endif +%endmacro + +%macro DCT4_1D 5 +%ifnum %5 + SUMSUB_BADC w, %4, %1, %3, %2, %5 + SUMSUB_BA w, %3, %4, %5 + SUMSUB2_AB w, %1, %2, %5 + SWAP %1, %3, %4, %5, %2 +%else + SUMSUB_BADC w, %4, %1, %3, %2 + SUMSUB_BA w, %3, %4 + mova [%5], m%2 + SUMSUB2_AB w, %1, [%5], %2 + SWAP %1, %3, %4, %2 +%endif +%endmacro + +%macro IDCT4_1D 6-7 +%ifnum %6 + SUMSUBD2_AB %1, %3, %5, %7, %6 + ; %3: %3>>1-%5 %5: %3+%5>>1 + SUMSUB_BA %1, %4, %2, %7 + ; %4: %2+%4 %2: %2-%4 + SUMSUB_BADC %1, %5, %4, %3, %2, %7 + ; %5: %2+%4 + (%3+%5>>1) + ; %4: %2+%4 - (%3+%5>>1) + ; %3: %2-%4 + (%3>>1-%5) + ; %2: %2-%4 - (%3>>1-%5) +%else +%ifidn %1, w + SUMSUBD2_AB %1, %3, %5, [%6], [%6+16] +%else + SUMSUBD2_AB %1, %3, %5, [%6], [%6+32] +%endif + SUMSUB_BA %1, %4, %2 + SUMSUB_BADC %1, %5, %4, %3, %2 +%endif + SWAP %2, %5, %4 + ; %2: %2+%4 + (%3+%5>>1) row0 + ; %3: %2-%4 + (%3>>1-%5) row1 + ; %4: %2-%4 - (%3>>1-%5) row2 + ; %5: %2+%4 - (%3+%5>>1) row3 +%endmacro + + +%macro LOAD_DIFF 5-6 1 +%if HIGH_BIT_DEPTH +%if %6 ; %5 aligned? + mova %1, %4 + psubw %1, %5 +%elif cpuflag(avx) + movu %1, %4 + psubw %1, %5 +%else + movu %1, %4 + movu %2, %5 + psubw %1, %2 +%endif +%else ; !HIGH_BIT_DEPTH + movh %1, %4 + movh %2, %5 +%ifidn %3, none + punpcklbw %1, %2 + punpcklbw %2, %2 +%else + punpcklbw %1, %3 + punpcklbw %2, %3 +%endif + psubw %1, %2 +%endif ; HIGH_BIT_DEPTH +%endmacro + +%macro LOAD_DIFF8x4 8 ; 4x dst, 1x tmp, 1x mul, 2x ptr +%if BIT_DEPTH == 8 && cpuflag(ssse3) + movh m%2, [%8+%1*FDEC_STRIDE] + movh m%1, [%7+%1*FENC_STRIDE] + punpcklbw m%1, m%2 + movh m%3, [%8+%2*FDEC_STRIDE] + movh m%2, [%7+%2*FENC_STRIDE] + punpcklbw m%2, m%3 + movh m%4, [%8+%3*FDEC_STRIDE] + movh m%3, [%7+%3*FENC_STRIDE] + punpcklbw m%3, m%4 + movh m%5, [%8+%4*FDEC_STRIDE] + movh m%4, [%7+%4*FENC_STRIDE] + punpcklbw m%4, m%5 + pmaddubsw m%1, m%6 + pmaddubsw m%2, m%6 + pmaddubsw m%3, m%6 + pmaddubsw m%4, m%6 +%else + LOAD_DIFF m%1, m%5, m%6, [%7+%1*FENC_STRIDEB], [%8+%1*FDEC_STRIDEB] + LOAD_DIFF m%2, m%5, m%6, [%7+%2*FENC_STRIDEB], [%8+%2*FDEC_STRIDEB] + LOAD_DIFF m%3, m%5, m%6, [%7+%3*FENC_STRIDEB], [%8+%3*FDEC_STRIDEB] + LOAD_DIFF m%4, m%5, m%6, [%7+%4*FENC_STRIDEB], [%8+%4*FDEC_STRIDEB] +%endif +%endmacro + +%macro STORE_DCT 6 + movq [%5+%6+ 0], m%1 + movq [%5+%6+ 8], m%2 + movq [%5+%6+16], m%3 + movq [%5+%6+24], m%4 + movhps [%5+%6+32], m%1 + movhps [%5+%6+40], m%2 + movhps [%5+%6+48], m%3 + movhps [%5+%6+56], m%4 +%endmacro + +%macro STORE_IDCT 4 + movhps [r0-4*FDEC_STRIDE], %1 + movh [r0-3*FDEC_STRIDE], %1 + movhps [r0-2*FDEC_STRIDE], %2 + movh [r0-1*FDEC_STRIDE], %2 + movhps [r0+0*FDEC_STRIDE], %3 + movh [r0+1*FDEC_STRIDE], %3 + movhps [r0+2*FDEC_STRIDE], %4 + movh [r0+3*FDEC_STRIDE], %4 +%endmacro + +%macro LOAD_DIFF_8x4P 7-11 r0,r2,0,1 ; 4x dest, 2x temp, 2x pointer, increment, aligned? + LOAD_DIFF m%1, m%5, m%7, [%8], [%9], %11 + LOAD_DIFF m%2, m%6, m%7, [%8+r1], [%9+r3], %11 + LOAD_DIFF m%3, m%5, m%7, [%8+2*r1], [%9+2*r3], %11 + LOAD_DIFF m%4, m%6, m%7, [%8+r4], [%9+r5], %11 +%if %10 + lea %8, [%8+4*r1] + lea %9, [%9+4*r3] +%endif +%endmacro + +; 2xdst, 2xtmp, 2xsrcrow +%macro LOAD_DIFF16x2_AVX2 6 + pmovzxbw m%1, [r1+%5*FENC_STRIDE] + pmovzxbw m%2, [r1+%6*FENC_STRIDE] + pmovzxbw m%3, [r2+(%5-4)*FDEC_STRIDE] + pmovzxbw m%4, [r2+(%6-4)*FDEC_STRIDE] + psubw m%1, m%3 + psubw m%2, m%4 +%endmacro + +%macro DIFFx2 6-7 + movh %3, %5 + punpcklbw %3, %4 + psraw %1, 6 + paddsw %1, %3 + movh %3, %6 + punpcklbw %3, %4 + psraw %2, 6 + paddsw %2, %3 + packuswb %2, %1 +%endmacro + +; (high depth) in: %1, %2, min to clip, max to clip, mem128 +; in: %1, tmp, %3, mem64 +%macro STORE_DIFF 4-5 +%if HIGH_BIT_DEPTH + psrad %1, 6 + psrad %2, 6 + packssdw %1, %2 + paddw %1, %5 + CLIPW %1, %3, %4 + mova %5, %1 +%else + movh %2, %4 + punpcklbw %2, %3 + psraw %1, 6 + paddsw %1, %2 + packuswb %1, %1 + movh %4, %1 +%endif +%endmacro + +%macro SHUFFLE_MASK_W 8 + %rep 8 + %if %1>=0x80 + db %1, %1 + %else + db %1*2 + db %1*2+1 + %endif + %rotate 1 + %endrep +%endmacro + +; instruction, accum, input, iteration (zero to swap, nonzero to add) +%macro ACCUM 4 +%if %4 + %1 m%2, m%3 +%else + SWAP %2, %3 +%endif +%endmacro + +; IACA support +%macro IACA_START 0 + mov ebx, 111 + db 0x64, 0x67, 0x90 +%endmacro + +%macro IACA_END 0 + mov ebx, 222 + db 0x64, 0x67, 0x90 +%endmacro diff --git a/Source/Lib/C_DEFAULT/CMakeLists.txt b/Source/Lib/C_DEFAULT/CMakeLists.txt index f32811a65..9b199ca79 100644 --- a/Source/Lib/C_DEFAULT/CMakeLists.txt +++ b/Source/Lib/C_DEFAULT/CMakeLists.txt @@ -19,6 +19,7 @@ add_library(C_DEFAULT STATIC EbIntraPrediction_C.h EbMcp_C.h EbMeSadCalculation_C.h + EbMeSatdCalculation_C.h EbPackUnPack_C.h EbPictureOperators_C.h EbSampleAdaptiveOffset_C.h @@ -30,7 +31,8 @@ add_library(C_DEFAULT STATIC EbIntraPrediction_C.c EbMcp_C.c EbMeSadCalculation_C.c - EbPackUnPack_C.c + EbMeSatdCalculation_C.c + EbPackUnPack_C.c EbPictureOperators_C.c EbSampleAdaptiveOffset_C.c EbTransforms_C.c diff --git a/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c b/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c new file mode 100644 index 000000000..75aa5f83a --- /dev/null +++ b/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c @@ -0,0 +1,80 @@ +/* +* Copyright(c) 2018 Intel Corporation +* SPDX - License - Identifier: BSD - 2 - Clause - Patent +*/ + +#include "EbMeSatdCalculation_C.h" + +#define BITS_PER_SUM (8 * sizeof(EB_U16)) + +inline EB_U32 abs2(EB_U32 a) +{ + EB_U32 s = ((a >> (BITS_PER_SUM - 1)) & (((EB_U32)1 << BITS_PER_SUM) + 1)) * ((EB_U32)-1); + + return (a + s) ^ s; +} + +#define HADAMARD4(d0, d1, d2, d3, s0, s1, s2, s3) { \ + EB_U32 t0 = s0 + s1; \ + EB_U32 t1 = s0 - s1; \ + EB_U32 t2 = s2 + s3; \ + EB_U32 t3 = s2 - s3; \ + d0 = t0 + t2; \ + d2 = t0 - t2; \ + d1 = t1 + t3; \ + d3 = t1 - t3; \ +} + +/******************************************* +Calcualte SATD for 8x4 sublcoks. +*******************************************/ +EB_U32 SatdCalculation_8x4( + EB_U8 *src, + EB_U32 srcStride, + EB_U8 *ref, + EB_U32 refStride) +{ + EB_U32 tmp[4][4]; + EB_U32 a0, a1, a2, a3; + EB_U32 sum = 0; + + for (EB_U64 i = 0; i < 4; i++, src += srcStride, ref += refStride) + { + a0 = (src[0] - ref[0]) + ((EB_U32)(src[4] - ref[4]) << BITS_PER_SUM); + a1 = (src[1] - ref[1]) + ((EB_U32)(src[5] - ref[5]) << BITS_PER_SUM); + a2 = (src[2] - ref[2]) + ((EB_U32)(src[6] - ref[6]) << BITS_PER_SUM); + a3 = (src[3] - ref[3]) + ((EB_U32)(src[7] - ref[7]) << BITS_PER_SUM); + HADAMARD4(tmp[i][0], tmp[i][1], tmp[i][2], tmp[i][3], a0, a1, a2, a3); + } + + for (EB_U64 i = 0; i < 4; i++) + { + HADAMARD4(a0, a1, a2, a3, tmp[0][i], tmp[1][i], tmp[2][i], tmp[3][i]); + sum += abs2(a0) + abs2(a1) + abs2(a2) + abs2(a3); + } + + return (((EB_U16)sum) + (sum >> BITS_PER_SUM)) >> 1; +} + +/******************************************* +Calcualte SATD for 16x16 sublcoks. +*******************************************/ +EB_U32 SatdCalculation_16x16( + EB_U8 *src, + EB_U32 srcStride, + EB_U8 *ref, + EB_U32 refStride) +{ + EB_U32 satd = 0; + + for (EB_U64 row = 0; row < 16; row += 4) + { + for (EB_U64 col = 0; col < 16; col += 8) + { + satd += SatdCalculation_8x4(src + row * srcStride + col, srcStride, + ref + row * refStride + col, refStride); + } + } + + return satd; +} diff --git a/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.h b/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.h new file mode 100644 index 000000000..0282c2434 --- /dev/null +++ b/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.h @@ -0,0 +1,23 @@ +/* +* Copyright(c) 2018 Intel Corporation +* SPDX - License - Identifier: BSD - 2 - Clause - Patent +*/ + +#ifndef EbMeSatdCalculation_C_h +#define EbMeSatdCalculation_C_h + +#include "EbDefinitions.h" +#ifdef __cplusplus +extern "C" { +#endif + +EB_U32 SatdCalculation_16x16( + EB_U8 *src, + EB_U32 srcStride, + EB_U8 *ref, + EB_U32 refStride); + +#ifdef __cplusplus +} +#endif +#endif // EbMeSadCalculation_C_h \ No newline at end of file diff --git a/Source/Lib/Codec/CMakeLists.txt b/Source/Lib/Codec/CMakeLists.txt index 527630c33..e8230f5c3 100644 --- a/Source/Lib/Codec/CMakeLists.txt +++ b/Source/Lib/Codec/CMakeLists.txt @@ -58,6 +58,7 @@ add_library(SvtHevcEnc SHARED EbMcp.h EbMdRateEstimation.h EbMeSadCalculation.h + EbMeSatdCalculation.h EbModeDecision.h EbModeDecisionConfiguration.h EbModeDecisionConfigurationProcess.h diff --git a/Source/Lib/Codec/EbMeSatdCalculation.h b/Source/Lib/Codec/EbMeSatdCalculation.h new file mode 100644 index 000000000..4dbd33cce --- /dev/null +++ b/Source/Lib/Codec/EbMeSatdCalculation.h @@ -0,0 +1,42 @@ +/* +* Copyright(c) 2018 Intel Corporation +* SPDX - License - Identifier: BSD - 2 - Clause - Patent +*/ + +#ifndef EbMeSatdCalculation_h +#define EbMeSatdCalculation_h + +#include "EbMeSatdCalculation_C.h" +#include "EbMeSatdCalculation_AVX2.h" + +#include "EbDefinitions.h" +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************** +* Function Types +***************************************/ +typedef EB_U32(*EB_SATDCALCULATION16X16_TYPE)( + EB_U8 *src, + EB_U32 srcStride, + EB_U8 *ref, + EB_U32 refStride); + +/*************************************** +* Function Tables +***************************************/ + +static EB_SATDCALCULATION16X16_TYPE SatdCalculation_16x16_funcPtrArray[EB_ASM_TYPE_TOTAL] = { + // C_DEFAULT + SatdCalculation_16x16, + // AVX2 + SatdCalculation_16x16_avx2 + /*SatdCalculation_16x16_SSE2_INTRIN,*/ +}; + + +#ifdef __cplusplus +} +#endif +#endif // EbMeSatdCalculation_h \ No newline at end of file diff --git a/Source/Lib/Codec/EbMotionEstimation.c b/Source/Lib/Codec/EbMotionEstimation.c index 06650860a..43c8aa0b6 100644 --- a/Source/Lib/Codec/EbMotionEstimation.c +++ b/Source/Lib/Codec/EbMotionEstimation.c @@ -16,6 +16,7 @@ #include "EbReferenceObject.h" #include "EbAvcStyleMcp.h" #include "EbMeSadCalculation.h" +#include "EbMeSatdCalculation.h" #include "EbIntraPrediction.h" #include "EbLambdaRateTables.h" @@ -3753,9 +3754,7 @@ EB_ERRORTYPE MotionEstimateLcu( EB_U64 ref0Poc = 0; EB_U64 ref1Poc = 0; - - EB_U64 i; - + EB_S16 hmeLevel1SearchAreaInWidth; EB_S16 hmeLevel1SearchAreaInHeight; @@ -4417,8 +4416,14 @@ EB_ERRORTYPE MotionEstimateLcu( // Compute the sum of the distortion of all 16 16x16 (best) blocks in the LCU pictureControlSetPtr->rcMEdistortion[lcuIndex] = 0; - for (i = 0; i < 16; i++) { - pictureControlSetPtr->rcMEdistortion[lcuIndex] += pictureControlSetPtr->meResults[lcuIndex][5 + i].distortionDirection[0].distortion; + pictureControlSetPtr->rcMESatdDistortion[lcuIndex] = 0; + for (puIndex = ME_TIER_ZERO_PU_16x16_0; puIndex <= ME_TIER_ZERO_PU_16x16_15; puIndex++) { + referenceObject = (EbPaReferenceObject_t*)pictureControlSetPtr->refPaPicPtrArray[0]->objectPtr; + refPicPtr = (EbPictureBufferDesc_t*)referenceObject->inputPaddedPicturePtr; + searchRegionIndex = (EB_S16)refPicPtr->originX + originX + ((EB_S16)refPicPtr->originY + originY) * refPicPtr->strideY; + pictureControlSetPtr->rcMESatdDistortion[lcuIndex] += SatdCalculation_16x16_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1] + (contextPtr->lcuSrcPtr, contextPtr->lcuSrcStride, &(refPicPtr->bufferY[searchRegionIndex]), refPicPtr->strideY); + pictureControlSetPtr->rcMEdistortion[lcuIndex] += pictureControlSetPtr->meResults[lcuIndex][puIndex].distortionDirection[0].distortion; } } @@ -5089,13 +5094,11 @@ EB_ERRORTYPE OpenLoopIntraSearchLcu( (EB_U32)EB_INTRA_PLANAR); //Distortion - oisCuPtr[0].distortion = (EB_U32)NxMSadKernel_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1][cuSize >> 3]( // Always SAD without weighting + oisCuPtr[0].distortion = SatdCalculation_16x16_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( &(inputPtr->bufferY[(inputPtr->originY + cuOriginY) * inputPtr->strideY + (inputPtr->originX + cuOriginX)]), inputPtr->strideY, &(contextPtr->meContextPtr->lcuBuffer[0]), - MAX_LCU_SIZE, - cuSize, - cuSize); + MAX_LCU_SIZE); oisCuPtr[0].intraMode = EB_INTRA_PLANAR; diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index 388cc4db4..43dd46151 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -680,9 +680,8 @@ EB_ERRORTYPE PictureParentControlSetCtor( } EB_MALLOC(EB_U32*, objectPtr->rcMEdistortion, sizeof(EB_U32) * objectPtr->lcuTotalCount, EB_N_PTR); + EB_MALLOC(EB_U32*, objectPtr->rcMESatdDistortion, sizeof(EB_U32) * objectPtr->lcuTotalCount, EB_N_PTR); - - // ME and OIS Distortion Histograms EB_MALLOC(EB_U16*, objectPtr->meDistortionHistogram, sizeof(EB_U16) * NUMBER_OF_SAD_INTERVALS, EB_N_PTR); diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index 8985b468b..526c864a6 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -448,6 +448,7 @@ typedef struct PictureParentControlSet_s MeCuResults_t **meResults; EB_U32 *rcMEdistortion; + EB_U32 *rcMESatdDistortion; // Motion Estimation Distortion and OIS Historgram EB_U16 *meDistortionHistogram; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index a0279a104..667b0b39b 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -966,10 +966,10 @@ void FrameLevelRcInputPictureMode3( else { for (EB_U16 lcuIndex = 0; lcuIndex < lcuTotalCount; lcuIndex++) - pictureControlSetPtr->sadCost += pictureControlSetPtr->ParentPcsPtr->rcMEdistortion[lcuIndex]; + pictureControlSetPtr->sadCost += pictureControlSetPtr->ParentPcsPtr->rcMESatdDistortion[lcuIndex]; } - pictureControlSetPtr->sadCost /= SAD_SATD_CONSTANT; + //pictureControlSetPtr->sadCost /= SAD_SATD_CONSTANT; if (pictureControlSetPtr->temporalLayerIndex > 1 && pictureControlSetPtr->sliceType != EB_I_PICTURE) { From 2442880640149f41b2f7b692efa2fc74144e5f48 Mon Sep 17 00:00:00 2001 From: zhoubo Date: Thu, 6 Jun 2019 16:07:25 +0800 Subject: [PATCH 59/93] Fix compiler errors on Linux. --- CMakeLists.txt | 2 +- Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a10191a86..befcc5829 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(CMAKE_ASM_NASM_FLAGS "-DUNIX64") + set(CMAKE_ASM_NASM_FLAGS "-DUNIX64 -DPIC") set(CMAKE_ASM_NASM_FLAGS_DEBUG "-g null") set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64) set(CMAKE_EXE_LINKER_FLAGS "-z noexecstack -z relro -z now -pie -lm") diff --git a/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c b/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c index 75aa5f83a..513735d4f 100644 --- a/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c +++ b/Source/Lib/C_DEFAULT/EbMeSatdCalculation_C.c @@ -7,7 +7,7 @@ #define BITS_PER_SUM (8 * sizeof(EB_U16)) -inline EB_U32 abs2(EB_U32 a) +static inline EB_U32 abs2(EB_U32 a) { EB_U32 s = ((a >> (BITS_PER_SUM - 1)) & (((EB_U32)1 << BITS_PER_SUM) + 1)) * ((EB_U32)-1); From c8e2fcd074b81ac968e1516401c77d3042d5c893 Mon Sep 17 00:00:00 2001 From: "Hu, Kelvin" Date: Tue, 11 Jun 2019 18:02:20 +0800 Subject: [PATCH 60/93] Fixed hrd 1 crash issues caused by writeUvlc long bits, memmove and SEIPeriod issues Signed-off-by: Hu, Kelvin --- Source/Lib/Codec/EbEntropyCoding.c | 42 +++++++++++++++-------- Source/Lib/Codec/EbPacketizationProcess.c | 2 +- Source/Lib/Codec/EbSei.c | 16 ++++----- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index e74374981..53e2d3a45 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -4749,10 +4749,22 @@ static void WriteUvlc( numberOfBits += 2; } - OutputBitstreamWrite( - bitstreamPtr, - bits, - numberOfBits); + if(numberOfBits<32) { + OutputBitstreamWrite( + bitstreamPtr, + bits, + numberOfBits); + } else + { + OutputBitstreamWrite( + bitstreamPtr, + 0, + numberOfBits>>1); + OutputBitstreamWrite( + bitstreamPtr, + bits, + (numberOfBits+1)>>1); + } } /************************************************** @@ -8779,17 +8791,6 @@ EB_ERRORTYPE CodeBufferingPeriodSEI( bufferingPeriodPtr->rapCpbParamsPresentFlag); } - // concatenation_flag - WriteFlagCavlc( - bitstreamPtr, - bufferingPeriodPtr->concatenationFlag); - - // au_cpb_removal_delay_delta_minus1 - WriteCodeCavlc( - bitstreamPtr, - bufferingPeriodPtr->auCpbRemovalDelayDeltaMinus1, - vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1); - if (bufferingPeriodPtr->rapCpbParamsPresentFlag){ // cpb_delay_offset WriteCodeCavlc( @@ -8803,6 +8804,17 @@ EB_ERRORTYPE CodeBufferingPeriodSEI( vuiPtr->hrdParametersPtr->dpbOutputDelayDuLengthMinus1 + 1); } + // concatenation_flag + WriteFlagCavlc( + bitstreamPtr, + bufferingPeriodPtr->concatenationFlag); + + // au_cpb_removal_delay_delta_minus1 + WriteCodeCavlc( + bitstreamPtr, + bufferingPeriodPtr->auCpbRemovalDelayDeltaMinus1, + vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1); + for (nalVclIndex = 0; nalVclIndex < 2; ++nalVclIndex){ if ((nalVclIndex == 0 && vuiPtr->hrdParametersPtr->nalHrdParametersPresentFlag) || (nalVclIndex == 1 && vuiPtr->hrdParametersPtr->vclHrdParametersPresentFlag)){ diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index c8e420a0d..5e45953cc 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -808,7 +808,7 @@ void* PacketizationKernel(void *inputPtr) startinBytes = queueEntryPtr->startSplicing; totalBytes = outputStreamPtr->nFilledLen; //Shift the bitstream by size of picture timing SEI - memcpy(outputStreamPtr->pBuffer + startinBytes + bufferWrittenBytesCount, outputStreamPtr->pBuffer + startinBytes, totalBytes - startinBytes); + memmove(outputStreamPtr->pBuffer + startinBytes + bufferWrittenBytesCount, outputStreamPtr->pBuffer + startinBytes, totalBytes - startinBytes); // Copy Picture Timing SEI to the Output Bitstream CopyRbspBitstreamToPayload( queueEntryPtr->bitStreamPtr2, diff --git a/Source/Lib/Codec/EbSei.c b/Source/Lib/Codec/EbSei.c index 688cad7d1..5fe283244 100644 --- a/Source/Lib/Codec/EbSei.c +++ b/Source/Lib/Codec/EbSei.c @@ -452,12 +452,6 @@ EB_U32 GetBufPeriodSEILength( seiLength += 1; } - // concatenation_flag - seiLength += 1; - - // au_cpb_removal_delay_delta_minus1 - seiLength += vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1; - if(bufferingPeriodPtr->rapCpbParamsPresentFlag) { // cpb_delay_offset seiLength += vuiPtr->hrdParametersPtr->initialCpbRemovalDelayLengthMinus1 + 1; @@ -466,6 +460,12 @@ EB_U32 GetBufPeriodSEILength( seiLength += vuiPtr->hrdParametersPtr->dpbOutputDelayDuLengthMinus1 + 1; } + // concatenation_flag + seiLength += 1; + + // au_cpb_removal_delay_delta_minus1 + seiLength += vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1; + for(nalVclIndex = 0; nalVclIndex < 2; ++nalVclIndex) { if((nalVclIndex == 0 && vuiPtr->hrdParametersPtr->nalHrdParametersPresentFlag) || (nalVclIndex == 1 && vuiPtr->hrdParametersPtr->vclHrdParametersPresentFlag)) { @@ -499,7 +499,7 @@ EB_U32 GetActiveParameterSetSEILength( EB_U32 seiLength = 0; // active_video_parameter_set_id - seiLength += GetUvlcCodeLength(activeParameterSet->activeVideoParameterSetid); + seiLength += 4; // self_contained_cvs_flag seiLength += 1; @@ -569,4 +569,4 @@ EB_U32 GetMasteringDisplayColorVolumeSEILength() seiLength = (seiLength + 7) >> 3; return seiLength; -} \ No newline at end of file +} From 80d1d8a1b1e8510de92a7d6f97cbeb64340f3ec1 Mon Sep 17 00:00:00 2001 From: Dinesh_MulticorewareINC Date: Fri, 14 Jun 2019 11:53:45 +0530 Subject: [PATCH 61/93] Modifed code as per coding guidelines --- Config/Sample.cfg | 4 +- Docs/svt-hevc_encoder_user_guide.md | 3 +- Source/API/EbApi.h | 4 + Source/Lib/Codec/EbEncHandle.c | 384 ++++++++++++++-------------- 4 files changed, 198 insertions(+), 197 deletions(-) diff --git a/Config/Sample.cfg b/Config/Sample.cfg index 727e62d4c..4b0cef5fe 100644 --- a/Config/Sample.cfg +++ b/Config/Sample.cfg @@ -76,13 +76,13 @@ SearchAreaHeight : 7 # Number of search posit ConstrainedIntra : 0 # Enable the use of Constrained Intra which results in sending two PPSs (0: OFF, 1: ON) # ====================== Rate Control =============================== -RateControlMode : 0 # Rate control mode (0: OFF(CQP), 1: VBR) +RateControlMode : 0 # Rate control mode (0: OFF(CQP), 1: VBR, 2: CRF) TargetBitRate : 7000000 # Target Bit Rate (in bits per second) MaxQpAllowed : 48 # maximum allowed QP when rate control is on - [0-51] MinQpAllowed : 10 # minimum allowed QP when rate control is on - [0-51] LookAheadDistance : 17 # Enable Look Ahead [0-250] SceneChangeDetection : 1 # Enable Scene Change Detection (0: OFF, 1: ON) - +ConstantRateFactor : 28 # CRF value allowed for rate control use - [0-51] # ====================== Tune =============================== Tune : 1 # Tune (0=SQ - visually optimized mode, 1=OQ - PSNR / SSIM optimized mode, 2=VMAF - VMAF optimized mode) diff --git a/Docs/svt-hevc_encoder_user_guide.md b/Docs/svt-hevc_encoder_user_guide.md index 03a727c40..84caa1f0f 100644 --- a/Docs/svt-hevc_encoder_user_guide.md +++ b/Docs/svt-hevc_encoder_user_guide.md @@ -281,7 +281,8 @@ The encoder parameters present in the Sample.cfg file are listed in this table b | **SearchAreaWidth** | -search-w | [1 - 256] | Depends on input resolution | Search Area in Width | | **SearchAreaHeight** | -search-h | [1 - 256] | Depends on input resolution | Search Area in Height | | **ConstrainedIntra** | -constrd-intra | [0,1] | 0 | Allow the use of Constrained Intra, when enabled, this features yields to sending two PPSs in the HEVC Elementary streams
0 = OFF, 1 = ON | -| **RateControlMode** | -rc | [0,1] | 0 | 0 : CQP , 1 : VBR | +| **RateControlMode** | -rc | [0,1] | 0 | 0 : CQP , 1 : VBR , 2 : CRF | +| **ConstantRateFactor** | -crf | [0 - 51] | 28 | CRF value allowed for rate control use, only apllicable when RateControlMode is set to 2 | | **TargetBitRate** | -tbr | Any Number | 7000000 | Target bitrate in bits / second. Only used when RateControlMode is set to 1 | | **MaxQpAllowed** | -max-qp | [0 - 51] | 48 | Maximum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be >= MinQpAllowed | | **MinQpAllowed** | -min-qp | [0 - 50] | 10 | Minimum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be < MaxQpAllowed | diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index 51741608b..63e8f8c14 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -420,6 +420,10 @@ typedef struct EB_H265_ENC_CONFIGURATION * * Default is 10. */ uint32_t minQpAllowed; + /* CRF value allowed for rate control use, only apllicable when rate + * control mode is set to 2. + * + * Default is 28. */ uint32_t crf; diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index 7cadf3de5..8558e21a2 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -485,7 +485,7 @@ static EB_ERRORTYPE EbEncHandleCtor( encHandlePtr->pictureDecisionThreadHandle = (EB_HANDLE) EB_NULL; encHandlePtr->motionEstimationThreadHandleArray = (EB_HANDLE*) EB_NULL; encHandlePtr->initialRateControlThreadHandle = (EB_HANDLE) EB_NULL; - encHandlePtr->sourceBasedOperationsThreadHandleArray = (EB_HANDLE*)EB_NULL; + encHandlePtr->sourceBasedOperationsThreadHandleArray = (EB_HANDLE*)EB_NULL; encHandlePtr->pictureManagerThreadHandle = (EB_HANDLE) EB_NULL; encHandlePtr->rateControlThreadHandle = (EB_HANDLE) EB_NULL; encHandlePtr->modeDecisionConfigurationThreadHandleArray = (EB_HANDLE*) EB_NULL; @@ -499,7 +499,7 @@ static EB_ERRORTYPE EbEncHandleCtor( encHandlePtr->pictureDecisionContextPtr = (EB_PTR) EB_NULL; encHandlePtr->motionEstimationContextPtrArray = (EB_PTR*) EB_NULL; encHandlePtr->initialRateControlContextPtr = (EB_PTR) EB_NULL; - encHandlePtr->sourceBasedOperationsContextPtrArray = (EB_PTR*)EB_NULL; + encHandlePtr->sourceBasedOperationsContextPtrArray = (EB_PTR*)EB_NULL; encHandlePtr->pictureManagerContextPtr = (EB_PTR) EB_NULL; encHandlePtr->rateControlContextPtr = (EB_PTR) EB_NULL; encHandlePtr->modeDecisionConfigurationContextPtrArray = (EB_PTR*) EB_NULL; @@ -514,7 +514,7 @@ static EB_ERRORTYPE EbEncHandleCtor( encHandlePtr->pictureAnalysisResultsResourcePtr = (EbSystemResource_t*) EB_NULL; encHandlePtr->pictureDecisionResultsResourcePtr = (EbSystemResource_t*) EB_NULL; encHandlePtr->motionEstimationResultsResourcePtr = (EbSystemResource_t*) EB_NULL; - encHandlePtr->initialRateControlResultsResourcePtr = (EbSystemResource_t*)EB_NULL; + encHandlePtr->initialRateControlResultsResourcePtr = (EbSystemResource_t*)EB_NULL; encHandlePtr->pictureDemuxResultsResourcePtr = (EbSystemResource_t*) EB_NULL; encHandlePtr->rateControlTasksResourcePtr = (EbSystemResource_t*) EB_NULL; encHandlePtr->rateControlResultsResourcePtr = (EbSystemResource_t*) EB_NULL; @@ -755,21 +755,21 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) inputData.pictureWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth; inputData.pictureHeight = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight; - inputData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->leftPadding; - inputData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->rightPadding; - inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; - inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; + inputData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->leftPadding; + inputData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->rightPadding; + inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; + inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; inputData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->outputBitdepth; inputData.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaFormatIdc; inputData.lcuSize = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize; inputData.maxDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxLcuDepth; inputData.is16bit = is16bit; - inputData.compressedTenBitFormat = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.compressedTenBitFormat; + inputData.compressedTenBitFormat = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.compressedTenBitFormat; - inputData.encMode = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.encMode; - inputData.speedControl = (EB_U8)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.speedControlFlag; + inputData.encMode = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.encMode; + inputData.speedControl = (EB_U8)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.speedControlFlag; inputData.tune = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.tune; - return_error = EbSystemResourceCtor( + return_error = EbSystemResourceCtor( &(encHandlePtr->pictureParentControlSetPoolPtrArray[instanceIndex]), encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->pictureControlSetPoolInitCount,//encHandlePtr->pictureControlSetPoolTotalCount, 1, @@ -809,10 +809,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) inputData.pictureWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth; inputData.pictureHeight = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaHeight; - inputData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->leftPadding; - inputData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->rightPadding; - inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; - inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; + inputData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->leftPadding; + inputData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->rightPadding; + inputData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->topPadding; + inputData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->botPadding; inputData.bitDepth = EB_8BIT; inputData.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaFormatIdc; inputData.lcuSize = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize; @@ -868,10 +868,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) referencePictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; referencePictureBufferDescInitData.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->chromaFormatIdc; referencePictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_FULL_MASK; - referencePictureBufferDescInitData.leftPadding = MAX_LCU_SIZE + MCPXPaddingOffset; - referencePictureBufferDescInitData.rightPadding = MAX_LCU_SIZE + MCPXPaddingOffset; - referencePictureBufferDescInitData.topPadding = MAX_LCU_SIZE + MCPYPaddingOffset; - referencePictureBufferDescInitData.botPadding = MAX_LCU_SIZE + MCPYPaddingOffset; + referencePictureBufferDescInitData.leftPadding = MAX_LCU_SIZE + MCPXPaddingOffset; + referencePictureBufferDescInitData.rightPadding = MAX_LCU_SIZE + MCPXPaddingOffset; + referencePictureBufferDescInitData.topPadding = MAX_LCU_SIZE + MCPYPaddingOffset; + referencePictureBufferDescInitData.botPadding = MAX_LCU_SIZE + MCPYPaddingOffset; referencePictureBufferDescInitData.splitMode = EB_FALSE; if (is16bit){ @@ -903,10 +903,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) referencePictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; referencePictureBufferDescInitData.colorFormat = EB_YUV420; referencePictureBufferDescInitData.bufferEnableMask = 0; - referencePictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; - referencePictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; - referencePictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; - referencePictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; + referencePictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; + referencePictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; + referencePictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; + referencePictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize + ME_FILTER_TAP; referencePictureBufferDescInitData.splitMode = EB_FALSE; quarterDecimPictureBufferDescInitData.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth >> 1; @@ -914,10 +914,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) quarterDecimPictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; quarterDecimPictureBufferDescInitData.colorFormat = EB_YUV420; quarterDecimPictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_LUMA_MASK; - quarterDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; - quarterDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; - quarterDecimPictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; - quarterDecimPictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; + quarterDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; + quarterDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; + quarterDecimPictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; + quarterDecimPictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 1; quarterDecimPictureBufferDescInitData.splitMode = EB_FALSE; sixteenthDecimPictureBufferDescInitData.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxInputLumaWidth >> 2; @@ -925,10 +925,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) sixteenthDecimPictureBufferDescInitData.bitDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->inputBitdepth; sixteenthDecimPictureBufferDescInitData.colorFormat = EB_YUV420; sixteenthDecimPictureBufferDescInitData.bufferEnableMask = PICTURE_BUFFER_DESC_LUMA_MASK; - sixteenthDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; - sixteenthDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; - sixteenthDecimPictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; - sixteenthDecimPictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; + sixteenthDecimPictureBufferDescInitData.leftPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; + sixteenthDecimPictureBufferDescInitData.rightPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; + sixteenthDecimPictureBufferDescInitData.topPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; + sixteenthDecimPictureBufferDescInitData.botPadding = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize >> 2; sixteenthDecimPictureBufferDescInitData.splitMode = EB_FALSE; EbPaReferenceObjectDescInitDataStructure.referencePictureDescInitData = referencePictureBufferDescInitData; @@ -1248,7 +1248,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) // Output Buffer Fifo Ptrs for(instanceIndex=0; instanceIndex < encHandlePtr->encodeInstanceTotalCount; ++instanceIndex) { - encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->streamOutputFifoPtr = (encHandlePtr->outputStreamBufferProducerFifoPtrDblArray[instanceIndex])[0]; + encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->streamOutputFifoPtr = (encHandlePtr->outputStreamBufferProducerFifoPtrDblArray[instanceIndex])[0]; if (encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->staticConfig.reconEnabled) { encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->reconOutputFifoPtr = (encHandlePtr->outputReconBufferProducerFifoPtrDblArray[instanceIndex])[0]; } @@ -1279,19 +1279,19 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) for(processIndex=0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->pictureAnalysisProcessInitCount; ++processIndex) { - EbPictureBufferDescInitData_t pictureBufferDescConf; - pictureBufferDescConf.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaWidth; - pictureBufferDescConf.maxHeight = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaHeight; - pictureBufferDescConf.bitDepth = EB_8BIT; + EbPictureBufferDescInitData_t pictureBufferDescConf; + pictureBufferDescConf.maxWidth = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaWidth; + pictureBufferDescConf.maxHeight = encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaHeight; + pictureBufferDescConf.bitDepth = EB_8BIT; pictureBufferDescConf.colorFormat = (EB_COLOR_FORMAT)encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->chromaFormatIdc; - pictureBufferDescConf.bufferEnableMask = PICTURE_BUFFER_DESC_Y_FLAG; - pictureBufferDescConf.leftPadding = 0; - pictureBufferDescConf.rightPadding = 0; - pictureBufferDescConf.topPadding = 0; - pictureBufferDescConf.botPadding = 0; - pictureBufferDescConf.splitMode = EB_FALSE; - - return_error = PictureAnalysisContextCtor( + pictureBufferDescConf.bufferEnableMask = PICTURE_BUFFER_DESC_Y_FLAG; + pictureBufferDescConf.leftPadding = 0; + pictureBufferDescConf.rightPadding = 0; + pictureBufferDescConf.topPadding = 0; + pictureBufferDescConf.botPadding = 0; + pictureBufferDescConf.splitMode = EB_FALSE; + + return_error = PictureAnalysisContextCtor( &pictureBufferDescConf, EB_TRUE, (PictureAnalysisContext_t**) &encHandlePtr->pictureAnalysisContextPtrArray[processIndex], @@ -1301,8 +1301,8 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) ((encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaHeight + MAX_LCU_SIZE - 1) / MAX_LCU_SIZE)); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; } } @@ -1314,8 +1314,8 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) (PictureDecisionContext_t**) &encHandlePtr->pictureDecisionContextPtr, encHandlePtr->pictureAnalysisResultsConsumerFifoPtrArray[0], encHandlePtr->pictureDecisionResultsProducerFifoPtrArray[0]); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; } } @@ -1324,10 +1324,10 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) for(processIndex=0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->motionEstimationProcessInitCount; ++processIndex) { - return_error = MotionEstimationContextCtor( - (MotionEstimationContext_t**) &encHandlePtr->motionEstimationContextPtrArray[processIndex], - encHandlePtr->pictureDecisionResultsConsumerFifoPtrArray[processIndex], - encHandlePtr->motionEstimationResultsProducerFifoPtrArray[processIndex]); + return_error = MotionEstimationContextCtor( + (MotionEstimationContext_t**) &encHandlePtr->motionEstimationContextPtrArray[processIndex], + encHandlePtr->pictureDecisionResultsConsumerFifoPtrArray[processIndex], + encHandlePtr->motionEstimationResultsProducerFifoPtrArray[processIndex]); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; @@ -1338,21 +1338,21 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) return_error = InitialRateControlContextCtor( (InitialRateControlContext_t**) &encHandlePtr->initialRateControlContextPtr, encHandlePtr->motionEstimationResultsConsumerFifoPtrArray[0], - encHandlePtr->initialRateControlResultsProducerFifoPtrArray[0]); + encHandlePtr->initialRateControlResultsProducerFifoPtrArray[0]); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } // Source Based Operations Context - EB_MALLOC(EB_PTR*, encHandlePtr->sourceBasedOperationsContextPtrArray, sizeof(EB_PTR) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount, EB_N_PTR); + EB_MALLOC(EB_PTR*, encHandlePtr->sourceBasedOperationsContextPtrArray, sizeof(EB_PTR) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount, EB_N_PTR); - for (processIndex = 0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount; ++processIndex) { + for (processIndex = 0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount; ++processIndex) { return_error = SourceBasedOperationsContextCtor( (SourceBasedOperationsContext_t**)&encHandlePtr->sourceBasedOperationsContextPtrArray[processIndex], encHandlePtr->initialRateControlResultsConsumerFifoPtrArray[processIndex], encHandlePtr->pictureDemuxResultsProducerFifoPtrArray[processIndex]); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; } } @@ -1371,8 +1371,8 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) (RateControlContext_t**) &encHandlePtr->rateControlContextPtr, encHandlePtr->rateControlTasksConsumerFifoPtrArray[0], encHandlePtr->rateControlResultsProducerFifoPtrArray[0], - encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->intraPeriodLength); - if (return_error == EB_ErrorInsufficientResources){ + encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->intraPeriodLength); + if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } @@ -1383,7 +1383,7 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) EB_MALLOC(EB_PTR*, encHandlePtr->modeDecisionConfigurationContextPtrArray, sizeof(EB_PTR) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->modeDecisionConfigurationProcessInitCount, EB_N_PTR); for(processIndex=0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->modeDecisionConfigurationProcessInitCount; ++processIndex) { - return_error = ModeDecisionConfigurationContextCtor( + return_error = ModeDecisionConfigurationContextCtor( (ModeDecisionConfigurationContext_t**) &encHandlePtr->modeDecisionConfigurationContextPtrArray[processIndex], encHandlePtr->rateControlResultsConsumerFifoPtrArray[processIndex], @@ -1391,10 +1391,9 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) ((encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaWidth + MAX_LCU_SIZE - 1) / MAX_LCU_SIZE) * ((encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->maxInputLumaHeight + MAX_LCU_SIZE - 1) / MAX_LCU_SIZE) ); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } } } @@ -1477,11 +1476,11 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) EB_CREATETHREAD(EB_HANDLE, encHandlePtr->initialRateControlThreadHandle, sizeof(EB_HANDLE), EB_THREAD, InitialRateControlKernel, encHandlePtr->initialRateControlContextPtr); // Source Based Oprations - EB_MALLOC(EB_HANDLE*, encHandlePtr->sourceBasedOperationsThreadHandleArray, sizeof(EB_HANDLE) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount, EB_N_PTR); + EB_MALLOC(EB_HANDLE*, encHandlePtr->sourceBasedOperationsThreadHandleArray, sizeof(EB_HANDLE) * encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount, EB_N_PTR); - for (processIndex = 0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount; ++processIndex) { - EB_CREATETHREAD(EB_HANDLE, encHandlePtr->sourceBasedOperationsThreadHandleArray[processIndex], sizeof(EB_HANDLE), EB_THREAD, SourceBasedOperationsKernel, encHandlePtr->sourceBasedOperationsContextPtrArray[processIndex]); - } + for (processIndex = 0; processIndex < encHandlePtr->sequenceControlSetInstanceArray[0]->sequenceControlSetPtr->sourceBasedOperationsProcessInitCount; ++processIndex) { + EB_CREATETHREAD(EB_HANDLE, encHandlePtr->sourceBasedOperationsThreadHandleArray[processIndex], sizeof(EB_HANDLE), EB_THREAD, SourceBasedOperationsKernel, encHandlePtr->sourceBasedOperationsContextPtrArray[processIndex]); + } // Picture Manager EB_CREATETHREAD(EB_HANDLE, encHandlePtr->pictureManagerThreadHandle, sizeof(EB_HANDLE), EB_THREAD, PictureManagerKernel, encHandlePtr->pictureManagerContextPtr); @@ -2133,7 +2132,7 @@ void CopyApiFromApp( static int VerifyHmeDimention(unsigned int index,unsigned int HmeLevel0SearchAreaInWidth, EB_U32 NumberHmeSearchRegionInWidth[EB_HME_SEARCH_AREA_ROW_MAX_COUNT], unsigned int numberHmeSearchRegionInWidth ) { int return_error = 0; - EB_U32 i; + EB_U32 i; EB_U32 totalSearchWidth = 0; for (i=0 ; i < numberHmeSearchRegionInWidth; i++){ @@ -2141,11 +2140,11 @@ static int VerifyHmeDimention(unsigned int index,unsigned int HmeLevel0SearchAre } if ((totalSearchWidth) != (HmeLevel0SearchAreaInWidth)) { SVT_LOG("SVT [Error]: Instance %u: Invalid HME Total Search Area. \n", index); - return_error = -1; - return return_error; + return_error = -1; + return return_error; } - return return_error; + return return_error; } static int VerifyHmeDimentionL1L2(unsigned int index, EB_U32 NumberHmeSearchRegionInWidth[EB_HME_SEARCH_AREA_ROW_MAX_COUNT], unsigned int numberHmeSearchRegionInWidth) @@ -2178,7 +2177,7 @@ static EB_ERRORTYPE VerifySettings(\ if ( config->tier > 1 ) { SVT_LOG("SVT [Error]: Instance %u: Tier must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } // For levels below level 4 (exclusive), only the main tier is allowed @@ -2267,14 +2266,14 @@ static EB_ERRORTYPE VerifySettings(\ break; } - if(levelIdx > TOTAL_LEVEL_COUNT){ + if(levelIdx > TOTAL_LEVEL_COUNT){ SVT_LOG("SVT [Error]: Instance %u: Unsupported level\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (sequenceControlSetPtr->maxInputLumaWidth < 64) { SVT_LOG("SVT [Error]: Instance %u: Source Width must be at least 64\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (sequenceControlSetPtr->maxInputLumaHeight < 64) { SVT_LOG("SVT [Error]: Instance %u: Source Width must be at least 64\n", channelNumber + 1); @@ -2295,7 +2294,7 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if (sequenceControlSetPtr->maxInputLumaWidth % 2) { + if (sequenceControlSetPtr->maxInputLumaWidth % 2) { SVT_LOG("SVT [Error]: Instance %u: Source Width must be even for YUV_420 colorspace\n",channelNumber+1); return_error = EB_ErrorBadParameter; } @@ -2306,19 +2305,19 @@ static EB_ERRORTYPE VerifySettings(\ } if (sequenceControlSetPtr->maxInputLumaWidth > 8192) { SVT_LOG("SVT [Error]: Instance %u: Source Width must be less than 8192\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (sequenceControlSetPtr->maxInputLumaHeight > 4320) { SVT_LOG("SVT [Error]: Instance %u: Source Height must be less than 4320\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - EB_U32 inputSize = (EB_U32)sequenceControlSetPtr->maxInputLumaWidth * (EB_U32)sequenceControlSetPtr->maxInputLumaHeight; + EB_U32 inputSize = (EB_U32)sequenceControlSetPtr->maxInputLumaWidth * (EB_U32)sequenceControlSetPtr->maxInputLumaHeight; - EB_U8 inputResolution = (inputSize < INPUT_SIZE_1080i_TH) ? INPUT_SIZE_576p_RANGE_OR_LOWER : - (inputSize < INPUT_SIZE_1080p_TH) ? INPUT_SIZE_1080i_RANGE : - (inputSize < INPUT_SIZE_4K_TH) ? INPUT_SIZE_1080p_RANGE : + EB_U8 inputResolution = (inputSize < INPUT_SIZE_1080i_TH) ? INPUT_SIZE_576p_RANGE_OR_LOWER : + (inputSize < INPUT_SIZE_1080p_TH) ? INPUT_SIZE_1080i_RANGE : + (inputSize < INPUT_SIZE_4K_TH) ? INPUT_SIZE_1080p_RANGE : INPUT_SIZE_4K_RANGE; if (inputResolution <= INPUT_SIZE_1080i_RANGE) { @@ -2352,14 +2351,14 @@ static EB_ERRORTYPE VerifySettings(\ sequenceControlSetPtr->maxEncMode = MAX_SUPPORTED_MODES_SUB1080P - 1; if (config->encMode > MAX_SUPPORTED_MODES_SUB1080P -1) { SVT_LOG("SVT [Error]: Instance %u: encMode must be [0 - %d]\n", channelNumber + 1, MAX_SUPPORTED_MODES_SUB1080P-1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } }else if (inputResolution == INPUT_SIZE_1080p_RANGE){ sequenceControlSetPtr->maxEncMode = MAX_SUPPORTED_MODES_1080P - 1; if (config->encMode > MAX_SUPPORTED_MODES_1080P - 1) { SVT_LOG("SVT [Error]: Instance %u: encMode must be [0 - %d]\n", channelNumber + 1, MAX_SUPPORTED_MODES_1080P - 1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } }else { if (config->tune == 0) sequenceControlSetPtr->maxEncMode = MAX_SUPPORTED_MODES_4K_SQ - 1; @@ -2368,29 +2367,29 @@ static EB_ERRORTYPE VerifySettings(\ if (config->encMode > MAX_SUPPORTED_MODES_4K_SQ - 1 && config->tune == 0) { SVT_LOG("SVT [Error]: Instance %u: encMode must be [0 - %d]\n", channelNumber + 1, MAX_SUPPORTED_MODES_4K_SQ-1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; }else if (config->encMode > MAX_SUPPORTED_MODES_4K_OQ - 1 && config->tune >= 1) { SVT_LOG("SVT [Error]: Instance %u: encMode must be [0 - %d]\n", channelNumber + 1, MAX_SUPPORTED_MODES_4K_OQ-1); - return_error = EB_ErrorBadParameter; - } - } + return_error = EB_ErrorBadParameter; + } + } if(config->qp > 51) { SVT_LOG("SVT [Error]: Instance %u: QP must be [0 - 51]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (config->hierarchicalLevels > 3) { SVT_LOG("SVT [Error]: Instance %u: Hierarchical Levels supported [0-3]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (config->intraPeriodLength < -2 || config->intraPeriodLength > 255) { SVT_LOG("SVT [Error]: Instance %u: The intra period must be [-2 - 255] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if( config->intraRefreshType > 2 || config->intraRefreshType < 1) { + if( config->intraRefreshType > 2 || config->intraRefreshType < 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid intra Refresh Type [1-2]\n",channelNumber+1); return_error = EB_ErrorBadParameter; } @@ -2403,33 +2402,31 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if ( config->disableDlfFlag > 1) { + if ( config->disableDlfFlag > 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid LoopFilterDisable. LoopFilterDisable must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if ( config->enableSaoFlag > 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid SAO. SAO range must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if ( config->useDefaultMeHme > 1 ){ + if ( config->useDefaultMeHme > 1 ){ SVT_LOG("SVT [Error]: Instance %u: invalid useDefaultMeHme. useDefaultMeHme must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if ( config->enableHmeFlag > 1 ){ SVT_LOG("SVT [Error]: Instance %u: invalid HME. HME must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } - if ((config->searchAreaWidth > 256) || (config->searchAreaWidth == 0)){ + return_error = EB_ErrorBadParameter; + } + if ((config->searchAreaWidth > 256) || (config->searchAreaWidth == 0)){ SVT_LOG("SVT [Error]: Instance %u: Invalid SearchAreaWidth. SearchAreaWidth must be [1 - 256]\n",channelNumber+1); return_error = EB_ErrorBadParameter; - } - if((config->searchAreaHeight > 256) || (config->searchAreaHeight == 0)) { + if((config->searchAreaHeight > 256) || (config->searchAreaHeight == 0)) { SVT_LOG("SVT [Error]: Instance %u: Invalid SearchAreaHeight. SearchAreaHeight must be [1 - 256]\n",channelNumber+1); return_error = EB_ErrorBadParameter; - } if (levelIdx < 13) { @@ -2445,47 +2442,47 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->rateControlMode) && (config->tier == 0) && ((config->targetBitRate*2) > mainTierMaxBitRate[levelIdx])){ + if ((config->level != 0) && (config->rateControlMode) && (config->tier == 0) && ((config->targetBitRate*2) > mainTierMaxBitRate[levelIdx])){ SVT_LOG("SVT [Error]: Instance %u: Allowed MaxBitRate exceeded for level %s and tier 0 \n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->rateControlMode) && (config->tier == 1) && ((config->targetBitRate*2) > highTierMaxBitRate[levelIdx])){ + if ((config->level != 0) && (config->rateControlMode) && (config->tier == 1) && ((config->targetBitRate*2) > highTierMaxBitRate[levelIdx])){ SVT_LOG("SVT [Error]: Instance %u: Allowed MaxBitRate exceeded for level %s and tier 1 \n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->rateControlMode) && (config->tier == 0) && ((config->targetBitRate * 3) > mainTierCPB[levelIdx])) { + if ((config->level != 0) && (config->rateControlMode) && (config->tier == 0) && ((config->targetBitRate * 3) > mainTierCPB[levelIdx])) { SVT_LOG("SVT [Error]: Instance %u: Out of bound maxBufferSize for level %s and tier 0 \n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->rateControlMode) && (config->tier == 1) && ((config->targetBitRate * 3) > highTierCPB[levelIdx])) { + if ((config->level != 0) && (config->rateControlMode) && (config->tier == 1) && ((config->targetBitRate * 3) > highTierCPB[levelIdx])) { SVT_LOG("SVT [Error]: Instance %u: Out of bound maxBufferSize for level %s and tier 1 \n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } // Table A.6 General tier and level limits - if ((config->level != 0) && (config->tileColumnCount > maxTileColumn[levelIdx])) { + if ((config->level != 0) && (config->tileColumnCount > maxTileColumn[levelIdx])) { SVT_LOG("SVT [Error]: Instance %u: Out of bound maxTileColumn for level %s\n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } - if ((config->level != 0) && (config->tileRowCount > maxTileRow[levelIdx])) { + if ((config->level != 0) && (config->tileRowCount > maxTileRow[levelIdx])) { SVT_LOG("SVT [Error]: Instance %u: Out of bound maxTileRow for level %s\n",channelNumber+1, levelIdc); return_error = EB_ErrorBadParameter; } } - if(config->profile > 4){ + if(config->profile > 4){ SVT_LOG("SVT [Error]: Instance %u: The maximum allowed Profile number is 4 or MAINEXT \n",channelNumber+1); return_error = EB_ErrorBadParameter; } - if (config->profile == 0){ + if (config->profile == 0){ SVT_LOG("SVT [Error]: Instance %u: The minimum allowed Profile number is 1 \n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } - if(config->profile == 3) { + if(config->profile == 3) { SVT_LOG("SVT [Error]: Instance %u: The Main Still Picture Profile is not supported \n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if(config->encoderColorFormat >= EB_YUV422 && config->profile != 4) { @@ -2507,7 +2504,7 @@ static EB_ERRORTYPE VerifySettings(\ SVT_LOG("SVT [Error]: Instance %u: The intra period must be [-2 - 255] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->constrainedIntra > 1) { + if (config->constrainedIntra > 1) { SVT_LOG("SVT [Error]: Instance %u: The constrained intra must be [0 - 1] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } @@ -2517,7 +2514,7 @@ static EB_ERRORTYPE VerifySettings(\ } if ((config->rateControlMode == 2) && (config->crf > 51)) { - SVT_LOG("Error instance %u:The crf value must be [0-51] \n", channelNumber + 1); + SVT_LOG("SVT [Error]: Instance %u:The crf value must be [0-51] \n", channelNumber + 1); } if (config->tune > 0 && config->bitRateReduction == 1){ SVT_LOG("SVT [Error]: Instance %u: Bit Rate Reduction is not supported for OQ mode (Tune = 1 ) and VMAF mode (Tune = 2)\n", channelNumber + 1); @@ -2533,73 +2530,73 @@ static EB_ERRORTYPE VerifySettings(\ SVT_LOG("SVT [Error]: Instance %u: The lookahead distance must be [0 - 250] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->sceneChangeDetection > 1) { + if (config->sceneChangeDetection > 1) { SVT_LOG("SVT [Error]: Instance %u: The scene change detection must be [0 - 1] \n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } - if ( config->maxQpAllowed > 51) { + return_error = EB_ErrorBadParameter; + } + if ( config->maxQpAllowed > 51) { SVT_LOG("SVT [Error]: Instance %u: MaxQpAllowed must be [0 - 51]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } - else if ( config->minQpAllowed > 50 ) { + return_error = EB_ErrorBadParameter; + } + else if ( config->minQpAllowed > 50 ) { SVT_LOG("SVT [Error]: Instance %u: MinQpAllowed must be [0 - 50]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } - else if ( (config->minQpAllowed) > (config->maxQpAllowed)) { + return_error = EB_ErrorBadParameter; + } + else if ( (config->minQpAllowed) > (config->maxQpAllowed)) { SVT_LOG("SVT [Error]: Instance %u: MinQpAllowed must be smaller than MaxQpAllowed\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } - if (config->videoUsabilityInfo > 1) { + if (config->videoUsabilityInfo > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid VideoUsabilityInfo. VideoUsabilityInfo must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (config->tune > 2) { SVT_LOG("SVT [Error]: Instance %u : Invalid Tune. Tune must be [0 - 2]\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->bitRateReduction > 1) { + if (config->bitRateReduction > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid BitRateReduction. BitRateReduction must be [0 - 1]\n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (config->improveSharpness > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid ImproveSharpness. ImproveSharpness must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (config->highDynamicRangeInput > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid HighDynamicRangeInput. HighDynamicRangeInput must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->accessUnitDelimiter > 1) { + if (config->accessUnitDelimiter > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid AccessUnitDelimiter. AccessUnitDelimiter must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->bufferingPeriodSEI > 1) { + if (config->bufferingPeriodSEI > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid BufferingPeriod. BufferingPeriod must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->pictureTimingSEI > 1) { + if (config->pictureTimingSEI > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid PictureTiming. PictureTiming must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->registeredUserDataSeiFlag > 1) { + if (config->registeredUserDataSeiFlag > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid RegisteredUserData. RegisteredUserData must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->unregisteredUserDataSeiFlag > 1) { + if (config->unregisteredUserDataSeiFlag > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid UnregisteredUserData. UnregisteredUserData must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } - if (config->recoveryPointSeiFlag > 1) { + if (config->recoveryPointSeiFlag > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid RecoveryPoint. RecoveryPoint must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (config->useMasteringDisplayColorVolume > 1) { @@ -2612,34 +2609,34 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if ((config->maxCLL && !config->highDynamicRangeInput) || (config->maxFALL && !config->highDynamicRangeInput)) { - SVT_LOG("Error Instance %u: maxCLL or maxFALL should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if ((config->maxCLL && !config->highDynamicRangeInput) || (config->maxFALL && !config->highDynamicRangeInput)) { + SVT_LOG("SVT [Error]: Instance %u: maxCLL or maxFALL should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->useMasteringDisplayColorVolume && !config->highDynamicRangeInput) { - SVT_LOG("Error Instance %u: MasterDisplay should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if (config->useMasteringDisplayColorVolume && !config->highDynamicRangeInput) { + SVT_LOG("SVT [Error]: Instance %u: MasterDisplay should be used only with high dynamic range input; set highDynamicRangeInput to 1\n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->dolbyVisionProfile != 0 && config->dolbyVisionProfile != 81) { - SVT_LOG("Error Instance %u: Only Dolby Vision Profile 8.1 is supported \n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if (config->dolbyVisionProfile != 0 && config->dolbyVisionProfile != 81) { + SVT_LOG("SVT [Error]: Instance %u: Only Dolby Vision Profile 8.1 is supported \n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->dolbyVisionProfile == 81 && config->encoderBitDepth != 10) { - SVT_LOG("Error Instance %u: Dolby Vision Profile 8.1 work only with main10 input \n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if (config->dolbyVisionProfile == 81 && config->encoderBitDepth != 10) { + SVT_LOG("SVT [Error]: Instance %u: Dolby Vision Profile 8.1 work only with main10 input \n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->dolbyVisionProfile == 81 && !config->useMasteringDisplayColorVolume) { - SVT_LOG("Error Instance %u: Dolby Vision Profile 8.1 requires mastering display color volume information \n", channelNumber); - return_error = EB_ErrorBadParameter; - } + if (config->dolbyVisionProfile == 81 && !config->useMasteringDisplayColorVolume) { + SVT_LOG("SVT [Error]: Instance %u: Dolby Vision Profile 8.1 requires mastering display color volume information \n", channelNumber); + return_error = EB_ErrorBadParameter; + } - if (config->enableTemporalId > 1) { + if (config->enableTemporalId > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid TemporalId. TemporalId must be [0 - 1]\n",channelNumber+1); - return_error = EB_ErrorBadParameter; + return_error = EB_ErrorBadParameter; } if (config->pictureTimingSEI && !config->videoUsabilityInfo){ @@ -2647,23 +2644,22 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } - if ( (config->encoderBitDepth !=8 ) && - (config->encoderBitDepth !=10 ) - ) { + if ( (config->encoderBitDepth !=8 ) && + (config->encoderBitDepth !=10 ) + ) { SVT_LOG("SVT [Error]: Instance %u: Encoder Bit Depth shall be only 8 or 10 \n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } // Check if the EncoderBitDepth is conformant with the Profile constraint - if(config->profile == 1 && config->encoderBitDepth == 10) { + if(config->profile == 1 && config->encoderBitDepth == 10) { SVT_LOG("SVT [Error]: Instance %u: The encoder bit depth shall be equal to 8 for Main Profile\n",channelNumber+1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } - if (config->compressedTenBitFormat > 1) - { + if (config->compressedTenBitFormat > 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid Compressed Ten Bit Format shall be only [0 - 1] \n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } + return_error = EB_ErrorBadParameter; + } if (config->speedControlFlag > 1) { SVT_LOG("SVT [Error]: Instance %u: Invalid Speed Control flag [0 - 1]\n", channelNumber + 1); @@ -2968,8 +2964,8 @@ EB_API EB_ERRORTYPE EbH265EncSetParameter( // Set the Prediction Structure pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->predStructPtr = GetPredictionStructure( - pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->predictionStructureGroupPtr, - pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.predStructure, + pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->predictionStructureGroupPtr, + pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.predStructure, pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxRefCount, pEncCompData->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxTemporalLayers); @@ -3076,7 +3072,7 @@ EB_API EB_ERRORTYPE EbH265EncStreamHeader( (EB_U32*) &(outputStreamBuffer->nFilledLen), (EB_U32*) &(outputStreamBuffer->nAllocLen), encodeContextPtr, - NAL_UNIT_INVALID); + NAL_UNIT_INVALID); *outputStreamPtr = outputStreamBuffer; @@ -3132,7 +3128,7 @@ EB_API EB_ERRORTYPE EbH265EncEosNal( (EB_U32*) &(outputStreamBuffer->nFilledLen), (EB_U32*) &(outputStreamBuffer->nAllocLen), encodeContextPtr, - NAL_UNIT_INVALID); + NAL_UNIT_INVALID); *outputStreamPtr = outputStreamBuffer; @@ -3817,7 +3813,7 @@ EB_ERRORTYPE EbOutputBufferHeaderCtor( outBufPtr->nAllocLen = nStride; outBufPtr->pAppPrivate = NULL; - (void)objectInitDataPtr; + (void)objectInitDataPtr; return EB_ErrorNone; } From ceb634783bcaa01e72f22251dfd2198c0e99c115 Mon Sep 17 00:00:00 2001 From: Kavitha Sampath Date: Thu, 27 Jun 2019 19:27:45 +0530 Subject: [PATCH 62/93] Fix linux build errors --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 03fa15000..5ba54d7c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) if(WIN32) set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DWIN64") else() - set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DUNIX64") + set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DUNIX64 -DPIC") endif() if(UNIX) @@ -111,6 +111,8 @@ set(debug_flags_to_test if(MSVC) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NXCompat /DynamicBase") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NXCompat /DynamicBase") +else() + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lm") endif() foreach(flag ${flags_to_test};${release_flags_to_test};${debug_flags_to_test}) From 5bb446b0d333ce58c5c4ab1f87ac2bfc4d3ad1e7 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 10 Jul 2019 16:10:19 +0530 Subject: [PATCH 63/93] Merge branch 'master' of https://github.com/OpenVisualCloud/SVT-HEVC # Conflicts: # Source/Lib/Codec/EbCodingLoop.c # Source/Lib/Codec/EbEncDecProcess.c # Source/Lib/Codec/EbEntropyCoding.h # Source/Lib/Codec/EbPacketizationProcess.c # Source/Lib/Codec/EbPictureControlSet.c # Source/Lib/Codec/EbSei.c --- .travis.yml | 223 ++++- CMakeLists.txt | 10 +- Source/API/EbApi.h | 2 - Source/App/EbAppConfig.c | 10 - Source/App/EbAppConfig.h | 2 - Source/App/EbAppContext.c | 2 - Source/Lib/ASM_AVX2/EbComputeSAD_AVX2.h | 15 + .../ASM_AVX2/EbComputeSAD_Intrinsic_AVX2.c | 74 +- .../EbComputeSAD_SadLoopKernel_AVX512.c | 6 +- .../EbPictureOperators_Intrinsic_SSE4_1.c | 56 +- .../Codec/EbAdaptiveMotionVectorPrediction.c | 17 +- Source/Lib/Codec/EbCodingLoop.c | 360 ++++--- Source/Lib/Codec/EbCodingUnit.h | 3 - Source/Lib/Codec/EbComputeSAD.h | 10 +- Source/Lib/Codec/EbDeblockingFilter.c | 105 ++- Source/Lib/Codec/EbDeblockingFilter.h | 6 +- Source/Lib/Codec/EbDefinitions.h | 11 +- Source/Lib/Codec/EbEncDecProcess.c | 545 ++++++----- Source/Lib/Codec/EbEncDecProcess.h | 7 +- Source/Lib/Codec/EbEncDecResults.h | 3 +- Source/Lib/Codec/EbEncDecTasks.h | 1 + Source/Lib/Codec/EbEncHandle.c | 32 +- Source/Lib/Codec/EbEntropyCoding.c | 112 ++- Source/Lib/Codec/EbEntropyCoding.h | 4 +- Source/Lib/Codec/EbEntropyCodingProcess.c | 488 ++++------ .../Lib/Codec/EbInitialRateControlProcess.c | 19 + Source/Lib/Codec/EbModeDecision.c | 6 +- .../EbModeDecisionConfigurationProcess.c | 44 +- Source/Lib/Codec/EbModeDecisionProcess.c | 41 +- Source/Lib/Codec/EbModeDecisionProcess.h | 10 +- Source/Lib/Codec/EbMotionEstimation.c | 10 +- Source/Lib/Codec/EbMotionEstimationProcess.c | 19 + Source/Lib/Codec/EbPacketizationProcess.c | 88 +- Source/Lib/Codec/EbPictureControlSet.c | 889 +++++++++--------- Source/Lib/Codec/EbPictureControlSet.h | 109 ++- Source/Lib/Codec/EbPictureManagerProcess.c | 81 +- Source/Lib/Codec/EbProductCodingLoop.c | 173 ++-- Source/Lib/Codec/EbRateControlProcess.c | 2 + Source/Lib/Codec/EbRateControlTasks.h | 3 +- .../Lib/Codec/EbResourceCoordinationProcess.c | 55 +- ...EbSampleAdaptiveOffsetGenerationDecision.c | 4 +- Source/Lib/Codec/EbSei.c | 5 +- Source/Lib/Codec/EbSequenceControlSet.c | 51 +- Source/Lib/Codec/EbSequenceControlSet.h | 4 +- 44 files changed, 2100 insertions(+), 1617 deletions(-) diff --git a/.travis.yml b/.travis.yml index 35fab3aa4..655a6c4b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,54 +1,189 @@ language: c +cache: ccache +os: + - linux + - osx dist: xenial - +osx_image: xcode10.2 +compiler: + - clang + - gcc addons: apt: packages: - - cmake - - yasm + - cmake + - yasm + - valgrind + - libgstreamer-plugins-base1.0-dev + - libgstreamer1.0-dev + - llvm + - clang homebrew: packages: + - ccache - yasm - -jobs: +notifications: + webhooks: + - https://coveralls.io/webhook +env: + - build_type=release +stages: + - name: style + - name: test + - name: Coveralls And Valgrind + if: type != pull_request +before_install: + - "sudo chown -R travis: $HOME/.ccache" + - export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig PATH="/usr/local/opt/ccache/libexec:$PATH" + - wget -nc https://raw.githubusercontent.com/OpenVisualCloud/SVT-AV1-Resources/master/video.tar.gz || wget -nc http://randomderp.com/video.tar.gz + - tar xf video.tar.gz + - mkdir -p $TRAVIS_BUILD_DIR/Build/linux/$build_type +install: + - pip install --user cpp-coveralls + - export PATH=/Users/travis/Library/Python/2.7/bin:${PATH} + - if [ $TRAVIS_OS_NAME = osx ]; then ulimit -n 512; fi + #- if [ $TRAVIS_OS_NAME = osx ]; then brew install --HEAD valgrind; fi +script: + - &base_script | + cd $TRAVIS_BUILD_DIR/Build/linux/$build_type + cmake $TRAVIS_BUILD_DIR -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=$build_type ${CMAKE_EFLAGS[@]} + cmake -j $(if [ $TRAVIS_OS_NAME = osx ]; then sysctl -n hw.ncpu; else nproc; fi) --build . && + sudo cmake --build . --target install && cd $TRAVIS_BUILD_DIR + - SvtHevcEncApp -encMode 9 -i akiyo_cif.y4m -b test1.h265 +before_cache: + - "sudo chown -R travis: $HOME/.ccache" +matrix: + fast_finish: true + allow_failures: + - name: Style check + - name: Binary Identical? + - os: osx + - name: Valgrind + - env: COVERALLS_PARALLEL=true build_type=debug CMAKE_EFLAGS="-DCOVERAGE=ON" + exclude: + - os: osx + compiler: gcc # gcc = clang in macOS, unecessary duplicate include: - # General Linux build job - - name: Build - script: - - cd Build/linux - - ./build.sh release - # General macOS build job - - name: macOS build - os: osx - script: - - cd Build/linux - - ./build.sh release - # Coveralls test job - - name: Test & Coveralls - before_install: - - pip install --user cpp-coveralls - script: - - cd Build/linux - - ./build.sh release - after_success: - - coveralls - + # valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./SvtHevcEncApp -help + # Coding style check + - name: Style check + stage: style + addons: skip + before_install: skip + install: skip + script: + - git grep -InP --heading "\t|\r| $" -- . && IS_FAIL=true || true + - git grep -I -l -z "" -- . | while IFS= read -r -d '' i; do + if [ -n "$(tail -c 1 "$i")" ]; then + echo "No newline at end of $i"; + IS_FAIL=true; + fi; + done + - git remote rm upstream 2> /dev/null || true + - git remote add upstream https://github.com/OpenVisualCloud/SVT-HEVC.git + - git fetch -q upstream master + - for i in $(git rev-list HEAD ^upstream/master); do + echo "Checking commit message of $i"; + msg="$(git log --format=%B -n 1 $i)"; + if [ -n "$(echo "$msg" | awk "NR==2")" ]; then + echo "Malformed commit message in $i, second line must be empty"; + IS_FAIL=true; + fi; + if echo "$msg" | head -1 | grep -q '\.$'; then + echo "Malformed commit message in $i, trailing period in subject line"; + IS_FAIL=true; + fi; + done + - test -z "$IS_FAIL" # FFmpeg interation build - - name: FFmpeg patch - script: - # Build and install SVT-HEVC - - cd $TRAVIS_BUILD_DIR - - cd Build - - cmake .. - - make -j$(nproc) - - sudo make install - # Apply SVT-HEVC plugin and enable libsvthevc to FFmpeg - - git clone https://github.com/FFmpeg/FFmpeg ffmpeg - - cd ffmpeg - - git checkout release/4.1 - - git apply $TRAVIS_BUILD_DIR/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch - - git apply $TRAVIS_BUILD_DIR/ffmpeg_plugin/0002-doc-Add-libsvt_hevc-encoder-docs.patch - - export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib - - export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig - - ./configure --enable-libsvthevc - - make --quiet -j$(nproc) + - name: FFmpeg patch + stage: test + compiler: gcc + env: build_type=release + script: + # Build and install SVT-HEVC + - *base_script + # Apply SVT-HEVC plugin and enable libsvthevc to FFmpeg + - git clone https://github.com/FFmpeg/FFmpeg --branch release/4.1 --depth=1 ffmpeg && cd ffmpeg + - git am $TRAVIS_BUILD_DIR/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch + - git am $TRAVIS_BUILD_DIR/ffmpeg_plugin/0002-doc-Add-libsvt_hevc-encoder-docs.patch + - export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig + - "sudo chown -R travis: $HOME/.ccache" + - ./configure --enable-libsvthevc || less ffbuild/config.log + - make --quiet -j$(nproc) && sudo make install + # GStreamer interation build + - name: GStreamer patch + stage: test + compiler: gcc + env: build_type=release + script: + # Build and install SVT-HEVC + - *base_script + # Build GST-SVT-HEVC plugin + - cd $TRAVIS_BUILD_DIR/gstreamer-plugin + - "sudo chown -R travis: $HOME/.ccache" + - cmake . + - make --quiet -j$(nproc) + - sudo make install + # Tests if .h265 files are identical on binary level + - name: Binary Identical? + stage: test + script: + - mv -t $HOME akiyo_cif.y4m + - *base_script + - cd $HOME + - SvtHevcEncApp -encMode 9 -i akiyo_cif.y4m -n 120 -b test-pr-m9.h265 + - SvtHevcEncApp -encMode 0 -i akiyo_cif.y4m -n 3 -b test-pr-m0.h265 + - rm -rf $TRAVIS_BUILD_DIR + - git clone --depth 1 https://github.com/OpenVisualCloud/SVT-HEVC.git $TRAVIS_BUILD_DIR && cd $TRAVIS_BUILD_DIR + - *base_script + - mkdir -p $TRAVIS_BUILD_DIR/Build/linux/$build_type + - cd $HOME + - SvtHevcEncApp -encMode 9 -i akiyo_cif.y4m -n 120 -b test-master-m9.h265 + - SvtHevcEncApp -encMode 0 -i akiyo_cif.y4m -n 3 -b test-master-m0.h265 + - diff test-pr-m9.h265 test-master-m9.h265 + - diff test-pr-m0.h265 test-master-m0.h265 + - name: Coveralls Linux+gcc + stage: Coveralls And Valgrind + os: linux + compiler: gcc + env: COVERALLS_PARALLEL=true build_type=debug CMAKE_EFLAGS="-DCOVERAGE=ON" + script: + - *base_script + - &coveralls_script | + SvtHevcEncApp -encMode 9 -i akiyo_cif.y4m -b test1.h265 + git clone https://github.com/FFmpeg/FFmpeg ffmpeg --depth=1 --branch release/4.1 + cd ffmpeg + git am $TRAVIS_BUILD_DIR/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch && + git am $TRAVIS_BUILD_DIR/ffmpeg_plugin/0002-doc-Add-libsvt_hevc-encoder-docs.patch + sudo chown -R travis: $HOME/.ccache + ./configure --enable-libsvthevc || cat ffbuild/config.log + make -s -j$(if [ $TRAVIS_OS_NAME = osx ]; then sysctl -n hw.ncpu; else nproc; fi) >/dev/null && + sudo make install + cd $TRAVIS_BUILD_DIR + ffmpeg -i akiyo_cif.y4m -c:v libsvt_hevc test.h265 + SvtHevcEncApp -i stdin -w 352 -h 288 -fps 30 -n 150 -b test2.h265 < akiyo_cif.y4m + after_script: &after_coveralls_script | + if [ $CC = "clang" ] && [ $TRAVIS_OS_NAME = linux ]; then + GCOV_FILE=llvm-cov GCOV_OPTIONS='gcov -pl' + else + GCOV_FILE=gcov GCOV_OPTIONS='\-lp' + fi + coveralls --root $TRAVIS_BUILD_DIR -i Source -E ".*CMakeFiles.*" -E ".*ffmpeg.*" --gcov $GCOV_FILE --gcov-options "$GCOV_OPTIONS" + - name: Coveralls osx+clang + stage: Coveralls And Valgrind + os: osx + compiler: clang + env: COVERALLS_PARALLEL=true build_type=debug CMAKE_EFLAGS="-DCOVERAGE=ON" + script: + - *base_script + - *coveralls_script + after_script: *after_coveralls_script + - name: Valgrind + stage: Coveralls And Valgrind + compiler: gcc + os: linux + env: build_type=debug + script: + - *base_script + - valgrind -- SvtHevcEncApp -encMode 9 -i akiyo_cif.y4m -n 150 -b test1.h265 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ba54d7c6..47f53b397 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,13 @@ if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) endif() set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_C_STANDARD 99) +if(CMAKE_VERSION VERSION_LESS "3.1") + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + set(CMAKE_C_FLAGS "--std=gnu99 ${CMAKE_C_FLAGS}") + endif() +else() + set(CMAKE_C_STANDARD 99) +endif() set(CAN_USE_ASSEMBLER TRUE) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -91,7 +97,7 @@ if(MSVC) set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od") else() - option(NATIVE "Build for native performance (march=native)" ON) + option(NATIVE "Build for native performance (march=native)") list(INSERT flags_to_test 0 -Wall) #for Mingw64 support, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65782 if(MINGW) diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index ee813c000..9698e9e40 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -324,11 +324,9 @@ typedef struct EB_H265_ENC_CONFIGURATION * Default is null.*/ uint8_t useQpFile; -#if 1//TILES uint8_t tileColumnCount; uint8_t tileRowCount; uint8_t tileSliceMode; -#endif // Deblock Filter diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index 06416dbaa..c1af7392b 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -37,11 +37,9 @@ #define BASE_LAYER_SWITCH_MODE_TOKEN "-base-layer-switch-mode" // no Eval #define QP_TOKEN "-q" #define USE_QP_FILE_TOKEN "-use-q-file" -#if 1//TILES #define TILE_ROW_COUNT_TOKEN "-tile_row_cnt" #define TILE_COL_COUNT_TOKEN "-tile_col_cnt" #define TILE_SLICE_MODE_TOKEN "-tile_slice_mode" -#endif #define TUNE_TOKEN "-tune" #define FRAME_RATE_TOKEN "-fps" #define FRAME_RATE_NUMERATOR_TOKEN "-fps-num" @@ -206,11 +204,9 @@ static void SetCfgPredStructure (const char *value, EbConfig_t * static void SetCfgQp (const char *value, EbConfig_t *cfg) {cfg->qp = strtoul(value, NULL, 0);}; static void SetCfgCrf (const char *value, EbConfig_t *cfg) {cfg->crf = strtoul(value, NULL, 0); }; static void SetCfgUseQpFile (const char *value, EbConfig_t *cfg) {cfg->useQpFile = (EB_BOOL)strtol(value, NULL, 0); }; -#if 1//TILES static void SetCfgTileColumnCount (const char *value, EbConfig_t *cfg) { cfg->tileColumnCount = (EB_BOOL)strtol(value, NULL, 0); }; static void SetCfgTileRowCount (const char *value, EbConfig_t *cfg) { cfg->tileRowCount = (EB_BOOL)strtol(value, NULL, 0); }; static void SetCfgTileSliceMode (const char *value, EbConfig_t *cfg) { cfg->tileSliceMode = (EB_BOOL)strtol(value, NULL, 0); }; -#endif static void SetDisableDlfFlag (const char *value, EbConfig_t *cfg) {cfg->disableDlfFlag = (EB_BOOL)strtoul(value, NULL, 0);}; static void SetEnableSaoFlag (const char *value, EbConfig_t *cfg) {cfg->enableSaoFlag = (EB_BOOL)strtoul(value, NULL, 0);}; static void SetEnableHmeFlag (const char *value, EbConfig_t *cfg) {cfg->enableHmeFlag = (EB_BOOL)strtoul(value, NULL, 0);}; @@ -305,11 +301,9 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, USE_QP_FILE_TOKEN, "UseQpFile", SetCfgUseQpFile }, { SINGLE_INPUT, QP_FILE_TOKEN, "QpFile", SetCfgQpFile }, -#if 1//TILES { SINGLE_INPUT, TILE_ROW_COUNT_TOKEN, "TileRowCount", SetCfgTileRowCount }, { SINGLE_INPUT, TILE_COL_COUNT_TOKEN, "TileColumnCount", SetCfgTileColumnCount }, { SINGLE_INPUT, TILE_SLICE_MODE_TOKEN, "TileSliceMode", SetCfgTileSliceMode }, -#endif // Encoding Presets { SINGLE_INPUT, ENCMODE_TOKEN, "EncoderMode", SetencMode }, @@ -468,11 +462,9 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->useQpFile = EB_FALSE; configPtr->qpFile = NULL; -#if 1//TILES configPtr->tileColumnCount = 1; configPtr->tileRowCount = 1; configPtr->tileSliceMode = 0; -#endif configPtr->sceneChangeDetection = 1; configPtr->rateControlMode = 0; configPtr->lookAheadDistance = (uint32_t)~0; @@ -972,7 +964,6 @@ static EB_ERRORTYPE VerifySettings(EbConfig_t *config, uint32_t channelNumber) return_error = EB_ErrorBadParameter; } -#if 1//TILES #define MAX_LCU_SIZE 64 @@ -1021,7 +1012,6 @@ static EB_ERRORTYPE VerifySettings(EbConfig_t *config, uint32_t channelNumber) } } -#endif if (config->separateFields > 1) { fprintf(config->errorLogFile, "SVT [Error]: Instance %u: Invalid SeperateFields Input\n", channelNumber + 1); diff --git a/Source/App/EbAppConfig.h b/Source/App/EbAppConfig.h index fd4a58a26..7a479891b 100644 --- a/Source/App/EbAppConfig.h +++ b/Source/App/EbAppConfig.h @@ -242,11 +242,9 @@ typedef struct EbConfig_s unsigned char y4m_buf[9]; EB_BOOL useQpFile; -#if 1//TILES uint8_t tileColumnCount; uint8_t tileRowCount; uint8_t tileSliceMode; -#endif int32_t frameRate; int32_t frameRateNumerator; int32_t frameRateDenominator; diff --git a/Source/App/EbAppContext.c b/Source/App/EbAppContext.c index 8a2b2569d..7097cf2a4 100644 --- a/Source/App/EbAppContext.c +++ b/Source/App/EbAppContext.c @@ -189,11 +189,9 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.vbvEndFrameAdjust = config->vbvEndFrameAdjust; callbackData->ebEncParameters.lowLevelVbv = config->lowLevelVbv; callbackData->ebEncParameters.useQpFile = (EB_BOOL)config->useQpFile; -#if 1//TILES callbackData->ebEncParameters.tileColumnCount = (EB_BOOL)config->tileColumnCount; callbackData->ebEncParameters.tileRowCount = (EB_BOOL)config->tileRowCount; callbackData->ebEncParameters.tileSliceMode = (EB_BOOL)config->tileSliceMode; -#endif callbackData->ebEncParameters.disableDlfFlag = (EB_BOOL)config->disableDlfFlag; callbackData->ebEncParameters.enableSaoFlag = (EB_BOOL)config->enableSaoFlag; callbackData->ebEncParameters.hrdFlag = (EB_BOOL)config->hrdFlag; diff --git a/Source/Lib/ASM_AVX2/EbComputeSAD_AVX2.h b/Source/Lib/ASM_AVX2/EbComputeSAD_AVX2.h index b47d9734d..57091cd83 100644 --- a/Source/Lib/ASM_AVX2/EbComputeSAD_AVX2.h +++ b/Source/Lib/ASM_AVX2/EbComputeSAD_AVX2.h @@ -28,6 +28,13 @@ EB_U32 Compute32xMSad_AVX2_INTRIN( EB_U32 height, // input parameter, block height (M) EB_U32 width); // input parameter, block width (N) +EB_U32 Compute40xMSad_AVX2_INTRIN( + EB_U8 *src, // input parameter, source samples Ptr + EB_U32 srcStride, // input parameter, source stride + EB_U8 *ref, // input parameter, reference samples Ptr + EB_U32 refStride, // input parameter, reference stride + EB_U32 height, // input parameter, block height (M) + EB_U32 width); // input parameter, block width (N) EB_U32 Compute48xMSad_AVX2_INTRIN( EB_U8 *src, // input parameter, source samples Ptr @@ -37,6 +44,14 @@ EB_U32 Compute48xMSad_AVX2_INTRIN( EB_U32 height, // input parameter, block height (M) EB_U32 width); // input parameter, block width (N) +EB_U32 Compute56xMSad_AVX2_INTRIN( + EB_U8 *src, // input parameter, source samples Ptr + EB_U32 srcStride, // input parameter, source stride + EB_U8 *ref, // input parameter, reference samples Ptr + EB_U32 refStride, // input parameter, reference stride + EB_U32 height, // input parameter, block height (M) + EB_U32 width); // input parameter, block width (N) + EB_U32 Compute64xMSad_AVX2_INTRIN( EB_U8 *src, // input parameter, source samples Ptr EB_U32 srcStride, // input parameter, source stride diff --git a/Source/Lib/ASM_AVX2/EbComputeSAD_Intrinsic_AVX2.c b/Source/Lib/ASM_AVX2/EbComputeSAD_Intrinsic_AVX2.c index a3611dee0..9c5edbeec 100644 --- a/Source/Lib/ASM_AVX2/EbComputeSAD_Intrinsic_AVX2.c +++ b/Source/Lib/ASM_AVX2/EbComputeSAD_Intrinsic_AVX2.c @@ -67,7 +67,7 @@ void SadLoopKernel_AVX2_INTRIN( pRef = ref + j; ss3 = ss5 = _mm256_setzero_si256(); for (k=0; klcuPtrArray[tbAddr]; -#if TILES const EB_BOOL pictureLeftBoundary = (lcuPtr->tileLeftEdgeFlag == EB_TRUE && (originX & (lcuSize - 1)) == 0) ? EB_TRUE : EB_FALSE; const EB_BOOL pictureTopBoundary = (lcuPtr->tileTopEdgeFlag == EB_TRUE && (originY & (lcuSize - 1)) == 0) ? EB_TRUE : EB_FALSE; const EB_BOOL pictureRightBoundary = (lcuPtr->tileRightEdgeFlag == EB_TRUE && ((originX + width) & (lcuSize - 1)) == 0) ? EB_TRUE : EB_FALSE; -#else - const EB_BOOL pictureLeftBoundary = (lcuPtr->pictureLeftEdgeFlag == EB_TRUE && (originX & (lcuSize-1)) == 0) ? EB_TRUE : EB_FALSE; - const EB_BOOL pictureTopBoundary = (lcuPtr->pictureTopEdgeFlag == EB_TRUE && (originY & (lcuSize-1)) == 0) ? EB_TRUE : EB_FALSE; - const EB_BOOL pictureRightBoundary = - (lcuPtr->pictureRightEdgeFlag == EB_TRUE && ((originX + width) & (lcuSize-1)) == 0) ? EB_TRUE : EB_FALSE; -#endif + EB_BOOL a0_availability; EB_BOOL a1_availability; EB_BOOL b0_availability; @@ -2182,17 +2176,12 @@ EB_ERRORTYPE GenerateL0L1AmvpMergeLists( originX, originY); const LargestCodingUnit_t *lcuPtr = pictureControlSetPtr->lcuPtrArray[tbAddr]; -#if TILES + const EB_BOOL pictureLeftBoundary = (lcuPtr->tileLeftEdgeFlag == EB_TRUE && (originX & (64 - 1)) == 0) ? EB_TRUE : EB_FALSE; const EB_BOOL pictureTopBoundary = (lcuPtr->tileTopEdgeFlag == EB_TRUE && (originY & (64 - 1)) == 0) ? EB_TRUE : EB_FALSE; const EB_BOOL pictureRightBoundary = (lcuPtr->tileRightEdgeFlag == EB_TRUE && ((originX + width) & (64 - 1)) == 0) ? EB_TRUE : EB_FALSE; -#else - const EB_BOOL pictureLeftBoundary = (lcuPtr->pictureLeftEdgeFlag == EB_TRUE && (originX & (63)) == 0) ? EB_TRUE : EB_FALSE; - const EB_BOOL pictureTopBoundary = (lcuPtr->pictureTopEdgeFlag == EB_TRUE && (originY & (63)) == 0) ? EB_TRUE : EB_FALSE; - const EB_BOOL pictureRightBoundary = - (lcuPtr->pictureRightEdgeFlag == EB_TRUE && ((originX + width) & (63)) == 0) ? EB_TRUE : EB_FALSE; -#endif + EB_BOOL a0_availability; EB_BOOL a1_availability; EB_BOOL b0_availability; diff --git a/Source/Lib/Codec/EbCodingLoop.c b/Source/Lib/Codec/EbCodingLoop.c index eed565ad2..ce410176e 100644 --- a/Source/Lib/Codec/EbCodingLoop.c +++ b/Source/Lib/Codec/EbCodingLoop.c @@ -22,6 +22,142 @@ #include "EbModeDecisionConfiguration.h" #include "emmintrin.h" +//#define DEBUG_REF_INFO +//#define DUMP_RECON +#ifdef DUMP_RECON +static void dump_buf_desc_to_file(EbPictureBufferDesc_t* reconBuffer, const char* filename, int POC) +{ + if (POC == 0) { + FILE* tmp=fopen(filename, "w"); + fclose(tmp); + } + FILE* fp = fopen(filename, "r+"); + assert(fp); + long descSize = reconBuffer->height * reconBuffer->width; //Luma + descSize += 2 * ((reconBuffer->height * reconBuffer->width) >> (3 - reconBuffer->colorFormat)); + long offset = descSize * POC; + fseek(fp, 0, SEEK_END); + long fileSize = ftell(fp); + if (offset > fileSize) { + int count = (offset - fileSize) / descSize; + char *tmpBuf = (char*)malloc(descSize); + for (int i=0;icolorFormat; // Chroma format + EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + unsigned char* luma_ptr = reconBuffer->bufferY + reconBuffer->strideY*(reconBuffer->originY) + reconBuffer->originX; + unsigned char* cb_ptr = reconBuffer->bufferCb + reconBuffer->strideCb*(reconBuffer->originY>>subHeightCMinus1) + (reconBuffer->originX>>subWidthCMinus1); + unsigned char* cr_ptr = reconBuffer->bufferCr + reconBuffer->strideCr*(reconBuffer->originY>>subHeightCMinus1) + (reconBuffer->originX>>subWidthCMinus1); + for (int i=0;iheight;i++) { + fwrite(luma_ptr, 1, reconBuffer->width, fp); + luma_ptr += reconBuffer->strideY; + } + + for (int i=0;iheight>>subHeightCMinus1;i++) { + fwrite(cb_ptr, 1, reconBuffer->width>>subWidthCMinus1, fp); + cb_ptr += reconBuffer->strideCb; + } + + for (int i=0;iheight>>subHeightCMinus1;i++) { + fwrite(cr_ptr, 1, reconBuffer->width>>subWidthCMinus1, fp); + cr_ptr += reconBuffer->strideCr; + } + fseek(fp, 0, SEEK_END); + //printf("After write POC %d, filesize %d\n", POC, ftell(fp)); + fclose(fp); + +} +#endif + +#ifdef DEBUG_REF_INFO +static void dump_left_array(NeighborArrayUnit_t *neighbor, int y_pos, int size) +{ + printf("*Dump left array\n"); + for (int i=0; ileftArray[i+y_pos]); + } + printf("\n----------------------\n"); +} + +static void dump_intra_ref(IntraReferenceSamples_t* ref, int size, int mask) +{ + unsigned char* ptr = NULL; + if (mask==0) { + ptr = ref->yIntraReferenceArray; + } else if (mask == 1) { + ptr = ref->cbIntraReferenceArray; + } else if (mask ==2) { + ptr = ref->crIntraReferenceArray; + } else { + assert(0); + } + + printf("*Dumping intra reference array for component %d\n", mask); + for (int i=0; ibitDepth; + int val=(bitDepth==8)?1:2; + EB_COLOR_FORMAT colorFormat = buf_tmp->colorFormat; // Chroma format + EB_U16 subWidthCMinus1 = (colorFormat==EB_YUV444?1:2)-1; + EB_U16 subHeightCMinus1 = (colorFormat>=EB_YUV422?1:2)-1; + if (componentMask ==0) { + buf=buf_tmp->bufferY; + stride=buf_tmp->strideY; + subWidthCMinus1=0; + subHeightCMinus1=0; + } else if (componentMask == 1) { + buf=buf_tmp->bufferCb; + stride=buf_tmp->strideCb; + } else if (componentMask == 2) { + buf=buf_tmp->bufferCr; + stride=buf_tmp->strideCr; + } else { + assert(0); + } + + int offset=((stride*(buf_tmp->originY+startY))>>subHeightCMinus1) +((startX+buf_tmp->originX)>>subWidthCMinus1); + printf("bitDepth is %d, dump block size %d at offset %d, (%d, %d), component is %s\n", + bitDepth, size, offset, startX, startY, componentMask==0?"luma":(componentMask==1?"Cb":"Cr")); + unsigned char* start_tmp=buf+offset*val; + for (int i=0;ipictureControlSetPtr->pictureNumber == 0) { + { + int chroma_size = tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize; + + printf("\n----- Dump coeff for 1st loop at (%d, %d), qp is %d -----\n", originX, originY, qp); + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { + dump_block_from_desc(tuSize, coeffSamplesTB, originX&63, originY&63, 0); + } + //if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { + // dump_block_from_desc(chroma_size, coeffSamplesTB, originX&63, originY&63, 1); + //} + + printf("\n----- Dump residual for 1st loop at (%d, %d)-----\n", originX, originY); + if (componentMask & PICTURE_BUFFER_DESC_LUMA_MASK) { + dump_block_from_desc(tuSize, residual16bit, originX&63, originY&63, 0); + } + //if (componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) { + // dump_block_from_desc(chroma_size, residual16bit, originX&63, originY&63, 1); + //} + } + } +#endif if ((componentMask & PICTURE_BUFFER_DESC_CHROMA_MASK) && secondChroma) { tuPtr->nzCoefCount2[0] = (EB_U16)countNonZeroCoeffs[1]; @@ -1723,8 +1882,8 @@ static void EncodePassMvPrediction( contextPtr->cuPtr->predictionUnitArray->interPredDirectionIndex == BI_PRED) { FillAMVPCandidates( - pictureControlSetPtr->epMvNeighborArray, - pictureControlSetPtr->epModeTypeNeighborArray, + pictureControlSetPtr->epMvNeighborArray[contextPtr->tileIndex], + pictureControlSetPtr->epModeTypeNeighborArray[contextPtr->tileIndex], contextPtr->cuOriginX, contextPtr->cuOriginY, contextPtr->cuStats->size, @@ -1770,8 +1929,8 @@ static void EncodePassMvPrediction( contextPtr->cuPtr->predictionUnitArray->interPredDirectionIndex == BI_PRED) { FillAMVPCandidates( - pictureControlSetPtr->epMvNeighborArray, - pictureControlSetPtr->epModeTypeNeighborArray, + pictureControlSetPtr->epMvNeighborArray[contextPtr->tileIndex], + pictureControlSetPtr->epModeTypeNeighborArray[contextPtr->tileIndex], contextPtr->cuOriginX, contextPtr->cuOriginY, contextPtr->cuStats->size, @@ -1830,10 +1989,8 @@ static void EncodePassUpdateQp( EB_U32 difCuDeltaQpDepth, EB_U8 *prevCodedQp, EB_U8 *prevQuantGroupCodedQp, -#if TILES EB_U32 tileOriginX, EB_U32 tileOriginY, -#endif EB_U32 lcuQp ) { @@ -1874,11 +2031,7 @@ static void EncodePassUpdateQp( *prevCodedQp = *prevQuantGroupCodedQp; } -#if TILES if ((quantGroupY > tileOriginY) && sameLcuCheckTop) { -#else - if (sameLcuCheckTop) { -#endif qpTopNeighborIndex = LUMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( quantGroupX, @@ -1890,11 +2043,7 @@ static void EncodePassUpdateQp( qpTopNeighbor = *prevCodedQp; } -#if TILES if ((quantGroupX > tileOriginX) && sameLcuCheckLeft) { -#else - if (sameLcuCheckLeft) { -#endif qpLeftNeighborIndex = LUMA_SAMPLE_PIC_WISE_LOCATION_TO_QP_ARRAY_IDX( quantGroupX - 1, @@ -2839,6 +2988,7 @@ EB_EXTERN void EncodePass( EB_COLOR_FORMAT colorFormat = contextPtr->colorFormat; const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; + EB_U32 tileIdx = contextPtr->tileIndex; EbPictureBufferDesc_t *reconBuffer = is16bit ? pictureControlSetPtr->reconPicture16bitPtr : pictureControlSetPtr->reconPicturePtr; EbPictureBufferDesc_t *coeffBufferTB = lcuPtr->quantizedCoeff; @@ -2898,14 +3048,14 @@ EB_EXTERN void EncodePass( EncodeContext_t *encodeContextPtr = NULL; // Dereferencing early - NeighborArrayUnit_t *epModeTypeNeighborArray = pictureControlSetPtr->epModeTypeNeighborArray; - NeighborArrayUnit_t *epIntraLumaModeNeighborArray = pictureControlSetPtr->epIntraLumaModeNeighborArray; - NeighborArrayUnit_t *epMvNeighborArray = pictureControlSetPtr->epMvNeighborArray; - NeighborArrayUnit_t *epLumaReconNeighborArray = is16bit ? pictureControlSetPtr->epLumaReconNeighborArray16bit : pictureControlSetPtr->epLumaReconNeighborArray; - NeighborArrayUnit_t *epCbReconNeighborArray = is16bit ? pictureControlSetPtr->epCbReconNeighborArray16bit : pictureControlSetPtr->epCbReconNeighborArray; - NeighborArrayUnit_t *epCrReconNeighborArray = is16bit ? pictureControlSetPtr->epCrReconNeighborArray16bit : pictureControlSetPtr->epCrReconNeighborArray; - NeighborArrayUnit_t *epSkipFlagNeighborArray = pictureControlSetPtr->epSkipFlagNeighborArray; - NeighborArrayUnit_t *epLeafDepthNeighborArray = pictureControlSetPtr->epLeafDepthNeighborArray; + NeighborArrayUnit_t *epModeTypeNeighborArray = pictureControlSetPtr->epModeTypeNeighborArray[tileIdx]; + NeighborArrayUnit_t *epIntraLumaModeNeighborArray = pictureControlSetPtr->epIntraLumaModeNeighborArray[tileIdx]; + NeighborArrayUnit_t *epMvNeighborArray = pictureControlSetPtr->epMvNeighborArray[tileIdx]; + NeighborArrayUnit_t *epLumaReconNeighborArray = is16bit ? pictureControlSetPtr->epLumaReconNeighborArray16bit[tileIdx] : pictureControlSetPtr->epLumaReconNeighborArray[tileIdx]; + NeighborArrayUnit_t *epCbReconNeighborArray = is16bit ? pictureControlSetPtr->epCbReconNeighborArray16bit[tileIdx] : pictureControlSetPtr->epCbReconNeighborArray[tileIdx]; + NeighborArrayUnit_t *epCrReconNeighborArray = is16bit ? pictureControlSetPtr->epCrReconNeighborArray16bit[tileIdx] : pictureControlSetPtr->epCrReconNeighborArray[tileIdx]; + NeighborArrayUnit_t *epSkipFlagNeighborArray = pictureControlSetPtr->epSkipFlagNeighborArray[tileIdx]; + NeighborArrayUnit_t *epLeafDepthNeighborArray = pictureControlSetPtr->epLeafDepthNeighborArray[tileIdx]; EB_BOOL constrainedIntraFlag = pictureControlSetPtr->constrainedIntraFlag; EB_BOOL enableStrongIntraSmoothing = sequenceControlSetPtr->enableStrongIntraSmoothing; @@ -3034,6 +3184,12 @@ EB_EXTERN void EncodePass( contextPtr->cuOriginX = (EB_U16)(lcuOriginX + cuStats->originX); contextPtr->cuOriginY = (EB_U16)(lcuOriginY + cuStats->originY); + EB_BOOL tileLeftBoundary = (lcuPtr->tileLeftEdgeFlag == EB_TRUE && ((contextPtr->cuOriginX & (lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; + EB_BOOL tileTopBoundary = (lcuPtr->tileTopEdgeFlag == EB_TRUE && ((contextPtr->cuOriginY & (lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; + EB_BOOL tileRightBoundary = (lcuPtr->tileRightEdgeFlag == EB_TRUE && (((contextPtr->cuOriginX + cuStats->size) & (lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; + //printf("LCU (%d, %d), left/top/right boundary %d/%d/%d\n", lcuOriginX, lcuOriginY, + // tileLeftBoundary, tileTopBoundary, tileRightBoundary); + EncodePassPreFetchRef( pictureControlSetPtr, contextPtr, @@ -3084,6 +3240,20 @@ EB_EXTERN void EncodePass( cbQp = MIN(qpScaled, 51); } + //if (pictureControlSetPtr->pictureNumber == 1) { + // printf("POC %d, ", pictureControlSetPtr->pictureNumber); + // if (cuPtr->predictionModeFlag == INTRA_MODE) { + // printf("(%d, %d), pu size %d, intraLumaMode %d\n", + // contextPtr->cuOriginX, contextPtr->cuOriginY, cuStats->size, cuPtr->predictionUnitArray->intraLumaMode); + // } else { + // printf("(%d, %d), pu size %d, inter mode, merge flag %d, mvp (%d, %d), tileIdx %d\n", + // contextPtr->cuOriginX, contextPtr->cuOriginY, cuStats->size, + // cuPtr->predictionUnitArray[0].mergeFlag, + // cuPtr->predictionUnitArray->mv[0].x, + // cuPtr->predictionUnitArray->mv[0].y, tileIdx); + // } + //} + if (cuPtr->predictionModeFlag == INTRA_MODE && cuPtr->predictionUnitArray->intraLumaMode != EB_INTRA_MODE_4x4) { @@ -3109,12 +3279,6 @@ EB_EXTERN void EncodePass( epModeTypeNeighborArray); -#if TILES - EB_BOOL tileLeftBoundary = (lcuPtr->tileLeftEdgeFlag == EB_TRUE && ((contextPtr->cuOriginX & (lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; - EB_BOOL tileTopBoundary = (lcuPtr->tileTopEdgeFlag == EB_TRUE && ((contextPtr->cuOriginY & (lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; - EB_BOOL tileRightBoundary = (lcuPtr->tileRightEdgeFlag == EB_TRUE && (((contextPtr->cuOriginX + cuStats->size) & (lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; - -#endif // Transform Loop (not supported) { // Generate Intra Reference Samples @@ -3133,13 +3297,7 @@ EB_EXTERN void EncodePass( epCrReconNeighborArray, is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, colorFormat, -#if TILES tileLeftBoundary, tileTopBoundary, tileRightBoundary); -#else - (contextPtr->cuOriginX == 0), - (contextPtr->cuOriginY == 0), - (contextPtr->cuOriginX + cuStats->size) == sequenceControlSetPtr->lumaWidth ? EB_TRUE : EB_FALSE); -#endif } else if (colorFormat == EB_YUV422 || colorFormat == EB_YUV444) { //Jing: TODO, add tiles support GenerateLumaIntraReferenceSamplesFuncTable[is16bit]( @@ -3155,13 +3313,7 @@ EB_EXTERN void EncodePass( epCbReconNeighborArray, epCrReconNeighborArray, is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, -#if TILES tileLeftBoundary, tileTopBoundary, tileRightBoundary); -#else - (contextPtr->cuOriginX == 0), - (contextPtr->cuOriginY == 0), - (contextPtr->cuOriginX + cuStats->size) == sequenceControlSetPtr->lumaWidth ? EB_TRUE : EB_FALSE); -#endif GenerateChromaIntraReferenceSamplesFuncTable[is16bit]( constrainedIntraFlag, @@ -3178,13 +3330,7 @@ EB_EXTERN void EncodePass( is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, colorFormat, EB_FALSE, -#if TILES tileLeftBoundary, tileTopBoundary, tileRightBoundary); -#else - (contextPtr->cuOriginX == 0), - (contextPtr->cuOriginY == 0), - (contextPtr->cuOriginX + cuStats->size) == sequenceControlSetPtr->lumaWidth ? EB_TRUE : EB_FALSE); -#endif } // Prediction @@ -3200,18 +3346,23 @@ EB_EXTERN void EncodePass( (EB_U32)puPtr->intraLumaMode, EB_INTRA_CHROMA_DM, PICTURE_BUFFER_DESC_FULL_MASK ); + +#ifdef DEBUG_REF_INFO + int originX = contextPtr->cuOriginX; + int originY = contextPtr->cuOriginY; + int tuSize = cuStats->size; + printf("\n----- Dump prediction for 1st loop at (%d, %d)-----\n", originX, originY); + + int chroma_size = tuSize > MIN_PU_SIZE? (tuSize >> subWidthCMinus1): tuSize; + + //dump_block_from_desc(chroma_size, reconBuffer, originX, originY, 1); + dump_block_from_desc(tuSize, reconBuffer, originX, originY, 0); +#endif // Encode Transform Unit -INTRA- { -#if TILES contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? EB_FALSE : lcuPtr->tileLeftEdgeFlag && ((contextPtr->cuOriginX & (63)) == 0) && (contextPtr->cuOriginY == lcuOriginY); -#else - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? - EB_FALSE : - lcuPtr->pictureLeftEdgeFlag && ((contextPtr->cuOriginX & (63)) == 0) && (contextPtr->cuOriginY == lcuOriginY); -#endif - SetPmEncDecMode( pictureControlSetPtr, contextPtr, @@ -3309,13 +3460,7 @@ EB_EXTERN void EncodePass( is16bit ? (void*)contextPtr->intraRefPtr16 : (void*)contextPtr->intraRefPtr, colorFormat, EB_TRUE, -#if TILES tileLeftBoundary, EB_FALSE, tileRightBoundary); -#else - (contextPtr->cuOriginX == 0), - EB_FALSE, - (contextPtr->cuOriginX + cuStats->size) == sequenceControlSetPtr->lumaWidth ? EB_TRUE : EB_FALSE); -#endif // Prediction EncodePassIntraPredictionFuncTable[is16bit]( @@ -3408,6 +3553,8 @@ EB_EXTERN void EncodePass( cuStats, lcuOriginX, lcuOriginY, + tileLeftBoundary, + tileTopBoundary, pictureControlSetPtr, pictureControlSetPtr->horizontalEdgeBSArray[tbAddr], pictureControlSetPtr->verticalEdgeBSArray[tbAddr]); @@ -3434,15 +3581,10 @@ EB_EXTERN void EncodePass( EB_U16 partitionOriginX = contextPtr->cuOriginX + INTRA_4x4_OFFSET_X[partitionIndex]; EB_U16 partitionOriginY = contextPtr->cuOriginY + INTRA_4x4_OFFSET_Y[partitionIndex]; -#if TILES EB_BOOL pictureLeftBoundary = (lcuPtr->tileLeftEdgeFlag == EB_TRUE && ((partitionOriginX & (lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; EB_BOOL pictureTopBoundary = (lcuPtr->tileTopEdgeFlag == EB_TRUE && ((partitionOriginY & (lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; EB_BOOL pictureRightBoundary = (lcuPtr->tileRightEdgeFlag == EB_TRUE && (((partitionOriginX + MIN_PU_SIZE) & (lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; -#else - EB_BOOL pictureLeftBoundary = (lcuPtr->pictureLeftEdgeFlag == EB_TRUE && ((partitionOriginX & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; - EB_BOOL pictureTopBoundary = (lcuPtr->pictureTopEdgeFlag == EB_TRUE && ((partitionOriginY & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; - EB_BOOL pictureRightBoundary = (lcuPtr->pictureRightEdgeFlag == EB_TRUE && (((partitionOriginX + MIN_PU_SIZE) & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; -#endif + EB_U8 intraLumaMode = lcuPtr->intra4x4Mode[((MD_SCAN_TO_RASTER_SCAN[cuItr] - 21) << 2) + partitionIndex]; EB_U8 intraLumaModeForChroma = lcuPtr->intra4x4Mode[((MD_SCAN_TO_RASTER_SCAN[cuItr] - 21) << 2)]; @@ -3488,17 +3630,9 @@ EB_EXTERN void EncodePass( // only the right picture edge check and not the left or top boundary checks as the block size // has no influence on those checks. if (colorFormat == EB_YUV444) { -#if TILES pictureRightBoundary = (lcuPtr->tileRightEdgeFlag == EB_TRUE && (((partitionOriginX + MIN_PU_SIZE) & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; -#else - pictureRightBoundary = (lcuPtr->pictureRightEdgeFlag == EB_TRUE && (((partitionOriginX + MIN_PU_SIZE) & (MAX_LCU_SIZE - 1)) == 0)) ? EB_TRUE : EB_FALSE; -#endif } else { -#if TILES pictureRightBoundary = (lcuPtr->tileRightEdgeFlag == EB_TRUE && ((((partitionOriginX / 2) + MIN_PU_SIZE) & ((MAX_LCU_SIZE / 2) - 1)) == 0)) ? EB_TRUE : EB_FALSE; -#else - pictureRightBoundary = (lcuPtr->pictureRightEdgeFlag == EB_TRUE && ((((partitionOriginX / 2) + MIN_PU_SIZE) & ((MAX_LCU_SIZE / 2) - 1)) == 0)) ? EB_TRUE : EB_FALSE; -#endif } componentMask = PICTURE_BUFFER_DESC_FULL_MASK; GenerateChromaIntraReferenceSamplesFuncTable[is16bit]( @@ -3690,11 +3824,7 @@ EB_EXTERN void EncodePass( //if QPM and Segments are used, First Cu in LCU row should have at least one coeff. EB_BOOL isFirstCUinRow = (useDeltaQp == 1) && !singleSegment && -#if TILES lcuPtr->tileLeftEdgeFlag && ((contextPtr->cuOriginX & (63)) == 0) && (contextPtr->cuOriginY == lcuOriginY) ? EB_TRUE : EB_FALSE;; -#else - (contextPtr->cuOriginX == 0 && contextPtr->cuOriginY == lcuOriginY) ? EB_TRUE : EB_FALSE; -#endif //Motion Compensation could be avoided in the case below EB_BOOL doMC = EB_TRUE; @@ -3846,15 +3976,9 @@ EB_EXTERN void EncodePass( } //TU LOOP for MV mode + Luma CBF decision. -#if TILES contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)) ? EB_FALSE : lcuPtr->tileLeftEdgeFlag && ((tuOriginX & 63) == 0) && (tuOriginY == lcuOriginY); -#else - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate)) ? - EB_FALSE : - (tuOriginX == 0) && (tuOriginY == lcuOriginY); -#endif SetPmEncDecMode( pictureControlSetPtr, @@ -4041,16 +4165,9 @@ EB_EXTERN void EncodePass( cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf2 = EB_FALSE; cuPtr->transformUnitArray[contextPtr->tuItr].crCbf2 = EB_FALSE; } else if (cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) { - -#if TILES contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)) ? EB_FALSE : lcuPtr->tileLeftEdgeFlag && ((tuOriginX & 63) == 0) && (tuOriginY == lcuOriginY); -#else - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)) ? - EB_FALSE : - (tuOriginX == 0) && (tuOriginY == lcuOriginY); -#endif SetPmEncDecMode( pictureControlSetPtr, @@ -4145,16 +4262,9 @@ EB_EXTERN void EncodePass( cuPtr->transformUnitArray[0].cbCbf2 = EB_TRUE; } - -#if TILES contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? EB_FALSE : lcuPtr->tileLeftEdgeFlag && ((tuOriginX & 63) == 0) && (tuOriginY == lcuOriginY); -#else - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag) ? - EB_FALSE : - (tuOriginX == 0) && (tuOriginY == lcuOriginY); -#endif if (cuPtr->transformUnitArray[contextPtr->tuItr].crCbf) { cuPtr->transformUnitArray[0].crCbf = EB_TRUE; @@ -4253,6 +4363,8 @@ EB_EXTERN void EncodePass( cuStats, lcuOriginX, lcuOriginY, + tileLeftBoundary, + tileTopBoundary, pictureControlSetPtr, pictureControlSetPtr->horizontalEdgeBSArray[tbAddr], pictureControlSetPtr->verticalEdgeBSArray[tbAddr]); @@ -4338,22 +4450,6 @@ EB_EXTERN void EncodePass( } -#if TILES - EB_U16 tileOriginX = 0; - EB_U16 tileOriginY = 0; - { - unsigned i = 0, j = 0; - - while (i < sequenceControlSetPtr->tileColumnCount && tileOriginX + sequenceControlSetPtr->tileColumnArray[i] * MAX_LCU_SIZE < lcuOriginX) { - tileOriginX += sequenceControlSetPtr->tileColumnArray[i++] * MAX_LCU_SIZE; - } - - while (j < sequenceControlSetPtr->tileRowCount && tileOriginY + sequenceControlSetPtr->tileRowArray[j] * MAX_LCU_SIZE < lcuOriginY) { - tileOriginY += sequenceControlSetPtr->tileRowArray[j++] * MAX_LCU_SIZE; - } - - } -#endif // Assign the LCU-level QP EncodePassUpdateQp( pictureControlSetPtr, @@ -4362,12 +4458,10 @@ EB_EXTERN void EncodePass( useDeltaQp, &isDeltaQpNotCoded, pictureControlSetPtr->difCuDeltaQpDepth, - &(pictureControlSetPtr->encPrevCodedQp[singleSegment ? 0 : lcuRowIndex]), - &(pictureControlSetPtr->encPrevQuantGroupCodedQp[singleSegment ? 0 : lcuRowIndex]), -#if TILES - tileOriginX, - tileOriginY, -#endif + &(pictureControlSetPtr->encPrevCodedQp[tileIdx][singleSegment ? 0 : lcuRowIndex]), + &(pictureControlSetPtr->encPrevQuantGroupCodedQp[tileIdx][singleSegment ? 0 : lcuRowIndex]), + lcuPtr->tileOriginX, + lcuPtr->tileOriginY, lcuQp); // Assign DLF QP @@ -4493,6 +4587,9 @@ EB_EXTERN void EncodePass( } // CU Loop + contextPtr->codedLcuCount++; + //Jing: + //For true tile mode, need to change DLF accordingly // First Pass Deblocking if (dlfEnableFlag){ @@ -4516,8 +4613,10 @@ EB_EXTERN void EncodePass( lcuHeight, pictureControlSetPtr->verticalEdgeBSArray[tbAddr], pictureControlSetPtr->horizontalEdgeBSArray[tbAddr], - lcuOriginY == 0 ? (EB_U8*)EB_NULL : pictureControlSetPtr->verticalEdgeBSArray[tbAddr - pictureWidthInLcu], - lcuOriginX == 0 ? (EB_U8*)EB_NULL : pictureControlSetPtr->horizontalEdgeBSArray[tbAddr - 1], + //lcuOriginY == 0 ? (EB_U8*)EB_NULL : pictureControlSetPtr->verticalEdgeBSArray[tbAddr - pictureWidthInLcu], + //lcuOriginX == 0 ? (EB_U8*)EB_NULL : pictureControlSetPtr->horizontalEdgeBSArray[tbAddr - 1], + lcuPtr->tileTopEdgeFlag ? (EB_U8*)EB_NULL : pictureControlSetPtr->verticalEdgeBSArray[tbAddr - pictureWidthInLcu], + lcuPtr->tileLeftEdgeFlag ? (EB_U8*)EB_NULL : pictureControlSetPtr->horizontalEdgeBSArray[tbAddr - 1], pictureControlSetPtr); LcuPicEdgeDLFCoreFuncTable[is16bit]( @@ -4540,22 +4639,25 @@ EB_EXTERN void EncodePass( SaoParameters_t *leftSaoPtr; SaoParameters_t *topSaoPtr; - if (lcuOriginY != 0){ + //Jing: Double check for multi-tile + //if (lcuOriginY != 0){ + if (!lcuPtr->tileTopEdgeFlag) { EB_U32 topSaoIndex = GetNeighborArrayUnitTopIndex( - pictureControlSetPtr->epSaoNeighborArray, + pictureControlSetPtr->epSaoNeighborArray[tileIdx], lcuOriginX); - topSaoPtr = ((SaoParameters_t*)pictureControlSetPtr->epSaoNeighborArray->topArray) + topSaoIndex; + topSaoPtr = ((SaoParameters_t*)pictureControlSetPtr->epSaoNeighborArray[tileIdx]->topArray) + topSaoIndex; } else{ topSaoPtr = (SaoParameters_t*)EB_NULL; } - if (lcuOriginX != 0){ + //if (lcuOriginX != 0){ + if (!lcuPtr->tileLeftEdgeFlag) { EB_U32 leftSaoIndex = GetNeighborArrayUnitLeftIndex( - pictureControlSetPtr->epSaoNeighborArray, + pictureControlSetPtr->epSaoNeighborArray[tileIdx], lcuOriginY); - leftSaoPtr = ((SaoParameters_t*)pictureControlSetPtr->epSaoNeighborArray->leftArray) + leftSaoIndex; + leftSaoPtr = ((SaoParameters_t*)pictureControlSetPtr->epSaoNeighborArray[tileIdx]->leftArray) + leftSaoIndex; } else{ leftSaoPtr = (SaoParameters_t*)EB_NULL; @@ -4647,12 +4749,12 @@ EB_EXTERN void EncodePass( lcuPtr->saoParams.saoMergeUpFlag = EB_FALSE; } } - } + } } // Update the SAO Neighbor Array EncodePassUpdateSaoNeighborArrays( - pictureControlSetPtr->epSaoNeighborArray, + pictureControlSetPtr->epSaoNeighborArray[tileIdx], &lcuPtr->saoParams, lcuOriginX, lcuOriginY, diff --git a/Source/Lib/Codec/EbCodingUnit.h b/Source/Lib/Codec/EbCodingUnit.h index 25efb6723..54c8b5c23 100644 --- a/Source/Lib/Codec/EbCodingUnit.h +++ b/Source/Lib/Codec/EbCodingUnit.h @@ -198,7 +198,6 @@ typedef struct LargestCodingUnit_s { EB_U8 chromaEncodeMode; EB_INTRA4x4_SEARCH_METHOD intra4x4SearchMethod; -#if TILES // Tiles EB_BOOL tileLeftEdgeFlag; EB_BOOL tileTopEdgeFlag; @@ -207,8 +206,6 @@ typedef struct LargestCodingUnit_s { EB_U16 tileOriginY; EB_U16 tileEndX; EB_U16 tileEndY; -#endif - } LargestCodingUnit_t; diff --git a/Source/Lib/Codec/EbComputeSAD.h b/Source/Lib/Codec/EbComputeSAD.h index 8faea9cab..e8311a801 100644 --- a/Source/Lib/Codec/EbComputeSAD.h +++ b/Source/Lib/Codec/EbComputeSAD.h @@ -101,9 +101,9 @@ extern "C" { /*2 16xM */ FastLoop_NxMSadKernel, /*3 24xM */ FastLoop_NxMSadKernel, /*4 32xM */ FastLoop_NxMSadKernel, - /*5 */ FastLoop_NxMSadKernel, + /*5 40xM */ FastLoop_NxMSadKernel, /*6 48xM */ FastLoop_NxMSadKernel, - /*7 */ FastLoop_NxMSadKernel, + /*7 56xM */ FastLoop_NxMSadKernel, /*8 64xM */ FastLoop_NxMSadKernel }, // AVX2 @@ -113,9 +113,9 @@ extern "C" { /*2 16xM */ Compute16xMSad_SSE2_INTRIN,//Compute16xMSad_AVX2_INTRIN is slower than the SSE2 version /*3 24xM */ Compute24xMSad_AVX2_INTRIN, /*4 32xM */ Compute32xMSad_AVX2_INTRIN, - /*5 */ (EB_SADKERNELNxM_TYPE)NxMSadKernelVoidFunc, + /*5 40xM */ Compute40xMSad_AVX2_INTRIN, /*6 48xM */ Compute48xMSad_AVX2_INTRIN, - /*7 */ (EB_SADKERNELNxM_TYPE)NxMSadKernelVoidFunc, + /*7 56xM */ Compute56xMSad_AVX2_INTRIN, /*8 64xM */ Compute64xMSad_AVX2_INTRIN, }, }; @@ -178,4 +178,4 @@ extern "C" { } #endif -#endif \ No newline at end of file +#endif diff --git a/Source/Lib/Codec/EbDeblockingFilter.c b/Source/Lib/Codec/EbDeblockingFilter.c index 31a91fd7d..41a8204a7 100644 --- a/Source/Lib/Codec/EbDeblockingFilter.c +++ b/Source/Lib/Codec/EbDeblockingFilter.c @@ -345,6 +345,8 @@ void SetBSArrayBasedOnPUBoundary( const CodedUnitStats_t *cuStatsPtr, EB_U32 tbOriginX, EB_U32 tbOriginY, + EB_BOOL tileLeftBoundary, + EB_BOOL tileTopBoundary, PictureControlSet_t *pictureControlSetPtr, EB_U8 *horizontalEdgeBSArray, EB_U8 *verticalEdgeBSArray) @@ -371,7 +373,8 @@ void SetBSArrayBasedOnPUBoundary( EB_U32 neighborPuIdx; // set bS for the horizontal PU boundary which lies on the 8 sample edge - if ((cuStatsPtr->originY & 7) == 0 && cuStatsPtr->originY + tbOriginY != 0) { + //if ((cuStatsPtr->originY & 7) == 0 && cuStatsPtr->originY + tbOriginY != 0) { + if ((cuStatsPtr->originY & 7) == 0 && !tileTopBoundary) { for (blk4x4Addr = puTopLeft4x4blkAddr; blk4x4Addr <= puTopRight4x4blkAddr; ++blk4x4Addr) { @@ -400,7 +403,8 @@ void SetBSArrayBasedOnPUBoundary( } // set bS for the vertical PU boundary which lies on the 8 sample edge - if ((cuStatsPtr->originX & 7) == 0 && cuStatsPtr->originX + tbOriginX != 0) { + //if ((cuStatsPtr->originX & 7) == 0 && cuStatsPtr->originX + tbOriginX != 0) { + if ((cuStatsPtr->originX & 7) == 0 && !tileLeftBoundary) { for (blk4x4Addr = puTopLeft4x4blkAddr; blk4x4Addr <= puBottomLeft4x4blkAddr; blk4x4Addr += MaxLcuSizeIn4x4blk) { blk4x4Pos_x = (blk4x4Addr & (MaxLcuSizeIn4x4blk - 1)) << 2; blk4x4Pos_y = (blk4x4Addr >> logMaxLcuSizeIn4x4blk) << 2; @@ -3518,10 +3522,6 @@ void LCUPictureEdgeDLFCore( EB_U32 lcuPos_y, //input parameter, the picture-wise vertical location of the LCU. EB_U32 lcuWidth, //input parameter, the LCU width. EB_U32 lcuHeight, //input parameter, the LCU height. - // EB_U8 *lcuVerticalEdgeBSArray, //input parameter, the pointer to the vertical edge BS array. - // EB_U8 *lcuHorizontalEdgeBSArray, //input parameter, the pointer to the horizontal edge BS array. - // EB_U8 *topLcuVerticalEdgeBSArray, //input parameter, the pointer to the vertical edge BS array of the top neighbour LCU. - // EB_U8 *leftLcuHorizontalEdgeBSArray, //input parameter, the pointer to the horizontal edge BS array of the left neighbour LCU. PictureControlSet_t *pictureControlSetPtr) //input parameter, picture control set. { const EB_U32 MaxLcuSizeIn4x4blk = MAX_LCU_SIZE >> 2; @@ -3536,7 +3536,11 @@ void LCUPictureEdgeDLFCore( EB_U32 blk4x4Addr; EB_U32 blk2x2Addr; EB_U32 pictureWidthInLcu; - EB_U32 pictureHeightInLcu; + //EB_U32 pictureHeightInLcu; + EB_U32 tileWidthInPxl; + EB_U32 tileHeightInPxl; + EB_U32 tileWidthEndInLcu; + EB_U32 tileHeightEndInLcu; EB_U32 fourSampleEdgeStartSamplePos_x; EB_U32 fourSampleEdgeStartSamplePos_y; EB_U8 bS; @@ -3568,25 +3572,25 @@ void LCUPictureEdgeDLFCore( LargestCodingUnit_t *lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; pictureWidthInLcu = (sequenceControlSet->lumaWidth + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; - pictureHeightInLcu = (sequenceControlSet->lumaHeight + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; + //pictureHeightInLcu = (sequenceControlSet->lumaHeight + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; + tileWidthEndInLcu = (lcuPtr->tileEndX + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; + tileHeightEndInLcu = (lcuPtr->tileEndY + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; + tileWidthInPxl = lcuPtr->tileEndX - lcuPtr->tileOriginX; + tileHeightInPxl = lcuPtr->tileEndY - lcuPtr->tileOriginY; lcuSize = sequenceControlSet->lcuSize; chromaLcuSizeX = lcuSize >> (colorFormat==EB_YUV444?0:1); chromaLcuSizeY = lcuSize >> (colorFormat==EB_YUV420?1:0); const EB_U32 subWidthShfitMinus1 = colorFormat==EB_YUV444?1:0; const EB_U32 subHeightShfitMinus1 = colorFormat==EB_YUV420?0:1; - if (lcuPos_x >> lcuPtr->sizeLog2 == pictureWidthInLcu - 1) { + if (lcuPos_x >> lcuPtr->sizeLog2 == tileWidthEndInLcu - 1) { /***** picture right-most 4 sample horizontal edges filtering *****/ // luma component filtering - //num4SampleHorizontalEdges = (sequenceControlSet->lumaHeight >> 3) - 1; num4SampleHorizontalEdges = (lcuHeight >> 3); - fourSampleEdgeStartSamplePos_x = sequenceControlSet->lumaWidth - 4; // picutre-wise position - //for(verticalIdx = 1; verticalIdx <= num4SampleHorizontalEdges; ++verticalIdx) { - for (verticalIdx = (lcuPos_y == 0); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { + fourSampleEdgeStartSamplePos_x = lcuPtr->tileEndX - 4; // picutre-wise position + for (verticalIdx = (lcuPos_y == lcuPtr->tileOriginY); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { // edge B - //fourSampleEdgeStartSamplePos_y = verticalIdx << 3; // picture-wise position fourSampleEdgeStartSamplePos_y = lcuPos_y + (verticalIdx << 3); // picture-wise position //lcuIdx = (fourSampleEdgeStartSamplePos_y >> logMaxLcuSize) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> logMaxLcuSize); - //lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; blk4x4Addr = GET_LUMA_4X4BLK_ADDR( fourSampleEdgeStartSamplePos_x & (lcuSize - 1), fourSampleEdgeStartSamplePos_y & (lcuSize - 1), @@ -3634,16 +3638,12 @@ void LCUPictureEdgeDLFCore( } // chroma component filtering - if ((sequenceControlSet->chromaWidth & 7) == 0) { - //num4SampleHorizontalEdges = (sequenceControlSet->chromaHeight >> 3) + ((sequenceControlSet->chromaHeight & 7) != 0) - 1; + if (((tileWidthInPxl >> subWidthCMinus1) & 7) == 0) { num4SampleHorizontalEdges = (lcuHeight >> (colorFormat==EB_YUV420?4:3)) + (((lcuHeight >> (colorFormat==EB_YUV420?1:0)) & (colorFormat==EB_YUV420?7:15)) != 0); - fourSampleEdgeStartSamplePos_x = sequenceControlSet->chromaWidth - 4; // Picture wise location + fourSampleEdgeStartSamplePos_x = (lcuPtr->tileEndX >> subWidthCMinus1) - 4; // Tile wise location //for(verticalIdx = 1; verticalIdx <= num4SampleHorizontalEdges; ++verticalIdx) { - for (verticalIdx = (lcuPos_y == 0); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { - //fourSampleEdgeStartSamplePos_y = verticalIdx << 3; // Picture wise location + for (verticalIdx = (lcuPos_y == lcuPtr->tileOriginY); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { fourSampleEdgeStartSamplePos_y = (lcuPos_y >> (colorFormat==EB_YUV420?1:0)) + (verticalIdx << 3); // Picture wise location - //lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize-1)) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize-1)); - //lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; // left 2 sample edge blk2x2Addr = GET_CHROMA_4X4BLK_ADDR( @@ -3745,14 +3745,12 @@ void LCUPictureEdgeDLFCore( } } - if (lcuPos_y >> lcuPtr->sizeLog2 == pictureHeightInLcu - 1) { + if (lcuPos_y >> lcuPtr->sizeLog2 == tileHeightEndInLcu - 1) { /***** picture bottom 4 sample vertical edges filtering *****/ // luma component filtering - //num4SampleVerticalEdges = (sequenceControlSet->lumaWidth >> 3) - 1; num4SampleVerticalEdges = (lcuWidth >> 3); - fourSampleEdgeStartSamplePos_y = sequenceControlSet->lumaHeight - 4; // picture-wise position - //for(horizontalIdx = 1; horizontalIdx <= num4SampleVerticalEdges; ++horizontalIdx) { - for (horizontalIdx = (lcuPos_x == 0); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { + fourSampleEdgeStartSamplePos_y = lcuPtr->tileEndY - 4; // picture-wise position + for (horizontalIdx = (lcuPos_x == lcuPtr->tileOriginX); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { // edge A fourSampleEdgeStartSamplePos_x = lcuPos_x + (horizontalIdx << 3); // picuture-wise position lcuIdx = (fourSampleEdgeStartSamplePos_y >> logMaxLcuSize) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> logMaxLcuSize); @@ -3803,13 +3801,12 @@ void LCUPictureEdgeDLFCore( } // chroma component filtering - if ((sequenceControlSet->chromaHeight & 7) == 0) { - //num4SampleVerticalEdges = (sequenceControlSet->chromaWidth >> 3) + ((sequenceControlSet->chromaWidth & 7) != 0) - 1; + if (((tileHeightInPxl>> subHeightCMinus1) & 7) == 0) { num4SampleVerticalEdges = (lcuWidth >> (3+(colorFormat==EB_YUV444?0:1))) + (((lcuWidth >> (colorFormat==EB_YUV444?0:1)) & (colorFormat==EB_YUV444?15:7)) != 0); fourSampleEdgeStartSamplePos_y = sequenceControlSet->chromaHeight - 4; // Picture wise location - //for(horizontalIdx = 1; horizontalIdx <= num4SampleVerticalEdges; ++horizontalIdx) { - for (horizontalIdx = (lcuPos_x == 0); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { + fourSampleEdgeStartSamplePos_y = (lcuPtr->tileEndY >> subHeightCMinus1) - 4; // Tile wise location + for (horizontalIdx = (lcuPos_x == lcuPtr->tileOriginX); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { fourSampleEdgeStartSamplePos_x = (lcuPos_x >> (colorFormat==EB_YUV444?0:1)) + (horizontalIdx << 3); // Picture wise location lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize - (colorFormat==EB_YUV420?1:0))) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize - (colorFormat==EB_YUV444?0:1))); lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; @@ -3941,7 +3938,13 @@ void LCUPictureEdgeDLFCore16bit( EB_U32 blk4x4Addr; EB_U32 blk2x2Addr; EB_U32 pictureWidthInLcu; - EB_U32 pictureHeightInLcu; + //EB_U32 pictureHeightInLcu; + + EB_U32 tileWidthInPxl; + EB_U32 tileHeightInPxl; + EB_U32 tileWidthEndInLcu; + EB_U32 tileHeightEndInLcu; + EB_U32 fourSampleEdgeStartSamplePos_x; EB_U32 fourSampleEdgeStartSamplePos_y; EB_U8 bS; @@ -3973,21 +3976,24 @@ void LCUPictureEdgeDLFCore16bit( LargestCodingUnit_t *lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; pictureWidthInLcu = (sequenceControlSet->lumaWidth + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; - pictureHeightInLcu = (sequenceControlSet->lumaHeight + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; + //pictureHeightInLcu = (sequenceControlSet->lumaHeight + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; + tileWidthEndInLcu = (lcuPtr->tileEndX + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; + tileHeightEndInLcu = (lcuPtr->tileEndY + sequenceControlSet->lcuSize - 1) / sequenceControlSet->lcuSize; + tileWidthInPxl = lcuPtr->tileEndX - lcuPtr->tileOriginX; + tileHeightInPxl = lcuPtr->tileEndY - lcuPtr->tileOriginY; lcuSize = sequenceControlSet->lcuSize; chromaLcuSizeX = lcuSize >> (colorFormat==EB_YUV444?0:1); chromaLcuSizeY = lcuSize >> (colorFormat==EB_YUV420?1:0); const EB_U32 subWidthShfitMinus1 = colorFormat==EB_YUV444?1:0; const EB_U32 subHeightShfitMinus1 = colorFormat==EB_YUV420?0:1; - if (lcuPos_x >> lcuPtr->sizeLog2 == pictureWidthInLcu - 1) { + if (lcuPos_x >> lcuPtr->sizeLog2 == tileWidthEndInLcu - 1) { /***** picture right-most 4 sample horizontal edges filtering *****/ // luma component filtering - //num4SampleHorizontalEdges = (sequenceControlSet->lumaHeight >> 3) - 1; num4SampleHorizontalEdges = (lcuHeight >> 3); - fourSampleEdgeStartSamplePos_x = sequenceControlSet->lumaWidth - 4; // picutre-wise position - //for(verticalIdx = 1; verticalIdx <= num4SampleHorizontalEdges; ++verticalIdx) { - for (verticalIdx = (lcuPos_y == 0); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { + fourSampleEdgeStartSamplePos_x = lcuPtr->tileEndX - 4; // picutre-wise position + + for (verticalIdx = (lcuPos_y == lcuPtr->tileOriginY); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { // edge B //fourSampleEdgeStartSamplePos_y = verticalIdx << 3; // picture-wise position fourSampleEdgeStartSamplePos_y = lcuPos_y + (verticalIdx << 3); // picture-wise position @@ -4043,12 +4049,11 @@ void LCUPictureEdgeDLFCore16bit( } // chroma component filtering - if ((sequenceControlSet->chromaWidth & 7) == 0) { + if (((tileWidthInPxl >> subWidthCMinus1) & 7) == 0) { //num4SampleHorizontalEdges = (sequenceControlSet->chromaHeight >> 3) + ((sequenceControlSet->chromaHeight & 7) != 0) - 1; num4SampleHorizontalEdges = (lcuHeight >> (colorFormat==EB_YUV420?4:3)) + (((lcuHeight >> (colorFormat==EB_YUV420?1:0)) & (colorFormat==EB_YUV420?7:15)) != 0); - fourSampleEdgeStartSamplePos_x = sequenceControlSet->chromaWidth - 4; // Picture wise location - //for(verticalIdx = 1; verticalIdx <= num4SampleHorizontalEdges; ++verticalIdx) { - for (verticalIdx = (lcuPos_y == 0); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { + fourSampleEdgeStartSamplePos_x = (lcuPtr->tileEndX >> subWidthCMinus1) - 4; // Tile wise location + for (verticalIdx = (lcuPos_y == lcuPtr->tileOriginY); verticalIdx < num4SampleHorizontalEdges; ++verticalIdx) { //fourSampleEdgeStartSamplePos_y = verticalIdx << 3; // Picture wise location fourSampleEdgeStartSamplePos_y = (lcuPos_y >> (colorFormat==EB_YUV420?1:0)) + (verticalIdx << 3); // Picture wise location //lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize-1)) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize-1)); @@ -4161,14 +4166,12 @@ void LCUPictureEdgeDLFCore16bit( } } - if (lcuPos_y >> lcuPtr->sizeLog2 == pictureHeightInLcu - 1) { + if (lcuPos_y >> lcuPtr->sizeLog2 == tileHeightEndInLcu - 1) { /***** picture bottom 4 sample vertical edges filtering *****/ // luma component filtering - //num4SampleVerticalEdges = (sequenceControlSet->lumaWidth >> 3) - 1; num4SampleVerticalEdges = (lcuWidth >> 3); - fourSampleEdgeStartSamplePos_y = sequenceControlSet->lumaHeight - 4; // picture-wise position - //for(horizontalIdx = 1; horizontalIdx <= num4SampleVerticalEdges; ++horizontalIdx) { - for (horizontalIdx = (lcuPos_x == 0); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { + fourSampleEdgeStartSamplePos_y = lcuPtr->tileEndY - 4; // picture-wise position + for (horizontalIdx = (lcuPos_x == lcuPtr->tileOriginX); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { // edge A fourSampleEdgeStartSamplePos_x = lcuPos_x + (horizontalIdx << 3); // picuture-wise position lcuIdx = (fourSampleEdgeStartSamplePos_y >> logMaxLcuSize) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> logMaxLcuSize); @@ -4221,12 +4224,10 @@ void LCUPictureEdgeDLFCore16bit( } // chroma component filtering - if ((sequenceControlSet->chromaHeight & 7) == 0) { - //num4SampleVerticalEdges = (sequenceControlSet->chromaWidth >> 3) + ((sequenceControlSet->chromaWidth & 7) != 0) - 1; + if (((tileHeightInPxl>> subHeightCMinus1) & 7) == 0) { num4SampleVerticalEdges = (lcuWidth >> (3+(colorFormat==EB_YUV444?0:1))) + (((lcuWidth >> (colorFormat==EB_YUV444?0:1)) & (colorFormat==EB_YUV444?15:7)) != 0); - fourSampleEdgeStartSamplePos_y = sequenceControlSet->chromaHeight - 4; // Picture wise location - //for(horizontalIdx = 1; horizontalIdx <= num4SampleVerticalEdges; ++horizontalIdx) { - for (horizontalIdx = (lcuPos_x == 0); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { + fourSampleEdgeStartSamplePos_y = (lcuPtr->tileEndY >> subHeightCMinus1) - 4; // Tile wise location + for (horizontalIdx = (lcuPos_x == lcuPtr->tileOriginX); horizontalIdx < num4SampleVerticalEdges; ++horizontalIdx) { fourSampleEdgeStartSamplePos_x = (lcuPos_x >> (colorFormat==EB_YUV444?0:1)) + (horizontalIdx << 3); // Picture wise location lcuIdx = (fourSampleEdgeStartSamplePos_y >> (logMaxLcuSize - (colorFormat==EB_YUV420?1:0))) * pictureWidthInLcu + (fourSampleEdgeStartSamplePos_x >> (logMaxLcuSize - (colorFormat==EB_YUV444?0:1))); lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIdx]; diff --git a/Source/Lib/Codec/EbDeblockingFilter.h b/Source/Lib/Codec/EbDeblockingFilter.h index ff2431287..1cc72cb66 100644 --- a/Source/Lib/Codec/EbDeblockingFilter.h +++ b/Source/Lib/Codec/EbDeblockingFilter.h @@ -76,6 +76,10 @@ extern void SetBSArrayBasedOnPUBoundary( const CodedUnitStats_t *cuStatsPtr, EB_U32 tbOriginX, EB_U32 tbOriginY, + EB_BOOL tileLeftBoundary, + EB_BOOL tileTopBoundary, + //EB_U32 tileOriginX, + //EB_U32 tileOriginY, PictureControlSet_t *pictureControlSetPtr, EB_U8 *horizontalEdgeBSArray, EB_U8 *verticalEdgeBSArray); @@ -271,4 +275,4 @@ static EB_LUMADLF_TYPE_16BIT FUNC_TABLE lumaDlf_funcPtrArray16bit[EB_ASM_TYPE_TO #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/Source/Lib/Codec/EbDefinitions.h b/Source/Lib/Codec/EbDefinitions.h index d9226540e..57edf866b 100644 --- a/Source/Lib/Codec/EbDefinitions.h +++ b/Source/Lib/Codec/EbDefinitions.h @@ -6,6 +6,8 @@ #ifndef EbDefinitions_h #define EbDefinitions_h +//#define BENCHMARK 0 +#define LATENCY_PROFILE 0 #include "EbApi.h" #ifdef __cplusplus extern "C" { @@ -51,15 +53,12 @@ extern "C" { #endif /* _ERRNO_T_DEFINED */ -#define TILES 1 //enable TILES. - -#if TILES //Maximum 8192x4320 -#define EB_TILE_COLUMN_MAX_COUNT 32 -#define EB_TILE_ROW_MAX_COUNT 68 -#endif +#define EB_TILE_COLUMN_MAX_COUNT 20u +#define EB_TILE_ROW_MAX_COUNT 22u +#define EB_TILE_MAX_COUNT 440u #define EB_MIN(a,b) (((a) < (b)) ? (a) : (b)) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 83178ccb3..c1ad4320b 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -57,6 +57,7 @@ EB_ERRORTYPE EncDecContextCtor( contextPtr->is16bit = is16bit; contextPtr->colorFormat = colorFormat; + contextPtr->tileRowIndex = 0; // Input/Output System Resource Manager FIFOs contextPtr->modeDecisionInputFifoPtr = modeDecisionConfigurationInputFifoPtr; @@ -211,6 +212,7 @@ EB_ERRORTYPE EncDecContextCtor( ********************************************/ static EB_ERRORTYPE ApplySaoOffsetsLcu( PictureControlSet_t *pictureControlSetPtr, + EB_U32 lcuIndex, EncDecContext_t *contextPtr, // input parameter, DLF context Ptr, used to store the intermediate source samples EB_U32 videoComponent, // input parameter, video component, Y:0 - U:1 - V:2 SaoParameters_t *saoPtr, // input parameter, LCU Ptr @@ -243,7 +245,16 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( encodeContextPtr = ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr; + LargestCodingUnit_t *lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIndex]; + + FirstColLcu = lcuPtr->tileLeftEdgeFlag; + LastColLcu = lcuPtr->tileRightEdgeFlag; + FirstRowLcu = lcuPtr->tileTopEdgeFlag; + LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); + LastRowLcu = (lcuPtr->tileEndY <= lcuPtr->originY + MAX_LCU_SIZE); + (void)bitDepth; + (void)pictureWidth; lcuHeightCount = 0; ReorderedsaoOffset[0] = (EB_S8)saoPtr->saoOffset[videoComponent][0]; @@ -269,8 +280,8 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( case 1: // EO - 0 degrees - FirstColLcu = (EB_BOOL)(tbOriginX == 0); - LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); + //FirstColLcu = (EB_BOOL)(tbOriginX == 0); + //LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -311,8 +322,8 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( case 2: // EO - 90 degrees - FirstRowLcu = (EB_BOOL)(tbOriginY == 0); - LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); + //FirstRowLcu = (EB_BOOL)(tbOriginY == 0); + //LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); //save the non filtered first row if this LCU is on the first LCU Row if (FirstRowLcu) { @@ -351,10 +362,10 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( case 3: // EO - 135 degrees - FirstColLcu = (EB_BOOL)(tbOriginX == 0); - LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); - FirstRowLcu = (EB_BOOL)(tbOriginY == 0); - LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); + //FirstColLcu = (EB_BOOL)(tbOriginX == 0); + //LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); + //FirstRowLcu = (EB_BOOL)(tbOriginY == 0); + //LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -417,10 +428,10 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu( case 4: // EO - 45 degrees - FirstColLcu = (EB_BOOL)(tbOriginX == 0); - LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); - FirstRowLcu = (EB_BOOL)(tbOriginY == 0); - LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); + //FirstColLcu = (EB_BOOL)(tbOriginX == 0); + //LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); + //FirstRowLcu = (EB_BOOL)(tbOriginY == 0); + //LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -578,7 +589,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( lcuHeightPlusOne = (sequenceControlSetPtr->lumaHeight == tbOriginY + lcuHeight) ? lcuHeight : lcuHeight + 1; - //Save last pixel colunm of this LCU for next LCU + //Save last pixel column of this LCU for next LCU for (lcuRow = 0; lcuRow < lcuHeightPlusOne; ++lcuRow) { contextPtr->saoLeftBuffer[pingpongIdxLeft][lcuRow] = reconPicturePtr->bufferY[reconSampleLumaIndex + lcuWidth - 1 + lcuRow*reconPicturePtr->strideY]; } @@ -586,6 +597,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( if (saoParams->saoTypeIndex[0]) ApplySaoOffsetsLcu( pictureControlSetPtr, + lcuIndex, contextPtr, 0, saoParams, @@ -647,6 +659,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( if (saoParams->saoTypeIndex[1]) ApplySaoOffsetsLcu( pictureControlSetPtr, + lcuIndex, contextPtr, 1, saoParams, @@ -710,6 +723,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( if (saoParams->saoTypeIndex[1]) ApplySaoOffsetsLcu( pictureControlSetPtr, + lcuIndex, contextPtr, 2, saoParams, @@ -745,6 +759,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture( ********************************************/ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( PictureControlSet_t *pictureControlSetPtr, + EB_U32 lcuIndex, EncDecContext_t *contextPtr, // input parameter, DLF context Ptr, used to store the intermediate source samples EB_U32 videoComponent, // input parameter, video component, Y:0 - U:1 - V:2 SaoParameters_t *saoPtr, // input parameter, LCU Ptr @@ -774,7 +789,16 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( const EB_COLOR_FORMAT colorFormat = pictureControlSetPtr->colorFormat; const EB_U16 subWidthCMinus1 = (colorFormat == EB_YUV444 ? 1 : 2) - 1; const EB_U16 subHeightCMinus1 = (colorFormat >= EB_YUV422 ? 1 : 2) - 1; + LargestCodingUnit_t *lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIndex]; + + FirstColLcu = lcuPtr->tileLeftEdgeFlag; + LastColLcu = lcuPtr->tileRightEdgeFlag; + FirstRowLcu = lcuPtr->tileTopEdgeFlag; + LastRowLcu = (EB_BOOL)((tbOriginY >> (isChroma ? subHeightCMinus1:0)) + lcuHeight == pictureHeight); + LastRowLcu = (lcuPtr->tileEndY <= lcuPtr->originY + MAX_LCU_SIZE); + (void)bitDepth; + (void)pictureWidth; lcuHeightCount = 0; ReorderedsaoOffset[0] = (EB_S8)saoPtr->saoOffset[videoComponent][0]; @@ -796,8 +820,8 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( case 1: // EO - 0 degrees - FirstColLcu = (EB_BOOL)(tbOriginX == 0); - LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); + //FirstColLcu = (EB_BOOL)(tbOriginX == 0); + //LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -838,8 +862,8 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( case 2: // EO - 90 degrees - FirstRowLcu = (EB_BOOL)((tbOriginY == 0)); - LastRowLcu = (EB_BOOL)(((tbOriginY >> (isChroma ? subHeightCMinus1 : 0)) + lcuHeight == pictureHeight)); + //FirstRowLcu = (EB_BOOL)((tbOriginY == 0)); + //LastRowLcu = (EB_BOOL)(((tbOriginY >> (isChroma ? subHeightCMinus1 : 0)) + lcuHeight == pictureHeight)); //save the non filtered first row if this LCU is on the first LCU Row if (FirstRowLcu) { @@ -879,10 +903,10 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( case 3: // EO - 135 degrees - FirstColLcu = (EB_BOOL)((tbOriginX == 0)); - LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); - FirstRowLcu = (EB_BOOL)((tbOriginY == 0)); - LastRowLcu = (EB_BOOL)(((tbOriginY >> (isChroma ? subHeightCMinus1 : 0)) + lcuHeight == pictureHeight)); + //FirstColLcu = (EB_BOOL)((tbOriginX == 0)); + //LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); + //FirstRowLcu = (EB_BOOL)((tbOriginY == 0)); + //LastRowLcu = (EB_BOOL)(((tbOriginY >> (isChroma ? subHeightCMinus1 : 0)) + lcuHeight == pictureHeight)); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -945,10 +969,10 @@ static EB_ERRORTYPE ApplySaoOffsetsLcu16bit( case 4: // EO - 45 degrees - FirstColLcu = (EB_BOOL)((tbOriginX == 0)); - LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); - FirstRowLcu = (EB_BOOL)((tbOriginY == 0)); - LastRowLcu = (EB_BOOL)(((tbOriginY >> (isChroma ? subHeightCMinus1 : 0)) + lcuHeight == pictureHeight)); + //FirstColLcu = (EB_BOOL)((tbOriginX == 0)); + //LastColLcu = (EB_BOOL)((tbOriginX >> (isChroma ? subWidthCMinus1 : 0)) + lcuWidth == pictureWidth); + //FirstRowLcu = (EB_BOOL)((tbOriginY == 0)); + //LastRowLcu = (EB_BOOL)(((tbOriginY >> (isChroma ? subHeightCMinus1 : 0)) + lcuHeight == pictureHeight)); //save the non filtered first colomn if it is the first LCU in the row. if (FirstColLcu) { @@ -1113,6 +1137,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture16bit( if (saoParams->saoTypeIndex[0]) ApplySaoOffsetsLcu16bit( pictureControlSetPtr, + lcuIndex, contextPtr, 0, saoParams, @@ -1179,6 +1204,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture16bit( if (saoParams->saoTypeIndex[1]) ApplySaoOffsetsLcu16bit( pictureControlSetPtr, + lcuIndex, contextPtr, 1, saoParams, @@ -1242,6 +1268,7 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture16bit( if (saoParams->saoTypeIndex[1]) ApplySaoOffsetsLcu16bit( pictureControlSetPtr, + lcuIndex, contextPtr, 2, saoParams, @@ -1275,20 +1302,17 @@ static EB_ERRORTYPE ApplySaoOffsetsPicture16bit( /************************************************** * Reset Mode Decision Neighbor Arrays *************************************************/ -static void ResetEncodePassNeighborArrays(PictureControlSet_t *pictureControlSetPtr) +static void ResetEncodePassNeighborArrays(PictureControlSet_t *pictureControlSetPtr, unsigned tileIdx) { - NeighborArrayUnitReset(pictureControlSetPtr->epIntraLumaModeNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->epMvNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->epSkipFlagNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->epModeTypeNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->epLeafDepthNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->epLumaReconNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->epCbReconNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->epCrReconNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->epSaoNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->amvpMvMergeMvNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->amvpMvMergeModeTypeNeighborArray); - + NeighborArrayUnitReset(pictureControlSetPtr->epIntraLumaModeNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->epMvNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->epSkipFlagNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->epModeTypeNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->epLeafDepthNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->epLumaReconNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->epCbReconNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->epCrReconNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->epSaoNeighborArray[tileIdx]); return; } @@ -1370,25 +1394,30 @@ static void ResetEncDec( } if (segmentIndex == 0) { - - // Reset CABAC Contexts - ResetEntropyCoder( - sequenceControlSetPtr->encodeContextPtr, - pictureControlSetPtr->coeffEstEntropyCoderPtr, - entropyCodingQp, - pictureControlSetPtr->sliceType); - - ResetEntropyCoder( - sequenceControlSetPtr->encodeContextPtr, - pictureControlSetPtr->tempEntropyCoderPtr, - entropyCodingQp, - pictureControlSetPtr->sliceType); - ResetEncodePassNeighborArrays(pictureControlSetPtr); - - //this fucntion could be optimized by removed chroma, and unessary TU sizes. - PrecomputeCabacCost(&(*pictureControlSetPtr->cabacCost), - (CabacEncodeContext_t*)pictureControlSetPtr->coeffEstEntropyCoderPtr->cabacEncodeContextPtr); - + //Jing: Double check the entropy context here + // Not good here, better to use a mutex, but should be OK since input from MDC is FIFO + if (contextPtr->tileRowIndex == 0) { + // Reset CABAC Contexts + ResetEntropyCoder( + sequenceControlSetPtr->encodeContextPtr, + pictureControlSetPtr->coeffEstEntropyCoderPtr, + entropyCodingQp, + pictureControlSetPtr->sliceType); + //ResetEntropyCoder( + // sequenceControlSetPtr->encodeContextPtr, + // pictureControlSetPtr->tempEntropyCoderPtr, + // entropyCodingQp, + // pictureControlSetPtr->sliceType); + //this function could be optimized by removed chroma, and unessary TU sizes. + PrecomputeCabacCost(&(*pictureControlSetPtr->cabacCost), + (CabacEncodeContext_t*)pictureControlSetPtr->coeffEstEntropyCoderPtr->cabacEncodeContextPtr); + } + + for (int tileIdx = contextPtr->tileRowIndex * sequenceControlSetPtr->tileColumnCount; + tileIdx < (contextPtr->tileRowIndex + 1) * sequenceControlSetPtr->tileColumnCount; + tileIdx++) { + ResetEncodePassNeighborArrays(pictureControlSetPtr, tileIdx); + } } if (contextPtr->mdContext->coeffCabacUpdate) @@ -1399,7 +1428,6 @@ static void ResetEncDec( //LatestValid <-- init EB_MEMCPY(&(contextPtr->mdContext->latestValidCoeffCtxModel), &(cabacCtxModelArray->lastSigXContextModel[0]), sizeof(CoeffCtxtMdl_t)); - } return; @@ -1625,6 +1653,8 @@ EB_BOOL AssignEncDecSegments( feedbackTaskPtr->inputType = ENCDEC_TASKS_ENCDEC_INPUT; feedbackTaskPtr->encDecSegmentRow = feedbackRowIndex; feedbackTaskPtr->pictureControlSetWrapperPtr = taskPtr->pictureControlSetWrapperPtr; + feedbackTaskPtr->tileRowIndex = taskPtr->tileRowIndex; + EbPostFullObject(wrapperPtr); } @@ -4032,6 +4062,8 @@ void* EncDecKernel(void *inputPtr) EB_U32 lcuRowIndexCount; EB_U32 pictureWidthInLcu; EB_U32 pictureHeightInLcu; + EB_U32 tileRowWidthInLcu; + //EB_U32 currentTileWidthInLcu; MdcLcuData_t *mdcPtr; //Row level vbv controls @@ -4052,6 +4084,9 @@ void* EncDecKernel(void *inputPtr) EB_U32 segmentBandIndex; EB_U32 segmentBandSize; EncDecSegments_t *segmentsPtr; + EB_U32 tileX, tileY, tileRowIndex; + EB_U32 i, j, tmp; + EB_U32 tileGroupLcuStartX, tileGroupLcuStartY; //Proxy entropy coding EbPictureBufferDesc_t * tempCoeffPicturePtr; @@ -4072,47 +4107,64 @@ void* EncDecKernel(void *inputPtr) pictureControlSetPtr = (PictureControlSet_t*)encDecTasksPtr->pictureControlSetWrapperPtr->objectPtr; sequenceControlSetPtr = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; enableSaoFlag = (sequenceControlSetPtr->staticConfig.enableSaoFlag) ? EB_TRUE : EB_FALSE; - segmentsPtr = pictureControlSetPtr->encDecSegmentCtrl; + tileRowIndex = encDecTasksPtr->tileRowIndex; + + segmentsPtr = pictureControlSetPtr->encDecSegmentCtrl[tileRowIndex]; + (void)tileX; + tileY = tileRowIndex; + + contextPtr->tileRowIndex = tileRowIndex; + contextPtr->tileIndex = 0; + + tileGroupLcuStartX = tileGroupLcuStartY = 0; + //for (i = 0; i < tileX; i++) { + // tileGroupLcuStartX += sequenceControlSetPtr->tileColumnArray[i]; + //} + for (j = 0; j < tileY; j++) { + tileGroupLcuStartY += sequenceControlSetPtr->tileRowArray[j]; + } lastLcuFlag = EB_FALSE; is16bit = (EB_BOOL)(sequenceControlSetPtr->staticConfig.encoderBitDepth > EB_8BIT); #if DEADLOCK_DEBUG SVT_LOG("POC %lld ENCDEC IN \n", pictureControlSetPtr->pictureNumber); #endif + // LCU Constants lcuSize = (EB_U8)sequenceControlSetPtr->lcuSize; lcuSizeLog2 = (EB_U8)Log2f(lcuSize); contextPtr->lcuSize = lcuSize; pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + lcuSize - 1) >> lcuSizeLog2; pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + lcuSize - 1) >> lcuSizeLog2; + tileRowWidthInLcu = pictureWidthInLcu; endOfRowFlag = EB_FALSE; lcuRowIndexStart = lcuRowIndexCount = 0; contextPtr->totIntraCodedArea = 0; + contextPtr->codedLcuCount = 0; // EncDec Kernel Signal(s) derivation if (sequenceControlSetPtr->staticConfig.tune == TUNE_SQ) { SignalDerivationEncDecKernelSq( - sequenceControlSetPtr, - pictureControlSetPtr, - contextPtr); + sequenceControlSetPtr, + pictureControlSetPtr, + contextPtr); } else if (sequenceControlSetPtr->staticConfig.tune == TUNE_VMAF) { - SignalDerivationEncDecKernelVmaf( - sequenceControlSetPtr, - pictureControlSetPtr, - contextPtr); - } + SignalDerivationEncDecKernelVmaf( + sequenceControlSetPtr, + pictureControlSetPtr, + contextPtr); + } else { SignalDerivationEncDecKernelOq( - sequenceControlSetPtr, - pictureControlSetPtr, - contextPtr); + sequenceControlSetPtr, + pictureControlSetPtr, + contextPtr); } #if 1//TILES //NEED these to test stream complaince - // contextPtr->pmMethod = 0; + // contextPtr->pmMethod = 0; contextPtr->mdContext->rdoqPmCoreMethod = EB_NO_RDOQ; //RDOQ make DLF cause MD5 mismatch when encDec segments+QP mod are ON.. contextPtr->allowEncDecMismatch = EB_FALSE; - #endif // Derive Interpoldation Method @ Fast-Loop @@ -4130,32 +4182,35 @@ void* EncDecKernel(void *inputPtr) EB_TRUE : EB_FALSE; - // Segment-loop while (AssignEncDecSegments(segmentsPtr, &segmentIndex, encDecTasksPtr, contextPtr->encDecFeedbackFifoPtr) == EB_TRUE) { + // Per tile group(tile row) xLcuStartIndex = segmentsPtr->xStartArray[segmentIndex]; yLcuStartIndex = segmentsPtr->yStartArray[segmentIndex]; - lcuStartIndex = yLcuStartIndex * pictureWidthInLcu + xLcuStartIndex; + + lcuStartIndex = yLcuStartIndex * tileRowWidthInLcu + xLcuStartIndex; lcuSegmentCount = segmentsPtr->validLcuCountArray[segmentIndex]; segmentRowIndex = segmentIndex / segmentsPtr->segmentBandCount; segmentBandIndex = segmentIndex - segmentRowIndex * segmentsPtr->segmentBandCount; segmentBandSize = (segmentsPtr->lcuBandCount * (segmentBandIndex + 1) + segmentsPtr->segmentBandCount - 1) / segmentsPtr->segmentBandCount; + // Reset Coding Loop State ProductResetModeDecision( // HT done - contextPtr->mdContext, - pictureControlSetPtr, - sequenceControlSetPtr, - segmentIndex); + contextPtr->mdContext, + pictureControlSetPtr, + sequenceControlSetPtr, + contextPtr->tileRowIndex, + segmentIndex); // Reset EncDec Coding State ResetEncDec( // HT done - contextPtr, - pictureControlSetPtr, - sequenceControlSetPtr, - segmentIndex); + contextPtr, + pictureControlSetPtr, + sequenceControlSetPtr, + segmentIndex); contextPtr->mdContext->CabacCost = pictureControlSetPtr->cabacCost; @@ -4167,144 +4222,161 @@ void* EncDecKernel(void *inputPtr) if (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) { QpmDeriveWeightsMinAndMax( - pictureControlSetPtr, - contextPtr); + pictureControlSetPtr, + contextPtr); } for (yLcuIndex = yLcuStartIndex, lcuSegmentIndex = lcuStartIndex; lcuSegmentIndex < lcuStartIndex + lcuSegmentCount; ++yLcuIndex) { - pictureControlSetPtr->rowStats[yLcuIndex]->totalCUEncoded=0; + pictureControlSetPtr->rowStats[yLcuIndex]->totalCUEncoded = 0; pictureControlSetPtr->rowStats[yLcuIndex]->encodedBits = 0; pictureControlSetPtr->rowStats[yLcuIndex]->numEncodedCUs = 0; - for (xLcuIndex = xLcuStartIndex; xLcuIndex < pictureWidthInLcu && (xLcuIndex + yLcuIndex < segmentBandSize) && lcuSegmentIndex < lcuStartIndex + lcuSegmentCount; ++xLcuIndex, ++lcuSegmentIndex) { + for (xLcuIndex = xLcuStartIndex; xLcuIndex < tileRowWidthInLcu && (xLcuIndex + yLcuIndex < segmentBandSize) && lcuSegmentIndex < lcuStartIndex + lcuSegmentCount; ++xLcuIndex, ++lcuSegmentIndex) { - lcuIndex = (EB_U16)(yLcuIndex * pictureWidthInLcu + xLcuIndex); + // LCU per picture-wise + lcuIndex = (EB_U16)((tileGroupLcuStartY + yLcuIndex) * pictureWidthInLcu + (tileGroupLcuStartX + xLcuIndex)); lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIndex]; rowIndex = (EB_U16)(yLcuIndex / pictureHeightInLcu); rowPtr = pictureControlSetPtr->rowStats[rowIndex]; pictureControlSetPtr->firstRowOfPicture = (xLcuIndex <= pictureWidthInLcu) ? EB_TRUE : EB_FALSE; - lcuOriginX = xLcuIndex << lcuSizeLog2; - lcuOriginY = yLcuIndex << lcuSizeLog2; - lastLcuFlag = (lcuIndex == pictureControlSetPtr->lcuTotalCount - 1) ? EB_TRUE : EB_FALSE; - endOfRowFlag = (xLcuIndex == pictureWidthInLcu - 1) ? EB_TRUE : EB_FALSE; - lcuRowIndexStart = (xLcuIndex == pictureWidthInLcu - 1 && lcuRowIndexCount == 0) ? yLcuIndex : lcuRowIndexStart; - lcuRowIndexCount = (xLcuIndex == pictureWidthInLcu - 1) ? lcuRowIndexCount + 1 : lcuRowIndexCount; + lcuOriginX = (xLcuIndex+tileGroupLcuStartX) << lcuSizeLog2; + lcuOriginY = (yLcuIndex+tileGroupLcuStartY) << lcuSizeLog2; + //printf("Process lcu (%d, %d), lcuIndex %d, segmentIndex %d\n", lcuOriginX, lcuOriginY, lcuIndex, segmentIndex); + + // Set current LCU tile Index + tmp = 0; + for (i = 0; i < sequenceControlSetPtr->tileColumnCount; i++) { + tmp += sequenceControlSetPtr->tileColumnArray[i]; + if (xLcuIndex < tmp) { + // Jing: Set correct tileIndex (in raster order) for ModeDecisionContext + contextPtr->mdContext->tileIndex = contextPtr->tileRowIndex * sequenceControlSetPtr->tileColumnCount + i; + contextPtr->tileIndex = contextPtr->tileRowIndex * sequenceControlSetPtr->tileColumnCount + i; + //currentTileWidthInLcu = sequenceControlSetPtr->tileColumnArray[i]; + break; + } + } + + + endOfRowFlag = (xLcuIndex == tileRowWidthInLcu - 1) ? EB_TRUE : EB_FALSE; + lcuRowIndexStart = (xLcuIndex == tileRowWidthInLcu - 1 && lcuRowIndexCount == 0) ? yLcuIndex : lcuRowIndexStart; + + // Jing: Send to entropy at tile group ends, not each tile for simplicity + lcuRowIndexCount = (xLcuIndex == tileRowWidthInLcu - 1) ? lcuRowIndexCount + 1 : lcuRowIndexCount; mdcPtr = &pictureControlSetPtr->mdcLcuArray[lcuIndex]; contextPtr->lcuIndex = lcuIndex; - tempCoeffPicturePtr = lcuPtr->quantizedCoeff; - + // Derive cuUseRefSrcFlag Flag contextPtr->mdContext->cuUseRefSrcFlag = (pictureControlSetPtr->ParentPcsPtr->useSrcRef) && (pictureControlSetPtr->ParentPcsPtr->edgeResultsPtr[lcuIndex].edgeBlockNum == EB_FALSE || pictureControlSetPtr->ParentPcsPtr->lcuFlatNoiseArray[lcuIndex]) ? EB_TRUE : EB_FALSE; // Derive restrictIntraGlobalMotion Flag contextPtr->mdContext->restrictIntraGlobalMotion = ((pictureControlSetPtr->ParentPcsPtr->isPan || pictureControlSetPtr->ParentPcsPtr->isTilt) && pictureControlSetPtr->ParentPcsPtr->nonMovingIndexArray[lcuIndex] < INTRA_GLOBAL_MOTION_NON_MOVING_INDEX_TH && pictureControlSetPtr->ParentPcsPtr->yMean[lcuIndex][RASTER_SCAN_CU_INDEX_64x64] < INTRA_GLOBAL_MOTION_DARK_LCU_TH); - // Configure the LCU + // Configure the LCU ModeDecisionConfigureLcu( // HT done - contextPtr->mdContext, - lcuPtr, - pictureControlSetPtr, - sequenceControlSetPtr, - contextPtr->qp, - lcuPtr->qp); + contextPtr->mdContext, + lcuPtr, + pictureControlSetPtr, + sequenceControlSetPtr, + contextPtr->qp, + lcuPtr->qp); LcuParams_t * lcuParamPtr = &sequenceControlSetPtr->lcuParamsArray[lcuIndex]; if ( - pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_FULL85_DEPTH_MODE || - pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_FULL84_DEPTH_MODE || - pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_OPEN_LOOP_DEPTH_MODE || - (pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_LCU_SWITCH_DEPTH_MODE && (pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_FULL85_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_FULL84_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_OPEN_LOOP_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_LIGHT_OPEN_LOOP_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_AVC_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_LIGHT_AVC_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_PRED_OPEN_LOOP_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_PRED_OPEN_LOOP_1_NFL_DEPTH_MODE))) { + pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_FULL85_DEPTH_MODE || + pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_FULL84_DEPTH_MODE || + pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_OPEN_LOOP_DEPTH_MODE || + (pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_LCU_SWITCH_DEPTH_MODE && (pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_FULL85_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_FULL84_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_OPEN_LOOP_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_LIGHT_OPEN_LOOP_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_AVC_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_LIGHT_AVC_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_PRED_OPEN_LOOP_DEPTH_MODE || pictureControlSetPtr->ParentPcsPtr->lcuMdModeArray[lcuIndex] == LCU_PRED_OPEN_LOOP_1_NFL_DEPTH_MODE))) { // Define Inputs / Outputs ModeDecisionLcu( // HT done - sequenceControlSetPtr, - pictureControlSetPtr, - mdcPtr, - lcuPtr, - (EB_U16)lcuOriginX, - (EB_U16)lcuOriginY, - (EB_U32)lcuIndex, - contextPtr->mdContext); + sequenceControlSetPtr, + pictureControlSetPtr, + mdcPtr, + lcuPtr, + (EB_U16)lcuOriginX, + (EB_U16)lcuOriginY, + (EB_U32)lcuIndex, + contextPtr->mdContext); // Muli-stage MD: INTRA_4x4 Refinment ModeDecisionRefinementLcu( - pictureControlSetPtr, - lcuPtr, - lcuOriginX, - lcuOriginY, - contextPtr->mdContext); + pictureControlSetPtr, + lcuPtr, + lcuOriginX, + lcuOriginY, + contextPtr->mdContext); // Link MD to BDP (could be done after INTRA4x4 refinment) if (pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_LCU_SWITCH_DEPTH_MODE && pictureControlSetPtr->bdpPresentFlag) { LinkMdtoBdp( - pictureControlSetPtr, - lcuPtr, - contextPtr->mdContext); + pictureControlSetPtr, + lcuPtr, + contextPtr->mdContext); } } else { // Pillar: 32x32 vs 16x16 BdpPillar( - sequenceControlSetPtr, - pictureControlSetPtr, - lcuParamPtr, - lcuPtr, - lcuIndex, - contextPtr->mdContext); + sequenceControlSetPtr, + pictureControlSetPtr, + lcuParamPtr, + lcuPtr, + lcuIndex, + contextPtr->mdContext); // If all 4 quadrants are CU32x32, THEN compare the 4 CU32x32 to CU64x64 EB_BOOL isFourCu32x32 = (lcuParamPtr->isCompleteLcu && pictureControlSetPtr->temporalLayerIndex > 0 && - lcuPtr->codedLeafArrayPtr[1]->splitFlag == EB_FALSE && - lcuPtr->codedLeafArrayPtr[22]->splitFlag == EB_FALSE && - lcuPtr->codedLeafArrayPtr[43]->splitFlag == EB_FALSE && - lcuPtr->codedLeafArrayPtr[64]->splitFlag == EB_FALSE); + lcuPtr->codedLeafArrayPtr[1]->splitFlag == EB_FALSE && + lcuPtr->codedLeafArrayPtr[22]->splitFlag == EB_FALSE && + lcuPtr->codedLeafArrayPtr[43]->splitFlag == EB_FALSE && + lcuPtr->codedLeafArrayPtr[64]->splitFlag == EB_FALSE); if (pictureControlSetPtr->sliceType != EB_I_PICTURE && isFourCu32x32) { // 64x64 refinement stage Bdp64x64vs32x32RefinementProcess( - pictureControlSetPtr, - lcuParamPtr, - lcuPtr, - lcuIndex, - contextPtr->mdContext); + pictureControlSetPtr, + lcuParamPtr, + lcuPtr, + lcuIndex, + contextPtr->mdContext); } // 8x8 refinement stage Bdp16x16vs8x8RefinementProcess( - sequenceControlSetPtr, - pictureControlSetPtr, - lcuParamPtr, - lcuPtr, - lcuIndex, - contextPtr->mdContext); - - // MV Merge Pass - if (pictureControlSetPtr->sliceType != EB_I_PICTURE) { - BdpMvMergePass( + sequenceControlSetPtr, pictureControlSetPtr, lcuParamPtr, lcuPtr, lcuIndex, contextPtr->mdContext); + + // MV Merge Pass + if (pictureControlSetPtr->sliceType != EB_I_PICTURE) { + BdpMvMergePass( + pictureControlSetPtr, + lcuParamPtr, + lcuPtr, + lcuIndex, + contextPtr->mdContext); } // Muli-stage MD: INTRA_4x4 Refinment ModeDecisionRefinementLcu( - pictureControlSetPtr, - lcuPtr, - lcuOriginX, - lcuOriginY, - contextPtr->mdContext); + pictureControlSetPtr, + lcuPtr, + lcuOriginX, + lcuOriginY, + contextPtr->mdContext); // Link BDP to MD (could be done after INTRA4x4 refinment) if (pictureControlSetPtr->ParentPcsPtr->depthMode == PICT_LCU_SWITCH_DEPTH_MODE && pictureControlSetPtr->mdPresentFlag) { LinkBdptoMd( - pictureControlSetPtr, - lcuPtr, - contextPtr->mdContext); + pictureControlSetPtr, + lcuPtr, + contextPtr->mdContext); } } @@ -4336,12 +4408,12 @@ void* EncDecKernel(void *inputPtr) // Configure the LCU EncDecConfigureLcu( // HT done - contextPtr, - lcuPtr, - pictureControlSetPtr, - sequenceControlSetPtr, - contextPtr->qp, - lcuPtr->qp); + contextPtr, + lcuPtr, + pictureControlSetPtr, + sequenceControlSetPtr, + contextPtr->qp, + lcuPtr->qp); // Encode Pass @@ -4358,16 +4430,16 @@ void* EncDecKernel(void *inputPtr) if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) { /*Entropy Estimation for LCU*/ - tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); EstimateLcu( lcuPtr, lcuOriginX, lcuOriginY, pictureControlSetPtr, sequenceControlSetPtr->lcuSize, - pictureControlSetPtr->tempEntropyCoderPtr, + pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr, tempCoeffPicturePtr, pictureControlSetPtr->tempModeTypeNeighborArray, pictureControlSetPtr->tempLeafDepthNeighborArray, @@ -4376,9 +4448,9 @@ void* EncDecKernel(void *inputPtr) 0, 0); - tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->tempEntropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*)pictureControlSetPtr->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); lcuPtr->proxytotalBits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; //Update CU Stats for row level vbv control EbBlockOnMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); @@ -4407,37 +4479,41 @@ void* EncDecKernel(void *inputPtr) EbBlockOnMutex(pictureControlSetPtr->intraMutex); pictureControlSetPtr->intraCodedArea += (EB_U32)contextPtr->totIntraCodedArea; + pictureControlSetPtr->encDecCodedLcuCount += (EB_U32)contextPtr->codedLcuCount; + lastLcuFlag = (pictureControlSetPtr->lcuTotalCount == pictureControlSetPtr->encDecCodedLcuCount); + //printf("[%p]: Tile %d, coded lcu count %d, total coded lcu count %d, lastLcuFlag is %d\n", + // contextPtr, encDecTasksPtr->tileIndex, + // contextPtr->codedLcuCount, + // pictureControlSetPtr->encDecCodedLcuCount, lastLcuFlag); EbReleaseMutex(pictureControlSetPtr->intraMutex); if (lastLcuFlag) { - if (pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr != NULL){ // copy stat to ref object (intraCodedArea, Luminance, Scene change detection flags) CopyStatisticsToRefObject( - pictureControlSetPtr, - sequenceControlSetPtr); + pictureControlSetPtr, + sequenceControlSetPtr); } - + EB_BOOL applySAOAtEncoderFlag = sequenceControlSetPtr->staticConfig.enableSaoFlag && (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag || - sequenceControlSetPtr->staticConfig.reconEnabled); + sequenceControlSetPtr->staticConfig.reconEnabled); applySAOAtEncoderFlag = contextPtr->allowEncDecMismatch ? EB_FALSE : applySAOAtEncoderFlag; if (applySAOAtEncoderFlag) { - if (is16bit) { ApplySaoOffsetsPicture16bit( - contextPtr, - sequenceControlSetPtr, - pictureControlSetPtr); + contextPtr, + sequenceControlSetPtr, + pictureControlSetPtr); } else { ApplySaoOffsetsPicture( - contextPtr, - sequenceControlSetPtr, - pictureControlSetPtr); + contextPtr, + sequenceControlSetPtr, + pictureControlSetPtr); } } @@ -4445,8 +4521,8 @@ void* EncDecKernel(void *inputPtr) // Pad the reference picture and set up TMVP flag and ref POC if (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag == EB_TRUE) PadRefAndSetFlags( - pictureControlSetPtr, - sequenceControlSetPtr); + pictureControlSetPtr, + sequenceControlSetPtr); if (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag == EB_TRUE && pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr) { @@ -4469,58 +4545,59 @@ void* EncDecKernel(void *inputPtr) for (verticalIdx = 0; verticalIdx < refDenPic->height; ++verticalIdx) { EB_MEMCPY(refDenPic->bufferY + ReflumaOffSet + verticalIdx*refDenPic->strideY, - inputPicturePtr->bufferY + SrclumaOffSet + verticalIdx* inputPicturePtr->strideY, - inputPicturePtr->width); + inputPicturePtr->bufferY + SrclumaOffSet + verticalIdx* inputPicturePtr->strideY, + inputPicturePtr->width); } for (verticalIdx = 0; verticalIdx < inputPicturePtr->height >> subHeightCMinus1; ++verticalIdx) { EB_MEMCPY(refDenPic->bufferCb + RefcbOffset + verticalIdx*refDenPic->strideCb, - inputPicturePtr->bufferCb + SrccbOffset + verticalIdx* inputPicturePtr->strideCb, - inputPicturePtr->width >> subWidthCMinus1); + inputPicturePtr->bufferCb + SrccbOffset + verticalIdx* inputPicturePtr->strideCb, + inputPicturePtr->width >> subWidthCMinus1); EB_MEMCPY(refDenPic->bufferCr + RefcrOffset + verticalIdx*refDenPic->strideCr, - inputPicturePtr->bufferCr + SrccrOffset + verticalIdx* inputPicturePtr->strideCr, - inputPicturePtr->width >> subWidthCMinus1 ); + inputPicturePtr->bufferCr + SrccrOffset + verticalIdx* inputPicturePtr->strideCr, + inputPicturePtr->width >> subWidthCMinus1 ); } GeneratePadding( - refDenPic->bufferY, - refDenPic->strideY, - refDenPic->width, - refDenPic->height, - refDenPic->originX, - refDenPic->originY); + refDenPic->bufferY, + refDenPic->strideY, + refDenPic->width, + refDenPic->height, + refDenPic->originX, + refDenPic->originY); GeneratePadding( - refDenPic->bufferCb, - refDenPic->strideCb, - refDenPic->width >> subWidthCMinus1, - refDenPic->height >> subHeightCMinus1, - refDenPic->originX >> subWidthCMinus1, - refDenPic->originY >> subHeightCMinus1); + refDenPic->bufferCb, + refDenPic->strideCb, + refDenPic->width >> subWidthCMinus1, + refDenPic->height >> subHeightCMinus1, + refDenPic->originX >> subWidthCMinus1, + refDenPic->originY >> subHeightCMinus1); GeneratePadding( - refDenPic->bufferCr, - refDenPic->strideCr, - refDenPic->width >> subWidthCMinus1, - refDenPic->height >> subHeightCMinus1, - refDenPic->originX >> subWidthCMinus1, - refDenPic->originY >> subHeightCMinus1); + refDenPic->bufferCr, + refDenPic->strideCr, + refDenPic->width >> subWidthCMinus1, + refDenPic->height >> subHeightCMinus1, + refDenPic->originX >> subWidthCMinus1, + refDenPic->originY >> subHeightCMinus1); } if (sequenceControlSetPtr->staticConfig.reconEnabled) { ReconOutput( - pictureControlSetPtr, - sequenceControlSetPtr); + pictureControlSetPtr, + sequenceControlSetPtr); } if (pictureControlSetPtr->ParentPcsPtr->isUsedAsReferenceFlag) { + //Jing: TODO: double check here // Get Empty EntropyCoding Results EbGetEmptyObject( - contextPtr->pictureDemuxOutputFifoPtr, - &pictureDemuxResultsWrapperPtr); + contextPtr->pictureDemuxOutputFifoPtr, + &pictureDemuxResultsWrapperPtr); pictureDemuxResultsPtr = (PictureDemuxResults_t*)pictureDemuxResultsWrapperPtr->objectPtr; pictureDemuxResultsPtr->referencePictureWrapperPtr = pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr; @@ -4530,6 +4607,25 @@ void* EncDecKernel(void *inputPtr) // Post Reference Picture EbPostFullObject(pictureDemuxResultsWrapperPtr); +#if LATENCY_PROFILE + double latency = 0.0; + EB_U64 finishTimeSeconds = 0; + EB_U64 finishTimeuSeconds = 0; + EbFinishTime((uint64_t*)&finishTimeSeconds, (uint64_t*)&finishTimeuSeconds); + + EbComputeOverallElapsedTimeMs( + pictureControlSetPtr->ParentPcsPtr->startTimeSeconds, + pictureControlSetPtr->ParentPcsPtr->startTimeuSeconds, + finishTimeSeconds, + finishTimeuSeconds, + &latency); + + SVT_LOG("[%lld]: POC %lld ENCDEC REF DONE, decoder order %d, latency %3.3f \n", + EbGetSysTimeMs(), + pictureControlSetPtr->pictureNumber, + pictureControlSetPtr->ParentPcsPtr->decodeOrder, + latency); +#endif } // When de interlacing is performed in the lib, each two consecutive pictures (fields: top & bottom) are going to use the same input buffer @@ -4545,21 +4641,24 @@ void* EncDecKernel(void *inputPtr) #endif // Send the Entropy Coder incremental updates as each LCU row becomes available - { if (endOfRowFlag == EB_TRUE) { - - // Get Empty EncDec Results - EbGetEmptyObject( - contextPtr->encDecOutputFifoPtr, - &encDecResultsWrapperPtr); - encDecResultsPtr = (EncDecResults_t*)encDecResultsWrapperPtr->objectPtr; - encDecResultsPtr->pictureControlSetWrapperPtr = encDecTasksPtr->pictureControlSetWrapperPtr; - encDecResultsPtr->completedLcuRowIndexStart = lcuRowIndexStart; - encDecResultsPtr->completedLcuRowCount = lcuRowIndexCount; - - // Post EncDec Results - EbPostFullObject(encDecResultsWrapperPtr); + for (unsigned int tileIdx = tileRowIndex * sequenceControlSetPtr->tileColumnCount; + tileIdx < (tileRowIndex + 1) * sequenceControlSetPtr->tileColumnCount; + tileIdx++) { + // Get Empty EncDec Results + EbGetEmptyObject( + contextPtr->encDecOutputFifoPtr, + &encDecResultsWrapperPtr); + encDecResultsPtr = (EncDecResults_t*)encDecResultsWrapperPtr->objectPtr; + encDecResultsPtr->pictureControlSetWrapperPtr = encDecTasksPtr->pictureControlSetWrapperPtr; + encDecResultsPtr->completedLcuRowIndexStart = lcuRowIndexStart; + encDecResultsPtr->completedLcuRowCount = lcuRowIndexCount; + encDecResultsPtr->tileIndex = tileIdx; + + // Post EncDec Results + EbPostFullObject(encDecResultsWrapperPtr); + } } } diff --git a/Source/Lib/Codec/EbEncDecProcess.h b/Source/Lib/Codec/EbEncDecProcess.h index 3f08176e6..89b25243b 100644 --- a/Source/Lib/Codec/EbEncDecProcess.h +++ b/Source/Lib/Codec/EbEncDecProcess.h @@ -114,6 +114,7 @@ typedef struct EncDecContext_s EB_U16 *saoLeftBuffer16[2]; EB_U64 totIntraCodedArea; + EB_U32 codedLcuCount; EB_U8 intraCodedAreaLCU[MAX_NUMBER_OF_TREEBLOCKS_PER_PICTURE];//percentage of intra coded area 0-100% EB_U8 cleanSparseCeoffPfEncDec; EB_U8 pmpMaskingLevelEncDec; @@ -145,6 +146,10 @@ typedef struct EncDecContext_s EB_U8 saoMode; EB_PM_MODE pmMode; // agressive vs. conservative EB_BOOL pmMethod; // 1-stgae vs. 2-stage + + EB_U16 tileRowIndex; + EB_U16 tileIndex; + //// } EncDecContext_t; /************************************** @@ -165,4 +170,4 @@ extern void* EncDecKernel(void *inputPtr); #ifdef __cplusplus } #endif -#endif // EbEncDecProcess_h \ No newline at end of file +#endif // EbEncDecProcess_h diff --git a/Source/Lib/Codec/EbEncDecResults.h b/Source/Lib/Codec/EbEncDecResults.h index 58e1d2d6c..5c5ba8556 100644 --- a/Source/Lib/Codec/EbEncDecResults.h +++ b/Source/Lib/Codec/EbEncDecResults.h @@ -20,6 +20,7 @@ typedef struct EncDecResults_s EB_U32 completedLcuRowIndexStart; EB_U32 completedLcuRowCount; + EB_U32 tileIndex; } EncDecResults_t; typedef struct EncDecResultsInitData_s @@ -38,4 +39,4 @@ extern EB_ERRORTYPE EncDecResultsCtor( #ifdef __cplusplus } #endif -#endif // EbEncDecResults_h \ No newline at end of file +#endif // EbEncDecResults_h diff --git a/Source/Lib/Codec/EbEncDecTasks.h b/Source/Lib/Codec/EbEncDecTasks.h index 67bb56b1e..7c9419f95 100644 --- a/Source/Lib/Codec/EbEncDecTasks.h +++ b/Source/Lib/Codec/EbEncDecTasks.h @@ -24,6 +24,7 @@ typedef struct EncDecTasks_s EB_U32 inputType; EB_S16 encDecSegmentRow; + EB_U32 tileRowIndex; } EncDecTasks_t; typedef struct EncDecTasksInitData_s diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index fb06860ef..1b16d13a7 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -818,6 +818,9 @@ EB_API EB_ERRORTYPE EbInitEncoder(EB_COMPONENTTYPE *h265EncComponent) inputData.lcuSize = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->lcuSize; inputData.maxDepth = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->maxLcuDepth; inputData.is16bit = is16bit; + inputData.tileRowCount = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.tileRowCount; + inputData.tileColumnCount = encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->staticConfig.tileColumnCount; + return_error = EbSystemResourceCtor( &(encHandlePtr->pictureControlSetPoolPtrArray[instanceIndex]), encHandlePtr->sequenceControlSetInstanceArray[instanceIndex]->sequenceControlSetPtr->pictureControlSetPoolInitCountChild, //EB_PictureControlSetPoolInitCountChild, @@ -1761,17 +1764,17 @@ void LoadDefaultBufferConfigurationSettings( //#====================== Inter process Fifos ====================== sequenceControlSetPtr->resourceCoordinationFifoInitCount = 300; - sequenceControlSetPtr->pictureAnalysisFifoInitCount = 300; - sequenceControlSetPtr->pictureDecisionFifoInitCount = 300; - sequenceControlSetPtr->initialRateControlFifoInitCount = 300; - sequenceControlSetPtr->pictureDemuxFifoInitCount = 300; - sequenceControlSetPtr->rateControlTasksFifoInitCount = 300; - sequenceControlSetPtr->rateControlFifoInitCount = 301; - sequenceControlSetPtr->modeDecisionFifoInitCount = 300; - sequenceControlSetPtr->modeDecisionConfigurationFifoInitCount = 300; - sequenceControlSetPtr->motionEstimationFifoInitCount = 300; - sequenceControlSetPtr->entropyCodingFifoInitCount = 300; - sequenceControlSetPtr->encDecFifoInitCount = 300; + sequenceControlSetPtr->pictureAnalysisFifoInitCount = 301; + sequenceControlSetPtr->pictureDecisionFifoInitCount = 302; + sequenceControlSetPtr->initialRateControlFifoInitCount = 303; + sequenceControlSetPtr->pictureDemuxFifoInitCount = 304; + sequenceControlSetPtr->rateControlTasksFifoInitCount = 305; + sequenceControlSetPtr->rateControlFifoInitCount = 306; + //sequenceControlSetPtr->modeDecisionFifoInitCount = 307; + sequenceControlSetPtr->modeDecisionConfigurationFifoInitCount = (300 * sequenceControlSetPtr->tileRowCount); + sequenceControlSetPtr->motionEstimationFifoInitCount = 308; + sequenceControlSetPtr->entropyCodingFifoInitCount = 309; + sequenceControlSetPtr->encDecFifoInitCount = 900; //#====================== Processes number ====================== sequenceControlSetPtr->totalProcessInitCount = 0; @@ -2076,6 +2079,9 @@ void CopyApiFromApp( sequenceControlSetPtr->intraRefreshType = sequenceControlSetPtr->staticConfig.intraRefreshType; sequenceControlSetPtr->maxTemporalLayers = sequenceControlSetPtr->staticConfig.hierarchicalLevels; sequenceControlSetPtr->maxRefCount = 1; + sequenceControlSetPtr->tileRowCount = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->tileRowCount; + sequenceControlSetPtr->tileColumnCount = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->tileColumnCount; + sequenceControlSetPtr->tileSliceMode = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->tileSliceMode; // Quantization @@ -2859,7 +2865,6 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } -#if TILES //Check tiles //TODO: Check maxTileCol/maxTileRow according to profile/level later uint32_t pictureWidthInLcu = (config->sourceWidth + MAX_LCU_SIZE - 1) / MAX_LCU_SIZE; @@ -2880,7 +2885,6 @@ static EB_ERRORTYPE VerifySettings(\ break; } } -#endif return return_error; } @@ -2914,11 +2918,9 @@ EB_ERRORTYPE EbH265EncInitParameter( configPtr->interlacedVideo = EB_FALSE; configPtr->qp = 32; configPtr->useQpFile = EB_FALSE; -#if TILES configPtr->tileRowCount = 1; configPtr->tileColumnCount = 1; configPtr->tileSliceMode = 0; -#endif configPtr->sceneChangeDetection = 1; configPtr->rateControlMode = 0; configPtr->lookAheadDistance = (EB_U32)~0; diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index ceedac3d6..14fd197d8 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -6191,9 +6191,8 @@ static void CodePPS( //SequenceControlSet_t *scsPtr = (SequenceControlSet_t*)pcsPtr->sequenceControlSetWrapperPtr->objectPtr; EB_BOOL disableDlfFlag = scsPtr->staticConfig.disableDlfFlag; -#if TILES EB_BOOL tileMode = (scsPtr->tileColumnCount > 1 || scsPtr->tileRowCount > 1) ? EB_TRUE : EB_FALSE; -#endif + // uiFirstByte //codeNALUnitHeader( NAL_UNIT_PPS, NAL_REF_IDC_PRIORITY_HIGHEST ); CodeNALUnitHeader( @@ -6315,17 +6314,13 @@ static void CodePPS( // "tiles_enabled_flag" WriteFlagCavlc( bitstreamPtr, -#if TILES tileMode); -#else - 0); -#endif // "entropy_coding_sync_enabled_flag" WriteFlagCavlc( bitstreamPtr, 0); -#if TILES + if (tileMode == EB_TRUE) { // Tiles Number of Columns @@ -6370,15 +6365,14 @@ static void CodePPS( //if(scsPtr->staticConfig.tileColumnCount != 1 || scsPtr->staticConfig.tileRowCount > 1) { WriteFlagCavlc( bitstreamPtr, - 1); + 0); //} } -#endif // "loop_filter_across_slices_enabled_flag" WriteFlagCavlc( bitstreamPtr, - 1); + 0); // "deblocking_filter_control_present_flag" WriteFlagCavlc( @@ -6439,6 +6433,29 @@ static void CodePPS( return; } +static EB_U32 GetEntropyCoderGetBitstreamSize(EntropyCoder_t *entropyCoderPtr) +{ + CabacEncodeContext_t *cabacEncCtxPtr = (CabacEncodeContext_t*)entropyCoderPtr->cabacEncodeContextPtr; + OutputBitstreamUnit_t *bitstreamPtr = &cabacEncCtxPtr->bacEncContext.m_pcTComBitIf; + unsigned payloadBytes = (cabacEncCtxPtr->bacEncContext.m_pcTComBitIf.writtenBitsCount) >> 3; + FlushBitstream(bitstreamPtr); + + unsigned char *buf = (unsigned char *)bitstreamPtr->bufferBegin; + unsigned int emulationCount = 0; + + //Count emulation counts 0x000001 and 0x000002 + if (payloadBytes >= 3) { + for (unsigned i = 0; i <= payloadBytes - 3; i++) { + if (buf[i] == 0 && buf[i+1] == 0 && (buf[i+2] <= 3)) { + emulationCount++; + i++; + } + } + } + + return (payloadBytes + emulationCount); +} + static void CodeSliceHeader( EB_U32 firstLcuAddr, EB_U32 pictureQp, @@ -6455,9 +6472,8 @@ static void CodeSliceHeader( EB_BOOL disableDlfFlag = sequenceControlSetPtr->staticConfig.disableDlfFlag; EB_U32 sliceType = (pcsPtr->ParentPcsPtr->idrFlag == EB_TRUE) ? EB_I_PICTURE : pcsPtr->sliceType; -#if TILES EB_BOOL tileMode = (sequenceControlSetPtr->tileColumnCount > 1 || sequenceControlSetPtr->tileRowCount > 1) ? EB_TRUE : EB_FALSE; -#endif + EB_U32 refPicsTotalCount = pcsPtr->ParentPcsPtr->predStructPtr->predStructEntryPtrArray[pcsPtr->ParentPcsPtr->predStructIndex]->negativeRefPicsTotalCount + pcsPtr->ParentPcsPtr->predStructPtr->predStructEntryPtrArray[pcsPtr->ParentPcsPtr->predStructIndex]->positiveRefPicsTotalCount; @@ -6728,26 +6744,53 @@ static void CodeSliceHeader( (pcsPtr->tcOffset >> 1)); } - if (!disableDlfFlag || (sequenceControlSetPtr->staticConfig.enableSaoFlag && (pcsPtr->saoFlag[0] || pcsPtr->saoFlag[1]))) { - // "slice_loop_filter_across_slices_enabled_flag" - WriteFlagCavlc( - bitstreamPtr, - 1); - } + //if (!disableDlfFlag || (sequenceControlSetPtr->staticConfig.enableSaoFlag && (pcsPtr->saoFlag[0] || pcsPtr->saoFlag[1]))) { + // // "slice_loop_filter_across_slices_enabled_flag" + // WriteFlagCavlc( + // bitstreamPtr, + // 1); + //} -#if TILES if (tileMode) { unsigned tileColumnNumMinus1 = sequenceControlSetPtr->tileColumnCount - 1; unsigned tileRowNumMinus1 = sequenceControlSetPtr->tileRowCount - 1; + unsigned num_entry_point_offsets = sequenceControlSetPtr->tileColumnCount * sequenceControlSetPtr->tileRowCount - 1; if (tileColumnNumMinus1 > 0 || tileRowNumMinus1 > 0) { + EB_U32 maxOffset = 0; + EB_U32 offset[EB_TILE_MAX_COUNT]; + for (unsigned tileIdx = 0; tileIdx < num_entry_point_offsets; tileIdx++) { + offset[tileIdx] = GetEntropyCoderGetBitstreamSize(pcsPtr->entropyCodingInfo[tileIdx]->entropyCoderPtr); + if (offset[tileIdx] > maxOffset) { + maxOffset = offset[tileIdx]; + } + //printf("tile %d, size %d\n", tileIdx, offset); + } + + EB_U32 offsetLenMinus1 = 0; + while (maxOffset >= (1u << (offsetLenMinus1 + 1))) { + offsetLenMinus1++; + //assert(offsetLenMinus1 + 1 < 32); + } + // "num_entry_point_offsets" WriteUvlc( bitstreamPtr, - 0); + num_entry_point_offsets); + + if (num_entry_point_offsets > 0) { + WriteUvlc( + bitstreamPtr, + offsetLenMinus1); + + for (unsigned tileIdx = 0; tileIdx < num_entry_point_offsets; tileIdx++) { + //EB_U32 offset = GetEntropyCoderGetBitstreamSize(pcsPtr->entropyCodingInfo[tileIdx]->entropyCoderPtr); + WriteCodeCavlc(bitstreamPtr, offset[tileIdx] - 1, offsetLenMinus1 + 1); + } + } } } -#endif + // Byte Alignment //pcBitstreamOut->write( 1, 1 ); @@ -6761,7 +6804,7 @@ static void CodeSliceHeader( bitstreamPtr); } -#if TILES + EB_ERRORTYPE EncodeTileFinish( EntropyCoder_t *entropyCoderPtr) { @@ -6785,7 +6828,7 @@ EB_ERRORTYPE EncodeTileFinish( return return_error; } -#endif + EB_ERRORTYPE EncodeLcuSaoParameters( LargestCodingUnit_t *tbPtr, EntropyCoder_t *entropyCoderPtr, @@ -6801,30 +6844,20 @@ EB_ERRORTYPE EncodeLcuSaoParameters( // This needs to be revisited when there is more than one slice per tile // Code Luma SAO parameters // Code Luma SAO parameters -#if TILES if (tbPtr->tileLeftEdgeFlag == EB_FALSE) { -#else - if (tbPtr->pictureLeftEdgeFlag == EB_FALSE) { -#endif EncodeSaoMerge( cabacEncodeCtxPtr, tbPtr->saoParams.saoMergeLeftFlag); - } - else { + } else { tbPtr->saoParams.saoMergeLeftFlag = EB_FALSE; } if (tbPtr->saoParams.saoMergeLeftFlag == 0) { -#if TILES if (tbPtr->tileTopEdgeFlag == EB_FALSE) { -#else - if (tbPtr->pictureTopEdgeFlag == EB_FALSE) { -#endif EncodeSaoMerge( cabacEncodeCtxPtr, tbPtr->saoParams.saoMergeUpFlag); - } - else { + } else { tbPtr->saoParams.saoMergeUpFlag = EB_FALSE; } } @@ -7777,6 +7810,7 @@ EB_ERRORTYPE EncodeLcu( NeighborArrayUnit_t *leafDepthNeighborArray, NeighborArrayUnit_t *intraLumaModeNeighborArray, NeighborArrayUnit_t *skipFlagNeighborArray, + EB_U16 tileIdx, EB_U32 pictureOriginX, EB_U32 pictureOriginY) { @@ -7880,8 +7914,8 @@ EB_ERRORTYPE EncodeLcu( sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? EB_TRUE : EB_FALSE, &entropyDeltaQpNotCoded, pictureControlSetPtr->difCuDeltaQpDepth, - &pictureControlSetPtr->prevCodedQp, - &pictureControlSetPtr->prevQuantGroupCodedQp, + &pictureControlSetPtr->prevCodedQp[tileIdx], + &pictureControlSetPtr->prevQuantGroupCodedQp[tileIdx], tbPtr->qp, pictureControlSetPtr, pictureOriginX, @@ -8797,7 +8831,7 @@ EB_ERRORTYPE CodeBufferingPeriodSEI( WriteCodeCavlc( bitstreamPtr, bufferingPeriodPtr->cpbDelayOffset, - vuiPtr->hrdParametersPtr->initialCpbRemovalDelayLengthMinus1 + 1); + vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1); // dpb_delay_offset WriteCodeCavlc( bitstreamPtr, @@ -8814,7 +8848,7 @@ EB_ERRORTYPE CodeBufferingPeriodSEI( WriteCodeCavlc( bitstreamPtr, bufferingPeriodPtr->auCpbRemovalDelayDeltaMinus1, - vuiPtr->hrdParametersPtr->initialCpbRemovalDelayLengthMinus1 + 1); + vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1); for (nalVclIndex = 0; nalVclIndex < 2; ++nalVclIndex){ if ((nalVclIndex == 0 && vuiPtr->hrdParametersPtr->nalHrdParametersPresentFlag) || diff --git a/Source/Lib/Codec/EbEntropyCoding.h b/Source/Lib/Codec/EbEntropyCoding.h index a5b807694..b29e893f1 100644 --- a/Source/Lib/Codec/EbEntropyCoding.h +++ b/Source/Lib/Codec/EbEntropyCoding.h @@ -41,6 +41,7 @@ extern EB_ERRORTYPE EncodeLcu( NeighborArrayUnit_t *leafDepthNeighborArray, NeighborArrayUnit_t *intraLumaModeNeighborArray, NeighborArrayUnit_t *skipFlagNeighborArray, + EB_U16 tileIdx, EB_U32 pictureOriginX, EB_U32 pictureOriginY); @@ -58,10 +59,9 @@ extern EB_ERRORTYPE EstimateLcu( NeighborArrayUnit_t *skipFlagNeighborArray, EB_U32 pictureOriginX, EB_U32 pictureOriginY); -#if TILES + extern EB_ERRORTYPE EncodeTileFinish( EntropyCoder_t *entropyCoderPtr); -#endif extern EB_ERRORTYPE EncodeLcuSaoParameters( LargestCodingUnit_t *tbPtr, diff --git a/Source/Lib/Codec/EbEntropyCodingProcess.c b/Source/Lib/Codec/EbEntropyCodingProcess.c index 7691eedce..33f167e15 100644 --- a/Source/Lib/Codec/EbEntropyCodingProcess.c +++ b/Source/Lib/Codec/EbEntropyCodingProcess.c @@ -4,7 +4,6 @@ */ #include - #include "EbEntropyCodingProcess.h" #include "EbTransforms.h" #include "EbEncDecResults.h" @@ -29,6 +28,7 @@ EB_ERRORTYPE EntropyCodingContextCtor( // Input/Output System Resource Manager FIFOs contextPtr->encDecInputFifoPtr = encDecInputFifoPtr; + //contextPtr->encDecInputFifoPtr->dbg_info = &contextPtr->debug_info; contextPtr->entropyCodingOutputFifoPtr = packetizationOutputFifoPtr; contextPtr->rateControlOutputFifoPtr = rateControlOutputFifoPtr; @@ -38,12 +38,12 @@ EB_ERRORTYPE EntropyCodingContextCtor( /*********************************************** * Entropy Coding Reset Neighbor Arrays ***********************************************/ -static void EntropyCodingResetNeighborArrays(PictureControlSet_t *pictureControlSetPtr) +static void EntropyCodingResetNeighborArrays(PictureControlSet_t *pictureControlSetPtr, EB_U16 tileIdx) { - NeighborArrayUnitReset(pictureControlSetPtr->modeTypeNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->leafDepthNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->skipFlagNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->intraLumaModeNeighborArray); + NeighborArrayUnitReset(pictureControlSetPtr->modeTypeNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->leafDepthNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->intraLumaModeNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->skipFlagNeighborArray[tileIdx]); return; } @@ -56,7 +56,12 @@ static void ResetEntropyCodingPicture( PictureControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr) { - ResetBitstream(EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCoderPtr)); + EB_U32 tileCnt = pictureControlSetPtr->tileRowCount * pictureControlSetPtr->tileColumnCount; + EB_U32 tileIdx = 0; + + for (tileIdx = 0; tileIdx < tileCnt; tileIdx++) { + ResetBitstream(EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCoderPtr)); + } EB_U32 entropyCodingQp; @@ -84,16 +89,19 @@ static void ResetEntropyCodingPicture( // Reset CABAC Contexts // Reset QP Assignement - pictureControlSetPtr->prevCodedQp = pictureControlSetPtr->pictureQp; - pictureControlSetPtr->prevQuantGroupCodedQp = pictureControlSetPtr->pictureQp; + for (tileIdx = 0; tileIdx < tileCnt; tileIdx++) { + pictureControlSetPtr->prevCodedQp[tileIdx] = pictureControlSetPtr->pictureQp; + pictureControlSetPtr->prevQuantGroupCodedQp[tileIdx] = pictureControlSetPtr->pictureQp; - ResetEntropyCoder( - sequenceControlSetPtr->encodeContextPtr, - pictureControlSetPtr->entropyCoderPtr, - entropyCodingQp, - pictureControlSetPtr->sliceType); + ResetEntropyCoder( + sequenceControlSetPtr->encodeContextPtr, + pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCoderPtr, + entropyCodingQp, + pictureControlSetPtr->sliceType); + + EntropyCodingResetNeighborArrays(pictureControlSetPtr, tileIdx); + } - EntropyCodingResetNeighborArrays(pictureControlSetPtr); return; } @@ -129,6 +137,7 @@ static void EntropyCodingLcu( EB_U32 lcuOriginX, EB_U32 lcuOriginY, EB_BOOL terminateSliceFlag, + EB_U16 tileIdx, EB_U32 pictureOriginX, EB_U32 pictureOriginY) { @@ -138,23 +147,23 @@ static void EntropyCodingLcu( //rate Control EB_U32 writtenBitsBeforeQuantizedCoeff; EB_U32 writtenBitsAfterQuantizedCoeff; - + EntropyCoder_t *entropyCoderPtr = pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCoderPtr; //store the number of written bits before coding quantized coeffs (flush is not called yet): // The total number of bits is // number of written bits // + 32 - bits remaining in interval Low Value // + number of buffered byte * 8 // This should be only for coeffs not any flag - writtenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*) pictureControlSetPtr->entropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*) pictureControlSetPtr->entropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum <<3); + writtenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(entropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*) entropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)entropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum <<3); if(sequenceControlSetPtr->staticConfig.enableSaoFlag && (pictureControlSetPtr->saoFlag[0] || pictureControlSetPtr->saoFlag[1])) { // Code SAO parameters EncodeLcuSaoParameters( lcuPtr, - pictureControlSetPtr->entropyCoderPtr, + entropyCoderPtr, pictureControlSetPtr->saoFlag[0], pictureControlSetPtr->saoFlag[1], (EB_U8)sequenceControlSetPtr->staticConfig.encoderBitDepth); @@ -166,23 +175,27 @@ static void EntropyCodingLcu( lcuOriginY, pictureControlSetPtr, sequenceControlSetPtr->lcuSize, - pictureControlSetPtr->entropyCoderPtr, + entropyCoderPtr, coeffPicturePtr, - pictureControlSetPtr->modeTypeNeighborArray, - pictureControlSetPtr->leafDepthNeighborArray, - pictureControlSetPtr->intraLumaModeNeighborArray, - pictureControlSetPtr->skipFlagNeighborArray, + pictureControlSetPtr->modeTypeNeighborArray[tileIdx], + pictureControlSetPtr->leafDepthNeighborArray[tileIdx], + pictureControlSetPtr->intraLumaModeNeighborArray[tileIdx], + pictureControlSetPtr->skipFlagNeighborArray[tileIdx], + tileIdx, pictureOriginX, pictureOriginY); + //Jing:TODO + // extend the totalBits to tile, for tile based brc + //store the number of written bits after coding quantized coeffs (flush is not called yet): // The total number of bits is // number of written bits // + 32 - bits remaining in interval Low Value // + number of buffered byte * 8 - writtenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*) pictureControlSetPtr->entropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*) pictureControlSetPtr->entropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum <<3); + writtenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(entropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*) entropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*) entropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum <<3); lcuPtr->totalBits = writtenBitsAfterQuantizedCoeff - writtenBitsBeforeQuantizedCoeff; @@ -199,7 +212,7 @@ static void EntropyCodingLcu( for the tile ending LCU. *********************************************************/ EncodeTerminateLcu( - pictureControlSetPtr->entropyCoderPtr, + entropyCoderPtr, terminateSliceFlag); return; @@ -247,151 +260,57 @@ static EB_BOOL UpdateEntropyCodingRows( PictureControlSet_t *pictureControlSetPtr, EB_U32 *rowIndex, EB_U32 rowCount, + EB_U32 tileIdx, EB_BOOL *initialProcessCall) { EB_BOOL processNextRow = EB_FALSE; + EntropyTileInfo *infoPtr = pictureControlSetPtr->entropyCodingInfo[tileIdx]; // Note, any writes & reads to status variables (e.g. inProgress) in MD-CTRL must be thread-safe - EbBlockOnMutex(pictureControlSetPtr->entropyCodingMutex); + EbBlockOnMutex(infoPtr->entropyCodingMutex); // Update availability mask if (*initialProcessCall == EB_TRUE) { unsigned i; for(i=*rowIndex; i < *rowIndex + rowCount; ++i) { - pictureControlSetPtr->entropyCodingRowArray[i] = EB_TRUE; + infoPtr->entropyCodingRowArray[i] = EB_TRUE; } - while(pictureControlSetPtr->entropyCodingRowArray[pictureControlSetPtr->entropyCodingCurrentAvailableRow] == EB_TRUE && - pictureControlSetPtr->entropyCodingCurrentAvailableRow < pictureControlSetPtr->entropyCodingRowCount) + while(infoPtr->entropyCodingRowArray[infoPtr->entropyCodingCurrentAvailableRow] == EB_TRUE && + infoPtr->entropyCodingCurrentAvailableRow < infoPtr->entropyCodingRowCount) { - ++pictureControlSetPtr->entropyCodingCurrentAvailableRow; + ++infoPtr->entropyCodingCurrentAvailableRow; } } // Release inProgress token - if(*initialProcessCall == EB_FALSE && pictureControlSetPtr->entropyCodingInProgress == EB_TRUE) { - pictureControlSetPtr->entropyCodingInProgress = EB_FALSE; + if(*initialProcessCall == EB_FALSE && infoPtr->entropyCodingInProgress == EB_TRUE) { + infoPtr->entropyCodingInProgress = EB_FALSE; } // Test if the picture is not already complete AND not currently being worked on by another ENCDEC process - if(pictureControlSetPtr->entropyCodingCurrentRow < pictureControlSetPtr->entropyCodingRowCount && - pictureControlSetPtr->entropyCodingRowArray[pictureControlSetPtr->entropyCodingCurrentRow] == EB_TRUE && - pictureControlSetPtr->entropyCodingInProgress == EB_FALSE) + if(infoPtr->entropyCodingCurrentRow < infoPtr->entropyCodingRowCount && + infoPtr->entropyCodingRowArray[infoPtr->entropyCodingCurrentRow] == EB_TRUE && + infoPtr->entropyCodingInProgress == EB_FALSE) { // Test if the next LCU-row is ready to go - if(pictureControlSetPtr->entropyCodingCurrentRow <= pictureControlSetPtr->entropyCodingCurrentAvailableRow) + if(infoPtr->entropyCodingCurrentRow <= infoPtr->entropyCodingCurrentAvailableRow) { - pictureControlSetPtr->entropyCodingInProgress = EB_TRUE; - *rowIndex = pictureControlSetPtr->entropyCodingCurrentRow++; + infoPtr->entropyCodingInProgress = EB_TRUE; + *rowIndex = infoPtr->entropyCodingCurrentRow++; processNextRow = EB_TRUE; } } *initialProcessCall = EB_FALSE; - EbReleaseMutex(pictureControlSetPtr->entropyCodingMutex); - - return processNextRow; -} -#if TILES - -static EB_BOOL WaitForAllEntropyCodingRows( - PictureControlSet_t *pictureControlSetPtr, - EB_U32 *rowIndex, - EB_U32 rowCount, - EB_BOOL *initialProcessCall) -{ - EB_BOOL processNextRow = EB_TRUE; - unsigned i; - - // Note, any writes & reads to status variables (e.g. inProgress) in MD-CTRL must be thread-safe - EbBlockOnMutex(pictureControlSetPtr->entropyCodingMutex); - - // Update availability mask - if (*initialProcessCall == EB_TRUE) { - - for (i = *rowIndex; i < *rowIndex + rowCount; ++i) { - pictureControlSetPtr->entropyCodingRowArray[i] = EB_TRUE; - } - - //pictureControlSetPtr->entropyCodingCurrentAvailableRow = MAX(((EB_S32) (*rowIndex)),pictureControlSetPtr->entropyCodingCurrentAvailableRow); - while (pictureControlSetPtr->entropyCodingRowArray[pictureControlSetPtr->entropyCodingCurrentAvailableRow] == EB_TRUE && - pictureControlSetPtr->entropyCodingCurrentAvailableRow < pictureControlSetPtr->entropyCodingRowCount) - { - ++pictureControlSetPtr->entropyCodingCurrentAvailableRow; - } - } - - // Release inProgress token - if (*initialProcessCall == EB_FALSE && pictureControlSetPtr->entropyCodingInProgress == EB_TRUE) { - pictureControlSetPtr->entropyCodingInProgress = EB_FALSE; - } - - // Test if all rows are available - for (i = 0; i < (unsigned)pictureControlSetPtr->entropyCodingRowCount; ++i) { - processNextRow = (pictureControlSetPtr->entropyCodingRowArray[i] == EB_FALSE) ? EB_FALSE : processNextRow; - } - - // Test if the picture is already being processed - processNextRow = (pictureControlSetPtr->entropyCodingInProgress) ? EB_FALSE : processNextRow; - - *initialProcessCall = EB_FALSE; - - EbReleaseMutex(pictureControlSetPtr->entropyCodingMutex); + EbReleaseMutex(infoPtr->entropyCodingMutex); return processNextRow; } -/************************************************** - * Reset Entropy Coding Tiles - **************************************************/ -static void ResetEntropyCodingTile( - EntropyCodingContext_t *contextPtr, - PictureControlSet_t *pictureControlSetPtr, - SequenceControlSet_t *sequenceControlSetPtr, - EB_U32 lcuRowIndex, - EB_BOOL tilesActiveFlag) -{ - EB_U32 entropyCodingQp; - - contextPtr->is16bit = (EB_BOOL)(sequenceControlSetPtr->staticConfig.encoderBitDepth > EB_8BIT); - - // SAO - pictureControlSetPtr->saoFlag[0] = EB_TRUE; - pictureControlSetPtr->saoFlag[1] = EB_TRUE; - - // QP - contextPtr->qp = pictureControlSetPtr->pictureQp; - contextPtr->chromaQp = MapChromaQp(contextPtr->qp); - - if (pictureControlSetPtr->useDeltaQp) { - entropyCodingQp = pictureControlSetPtr->pictureQp; - } - else { - entropyCodingQp = pictureControlSetPtr->pictureQp; - } - - - // Reset CABAC Contexts - if (lcuRowIndex == 0 || tilesActiveFlag) - { - // Reset QP Assignement - pictureControlSetPtr->prevCodedQp = pictureControlSetPtr->pictureQp; - pictureControlSetPtr->prevQuantGroupCodedQp = pictureControlSetPtr->pictureQp; - - ResetEntropyCoder( - sequenceControlSetPtr->encodeContextPtr, - pictureControlSetPtr->entropyCoderPtr, - entropyCodingQp, - pictureControlSetPtr->sliceType); - EntropyCodingResetNeighborArrays(pictureControlSetPtr); - } - - return; -} -#endif /****************************************************** * Entropy Coding Kernel ******************************************************/ @@ -419,10 +338,17 @@ void* EntropyCodingKernel(void *inputPtr) EB_U32 yLcuIndex; EB_U32 lcuOriginX; EB_U32 lcuOriginY; - EB_BOOL lastLcuFlag; + EB_BOOL lastLcuFlagInSlice; + EB_BOOL lastLcuFlagInTile; EB_U32 pictureWidthInLcu; + EB_U32 tileWidthInLcu; + EB_U32 tileHeightInLcu; // Variables EB_BOOL initialProcessCall; + EB_U32 tileIdx; + EB_U32 tileCnt; + EB_U32 xLcuStart; + EB_U32 yLcuStart; for(;;) { @@ -433,45 +359,72 @@ void* EntropyCodingKernel(void *inputPtr) encDecResultsPtr = (EncDecResults_t*) encDecResultsWrapperPtr->objectPtr; pictureControlSetPtr = (PictureControlSet_t*) encDecResultsPtr->pictureControlSetWrapperPtr->objectPtr; sequenceControlSetPtr = (SequenceControlSet_t*) pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; - lastLcuFlag = EB_FALSE; + tileIdx = encDecResultsPtr->tileIndex; + lastLcuFlagInSlice = EB_FALSE; + lastLcuFlagInTile = EB_FALSE; + tileCnt = pictureControlSetPtr->tileRowCount * pictureControlSetPtr->tileColumnCount; #if DEADLOCK_DEBUG SVT_LOG("POC %lld EC IN \n", pictureControlSetPtr->pictureNumber); #endif + //SVT_LOG("[%lld]: POC %lld EC IN, tile %d, (%d, %d) \n", + // EbGetSysTimeMs(), + // pictureControlSetPtr->pictureNumber, tileIdx, + // encDecResultsPtr->completedLcuRowIndexStart, + // encDecResultsPtr->completedLcuRowIndexStart + encDecResultsPtr->completedLcuRowCount); // LCU Constants lcuSize = sequenceControlSetPtr->lcuSize; lcuSizeLog2 = (EB_U8)Log2f(lcuSize); contextPtr->lcuSize = lcuSize; pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + lcuSize - 1) >> lcuSizeLog2; -#if TILES - if (sequenceControlSetPtr->tileRowCount * sequenceControlSetPtr->tileColumnCount == 1) -#endif - { + tileWidthInLcu = sequenceControlSetPtr->tileColumnArray[tileIdx % pictureControlSetPtr->tileColumnCount]; + tileHeightInLcu = sequenceControlSetPtr->tileRowArray[tileIdx / pictureControlSetPtr->tileColumnCount]; + xLcuStart = yLcuStart = 0; + for (unsigned int i = 0; i < (tileIdx % pictureControlSetPtr->tileColumnCount); i++) { + xLcuStart += sequenceControlSetPtr->tileColumnArray[i]; + } + for (unsigned int i = 0; i < (tileIdx / pictureControlSetPtr->tileColumnCount); i++) { + yLcuStart += sequenceControlSetPtr->tileRowArray[i]; + } + + //assert(tileCnt >= 1); + if (tileCnt >= 1) { initialProcessCall = EB_TRUE; yLcuIndex = encDecResultsPtr->completedLcuRowIndexStart; // LCU-loops - while(UpdateEntropyCodingRows(pictureControlSetPtr, &yLcuIndex, encDecResultsPtr->completedLcuRowCount, &initialProcessCall) == EB_TRUE) + while(UpdateEntropyCodingRows(pictureControlSetPtr, &yLcuIndex, encDecResultsPtr->completedLcuRowCount, tileIdx, &initialProcessCall) == EB_TRUE) { EB_U32 rowTotalBits = 0; if(yLcuIndex == 0) { - ResetEntropyCodingPicture( - contextPtr, - pictureControlSetPtr, - sequenceControlSetPtr); - pictureControlSetPtr->entropyCodingPicDone = EB_FALSE; + EbBlockOnMutex(pictureControlSetPtr->entropyCodingPicMutex); + if (pictureControlSetPtr->entropyCodingPicResetFlag) { + //printf("[%lld]:Reset pic %d at tile %d, yLcuIndex is %d\n", + // EbGetSysTimeMs(), + // pictureControlSetPtr->pictureNumber, tileIdx, yLcuIndex + yLcuStart); + pictureControlSetPtr->entropyCodingPicResetFlag = EB_FALSE; + ResetEntropyCodingPicture( + contextPtr, + pictureControlSetPtr, + sequenceControlSetPtr); + } + EbReleaseMutex(pictureControlSetPtr->entropyCodingPicMutex); + pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingPicDone = EB_FALSE; } - for(xLcuIndex = 0; xLcuIndex < pictureWidthInLcu; ++xLcuIndex) - { - - - lcuIndex = (EB_U16)(xLcuIndex + yLcuIndex * pictureWidthInLcu); + for(xLcuIndex = 0; xLcuIndex < tileWidthInLcu; ++xLcuIndex) { + lcuIndex = (EB_U16)((xLcuIndex + xLcuStart) + (yLcuIndex + yLcuStart) * pictureWidthInLcu); lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIndex]; - lcuOriginX = xLcuIndex << lcuSizeLog2; - lcuOriginY = yLcuIndex << lcuSizeLog2; - lastLcuFlag = (lcuIndex == pictureControlSetPtr->lcuTotalCount - 1) ? EB_TRUE : EB_FALSE; + lcuOriginX = (xLcuIndex + xLcuStart) << lcuSizeLog2; + lcuOriginY = (yLcuIndex + yLcuStart) << lcuSizeLog2; + //Jing: + //Check for lastLcu, since tiles are parallelized, last LCU may not be the the last one in slice + lastLcuFlagInSlice = (lcuIndex == pictureControlSetPtr->lcuTotalCount - 1) ? EB_TRUE : EB_FALSE; + lastLcuFlagInTile = (xLcuIndex == tileWidthInLcu - 1 && yLcuIndex == tileHeightInLcu - 1) ? EB_TRUE : EB_FALSE; + if (sequenceControlSetPtr->tileSliceMode) { + lastLcuFlagInSlice = lastLcuFlagInTile; + } // Configure the LCU EntropyCodingConfigureLcu( @@ -486,15 +439,18 @@ void* EntropyCodingKernel(void *inputPtr) sequenceControlSetPtr, lcuOriginX, lcuOriginY, - lastLcuFlag, - 0, - 0); + lastLcuFlagInSlice, + tileIdx, + (xLcuIndex + xLcuStart) * lcuSize, + (yLcuIndex + yLcuStart) * lcuSize); rowTotalBits += lcuPtr->totalBits; } // At the end of each LCU-row, send the updated bit-count to Entropy Coding { + //Jing: TODO + //this is per tile, brc can use it or just ignore it EbObjectWrapper_t *rateControlTaskWrapperPtr; RateControlTasks_t *rateControlTaskPtr; @@ -505,7 +461,8 @@ void* EntropyCodingKernel(void *inputPtr) rateControlTaskPtr = (RateControlTasks_t*) rateControlTaskWrapperPtr->objectPtr; rateControlTaskPtr->taskType = RC_ENTROPY_CODING_ROW_FEEDBACK_RESULT; rateControlTaskPtr->pictureNumber = pictureControlSetPtr->pictureNumber; - rateControlTaskPtr->rowNumber = yLcuIndex; + rateControlTaskPtr->tileIndex = tileIdx; + rateControlTaskPtr->rowNumber = yLcuIndex; //Jing: yLcuIndex within tile rateControlTaskPtr->bitCount = rowTotalBits; rateControlTaskPtr->pictureControlSetWrapperPtr = 0; @@ -515,178 +472,73 @@ void* EntropyCodingKernel(void *inputPtr) EbPostFullObject(rateControlTaskWrapperPtr); } - EbBlockOnMutex(pictureControlSetPtr->entropyCodingMutex); - if (pictureControlSetPtr->entropyCodingPicDone == EB_FALSE) { + EbBlockOnMutex(pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingMutex); + if (pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingPicDone == EB_FALSE) { + //Jing: Store the av(e) part for different tiles and copy it as a whole to slice bitstream // If the picture is complete, terminate the slice - if (pictureControlSetPtr->entropyCodingCurrentRow == pictureControlSetPtr->entropyCodingRowCount) + if (pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingCurrentRow == pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingRowCount) { EB_U32 refIdx; + EB_BOOL pic_ready = EB_TRUE; - pictureControlSetPtr->entropyCodingPicDone = EB_TRUE; - - EncodeSliceFinish(pictureControlSetPtr->entropyCoderPtr); + //assert(lastLcuFlagInTile == EB_TRUE); - // Release the List 0 Reference Pictures - for (refIdx = 0; refIdx < pictureControlSetPtr->ParentPcsPtr->refList0Count; ++refIdx) { - if (pictureControlSetPtr->refPicPtrArray[0] != EB_NULL) { + //Jing:tile end, may not be the slice end + if (!lastLcuFlagInSlice) { + //printf("[%lld]:Encode tile end for tile %d\n", EbGetSysTimeMs(), tileIdx); + EncodeTileFinish(pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCoderPtr); + } else { + //printf("[%lld]:Encode slice end for tile %d\n", EbGetSysTimeMs(), tileIdx); + EncodeSliceFinish(pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCoderPtr); + } - EbReleaseObject(pictureControlSetPtr->refPicPtrArray[0]); + //Jing: TODO + //Release the ref if the whole pic are done + EbBlockOnMutex(pictureControlSetPtr->entropyCodingPicMutex); + pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingPicDone = EB_TRUE; + for (EB_U32 i = 0; i < tileCnt; i++) { + if (pictureControlSetPtr->entropyCodingInfo[i]->entropyCodingPicDone == EB_FALSE) { + pic_ready = EB_FALSE; + //printf("current POC %d not fully ready, tile %d missing\n", pictureControlSetPtr->pictureNumber, i); + break; } - } - - // Release the List 1 Reference Pictures - for (refIdx = 0; refIdx < pictureControlSetPtr->ParentPcsPtr->refList1Count; ++refIdx) { - if (pictureControlSetPtr->refPicPtrArray[1] != EB_NULL) { - - EbReleaseObject(pictureControlSetPtr->refPicPtrArray[1]); - } - } - - // Get Empty Entropy Coding Results - EbGetEmptyObject( - contextPtr->entropyCodingOutputFifoPtr, - &entropyCodingResultsWrapperPtr); - entropyCodingResultsPtr = (EntropyCodingResults_t*)entropyCodingResultsWrapperPtr->objectPtr; - entropyCodingResultsPtr->pictureControlSetWrapperPtr = encDecResultsPtr->pictureControlSetWrapperPtr; - - // Post EntropyCoding Results - EbPostFullObject(entropyCodingResultsWrapperPtr); - - } // End if(PictureCompleteFlag) - } - EbReleaseMutex(pictureControlSetPtr->entropyCodingMutex); - - - } - } -#if TILES - else - { - unsigned xLcuStart, yLcuStart, rowIndex, columnIndex; - - initialProcessCall = EB_TRUE; - - yLcuIndex = encDecResultsPtr->completedLcuRowIndexStart; - - // Update EC-rows, exit if picture is "in-progress" - if (WaitForAllEntropyCodingRows(pictureControlSetPtr, &yLcuIndex, encDecResultsPtr->completedLcuRowCount, &initialProcessCall) == EB_TRUE) - { + } + EbReleaseMutex(pictureControlSetPtr->entropyCodingPicMutex); + if (pic_ready) { + // Release the List 0 Reference Pictures + for (refIdx = 0; refIdx < pictureControlSetPtr->ParentPcsPtr->refList0Count; ++refIdx) { + if (pictureControlSetPtr->refPicPtrArray[0] != EB_NULL) { - ResetEntropyCodingPicture( - contextPtr, - pictureControlSetPtr, - sequenceControlSetPtr); + EbReleaseObject(pictureControlSetPtr->refPicPtrArray[0]); + } + } + // Release the List 1 Reference Pictures + for (refIdx = 0; refIdx < pictureControlSetPtr->ParentPcsPtr->refList1Count; ++refIdx) { + if (pictureControlSetPtr->refPicPtrArray[1] != EB_NULL) { - // Tile-loops - yLcuStart = 0; - for (rowIndex = 0; rowIndex < sequenceControlSetPtr->tileRowCount; ++rowIndex) - { - xLcuStart = 0; - for (columnIndex = 0; columnIndex < sequenceControlSetPtr->tileColumnCount; ++columnIndex) - { - ResetEntropyCodingTile( - contextPtr, - pictureControlSetPtr, - sequenceControlSetPtr, - yLcuIndex, - EB_TRUE); - - if (sequenceControlSetPtr->tileSliceMode == 1 && (xLcuStart !=0 || yLcuStart != 0)) { - CabacEncodeContext_t *cabacEncodeCtxPtr = (CabacEncodeContext_t*)pictureControlSetPtr->entropyCoderPtr->cabacEncodeContextPtr; - EncodeSliceHeader( - xLcuStart + yLcuStart * pictureWidthInLcu, - pictureControlSetPtr->pictureQp, - pictureControlSetPtr, - (OutputBitstreamUnit_t*) &(cabacEncodeCtxPtr->bacEncContext.m_pcTComBitIf)); - } - - // LCU-loops - for (yLcuIndex = yLcuStart; yLcuIndex < yLcuStart + sequenceControlSetPtr->tileRowArray[rowIndex]; ++yLcuIndex) - { - for (xLcuIndex = xLcuStart; xLcuIndex < xLcuStart + sequenceControlSetPtr->tileColumnArray[columnIndex]; ++xLcuIndex) - { - lcuIndex = xLcuIndex + yLcuIndex * pictureWidthInLcu; - lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIndex]; - lcuOriginX = xLcuIndex << lcuSizeLog2; - lcuOriginY = yLcuIndex << lcuSizeLog2; - if (sequenceControlSetPtr->tileSliceMode == 0) { - lastLcuFlag = (lcuIndex == pictureControlSetPtr->lcuTotalCount - 1) ? EB_TRUE : EB_FALSE; - } else - { - lastLcuFlag = (xLcuIndex == (xLcuStart + sequenceControlSetPtr->tileColumnArray[columnIndex] -1) && yLcuIndex == (yLcuStart + sequenceControlSetPtr->tileRowArray[rowIndex] -1)) ? EB_TRUE : EB_FALSE; + EbReleaseObject(pictureControlSetPtr->refPicPtrArray[1]); + } } - // Configure the LCU - EntropyCodingConfigureLcu( - contextPtr, - lcuPtr, - pictureControlSetPtr); + // Get Empty Entropy Coding Results + EbGetEmptyObject( + contextPtr->entropyCodingOutputFifoPtr, + &entropyCodingResultsWrapperPtr); + entropyCodingResultsPtr = (EntropyCodingResults_t*)entropyCodingResultsWrapperPtr->objectPtr; + entropyCodingResultsPtr->pictureControlSetWrapperPtr = encDecResultsPtr->pictureControlSetWrapperPtr; - // Entropy Coding - EntropyCodingLcu( - lcuPtr, - pictureControlSetPtr, - sequenceControlSetPtr, - lcuOriginX, - lcuOriginY, - lastLcuFlag, - xLcuIndex * lcuSize, - yLcuIndex * lcuSize); + //SVT_LOG("[%lld]: Entropy post result, POC %d\n", EbGetSysTimeMs(), pictureControlSetPtr->pictureNumber); + // Post EntropyCoding Results + EbPostFullObject(entropyCodingResultsWrapperPtr); } - } - - if (sequenceControlSetPtr->tileSliceMode == 0 && lastLcuFlag == EB_FALSE) { - EncodeTileFinish(pictureControlSetPtr->entropyCoderPtr); - } else if (sequenceControlSetPtr->tileSliceMode) { - EncodeSliceFinish(pictureControlSetPtr->entropyCoderPtr); - } - - xLcuStart += sequenceControlSetPtr->tileColumnArray[columnIndex]; - } - yLcuStart += sequenceControlSetPtr->tileRowArray[rowIndex]; - } - - // If the picture is complete, terminate the slice, 2nd pass DLF, SAO application - if (lastLcuFlag == EB_TRUE) - { - EB_U32 refIdx; - - if (sequenceControlSetPtr->tileSliceMode == 0) { - EncodeSliceFinish(pictureControlSetPtr->entropyCoderPtr); - } - - // Release the List 0 Reference Pictures - for (refIdx = 0; refIdx < pictureControlSetPtr->ParentPcsPtr->refList0Count; ++refIdx) { - if (pictureControlSetPtr->refPicPtrArray[0]/*[refIdx]*/ != EB_NULL) { - EbReleaseObject(pictureControlSetPtr->refPicPtrArray[0]/*[refIdx]*/); - } - } - - // Release the List 1 Reference Pictures - for (refIdx = 0; refIdx < pictureControlSetPtr->ParentPcsPtr->refList1Count; ++refIdx) { - if (pictureControlSetPtr->refPicPtrArray[1]/*[refIdx]*/ != EB_NULL) { - EbReleaseObject(pictureControlSetPtr->refPicPtrArray[1]/*[refIdx]*/); - } - } - - // Get Empty Entropy Coding Results - EbGetEmptyObject( - contextPtr->entropyCodingOutputFifoPtr, - &entropyCodingResultsWrapperPtr); - entropyCodingResultsPtr = (EntropyCodingResults_t*)entropyCodingResultsWrapperPtr->objectPtr; - entropyCodingResultsPtr->pictureControlSetWrapperPtr = encDecResultsPtr->pictureControlSetWrapperPtr; - - // Post EntropyCoding Results - EbPostFullObject(entropyCodingResultsWrapperPtr); - - } // End if(PictureCompleteFlag) - } + } // End if(PictureCompleteFlag) + } + EbReleaseMutex(pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingMutex); + } } -#endif - #if DEADLOCK_DEBUG SVT_LOG("POC %lld EC OUT \n", pictureControlSetPtr->pictureNumber); diff --git a/Source/Lib/Codec/EbInitialRateControlProcess.c b/Source/Lib/Codec/EbInitialRateControlProcess.c index 6b7f4d337..a8a442810 100644 --- a/Source/Lib/Codec/EbInitialRateControlProcess.c +++ b/Source/Lib/Codec/EbInitialRateControlProcess.c @@ -1134,6 +1134,25 @@ void* InitialRateControlKernel(void *inputPtr) ///////////////////////////// // Post the Full Results Object EbPostFullObject(outputResultsWrapperPtr); +#if LATENCY_PROFILE + double latency = 0.0; + EB_U64 finishTimeSeconds = 0; + EB_U64 finishTimeuSeconds = 0; + EbFinishTime((uint64_t*)&finishTimeSeconds, (uint64_t*)&finishTimeuSeconds); + + EbComputeOverallElapsedTimeMs( + pictureControlSetPtr->startTimeSeconds, + pictureControlSetPtr->startTimeuSeconds, + finishTimeSeconds, + finishTimeuSeconds, + &latency); + + SVT_LOG("[%lld]: POC %lld IRC OUT, decoder order %d, latency %3.3f \n", + EbGetSysTimeMs(), + pictureControlSetPtr->pictureNumber, + pictureControlSetPtr->decodeOrder, + latency); +#endif // Reset the Reorder Queue Entry queueEntryPtr->pictureNumber += INITIAL_RATE_CONTROL_REORDER_QUEUE_MAX_DEPTH; diff --git a/Source/Lib/Codec/EbModeDecision.c b/Source/Lib/Codec/EbModeDecision.c index 182ea4586..15be0cc23 100644 --- a/Source/Lib/Codec/EbModeDecision.c +++ b/Source/Lib/Codec/EbModeDecision.c @@ -411,7 +411,7 @@ void LimitMvOverBound( EB_S32 endY = (EB_S32)sCSet->lumaHeight << 2; EB_S32 cuSize = (EB_S32)ctxtPtr->cuStats->size << 2; EB_S32 pad = (4 << 2); -#if TILES + if ((sCSet->tileRowCount * sCSet->tileColumnCount) > 1) { const unsigned lcuIndex = ctxtPtr->cuOriginX/sCSet->lcuSize + (ctxtPtr->cuOriginY/sCSet->lcuSize) * sCSet->pictureWidthInLcu; startX = (EB_S32)sCSet->lcuParamsArray[lcuIndex].tileStartX << 2; @@ -419,7 +419,6 @@ void LimitMvOverBound( endX = (EB_S32)sCSet->lcuParamsArray[lcuIndex].tileEndX << 2; endY = (EB_S32)sCSet->lcuParamsArray[lcuIndex].tileEndY << 2; } -#endif //Jing: if MV is quarter/half, the 7,8 tap interpolation will cross the boundary //Just clamp the MV to integer @@ -1470,7 +1469,7 @@ EB_BOOL CheckForMvOverBound( EB_S32 endY = (EB_S32)sCSet->lumaHeight << 2; EB_S32 cuSize = (EB_S32)ctxtPtr->cuStats->size << 2; EB_S32 pad = 4 << 2; -#if TILES + if ((sCSet->tileRowCount * sCSet->tileColumnCount) > 1) { const unsigned lcuIndex = ctxtPtr->cuOriginX/sCSet->lcuSize + (ctxtPtr->cuOriginY/sCSet->lcuSize) * sCSet->pictureWidthInLcu; startX = (EB_S32)sCSet->lcuParamsArray[lcuIndex].tileStartX << 2; @@ -1478,7 +1477,6 @@ EB_BOOL CheckForMvOverBound( endX = (EB_S32)sCSet->lcuParamsArray[lcuIndex].tileEndX << 2; endY = (EB_S32)sCSet->lcuParamsArray[lcuIndex].tileEndY << 2; } -#endif if (cuOriginX + mvxF + cuSize > (endX - pad)) { diff --git a/Source/Lib/Codec/EbModeDecisionConfigurationProcess.c b/Source/Lib/Codec/EbModeDecisionConfigurationProcess.c index 13040522e..50f541651 100644 --- a/Source/Lib/Codec/EbModeDecisionConfigurationProcess.c +++ b/Source/Lib/Codec/EbModeDecisionConfigurationProcess.c @@ -2343,7 +2343,6 @@ void* ModeDecisionConfigurationKernel(void *inputPtr) EB_U32 pictureHeightInLcu; for(;;) { - // Get RateControl Results EbGetFullObject( contextPtr->rateControlInputFifoPtr, @@ -2519,16 +2518,41 @@ void* ModeDecisionConfigurationKernel(void *inputPtr) SVT_LOG("POC %lld MDC OUT \n", pictureControlSetPtr->pictureNumber); #endif // Post the results to the MD processes - EbGetEmptyObject( - contextPtr->modeDecisionConfigurationOutputFifoPtr, - &encDecTasksWrapperPtr); - encDecTasksPtr = (EncDecTasks_t*) encDecTasksWrapperPtr->objectPtr; - encDecTasksPtr->pictureControlSetWrapperPtr = rateControlResultsPtr->pictureControlSetWrapperPtr; - encDecTasksPtr->inputType = ENCDEC_TASKS_MDC_INPUT; - - // Post the Full Results Object - EbPostFullObject(encDecTasksWrapperPtr); + //printf("MDC, post POC %d, decoder order %d\n", + // pictureControlSetPtr->pictureNumber, pictureControlSetPtr->ParentPcsPtr->decodeOrder); + for (unsigned tileRowIdx = 0; tileRowIdx < sequenceControlSetPtr->tileRowCount; tileRowIdx++) { + // TODO: Too many objects may drain the FIFO and downgrade the perf + EbGetEmptyObject( + contextPtr->modeDecisionConfigurationOutputFifoPtr, + &encDecTasksWrapperPtr); + encDecTasksPtr = (EncDecTasks_t*) encDecTasksWrapperPtr->objectPtr; + encDecTasksPtr->pictureControlSetWrapperPtr = rateControlResultsPtr->pictureControlSetWrapperPtr; + encDecTasksPtr->inputType = ENCDEC_TASKS_MDC_INPUT; + encDecTasksPtr->tileRowIndex = tileRowIdx; + + // Post the Full Results Object + EbPostFullObject(encDecTasksWrapperPtr); + } +#if LATENCY_PROFILE + double latency = 0.0; + EB_U64 finishTimeSeconds = 0; + EB_U64 finishTimeuSeconds = 0; + EbFinishTime((uint64_t*)&finishTimeSeconds, (uint64_t*)&finishTimeuSeconds); + + EbComputeOverallElapsedTimeMs( + pictureControlSetPtr->ParentPcsPtr->startTimeSeconds, + pictureControlSetPtr->ParentPcsPtr->startTimeuSeconds, + finishTimeSeconds, + finishTimeuSeconds, + &latency); + + SVT_LOG("[%lld]: POC %lld MDC OUT, decoder order %d, latency %3.3f \n", + EbGetSysTimeMs(), + pictureControlSetPtr->ParentPcsPtr->pictureNumber, + pictureControlSetPtr->ParentPcsPtr->decodeOrder, + latency); +#endif // Release Rate Control Results EbReleaseObject(rateControlResultsWrapperPtr); diff --git a/Source/Lib/Codec/EbModeDecisionProcess.c b/Source/Lib/Codec/EbModeDecisionProcess.c index b379c7c43..0e5009354 100644 --- a/Source/Lib/Codec/EbModeDecisionProcess.c +++ b/Source/Lib/Codec/EbModeDecisionProcess.c @@ -182,26 +182,26 @@ EB_ERRORTYPE ModeDecisionContextCtor( /************************************************** * Reset Mode Decision Neighbor Arrays *************************************************/ -void ResetModeDecisionNeighborArrays(PictureControlSet_t *pictureControlSetPtr) +static void ResetModeDecisionNeighborArrays(PictureControlSet_t *pictureControlSetPtr, EB_U32 tileIdx) { EB_U8 depth; for (depth = 0; depth < NEIGHBOR_ARRAY_TOTAL_COUNT; depth++) { - NeighborArrayUnitReset(pictureControlSetPtr->mdIntraLumaModeNeighborArray[depth]); - NeighborArrayUnitReset(pictureControlSetPtr->mdMvNeighborArray[depth]); - NeighborArrayUnitReset(pictureControlSetPtr->mdSkipFlagNeighborArray[depth]); - NeighborArrayUnitReset(pictureControlSetPtr->mdModeTypeNeighborArray[depth]); - NeighborArrayUnitReset(pictureControlSetPtr->mdLeafDepthNeighborArray[depth]); + NeighborArrayUnitReset(pictureControlSetPtr->mdIntraLumaModeNeighborArray[depth][tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->mdMvNeighborArray[depth][tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->mdSkipFlagNeighborArray[depth][tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->mdModeTypeNeighborArray[depth][tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->mdLeafDepthNeighborArray[depth][tileIdx]); } return; } -void ResetMdRefinmentNeighborArrays(PictureControlSet_t *pictureControlSetPtr) +static void ResetMdRefinmentNeighborArrays(PictureControlSet_t *pictureControlSetPtr, EB_U32 tileIdx) { - NeighborArrayUnitReset(pictureControlSetPtr->mdRefinementIntraLumaModeNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->mdRefinementModeTypeNeighborArray); - NeighborArrayUnitReset(pictureControlSetPtr->mdRefinementLumaReconNeighborArray); + NeighborArrayUnitReset(pictureControlSetPtr->mdRefinementIntraLumaModeNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->mdRefinementModeTypeNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->mdRefinementLumaReconNeighborArray[tileIdx]); return; } @@ -315,6 +315,7 @@ void ProductResetModeDecision( ModeDecisionContext_t *contextPtr, PictureControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr, + EB_U32 tileRowIndex, EB_U32 segmentIndex) { EB_PICTURE sliceType; @@ -407,14 +408,22 @@ void ProductResetModeDecision( contextPtr->coeffEstEntropyCoderPtr = pictureControlSetPtr->coeffEstEntropyCoderPtr; // Reset Neighbor Arrays at start of new Segment / Picture + // Jing: Current segments will cross tiles if (segmentIndex == 0) { - ResetModeDecisionNeighborArrays(pictureControlSetPtr); - ResetMdRefinmentNeighborArrays(pictureControlSetPtr); + for (unsigned int tileIdx = tileRowIndex * sequenceControlSetPtr->tileColumnCount; + tileIdx < (tileRowIndex + 1) * sequenceControlSetPtr->tileColumnCount; + tileIdx++) { + ResetModeDecisionNeighborArrays(pictureControlSetPtr, tileIdx); + ResetMdRefinmentNeighborArrays(pictureControlSetPtr, tileIdx); + + //Jing: TODO Change to tile + // Used in DLF, need to double check if need tile level parameters + for(lcuRowIndex = 0; lcuRowIndex< ((sequenceControlSetPtr->lumaHeight + MAX_LCU_SIZE - 1)/MAX_LCU_SIZE); lcuRowIndex++){ + pictureControlSetPtr->encPrevCodedQp[tileIdx][lcuRowIndex] = (EB_U8)pictureControlSetPtr->pictureQp; + pictureControlSetPtr->encPrevQuantGroupCodedQp[tileIdx][lcuRowIndex] = (EB_U8)pictureControlSetPtr->pictureQp; + } + } - for(lcuRowIndex = 0; lcuRowIndex< ((sequenceControlSetPtr->lumaHeight + MAX_LCU_SIZE - 1)/MAX_LCU_SIZE); lcuRowIndex++){ - pictureControlSetPtr->encPrevCodedQp[lcuRowIndex] = (EB_U8)pictureControlSetPtr->pictureQp; - pictureControlSetPtr->encPrevQuantGroupCodedQp[lcuRowIndex] = (EB_U8)pictureControlSetPtr->pictureQp; - } } return; diff --git a/Source/Lib/Codec/EbModeDecisionProcess.h b/Source/Lib/Codec/EbModeDecisionProcess.h index 7fe831450..19c1e6dc3 100644 --- a/Source/Lib/Codec/EbModeDecisionProcess.h +++ b/Source/Lib/Codec/EbModeDecisionProcess.h @@ -267,6 +267,8 @@ typedef struct ModeDecisionContext_s EB_U8 nflLevelMd; EB_U8 nflLevelPillar8x8ref; EB_U8 nflLevelMvMerge64x64ref; + + EB_U16 tileIndex; } ModeDecisionContext_t; typedef void(*EB_LAMBDA_ASSIGN_FUNC)( @@ -291,8 +293,9 @@ extern EB_ERRORTYPE ModeDecisionContextCtor( EB_BOOL is16bit); -extern void ResetModeDecisionNeighborArrays(PictureControlSet_t *pictureControlSetPtr); - +//extern void ResetModeDecisionNeighborArrays( +// PictureControlSet_t *pictureControlSetPtr, +// EB_U32 tileIndex); extern void ApplyIntraInterModeBias( EB_BOOL intraInterCond1, @@ -306,6 +309,7 @@ extern void ProductResetModeDecision( ModeDecisionContext_t *contextPtr, PictureControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr, + EB_U32 tileRowIndex, EB_U32 segmentIndex); extern void ModeDecisionConfigureLcu( ModeDecisionContext_t *contextPtr, @@ -319,4 +323,4 @@ extern void ModeDecisionConfigureLcu( #ifdef __cplusplus } #endif -#endif // EbModeDecisionProcess_h \ No newline at end of file +#endif // EbModeDecisionProcess_h diff --git a/Source/Lib/Codec/EbMotionEstimation.c b/Source/Lib/Codec/EbMotionEstimation.c index 775833346..f48aa4a46 100644 --- a/Source/Lib/Codec/EbMotionEstimation.c +++ b/Source/Lib/Codec/EbMotionEstimation.c @@ -4108,7 +4108,6 @@ EB_ERRORTYPE MotionEstimateLcu( } } -#if TILES if (sequenceControlSetPtr->staticConfig.unrestrictedMotionVector == 0 && (sequenceControlSetPtr->tileRowCount * sequenceControlSetPtr->tileColumnCount) > 1) { int tileStartX = sequenceControlSetPtr->lcuParamsArray[lcuIndex].tileStartX; @@ -4131,9 +4130,7 @@ EB_ERRORTYPE MotionEstimateLcu( searchAreaWidth = ((originX + xSearchAreaOrigin + searchAreaWidth) > tileEndX) ? MAX(1, searchAreaWidth - ((originX + xSearchAreaOrigin + searchAreaWidth) - tileEndX)) : searchAreaWidth; - } else -#endif - { + } else { // Correct the left edge of the Search Area if it is not on the reference Picture xSearchAreaOrigin = ((originX + xSearchAreaOrigin) < -padWidth) ? -padWidth - originX : @@ -4153,7 +4150,6 @@ EB_ERRORTYPE MotionEstimateLcu( searchAreaWidth; } -#if TILES if (sequenceControlSetPtr->staticConfig.unrestrictedMotionVector == 0 && (sequenceControlSetPtr->tileRowCount * sequenceControlSetPtr->tileColumnCount) > 1) { int tileStartY = sequenceControlSetPtr->lcuParamsArray[lcuIndex].tileStartY; @@ -4176,9 +4172,7 @@ EB_ERRORTYPE MotionEstimateLcu( searchAreaHeight = (originY + ySearchAreaOrigin + searchAreaHeight > tileEndY) ? MAX(1, searchAreaHeight - ((originY + ySearchAreaOrigin + searchAreaHeight) - tileEndY)) : searchAreaHeight; - } else -#endif - { + } else { // Correct the top edge of the Search Area if it is not on the reference Picture ySearchAreaOrigin = ((originY + ySearchAreaOrigin) < -padHeight) ? -padHeight - originY : diff --git a/Source/Lib/Codec/EbMotionEstimationProcess.c b/Source/Lib/Codec/EbMotionEstimationProcess.c index 7ee4a472b..781107797 100644 --- a/Source/Lib/Codec/EbMotionEstimationProcess.c +++ b/Source/Lib/Codec/EbMotionEstimationProcess.c @@ -1370,6 +1370,25 @@ void* MotionEstimationKernel(void *inputPtr) // Post the Full Results Object EbPostFullObject(outputResultsWrapperPtr); +#if LATENCY_PROFILE + double latency = 0.0; + EB_U64 finishTimeSeconds = 0; + EB_U64 finishTimeuSeconds = 0; + EbFinishTime((uint64_t*)&finishTimeSeconds, (uint64_t*)&finishTimeuSeconds); + + EbComputeOverallElapsedTimeMs( + pictureControlSetPtr->startTimeSeconds, + pictureControlSetPtr->startTimeuSeconds, + finishTimeSeconds, + finishTimeuSeconds, + &latency); + + SVT_LOG("[%lld]: POC %lld ME OUT, decoder order %d, latency %3.3f \n", + EbGetSysTimeMs(), + pictureControlSetPtr->pictureNumber, + pictureControlSetPtr->decodeOrder, + latency); +#endif } return EB_NULL; } diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index 98c091340..76f14f3f3 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -156,6 +156,8 @@ void* PacketizationKernel(void *inputPtr) EB_U64 filler; EB_U32 fillerBytes; EB_U64 bufferRate; + EB_U16 tileIdx; + EB_U16 tileCnt; for(;;) { // Get EntropyCoding Results @@ -166,9 +168,11 @@ void* PacketizationKernel(void *inputPtr) pictureControlSetPtr = (PictureControlSet_t*) entropyCodingResultsPtr->pictureControlSetWrapperPtr->objectPtr; sequenceControlSetPtr = (SequenceControlSet_t*) pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; encodeContextPtr = (EncodeContext_t*) sequenceControlSetPtr->encodeContextPtr; + tileCnt = pictureControlSetPtr->tileRowCount * pictureControlSetPtr->tileColumnCount; #if DEADLOCK_DEBUG SVT_LOG("POC %lld PK IN \n", pictureControlSetPtr->pictureNumber); #endif + //**************************************************** // Input Entropy Results into Reordering Queue //**************************************************** @@ -328,7 +332,7 @@ void* PacketizationKernel(void *inputPtr) lcuTotalCount = pictureControlSetPtr->lcuTotalCount; // LCU Loop - if (sequenceControlSetPtr->staticConfig.rateControlMode == 1){ + if (sequenceControlSetPtr->staticConfig.rateControlMode > 0){ EB_U64 sadBits[NUMBER_OF_SAD_INTERVALS]= {0}; EB_U32 count[NUMBER_OF_SAD_INTERVALS] = {0}; @@ -624,41 +628,61 @@ void* PacketizationKernel(void *inputPtr) } } - EncodeSliceHeader( - 0, - packetizationQp, - pictureControlSetPtr, - (OutputBitstreamUnit_t*) pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - // Flush the Bitstream - FlushBitstream( - pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); - - // Copy Slice Header to the Output Bitstream - CopyRbspBitstreamToPayload( - pictureControlSetPtr->bitstreamPtr, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr, - NAL_UNIT_INVALID); + // Jing: process multiple tiles + for (tileIdx = 0; tileIdx < tileCnt; tileIdx++) { + EB_U32 lcuSize = sequenceControlSetPtr->lcuSize; + EB_U32 lcuSizeLog2 = (EB_U8)Log2f(lcuSize); + EB_U32 pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + lcuSize - 1) >> lcuSizeLog2; + EB_U32 xLcuStart = 0; + EB_U32 yLcuStart = 0; + EB_U32 lcuIndex = 0; + for (EB_U32 i = 0; i < (tileIdx % pictureControlSetPtr->tileColumnCount); i++) { + xLcuStart += sequenceControlSetPtr->tileColumnArray[i]; + } + for (EB_U32 i = 0; i < (tileIdx / pictureControlSetPtr->tileColumnCount); i++) { + yLcuStart += sequenceControlSetPtr->tileRowArray[i]; + } + lcuIndex = xLcuStart + yLcuStart * pictureWidthInLcu; - // Reset the bitstream - ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + // Encode slice header + if (tileIdx == 0 || sequenceControlSetPtr->tileSliceMode == 1) { + EncodeSliceHeader( + lcuIndex, + packetizationQp, + pictureControlSetPtr, + (OutputBitstreamUnit_t*)pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + + // Flush the Bitstream + FlushBitstream( + pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + + // Copy Slice Header to the Output Bitstream + CopyRbspBitstreamToPayload( + pictureControlSetPtr->bitstreamPtr, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr, + NAL_UNIT_INVALID); - // Write the slice data into the bitstream - bitstream.outputBitstreamPtr = EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCoderPtr); + // Reset the bitstream + ResetBitstream(pictureControlSetPtr->bitstreamPtr->outputBitstreamPtr); + } - FlushBitstream(bitstream.outputBitstreamPtr); + // Write the slice data into the bitstream + bitstream.outputBitstreamPtr = EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCoderPtr); - CopyRbspBitstreamToPayload( - &bitstream, - outputStreamPtr->pBuffer, - (EB_U32*) &(outputStreamPtr->nFilledLen), - (EB_U32*) &(outputStreamPtr->nAllocLen), - encodeContextPtr, - NAL_UNIT_INVALID); + FlushBitstream(bitstream.outputBitstreamPtr); + CopyRbspBitstreamToPayload( + &bitstream, + outputStreamPtr->pBuffer, + (EB_U32*) &(outputStreamPtr->nFilledLen), + (EB_U32*) &(outputStreamPtr->nAllocLen), + encodeContextPtr, + NAL_UNIT_INVALID); + } bufferRate = encodeContextPtr->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16); queueEntryPtr->fillerBitsSent = 0; if ((sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate) && (sequenceControlSetPtr->staticConfig.vbvMaxrate == sequenceControlSetPtr->staticConfig.targetBitRate)) @@ -676,7 +700,6 @@ void* PacketizationKernel(void *inputPtr) queueEntryPtr->fillerBitsSent = filler; } } - // Send the number of bytes per frame to RC pictureControlSetPtr->ParentPcsPtr->totalNumBits = outputStreamPtr->nFilledLen << 3; @@ -776,7 +799,8 @@ void* PacketizationKernel(void *inputPtr) finishTimeSeconds, finishTimeuSeconds, &latency); - //Block to update decode order of the lsat + //printf("pts %d, dts %d, Packetization latency %3.3f\n", outputStreamPtr->pts, outputStreamPtr->dts, latency); + outputStreamPtr->nTickCount = (EB_U32)latency; if (sequenceControlSetPtr->staticConfig.pictureTimingSEI) { if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index 5cc057daf..b1b7d7bcc 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -21,6 +21,15 @@ EB_ERRORTYPE PictureControlSetCtor( // Max/Min CU Sizes const EB_U32 maxCuSize = initDataPtr->lcuSize; + EB_U32 encDecSegRow = initDataPtr->encDecSegmentRow; + EB_U32 encDecSegCol = initDataPtr->encDecSegmentCol; + + + // Tile info + EB_U32 totalTileCount = initDataPtr->tileRowCount * initDataPtr->tileColumnCount; + totalTileCount = (totalTileCount > 0) ? totalTileCount : 1; + EB_U16 tileIdx; + EB_U16 r, c; // LCUs const EB_U16 pictureLcuWidth = (EB_U16)((initDataPtr->pictureWidth + initDataPtr->lcuSize - 1) / initDataPtr->lcuSize); @@ -68,6 +77,9 @@ EB_ERRORTYPE PictureControlSetCtor( objectPtr->colorFormat = initDataPtr->colorFormat; objectPtr->reconPicture16bitPtr = (EbPictureBufferDesc_t *)EB_NULL; objectPtr->reconPicturePtr = (EbPictureBufferDesc_t *)EB_NULL; + objectPtr->tileColumnCount = initDataPtr->tileColumnCount; + objectPtr->tileRowCount = initDataPtr->tileRowCount; + // Reconstructed Picture Buffer if(initDataPtr->is16bit == EB_TRUE){ return_error = EbReconPictureBufferDescCtor( @@ -85,21 +97,7 @@ EB_ERRORTYPE PictureControlSetCtor( if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } - // Entropy Coder - return_error = EntropyCoderCtor( - &objectPtr->entropyCoderPtr, - SEGMENT_ENTROPY_BUFFER_SIZE); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - //Proxy Entropy Coder to be used in Encdec - return_error = EntropyCoderCtor( - &objectPtr->tempEntropyCoderPtr, - SEGMENT_ENTROPY_BUFFER_SIZE); - if (return_error == EB_ErrorInsufficientResources) { - return EB_ErrorInsufficientResources; - } // Cabaccost EB_MALLOC(CabacCost_t*, objectPtr->cabacCost, sizeof(CabacCost_t), EB_N_PTR); @@ -189,449 +187,480 @@ EB_ERRORTYPE PictureControlSetCtor( // Allocate memory for cbf array (used by DLF) EB_MALLOC(EB_U8*, objectPtr->cbfMapArray, sizeof(EB_U8) * ((initDataPtr->pictureWidth >> 2) * (initDataPtr->pictureHeight >> 2)), EB_N_PTR); + // Jing: TO enable multi-tile, need to have neighbor per tile for EncodePass + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epIntraLumaModeNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epMvNeighborArray , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epSkipFlagNeighborArray , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epModeTypeNeighborArray , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epLeafDepthNeighborArray , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epLumaReconNeighborArray , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epCbReconNeighborArray , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epCrReconNeighborArray , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epSaoNeighborArray , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epLumaReconNeighborArray16bit, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epCbReconNeighborArray16bit , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->epCrReconNeighborArray16bit , sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdRefinementIntraLumaModeNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdRefinementModeTypeNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdRefinementLumaReconNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + + // For entropy + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->modeTypeNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->leafDepthNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->intraLumaModeNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->skipFlagNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + // Mode Decision Neighbor Arrays EB_U8 depth; + EB_U16 array_size = sizeof(NeighborArrayUnit_t*) * totalTileCount; for (depth = 0; depth < NEIGHBOR_ARRAY_TOTAL_COUNT; depth++) { - return_error = NeighborArrayUnitCtor( - &objectPtr->mdIntraLumaModeNeighborArray[depth], - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->mdMvNeighborArray[depth], - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(MvUnit_t), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdIntraLumaModeNeighborArray[depth], array_size, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdMvNeighborArray[depth], array_size, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdSkipFlagNeighborArray[depth], array_size, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdModeTypeNeighborArray[depth], array_size, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdLeafDepthNeighborArray[depth], array_size, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdLumaReconNeighborArray[depth], array_size, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdCbReconNeighborArray[depth], array_size, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->mdCrReconNeighborArray[depth], array_size, EB_N_PTR); + } + + for (r = 0; r < initDataPtr->tileRowCount; r++) { + for (c = 0; c < initDataPtr->tileColumnCount; c++) { + tileIdx = r * initDataPtr->tileColumnCount + c; + for (depth = 0; depth < NEIGHBOR_ARRAY_TOTAL_COUNT; depth++) { + return_error = NeighborArrayUnitCtor( + &objectPtr->mdIntraLumaModeNeighborArray[depth][tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->mdMvNeighborArray[depth][tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(MvUnit_t), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->mdSkipFlagNeighborArray[depth][tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->mdModeTypeNeighborArray[depth][tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->mdLeafDepthNeighborArray[depth][tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->mdLumaReconNeighborArray[depth][tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->mdCbReconNeighborArray[depth][tileIdx], + MAX_PICTURE_WIDTH_SIZE >> 1, + MAX_PICTURE_HEIGHT_SIZE >> 1, + sizeof(EB_U8), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->mdCrReconNeighborArray[depth][tileIdx], + MAX_PICTURE_WIDTH_SIZE >> 1, + MAX_PICTURE_HEIGHT_SIZE >> 1, + sizeof(EB_U8), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->mdRefinementIntraLumaModeNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->mdRefinementModeTypeNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->mdRefinementLumaReconNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + // Encode Pass Neighbor Arrays + return_error = NeighborArrayUnitCtor( + &objectPtr->epIntraLumaModeNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, //Jing: change to tile size + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->epMvNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(MvUnit_t), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->epSkipFlagNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->epModeTypeNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->epLeafDepthNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->epLumaReconNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->epCbReconNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, + MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, + sizeof(EB_U8), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->epCrReconNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, + MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, + sizeof(EB_U8), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + if(is16bit){ + return_error = NeighborArrayUnitCtor( + &objectPtr->epLumaReconNeighborArray16bit[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U16), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->epCbReconNeighborArray16bit[tileIdx], + MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, + MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, + sizeof(EB_U16), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->epCrReconNeighborArray16bit[tileIdx], + MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, + MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, + sizeof(EB_U16), + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_FULL_MASK); + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + } + else { + objectPtr->epLumaReconNeighborArray16bit = 0; + objectPtr->epCbReconNeighborArray16bit = 0; + objectPtr->epCrReconNeighborArray16bit = 0; + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->epSaoNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(SaoParameters_t), + LCU_NEIGHBOR_ARRAY_GRANULARITY, + LCU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + // Entropy Coding Neighbor Arrays + return_error = NeighborArrayUnitCtor( + &objectPtr->modeTypeNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->leafDepthNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->skipFlagNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->intraLumaModeNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + if (return_error == EB_ErrorInsufficientResources){ + return EB_ErrorInsufficientResources; + } } - return_error = NeighborArrayUnitCtor( - &objectPtr->mdSkipFlagNeighborArray[depth], - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - CU_NEIGHBOR_ARRAY_GRANULARITY, - CU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + } + //return_error = NeighborArrayUnitCtor( + // &objectPtr->amvpMvMergeMvNeighborArray, + // MAX_PICTURE_WIDTH_SIZE, + // MAX_PICTURE_HEIGHT_SIZE, + // sizeof(MvUnit_t), + // PU_NEIGHBOR_ARRAY_GRANULARITY, + // PU_NEIGHBOR_ARRAY_GRANULARITY, + // NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + //if (return_error == EB_ErrorInsufficientResources){ + // return EB_ErrorInsufficientResources; + //} + //return_error = NeighborArrayUnitCtor( + // &objectPtr->amvpMvMergeModeTypeNeighborArray, + // MAX_PICTURE_WIDTH_SIZE, + // MAX_PICTURE_HEIGHT_SIZE, + // sizeof(EB_U8), + // PU_NEIGHBOR_ARRAY_GRANULARITY, + // PU_NEIGHBOR_ARRAY_GRANULARITY, + // NEIGHBOR_ARRAY_UNIT_FULL_MASK); + + //if (return_error == EB_ErrorInsufficientResources){ + // return EB_ErrorInsufficientResources; + //} - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->mdModeTypeNeighborArray[depth], - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); + // Note - non-zero offsets are not supported (to be fixed later in DLF chroma filtering) + objectPtr->cbQpOffset = 0; + objectPtr->crQpOffset = 0; - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } + objectPtr->sliceLevelChromaQpFlag = EB_TRUE; + // slice level chroma QP offsets + objectPtr->sliceCbQpOffset = 0; + objectPtr->sliceCrQpOffset = 0; - return_error = NeighborArrayUnitCtor( - &objectPtr->mdLeafDepthNeighborArray[depth], - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - CU_NEIGHBOR_ARRAY_GRANULARITY, - CU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->mdLumaReconNeighborArray[depth], - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } + //objectPtr->totalNumBits = 0; - return_error = NeighborArrayUnitCtor( - &objectPtr->mdCbReconNeighborArray[depth], - MAX_PICTURE_WIDTH_SIZE >> 1, - MAX_PICTURE_HEIGHT_SIZE >> 1, - sizeof(EB_U8), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); + // Error Resilience + objectPtr->constrainedIntraFlag = EB_FALSE; - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; + //Jing: + //Alloc segment per tile group + // Segments + EB_MALLOC(EncDecSegments_t**, objectPtr->encDecSegmentCtrl, sizeof(EncDecSegments_t*) * initDataPtr->tileRowCount, EB_N_PTR); + + for (tileIdx = 0; tileIdx < initDataPtr->tileRowCount; tileIdx++) { + if (totalTileCount > 1) { + //Jing: Tuning segments number, put tile info to pps + encDecSegRow = pictureLcuHeight / initDataPtr->tileRowCount; + encDecSegCol = pictureLcuWidth; } - return_error = NeighborArrayUnitCtor( - &objectPtr->mdCrReconNeighborArray[depth], - MAX_PICTURE_WIDTH_SIZE >> 1, - MAX_PICTURE_HEIGHT_SIZE >> 1, - sizeof(EB_U8), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); + return_error = EncDecSegmentsCtor( + &(objectPtr->encDecSegmentCtrl[tileIdx]), + encDecSegCol, + encDecSegRow); if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } } - return_error = NeighborArrayUnitCtor( - &objectPtr->mdRefinementIntraLumaModeNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - - return_error = NeighborArrayUnitCtor( - &objectPtr->mdRefinementModeTypeNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - - return_error = NeighborArrayUnitCtor( - &objectPtr->mdRefinementLumaReconNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - - // Encode Pass Neighbor Arrays - return_error = NeighborArrayUnitCtor( - &objectPtr->epIntraLumaModeNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->epMvNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(MvUnit_t), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->epSkipFlagNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - CU_NEIGHBOR_ARRAY_GRANULARITY, - CU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->epModeTypeNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->epLeafDepthNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - CU_NEIGHBOR_ARRAY_GRANULARITY, - CU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - - return_error = NeighborArrayUnitCtor( - &objectPtr->epLumaReconNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->epCbReconNeighborArray, - MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, - MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, - sizeof(EB_U8), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->epCrReconNeighborArray, - MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, - MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, - sizeof(EB_U8), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); + EB_MALLOC(EntropyTileInfo**, objectPtr->entropyCodingInfo, sizeof(EntropyTileInfo*) * totalTileCount, EB_N_PTR); + for (tileIdx = 0; tileIdx < totalTileCount; tileIdx++) { + // Entropy Rows per tile + EB_MALLOC(EntropyTileInfo*, objectPtr->entropyCodingInfo[tileIdx], sizeof(EntropyTileInfo), EB_N_PTR); + EB_CREATEMUTEX(EB_HANDLE, objectPtr->entropyCodingInfo[tileIdx]->entropyCodingMutex, sizeof(EB_HANDLE), EB_MUTEX); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } + // Entropy Coder + return_error = EntropyCoderCtor( + &objectPtr->entropyCodingInfo[tileIdx]->entropyCoderPtr, + SEGMENT_ENTROPY_BUFFER_SIZE); - if(is16bit){ - return_error = NeighborArrayUnitCtor( - &objectPtr->epLumaReconNeighborArray16bit, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U16), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->epCbReconNeighborArray16bit, - MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, - MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, - sizeof(EB_U16), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ + if (return_error == EB_ErrorInsufficientResources) { return EB_ErrorInsufficientResources; } - return_error = NeighborArrayUnitCtor( - &objectPtr->epCrReconNeighborArray16bit, - MAX_PICTURE_WIDTH_SIZE >> subWidthCMinus1, - MAX_PICTURE_HEIGHT_SIZE >> subHeightCMinus1, - sizeof(EB_U16), - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - SAMPLE_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - if (return_error == EB_ErrorInsufficientResources){ + //Proxy Entropy Coder to be used in Encdec + return_error = EntropyCoderCtor( + &objectPtr->entropyCodingInfo[tileIdx]->tempEntropyCoderPtr, + SEGMENT_ENTROPY_BUFFER_SIZE); + if (return_error == EB_ErrorInsufficientResources) { return EB_ErrorInsufficientResources; } } - else { - objectPtr->epLumaReconNeighborArray16bit = 0; - objectPtr->epCbReconNeighborArray16bit = 0; - objectPtr->epCrReconNeighborArray16bit = 0; - } - - return_error = NeighborArrayUnitCtor( - &objectPtr->epSaoNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(SaoParameters_t), - LCU_NEIGHBOR_ARRAY_GRANULARITY, - LCU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->amvpMvMergeMvNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(MvUnit_t), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->amvpMvMergeModeTypeNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_FULL_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - // Entropy Coding Neighbor Arrays - return_error = NeighborArrayUnitCtor( - &objectPtr->modeTypeNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - // Proxy Entropy Coding Neighbor Arrays to be used in Encdec - return_error = NeighborArrayUnitCtor( - &objectPtr->tempModeTypeNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources) { - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->leafDepthNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - CU_NEIGHBOR_ARRAY_GRANULARITY, - CU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->tempLeafDepthNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - CU_NEIGHBOR_ARRAY_GRANULARITY, - CU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources) { - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->skipFlagNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - CU_NEIGHBOR_ARRAY_GRANULARITY, - CU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->tempSkipFlagNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - CU_NEIGHBOR_ARRAY_GRANULARITY, - CU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - - if (return_error == EB_ErrorInsufficientResources) { - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->intraLumaModeNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - return_error = NeighborArrayUnitCtor( - &objectPtr->tempIntraLumaModeNeighborArray, - MAX_PICTURE_WIDTH_SIZE, - MAX_PICTURE_HEIGHT_SIZE, - sizeof(EB_U8), - PU_NEIGHBOR_ARRAY_GRANULARITY, - PU_NEIGHBOR_ARRAY_GRANULARITY, - NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); - if (return_error == EB_ErrorInsufficientResources) { - return EB_ErrorInsufficientResources; - } - - // Note - non-zero offsets are not supported (to be fixed later in DLF chroma filtering) - objectPtr->cbQpOffset = 0; - objectPtr->crQpOffset = 0; - - objectPtr->sliceLevelChromaQpFlag = EB_TRUE; - // slice level chroma QP offsets - objectPtr->sliceCbQpOffset = 0; - objectPtr->sliceCrQpOffset = 0; - - - //objectPtr->totalNumBits = 0; - - // Error Resilience - objectPtr->constrainedIntraFlag = EB_FALSE; - - // Segments - return_error = EncDecSegmentsCtor( - &objectPtr->encDecSegmentCtrl, - initDataPtr->encDecSegmentCol, - initDataPtr->encDecSegmentRow); - if (return_error == EB_ErrorInsufficientResources){ - return EB_ErrorInsufficientResources; - } - // Entropy Rows - EB_CREATEMUTEX(EB_HANDLE, objectPtr->entropyCodingMutex, sizeof(EB_HANDLE), EB_MUTEX); + // Entropy picture level mutex + EB_CREATEMUTEX(EB_HANDLE, objectPtr->entropyCodingPicMutex, sizeof(EB_HANDLE), EB_MUTEX); EB_CREATEMUTEX(EB_HANDLE, objectPtr->intraMutex, sizeof(EB_HANDLE), EB_MUTEX); + objectPtr->encDecCodedLcuCount = 0; return EB_ErrorNone; } diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index 44562a6b6..4aee3883d 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -131,6 +131,19 @@ typedef struct MdSegmentCtrl_s struct CodedTreeblock_s; struct LargestCodingUnit_s; +typedef struct EntropyTileInfo_s +{ + EB_S8 entropyCodingCurrentAvailableRow; + EB_BOOL entropyCodingRowArray[MAX_LCU_ROWS]; + EB_S8 entropyCodingCurrentRow; + EB_S8 entropyCodingRowCount; + EB_HANDLE entropyCodingMutex; + EB_BOOL entropyCodingInProgress; + EB_BOOL entropyCodingPicDone; + EntropyCoder_t *entropyCoderPtr; + EntropyCoder_t *tempEntropyCoderPtr; +} EntropyTileInfo; + typedef struct PictureControlSet_s { EbObjectWrapper_t *sequenceControlSetWrapperPtr; @@ -138,9 +151,8 @@ typedef struct PictureControlSet_s EbPictureBufferDesc_t *reconPicture16bitPtr; - EntropyCoder_t *entropyCoderPtr; - - EntropyCoder_t *tempEntropyCoderPtr; + // Jing: move into entropyCodingInfo + //EntropyCoder_t *entropyCoderPtr; // Packetization (used to encode SPS, PPS, etc) Bitstream_t *bitstreamPtr; @@ -158,19 +170,19 @@ typedef struct PictureControlSet_s EB_COLOR_FORMAT colorFormat; - EncDecSegments_t *encDecSegmentCtrl; + EncDecSegments_t **encDecSegmentCtrl; - // Entropy Process Rows - EB_S8 entropyCodingCurrentAvailableRow; - EB_BOOL entropyCodingRowArray[MAX_LCU_ROWS]; - EB_S8 entropyCodingCurrentRow; - EB_S8 entropyCodingRowCount; - EB_HANDLE entropyCodingMutex; - EB_BOOL entropyCodingInProgress; - EB_BOOL entropyCodingPicDone; + // Tile Count + EB_U16 tileRowCount; + EB_U16 tileColumnCount; + + EntropyTileInfo **entropyCodingInfo; + EB_HANDLE entropyCodingPicMutex; + EB_BOOL entropyCodingPicResetFlag; EB_HANDLE intraMutex; EB_U32 intraCodedArea; + EB_U32 encDecCodedLcuCount; // Mode Decision Config MdcLcuData_t *mdcLcuArray; @@ -202,55 +214,55 @@ typedef struct PictureControlSet_s EB_U8 *cbfMapArray; // QP Assignment - EB_U8 prevCodedQp; - EB_U8 prevQuantGroupCodedQp; + EB_U8 prevCodedQp[EB_TILE_MAX_COUNT]; + EB_U8 prevQuantGroupCodedQp[EB_TILE_MAX_COUNT]; // Enc/DecQP Assignment - EB_U8 encPrevCodedQp[MAX_PICTURE_HEIGHT_SIZE / MAX_LCU_SIZE]; - EB_U8 encPrevQuantGroupCodedQp[MAX_PICTURE_HEIGHT_SIZE / MAX_LCU_SIZE]; + EB_U8 encPrevCodedQp[EB_TILE_MAX_COUNT][MAX_PICTURE_HEIGHT_SIZE / MAX_LCU_SIZE]; + EB_U8 encPrevQuantGroupCodedQp[EB_TILE_MAX_COUNT][MAX_PICTURE_HEIGHT_SIZE / MAX_LCU_SIZE]; // EncDec Entropy Coder (for rate estimation) EntropyCoder_t *coeffEstEntropyCoderPtr; CabacCost_t *cabacCost; // Mode Decision Neighbor Arrays - NeighborArrayUnit_t *mdIntraLumaModeNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; - NeighborArrayUnit_t *mdMvNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; - NeighborArrayUnit_t *mdSkipFlagNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; - NeighborArrayUnit_t *mdModeTypeNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; - NeighborArrayUnit_t *mdLeafDepthNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; - NeighborArrayUnit_t *mdLumaReconNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; - NeighborArrayUnit_t *mdCbReconNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; - NeighborArrayUnit_t *mdCrReconNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; + NeighborArrayUnit_t **mdIntraLumaModeNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; + NeighborArrayUnit_t **mdMvNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; + NeighborArrayUnit_t **mdSkipFlagNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; + NeighborArrayUnit_t **mdModeTypeNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; + NeighborArrayUnit_t **mdLeafDepthNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; + NeighborArrayUnit_t **mdLumaReconNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; + NeighborArrayUnit_t **mdCbReconNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; + NeighborArrayUnit_t **mdCrReconNeighborArray[NEIGHBOR_ARRAY_TOTAL_COUNT]; // Mode Decision Refinement Neighbor Arrays - NeighborArrayUnit_t *mdRefinementIntraLumaModeNeighborArray; - NeighborArrayUnit_t *mdRefinementModeTypeNeighborArray; - NeighborArrayUnit_t *mdRefinementLumaReconNeighborArray; + NeighborArrayUnit_t **mdRefinementIntraLumaModeNeighborArray; + NeighborArrayUnit_t **mdRefinementModeTypeNeighborArray; + NeighborArrayUnit_t **mdRefinementLumaReconNeighborArray; // Encode Pass Neighbor Arrays - NeighborArrayUnit_t *epIntraLumaModeNeighborArray; - NeighborArrayUnit_t *epMvNeighborArray; - NeighborArrayUnit_t *epSkipFlagNeighborArray; - NeighborArrayUnit_t *epModeTypeNeighborArray; - NeighborArrayUnit_t *epLeafDepthNeighborArray; - NeighborArrayUnit_t *epLumaReconNeighborArray; - NeighborArrayUnit_t *epCbReconNeighborArray; - NeighborArrayUnit_t *epCrReconNeighborArray; - NeighborArrayUnit_t *epSaoNeighborArray; - NeighborArrayUnit_t *epLumaReconNeighborArray16bit; - NeighborArrayUnit_t *epCbReconNeighborArray16bit; - NeighborArrayUnit_t *epCrReconNeighborArray16bit; + NeighborArrayUnit_t **epIntraLumaModeNeighborArray; + NeighborArrayUnit_t **epMvNeighborArray; + NeighborArrayUnit_t **epSkipFlagNeighborArray; + NeighborArrayUnit_t **epModeTypeNeighborArray; + NeighborArrayUnit_t **epLeafDepthNeighborArray; + NeighborArrayUnit_t **epLumaReconNeighborArray; + NeighborArrayUnit_t **epCbReconNeighborArray; + NeighborArrayUnit_t **epCrReconNeighborArray; + NeighborArrayUnit_t **epSaoNeighborArray; + NeighborArrayUnit_t **epLumaReconNeighborArray16bit; + NeighborArrayUnit_t **epCbReconNeighborArray16bit; + NeighborArrayUnit_t **epCrReconNeighborArray16bit; // AMVP & MV Merge Neighbor Arrays - NeighborArrayUnit_t *amvpMvMergeMvNeighborArray; - NeighborArrayUnit_t *amvpMvMergeModeTypeNeighborArray; + //NeighborArrayUnit_t *amvpMvMergeMvNeighborArray; + //NeighborArrayUnit_t *amvpMvMergeModeTypeNeighborArray; // Entropy Coding Neighbor Arrays - NeighborArrayUnit_t *modeTypeNeighborArray; - NeighborArrayUnit_t *leafDepthNeighborArray; - NeighborArrayUnit_t *intraLumaModeNeighborArray; - NeighborArrayUnit_t *skipFlagNeighborArray; + NeighborArrayUnit_t **modeTypeNeighborArray; + NeighborArrayUnit_t **leafDepthNeighborArray; + NeighborArrayUnit_t **intraLumaModeNeighborArray; + NeighborArrayUnit_t **skipFlagNeighborArray; NeighborArrayUnit_t *tempModeTypeNeighborArray; NeighborArrayUnit_t *tempLeafDepthNeighborArray; @@ -314,7 +326,7 @@ typedef struct LcuParameters_s { EB_BOOL rasterScanCuValidity[CU_MAX_COUNT]; EB_U8 potentialLogoLcu; EB_U8 isEdgeLcu; -#if TILES + EB_U32 tileStartX; EB_U32 tileStartY; EB_U32 tileEndX; @@ -322,7 +334,7 @@ typedef struct LcuParameters_s { EB_BOOL tileLeftEdgeFlag; EB_BOOL tileTopEdgeFlag; EB_BOOL tileRightEdgeFlag; -#endif + EB_U32 tileIndex; } LcuParams_t; typedef struct CuStat_s { @@ -624,7 +636,8 @@ typedef struct PictureControlSetInitData_s EB_U8 tune; - + EB_U16 tileRowCount; + EB_U16 tileColumnCount; } PictureControlSetInitData_t; /************************************** diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index 707c4cb29..485f2683b 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -562,7 +562,7 @@ void* PictureManagerKernel(void *inputPtr) encodeContextPtr = (EncodeContext_t*) EB_NULL; break; - } + } // *********************************** // Common Code @@ -684,6 +684,10 @@ void* PictureManagerKernel(void *inputPtr) ChildPictureControlSetPtr->ParentPcsPtr->quantizedCoeffNumBits = 0; ChildPictureControlSetPtr->encMode = entryPictureControlSetPtr->encMode; + ChildPictureControlSetPtr->encDecCodedLcuCount = 0; + ChildPictureControlSetPtr->tileRowCount = entrySequenceControlSetPtr->tileRowCount; + ChildPictureControlSetPtr->tileColumnCount = entrySequenceControlSetPtr->tileColumnCount; + // Update temporal ID @@ -697,7 +701,6 @@ void* PictureManagerKernel(void *inputPtr) pictureWidthInLcu = (EB_U8)((entrySequenceControlSetPtr->lumaWidth + entrySequenceControlSetPtr->lcuSize - 1) / entrySequenceControlSetPtr->lcuSize); pictureHeightInLcu = (EB_U8)((entrySequenceControlSetPtr->lumaHeight + entrySequenceControlSetPtr->lcuSize - 1) / entrySequenceControlSetPtr->lcuSize); -#if TILES for (unsigned lcuIndex = 0; lcuIndex < (pictureWidthInLcu * pictureHeightInLcu); lcuIndex++) { ChildPictureControlSetPtr->lcuPtrArray[lcuIndex]->tileLeftEdgeFlag = entrySequenceControlSetPtr->lcuParamsArray[lcuIndex].tileLeftEdgeFlag; ChildPictureControlSetPtr->lcuPtrArray[lcuIndex]->tileTopEdgeFlag = entrySequenceControlSetPtr->lcuParamsArray[lcuIndex].tileTopEdgeFlag; @@ -707,28 +710,45 @@ void* PictureManagerKernel(void *inputPtr) ChildPictureControlSetPtr->lcuPtrArray[lcuIndex]->tileEndX = entrySequenceControlSetPtr->lcuParamsArray[lcuIndex].tileEndX; ChildPictureControlSetPtr->lcuPtrArray[lcuIndex]->tileEndY = entrySequenceControlSetPtr->lcuParamsArray[lcuIndex].tileEndY; } -#endif + + EB_U32 encDecSegRow = entrySequenceControlSetPtr->encDecSegmentRowCountArray[entryPictureControlSetPtr->temporalLayerIndex]; + EB_U32 encDecSegCol = entrySequenceControlSetPtr->encDecSegmentColCountArray[entryPictureControlSetPtr->temporalLayerIndex]; + + if (entrySequenceControlSetPtr->tileRowCount * entrySequenceControlSetPtr->tileColumnCount> 1) { + //Jing: Tuning segments number, better to put tile info to pps + encDecSegCol = pictureWidthInLcu;// / entrySequenceControlSetPtr->tileColumnCount; + encDecSegRow = pictureHeightInLcu / entrySequenceControlSetPtr->tileRowCount; + } + // EncDec Segments - EncDecSegmentsInit( - ChildPictureControlSetPtr->encDecSegmentCtrl, - entrySequenceControlSetPtr->encDecSegmentColCountArray[entryPictureControlSetPtr->temporalLayerIndex], - entrySequenceControlSetPtr->encDecSegmentRowCountArray[entryPictureControlSetPtr->temporalLayerIndex], - pictureWidthInLcu, - pictureHeightInLcu); - - // Entropy Coding Rows - { - unsigned rowIndex; - - ChildPictureControlSetPtr->entropyCodingCurrentRow = 0; - ChildPictureControlSetPtr->entropyCodingCurrentAvailableRow = 0; - ChildPictureControlSetPtr->entropyCodingRowCount = pictureHeightInLcu; - ChildPictureControlSetPtr->entropyCodingInProgress = EB_FALSE; - - for(rowIndex=0; rowIndex < MAX_LCU_ROWS; ++rowIndex) { - ChildPictureControlSetPtr->entropyCodingRowArray[rowIndex] = EB_FALSE; + for (int r = 0; r < entrySequenceControlSetPtr->tileRowCount; r++) { + EncDecSegmentsInit( + ChildPictureControlSetPtr->encDecSegmentCtrl[r], + encDecSegCol, + encDecSegRow, + //sequenceControlSetPtr->tileColumnArray[c], + pictureWidthInLcu, + sequenceControlSetPtr->tileRowArray[r]); + + // Jing: Apply with tile row for better parallelism.. + // Entropy Coding Rows + for (int c = 0; c < entrySequenceControlSetPtr->tileColumnCount; c++) { + int tileIdx = r * entrySequenceControlSetPtr->tileColumnCount + c; + ChildPictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingCurrentRow = 0; + ChildPictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingCurrentAvailableRow = 0; + ChildPictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingRowCount = sequenceControlSetPtr->tileRowArray[r]; + ChildPictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingInProgress = EB_FALSE; + ChildPictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingPicDone = EB_FALSE; + + for(unsigned rowIndex=0; rowIndex < MAX_LCU_ROWS; ++rowIndex) { + ChildPictureControlSetPtr->entropyCodingInfo[tileIdx]->entropyCodingRowArray[rowIndex] = EB_FALSE; + } } + ChildPictureControlSetPtr->entropyCodingPicResetFlag = EB_TRUE; } + + //Jing: TODO + //Check if need to check tile edges // Picture edges ConfigurePictureEdges(entrySequenceControlSetPtr, ChildPictureControlSetPtr); @@ -858,6 +878,25 @@ void* PictureManagerKernel(void *inputPtr) // Post the Full Results Object EbPostFullObject(outputWrapperPtr); +#if LATENCY_PROFILE + double latency = 0.0; + EB_U64 finishTimeSeconds = 0; + EB_U64 finishTimeuSeconds = 0; + EbFinishTime((uint64_t*)&finishTimeSeconds, (uint64_t*)&finishTimeuSeconds); + + EbComputeOverallElapsedTimeMs( + ChildPictureControlSetPtr->ParentPcsPtr->startTimeSeconds, + ChildPictureControlSetPtr->ParentPcsPtr->startTimeuSeconds, + finishTimeSeconds, + finishTimeuSeconds, + &latency); + + SVT_LOG("[%lld]: POC %lld PM OUT, decoder order %d, latency %3.3f \n", + EbGetSysTimeMs(), + ChildPictureControlSetPtr->pictureNumber, + ChildPictureControlSetPtr->ParentPcsPtr->decodeOrder, + latency); +#endif // Remove the Input Entry from the Input Queue inputEntryPtr->inputObjectPtr = (EbObjectWrapper_t*) EB_NULL; diff --git a/Source/Lib/Codec/EbProductCodingLoop.c b/Source/Lib/Codec/EbProductCodingLoop.c index b42ce9995..db858c2aa 100644 --- a/Source/Lib/Codec/EbProductCodingLoop.c +++ b/Source/Lib/Codec/EbProductCodingLoop.c @@ -271,11 +271,9 @@ extern void GenerateIntraLumaReferenceSamplesMd( EbPictureBufferDesc_t *inputPicturePtr) { -#if TILES EB_BOOL pictureLeftBoundary = (contextPtr->lcuPtr->tileLeftEdgeFlag == EB_TRUE && ((contextPtr->cuOriginX & (contextPtr->lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; EB_BOOL pictureTopBoundary = (contextPtr->lcuPtr->tileTopEdgeFlag == EB_TRUE && ((contextPtr->cuOriginY & (contextPtr->lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; EB_BOOL pictureRightBoundary = (contextPtr->lcuPtr->tileRightEdgeFlag == EB_TRUE && (((contextPtr->cuOriginX + contextPtr->cuStats->size) & (contextPtr->lcuPtr->size - 1)) == 0)) ? EB_TRUE : EB_FALSE; -#endif if (contextPtr->intraMdOpenLoopFlag == EB_FALSE) { @@ -292,15 +290,9 @@ extern void GenerateIntraLumaReferenceSamplesMd( (NeighborArrayUnit_t *) EB_NULL, (NeighborArrayUnit_t *) EB_NULL, contextPtr->intraRefPtr, -#if TILES pictureLeftBoundary, pictureTopBoundary, pictureRightBoundary); -#else - EB_FALSE, - EB_FALSE, - EB_FALSE); -#endif contextPtr->lumaIntraRefSamplesGenDone = EB_TRUE; @@ -506,6 +498,7 @@ void MvMergePassUpdateNeighborArrays( EB_U32 originX, EB_U32 originY, EB_U32 size, + EB_U16 tileIdx, EB_BOOL useIntraChromaflag) { @@ -595,21 +588,21 @@ void MvMergePassUpdateNeighborArrays( for (depthIndex = PILLAR_NEIGHBOR_ARRAY_INDEX; depthIndex <= REFINEMENT_NEIGHBOR_ARRAY_INDEX; depthIndex++) { NeighborArrayUnitDepthSkipWrite( - pictureControlSetPtr->mdLeafDepthNeighborArray[depthIndex], + pictureControlSetPtr->mdLeafDepthNeighborArray[depthIndex][tileIdx], (EB_U8*)depth, originX, originY, size); NeighborArrayUnitModeTypeWrite( - pictureControlSetPtr->mdModeTypeNeighborArray[depthIndex], + pictureControlSetPtr->mdModeTypeNeighborArray[depthIndex][tileIdx], (EB_U8*)modeType, originX, originY, size); NeighborArrayUnitIntraWrite( - pictureControlSetPtr->mdIntraLumaModeNeighborArray[depthIndex], + pictureControlSetPtr->mdIntraLumaModeNeighborArray[depthIndex][tileIdx], (EB_U8*)lumaMode, originX, originY, @@ -617,14 +610,14 @@ void MvMergePassUpdateNeighborArrays( // *Note - this has to be changed for non-square PU support -- JMJ NeighborArrayUnitMvWrite( - pictureControlSetPtr->mdMvNeighborArray[depthIndex], + pictureControlSetPtr->mdMvNeighborArray[depthIndex][tileIdx], (EB_U8*)mvUnit, originX, originY, size); NeighborArrayUnitDepthSkipWrite( - pictureControlSetPtr->mdSkipFlagNeighborArray[depthIndex], + pictureControlSetPtr->mdSkipFlagNeighborArray[depthIndex][tileIdx], (EB_U8*)skipFlag, originX, originY, @@ -633,7 +626,7 @@ void MvMergePassUpdateNeighborArrays( if (intraMdOpenLoop == EB_FALSE) { // Recon Samples - Luma NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdLumaReconNeighborArray[depthIndex], + pictureControlSetPtr->mdLumaReconNeighborArray[depthIndex][tileIdx], reconBuffer->bufferY, reconBuffer->strideY, originX & (lcuSize - 1), @@ -647,7 +640,7 @@ void MvMergePassUpdateNeighborArrays( if (useIntraChromaflag){ // Recon Samples - Cb NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdCbReconNeighborArray[depthIndex], + pictureControlSetPtr->mdCbReconNeighborArray[depthIndex][tileIdx], reconBuffer->bufferCb, reconBuffer->strideCb, (originX & (lcuSize - 1)) >> 1, @@ -660,7 +653,7 @@ void MvMergePassUpdateNeighborArrays( // Recon Samples - Cr NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdCrReconNeighborArray[depthIndex], + pictureControlSetPtr->mdCrReconNeighborArray[depthIndex][tileIdx], reconBuffer->bufferCr, reconBuffer->strideCr, (originX & (lcuSize - 1)) >> 1, @@ -702,6 +695,7 @@ void Bdp16x16vs8x8RefinementUpdateNeighborArrays( EB_U32 originX, EB_U32 originY, EB_U32 size, + EB_U16 tileIdx, EB_BOOL useIntraChromaflag) { @@ -791,7 +785,7 @@ void Bdp16x16vs8x8RefinementUpdateNeighborArrays( if (pictureControlSetPtr->sliceType == EB_I_PICTURE) { NeighborArrayUnitDepthSkipWrite( - pictureControlSetPtr->mdLeafDepthNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX], + pictureControlSetPtr->mdLeafDepthNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx], (EB_U8*)depth, originX, originY, @@ -799,14 +793,14 @@ void Bdp16x16vs8x8RefinementUpdateNeighborArrays( if (intra4x4Selected == EB_FALSE){ NeighborArrayUnitModeTypeWrite( - pictureControlSetPtr->mdModeTypeNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX], + pictureControlSetPtr->mdModeTypeNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx], (EB_U8*)modeType, originX, originY, size); NeighborArrayUnitIntraWrite( - pictureControlSetPtr->mdIntraLumaModeNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX], + pictureControlSetPtr->mdIntraLumaModeNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx], (EB_U8*)lumaMode, originX, originY, @@ -815,14 +809,14 @@ void Bdp16x16vs8x8RefinementUpdateNeighborArrays( // *Note - this has to be changed for non-square PU support -- JMJ NeighborArrayUnitMvWrite( - pictureControlSetPtr->mdMvNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX], + pictureControlSetPtr->mdMvNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx], (EB_U8*)mvUnit, originX, originY, size); NeighborArrayUnitDepthSkipWrite( - pictureControlSetPtr->mdSkipFlagNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX], + pictureControlSetPtr->mdSkipFlagNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx], (EB_U8*)skipFlag, originX, originY, @@ -831,7 +825,7 @@ void Bdp16x16vs8x8RefinementUpdateNeighborArrays( if (intraMdOpenLoop == EB_FALSE && intra4x4Selected == EB_FALSE){ // Recon Samples - Luma NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdLumaReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX], + pictureControlSetPtr->mdLumaReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx], reconBuffer->bufferY, reconBuffer->strideY, originX & (lcuSize - 1), @@ -845,7 +839,7 @@ void Bdp16x16vs8x8RefinementUpdateNeighborArrays( if (useIntraChromaflag){ // Recon Samples - Cb NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdCbReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX], + pictureControlSetPtr->mdCbReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx], reconBuffer->bufferCb, reconBuffer->strideCb, (originX & (lcuSize - 1)) >> 1, @@ -858,7 +852,7 @@ void Bdp16x16vs8x8RefinementUpdateNeighborArrays( // Recon Samples - Cr NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdCrReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX], + pictureControlSetPtr->mdCrReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx], reconBuffer->bufferCr, reconBuffer->strideCr, (originX & (lcuSize - 1)) >> 1, @@ -3722,22 +3716,23 @@ void UpdateMdNeighborArrays( EB_BOOL chromaModeFull) { + EB_U16 tileIdx = contextPtr->tileIndex; NeighborArrayUnitDepthSkipWrite( - pictureControlSetPtr->mdLeafDepthNeighborArray[0], + pictureControlSetPtr->mdLeafDepthNeighborArray[0][tileIdx], (EB_U8*)depth, originX, originY, size); NeighborArrayUnitModeTypeWrite( - pictureControlSetPtr->mdModeTypeNeighborArray[0], + pictureControlSetPtr->mdModeTypeNeighborArray[0][tileIdx], (EB_U8*)modeType, originX, originY, size); NeighborArrayUnitIntraWrite( - pictureControlSetPtr->mdIntraLumaModeNeighborArray[0], + pictureControlSetPtr->mdIntraLumaModeNeighborArray[0][tileIdx], (EB_U8*)lumaMode, originX, originY, @@ -3746,14 +3741,14 @@ void UpdateMdNeighborArrays( // *Note - this has to be changed for non-square PU support -- JMJ NeighborArrayUnitMvWrite( - pictureControlSetPtr->mdMvNeighborArray[0], + pictureControlSetPtr->mdMvNeighborArray[0][tileIdx], (EB_U8*)mvUnit, originX, originY, size); NeighborArrayUnitDepthSkipWrite( - pictureControlSetPtr->mdSkipFlagNeighborArray[0], + pictureControlSetPtr->mdSkipFlagNeighborArray[0][tileIdx], (EB_U8*)skipFlag, originX, originY, @@ -3762,7 +3757,7 @@ void UpdateMdNeighborArrays( if (contextPtr->intraMdOpenLoopFlag == EB_FALSE){ // Recon Samples - Luma NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdLumaReconNeighborArray[0], + pictureControlSetPtr->mdLumaReconNeighborArray[0][tileIdx], reconBuffer->bufferY, reconBuffer->strideY, originX & (lcuSize - 1), @@ -3776,7 +3771,7 @@ void UpdateMdNeighborArrays( if (chromaModeFull){ // Recon Samples - Cb NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdCbReconNeighborArray[0], + pictureControlSetPtr->mdCbReconNeighborArray[0][tileIdx], reconBuffer->bufferCb, reconBuffer->strideCb, (originX & (lcuSize - 1)) >> 1, @@ -3789,7 +3784,7 @@ void UpdateMdNeighborArrays( // Recon Samples - Cr NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdCrReconNeighborArray[0], + pictureControlSetPtr->mdCrReconNeighborArray[0][tileIdx], reconBuffer->bufferCr, reconBuffer->strideCr, (originX & (lcuSize - 1)) >> 1, @@ -3887,25 +3882,26 @@ void UpdateBdpNeighborArrays( { EB_U8 depthIndex; + EB_U16 tileIdx = contextPtr->tileIndex; for (depthIndex = PILLAR_NEIGHBOR_ARRAY_INDEX; depthIndex < NEIGHBOR_ARRAY_TOTAL_COUNT; depthIndex++) { NeighborArrayUnitDepthSkipWrite( - pictureControlSetPtr->mdLeafDepthNeighborArray[depthIndex], + pictureControlSetPtr->mdLeafDepthNeighborArray[depthIndex][tileIdx], (EB_U8*)depth, originX, originY, size); NeighborArrayUnitModeTypeWrite( - pictureControlSetPtr->mdModeTypeNeighborArray[depthIndex], + pictureControlSetPtr->mdModeTypeNeighborArray[depthIndex][tileIdx], (EB_U8*)modeType, originX, originY, size); NeighborArrayUnitIntraWrite( - pictureControlSetPtr->mdIntraLumaModeNeighborArray[depthIndex], + pictureControlSetPtr->mdIntraLumaModeNeighborArray[depthIndex][tileIdx], (EB_U8*)lumaMode, originX, originY, @@ -3913,14 +3909,14 @@ void UpdateBdpNeighborArrays( // *Note - this has to be changed for non-square PU support -- JMJ NeighborArrayUnitMvWrite( - pictureControlSetPtr->mdMvNeighborArray[depthIndex], + pictureControlSetPtr->mdMvNeighborArray[depthIndex][tileIdx], (EB_U8*)mvUnit, originX, originY, size); NeighborArrayUnitDepthSkipWrite( - pictureControlSetPtr->mdSkipFlagNeighborArray[depthIndex], + pictureControlSetPtr->mdSkipFlagNeighborArray[depthIndex][tileIdx], (EB_U8*)skipFlag, originX, originY, @@ -3929,7 +3925,7 @@ void UpdateBdpNeighborArrays( if (contextPtr->intraMdOpenLoopFlag == EB_FALSE){ // Recon Samples - Luma NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdLumaReconNeighborArray[depthIndex], + pictureControlSetPtr->mdLumaReconNeighborArray[depthIndex][tileIdx], reconBuffer->bufferY, reconBuffer->strideY, originX & (lcuSize - 1), @@ -3943,7 +3939,7 @@ void UpdateBdpNeighborArrays( if (chromaModeFull){ // Recon Samples - Cb NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdCbReconNeighborArray[depthIndex], + pictureControlSetPtr->mdCbReconNeighborArray[depthIndex][tileIdx], reconBuffer->bufferCb, reconBuffer->strideCb, (originX & (lcuSize - 1)) >> 1, @@ -3956,7 +3952,7 @@ void UpdateBdpNeighborArrays( // Recon Samples - Cr NeighborArrayUnitSampleWrite( - pictureControlSetPtr->mdCrReconNeighborArray[depthIndex], + pictureControlSetPtr->mdCrReconNeighborArray[depthIndex][tileIdx], reconBuffer->bufferCr, reconBuffer->strideCr, (originX & (lcuSize - 1)) >> 1, @@ -4046,9 +4042,10 @@ EB_EXTERN EB_ERRORTYPE ModeDecisionRefinementLcu( { EB_ERRORTYPE return_error = EB_ErrorNone; - contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdRefinementIntraLumaModeNeighborArray; - contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdRefinementModeTypeNeighborArray; - contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdRefinementLumaReconNeighborArray; + EB_U16 tileIdx = contextPtr->tileIndex; + contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdRefinementIntraLumaModeNeighborArray[tileIdx]; + contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdRefinementModeTypeNeighborArray[tileIdx]; + contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdRefinementLumaReconNeighborArray[tileIdx]; contextPtr->lcuPtr = lcuPtr; @@ -4710,6 +4707,8 @@ EB_EXTERN EB_ERRORTYPE ModeDecisionLcu( EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; + EB_U16 tileIdx = contextPtr->tileIndex; + // Mode Decision Candidate Buffers EB_U32 bufferTotalCount; ModeDecisionCandidateBuffer_t *candidateBuffer; @@ -4766,14 +4765,14 @@ EB_EXTERN EB_ERRORTYPE ModeDecisionLcu( mdcResultTbPtr); // Mode Decision Neighbor Arrays - contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[MD_NEIGHBOR_ARRAY_INDEX]; - contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[MD_NEIGHBOR_ARRAY_INDEX]; - contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[MD_NEIGHBOR_ARRAY_INDEX]; - contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[MD_NEIGHBOR_ARRAY_INDEX]; - contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[MD_NEIGHBOR_ARRAY_INDEX]; - contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[MD_NEIGHBOR_ARRAY_INDEX]; - contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[MD_NEIGHBOR_ARRAY_INDEX]; - contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[MD_NEIGHBOR_ARRAY_INDEX]; + contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[MD_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[MD_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[MD_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[MD_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[MD_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[MD_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[MD_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[MD_NEIGHBOR_ARRAY_INDEX][tileIdx]; contextPtr->edgeBlockNumFlag = (EB_BOOL)pictureControlSetPtr->ParentPcsPtr->edgeResultsPtr[lcuAddr].edgeBlockNum; // First CU Loop cuIdx = 0; @@ -5190,6 +5189,7 @@ EB_EXTERN EB_ERRORTYPE BdpPillar( // Input EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; + EB_U16 tileIdx = lcuParamPtr->tileIndex; // Mode Decision Candidate Buffers @@ -5236,14 +5236,14 @@ EB_EXTERN EB_ERRORTYPE BdpPillar( contextPtr->groupOf16x16BlocksCount = 0; // Mode Decision Neighbor Arrays - contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX]; - contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX]; - contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX]; - contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX]; - contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX]; - contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX]; - contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX]; - contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX]; + contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[PILLAR_NEIGHBOR_ARRAY_INDEX][tileIdx]; contextPtr->edgeBlockNumFlag = (EB_BOOL)pictureControlSetPtr->ParentPcsPtr->edgeResultsPtr[lcuAddr].edgeBlockNum; @@ -5614,18 +5614,19 @@ EB_EXTERN EB_ERRORTYPE Bdp64x64vs32x32RefinementProcess( ModeDecisionCandidateBuffer_t **candidateBufferPtrArray; EB_U32 maxBuffers; + EB_U16 tileIdx = lcuParamPtr->tileIndex; // Keep track of the LCU Ptr contextPtr->lcuPtr = lcuPtr; - contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; + contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; EB_U8 leafIndex = 0; @@ -5871,6 +5872,8 @@ EB_EXTERN EB_ERRORTYPE Bdp16x16vs8x8RefinementProcess( // Input EbPictureBufferDesc_t *inputPicturePtr = pictureControlSetPtr->ParentPcsPtr->chromaDownSamplePicturePtr; + EB_U16 tileIdx = contextPtr->tileIndex; + // Mode Decision Candidate Buffers EB_U32 bufferTotalCount; ModeDecisionCandidateBuffer_t *candidateBuffer; @@ -5902,14 +5905,14 @@ EB_EXTERN EB_ERRORTYPE Bdp16x16vs8x8RefinementProcess( // Keep track of the LCU Ptr contextPtr->lcuPtr = lcuPtr; - contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; - contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX]; + contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[REFINEMENT_NEIGHBOR_ARRAY_INDEX][tileIdx]; EB_U8 parentLeafIndex = 0; @@ -6285,6 +6288,7 @@ EB_EXTERN EB_ERRORTYPE Bdp16x16vs8x8RefinementProcess( contextPtr->cuOriginX, contextPtr->cuOriginY, contextPtr->cuStats->size, + tileIdx, contextPtr->useChromaInformationInFullLoop ? EB_TRUE : EB_FALSE); if (contextPtr->intraMdOpenLoopFlag == EB_FALSE) @@ -6340,18 +6344,19 @@ EB_EXTERN EB_ERRORTYPE BdpMvMergePass( ModeDecisionCandidateBuffer_t **candidateBufferPtrArray; EB_U32 maxBuffers; + EB_U16 tileIdx = contextPtr->tileIndex; // Keep track of the LCU Ptr contextPtr->lcuPtr = lcuPtr; - contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX]; - contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX]; - contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX]; - contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX]; - contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX]; - contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX]; - contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX]; - contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX]; + contextPtr->intraLumaModeNeighborArray = pictureControlSetPtr->mdIntraLumaModeNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->mvNeighborArray = pictureControlSetPtr->mdMvNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->skipFlagNeighborArray = pictureControlSetPtr->mdSkipFlagNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->modeTypeNeighborArray = pictureControlSetPtr->mdModeTypeNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->leafDepthNeighborArray = pictureControlSetPtr->mdLeafDepthNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->lumaReconNeighborArray = pictureControlSetPtr->mdLumaReconNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->cbReconNeighborArray = pictureControlSetPtr->mdCbReconNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX][tileIdx]; + contextPtr->crReconNeighborArray = pictureControlSetPtr->mdCrReconNeighborArray[MV_MERGE_PASS_NEIGHBOR_ARRAY_INDEX][tileIdx]; // First CU Loop EB_U8 leafIndex = 0; @@ -6562,6 +6567,7 @@ EB_EXTERN EB_ERRORTYPE BdpMvMergePass( contextPtr->cuOriginX, contextPtr->cuOriginY, contextPtr->cuStats->size, + tileIdx, contextPtr->useChromaInformationInFullLoop ? EB_TRUE : EB_FALSE); if (contextPtr->intraMdOpenLoopFlag == EB_FALSE) @@ -6619,6 +6625,7 @@ EB_EXTERN EB_ERRORTYPE BdpMvMergePass( contextPtr->cuOriginX, contextPtr->cuOriginY, contextPtr->cuStats->size, + tileIdx, contextPtr->useChromaInformationInFullLoop ? EB_TRUE : EB_FALSE); } leafIndex += DepthOffset[contextPtr->cuStats->depth]; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 212fcfd8f..b183a65d1 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -631,7 +631,9 @@ void HighLevelRcInputPictureMode2( hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp] = 0; if (refQpTableIndex == previousSelectedRefQp){ + EbBlockOnMutex(sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueueMutex); hlRateControlHistogramPtrTemp->lifeCount--; + EbReleaseMutex(sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueueMutex); } if (hlRateControlHistogramPtrTemp->isCoded){ // If the frame is already coded, use the actual number of bits diff --git a/Source/Lib/Codec/EbRateControlTasks.h b/Source/Lib/Codec/EbRateControlTasks.h index aab8e3010..2c5f935ec 100644 --- a/Source/Lib/Codec/EbRateControlTasks.h +++ b/Source/Lib/Codec/EbRateControlTasks.h @@ -32,6 +32,7 @@ typedef struct RateControlTasks_s // Following are valid for RC_ENTROPY_CODING_ROW_FEEDBACK_RESULT only EB_U64 pictureNumber; + EB_U16 tileIndex; EB_U32 rowNumber; EB_U32 bitCount; @@ -50,4 +51,4 @@ extern EB_ERRORTYPE RateControlTasksCtor( EB_PTR objectInitDataPtr); -#endif // EbRateControlTasks_h \ No newline at end of file +#endif // EbRateControlTasks_h diff --git a/Source/Lib/Codec/EbResourceCoordinationProcess.c b/Source/Lib/Codec/EbResourceCoordinationProcess.c index 50304b407..8634951ca 100644 --- a/Source/Lib/Codec/EbResourceCoordinationProcess.c +++ b/Source/Lib/Codec/EbResourceCoordinationProcess.c @@ -456,6 +456,52 @@ EB_ERRORTYPE SignalDerivationPreAnalysisVmaf( return return_error; } +static void InitTileInfo(SequenceControlSet_t *scsPtr) +{ + scsPtr->tileUniformSpacing = 1; + scsPtr->tileColumnCount = scsPtr->staticConfig.tileColumnCount; + scsPtr->tileRowCount = scsPtr->staticConfig.tileRowCount; + scsPtr->tileSliceMode = scsPtr->staticConfig.tileSliceMode; + + scsPtr->pictureWidthInLcu = (EB_U8)((scsPtr->lumaWidth + scsPtr->lcuSize - 1) / scsPtr->lcuSize); + scsPtr->pictureHeightInLcu = (EB_U8)((scsPtr->lumaHeight + scsPtr->lcuSize - 1) / scsPtr->lcuSize); + + scsPtr->lcuTotalCount = scsPtr->pictureWidthInLcu * scsPtr->pictureHeightInLcu; + + EB_U16 lastColumnWidth = scsPtr->pictureWidthInLcu; + EB_U16 lastRowHeight = scsPtr->pictureHeightInLcu; + EB_U16 rowIndex, columnIndex; + + scsPtr->tileUniformSpacing = 1; + if (scsPtr->tileUniformSpacing == 1) { + for (columnIndex = 0; columnIndex + 1 < scsPtr->tileColumnCount; ++columnIndex) { + scsPtr->tileColumnArray[columnIndex] = (EB_U16)((columnIndex + 1) * scsPtr->pictureWidthInLcu / scsPtr->tileColumnCount - + columnIndex * scsPtr->pictureWidthInLcu / scsPtr->tileColumnCount); + lastColumnWidth -= scsPtr->tileColumnArray[columnIndex]; + } + scsPtr->tileColumnArray[columnIndex] = (EB_U16)lastColumnWidth; + + for (rowIndex = 0; rowIndex + 1 < scsPtr->tileRowCount; ++rowIndex) { + scsPtr->tileRowArray[rowIndex] = (EB_U16)((rowIndex + 1) * scsPtr->pictureHeightInLcu / scsPtr->tileRowCount - + rowIndex * scsPtr->pictureHeightInLcu / scsPtr->tileRowCount); + lastRowHeight -= scsPtr->tileRowArray[rowIndex]; + } + scsPtr->tileRowArray[rowIndex] = (EB_U16)lastRowHeight; + } else { + for (columnIndex = 0; columnIndex + 1 < scsPtr->tileColumnCount; ++columnIndex) { + scsPtr->tileColumnArray[columnIndex] = (EB_U16)scsPtr->tileColumnWidthArray[columnIndex]; + lastColumnWidth -= scsPtr->tileColumnArray[columnIndex]; + } + scsPtr->tileColumnArray[columnIndex] = (EB_U16)lastColumnWidth; + + for (rowIndex = 0; rowIndex + 1 < scsPtr->tileRowCount; ++rowIndex) { + scsPtr->tileRowArray[rowIndex] = (EB_U16)scsPtr->tileRowHeightArray[rowIndex]; + lastRowHeight -= scsPtr->tileRowArray[rowIndex]; + } + scsPtr->tileRowArray[rowIndex] = (EB_U16)lastRowHeight; + } +} + /*************************************** * ResourceCoordination Kernel @@ -606,12 +652,8 @@ void* ResourceCoordinationKernel(void *inputPtr) // Set the current SequenceControlSet sequenceControlSetPtr = (SequenceControlSet_t*) contextPtr->sequenceControlSetActiveArray[instanceIndex]->objectPtr; -#if TILES - sequenceControlSetPtr->tileUniformSpacing = 1; - sequenceControlSetPtr->tileColumnCount = sequenceControlSetPtr->staticConfig.tileColumnCount; - sequenceControlSetPtr->tileRowCount = sequenceControlSetPtr->staticConfig.tileRowCount; - sequenceControlSetPtr->tileSliceMode = sequenceControlSetPtr->staticConfig.tileSliceMode; -#endif + InitTileInfo(sequenceControlSetPtr); + // Init LCU Params if (contextPtr->sequenceControlSetInstanceArray[instanceIndex]->encodeContextPtr->initialPicture) { DeriveInputResolution( @@ -797,7 +839,6 @@ void* ResourceCoordinationKernel(void *inputPtr) // Post the finished Results Object EbPostFullObject(outputWrapperPtr); - } prevPictureControlSetWrapperPtr = pictureControlSetWrapperPtr; diff --git a/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c b/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c index 355513761..18de7acbc 100644 --- a/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c +++ b/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c @@ -729,7 +729,7 @@ EB_ERRORTYPE SaoGenerationDecision( SaoGatherFunctionTableLossy[!!(ASM_TYPES & PREAVX2_MASK)]( &(inputPicturePtr->bufferCr[(((inputPicturePtr->originY + tbOriginY) * inputPicturePtr->strideCr) >> subHeightCMinus1) + ((inputPicturePtr->originX + tbOriginX) >> subWidthCMinus1)]), inputPicturePtr->strideCr, - &(reconPicturePtr->bufferCr[(((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCr) >> subHeightCMinus1) + ((reconPicturePtr->originX + tbOriginX) >> subHeightCMinus1)]), + &(reconPicturePtr->bufferCr[(((reconPicturePtr->originY + tbOriginY) * reconPicturePtr->strideCr) >> subHeightCMinus1) + ((reconPicturePtr->originX + tbOriginX) >> subWidthCMinus1)]), reconPicturePtr->strideCr, lcuWidth >> subWidthCMinus1, lcuHeight >> subHeightCMinus1, @@ -1142,4 +1142,4 @@ EB_ERRORTYPE SaoGenerationDecision16bit( } return return_error; -} \ No newline at end of file +} diff --git a/Source/Lib/Codec/EbSei.c b/Source/Lib/Codec/EbSei.c index e4f905dbb..7fe1e3039 100644 --- a/Source/Lib/Codec/EbSei.c +++ b/Source/Lib/Codec/EbSei.c @@ -454,7 +454,7 @@ EB_U32 GetBufPeriodSEILength( if(bufferingPeriodPtr->rapCpbParamsPresentFlag) { // cpb_delay_offset - seiLength += vuiPtr->hrdParametersPtr->initialCpbRemovalDelayLengthMinus1 + 1; + seiLength += vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1; // dpb_delay_offset seiLength += vuiPtr->hrdParametersPtr->dpbOutputDelayDuLengthMinus1 + 1; @@ -464,7 +464,8 @@ EB_U32 GetBufPeriodSEILength( seiLength += 1; // au_cpb_removal_delay_delta_minus1 - seiLength += vuiPtr->hrdParametersPtr->initialCpbRemovalDelayLengthMinus1 + 1; + seiLength += vuiPtr->hrdParametersPtr->auCpbRemovalDelayLengthMinus1 + 1; + for(nalVclIndex = 0; nalVclIndex < 2; ++nalVclIndex) { if((nalVclIndex == 0 && vuiPtr->hrdParametersPtr->nalHrdParametersPresentFlag) || (nalVclIndex == 1 && vuiPtr->hrdParametersPtr->vclHrdParametersPresentFlag)) { diff --git a/Source/Lib/Codec/EbSequenceControlSet.c b/Source/Lib/Codec/EbSequenceControlSet.c index e2340ab53..663a296c4 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.c +++ b/Source/Lib/Codec/EbSequenceControlSet.c @@ -349,7 +349,6 @@ extern EB_ERRORTYPE LcuParamsCtor( return return_error; } -#if TILES /************************************************ * Configure ME Tiles ************************************************/ @@ -357,50 +356,14 @@ static void ConfigureTiles( SequenceControlSet_t *scsPtr) { // Tiles Initialisation - const unsigned pictureWidthInLcu = scsPtr->pictureWidthInLcu; - const unsigned pictureHeightInLcu = scsPtr->pictureHeightInLcu; - const unsigned tileColumns = scsPtr->tileColumnCount; const unsigned tileRows = scsPtr->tileRowCount; - unsigned lastColumnWidth = pictureWidthInLcu; - unsigned lastRowHeight = pictureHeightInLcu; unsigned rowIndex, columnIndex; unsigned xLcuIndex, yLcuIndex, lcuIndex; unsigned xLcuStart, yLcuStart; - if (scsPtr->tileUniformSpacing == 1) - { - for (columnIndex = 0; columnIndex < tileColumns - 1; ++columnIndex) { - scsPtr->tileColumnArray[columnIndex] = (EB_U16)((columnIndex + 1) * pictureWidthInLcu / tileColumns - - columnIndex * pictureWidthInLcu / tileColumns); - lastColumnWidth -= scsPtr->tileColumnArray[columnIndex]; - } - scsPtr->tileColumnArray[columnIndex] = (EB_U16)lastColumnWidth; - - for (rowIndex = 0; rowIndex < tileRows - 1; ++rowIndex) { - scsPtr->tileRowArray[rowIndex] = (EB_U16)((rowIndex + 1) * pictureHeightInLcu / tileRows - - rowIndex * pictureHeightInLcu / tileRows); - lastRowHeight -= scsPtr->tileRowArray[rowIndex]; - } - scsPtr->tileRowArray[rowIndex] = (EB_U16)lastRowHeight; - } - else - { - for (columnIndex = 0; columnIndex < tileColumns - 1; ++columnIndex) { - scsPtr->tileColumnArray[columnIndex] = (EB_U16)scsPtr->tileColumnWidthArray[columnIndex]; - lastColumnWidth -= scsPtr->tileColumnArray[columnIndex]; - } - scsPtr->tileColumnArray[columnIndex] = (EB_U16)lastColumnWidth; - - for (rowIndex = 0; rowIndex < tileRows - 1; ++rowIndex) { - scsPtr->tileRowArray[rowIndex] = (EB_U16)scsPtr->tileRowHeightArray[rowIndex]; - lastRowHeight -= scsPtr->tileRowArray[rowIndex]; - } - scsPtr->tileRowArray[rowIndex] = (EB_U16)lastRowHeight; - } - // Tile-loops yLcuStart = 0; for (rowIndex = 0; rowIndex < tileRows; ++rowIndex) { @@ -410,7 +373,7 @@ static void ConfigureTiles( // LCU-loops for (yLcuIndex = yLcuStart; yLcuIndex < yLcuStart + scsPtr->tileRowArray[rowIndex]; ++yLcuIndex) { for (xLcuIndex = xLcuStart; xLcuIndex < xLcuStart + scsPtr->tileColumnArray[columnIndex]; ++xLcuIndex) { - lcuIndex = xLcuIndex + yLcuIndex * pictureWidthInLcu; + lcuIndex = xLcuIndex + yLcuIndex * scsPtr->pictureWidthInLcu; scsPtr->lcuParamsArray[lcuIndex].tileLeftEdgeFlag = (xLcuIndex == xLcuStart) ? EB_TRUE : EB_FALSE; scsPtr->lcuParamsArray[lcuIndex].tileTopEdgeFlag = (yLcuIndex == yLcuStart) ? EB_TRUE : EB_FALSE; scsPtr->lcuParamsArray[lcuIndex].tileRightEdgeFlag = @@ -419,6 +382,7 @@ static void ConfigureTiles( scsPtr->lcuParamsArray[lcuIndex].tileStartY = yLcuStart * scsPtr->lcuSize; scsPtr->lcuParamsArray[lcuIndex].tileEndX = (columnIndex == (tileColumns - 1)) ? scsPtr->lumaWidth : (xLcuStart + scsPtr->tileColumnArray[columnIndex]) * scsPtr->lcuSize; scsPtr->lcuParamsArray[lcuIndex].tileEndY = (rowIndex == (tileRows - 1)) ? scsPtr->lumaHeight : (yLcuStart + scsPtr->tileRowArray[rowIndex]) * scsPtr->lcuSize; + scsPtr->lcuParamsArray[lcuIndex].tileIndex = rowIndex * tileColumns + columnIndex; } } xLcuStart += scsPtr->tileColumnArray[columnIndex]; @@ -428,7 +392,6 @@ static void ConfigureTiles( return; } -#endif extern EB_ERRORTYPE LcuParamsInit( SequenceControlSet_t *sequenceControlSetPtr) { @@ -437,8 +400,8 @@ extern EB_ERRORTYPE LcuParamsInit( EB_U16 lcuIndex; EB_U16 rasterScanCuIndex; - EB_U8 pictureLcuWidth = (EB_U8)((sequenceControlSetPtr->lumaWidth + sequenceControlSetPtr->lcuSize - 1) / sequenceControlSetPtr->lcuSize); - EB_U8 pictureLcuHeight = (EB_U8)((sequenceControlSetPtr->lumaHeight + sequenceControlSetPtr->lcuSize - 1) / sequenceControlSetPtr->lcuSize); + EB_U8 pictureLcuWidth = sequenceControlSetPtr->pictureWidthInLcu; + EB_U8 pictureLcuHeight = sequenceControlSetPtr->pictureHeightInLcu; EB_MALLOC(LcuParams_t*, sequenceControlSetPtr->lcuParamsArray, sizeof(LcuParams_t) * pictureLcuWidth * pictureLcuHeight, EB_N_PTR); for (lcuIndex = 0; lcuIndex < pictureLcuWidth * pictureLcuHeight; ++lcuIndex) { @@ -532,13 +495,7 @@ extern EB_ERRORTYPE LcuParamsInit( } } - sequenceControlSetPtr->pictureWidthInLcu = pictureLcuWidth; - sequenceControlSetPtr->pictureHeightInLcu = pictureLcuHeight; - sequenceControlSetPtr->lcuTotalCount = pictureLcuWidth * pictureLcuHeight; - -#if TILES ConfigureTiles(sequenceControlSetPtr); -#endif return return_error; } diff --git a/Source/Lib/Codec/EbSequenceControlSet.h b/Source/Lib/Codec/EbSequenceControlSet.h index 2f0b94a9f..22cdcad88 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.h +++ b/Source/Lib/Codec/EbSequenceControlSet.h @@ -25,7 +25,6 @@ typedef struct SequenceControlSet_s { EB_H265_ENC_CONFIGURATION staticConfig; -#if TILES // Tiles // Better to put into PictureControlSet EB_U32 tileUniformSpacing; @@ -36,7 +35,6 @@ typedef struct SequenceControlSet_s EB_U16 tileRowHeightArray[EB_TILE_ROW_MAX_COUNT]; EB_U16 tileColumnArray[EB_TILE_COLUMN_MAX_COUNT]; EB_U16 tileRowArray[EB_TILE_ROW_MAX_COUNT]; -#endif // Encoding Context EncodeContext_t *encodeContextPtr; @@ -181,7 +179,7 @@ typedef struct SequenceControlSet_s EB_U32 rateControlTasksFifoInitCount; EB_U32 rateControlFifoInitCount; EB_U32 modeDecisionConfigurationFifoInitCount; - EB_U32 modeDecisionFifoInitCount; + //EB_U32 modeDecisionFifoInitCount; EB_U32 encDecFifoInitCount; EB_U32 entropyCodingFifoInitCount; EB_U32 pictureAnalysisProcessInitCount; From c9ce68b42bfc0803f37978e0783910ca1e20075d Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 10 Jul 2019 17:54:46 +0530 Subject: [PATCH 64/93] Cleanup unused variables --- Source/Lib/Codec/EbEncDecProcess.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index c1ad4320b..801242d91 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3863,7 +3863,7 @@ EB_U32 predBitsPerLcu(PictureControlSet_t* pictureControlSetPtr, EncodeContext_t return sadBits; } -EB_U64 predictBitsDup(SequenceControlSet_t *sequenceControlSetPtr, PictureControlSet_t* pictureControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp,EB_U32 qp) +EB_U64 predictBitsDup(SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp,EB_U32 qp) { EB_U64 totalBits = 0; RateControlTables_t *rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qp]; @@ -3914,15 +3914,10 @@ EB_U64 predictRowsSizeSum(PictureControlSet_t* pictureControlSetPtr, SequenceCon { EB_U64 predictedBitsForFrame = 0; EB_U64 predictedBitsDoneSoFar = 0; - EB_U64 distortionBitsSoFar = 0; *encodedBitsSoFar = 0; - EB_U64 distortionSofar = 0; - EB_U64 distortionSofarIntra = 0; - EB_U64 distortionSofarInter = 0; EB_U32 queueEntryIndexTemp; EB_U32 queueEntryIndexHeadTemp; HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp; - EB_BOOL useIntraSadFlag = EB_TRUE; EB_U8 lcuSizeLog2 = (EB_U8)Log2f(sequenceControlSetPtr->lcuSize); EB_U8 pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; EB_U8 pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; @@ -3947,12 +3942,12 @@ EB_U64 predictRowsSizeSum(PictureControlSet_t* pictureControlSetPtr, SequenceCon *encodedBitsSoFar += pictureControlSetPtr->rowStats[row]->encodedBits; predictedBitsDoneSoFar += pictureControlSetPtr->rowStats[row]->predictedBits; } - predictedBitsForFrame = predictBitsDup(sequenceControlSetPtr, pictureControlSetPtr, sequenceControlSetPtr->encodeContextPtr, hlRateControlHistogramPtrTemp, qpVbv); + predictedBitsForFrame = predictBitsDup(sequenceControlSetPtr, sequenceControlSetPtr->encodeContextPtr, hlRateControlHistogramPtrTemp, qpVbv); EB_U64 framesizeEstimated = (predictedBitsForFrame - predictedBitsDoneSoFar) + (*encodedBitsSoFar); return framesizeEstimated; } -EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,///mutex to be added since Buffer fill is read +EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,//mutex to be added since Buffer fill is read SequenceControlSet_t *sequenceControlSetPtr, RCStatRow_t *rowPtr, EncodeContext_t *rcData, @@ -3963,18 +3958,15 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,///mutex to EB_U8 qpAbsoluteMax = sequenceControlSetPtr->staticConfig.maxQpAllowed; EB_U8 qpAbsoluteMin = sequenceControlSetPtr->staticConfig.minQpAllowed; EB_U8 lcuSizeLog2 = (EB_U8)Log2f(sequenceControlSetPtr->lcuSize); - EB_U8 pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; EB_U8 pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; EB_U8 qpMax = MIN(prevRowQp + 4, qpAbsoluteMax); EB_U8 qpMin = MAX(prevRowQp - 4, qpAbsoluteMin); EB_U64 bufferLeftPlanned = pictureControlSetPtr->bufferFillPerFrame - pictureControlSetPtr->frameSizePlanned; - double maxFrameError = MAX(0.05, 1.0 / pictureHeightInLcu); if (rowPtr->rowIndexframeNumThreads * m_rateTolerance; + EB_U64 rcTol = 1 * bufferLeftPlanned; EB_U64 encodedBitsSoFar = 0; EB_U64 accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); @@ -4014,12 +4006,6 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,///mutex to pictureControlSetPtr->frameSizeEstimated = accFrameBits; - ///* If the current row was large enough to cause a large QP jump */ - //if (qpVbv > qpMax && prevRowQp < qpMax) - //{ - // /* Bump QP to halfway in between... close enough. */ - // qpVbv = CLIP3(prevRowQp, qpMax, (EB_U8)((prevRowQp + qpVbv) * 0.5)); - //} } From 5f9571066b0998c176d12ad741ee0f1c65db39e5 Mon Sep 17 00:00:00 2001 From: kirithika7 <36920443+kirithika7@users.noreply.github.com> Date: Wed, 10 Jul 2019 18:34:53 +0530 Subject: [PATCH 65/93] Update CMakeLists.txt Modify code as per suggestion Co-Authored-By: Christopher Degawa --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47f53b397..bd3023033 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,7 +118,10 @@ if(MSVC) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NXCompat /DynamicBase") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NXCompat /DynamicBase") else() - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lm") + find_library(M_LIB name m) + if(M_LIB) + link_libraries(m) + endif() endif() foreach(flag ${flags_to_test};${release_flags_to_test};${debug_flags_to_test}) From 1165741b95a16746fb47baa1afe71c20f670b7f5 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 10 Jul 2019 19:19:38 +0530 Subject: [PATCH 66/93] Revert "Update CMakeLists.txt " This reverts commit 5f9571066b0998c176d12ad741ee0f1c65db39e5. --- CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd3023033..47f53b397 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,10 +118,7 @@ if(MSVC) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NXCompat /DynamicBase") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NXCompat /DynamicBase") else() - find_library(M_LIB name m) - if(M_LIB) - link_libraries(m) - endif() + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lm") endif() foreach(flag ${flags_to_test};${release_flags_to_test};${debug_flags_to_test}) From 5e6e924099472d2562af53cc752ba72c85473a41 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 15 Jul 2019 13:18:02 +0530 Subject: [PATCH 67/93] Fix make error caught while linking math library --- CMakeLists.txt | 2 -- Source/App/CMakeLists.txt | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47f53b397..d649a7246 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,8 +117,6 @@ set(debug_flags_to_test if(MSVC) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NXCompat /DynamicBase") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NXCompat /DynamicBase") -else() - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lm") endif() foreach(flag ${flags_to_test};${release_flags_to_test};${debug_flags_to_test}) diff --git a/Source/App/CMakeLists.txt b/Source/App/CMakeLists.txt index b57121c83..4f1077570 100644 --- a/Source/App/CMakeLists.txt +++ b/Source/App/CMakeLists.txt @@ -32,7 +32,7 @@ target_link_libraries(SvtHevcEncApp if(UNIX) target_link_libraries(SvtHevcEncApp - pthread) + pthread m) if(NOT APPLE) target_link_libraries(SvtHevcEncApp rt) From d3d530c3b4e61c2c6a4a499255eba12aed273b15 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 15 Jul 2019 16:17:37 +0530 Subject: [PATCH 68/93] Fix SVT-HEVC ffmpeg integration error This commit fixes the error- SvtHevcEnc not found using pkg-config due to improper linking of math library --- Source/Lib/Codec/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Lib/Codec/CMakeLists.txt b/Source/Lib/Codec/CMakeLists.txt index 057c24a0b..1b8c0b9cf 100644 --- a/Source/Lib/Codec/CMakeLists.txt +++ b/Source/Lib/Codec/CMakeLists.txt @@ -184,6 +184,7 @@ add_library(SvtHevcEnc SHARED if(UNIX) set(LIBS "-lpthread") + set(LIBS "${LIBS} -lm") # Set version number for SONAME. set_target_properties(SvtHevcEnc PROPERTIES SOVERSION ${SVT_HEVC_SOVERSION}) From 85a36e2ddd90e2fc19ad34e82f351b487a50f9fa Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 30 Jul 2019 18:53:38 +0530 Subject: [PATCH 69/93] Fix license header in newly added files Signed-off-by: kirithika --- .../Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm | 35 +++---------------- Source/Lib/ASM_AVX2/const-a.asm | 31 +++------------- Source/Lib/ASM_AVX2/x86util.asm | 29 +++------------ 3 files changed, 13 insertions(+), 82 deletions(-) diff --git a/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm b/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm index 4e795dc21..2b05971df 100644 --- a/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm +++ b/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm @@ -1,34 +1,7 @@ -;***************************************************************************** -;* pixel.asm: x86 pixel metrics -;***************************************************************************** -;* Copyright (C) 2003-2013 x264 project -;* Copyright (C) 2013-2017 MulticoreWare, Inc -;* -;* Authors: Loren Merritt -;* Holger Lubitz -;* Laurent Aimar -;* Alex Izvorski -;* Fiona Glaser -;* Oskar Arvidsson -;* Min Chen -;* -;* This program is free software; you can redistribute it and/or modify -;* it under the terms of the GNU General Public License as published by -;* the Free Software Foundation; either version 2 of the License, or -;* (at your option) any later version. -;* -;* This program is distributed in the hope that it will be useful, -;* but WITHOUT ANY WARRANTY; without even the implied warranty of -;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;* GNU General Public License for more details. -;* -;* You should have received a copy of the GNU General Public License -;* along with this program; if not, write to the Free Software -;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. -;* -;* This program is also available under a commercial proprietary license. -;* For more information, contact us at license @ x265.com. -;***************************************************************************** +; +; Copyright(c) 2018 Intel Corporation +; SPDX - License - Identifier: BSD - 2 - Clause - Patent +; %include "x86inc.asm" %include "x86util.asm" diff --git a/Source/Lib/ASM_AVX2/const-a.asm b/Source/Lib/ASM_AVX2/const-a.asm index 4553a21b1..81186de4e 100644 --- a/Source/Lib/ASM_AVX2/const-a.asm +++ b/Source/Lib/ASM_AVX2/const-a.asm @@ -1,30 +1,7 @@ -;***************************************************************************** -;* const-a.asm: x86 global constants -;***************************************************************************** -;* Copyright (C) 2003-2013 x264 project -;* Copyright (C) 2013-2017 MulticoreWare, Inc -;* -;* Authors: Loren Merritt -;* Fiona Glaser -;* Min Chen -;* Praveen Kumar Tiwari -;* This program is free software; you can redistribute it and/or modify -;* it under the terms of the GNU General Public License as published by -;* the Free Software Foundation; either version 2 of the License, or -;* (at your option) any later version. -;* -;* This program is distributed in the hope that it will be useful, -;* but WITHOUT ANY WARRANTY; without even the implied warranty of -;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;* GNU General Public License for more details. -;* -;* You should have received a copy of the GNU General Public License -;* along with this program; if not, write to the Free Software -;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. -;* -;* This program is also available under a commercial proprietary license. -;* For more information, contact us at license @ x265.com. -;***************************************************************************** +; +; Copyright(c) 2018 Intel Corporation +; SPDX - License - Identifier: BSD - 2 - Clause - Patent +; %include "x86inc.asm" diff --git a/Source/Lib/ASM_AVX2/x86util.asm b/Source/Lib/ASM_AVX2/x86util.asm index 36bd3b0e0..4139fba4b 100644 --- a/Source/Lib/ASM_AVX2/x86util.asm +++ b/Source/Lib/ASM_AVX2/x86util.asm @@ -1,30 +1,11 @@ ;***************************************************************************** ;* x86util.asm: x86 utility macros ;***************************************************************************** -;* Copyright (C) 2003-2013 x264 project -;* Copyright (C) 2013-2017 MulticoreWare, Inc -;* -;* Authors: Holger Lubitz -;* Loren Merritt -;* Min Chen -;* -;* This program is free software; you can redistribute it and/or modify -;* it under the terms of the GNU General Public License as published by -;* the Free Software Foundation; either version 2 of the License, or -;* (at your option) any later version. -;* -;* This program is distributed in the hope that it will be useful, -;* but WITHOUT ANY WARRANTY; without even the implied warranty of -;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;* GNU General Public License for more details. -;* -;* You should have received a copy of the GNU General Public License -;* along with this program; if not, write to the Free Software -;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. -;* -;* This program is also available under a commercial proprietary license. -;* For more information, contact us at license @ x265.com. -;***************************************************************************** +; +; Copyright(c) 2018 Intel Corporation +; SPDX - License - Identifier: BSD - 2 - Clause - Patent +; + %define ARCH_X86_64 1 %define HIGH_BIT_DEPTH 0 From c5bd91699dfa17596ddae714ff2b0f723e9002f3 Mon Sep 17 00:00:00 2001 From: kirithika Date: Thu, 1 Aug 2019 23:13:17 +0530 Subject: [PATCH 70/93] Fix initialization of Neighbor arrays in EstimateLcu() Signed-off-by: kirithika --- Source/Lib/Codec/EbEncDecProcess.c | 50 +++++++++++++++++++--- Source/Lib/Codec/EbPictureControlSet.c | 57 ++++++++++++++++++++++++++ Source/Lib/Codec/EbPictureControlSet.h | 9 ++-- 3 files changed, 106 insertions(+), 10 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 0dbb31cd2..2f3bcd44d 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -1316,6 +1316,34 @@ static void ResetEncodePassNeighborArrays(PictureControlSet_t *pictureControlSet return; } +//Reset Proxy Neighbor arrays in Encdec +static void EntropyCodingResetTempNeighborArrays(PictureControlSet_t *pictureControlSetPtr, EB_U16 tileIdx) +{ + NeighborArrayUnitReset(pictureControlSetPtr->tempModeTypeNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->tempLeafDepthNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->tempIntraLumaModeNeighborArray[tileIdx]); + NeighborArrayUnitReset(pictureControlSetPtr->tempSkipFlagNeighborArray[tileIdx]); + + return; +} + +static void ResetTempEntropy( + EncDecContext_t *contextPtr, + PictureControlSet_t *pictureControlSetPtr, + SequenceControlSet_t *sequenceControlSetPtr) { + EB_U32 tileCnt = pictureControlSetPtr->tileRowCount * pictureControlSetPtr->tileColumnCount; + EB_U32 tileIdx = 0; + EB_U32 entropyCodingQp = pictureControlSetPtr->pictureQp; + for (tileIdx = 0; tileIdx < tileCnt; tileIdx++) { + ResetEntropyCoder( + sequenceControlSetPtr->encodeContextPtr, + pictureControlSetPtr->entropyCodingInfo[tileIdx]->tempEntropyCoderPtr, + entropyCodingQp, + pictureControlSetPtr->sliceType); + + EntropyCodingResetTempNeighborArrays(pictureControlSetPtr, tileIdx); + } +} /************************************************** * Reset Coding Loop **************************************************/ @@ -4142,6 +4170,7 @@ void* EncDecKernel(void *inputPtr) contextPtr); } + #if 1//TILES //NEED these to test stream complaince // contextPtr->pmMethod = 0; contextPtr->mdContext->rdoqPmCoreMethod = EB_NO_RDOQ; //RDOQ make DLF cause MD5 mismatch when encDec segments+QP mod are ON.. @@ -4192,7 +4221,15 @@ void* EncDecKernel(void *inputPtr) pictureControlSetPtr, sequenceControlSetPtr, segmentIndex); - + //Reset Temp Entropy Coding + if (segmentIndex == 0) { + if (contextPtr->tileRowIndex == 0) { + if (pictureControlSetPtr->resetProxyFlag == EB_FALSE) { + ResetTempEntropy(contextPtr, pictureControlSetPtr, sequenceControlSetPtr); + pictureControlSetPtr->resetProxyFlag = EB_TRUE; + } + } + } contextPtr->mdContext->CabacCost = pictureControlSetPtr->cabacCost; if (pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr != NULL) { @@ -4219,6 +4256,7 @@ void* EncDecKernel(void *inputPtr) rowIndex = (EB_U16)(yLcuIndex / pictureHeightInLcu); rowPtr = pictureControlSetPtr->rowStats[rowIndex]; pictureControlSetPtr->firstRowOfPicture = (xLcuIndex <= pictureWidthInLcu) ? EB_TRUE : EB_FALSE; + lcuOriginX = (xLcuIndex+tileGroupLcuStartX) << lcuSizeLog2; lcuOriginY = (yLcuIndex+tileGroupLcuStartY) << lcuSizeLog2; //printf("Process lcu (%d, %d), lcuIndex %d, segmentIndex %d\n", lcuOriginX, lcuOriginY, lcuIndex, segmentIndex); @@ -4414,7 +4452,7 @@ void* EncDecKernel(void *inputPtr) tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); - EstimateLcu( + EstimateLcu( lcuPtr, lcuOriginX, lcuOriginY, @@ -4422,10 +4460,10 @@ void* EncDecKernel(void *inputPtr) sequenceControlSetPtr->lcuSize, pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr, tempCoeffPicturePtr, - pictureControlSetPtr->tempModeTypeNeighborArray, - pictureControlSetPtr->tempLeafDepthNeighborArray, - pictureControlSetPtr->tempIntraLumaModeNeighborArray, - pictureControlSetPtr->tempSkipFlagNeighborArray, + pictureControlSetPtr->tempModeTypeNeighborArray[contextPtr->tileIndex], + pictureControlSetPtr->tempLeafDepthNeighborArray[contextPtr->tileIndex], + pictureControlSetPtr->tempIntraLumaModeNeighborArray[contextPtr->tileIndex], + pictureControlSetPtr->tempSkipFlagNeighborArray[contextPtr->tileIndex], 0, 0); diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index b1b7d7bcc..500845c1d 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -211,6 +211,12 @@ EB_ERRORTYPE PictureControlSetCtor( EB_MALLOC(NeighborArrayUnit_t**, objectPtr->intraLumaModeNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); EB_MALLOC(NeighborArrayUnit_t**, objectPtr->skipFlagNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + // For proxy entropy + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->tempModeTypeNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->tempLeafDepthNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->tempIntraLumaModeNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + EB_MALLOC(NeighborArrayUnit_t**, objectPtr->tempSkipFlagNeighborArray, sizeof(NeighborArrayUnit_t*) * totalTileCount, EB_N_PTR); + // Mode Decision Neighbor Arrays EB_U8 depth; EB_U16 array_size = sizeof(NeighborArrayUnit_t*) * totalTileCount; @@ -570,6 +576,56 @@ EB_ERRORTYPE PictureControlSetCtor( if (return_error == EB_ErrorInsufficientResources){ return EB_ErrorInsufficientResources; } + + //Proxy entropy Neighbor Arrays + return_error = NeighborArrayUnitCtor( + &objectPtr->tempModeTypeNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->tempLeafDepthNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } + return_error = NeighborArrayUnitCtor( + &objectPtr->tempSkipFlagNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + CU_NEIGHBOR_ARRAY_GRANULARITY, + CU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } + + return_error = NeighborArrayUnitCtor( + &objectPtr->tempIntraLumaModeNeighborArray[tileIdx], + MAX_PICTURE_WIDTH_SIZE, + MAX_PICTURE_HEIGHT_SIZE, + sizeof(EB_U8), + PU_NEIGHBOR_ARRAY_GRANULARITY, + PU_NEIGHBOR_ARRAY_GRANULARITY, + NEIGHBOR_ARRAY_UNIT_TOP_AND_LEFT_ONLY_MASK); + if (return_error == EB_ErrorInsufficientResources) { + return EB_ErrorInsufficientResources; + } } } //return_error = NeighborArrayUnitCtor( @@ -661,6 +717,7 @@ EB_ERRORTYPE PictureControlSetCtor( EB_CREATEMUTEX(EB_HANDLE, objectPtr->intraMutex, sizeof(EB_HANDLE), EB_MUTEX); objectPtr->encDecCodedLcuCount = 0; + objectPtr->resetProxyFlag = EB_FALSE; return EB_ErrorNone; } diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index bc83d49ba..5fdb3f632 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -264,10 +264,10 @@ typedef struct PictureControlSet_s NeighborArrayUnit_t **intraLumaModeNeighborArray; NeighborArrayUnit_t **skipFlagNeighborArray; - NeighborArrayUnit_t *tempModeTypeNeighborArray; - NeighborArrayUnit_t *tempLeafDepthNeighborArray; - NeighborArrayUnit_t *tempIntraLumaModeNeighborArray; - NeighborArrayUnit_t *tempSkipFlagNeighborArray; + NeighborArrayUnit_t **tempModeTypeNeighborArray; + NeighborArrayUnit_t **tempLeafDepthNeighborArray; + NeighborArrayUnit_t **tempIntraLumaModeNeighborArray; + NeighborArrayUnit_t **tempSkipFlagNeighborArray; EB_REFLIST colocatedPuRefList; EB_BOOL isLowDelay; @@ -301,6 +301,7 @@ typedef struct PictureControlSet_s //Row level vbv data RCStatRow_t **rowStats; EB_BOOL firstRowOfPicture; + EB_BOOL resetProxyFlag; EB_U64 frameSizePlanned; EB_U64 frameSizeEstimated; From e393fd0d33d4ec7763d4ccfcf5adc9b777176baa Mon Sep 17 00:00:00 2001 From: kirithika Date: Thu, 8 Aug 2019 10:29:34 +0530 Subject: [PATCH 71/93] Update Estimatelcu() to that of master Signed-off-by: kirithika --- Source/Lib/Codec/EbEncDecProcess.c | 3 + Source/Lib/Codec/EbEntropyCoding.c | 96 +++++++++++++++----------- Source/Lib/Codec/EbEntropyCoding.h | 1 + Source/Lib/Codec/EbPictureControlSet.h | 4 ++ 4 files changed, 64 insertions(+), 40 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 2f3bcd44d..f0d24fe7f 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -1341,6 +1341,8 @@ static void ResetTempEntropy( entropyCodingQp, pictureControlSetPtr->sliceType); + pictureControlSetPtr->tempprevCodedQp[tileIdx] = pictureControlSetPtr->pictureQp; + pictureControlSetPtr->tempprevQuantGroupCodedQp[tileIdx] = pictureControlSetPtr->pictureQp; EntropyCodingResetTempNeighborArrays(pictureControlSetPtr, tileIdx); } } @@ -4464,6 +4466,7 @@ void* EncDecKernel(void *inputPtr) pictureControlSetPtr->tempLeafDepthNeighborArray[contextPtr->tileIndex], pictureControlSetPtr->tempIntraLumaModeNeighborArray[contextPtr->tileIndex], pictureControlSetPtr->tempSkipFlagNeighborArray[contextPtr->tileIndex], + contextPtr->tileIndex, 0, 0); diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index 14fd197d8..0556b5dc8 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -7345,10 +7345,8 @@ static EB_ERRORTYPE Intra4x4EncodeCoeff( return return_error; } -/********************************************** -* Estimate Lcu -**********************************************/ -EB_ERRORTYPE EstimateLcu( + +EB_ERRORTYPE EstimateLcu ( LargestCodingUnit_t *tbPtr, EB_U32 lcuOriginX, EB_U32 lcuOriginY, @@ -7360,6 +7358,7 @@ EB_ERRORTYPE EstimateLcu( NeighborArrayUnit_t *leafDepthNeighborArray, NeighborArrayUnit_t *intraLumaModeNeighborArray, NeighborArrayUnit_t *skipFlagNeighborArray, + EB_U16 tileIdx, EB_U32 pictureOriginX, EB_U32 pictureOriginY) { @@ -7368,6 +7367,7 @@ EB_ERRORTYPE EstimateLcu( CabacEncodeContext_t *cabacEncodeCtxPtr = (CabacEncodeContext_t*)entropyCoderPtr->cabacEncodeContextPtr; SequenceControlSet_t *sequenceControlSetPtr = (SequenceControlSet_t*)pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr; EncodeContext_t *encodeContextPtr = ((SequenceControlSet_t*)(pictureControlSetPtr->sequenceControlSetWrapperPtr->objectPtr))->encodeContextPtr; + // CU Varaiables const CodedUnitStats_t *cuStatsPtr; CodingUnit_t *cuPtr; @@ -7379,6 +7379,7 @@ EB_ERRORTYPE EstimateLcu( EB_U32 cuSize; EB_U8 cuDepth; EB_BOOL availableCoeff; + cabacEncodeCtxPtr->colorFormat = pictureControlSetPtr->colorFormat; // PU Varaiables PredictionUnit_t *puPtr; @@ -7396,6 +7397,7 @@ EB_ERRORTYPE EstimateLcu( do { EB_BOOL codeCuCond = EB_TRUE; // Code cu only if it is inside the picture cuPtr = tbPtr->codedLeafArrayPtr[cuIndex]; + if (checkCuOutOfBound) codeCuCond = (EB_BOOL)lcuParam->rasterScanCuValidity[MD_SCAN_TO_RASTER_SCAN[cuIndex]]; // check if cu is inside the picture @@ -7426,38 +7428,47 @@ EB_ERRORTYPE EstimateLcu( } if (cuPtr->splitFlag == EB_FALSE) { - if (cuPtr->predictionModeFlag == INTRA_MODE && cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) - + if (cuPtr->predictionModeFlag == INTRA_MODE && + cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) { availableCoeff = ( cuPtr->transformUnitArray[1].lumaCbf || cuPtr->transformUnitArray[2].lumaCbf || cuPtr->transformUnitArray[3].lumaCbf || cuPtr->transformUnitArray[4].lumaCbf || cuPtr->transformUnitArray[1].crCbf || - cuPtr->transformUnitArray[1].cbCbf) ? EB_TRUE : EB_FALSE; - - else + cuPtr->transformUnitArray[1].cbCbf || + cuPtr->transformUnitArray[2].crCbf || + cuPtr->transformUnitArray[2].cbCbf || + cuPtr->transformUnitArray[3].crCbf || + cuPtr->transformUnitArray[3].cbCbf || + cuPtr->transformUnitArray[4].crCbf || // 422 case will use 3rd 4x4 for the 2nd chroma + cuPtr->transformUnitArray[4].cbCbf) ? EB_TRUE : EB_FALSE; + } + else { availableCoeff = (cuPtr->predictionModeFlag == INTER_MODE) ? (EB_BOOL)cuPtr->rootCbf : - (cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].lumaCbf || - cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].crCbf || - cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].cbCbf) ? EB_TRUE : EB_FALSE; - // Estimate function is overwritting the values of pictureControlSetPtr->prevCodedQp which are used in EC - //EntropyCodingUpdateQp( - // cuPtr, - // availableCoeff, - // cuOriginX, - // cuOriginY, - // cuSize, - // sequenceControlSetPtr->lcuSize, - // sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? EB_TRUE : EB_FALSE, - // &entropyDeltaQpNotCoded, - // pictureControlSetPtr->difCuDeltaQpDepth, - // &pictureControlSetPtr->prevCodedQp, - // &pictureControlSetPtr->prevQuantGroupCodedQp, - // tbPtr->qp, - // pictureControlSetPtr, - // pictureOriginX, - // pictureOriginY); + (cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].lumaCbf || + cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].crCbf || + cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].crCbf2 || + cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].cbCbf || + cuPtr->transformUnitArray[cuSize == sequenceControlSetPtr->lcuSize ? 1 : 0].cbCbf2) ? EB_TRUE : EB_FALSE; + } + + EntropyCodingUpdateQp( + cuPtr, + availableCoeff, + cuOriginX, + cuOriginY, + cuSize, + sequenceControlSetPtr->lcuSize, + sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? EB_TRUE : EB_FALSE, + &entropyDeltaQpNotCoded, + pictureControlSetPtr->difCuDeltaQpDepth, + &pictureControlSetPtr->tempprevCodedQp[tileIdx], + &pictureControlSetPtr->tempprevQuantGroupCodedQp[tileIdx], + tbPtr->qp, + pictureControlSetPtr, + pictureOriginX, + pictureOriginY); // Assign DLF QP entropySetQpArrayBasedOnCU( @@ -7469,7 +7480,8 @@ EB_ERRORTYPE EstimateLcu( cuPtr->qp); // Code the skip flag - if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) { + if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) + { EncodeSkipFlag( cabacEncodeCtxPtr, (EB_BOOL)cuPtr->skipFlag, @@ -7479,25 +7491,28 @@ EB_ERRORTYPE EstimateLcu( skipFlagNeighborArray); } - if (cuPtr->skipFlag) { + if (cuPtr->skipFlag) + { // Merge Index EncodeMergeIndex( cabacEncodeCtxPtr, &cuPtr->predictionUnitArray[0]); } - else { + else + { // Code CU pred mode (I, P, B, etc.) // (not needed for Intra Slice) - if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) { + if (pictureControlSetPtr->sliceType == EB_P_PICTURE || pictureControlSetPtr->sliceType == EB_B_PICTURE) + { EncodePredictionMode( cabacEncodeCtxPtr, cuPtr); } - switch (cuPtr->predictionModeFlag) { + switch (cuPtr->predictionModeFlag) { case INTRA_MODE: - if (cuPtr->predictionModeFlag == INTRA_MODE && cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) { - + if (cuPtr->predictionModeFlag == INTRA_MODE && + cuPtr->predictionUnitArray->intraLumaMode == EB_INTRA_MODE_4x4) { // Code Partition Size EncodeIntra4x4PartitionSize( cabacEncodeCtxPtr, @@ -7572,8 +7587,11 @@ EB_ERRORTYPE EstimateLcu( } // Code Chroma Mode for Intra - EncodeIntraChromaMode( - cabacEncodeCtxPtr); + for (partitionIndex = 0; + partitionIndex < ((cabacEncodeCtxPtr->colorFormat == EB_YUV444) ? 4 : 1); + partitionIndex++) { + EncodeIntraChromaMode(cabacEncodeCtxPtr); + } // Encode Transform Unit Split & CBFs Intra4x4EncodeCoeff( @@ -7587,7 +7605,6 @@ EB_ERRORTYPE EstimateLcu( &deltaQpNotCoded); tbPtr->quantizedCoeffsBits += cuQuantizedCoeffsBits; - } else { // Code Partition Size @@ -7660,7 +7677,6 @@ EB_ERRORTYPE EstimateLcu( &deltaQpNotCoded); tbPtr->quantizedCoeffsBits += cuQuantizedCoeffsBits; - } break; diff --git a/Source/Lib/Codec/EbEntropyCoding.h b/Source/Lib/Codec/EbEntropyCoding.h index b29e893f1..5ea515cde 100644 --- a/Source/Lib/Codec/EbEntropyCoding.h +++ b/Source/Lib/Codec/EbEntropyCoding.h @@ -57,6 +57,7 @@ extern EB_ERRORTYPE EstimateLcu( NeighborArrayUnit_t *leafDepthNeighborArray, NeighborArrayUnit_t *intraLumaModeNeighborArray, NeighborArrayUnit_t *skipFlagNeighborArray, + EB_U16 tileIdx, EB_U32 pictureOriginX, EB_U32 pictureOriginY); diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index 5fdb3f632..40aa8ce5e 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -217,6 +217,10 @@ typedef struct PictureControlSet_s EB_U8 prevCodedQp[EB_TILE_MAX_COUNT]; EB_U8 prevQuantGroupCodedQp[EB_TILE_MAX_COUNT]; + // Temp QP Assignment + EB_U8 tempprevCodedQp[EB_TILE_MAX_COUNT]; + EB_U8 tempprevQuantGroupCodedQp[EB_TILE_MAX_COUNT]; + // Enc/DecQP Assignment EB_U8 encPrevCodedQp[EB_TILE_MAX_COUNT][MAX_PICTURE_HEIGHT_SIZE / MAX_LCU_SIZE]; EB_U8 encPrevQuantGroupCodedQp[EB_TILE_MAX_COUNT][MAX_PICTURE_HEIGHT_SIZE / MAX_LCU_SIZE]; From d499b4f9ce4b37cbf8880d9ebf3b8bc10bdca381 Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 9 Aug 2019 20:21:56 +0530 Subject: [PATCH 72/93] Fix the Encoder crash when Frames exceeds the CircularQueue length --- Source/Lib/Codec/EbEncDecProcess.c | 13 +------------ Source/Lib/Codec/EbInitialRateControlProcess.c | 1 + Source/Lib/Codec/EbPictureControlSet.h | 2 ++ Source/Lib/Codec/EbRateControlProcess.c | 14 ++------------ 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index f0d24fe7f..1828fb45d 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3941,25 +3941,14 @@ EB_U64 predictRowsSizeSum(PictureControlSet_t* pictureControlSetPtr, SequenceCon EB_U64 predictedBitsForFrame = 0; EB_U64 predictedBitsDoneSoFar = 0; *encodedBitsSoFar = 0; - EB_U32 queueEntryIndexTemp; - EB_U32 queueEntryIndexHeadTemp; HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp; EB_U8 lcuSizeLog2 = (EB_U8)Log2f(sequenceControlSetPtr->lcuSize); EB_U8 pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; EB_U8 pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; - queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueue[sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); - queueEntryIndexHeadTemp += sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; - queueEntryIndexHeadTemp = (queueEntryIndexHeadTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? - queueEntryIndexHeadTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : - queueEntryIndexHeadTemp; - queueEntryIndexTemp = queueEntryIndexHeadTemp; - - - EB_U32 currentInd = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; - hlRateControlHistogramPtrTemp = (sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueue[currentInd]); for (EB_U8 row = 0; row < pictureHeightInLcu; row++) { + hlRateControlHistogramPtrTemp = (sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueue[pictureControlSetPtr->ParentPcsPtr->hlHistogramQueueIndex]); pictureControlSetPtr->rowStats[row]->predictedBits = 0; for (EB_U8 col = 0; col < pictureControlSetPtr->rowStats[row]->totalCUEncoded; col++) { diff --git a/Source/Lib/Codec/EbInitialRateControlProcess.c b/Source/Lib/Codec/EbInitialRateControlProcess.c index a8a442810..edf2edcc2 100644 --- a/Source/Lib/Codec/EbInitialRateControlProcess.c +++ b/Source/Lib/Codec/EbInitialRateControlProcess.c @@ -812,6 +812,7 @@ void GetHistogramQueueData( histogramQueueEntryIndex = (histogramQueueEntryIndex > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? histogramQueueEntryIndex - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : histogramQueueEntryIndex; + pictureControlSetPtr->hlHistogramQueueIndex = histogramQueueEntryIndex; histogramQueueEntryPtr = encodeContextPtr->hlRateControlHistorgramQueue[histogramQueueEntryIndex]; diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index 40aa8ce5e..4792eade0 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -434,6 +434,8 @@ typedef struct PictureParentControlSet_s EB_U16 lcuTotalCount; EB_BOOL endOfSequenceRegion; EB_BOOL sceneChangeInGop; + EB_S32 hlHistogramQueueIndex; + // used for Look ahead EB_U8 framesInSw; EB_S16 historgramLifeCount; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index b183a65d1..cc784fe1a 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2758,22 +2758,12 @@ void* RateControlKernel(void *inputPtr) pictureControlSetPtr->pictureQp); } } - if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize) - { + if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize && sequenceControlSetPtr->staticConfig.lookAheadDistance > 0) { EbBlockOnMutex(encodeContextPtr->bufferFillMutex); pictureControlSetPtr->qpNoVbv = pictureControlSetPtr->pictureQp; pictureControlSetPtr->bufferFillPerFrame = encodeContextPtr->bufferFill; pictureControlSetPtr->pictureQp = (EB_U8)Vbv_Buf_Calc(pictureControlSetPtr, sequenceControlSetPtr, encodeContextPtr); - queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - encodeContextPtr->hlRateControlHistorgramQueue[encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); - queueEntryIndexHeadTemp += encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; - queueEntryIndexHeadTemp = (queueEntryIndexHeadTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? - queueEntryIndexHeadTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : - queueEntryIndexHeadTemp; - - queueEntryIndexTemp2 = queueEntryIndexHeadTemp; - - EB_U32 currentInd = (queueEntryIndexTemp2 > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp2 - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp2; - hlRateControlHistogramPtrTemp = encodeContextPtr->hlRateControlHistorgramQueue[currentInd]; + hlRateControlHistogramPtrTemp = encodeContextPtr->hlRateControlHistorgramQueue[pictureControlSetPtr->ParentPcsPtr->hlHistogramQueueIndex]; pictureControlSetPtr->frameSizePlanned = predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, pictureControlSetPtr->pictureQp); EbReleaseMutex(encodeContextPtr->bufferFillMutex); } From c710fc5e96753e2bf96ad6638dea008e6e5ea63e Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 9 Aug 2019 20:24:05 +0530 Subject: [PATCH 73/93] Remove reassignment of lcu qp by Entropy process --- Source/Lib/Codec/EbEntropyCodingProcess.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Lib/Codec/EbEntropyCodingProcess.c b/Source/Lib/Codec/EbEntropyCodingProcess.c index 33f167e15..6d66ab7d9 100644 --- a/Source/Lib/Codec/EbEntropyCodingProcess.c +++ b/Source/Lib/Codec/EbEntropyCodingProcess.c @@ -115,7 +115,6 @@ static void EntropyCodingConfigureLcu( LargestCodingUnit_t *lcuPtr, PictureControlSet_t *pictureControlSetPtr) { - contextPtr->qp = pictureControlSetPtr->pictureQp; // Asuming cb and cr offset to be the same for chroma QP in both slice and pps for lambda computation @@ -426,6 +425,8 @@ void* EntropyCodingKernel(void *inputPtr) lastLcuFlagInSlice = lastLcuFlagInTile; } + if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) + contextPtr->qp = lcuPtr->qp; // Configure the LCU EntropyCodingConfigureLcu( contextPtr, From 84979ba3a23e7dc14c2b8272cd1fda54ea6d6c72 Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 9 Aug 2019 20:28:44 +0530 Subject: [PATCH 74/93] Fix reset of Row stats once per picture --- Source/Lib/Codec/EbEncDecProcess.c | 22 +++++++++++++++------- Source/Lib/Codec/EbPictureControlSet.c | 1 - Source/Lib/Codec/EbPictureControlSet.h | 1 - 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 1828fb45d..2b3eaf967 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -1346,6 +1346,19 @@ static void ResetTempEntropy( EntropyCodingResetTempNeighborArrays(pictureControlSetPtr, tileIdx); } } + +static void ResetRowStats( + PictureControlSet_t *pictureControlSetPtr,SequenceControlSet_t *sequenceControlSetPtr) { + EB_U8 lcuSize = (EB_U8)sequenceControlSetPtr->lcuSize; + EB_U8 lcuSizeLog2 = (EB_U8)Log2f(lcuSize); + for (EB_U8 row = 0; row < ((sequenceControlSetPtr->lumaHeight + lcuSize - 1) >> lcuSizeLog2); row++) { + EbBlockOnMutex(pictureControlSetPtr->rowStats[row]->rowUpdateMutex); + pictureControlSetPtr->rowStats[row]->totalCUEncoded = 0; + pictureControlSetPtr->rowStats[row]->encodedBits = 0; + pictureControlSetPtr->rowStats[row]->numEncodedCUs = 0; + EbReleaseMutex(pictureControlSetPtr->rowStats[row]->rowUpdateMutex); + } +} /************************************************** * Reset Coding Loop **************************************************/ @@ -4212,15 +4225,13 @@ void* EncDecKernel(void *inputPtr) pictureControlSetPtr, sequenceControlSetPtr, segmentIndex); - //Reset Temp Entropy Coding + //Reset Stats required for low level vbv if (segmentIndex == 0) { if (contextPtr->tileRowIndex == 0) { - if (pictureControlSetPtr->resetProxyFlag == EB_FALSE) { ResetTempEntropy(contextPtr, pictureControlSetPtr, sequenceControlSetPtr); - pictureControlSetPtr->resetProxyFlag = EB_TRUE; + ResetRowStats(pictureControlSetPtr,sequenceControlSetPtr); } } - } contextPtr->mdContext->CabacCost = pictureControlSetPtr->cabacCost; if (pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr != NULL) { @@ -4236,9 +4247,6 @@ void* EncDecKernel(void *inputPtr) } for (yLcuIndex = yLcuStartIndex, lcuSegmentIndex = lcuStartIndex; lcuSegmentIndex < lcuStartIndex + lcuSegmentCount; ++yLcuIndex) { - pictureControlSetPtr->rowStats[yLcuIndex]->totalCUEncoded = 0; - pictureControlSetPtr->rowStats[yLcuIndex]->encodedBits = 0; - pictureControlSetPtr->rowStats[yLcuIndex]->numEncodedCUs = 0; for (xLcuIndex = xLcuStartIndex; xLcuIndex < tileRowWidthInLcu && (xLcuIndex + yLcuIndex < segmentBandSize) && lcuSegmentIndex < lcuStartIndex + lcuSegmentCount; ++xLcuIndex, ++lcuSegmentIndex) { // LCU per picture-wise diff --git a/Source/Lib/Codec/EbPictureControlSet.c b/Source/Lib/Codec/EbPictureControlSet.c index 500845c1d..f81bd5835 100644 --- a/Source/Lib/Codec/EbPictureControlSet.c +++ b/Source/Lib/Codec/EbPictureControlSet.c @@ -717,7 +717,6 @@ EB_ERRORTYPE PictureControlSetCtor( EB_CREATEMUTEX(EB_HANDLE, objectPtr->intraMutex, sizeof(EB_HANDLE), EB_MUTEX); objectPtr->encDecCodedLcuCount = 0; - objectPtr->resetProxyFlag = EB_FALSE; return EB_ErrorNone; } diff --git a/Source/Lib/Codec/EbPictureControlSet.h b/Source/Lib/Codec/EbPictureControlSet.h index 4792eade0..627992341 100644 --- a/Source/Lib/Codec/EbPictureControlSet.h +++ b/Source/Lib/Codec/EbPictureControlSet.h @@ -305,7 +305,6 @@ typedef struct PictureControlSet_s //Row level vbv data RCStatRow_t **rowStats; EB_BOOL firstRowOfPicture; - EB_BOOL resetProxyFlag; EB_U64 frameSizePlanned; EB_U64 frameSizeEstimated; From b6ae7212f2c73302a2c947693cb5031bebdad4d8 Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 9 Aug 2019 20:32:24 +0530 Subject: [PATCH 75/93] Use Sad Interval Index updated by Motion Estimation Process for LCU bits prediction --- Source/Lib/Codec/EbCodingUnit.c | 2 + Source/Lib/Codec/EbCodingUnit.h | 5 +- Source/Lib/Codec/EbEncDecProcess.c | 78 ++++++++++++++---------------- 3 files changed, 42 insertions(+), 43 deletions(-) diff --git a/Source/Lib/Codec/EbCodingUnit.c b/Source/Lib/Codec/EbCodingUnit.c index d176cb906..22d2c7cde 100644 --- a/Source/Lib/Codec/EbCodingUnit.c +++ b/Source/Lib/Codec/EbCodingUnit.c @@ -89,6 +89,8 @@ EB_ERRORTYPE LargestCodingUnitCtor( largestCodingUnitPtr->index = lcuIndex; largestCodingUnitPtr->proxytotalBits = 0; largestCodingUnitPtr->rowInd = 0; + largestCodingUnitPtr->intraSadInterval = 0; + largestCodingUnitPtr->intraSadInterval = 0; EB_MALLOC(CodingUnit_t**, largestCodingUnitPtr->codedLeafArrayPtr, sizeof(CodingUnit_t*) * CU_MAX_COUNT, EB_N_PTR); for(codedLeafIndex=0; codedLeafIndex < CU_MAX_COUNT; ++codedLeafIndex) { EB_MALLOC(CodingUnit_t*, largestCodingUnitPtr->codedLeafArrayPtr[codedLeafIndex], sizeof(CodingUnit_t) , EB_N_PTR); diff --git a/Source/Lib/Codec/EbCodingUnit.h b/Source/Lib/Codec/EbCodingUnit.h index 54c8b5c23..538fcad60 100644 --- a/Source/Lib/Codec/EbCodingUnit.h +++ b/Source/Lib/Codec/EbCodingUnit.h @@ -187,8 +187,9 @@ typedef struct LargestCodingUnit_s { EB_U32 totalBits; EB_U32 proxytotalBits; EB_U32 rowInd; - EB_U32 intraDistortion; - EB_U32 interDistortion; + EB_U32 intraSadInterval; + EB_U32 interSadInterval; + EB_U8 fullLcu; // Quantized Coefficients EbPictureBufferDesc_t *quantizedCoeff; diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 2b3eaf967..309bc6627 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3851,54 +3851,41 @@ EB_ERRORTYPE SignalDerivationEncDecKernelVmaf( return return_error; } + EB_U32 predBitsPerLcu(PictureControlSet_t* pictureControlSetPtr, EncodeContext_t* encodeContextPtr, LargestCodingUnit_t* lcuPtr, EB_U8 qpVbv) { - EB_U16 sadIntervalIndex = 0; - EB_U16 intraSadIntervalIndex = 0; EB_U32 sadBits; EB_U32 intraSadBits; EB_U32 interSadBits; RateControlTables_t *rateControlTablesPtr; EB_Bit_Number *sadBitsArrayPtr; EB_Bit_Number *intraSadBitsArrayPtr; - intraSadIntervalIndex = (EB_U16)(lcuPtr->intraDistortion >> (12 - SAD_PRECISION_INTERVAL));//change 12 to 2*log2(64) - - intraSadIntervalIndex = (EB_U16)(intraSadIntervalIndex >> 2); - if (intraSadIntervalIndex > (NUMBER_OF_SAD_INTERVALS >> 1) - 1) { - EB_U16 intraSadIntervalIndexTemp = intraSadIntervalIndex - ((NUMBER_OF_SAD_INTERVALS >> 1) - 1); - - intraSadIntervalIndex = ((NUMBER_OF_SAD_INTERVALS >> 1) - 1) + (intraSadIntervalIndexTemp >> 3); - } - if (pictureControlSetPtr->sliceType != EB_I_PICTURE) - { - sadIntervalIndex = (EB_U16)(lcuPtr->interDistortion >> (12 - SAD_PRECISION_INTERVAL));//change 12 to 2*log2(64) - - sadIntervalIndex = (EB_U16)(sadIntervalIndex >> 2); - if (sadIntervalIndex > (NUMBER_OF_SAD_INTERVALS >> 1) - 1) { - EB_U16 sadIntervalIndexTemp = sadIntervalIndex - ((NUMBER_OF_SAD_INTERVALS >> 1) - 1); - sadIntervalIndex = ((NUMBER_OF_SAD_INTERVALS >> 1) - 1) + (sadIntervalIndexTemp >> 3); - - } - } rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qpVbv]; sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[pictureControlSetPtr->temporalLayerIndex]; intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; - if (pictureControlSetPtr->sliceType == EB_I_PICTURE) - { - intraSadBits = intraSadBitsArrayPtr[intraSadIntervalIndex]; - interSadBits = 0; - sadBits = intraSadBits; + if (pictureControlSetPtr->sliceType == EB_I_PICTURE) { + if (lcuPtr->fullLcu) { + intraSadBits = intraSadBitsArrayPtr[lcuPtr->intraSadInterval]; + interSadBits = 0; + sadBits = intraSadBits; + } + else + return 0; } else - { - intraSadBits = intraSadBitsArrayPtr[intraSadIntervalIndex]; - interSadBits = sadBitsArrayPtr[intraSadIntervalIndex]; - sadBits = interSadBits; - if (interSadBits > (intraSadBits * 3)) - sadBits = intraSadBits; + { + if (lcuPtr->fullLcu) { + intraSadBits = intraSadBitsArrayPtr[lcuPtr->intraSadInterval]; + interSadBits = sadBitsArrayPtr[lcuPtr->interSadInterval]; + sadBits = interSadBits; + if (interSadBits > (intraSadBits * 3)) + sadBits = intraSadBits; } + else + return 0; + } return sadBits; } @@ -4108,6 +4095,8 @@ void* EncDecKernel(void *inputPtr) EB_U32 tempWrittenBitsAfterQuantizedCoeff; EB_U32 bestOisCuIndex = 0; EB_U8 baseQp; + EB_U32 lcuWidth; + EB_U32 lcuHeight; for (;;) { @@ -4471,17 +4460,24 @@ void* EncDecKernel(void *inputPtr) 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); lcuPtr->proxytotalBits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; + lcuWidth = (sequenceControlSetPtr->lumaWidth - lcuOriginX) < MAX_LCU_SIZE ? sequenceControlSetPtr->lumaWidth - lcuOriginX : MAX_LCU_SIZE; + lcuHeight = (sequenceControlSetPtr->lumaHeight - lcuOriginY) < MAX_LCU_SIZE ? sequenceControlSetPtr->lumaHeight - lcuOriginY : MAX_LCU_SIZE; + lcuPtr->fullLcu = 0; + if (pictureControlSetPtr->sliceType == EB_I_PICTURE) { + if (lcuWidth == MAX_LCU_SIZE && lcuHeight == MAX_LCU_SIZE) { + lcuPtr->intraSadInterval = pictureControlSetPtr->ParentPcsPtr->intraSadIntervalIndex[lcuIndex]; + lcuPtr->fullLcu = 1; + } + } + else { + if (lcuWidth == MAX_LCU_SIZE && lcuHeight == MAX_LCU_SIZE) { + lcuPtr->intraSadInterval = pictureControlSetPtr->ParentPcsPtr->intraSadIntervalIndex[lcuIndex]; + lcuPtr->interSadInterval = pictureControlSetPtr->ParentPcsPtr->interSadIntervalIndex[lcuIndex]; + lcuPtr->fullLcu = 1; + } + } //Update CU Stats for row level vbv control EbBlockOnMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); - lcuPtr->intraDistortion = pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[1][bestOisCuIndex].distortion + - - pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[2][bestOisCuIndex].distortion + - - pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[3][bestOisCuIndex].distortion + - - pictureControlSetPtr->ParentPcsPtr->oisCu32Cu16Results[lcuIndex]->sortedOisCandidate[4][bestOisCuIndex].distortion; - if (pictureControlSetPtr->sliceType != EB_I_PICTURE) - lcuPtr->interDistortion = pictureControlSetPtr->ParentPcsPtr->rcMEdistortion[lcuIndex]; pictureControlSetPtr->rowStats[yLcuIndex]->encodedBits += lcuPtr->proxytotalBits; pictureControlSetPtr->rowStats[yLcuIndex]->totalCUEncoded++; pictureControlSetPtr->rowStats[yLcuIndex]->numEncodedCUs = lcuPtr->index; From ce88bc4cd6f76a75dcf729c92af49debe7e8116b Mon Sep 17 00:00:00 2001 From: kirithika Date: Fri, 9 Aug 2019 20:36:18 +0530 Subject: [PATCH 76/93] Clean up --- Source/Lib/Codec/EbEncDecProcess.c | 55 ++++++++++++++---------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 309bc6627..4bd0c90cc 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3891,7 +3891,7 @@ EB_U32 predBitsPerLcu(PictureControlSet_t* pictureControlSetPtr, EncodeContext_t EB_U64 predictBitsDup(SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp,EB_U32 qp) { - EB_U64 totalBits = 0; + EB_U64 totalBits = 0; RateControlTables_t *rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qp]; EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[hlRateControlHistogramPtrTemp->temporalLayerIndex]; EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; @@ -3941,24 +3941,23 @@ EB_U64 predictRowsSizeSum(PictureControlSet_t* pictureControlSetPtr, SequenceCon EB_U64 predictedBitsForFrame = 0; EB_U64 predictedBitsDoneSoFar = 0; *encodedBitsSoFar = 0; + EB_U64 framesizeEstimated = 0; HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp; EB_U8 lcuSizeLog2 = (EB_U8)Log2f(sequenceControlSetPtr->lcuSize); EB_U8 pictureWidthInLcu = (sequenceControlSetPtr->lumaWidth + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; EB_U8 pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; - for (EB_U8 row = 0; row < pictureHeightInLcu; row++) - { hlRateControlHistogramPtrTemp = (sequenceControlSetPtr->encodeContextPtr->hlRateControlHistorgramQueue[pictureControlSetPtr->ParentPcsPtr->hlHistogramQueueIndex]); + for (EB_U8 row = 0; row < pictureHeightInLcu; row++) { pictureControlSetPtr->rowStats[row]->predictedBits = 0; - for (EB_U8 col = 0; col < pictureControlSetPtr->rowStats[row]->totalCUEncoded; col++) - { + for (EB_U8 col = 0; col < pictureControlSetPtr->rowStats[row]->totalCUEncoded; col++) { pictureControlSetPtr->rowStats[row]->predictedBits += predBitsPerLcu(pictureControlSetPtr, sequenceControlSetPtr->encodeContextPtr,pictureControlSetPtr->lcuPtrArray[row*pictureWidthInLcu+col],qpVbv); } *encodedBitsSoFar += pictureControlSetPtr->rowStats[row]->encodedBits; predictedBitsDoneSoFar += pictureControlSetPtr->rowStats[row]->predictedBits; } predictedBitsForFrame = predictBitsDup(sequenceControlSetPtr, sequenceControlSetPtr->encodeContextPtr, hlRateControlHistogramPtrTemp, qpVbv); - EB_U64 framesizeEstimated = (predictedBitsForFrame - predictedBitsDoneSoFar) + (*encodedBitsSoFar); + framesizeEstimated = (predictedBitsForFrame - predictedBitsDoneSoFar) + (*encodedBitsSoFar); return framesizeEstimated; } @@ -3970,25 +3969,23 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,//mutex to { /* tweak quality based on difference from predicted size */ EB_U8 prevRowQp= qpVbv; - EB_U8 qpAbsoluteMax = sequenceControlSetPtr->staticConfig.maxQpAllowed; - EB_U8 qpAbsoluteMin = sequenceControlSetPtr->staticConfig.minQpAllowed; EB_U8 lcuSizeLog2 = (EB_U8)Log2f(sequenceControlSetPtr->lcuSize); EB_U8 pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; - + EB_U8 qpAbsoluteMax = sequenceControlSetPtr->staticConfig.maxQpAllowed; + EB_U8 qpAbsoluteMin = sequenceControlSetPtr->staticConfig.minQpAllowed; EB_U8 qpMax = MIN(prevRowQp + 4, qpAbsoluteMax); EB_U8 qpMin = MAX(prevRowQp - 4, qpAbsoluteMin); EB_U64 bufferLeftPlanned = pictureControlSetPtr->bufferFillPerFrame - pictureControlSetPtr->frameSizePlanned; - if (rowPtr->rowIndexrowIndexframeSizePlanned)) qpMax = qpAbsoluteMax = prevRowQp; @@ -3997,27 +3994,27 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,//mutex to qpMin = MAX(qpMin, pictureControlSetPtr->qpNoVbv); + //Increase the Qp when the current frame size exceeds the estimated frame size while (qpVbv < qpMax && (((accFrameBits > pictureControlSetPtr->frameSizePlanned + rcTol) || (pictureControlSetPtr->bufferFillPerFrame - accFrameBits < (EB_U64)(bufferLeftPlanned * 0.5)) || (accFrameBits > pictureControlSetPtr->frameSizePlanned && qpVbv < pictureControlSetPtr->qpNoVbv) - ))) - { - qpVbv += 1; - encodedBitsSoFar = 0; - accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); - } + ))) { + qpVbv += 1; + encodedBitsSoFar = 0; + accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); + } + //Decrease the Qp when the current frame size is lower that that of the estimated frame size while (qpVbv > qpMin && (qpVbv > pictureControlSetPtr->rowStats[0]->rowQp ) && (((accFrameBits < (EB_U64)(pictureControlSetPtr->frameSizePlanned * 0.8f) && qpVbv <= prevRowQp) - || accFrameBits < (EB_U64)((pictureControlSetPtr->bufferFillPerFrame - rcData->vbvBufsize + rcData->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16)) * 1.1)) - )) - { - qpVbv -= 1; - encodedBitsSoFar = 0; - accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); - } + || (EB_S64)accFrameBits < (EB_S64)((EB_S64)(pictureControlSetPtr->bufferFillPerFrame - (rcData->vbvBufsize + (rcData->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16)))) * 1.1)) + )) { + qpVbv -= 1; + encodedBitsSoFar = 0; + accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); + } pictureControlSetPtr->frameSizeEstimated = accFrameBits; From b07731fd63da75e9d82b88546d955a7b88d6457c Mon Sep 17 00:00:00 2001 From: kirithika Date: Sat, 10 Aug 2019 12:58:06 +0530 Subject: [PATCH 77/93] Revert vbv-end and vbv end frame adjust option --- Docs/svt-hevc_encoder_user_guide.md | 2 - Source/API/EbApi.h | 10 +-- Source/App/EbAppConfig.c | 20 ++---- Source/App/EbAppConfig.h | 3 +- Source/App/EbAppContext.c | 2 - Source/Lib/Codec/EbEncHandle.c | 11 +--- Source/Lib/Codec/EbEncodeContext.c | 1 - Source/Lib/Codec/EbEncodeContext.h | 1 - Source/Lib/Codec/EbRateControlProcess.c | 84 ++++++++----------------- 9 files changed, 35 insertions(+), 99 deletions(-) diff --git a/Docs/svt-hevc_encoder_user_guide.md b/Docs/svt-hevc_encoder_user_guide.md index 544427a51..9ce547516 100644 --- a/Docs/svt-hevc_encoder_user_guide.md +++ b/Docs/svt-hevc_encoder_user_guide.md @@ -301,8 +301,6 @@ The encoder parameters present in the Sample.cfg file are listed in this table b | **vbvMaxrate** | -vbv-maxrate | Any Number | 0 | VBVMaxrate in bits / second. Only used when RateControlMode is set to 1 | | **vbvBufsize** | -vbv-bufsize | Any Number | 0 | VBV BufferSize in bits / second. Only used when RateControlMode is set to 1 | | **vbvBufInit** | -vbv-init | [0 - 100] | 90 | Sets how full the VBV buffer to be| -| **vbvBufEnd** | -vbv-end | [0 - 100] | 0 | Sets how VBV Buffer ends| -| **vbvEndFrameAdjust** | -vbv-end-fr-adj | [0 - 100] | 0 | Sets vbvEndFrameAdjust| | **hrdFlag** | -hrd | [0,1] | 0 | HRD Flag, 0 = OFF, 1 = ON |When hrdFlag is set to 1 it requires vbvMaxrate and vbvBufsize to be greater than 0 | | **MaxQpAllowed** | -max-qp | [0 - 51] | 48 | Maximum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be >= MinQpAllowed | | **MinQpAllowed** | -min-qp | [0 - 50] | 10 | Minimum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be < MaxQpAllowed | diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index 9698e9e40..84957224f 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -529,14 +529,6 @@ typedef struct EB_H265_ENC_CONFIGURATION * * Default is 90. */ uint64_t vbvBufInit; - /* Sets how full the VBV buffer must be end. - * - * Default is 0. */ - uint64_t vbvBufEnd; - /* Sets vbvEndFrameAdjust. - * - * Default is 0. */ - uint64_t vbvEndFrameAdjust; /* Enables the buffering period SEI and picture timing SEI to signal the HRD * parameters. * @@ -545,7 +537,7 @@ typedef struct EB_H265_ENC_CONFIGURATION * * Default is 0. */ uint32_t hrdFlag; - + /* ID assigned to each channel when multiple instances are running within the * same application. */ uint32_t channelId; diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index c1af7392b..1f2311fdc 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -88,8 +88,6 @@ #define VBV_MAX_RATE_TOKEN "-vbv-maxrate" #define VBV_BUFFER_SIZE_TOKEN "-vbv-bufsize" #define VBV_BUFFER_INIT_TOKEN "-vbv-init" -#define VBV_BUFFER_END_TOKEN "-vbv-end" -#define VBV_END_FRAME_ADJUST_TOKEN "-vbv-end-fr-adj" #define ENABLE_LOW_LEVEL_VBV_TOKEN "-low-level-vbv" #define HRD_TOKEN "-hrd" #define MAX_QP_TOKEN "-max-qp" @@ -223,13 +221,11 @@ static void SetEnableConstrainedIntra (const char *value, EbConfig_t * static void SetCfgTune (const char *value, EbConfig_t *cfg) {cfg->tune = (uint8_t)strtoul(value, NULL, 0); }; static void SetBitRateReduction (const char *value, EbConfig_t *cfg) {cfg->bitRateReduction = (EB_BOOL)strtol(value, NULL, 0); }; static void SetImproveSharpness (const char *value, EbConfig_t *cfg) {cfg->improveSharpness = (EB_BOOL)strtol(value, NULL, 0);}; -static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0); }; -static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0); }; -static void SetVbvBufInit (const char *value, EbConfig_t *cfg) { cfg->vbvBufInit = strtoul(value, NULL, 0); }; -static void SetVbvEndFrameAdjust (const char *value, EbConfig_t *cfg) { cfg->vbvEndFrameAdjust = strtoul(value, NULL, 0); }; -static void SetVbvBufEnd (const char *value, EbConfig_t *cfg) { cfg->vbvBufEnd = strtoul(value, NULL, 0); }; -static void SetLowLevelVbv (const char *value, EbConfig_t *cfg) { cfg->lowLevelVbv = (EB_BOOL)strtol(value, NULL, 0); }; -static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0); }; +static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0); }; +static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0); }; +static void SetVbvBufInit (const char *value, EbConfig_t *cfg) { cfg->vbvBufInit = strtoul(value, NULL, 0); }; +static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0); }; +static void SetLowLevelVbv(const char *value, EbConfig_t *cfg) { cfg->lowLevelVbv = (EB_BOOL)strtol(value, NULL, 0); }; static void SetVideoUsabilityInfo (const char *value, EbConfig_t *cfg) {cfg->videoUsabilityInfo = strtol(value, NULL, 0);}; static void SetHighDynamicRangeInput (const char *value, EbConfig_t *cfg) {cfg->highDynamicRangeInput = strtol(value, NULL, 0);}; static void SetAccessUnitDelimiter (const char *value, EbConfig_t *cfg) {cfg->accessUnitDelimiter = strtol(value, NULL, 0);}; @@ -366,11 +362,9 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, VBV_BUFFER_SIZE_TOKEN, "vbvBufsize", SetVbvBufsize }, { SINGLE_INPUT, HRD_TOKEN, "hrd", SetHrdFlag }, { SINGLE_INPUT, VBV_BUFFER_INIT_TOKEN, "vbvBufInit", SetVbvBufInit}, - { SINGLE_INPUT, VBV_BUFFER_END_TOKEN, "vbvBufEnd", SetVbvBufEnd}, - { SINGLE_INPUT, VBV_END_FRAME_ADJUST_TOKEN, "vbvEndFrameAdjustToken", SetVbvEndFrameAdjust}, - { SINGLE_INPUT, ENABLE_LOW_LEVEL_VBV_TOKEN,"lowLevelVbv",SetLowLevelVbv}, + // Deblock Filter { SINGLE_INPUT, LOOP_FILTER_DISABLE_TOKEN, "LoopFilterDisable", SetDisableDlfFlag }, @@ -476,8 +470,6 @@ void EbConfigCtor(EbConfig_t *configPtr) configPtr->vbvMaxRate = 0; configPtr->vbvBufsize = 0; configPtr->vbvBufInit = 90; - configPtr->vbvBufEnd = 0; - configPtr->vbvEndFrameAdjust = 0; configPtr->hrdFlag = 0; configPtr->lowLevelVbv = 0; configPtr->intraPeriod = -2; diff --git a/Source/App/EbAppConfig.h b/Source/App/EbAppConfig.h index 7a479891b..cdd126935 100644 --- a/Source/App/EbAppConfig.h +++ b/Source/App/EbAppConfig.h @@ -329,8 +329,7 @@ typedef struct EbConfig_s uint32_t vbvMaxRate; uint32_t vbvBufsize; uint64_t vbvBufInit; - uint64_t vbvBufEnd; - uint64_t vbvEndFrameAdjust; + /**************************************** * TUNE ****************************************/ diff --git a/Source/App/EbAppContext.c b/Source/App/EbAppContext.c index 7097cf2a4..36f022ca2 100644 --- a/Source/App/EbAppContext.c +++ b/Source/App/EbAppContext.c @@ -185,8 +185,6 @@ EB_ERRORTYPE CopyConfigurationParameters( callbackData->ebEncParameters.vbvMaxrate = config->vbvMaxRate; callbackData->ebEncParameters.vbvBufsize = config->vbvBufsize; callbackData->ebEncParameters.vbvBufInit = config->vbvBufInit; - callbackData->ebEncParameters.vbvBufEnd = config->vbvBufEnd; - callbackData->ebEncParameters.vbvEndFrameAdjust = config->vbvEndFrameAdjust; callbackData->ebEncParameters.lowLevelVbv = config->lowLevelVbv; callbackData->ebEncParameters.useQpFile = (EB_BOOL)config->useQpFile; callbackData->ebEncParameters.tileColumnCount = (EB_BOOL)config->tileColumnCount; diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index 1b16d13a7..813fe7851 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2117,9 +2117,8 @@ void CopyApiFromApp( sequenceControlSetPtr->staticConfig.vbvMaxrate = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvMaxrate; sequenceControlSetPtr->staticConfig.vbvBufsize = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufsize; sequenceControlSetPtr->staticConfig.vbvBufInit = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufInit; - sequenceControlSetPtr->staticConfig.vbvBufEnd = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvBufEnd; - sequenceControlSetPtr->staticConfig.vbvEndFrameAdjust = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->vbvEndFrameAdjust; sequenceControlSetPtr->staticConfig.lowLevelVbv= ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->lowLevelVbv; + sequenceControlSetPtr->staticConfig.lookAheadDistance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->lookAheadDistance; sequenceControlSetPtr->staticConfig.framesToBeEncoded = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->framesToBeEncoded; @@ -2826,14 +2825,6 @@ static EB_ERRORTYPE VerifySettings(\ SVT_LOG("SVT [Error]: Instance %u: Invalid vbvBufInit [0 - 100]\n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->vbvBufEnd > 100) { - SVT_LOG("SVT [Error]: Instance %u: Invalid vbvBufEnd [0 - 100]\n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } - if (config->vbvEndFrameAdjust > 100) { - SVT_LOG("SVT [Error]: Instance %u: Invalid vbvEndFrameAdjust [0 - 100]\n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } if (config->lowLevelVbv > 1) { SVT_LOG("SVT [Error]: Instance %u : Invalid lowLevelVbv flag [0 - 1]\n", channelNumber + 1); diff --git a/Source/Lib/Codec/EbEncodeContext.c b/Source/Lib/Codec/EbEncodeContext.c index 66734358e..0ed5be04d 100644 --- a/Source/Lib/Codec/EbEncodeContext.c +++ b/Source/Lib/Codec/EbEncodeContext.c @@ -210,7 +210,6 @@ EB_ERRORTYPE EncodeContextCtor( encodeContextPtr->vbvBufsize = 0; encodeContextPtr->vbvMaxrate = 0; encodeContextPtr->fillerBitError = 0; - encodeContextPtr->vbvEndEmptiness = 0; // Rate Control Bit Tables EB_MALLOC(RateControlTables_t*, encodeContextPtr->rateControlTablesArray, sizeof(RateControlTables_t) * TOTAL_NUMBER_OF_INITIAL_RC_TABLES_ENTRY, EB_N_PTR); diff --git a/Source/Lib/Codec/EbEncodeContext.h b/Source/Lib/Codec/EbEncodeContext.h index d347e1261..c56556c4e 100644 --- a/Source/Lib/Codec/EbEncodeContext.h +++ b/Source/Lib/Codec/EbEncodeContext.h @@ -147,7 +147,6 @@ typedef struct EncodeContext_s EB_U32 vbvMaxrate; EB_U32 vbvBufsize; EB_U64 bufferFill; - EB_U64 vbvEndEmptiness; EB_S64 fillerBitError; EB_HANDLE fillerBitMutex; EB_HANDLE bufferFillMutex; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index cc784fe1a..d266424c9 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2391,63 +2391,32 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet queueEntryIndexTemp++; } - if (encodeContextPtr->vbvEndEmptiness && pictureControlSetPtr->ParentPcsPtr->decodeOrder >= (EB_U32)((sequenceControlSetPtr->staticConfig.vbvEndFrameAdjust / 100.0) * sequenceControlSetPtr->staticConfig.framesToBeEncoded)) - { - EB_BOOL loopBreak = EB_FALSE; - double bufferDiff = (encodeContextPtr->vbvEndEmptiness / 100.0) - ((double)encodeContextPtr->bufferFill / (double)encodeContextPtr->vbvBufsize); - targetFill = encodeContextPtr->bufferFill + encodeContextPtr->vbvBufsize * (bufferDiff / (sequenceControlSetPtr->staticConfig.framesToBeEncoded - pictureControlSetPtr->ParentPcsPtr->decodeOrder)); - if (bufferFillCur < targetFill) - { - q++; - q = CLIP3( - sequenceControlSetPtr->staticConfig.minQpAllowed, - sequenceControlSetPtr->staticConfig.maxQpAllowed, - q); - loopTerminate |= 1; - loopBreak = EB_TRUE; - } - if (bufferFillCur > ((encodeContextPtr->vbvEndEmptiness / 100.0) * encodeContextPtr->vbvBufsize)) - { - q--; - q = CLIP3( - sequenceControlSetPtr->staticConfig.minQpAllowed, - sequenceControlSetPtr->staticConfig.maxQpAllowed, - q); - loopTerminate |= 2; - loopBreak = EB_TRUE; - } - if (!loopBreak) - break; - } - - else { - targetFill = MIN(encodeContextPtr->bufferFill + totalDuration * encodeContextPtr->vbvMaxrate * 0.5, encodeContextPtr->vbvBufsize * (1 - 0.5)); - if (bufferFillCur < targetFill) - { - q++; - q = CLIP3( - sequenceControlSetPtr->staticConfig.minQpAllowed, - sequenceControlSetPtr->staticConfig.maxQpAllowed, - q); - loopTerminate |= 1; - continue; - } - - targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.05), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); - if ((bitrateFlag) && (bufferFillCur > targetFill)) - { - q--; - q = CLIP3( - sequenceControlSetPtr->staticConfig.minQpAllowed, - sequenceControlSetPtr->staticConfig.maxQpAllowed, - q); - loopTerminate |= 2; - continue; - } - break; - } - } - q = MAX(q0 / 2, q); + targetFill = MIN(encodeContextPtr->bufferFill + totalDuration * encodeContextPtr->vbvMaxrate * 0.5, encodeContextPtr->vbvBufsize * (1 - 0.5)); + if (bufferFillCur < targetFill) + { + q++; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 1; + continue; + } + + targetFill = CLIP3(encodeContextPtr->vbvBufsize * (1 - 0.05), encodeContextPtr->vbvBufsize, encodeContextPtr->bufferFill - totalDuration * encodeContextPtr->vbvMaxrate * 0.5); + if ((bitrateFlag) && (bufferFillCur > targetFill)) + { + q--; + q = CLIP3( + sequenceControlSetPtr->staticConfig.minQpAllowed, + sequenceControlSetPtr->staticConfig.maxQpAllowed, + q); + loopTerminate |= 2; + continue; + } + break; + } + q = MAX(q0 / 2, q); return (EB_U8)q; } @@ -2523,7 +2492,6 @@ void* RateControlKernel(void *inputPtr) contextPtr->highLevelRateControlPtr->channelBitRatePerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerFrame * (sequenceControlSetPtr->staticConfig.lookAheadDistance + 1); contextPtr->highLevelRateControlPtr->bitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; encodeContextPtr->bufferFill = (EB_U64)(sequenceControlSetPtr->staticConfig.vbvBufsize * sequenceControlSetPtr->staticConfig.vbvBufInit / 100); - encodeContextPtr->vbvEndEmptiness = sequenceControlSetPtr->staticConfig.vbvBufEnd; #if RC_UPDATE_TARGET_RATE contextPtr->highLevelRateControlPtr->previousUpdatedBitConstraintPerSw = contextPtr->highLevelRateControlPtr->channelBitRatePerSw; #endif From ef9c29a441ee627f90b82b24d3fa17c41b00d374 Mon Sep 17 00:00:00 2001 From: kirithika Date: Sat, 10 Aug 2019 15:59:01 +0530 Subject: [PATCH 78/93] Cleanup unneccesary file changes Signed-off-by: kirithika --- Config/Sample.cfg | 3 +- Source/API/EbApi.h | 1 - Source/App/EbAppConfig.c | 14 +--- Source/Lib/Codec/EbCodingUnit.c | 8 +- Source/Lib/Codec/EbCodingUnit.h | 2 +- Source/Lib/Codec/EbEncDecProcess.c | 1 - Source/Lib/Codec/EbEncHandle.c | 65 ++------------- Source/Lib/Codec/EbEntropyCoding.c | 26 +++--- Source/Lib/Codec/EbIntraPrediction.c | 82 ++++++++++++++----- Source/Lib/Codec/EbPacketizationProcess.c | 29 ++++--- Source/Lib/Codec/EbRateControlProcess.c | 29 +++---- .../Lib/Codec/EbResourceCoordinationProcess.c | 2 +- ...EbSampleAdaptiveOffsetGenerationDecision.c | 2 - Source/Lib/Codec/EbSequenceControlSet.c | 1 + Source/Lib/Codec/EbSequenceControlSet.h | 1 + ...hevc-add-libsvt-hevc-encoder-wrapper.patch | 8 +- 16 files changed, 121 insertions(+), 153 deletions(-) diff --git a/Config/Sample.cfg b/Config/Sample.cfg index bf8222ac1..974754be1 100644 --- a/Config/Sample.cfg +++ b/Config/Sample.cfg @@ -88,8 +88,7 @@ MinQpAllowed : 10 # minimum allowed QP whe LookAheadDistance : 17 # Enable Look Ahead [0-250] SceneChangeDetection : 1 # Enable Scene Change Detection (0: OFF, 1: ON) ConstantRateFactor : 28 # CRF value allowed for rate control use - [0-51] -# ====================== Tune =============================== -Tune : 1 # Tune (0=SQ - visually optimized mode, 1=OQ - PSNR / SSIM optimized mode, 2=VMAF - VMAF optimized mode) + # ====================== Adaptive QP Params =============================== BitRateReduction : 1 # BitRate Reduction (only applicable when Tune is set to 0) (0= OFF, 1=ON ) diff --git a/Source/API/EbApi.h b/Source/API/EbApi.h index 45bc24a93..865ebde6f 100644 --- a/Source/API/EbApi.h +++ b/Source/API/EbApi.h @@ -569,7 +569,6 @@ typedef struct EB_H265_ENC_CONFIGURATION * Default is -1. */ int32_t targetSocket; - /* Flag to enable threads to real time priority. Running with sudo privilege * utilizes full resource. Only applicable to Linux. * diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index fb97b5b1d..667857016 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -334,6 +334,7 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, FRAME_RATE_TOKEN, "FrameRate", SetFrameRate }, { SINGLE_INPUT, FRAME_RATE_NUMERATOR_TOKEN, "FrameRateNumerator", SetFrameRateNumerator }, { SINGLE_INPUT, FRAME_RATE_DENOMINATOR_TOKEN, "FrameRateDenominator", SetFrameRateDenominator }, + // Coding Structure { SINGLE_INPUT, HIERARCHICAL_LEVELS_TOKEN, "HierarchicalLevels", SetHierarchicalLevels }, { SINGLE_INPUT, BASE_LAYER_SWITCH_MODE_TOKEN, "BaseLayerSwitchMode", SetBaseLayerSwitchMode }, @@ -347,19 +348,6 @@ config_entry_t config_entry[] = { { SINGLE_INPUT, VBV_BUFFER_SIZE_TOKEN, "vbvBufsize", SetVbvBufsize }, { SINGLE_INPUT, HRD_TOKEN, "hrd", SetHrdFlag }, { SINGLE_INPUT, VBV_BUFFER_INIT_TOKEN, "vbvBufInit", SetVbvBufInit}, - - - - { SINGLE_INPUT, USE_QP_FILE_TOKEN, "UseQpFile", SetCfgUseQpFile }, - { SINGLE_INPUT, RATE_CONTROL_ENABLE_TOKEN, "RateControlMode", SetRateControlMode }, - { SINGLE_INPUT, LOOK_AHEAD_DIST_TOKEN, "LookAheadDistance", SetLookAheadDistance}, - { SINGLE_INPUT, TARGET_BIT_RATE_TOKEN, "TargetBitRate", SetTargetBitRate }, - { SINGLE_INPUT, MAX_QP_TOKEN, "MaxQpAllowed", SetMaxQpAllowed }, - { SINGLE_INPUT, MIN_QP_TOKEN, "MinQpAllowed", SetMinQpAllowed }, - { SINGLE_INPUT, VBV_MAX_RATE_TOKEN, "vbvMaxRate", SetVbvMaxrate }, - { SINGLE_INPUT, VBV_BUFFER_SIZE_TOKEN, "vbvBufsize", SetVbvBufsize }, - { SINGLE_INPUT, HRD_TOKEN, "hrd", SetHrdFlag }, - { SINGLE_INPUT, VBV_BUFFER_INIT_TOKEN, "vbvBufInit", SetVbvBufInit}, { SINGLE_INPUT, ENABLE_LOW_LEVEL_VBV_TOKEN,"lowLevelVbv",SetLowLevelVbv}, diff --git a/Source/Lib/Codec/EbCodingUnit.c b/Source/Lib/Codec/EbCodingUnit.c index 22d2c7cde..e54859ff1 100644 --- a/Source/Lib/Codec/EbCodingUnit.c +++ b/Source/Lib/Codec/EbCodingUnit.c @@ -87,10 +87,10 @@ EB_ERRORTYPE LargestCodingUnitCtor( largestCodingUnitPtr->originY = lcuOriginY; largestCodingUnitPtr->index = lcuIndex; - largestCodingUnitPtr->proxytotalBits = 0; - largestCodingUnitPtr->rowInd = 0; - largestCodingUnitPtr->intraSadInterval = 0; - largestCodingUnitPtr->intraSadInterval = 0; + largestCodingUnitPtr->proxytotalBits = 0; + largestCodingUnitPtr->rowInd = 0; + largestCodingUnitPtr->intraSadInterval = 0; + largestCodingUnitPtr->intraSadInterval = 0; EB_MALLOC(CodingUnit_t**, largestCodingUnitPtr->codedLeafArrayPtr, sizeof(CodingUnit_t*) * CU_MAX_COUNT, EB_N_PTR); for(codedLeafIndex=0; codedLeafIndex < CU_MAX_COUNT; ++codedLeafIndex) { EB_MALLOC(CodingUnit_t*, largestCodingUnitPtr->codedLeafArrayPtr[codedLeafIndex], sizeof(CodingUnit_t) , EB_N_PTR); diff --git a/Source/Lib/Codec/EbCodingUnit.h b/Source/Lib/Codec/EbCodingUnit.h index 538fcad60..b6272b35b 100644 --- a/Source/Lib/Codec/EbCodingUnit.h +++ b/Source/Lib/Codec/EbCodingUnit.h @@ -214,7 +214,7 @@ typedef struct LargestCodingUnit_s { /************************************** - * Row level vbv + * Low level vbv **************************************/ typedef struct RCStatRow_s { diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 6d8ea05c8..f9f58c7aa 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -4105,7 +4105,6 @@ void* EncDecKernel(void *inputPtr) EbPictureBufferDesc_t * tempCoeffPicturePtr; EB_U32 tempWrittenBitsBeforeQuantizedCoeff; EB_U32 tempWrittenBitsAfterQuantizedCoeff; - EB_U32 bestOisCuIndex = 0; EB_U8 baseQp; EB_U32 lcuWidth; EB_U32 lcuHeight; diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index 12409909c..6ef5d10cd 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2100,54 +2100,7 @@ void CopyApiFromApp( } sequenceControlSetPtr->chromaFormatIdc = (EB_U32)(sequenceControlSetPtr->staticConfig.encoderColorFormat); sequenceControlSetPtr->enableTmvpSps = sequenceControlSetPtr->staticConfig.unrestrictedMotionVector; - sequenceControlSetPtr->staticConfig.compressedTenBitFormat = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->compressedTenBitFormat; - sequenceControlSetPtr->staticConfig.videoUsabilityInfo = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->videoUsabilityInfo; - sequenceControlSetPtr->staticConfig.highDynamicRangeInput = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->highDynamicRangeInput; - sequenceControlSetPtr->staticConfig.accessUnitDelimiter = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->accessUnitDelimiter; - sequenceControlSetPtr->staticConfig.bufferingPeriodSEI = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->bufferingPeriodSEI; - sequenceControlSetPtr->staticConfig.pictureTimingSEI = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->pictureTimingSEI; - sequenceControlSetPtr->staticConfig.registeredUserDataSeiFlag = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->registeredUserDataSeiFlag; - sequenceControlSetPtr->staticConfig.unregisteredUserDataSeiFlag = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->unregisteredUserDataSeiFlag; - sequenceControlSetPtr->staticConfig.recoveryPointSeiFlag = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->recoveryPointSeiFlag; - sequenceControlSetPtr->staticConfig.enableTemporalId = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->enableTemporalId; - - // Annex A parameters - sequenceControlSetPtr->staticConfig.profile = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->profile; - sequenceControlSetPtr->staticConfig.tier = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->tier; - sequenceControlSetPtr->staticConfig.level = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->level; - - sequenceControlSetPtr->staticConfig.injectorFrameRate = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->injectorFrameRate; - sequenceControlSetPtr->staticConfig.speedControlFlag = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->speedControlFlag; - //sequenceControlSetPtr->staticConfig.latencyMode = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->latencyMode; - sequenceControlSetPtr->staticConfig.latencyMode = 0; - sequenceControlSetPtr->staticConfig.asmType = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->asmType; - sequenceControlSetPtr->staticConfig.channelId = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->channelId; - sequenceControlSetPtr->staticConfig.activeChannelCount = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->activeChannelCount; - sequenceControlSetPtr->staticConfig.logicalProcessors = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->logicalProcessors; - sequenceControlSetPtr->staticConfig.targetSocket = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->targetSocket; - sequenceControlSetPtr->staticConfig.frameRateDenominator = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->frameRateDenominator; - sequenceControlSetPtr->staticConfig.frameRateNumerator = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->frameRateNumerator; - sequenceControlSetPtr->staticConfig.reconEnabled = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->reconEnabled; - sequenceControlSetPtr->staticConfig.hrdFlag = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->hrdFlag; - sequenceControlSetPtr->staticConfig.unrestrictedMotionVector = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->unrestrictedMotionVector; - sequenceControlSetPtr->enableTmvpSps = sequenceControlSetPtr->staticConfig.unrestrictedMotionVector; - sequenceControlSetPtr->staticConfig.maxCLL = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxCLL; - sequenceControlSetPtr->staticConfig.maxFALL = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxFALL; - - sequenceControlSetPtr->staticConfig.useMasteringDisplayColorVolume = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->useMasteringDisplayColorVolume; - sequenceControlSetPtr->staticConfig.useNaluFile = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->useNaluFile; - sequenceControlSetPtr->staticConfig.displayPrimaryX[0] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryX[0]; - sequenceControlSetPtr->staticConfig.displayPrimaryX[1] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryX[1]; - sequenceControlSetPtr->staticConfig.displayPrimaryX[2] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryX[2]; - sequenceControlSetPtr->staticConfig.displayPrimaryY[0] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryY[0]; - sequenceControlSetPtr->staticConfig.displayPrimaryY[1] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryY[1]; - sequenceControlSetPtr->staticConfig.displayPrimaryY[2] = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->displayPrimaryY[2]; - sequenceControlSetPtr->staticConfig.whitePointX = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->whitePointX; - sequenceControlSetPtr->staticConfig.whitePointY = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->whitePointY; - sequenceControlSetPtr->staticConfig.maxDisplayMasteringLuminance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->maxDisplayMasteringLuminance; - sequenceControlSetPtr->staticConfig.minDisplayMasteringLuminance = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->minDisplayMasteringLuminance; - sequenceControlSetPtr->staticConfig.dolbyVisionProfile = ((EB_H265_ENC_CONFIGURATION*)pComponentParameterStructure)->dolbyVisionProfile; - + // Copying to masteringDisplayColorVolume structure sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryX[0] = sequenceControlSetPtr->staticConfig.displayPrimaryX[0]; sequenceControlSetPtr->masteringDisplayColorVolume.displayPrimaryX[1] = sequenceControlSetPtr->staticConfig.displayPrimaryX[1]; @@ -2194,6 +2147,7 @@ void CopyApiFromApp( sequenceControlSetPtr->videoUsabilityInfoPtr->hrdParametersPtr->nalHrdParametersPresentFlag = 1; sequenceControlSetPtr->videoUsabilityInfoPtr->hrdParametersPtr->cpbDpbDelaysPresentFlag = 1; } + return; } @@ -2564,16 +2518,15 @@ static EB_ERRORTYPE VerifySettings(\ SVT_LOG("SVT [Error]: Instance %u: The constrained intra must be [0 - 1] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->rateControlMode > 2) { - SVT_LOG("SVT [Error]: Instance %u: The rate control mode must be [0 - 2] \n", channelNumber + 1); + if (config->rateControlMode > 2) { + SVT_LOG("SVT [Error]: Instance %u: The rate control mode must be [0 - 2] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - - if (config->rateControlMode > 2) { - SVT_LOG("SVT [Error]: Instance %u: The rate control mode must be [0 - 2] \n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } - + if ((config->rateControlMode == 2) && (config->crf > 51)) + { + SVT_LOG("SVT [Error]: Instance %u:The crf value must be [0-51] \n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } if (config->lookAheadDistance > 250 && config->lookAheadDistance != (EB_U32)~0) { SVT_LOG("SVT [Error]: Instance %u: The lookahead distance must be [0 - 250] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index 677889513..bbf73ae7b 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -5473,34 +5473,34 @@ static void CodeVPS( WriteFlagCavlc( bitstreamPtr, scsPtr->staticConfig.fpsInVps == 1 ? EB_TRUE : EB_FALSE); - if (scsPtr->staticConfig.fpsInVps == 1) { + if (scsPtr->staticConfig.fpsInVps == 1) { if (scsPtr->staticConfig.frameRateDenominator != 0 && scsPtr->staticConfig.frameRateNumerator != 0) { // vps_num_units_in_tick WriteCodeCavlc( - bitstreamPtr, - scsPtr->staticConfig.frameRateNumerator, - 32); + bitstreamPtr, + scsPtr->staticConfig.frameRateDenominator, + 32); // vps_time_scale WriteCodeCavlc( - bitstreamPtr, - scsPtr->staticConfig.frameRateDenominator, - 32); + bitstreamPtr, + scsPtr->staticConfig.frameRateNumerator, + 32); } else { // vps_num_units_in_tick WriteCodeCavlc( - bitstreamPtr, - scsPtr->frameRate > 1000 ? scsPtr->frameRate : scsPtr->frameRate << 16, - 32); + bitstreamPtr, + 1 << 16, + 32); // vps_time_scale WriteCodeCavlc( - bitstreamPtr, - 1 << 16, - 32); + bitstreamPtr, + scsPtr->frameRate > 1000 ? scsPtr->frameRate : scsPtr->frameRate << 16, + 32); } // vps_poc_proportional_to_timing_flag diff --git a/Source/Lib/Codec/EbIntraPrediction.c b/Source/Lib/Codec/EbIntraPrediction.c index 5eeb89ac7..d0d9c2979 100644 --- a/Source/Lib/Codec/EbIntraPrediction.c +++ b/Source/Lib/Codec/EbIntraPrediction.c @@ -3574,6 +3574,7 @@ static inline void IntraModeAngular16bit_all( switch(mode){ case 34: + IntraAng34_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, refSamples, @@ -3709,6 +3710,7 @@ EB_ERRORTYPE IntraPredictionCl( switch(funcIndex) { case 0: + yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -3722,6 +3724,7 @@ EB_ERRORTYPE IntraPredictionCl( break; case 1: + yIntraReferenceArray = contextPtr->yIntraReferenceArrayReverse; IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( @@ -3734,7 +3737,8 @@ EB_ERRORTYPE IntraPredictionCl( break; case 2: - yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : + + yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -3747,6 +3751,7 @@ EB_ERRORTYPE IntraPredictionCl( break; case 3: + yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -3760,6 +3765,7 @@ EB_ERRORTYPE IntraPredictionCl( break; case 4: + yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArray : contextPtr->yIntraReferenceArray; yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : @@ -3809,6 +3815,7 @@ EB_ERRORTYPE IntraPredictionCl( switch(funcIndex) { case 0: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( @@ -3832,6 +3839,7 @@ EB_ERRORTYPE IntraPredictionCl( break; case 2: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraVerticalChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -3855,6 +3863,7 @@ EB_ERRORTYPE IntraPredictionCl( break; case 3: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraHorzChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -3878,6 +3887,7 @@ EB_ERRORTYPE IntraPredictionCl( break; case 1: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraDCChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -3901,6 +3911,7 @@ EB_ERRORTYPE IntraPredictionCl( break; case 4: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraModeAngular_all( @@ -4004,6 +4015,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( switch(funcIndex) { case 0: + yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -4017,6 +4029,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 1: + yIntraReferenceArray = contextPtr->yIntraReferenceArrayReverse; IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( @@ -4029,6 +4042,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 2: + yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -4042,6 +4056,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 3: + yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : contextPtr->yIntraReferenceArrayReverse; @@ -4055,6 +4070,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 4: + yIntraReferenceArray = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArray : contextPtr->yIntraReferenceArray; yIntraReferenceArrayReverse = (diffMode > intraLumaFilterTable[Log2f(puWidth)-2])? contextPtr->yIntraFilteredReferenceArrayReverse : @@ -4095,7 +4111,8 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( switch(funcIndex) { case 0: - // Cb Intra Prediction + + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( chromaPuSize, @@ -4118,6 +4135,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 2: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraVerticalChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -4141,6 +4159,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 3: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraHorzChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -4164,6 +4183,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 1: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraDCChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -4187,6 +4207,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionCl( break; case 4: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraModeAngular_all( @@ -4261,6 +4282,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( switch(funcIndex) { case 0: + IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -4271,6 +4293,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( break; case 1: + IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -4281,6 +4304,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( break; case 2: + IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -4291,7 +4315,8 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( break; case 3: - IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( + + IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, &(candidateBufferPtr->predictionPtr->bufferY[puOriginIndex]), @@ -4301,6 +4326,7 @@ EB_ERRORTYPE Intra4x4IntraPredictionOl( break; case 4: + IntraModeAngular_all( openLoopIntraCandidateIndex, puSize, @@ -4381,7 +4407,7 @@ EB_ERRORTYPE EncodePassIntraPrediction( switch(lumaMode) { case EB_INTRA_PLANAR: - IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( + IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, yIntraReferenceArrayReverse, predictionPtr->bufferY + lumaOffset, @@ -4484,7 +4510,6 @@ EB_ERRORTYPE EncodePassIntraPrediction( switch(chromaModeAdj) { case EB_INTRA_PLANAR: // Cb Intra Prediction - IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puChromaSize, cbIntraReferenceArrayReverse, @@ -4503,8 +4528,8 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; case EB_INTRA_VERTICAL: + // Cb Intra Prediction - IntraVerticalChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, cbIntraReferenceArrayReverse, @@ -4513,7 +4538,6 @@ EB_ERRORTYPE EncodePassIntraPrediction( EB_FALSE); // Cr Intra Prediction - IntraVerticalChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, crIntraReferenceArrayReverse, @@ -4524,8 +4548,8 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; case EB_INTRA_HORIZONTAL: - // Cb Intra Prediction - + + // Cb Intra Prediction IntraHorzChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, cbIntraReferenceArrayReverse, @@ -4535,7 +4559,6 @@ EB_ERRORTYPE EncodePassIntraPrediction( // Cr Intra Prediction - IntraHorzChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, crIntraReferenceArrayReverse, @@ -4546,8 +4569,8 @@ EB_ERRORTYPE EncodePassIntraPrediction( break; case EB_INTRA_DC: + // Cb Intra Prediction - IntraDCChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, cbIntraReferenceArrayReverse, @@ -4557,7 +4580,6 @@ EB_ERRORTYPE EncodePassIntraPrediction( // Cr Intra Prediction - IntraDCChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, crIntraReferenceArrayReverse, @@ -4666,7 +4688,7 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( switch(lumaMode) { case EB_INTRA_PLANAR: - IntraPlanar_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( + IntraPlanar_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, yIntraReferenceArrayReverse, (EB_U16*)predictionPtr->bufferY + lumaOffset, @@ -4772,7 +4794,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( predictionPtr->strideCb, EB_FALSE); - IntraPlanar_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, crIntraReferenceArrayReverse, @@ -4789,7 +4810,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( predictionPtr->strideCb, EB_FALSE); - IntraVerticalChroma_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, crIntraReferenceArrayReverse, @@ -4806,7 +4826,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( predictionPtr->strideCb, EB_FALSE); - IntraHorzChroma_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, crIntraReferenceArrayReverse, @@ -4823,7 +4842,6 @@ EB_ERRORTYPE EncodePassIntraPrediction16bit( predictionPtr->strideCb, EB_FALSE); - IntraDCChroma_16bit_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puChromaSize, crIntraReferenceArrayReverse, @@ -5296,7 +5314,8 @@ EB_ERRORTYPE IntraPredictionOpenLoop( switch(funcIndex) { case 0: - IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( + + IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( cuSize, contextPtr->intraRefPtr->yIntraReferenceArrayReverse, (&(contextPtr->meContextPtr->lcuBuffer[0])), @@ -5306,7 +5325,8 @@ EB_ERRORTYPE IntraPredictionOpenLoop( break; case 1: - IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( + + IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( cuSize, contextPtr->intraRefPtr->yIntraReferenceArrayReverse, (&(contextPtr->meContextPtr->lcuBuffer[0])), @@ -5316,7 +5336,8 @@ EB_ERRORTYPE IntraPredictionOpenLoop( break; case 2: - IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( + + IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( cuSize, contextPtr->intraRefPtr->yIntraReferenceArrayReverse, (&(contextPtr->meContextPtr->lcuBuffer[0])), @@ -5326,7 +5347,8 @@ EB_ERRORTYPE IntraPredictionOpenLoop( break; case 3: - IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( + + IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( cuSize, contextPtr->intraRefPtr->yIntraReferenceArrayReverse, (&(contextPtr->meContextPtr->lcuBuffer[0])), @@ -5336,7 +5358,8 @@ EB_ERRORTYPE IntraPredictionOpenLoop( break; case 4: - IntraModeAngular_all( + + IntraModeAngular_all( openLoopIntraCandidateIndex, cuSize, contextPtr->intraRefPtr->yIntraReferenceArray, @@ -5409,6 +5432,7 @@ EB_ERRORTYPE IntraPredictionOl( switch (funcIndex) { case 0: + IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5419,6 +5443,7 @@ EB_ERRORTYPE IntraPredictionOl( break; case 1: + IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5429,6 +5454,7 @@ EB_ERRORTYPE IntraPredictionOl( break; case 2: + IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5439,6 +5465,7 @@ EB_ERRORTYPE IntraPredictionOl( break; case 3: + IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5449,6 +5476,7 @@ EB_ERRORTYPE IntraPredictionOl( break; case 4: + IntraModeAngular_all( openLoopIntraCandidateIndex, puSize, @@ -5495,6 +5523,7 @@ EB_ERRORTYPE IntraPredictionOl( switch (funcIndex) { case 0: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( @@ -5518,6 +5547,7 @@ EB_ERRORTYPE IntraPredictionOl( break; case 2: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraVerticalChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -5541,6 +5571,7 @@ EB_ERRORTYPE IntraPredictionOl( break; case 3: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraHorzChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -5564,6 +5595,7 @@ EB_ERRORTYPE IntraPredictionOl( break; case 1: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraDCChroma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( @@ -5587,6 +5619,7 @@ EB_ERRORTYPE IntraPredictionOl( break; case 4: + // Cb Intra Prediction if (componentMask & PICTURE_BUFFER_DESC_Cb_FLAG) { IntraModeAngular_all( @@ -5677,6 +5710,7 @@ EB_ERRORTYPE IntraPredOnSrc( switch (funcIndex) { case 0: + IntraPlanar_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5687,6 +5721,7 @@ EB_ERRORTYPE IntraPredOnSrc( break; case 1: + IntraDCLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5697,6 +5732,7 @@ EB_ERRORTYPE IntraPredOnSrc( break; case 2: + IntraVerticalLuma_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5707,6 +5743,7 @@ EB_ERRORTYPE IntraPredOnSrc( break; case 3: + IntraHorzLuma_funcPtrArray[!!(ASM_TYPES & PREAVX2_MASK)]( puSize, intraRefPtr->yIntraReferenceArrayReverse, @@ -5717,6 +5754,7 @@ EB_ERRORTYPE IntraPredOnSrc( break; case 4: + IntraModeAngular_all( openLoopIntraCandidateIndex, puSize, diff --git a/Source/Lib/Codec/EbPacketizationProcess.c b/Source/Lib/Codec/EbPacketizationProcess.c index e5a30a9ac..bfc1058ad 100644 --- a/Source/Lib/Codec/EbPacketizationProcess.c +++ b/Source/Lib/Codec/EbPacketizationProcess.c @@ -287,22 +287,19 @@ void* PacketizationKernel(void *inputPtr) contextPtr->ppsConfig); } + if (sequenceControlSetPtr->staticConfig.maxCLL || sequenceControlSetPtr->staticConfig.maxFALL) { + sequenceControlSetPtr->contentLightLevel.maxContentLightLevel = sequenceControlSetPtr->staticConfig.maxCLL; + sequenceControlSetPtr->contentLightLevel.maxPicAverageLightLevel = sequenceControlSetPtr->staticConfig.maxFALL; + EncodeContentLightLevelSEI( + pictureControlSetPtr->bitstreamPtr, + &sequenceControlSetPtr->contentLightLevel); + } - if (sequenceControlSetPtr->staticConfig.maxCLL || sequenceControlSetPtr->staticConfig.maxFALL) { - sequenceControlSetPtr->contentLightLevel.maxContentLightLevel = sequenceControlSetPtr->staticConfig.maxCLL; - sequenceControlSetPtr->contentLightLevel.maxPicAverageLightLevel = sequenceControlSetPtr->staticConfig.maxFALL; - EncodeContentLightLevelSEI( - pictureControlSetPtr->bitstreamPtr, - &sequenceControlSetPtr->contentLightLevel); - } - - if (sequenceControlSetPtr->staticConfig.useMasteringDisplayColorVolume) { - EncodeMasteringDisplayColorVolumeSEI( - pictureControlSetPtr->bitstreamPtr, - &sequenceControlSetPtr->masteringDisplayColorVolume); - } - - + if (sequenceControlSetPtr->staticConfig.useMasteringDisplayColorVolume) { + EncodeMasteringDisplayColorVolumeSEI( + pictureControlSetPtr->bitstreamPtr, + &sequenceControlSetPtr->masteringDisplayColorVolume); + } if (sequenceControlSetPtr->staticConfig.hrdFlag == 1) { @@ -706,7 +703,9 @@ void* PacketizationKernel(void *inputPtr) queueEntryPtr->fillerBitsSent = filler; } } + } + // Send the number of bytes per frame to RC pictureControlSetPtr->ParentPcsPtr->totalNumBits = outputStreamPtr->nFilledLen << 3; diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 135ae351e..9d5282d3d 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -412,7 +412,6 @@ void HighLevelRcInputPictureMode2( EB_Bit_Number *sadBitsArrayPtr; EB_Bit_Number *intraSadBitsArrayPtr; EB_U32 predBitsRefQp; - EB_U32 bestQp = 0; for (temporalLayerIndex = 0; temporalLayerIndex< EB_MAX_TEMPORAL_LAYERS; temporalLayerIndex++){ pictureControlSetPtr->bitsPerSwPerLayer[temporalLayerIndex] = 0; @@ -542,7 +541,6 @@ void HighLevelRcInputPictureMode2( // Store the predBitsRefQp for the first frame in the window to PCS pictureControlSetPtr->predBitsRefQp[refQpIndexTemp] = hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp]; - bestQp = refQpIndexTemp; } } @@ -612,7 +610,7 @@ void HighLevelRcInputPictureMode2( endOfSequenceFlag = EB_FALSE; while (!endOfSequenceFlag && - queueEntryIndexTemp <= queueEntryIndexHeadTemp + sequenceControlSetPtr->staticConfig.lookAheadDistance){ //iterate through lookahead number of frames + queueEntryIndexTemp <= queueEntryIndexHeadTemp + sequenceControlSetPtr->staticConfig.lookAheadDistance){ queueEntryIndexTemp2 = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; hlRateControlHistogramPtrTemp = (encodeContextPtr->hlRateControlHistorgramQueue[queueEntryIndexTemp2]); @@ -699,7 +697,6 @@ void HighLevelRcInputPictureMode2( } else{ bestQpFound = EB_TRUE; - bestQp = refQpIndex; } if (refQpTableIndex == previousSelectedRefQp){ @@ -713,7 +710,6 @@ void HighLevelRcInputPictureMode2( refQpTableIndex = (EB_U32)(refQpTableIndex + qpStep); } - bestQp = previousSelectedRefQp; } #if RC_UPDATE_TARGET_RATE @@ -732,7 +728,8 @@ void HighLevelRcInputPictureMode2( if (highLevelRateControlPtr->predBitsRefQpPerSw[refQpIndex] == 0){ - /* Finding the predicted bits for each frame in the sliding window at the reference Qp(s) */ + // Finding the predicted bits for each frame in the sliding window at the reference Qp(s) + //queueEntryIndexTemp = encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; queueEntryIndexHeadTemp = (EB_S32)(pictureControlSetPtr->pictureNumber - encodeContextPtr->hlRateControlHistorgramQueue[encodeContextPtr->hlRateControlHistorgramQueueHeadIndex]->pictureNumber); queueEntryIndexHeadTemp += encodeContextPtr->hlRateControlHistorgramQueueHeadIndex; queueEntryIndexHeadTemp = (queueEntryIndexHeadTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? @@ -741,10 +738,11 @@ void HighLevelRcInputPictureMode2( queueEntryIndexTemp = queueEntryIndexHeadTemp; - /* This is set to false, so the last frame would go inside the loop */ + // This is set to false, so the last frame would go inside the loop endOfSequenceFlag = EB_FALSE; while (!endOfSequenceFlag && + //queueEntryIndexTemp <= encodeContextPtr->hlRateControlHistorgramQueueHeadIndex+sequenceControlSetPtr->staticConfig.lookAheadDistance){ queueEntryIndexTemp <= queueEntryIndexHeadTemp + sequenceControlSetPtr->staticConfig.lookAheadDistance){ queueEntryIndexTemp2 = (queueEntryIndexTemp > HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH - 1) ? queueEntryIndexTemp - HIGH_LEVEL_RATE_CONTROL_HISTOGRAM_QUEUE_MAX_DEPTH : queueEntryIndexTemp; @@ -808,13 +806,14 @@ void HighLevelRcInputPictureMode2( hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp] += predBitsRefQp; } - /* Scale for in complete - predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries */ + // Scale for in complete + // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp] = hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp] * (EB_U64)areaInPixel / (numOfFullLcus << 12); } highLevelRateControlPtr->predBitsRefQpPerSw[refQpIndex] += hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp]; - /* Store the predBitsRefQp for the first frame in the window to PCS */ + // Store the predBitsRefQp for the first frame in the window to PCS + // if(encodeContextPtr->hlRateControlHistorgramQueueHeadIndex == queueEntryIndexTemp2) if (queueEntryIndexHeadTemp == queueEntryIndexTemp2) pictureControlSetPtr->predBitsRefQp[refQpIndexTemp] = hlRateControlHistogramPtrTemp->predBitsRefQp[refQpIndexTemp]; @@ -931,10 +930,7 @@ void HighLevelRcInputPictureMode2( } } #endif - //pictureControlSetPtr->targetBitsBestPredQp = pictureControlSetPtr->predBitsRefQp[pictureControlSetPtr->bestPredQp]; - - //contextPtr->bufferFill -= pictureControlSetPtr->targetBitsBestPredQp; - //highLevelRateControlPtr->bufferFill -= pictureControlSetPtr->targetBitsBestPredQp; + pictureControlSetPtr->targetBitsBestPredQp = pictureControlSetPtr->predBitsRefQp[pictureControlSetPtr->bestPredQp]; //if (pictureControlSetPtr->sliceType == 2) // { //SVT_LOG("\nTID: %d\t", pictureControlSetPtr->temporalLayerIndex); @@ -2370,7 +2366,7 @@ EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet double fps = 1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION); double totalDuration = fps; queueEntryIndexTemp = currentInd; - queueEntryIndexTemp++; + /* Loop over the planned future frames. */ for (EB_S32 j = 0; bufferFillCur >= 0; j++) { @@ -2455,11 +2451,8 @@ void* RateControlKernel(void *inputPtr) EB_U32 bestOisCuIndex = 0; RATE_CONTROL_TASKTYPES taskType; - EB_U32 queueEntryIndexTemp2; - EB_U32 queueEntryIndexHeadTemp; HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp; - for (;;) { // Get RateControl Task diff --git a/Source/Lib/Codec/EbResourceCoordinationProcess.c b/Source/Lib/Codec/EbResourceCoordinationProcess.c index 2d51cac18..b5a5b37f3 100644 --- a/Source/Lib/Codec/EbResourceCoordinationProcess.c +++ b/Source/Lib/Codec/EbResourceCoordinationProcess.c @@ -777,7 +777,7 @@ void* ResourceCoordinationKernel(void *inputPtr) // Rate Control // Set the ME Distortion and OIS Historgrams to zero - if (sequenceControlSetPtr->staticConfig.rateControlMode==1){ + if (sequenceControlSetPtr->staticConfig.rateControlMode == 1){ EB_MEMSET(pictureControlSetPtr->meDistortionHistogram, 0, NUMBER_OF_SAD_INTERVALS*sizeof(EB_U16)); EB_MEMSET(pictureControlSetPtr->oisDistortionHistogram, 0, NUMBER_OF_INTRA_SAD_INTERVALS*sizeof(EB_U16)); } diff --git a/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c b/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c index 18de7acbc..f08660987 100644 --- a/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c +++ b/Source/Lib/Codec/EbSampleAdaptiveOffsetGenerationDecision.c @@ -711,7 +711,6 @@ EB_ERRORTYPE SaoGenerationDecision( saoStats->eoCount[0]); // U - SaoGatherFunctionTableLossy[!!(ASM_TYPES & PREAVX2_MASK)]( &(inputPicturePtr->bufferCb[(((inputPicturePtr->originY + tbOriginY) * inputPicturePtr->strideCb) >> subHeightCMinus1) + ((inputPicturePtr->originX + tbOriginX) >> subWidthCMinus1)]), inputPicturePtr->strideCb, @@ -725,7 +724,6 @@ EB_ERRORTYPE SaoGenerationDecision( saoStats->eoCount[1]); // V - SaoGatherFunctionTableLossy[!!(ASM_TYPES & PREAVX2_MASK)]( &(inputPicturePtr->bufferCr[(((inputPicturePtr->originY + tbOriginY) * inputPicturePtr->strideCr) >> subHeightCMinus1) + ((inputPicturePtr->originX + tbOriginX) >> subWidthCMinus1)]), inputPicturePtr->strideCr, diff --git a/Source/Lib/Codec/EbSequenceControlSet.c b/Source/Lib/Codec/EbSequenceControlSet.c index 663a296c4..a888ff443 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.c +++ b/Source/Lib/Codec/EbSequenceControlSet.c @@ -113,6 +113,7 @@ EB_ERRORTYPE EbSequenceControlSetCtor( sequenceControlSetPtr->enableStrongIntraSmoothing = EB_TRUE; // Rate Control + sequenceControlSetPtr->rateControlMode = 0; sequenceControlSetPtr->targetBitrate = 0x1000; sequenceControlSetPtr->availableBandwidth = 0x1000; diff --git a/Source/Lib/Codec/EbSequenceControlSet.h b/Source/Lib/Codec/EbSequenceControlSet.h index 22cdcad88..4a001ecbd 100644 --- a/Source/Lib/Codec/EbSequenceControlSet.h +++ b/Source/Lib/Codec/EbSequenceControlSet.h @@ -104,6 +104,7 @@ typedef struct SequenceControlSet_s EB_U32 generalFrameOnlyConstraintFlag; // Rate Control + EB_U32 rateControlMode; EB_U32 targetBitrate; EB_U32 availableBandwidth; diff --git a/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch b/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch index 897218ca4..7c2b45e70 100644 --- a/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch +++ b/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch @@ -47,7 +47,7 @@ index a9644e2..2f61c82 100755 enabled libssh && require_pkg_config libssh libssh libssh/sftp.h sftp_init enabled libspeex && require_pkg_config libspeex speex speex/speex.h speex_decoder_init enabled libsrt && require_pkg_config libsrt "srt >= 1.3.0" srt/srt.h srt_socket -+enabled libsvthevc && require_pkg_config libsvthevc SvtHevcEnc svt-hevc/EbApi.h EbInitHandle ++enabled libsvthevc && require_pkg_config libsvthevc SvtHevcEnc EbApi.h EbInitHandle enabled libtensorflow && require libtensorflow tensorflow/c/c_api.h TF_Version -ltensorflow enabled libtesseract && require_pkg_config libtesseract tesseract tesseract/capi.h TessBaseAPICreate enabled libtheora && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg @@ -103,9 +103,9 @@ index 0000000..d9ac04c +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + -+#include "svt-hevc/EbErrorCodes.h" -+#include "svt-hevc/EbTime.h" -+#include "svt-hevc/EbApi.h" ++#include "EbErrorCodes.h" ++#include "EbTime.h" ++#include "EbApi.h" + +#include "libavutil/common.h" +#include "libavutil/frame.h" From 72a99e98052a9332bad2edc232063bed2ee0ed19 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 12 Aug 2019 13:49:01 +0530 Subject: [PATCH 79/93] Cleanup the usage of a udeclared variable --- Source/Lib/Codec/EbRateControlProcess.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 9d5282d3d..c7074cd77 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -561,7 +561,6 @@ void HighLevelRcInputPictureMode2( highLevelRateControlPtr->predBitsRefQpPerSw[refQpTableIndex] = 0; } - bestQp = qpSearchMin; bitConstraintPerSw = highLevelRateControlPtr->bitConstraintPerSw * pictureControlSetPtr->framesInSw / (sequenceControlSetPtr->staticConfig.lookAheadDistance + 1); // Update the target rate for the sliding window based on the status of RC From c16092ce456d041f53c00c1adc9f10fdedc0f5ea Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 12 Aug 2019 13:55:10 +0530 Subject: [PATCH 80/93] Enable Low level VBV only if frame level VBV is enabled and remove redudant checks --- Config/Sample.cfg | 2 +- Docs/svt-hevc_encoder_user_guide.md | 2 +- Source/Lib/Codec/EbCodingLoop.c | 14 +++++++------- Source/Lib/Codec/EbEncDecProcess.c | 22 ++++++++++------------ Source/Lib/Codec/EbEncHandle.c | 6 ++++++ Source/Lib/Codec/EbEntropyCoding.c | 8 ++++---- Source/Lib/Codec/EbEntropyCodingProcess.c | 2 +- Source/Lib/Codec/EbPictureManagerProcess.c | 4 ++-- 8 files changed, 32 insertions(+), 28 deletions(-) diff --git a/Config/Sample.cfg b/Config/Sample.cfg index 974754be1..fe0fc99a3 100644 --- a/Config/Sample.cfg +++ b/Config/Sample.cfg @@ -82,7 +82,7 @@ vbvMaxrate : 0 # VBV MaxRate (in bits p vbvBufsize : 0 # VBV Bufsize (in bits per second) vbvBufInit : 90 # Sets how full the VBV buffer to be - [0 - 100] hrdFlag : 0 # hrdflag (When hrdFlag is set to 1 it requires vbvMaxrate and vbvBufsize to be greater than 0)(0: disable , 1: enable) -lowLevelVbv : 0 # Enables loeLevelVBV Algorithm (0: OFF, 1: ON) +lowLevelVbv : 0 # Enables lowLevelVBV Algorithm (When lowLevelVbv is set to 1 it requires vbvMaxrate and vbvBufsize to be greater than 0)(0: OFF, 1: ON) MaxQpAllowed : 48 # maximum allowed QP when rate control is on - [0-51] MinQpAllowed : 10 # minimum allowed QP when rate control is on - [0-51] LookAheadDistance : 17 # Enable Look Ahead [0-250] diff --git a/Docs/svt-hevc_encoder_user_guide.md b/Docs/svt-hevc_encoder_user_guide.md index 7c7e38e49..c1ce3bf41 100644 --- a/Docs/svt-hevc_encoder_user_guide.md +++ b/Docs/svt-hevc_encoder_user_guide.md @@ -296,7 +296,7 @@ The encoder parameters present in the Sample.cfg file are listed in this table b | **vbvBufsize** | -vbv-bufsize | Any Number | 0 | VBV BufferSize in bits / second. Only used when RateControlMode is set to 1 | | **vbvBufInit** | -vbv-init | [0 - 100] | 90 | Sets how full the VBV buffer to be| | **hrdFlag** | -hrd | [0,1] | 0 | HRD Flag, 0 = OFF, 1 = ON |When hrdFlag is set to 1 it requires vbvMaxrate and vbvBufsize to be greater than 0 | -| **lowLevelVbv** | -low-level-vbv | [0,1] | 0 | Enable lowLevelVBV algorithm. 0 = OFF, 1 = ON | +| **lowLevelVbv** | -low-level-vbv | [0,1] | 0 | Enable lowLevelVBV algorithm. it requires vbvMaxrate and vbvBufsize to be greater than 0 0 = OFF, 1 = ON | | **MaxQpAllowed** | -max-qp | [0 - 51] | 48 | Maximum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be >= MinQpAllowed | | **MinQpAllowed** | -min-qp | [0 - 50] | 10 | Minimum QP value allowed for rate control use. Only used when RateControlMode is set to 1. Has to be < MaxQpAllowed | | **LookAheadDistance** | -lad | [0 - 250] | Depending on BRC mode | When RateControlMode is set to 1 it's best to set this parameter to be equal to the Intra period value (such is the default set by the encoder), When CQP is chosen, then a (2 \* minigopsize +1) look ahead is recommended. | diff --git a/Source/Lib/Codec/EbCodingLoop.c b/Source/Lib/Codec/EbCodingLoop.c index ce410176e..918a67cf2 100644 --- a/Source/Lib/Codec/EbCodingLoop.c +++ b/Source/Lib/Codec/EbCodingLoop.c @@ -3116,7 +3116,7 @@ EB_EXTERN void EncodePass( } } } - if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) { + if (sequenceControlSetPtr->staticConfig.lowLevelVbv) { contextPtr->skipQpmFlag = EB_TRUE; } else @@ -3148,11 +3148,11 @@ EB_EXTERN void EncodePass( } - EB_BOOL useDeltaQp = (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction ||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)); + EB_BOOL useDeltaQp = (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction ||( sequenceControlSetPtr->staticConfig.lowLevelVbv)); EB_BOOL singleSegment = (sequenceControlSetPtr->encDecSegmentColCountArray[pictureControlSetPtr->temporalLayerIndex] == 1) && (sequenceControlSetPtr->encDecSegmentRowCountArray[pictureControlSetPtr->temporalLayerIndex] == 1); - EB_BOOL useDeltaQpSegments = singleSegment ? 0 : (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)); + EB_BOOL useDeltaQpSegments = singleSegment ? 0 : (EB_BOOL)(sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction||(sequenceControlSetPtr->staticConfig.lowLevelVbv)); if (is16bit) { EncodePassPackLcu( @@ -3201,9 +3201,9 @@ EB_EXTERN void EncodePass( cuPtr->deltaQp = 0; - cuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? + cuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.lowLevelVbv) ? contextPtr->qpmQp : lcuPtr->qp; - lcuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? + lcuPtr->qp = (sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction) && !(sequenceControlSetPtr->staticConfig.lowLevelVbv) ? contextPtr->qpmQp : lcuPtr->qp; cuPtr->orgDeltaQp = cuPtr->deltaQp; @@ -3690,7 +3690,7 @@ EB_EXTERN void EncodePass( // Encode Transform Unit -INTRA- - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)) ? + contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.lowLevelVbv)) ? EB_FALSE : lcuPtr->tileLeftEdgeFlag && ((contextPtr->cuOriginX & (63)) == 0) && (contextPtr->cuOriginY == lcuOriginY); @@ -4165,7 +4165,7 @@ EB_EXTERN void EncodePass( cuPtr->transformUnitArray[contextPtr->tuItr].cbCbf2 = EB_FALSE; cuPtr->transformUnitArray[contextPtr->tuItr].crCbf2 = EB_FALSE; } else if (cuPtr->predictionUnitArray[0].mergeFlag == EB_TRUE) { - contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)) ? + contextPtr->forceCbfFlag = (contextPtr->skipQpmFlag && !(sequenceControlSetPtr->staticConfig.lowLevelVbv)) ? EB_FALSE : lcuPtr->tileLeftEdgeFlag && ((tuOriginX & 63) == 0) && (tuOriginY == lcuOriginY); diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index f9f58c7aa..d99d41b62 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -4393,8 +4393,7 @@ void* EncDecKernel(void *inputPtr) } //Block level vbv tuning starts here - if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) - { + if (sequenceControlSetPtr->staticConfig.lowLevelVbv) { EbBlockOnMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); rowPtr = pictureControlSetPtr->rowStats[yLcuIndex]; rowPtr->rowIndex = yLcuIndex; @@ -4431,16 +4430,15 @@ void* EncDecKernel(void *inputPtr) // Encode Pass EncodePass( // HT done sequenceControlSetPtr, - pictureControlSetPtr, - lcuPtr, - lcuIndex, - lcuOriginX, - lcuOriginY, - lcuPtr->qp, - enableSaoFlag, - contextPtr); - if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) - { + pictureControlSetPtr, + lcuPtr, + lcuIndex, + lcuOriginX, + lcuOriginY, + lcuPtr->qp, + enableSaoFlag, + contextPtr); + if (sequenceControlSetPtr->staticConfig.lowLevelVbv) { /*Entropy Estimation for LCU*/ tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index cf6832aee..ce0f43c35 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -3053,6 +3053,12 @@ static EB_ERRORTYPE VerifySettings(\ return_error = EB_ErrorBadParameter; } + + if (config->lowLevelVbv == 1 && ((config->vbvBufsize <= 0) || (config->vbvMaxrate <= 0))) { + SVT_LOG("SVT [Error]: Instance %u: Enabling Low level vbv requires Frame Level Vbv to be enabled", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } + if (config->tileColumnCount < 1 || config->tileColumnCount > 20) { SVT_LOG("SVT [Error]: Instance %u : Invalid tile column count\n", channelNumber + 1); return_error = EB_ErrorBadParameter; diff --git a/Source/Lib/Codec/EbEntropyCoding.c b/Source/Lib/Codec/EbEntropyCoding.c index bbf73ae7b..f0b5f37c8 100644 --- a/Source/Lib/Codec/EbEntropyCoding.c +++ b/Source/Lib/Codec/EbEntropyCoding.c @@ -6272,9 +6272,9 @@ static void CodePPS( // "cu_qp_delta_enabled_flag" WriteFlagCavlc( bitstreamPtr, - scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction ||(scsPtr->staticConfig.vbvBufsize && scsPtr->staticConfig.vbvMaxrate && scsPtr->staticConfig.lowLevelVbv));// pcsPtr->useDeltaQp); + scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction ||(scsPtr->staticConfig.lowLevelVbv));// pcsPtr->useDeltaQp); - if (scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction|| (scsPtr->staticConfig.vbvBufsize && scsPtr->staticConfig.vbvMaxrate && scsPtr->staticConfig.lowLevelVbv)) { //pcsPtr->useDeltaQp) { + if (scsPtr->staticConfig.improveSharpness || scsPtr->staticConfig.bitRateReduction|| (scsPtr->staticConfig.lowLevelVbv)) { //pcsPtr->useDeltaQp) { // "diff_cu_qp_delta_depth" WriteUvlc( bitstreamPtr, @@ -7468,7 +7468,7 @@ EB_ERRORTYPE EstimateLcu ( cuOriginY, cuSize, sequenceControlSetPtr->lcuSize, - sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? EB_TRUE : EB_FALSE, + sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.lowLevelVbv) ? EB_TRUE : EB_FALSE, &entropyDeltaQpNotCoded, pictureControlSetPtr->difCuDeltaQpDepth, &pictureControlSetPtr->tempprevCodedQp[tileIdx], @@ -7935,7 +7935,7 @@ EB_ERRORTYPE EncodeLcu( cuOriginY, cuSize, sequenceControlSetPtr->lcuSize, - sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) ? EB_TRUE : EB_FALSE, + sequenceControlSetPtr->staticConfig.improveSharpness || sequenceControlSetPtr->staticConfig.bitRateReduction || (sequenceControlSetPtr->staticConfig.lowLevelVbv) ? EB_TRUE : EB_FALSE, &entropyDeltaQpNotCoded, pictureControlSetPtr->difCuDeltaQpDepth, &pictureControlSetPtr->prevCodedQp[tileIdx], diff --git a/Source/Lib/Codec/EbEntropyCodingProcess.c b/Source/Lib/Codec/EbEntropyCodingProcess.c index 0008d3ca5..ccdce5043 100644 --- a/Source/Lib/Codec/EbEntropyCodingProcess.c +++ b/Source/Lib/Codec/EbEntropyCodingProcess.c @@ -426,7 +426,7 @@ void* EntropyCodingKernel(void *inputPtr) lastLcuFlagInSlice = lastLcuFlagInTile; } - if (sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv) + if (sequenceControlSetPtr->staticConfig.lowLevelVbv) contextPtr->qp = lcuPtr->qp; // Configure the LCU EntropyCodingConfigureLcu( diff --git a/Source/Lib/Codec/EbPictureManagerProcess.c b/Source/Lib/Codec/EbPictureManagerProcess.c index 06c5cbf4f..87b3fe9dc 100644 --- a/Source/Lib/Codec/EbPictureManagerProcess.c +++ b/Source/Lib/Codec/EbPictureManagerProcess.c @@ -769,9 +769,9 @@ void* PictureManagerKernel(void *inputPtr) // Rate Control - ChildPictureControlSetPtr->useDeltaQp = (EB_U8)(entrySequenceControlSetPtr->staticConfig.improveSharpness || entrySequenceControlSetPtr->staticConfig.bitRateReduction ||(sequenceControlSetPtr->staticConfig.vbvBufsize && sequenceControlSetPtr->staticConfig.vbvMaxrate && sequenceControlSetPtr->staticConfig.lowLevelVbv)); + ChildPictureControlSetPtr->useDeltaQp = (EB_U8)(entrySequenceControlSetPtr->staticConfig.improveSharpness || entrySequenceControlSetPtr->staticConfig.bitRateReduction ||(sequenceControlSetPtr->staticConfig.lowLevelVbv)); - // Check resolution + // Check resoluti if (entrySequenceControlSetPtr->inputResolution < INPUT_SIZE_1080p_RANGE) ChildPictureControlSetPtr->difCuDeltaQpDepth = 2; else From c0b0ece294a11b8488a73b5b44d39f8d9a266fa7 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 12 Aug 2019 14:17:11 +0530 Subject: [PATCH 81/93] Fix inconsistent line endings in EbEncDecProcess.c --- Source/Lib/Codec/EbEncDecProcess.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index d99d41b62..322304ee0 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -1353,8 +1353,8 @@ static void ResetRowStats( EB_U8 lcuSizeLog2 = (EB_U8)Log2f(lcuSize); for (EB_U8 row = 0; row < ((sequenceControlSetPtr->lumaHeight + lcuSize - 1) >> lcuSizeLog2); row++) { EbBlockOnMutex(pictureControlSetPtr->rowStats[row]->rowUpdateMutex); - pictureControlSetPtr->rowStats[row]->totalCUEncoded = 0; - pictureControlSetPtr->rowStats[row]->encodedBits = 0; + pictureControlSetPtr->rowStats[row]->totalCUEncoded = 0; + pictureControlSetPtr->rowStats[row]->encodedBits = 0; pictureControlSetPtr->rowStats[row]->numEncodedCUs = 0; EbReleaseMutex(pictureControlSetPtr->rowStats[row]->rowUpdateMutex); } @@ -3986,7 +3986,7 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,//mutex to EB_U8 prevRowQp= qpVbv; EB_U8 lcuSizeLog2 = (EB_U8)Log2f(sequenceControlSetPtr->lcuSize); EB_U8 pictureHeightInLcu = (sequenceControlSetPtr->lumaHeight + sequenceControlSetPtr->lcuSize - 1) >> lcuSizeLog2; - EB_U8 qpAbsoluteMax = sequenceControlSetPtr->staticConfig.maxQpAllowed; + EB_U8 qpAbsoluteMax = sequenceControlSetPtr->staticConfig.maxQpAllowed; EB_U8 qpAbsoluteMin = sequenceControlSetPtr->staticConfig.minQpAllowed; EB_U8 qpMax = MIN(prevRowQp + 4, qpAbsoluteMax); EB_U8 qpMin = MAX(prevRowQp - 4, qpAbsoluteMin); From 1335e730013c3d86f0974a8290a993c6f3a23254 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 12 Aug 2019 15:04:23 +0530 Subject: [PATCH 82/93] Remove the SATD asm implementation Signed-off-by: kirithika --- Source/Lib/ASM_AVX2/CMakeLists.txt | 7 +- .../Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm | 10093 ---------------- .../Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h | 23 - Source/Lib/ASM_AVX2/const-a.asm | 136 - Source/Lib/ASM_AVX2/x86inc.asm | 1664 --- Source/Lib/ASM_AVX2/x86util.asm | 892 -- Source/Lib/Codec/CMakeLists.txt | 1 - Source/Lib/Codec/EbMeSatdCalculation.h | 42 - Source/Lib/Codec/EbMotionEstimation.c | 8 +- 9 files changed, 5 insertions(+), 12861 deletions(-) delete mode 100644 Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm delete mode 100644 Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h delete mode 100644 Source/Lib/ASM_AVX2/const-a.asm delete mode 100644 Source/Lib/ASM_AVX2/x86inc.asm delete mode 100644 Source/Lib/ASM_AVX2/x86util.asm delete mode 100644 Source/Lib/Codec/EbMeSatdCalculation.h diff --git a/Source/Lib/ASM_AVX2/CMakeLists.txt b/Source/Lib/ASM_AVX2/CMakeLists.txt index 50b12f33c..5e1f922b5 100644 --- a/Source/Lib/ASM_AVX2/CMakeLists.txt +++ b/Source/Lib/ASM_AVX2/CMakeLists.txt @@ -53,7 +53,6 @@ add_library(HEVC_ASM_AVX2 OBJECT EbComputeSAD_SadLoopKernel_AVX512.h EbIntraPrediction_AVX2.h EbMcp_AVX2.h - EbMeSatdCalculation_AVX2.h EbNoiseExtractAVX2.h EbPackUnPack_Intrinsic_AVX2.h EbPictureOperators_AVX2.h @@ -67,8 +66,4 @@ add_library(HEVC_ASM_AVX2 OBJECT EbNoiseExtractAVX2.c EbPackUnPack_Intrinsic_AVX2.c EbPictureOperators_Intrinsic_AVX2.c - EbTransforms_Intrinsic_AVX2.c - EbMeSatdCalculation_AVX2.asm - const-a.asm - x86inc.asm - x86util.asm) + EbTransforms_Intrinsic_AVX2.c) diff --git a/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm b/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm deleted file mode 100644 index 2b05971df..000000000 --- a/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.asm +++ /dev/null @@ -1,10093 +0,0 @@ -; -; Copyright(c) 2018 Intel Corporation -; SPDX - License - Identifier: BSD - 2 - Clause - Patent -; - -%include "x86inc.asm" -%include "x86util.asm" - -SECTION_RODATA 32 -hmul_8p: times 8 db 1 - times 4 db 1, -1 - times 8 db 1 - times 4 db 1, -1 -hmul_4p: times 4 db 1, 1, 1, 1, 1, -1, 1, -1 -mask_10: times 4 dw 0, -1 -mask_1100: times 2 dd 0, -1 -hmul_8w: times 4 dw 1 - times 2 dw 1, -1 - times 4 dw 1 - times 2 dw 1, -1 -psy_pp_shuff1: dq 0, 1, 8, 9, 4, 5, 12, 13 -psy_pp_shuff2: dq 2, 3, 10, 11, 6, 7, 14, 15 -psy_pp_shuff3: dq 0, 0, 8, 8, 1, 1, 9, 9 - -ALIGN 32 -transd_shuf1: SHUFFLE_MASK_W 0, 8, 2, 10, 4, 12, 6, 14 -transd_shuf2: SHUFFLE_MASK_W 1, 9, 3, 11, 5, 13, 7, 15 - -SECTION .text - -cextern pb_0 -cextern pb_1 -cextern pw_1 -cextern pw_8 -cextern pw_16 -cextern pw_32 -cextern pw_00ff -cextern pw_ppppmmmm -cextern pw_ppmmppmm -cextern pw_pmpmpmpm -cextern pw_pmmpzzzz -cextern pd_1 -cextern pd_2 -cextern hmul_16p -cextern pb_movemask -cextern pb_movemask_32 -cextern pw_pixel_max - - -;============================================================================= -; SATD -;============================================================================= - -%macro JDUP 2 -%if cpuflag(sse4) - ; just use shufps on anything post conroe - shufps %1, %2, 0 -%elif cpuflag(ssse3) && notcpuflag(atom) - ; join 2x 32 bit and duplicate them - ; emulating shufps is faster on conroe - punpcklqdq %1, %2 - movsldup %1, %1 -%else - ; doesn't need to dup. sse2 does things by zero extending to words and full h_2d - punpckldq %1, %2 -%endif -%endmacro - -%macro HSUMSUB 5 - pmaddubsw m%2, m%5 - pmaddubsw m%1, m%5 - pmaddubsw m%4, m%5 - pmaddubsw m%3, m%5 -%endmacro - -%macro DIFF_UNPACK_SSE2 5 - punpcklbw m%1, m%5 - punpcklbw m%2, m%5 - punpcklbw m%3, m%5 - punpcklbw m%4, m%5 - psubw m%1, m%2 - psubw m%3, m%4 -%endmacro - -%macro DIFF_SUMSUB_SSSE3 5 - HSUMSUB %1, %2, %3, %4, %5 - psubw m%1, m%2 - psubw m%3, m%4 -%endmacro - -%macro LOAD_DUP_2x4P 4 ; dst, tmp, 2* pointer - movd %1, %3 - movd %2, %4 - JDUP %1, %2 -%endmacro - -%macro LOAD_DUP_4x8P_CONROE 8 ; 4*dst, 4*pointer - movddup m%3, %6 - movddup m%4, %8 - movddup m%1, %5 - movddup m%2, %7 -%endmacro - -%macro LOAD_DUP_4x8P_PENRYN 8 - ; penryn and nehalem run punpcklqdq and movddup in different units - movh m%3, %6 - movh m%4, %8 - punpcklqdq m%3, m%3 - movddup m%1, %5 - punpcklqdq m%4, m%4 - movddup m%2, %7 -%endmacro - -%macro LOAD_SUMSUB_8x2P 9 - LOAD_DUP_4x8P %1, %2, %3, %4, %6, %7, %8, %9 - DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %5 -%endmacro - -%macro LOAD_SUMSUB_8x4P_SSSE3 7-11 r0, r2, 0, 0 -; 4x dest, 2x tmp, 1x mul, [2* ptr], [increment?] - LOAD_SUMSUB_8x2P %1, %2, %5, %6, %7, [%8], [%9], [%8+r1], [%9+r3] - LOAD_SUMSUB_8x2P %3, %4, %5, %6, %7, [%8+2*r1], [%9+2*r3], [%8+r4], [%9+r5] -%if %10 - lea %8, [%8+4*r1] - lea %9, [%9+4*r3] -%endif -%endmacro - -%macro LOAD_SUMSUB_16P_SSSE3 7 ; 2*dst, 2*tmp, mul, 2*ptr - movddup m%1, [%7] - movddup m%2, [%7+8] - mova m%4, [%6] - movddup m%3, m%4 - punpckhqdq m%4, m%4 - DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %5 -%endmacro - -%macro LOAD_SUMSUB_16P_SSE2 7 ; 2*dst, 2*tmp, mask, 2*ptr - movu m%4, [%7] - mova m%2, [%6] - DEINTB %1, %2, %3, %4, %5 - psubw m%1, m%3 - psubw m%2, m%4 - SUMSUB_BA w, %1, %2, %3 -%endmacro - -%macro LOAD_SUMSUB_16x4P 10-13 r0, r2, none -; 8x dest, 1x tmp, 1x mul, [2* ptr] [2nd tmp] - LOAD_SUMSUB_16P %1, %5, %2, %3, %10, %11, %12 - LOAD_SUMSUB_16P %2, %6, %3, %4, %10, %11+r1, %12+r3 - LOAD_SUMSUB_16P %3, %7, %4, %9, %10, %11+2*r1, %12+2*r3 - LOAD_SUMSUB_16P %4, %8, %13, %9, %10, %11+r4, %12+r5 -%endmacro - -%macro LOAD_SUMSUB_16x2P_AVX2 9 -; 2*dst, 2*tmp, mul, 4*ptr - vbroadcasti128 m%1, [%6] - vbroadcasti128 m%3, [%7] - vbroadcasti128 m%2, [%8] - vbroadcasti128 m%4, [%9] - DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %5 -%endmacro - -%macro LOAD_SUMSUB_16x4P_AVX2 7-11 r0, r2, 0, 0 -; 4x dest, 2x tmp, 1x mul, [2* ptr], [increment?] - LOAD_SUMSUB_16x2P_AVX2 %1, %2, %5, %6, %7, %8, %9, %8+r1, %9+r3 - LOAD_SUMSUB_16x2P_AVX2 %3, %4, %5, %6, %7, %8+2*r1, %9+2*r3, %8+r4, %9+r5 -%if %10 - lea %8, [%8+4*r1] - lea %9, [%9+4*r3] -%endif -%endmacro - -%macro LOAD_DUP_4x16P_AVX2 8 ; 4*dst, 4*pointer - mova xm%3, %6 - mova xm%4, %8 - mova xm%1, %5 - mova xm%2, %7 - vpermq m%3, m%3, q0011 - vpermq m%4, m%4, q0011 - vpermq m%1, m%1, q0011 - vpermq m%2, m%2, q0011 -%endmacro - -%macro LOAD_SUMSUB8_16x2P_AVX2 9 -; 2*dst, 2*tmp, mul, 4*ptr - LOAD_DUP_4x16P_AVX2 %1, %2, %3, %4, %6, %7, %8, %9 - DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %5 -%endmacro - -%macro LOAD_SUMSUB8_16x4P_AVX2 7-11 r0, r2, 0, 0 -; 4x dest, 2x tmp, 1x mul, [2* ptr], [increment?] - LOAD_SUMSUB8_16x2P_AVX2 %1, %2, %5, %6, %7, [%8], [%9], [%8+r1], [%9+r3] - LOAD_SUMSUB8_16x2P_AVX2 %3, %4, %5, %6, %7, [%8+2*r1], [%9+2*r3], [%8+r4], [%9+r5] -%if %10 - lea %8, [%8+4*r1] - lea %9, [%9+4*r3] -%endif -%endmacro - -; in: r4=3*stride1, r5=3*stride2 -; in: %2 = horizontal offset -; in: %3 = whether we need to increment pix1 and pix2 -; clobber: m3..m7 -; out: %1 = satd -%macro SATD_4x4_MMX 3 - %xdefine %%n nn%1 - %assign offset %2*SIZEOF_PIXEL - LOAD_DIFF m4, m3, none, [r0+ offset], [r2+ offset] - LOAD_DIFF m5, m3, none, [r0+ r1+offset], [r2+ r3+offset] - LOAD_DIFF m6, m3, none, [r0+2*r1+offset], [r2+2*r3+offset] - LOAD_DIFF m7, m3, none, [r0+ r4+offset], [r2+ r5+offset] -%if %3 - lea r0, [r0+4*r1] - lea r2, [r2+4*r3] -%endif - HADAMARD4_2D 4, 5, 6, 7, 3, %%n - paddw m4, m6 -;%if HIGH_BIT_DEPTH && (BIT_DEPTH == 12) -; pxor m5, m5 -; punpcklwd m6, m4, m5 -; punpckhwd m4, m5 -; paddd m4, m6 -;%endif - SWAP %%n, 4 -%endmacro - -; in: %1 = horizontal if 0, vertical if 1 -%macro SATD_8x4_SSE 8-9 -%if %1 - HADAMARD4_2D_SSE %2, %3, %4, %5, %6, amax -%else - HADAMARD4_V %2, %3, %4, %5, %6 - ; doing the abs first is a slight advantage - ABSW2 m%2, m%4, m%2, m%4, m%6, m%7 - ABSW2 m%3, m%5, m%3, m%5, m%6, m%7 - HADAMARD 1, max, %2, %4, %6, %7 -%endif -%ifnidn %9, swap - %if (BIT_DEPTH == 12) - pxor m%6, m%6 - punpcklwd m%7, m%2, m%6 - punpckhwd m%2, m%6 - paddd m%8, m%7 - paddd m%8, m%2 - %else - paddw m%8, m%2 - %endif -%else - SWAP %8, %2 - %if (BIT_DEPTH == 12) - pxor m%6, m%6 - punpcklwd m%7, m%8, m%6 - punpckhwd m%8, m%6 - paddd m%8, m%7 - %endif -%endif -%if %1 - %if (BIT_DEPTH == 12) - pxor m%6, m%6 - punpcklwd m%7, m%4, m%6 - punpckhwd m%4, m%6 - paddd m%8, m%7 - paddd m%8, m%4 - %else - paddw m%8, m%4 - %endif -%else - HADAMARD 1, max, %3, %5, %6, %7 - %if (BIT_DEPTH == 12) - pxor m%6, m%6 - punpcklwd m%7, m%3, m%6 - punpckhwd m%3, m%6 - paddd m%8, m%7 - paddd m%8, m%3 - %else - paddw m%8, m%3 - %endif -%endif -%endmacro - -%macro SATD_8x4_1_SSE 10 -%if %1 - HADAMARD4_2D_SSE %2, %3, %4, %5, %6, amax -%else - HADAMARD4_V %2, %3, %4, %5, %6 - ; doing the abs first is a slight advantage - ABSW2 m%2, m%4, m%2, m%4, m%6, m%7 - ABSW2 m%3, m%5, m%3, m%5, m%6, m%7 - HADAMARD 1, max, %2, %4, %6, %7 -%endif - - pxor m%10, m%10 - punpcklwd m%9, m%2, m%10 - paddd m%8, m%9 - punpckhwd m%9, m%2, m%10 - paddd m%8, m%9 - -%if %1 - pxor m%10, m%10 - punpcklwd m%9, m%4, m%10 - paddd m%8, m%9 - punpckhwd m%9, m%4, m%10 - paddd m%8, m%9 -%else - HADAMARD 1, max, %3, %5, %6, %7 - pxor m%10, m%10 - punpcklwd m%9, m%3, m%10 - paddd m%8, m%9 - punpckhwd m%9, m%3, m%10 - paddd m%8, m%9 -%endif -%endmacro - -%macro SATD_START_MMX 0 - FIX_STRIDES r1, r3 - lea r4, [3*r1] ; 3*stride1 - lea r5, [3*r3] ; 3*stride2 -%endmacro - -%macro SATD_END_MMX 0 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 - movd eax, m0 -%else ; !HIGH_BIT_DEPTH - pshufw m1, m0, q1032 - paddw m0, m1 - pshufw m1, m0, q2301 - paddw m0, m1 - movd eax, m0 - and eax, 0xffff -%endif ; HIGH_BIT_DEPTH - EMMS - RET -%endmacro - -; FIXME avoid the spilling of regs to hold 3*stride. -; for small blocks on x86_32, modify pixel pointer instead. - -;----------------------------------------------------------------------------- -; int pixel_satd_16x16( uint8_t *, intptr_t, uint8_t *, intptr_t ) -;----------------------------------------------------------------------------- -INIT_MMX mmx2 -cglobal pixel_satd_4x4, 4,6 - SATD_START_MMX - SATD_4x4_MMX m0, 0, 0 - SATD_END_MMX - -%macro SATD_START_SSE2 2-3 0 - FIX_STRIDES r1, r3 -%if HIGH_BIT_DEPTH && %3 - pxor %2, %2 -%elif cpuflag(ssse3) && notcpuflag(atom) -%if mmsize==32 - mova %2, [hmul_16p] -%else - mova %2, [hmul_8p] -%endif -%endif - lea r4, [3*r1] - lea r5, [3*r3] - pxor %1, %1 -%endmacro - -%macro SATD_END_SSE2 1-2 -%if HIGH_BIT_DEPTH - %if BIT_DEPTH == 12 - HADDD %1, xm0 - %else ; BIT_DEPTH == 12 - HADDUW %1, xm0 - %endif ; BIT_DEPTH == 12 - %if %0 == 2 - paddd %1, %2 - %endif -%else - HADDW %1, xm7 -%endif - movd eax, %1 - RET -%endmacro - -%macro SATD_ACCUM 3 -%if HIGH_BIT_DEPTH - HADDUW %1, %2 - paddd %3, %1 - pxor %1, %1 -%endif -%endmacro - -%macro BACKUP_POINTERS 0 -%if ARCH_X86_64 -%if WIN64 - PUSH r7 -%endif - mov r6, r0 - mov r7, r2 -%endif -%endmacro - -%macro RESTORE_AND_INC_POINTERS 0 -%if ARCH_X86_64 - lea r0, [r6+8*SIZEOF_PIXEL] - lea r2, [r7+8*SIZEOF_PIXEL] -%if WIN64 - POP r7 -%endif -%else - mov r0, r0mp - mov r2, r2mp - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL -%endif -%endmacro - -%macro SATD_4x8_SSE 3-4 -%if HIGH_BIT_DEPTH - movh m0, [r0+0*r1] - movh m4, [r2+0*r3] - movh m1, [r0+1*r1] - movh m5, [r2+1*r3] - movhps m0, [r0+4*r1] - movhps m4, [r2+4*r3] - movh m2, [r0+2*r1] - movh m6, [r2+2*r3] - psubw m0, m4 - movh m3, [r0+r4] - movh m4, [r2+r5] - lea r0, [r0+4*r1] - lea r2, [r2+4*r3] - movhps m1, [r0+1*r1] - movhps m5, [r2+1*r3] - movhps m2, [r0+2*r1] - movhps m6, [r2+2*r3] - psubw m1, m5 - movhps m3, [r0+r4] - movhps m4, [r2+r5] - psubw m2, m6 - psubw m3, m4 -%else ; !HIGH_BIT_DEPTH - movd m4, [r2] - movd m5, [r2+r3] - movd m6, [r2+2*r3] - add r2, r5 - movd m0, [r0] - movd m1, [r0+r1] - movd m2, [r0+2*r1] - add r0, r4 - movd m3, [r2+r3] - JDUP m4, m3 - movd m3, [r0+r1] - JDUP m0, m3 - movd m3, [r2+2*r3] - JDUP m5, m3 - movd m3, [r0+2*r1] - JDUP m1, m3 -%if %1==0 && %2==1 - mova m3, [hmul_4p] - DIFFOP 0, 4, 1, 5, 3 -%else - DIFFOP 0, 4, 1, 5, 7 -%endif - movd m5, [r2] - add r2, r5 - movd m3, [r0] - add r0, r4 - movd m4, [r2] - JDUP m6, m4 - movd m4, [r0] - JDUP m2, m4 - movd m4, [r2+r3] - JDUP m5, m4 - movd m4, [r0+r1] - JDUP m3, m4 -%if %1==0 && %2==1 - mova m4, [hmul_4p] - DIFFOP 2, 6, 3, 5, 4 -%else - DIFFOP 2, 6, 3, 5, 7 -%endif -%endif ; HIGH_BIT_DEPTH -%if %0 == 4 - SATD_8x4_1_SSE %1, 0, 1, 2, 3, 4, 5, 7, %3, %4 -%else - SATD_8x4_SSE %1, 0, 1, 2, 3, 4, 5, 7, %3 -%endif -%endmacro - -;----------------------------------------------------------------------------- -; int pixel_satd_8x4( uint8_t *, intptr_t, uint8_t *, intptr_t ) -;----------------------------------------------------------------------------- -%macro SATDS_SSE2 0 -%define vertical ((notcpuflag(ssse3) || cpuflag(atom)) || HIGH_BIT_DEPTH) - -%if cpuflag(ssse3) && (vertical==0 || HIGH_BIT_DEPTH) -cglobal pixel_satd_4x4, 4, 6, 6 - SATD_START_MMX - mova m4, [hmul_4p] - LOAD_DUP_2x4P m2, m5, [r2], [r2+r3] - LOAD_DUP_2x4P m3, m5, [r2+2*r3], [r2+r5] - LOAD_DUP_2x4P m0, m5, [r0], [r0+r1] - LOAD_DUP_2x4P m1, m5, [r0+2*r1], [r0+r4] - DIFF_SUMSUB_SSSE3 0, 2, 1, 3, 4 - HADAMARD 0, sumsub, 0, 1, 2, 3 - HADAMARD 4, sumsub, 0, 1, 2, 3 - HADAMARD 1, amax, 0, 1, 2, 3 - HADDW m0, m1 - movd eax, m0 - RET -%endif - -cglobal pixel_satd_4x8, 4, 6, 8 - SATD_START_MMX -%if vertical==0 - mova m7, [hmul_4p] -%endif - SATD_4x8_SSE vertical, 0, swap -%if BIT_DEPTH == 12 - HADDD m7, m1 -%else - HADDUW m7, m1 -%endif - movd eax, m7 - RET - -cglobal pixel_satd_4x16, 4, 6, 8 - SATD_START_MMX -%if vertical==0 - mova m7, [hmul_4p] -%endif - SATD_4x8_SSE vertical, 0, swap - lea r0, [r0+r1*2*SIZEOF_PIXEL] - lea r2, [r2+r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add -%if BIT_DEPTH == 12 - HADDD m7, m1 -%else - HADDUW m7, m1 -%endif - movd eax, m7 - RET - -cglobal pixel_satd_8x8_internal - LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 - SATD_8x4_SSE vertical, 0, 1, 2, 3, 4, 5, 6 -%%pixel_satd_8x4_internal: - LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 - SATD_8x4_SSE vertical, 0, 1, 2, 3, 4, 5, 6 - ret - -cglobal pixel_satd_8x8_internal2 -%if WIN64 - LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 - SATD_8x4_1_SSE vertical, 0, 1, 2, 3, 4, 5, 6, 12, 13 -%%pixel_satd_8x4_internal2: - LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 - SATD_8x4_1_SSE vertical, 0, 1, 2, 3, 4, 5, 6, 12, 13 -%else - LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 - SATD_8x4_1_SSE vertical, 0, 1, 2, 3, 4, 5, 6, 4, 5 -%%pixel_satd_8x4_internal2: - LOAD_SUMSUB_8x4P 0, 1, 2, 3, 4, 5, 7, r0, r2, 1, 0 - SATD_8x4_1_SSE vertical, 0, 1, 2, 3, 4, 5, 6, 4, 5 -%endif - ret - -; 16x8 regresses on phenom win64, 16x16 is almost the same (too many spilled registers) -; These aren't any faster on AVX systems with fast movddup (Bulldozer, Sandy Bridge) -%if HIGH_BIT_DEPTH == 0 && (WIN64 || UNIX64) && notcpuflag(avx) - -cglobal pixel_satd_16x4_internal2 - LOAD_SUMSUB_16x4P 0, 1, 2, 3, 4, 8, 5, 9, 6, 7, r0, r2, 11 - lea r2, [r2+4*r3] - lea r0, [r0+4*r1] - SATD_8x4_1_SSE 0, 0, 1, 2, 3, 6, 11, 10, 12, 13 - SATD_8x4_1_SSE 0, 4, 8, 5, 9, 6, 3, 10, 12, 13 - ret - -cglobal pixel_satd_16x4, 4,6,14 - SATD_START_SSE2 m10, m7 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_16x8, 4,6,14 - SATD_START_SSE2 m10, m7 -%if vertical - mova m7, [pw_00ff] -%endif - jmp %%pixel_satd_16x8_internal - -cglobal pixel_satd_16x12, 4,6,14 - SATD_START_SSE2 m10, m7 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - jmp %%pixel_satd_16x8_internal - -cglobal pixel_satd_16x32, 4,6,14 - SATD_START_SSE2 m10, m7 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - jmp %%pixel_satd_16x8_internal - -cglobal pixel_satd_16x64, 4,6,14 - SATD_START_SSE2 m10, m7 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - jmp %%pixel_satd_16x8_internal - -cglobal pixel_satd_16x16, 4,6,14 - SATD_START_SSE2 m10, m7 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 -%%pixel_satd_16x8_internal: - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_32x8, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_32x16, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_32x24, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_32x32, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_32x64, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_48x64, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_64x16, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_64x32, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_64x48, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - - HADDD m10, m0 - movd eax, m10 - RET - -cglobal pixel_satd_64x64, 4,8,14 ;if WIN64 && notcpuflag(avx) - SATD_START_SSE2 m10, m7 - mov r6, r0 - mov r7, r2 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - call pixel_satd_16x4_internal2 - - HADDD m10, m0 - movd eax, m10 - RET - -%else -%if WIN64 -cglobal pixel_satd_16x24, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_16x24, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif -%if WIN64 -cglobal pixel_satd_32x48, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_32x48, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_24x64, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_24x64, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_8x64, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_8x64, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_8x12, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call %%pixel_satd_8x4_internal2 - pxor m7, m7 - movhlps m7, m6 - paddd m6, m7 - pshufd m7, m6, 1 - paddd m6, m7 - movd eax, m6 - RET -%else -cglobal pixel_satd_8x12, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call %%pixel_satd_8x4_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if HIGH_BIT_DEPTH -%if WIN64 -cglobal pixel_satd_12x32, 4,8,8 ;if WIN64 && cpuflag(avx) - SATD_START_MMX - mov r6, r0 - mov r7, r2 - pxor m7, m7 - SATD_4x8_SSE vertical, 0, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r6 + 4*SIZEOF_PIXEL] - lea r2, [r7 + 4*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - HADDD m7, m0 - movd eax, m7 - RET -%else -cglobal pixel_satd_12x32, 4,7,8,0-gprsize - SATD_START_MMX - mov r6, r0 - mov [rsp], r2 - pxor m7, m7 - SATD_4x8_SSE vertical, 0, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r6 + 4*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 4*SIZEOF_PIXEL - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - HADDD m7, m0 - movd eax, m7 - RET -%endif -%else ;HIGH_BIT_DEPTH -%if WIN64 -cglobal pixel_satd_12x32, 4,8,8 ;if WIN64 && cpuflag(avx) - SATD_START_MMX - mov r6, r0 - mov r7, r2 -%if vertical==0 - mova m7, [hmul_4p] -%endif - SATD_4x8_SSE vertical, 0, swap - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r6 + 4*SIZEOF_PIXEL] - lea r2, [r7 + 4*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - HADDW m7, m1 - movd eax, m7 - RET -%else -cglobal pixel_satd_12x32, 4,7,8,0-gprsize - SATD_START_MMX - mov r6, r0 - mov [rsp], r2 -%if vertical==0 - mova m7, [hmul_4p] -%endif - SATD_4x8_SSE vertical, 0, swap - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r6 + 4*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 4*SIZEOF_PIXEL - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - HADDW m7, m1 - movd eax, m7 - RET -%endif -%endif - -%if HIGH_BIT_DEPTH -%if WIN64 -cglobal pixel_satd_4x32, 4,8,8 ;if WIN64 && cpuflag(avx) - SATD_START_MMX - mov r6, r0 - mov r7, r2 - pxor m7, m7 - SATD_4x8_SSE vertical, 0, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - HADDD m7, m0 - movd eax, m7 - RET -%else -cglobal pixel_satd_4x32, 4,7,8,0-gprsize - SATD_START_MMX - mov r6, r0 - mov [rsp], r2 - pxor m7, m7 - SATD_4x8_SSE vertical, 0, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - pxor m1, m1 - movhlps m1, m7 - paddd m7, m1 - pshufd m1, m7, 1 - paddd m7, m1 - movd eax, m7 - RET -%endif -%else -%if WIN64 -cglobal pixel_satd_4x32, 4,8,8 ;if WIN64 && cpuflag(avx) - SATD_START_MMX - mov r6, r0 - mov r7, r2 -%if vertical==0 - mova m7, [hmul_4p] -%endif - SATD_4x8_SSE vertical, 0, swap - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - HADDW m7, m1 - movd eax, m7 - RET -%else -cglobal pixel_satd_4x32, 4,7,8,0-gprsize - SATD_START_MMX - mov r6, r0 - mov [rsp], r2 -%if vertical==0 - mova m7, [hmul_4p] -%endif - SATD_4x8_SSE vertical, 0, swap - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - HADDW m7, m1 - movd eax, m7 - RET -%endif -%endif - -%if WIN64 -cglobal pixel_satd_32x8, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_32x8, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_32x16, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_32x16, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_32x24, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_32x24, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_32x32, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_32x32, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_32x64, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_32x64, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_48x64, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - lea r2, [r7 + 32*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - lea r2, [r7 + 40*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_48x64, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,32*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,40*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - - -%if WIN64 -cglobal pixel_satd_64x16, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - lea r2, [r7 + 32*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - lea r2, [r7 + 40*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 48*SIZEOF_PIXEL] - lea r2, [r7 + 48*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 56*SIZEOF_PIXEL] - lea r2, [r7 + 56*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_64x16, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,32*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,40*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 48*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,48*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 56*SIZEOF_PIXEL] - mov r2, [rsp] - add r2,56*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_64x32, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - lea r2, [r7 + 32*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - lea r2, [r7 + 40*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 48*SIZEOF_PIXEL] - lea r2, [r7 + 48*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 56*SIZEOF_PIXEL] - lea r2, [r7 + 56*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_64x32, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 32*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 40*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 48*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 48*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 56*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 56*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_64x48, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - lea r2, [r7 + 32*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - lea r2, [r7 + 40*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 48*SIZEOF_PIXEL] - lea r2, [r7 + 48*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 56*SIZEOF_PIXEL] - lea r2, [r7 + 56*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_64x48, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 32*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 40*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 48*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 48*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 56*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 56*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_64x64, 4,8,14 ;if WIN64 && cpuflag(avx) - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - lea r2, [r7 + 24*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - lea r2, [r7 + 32*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - lea r2, [r7 + 40*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 48*SIZEOF_PIXEL] - lea r2, [r7 + 48*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 56*SIZEOF_PIXEL] - lea r2, [r7 + 56*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_64x64, 4,7,8,0-gprsize ;if !WIN64 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 24*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 24*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 32*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 32*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 40*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 40*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 48*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 48*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 56*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 56*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if WIN64 -cglobal pixel_satd_16x4, 4,6,14 -%else -cglobal pixel_satd_16x4, 4,6,8 -%endif - SATD_START_SSE2 m6, m7 - BACKUP_POINTERS - call %%pixel_satd_8x4_internal2 - RESTORE_AND_INC_POINTERS - call %%pixel_satd_8x4_internal2 - HADDD m6, m0 - movd eax, m6 - RET - -%if WIN64 -cglobal pixel_satd_16x8, 4,6,14 -%else -cglobal pixel_satd_16x8, 4,6,8 -%endif - SATD_START_SSE2 m6, m7 - BACKUP_POINTERS - call pixel_satd_8x8_internal2 - RESTORE_AND_INC_POINTERS - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET - -%if WIN64 -cglobal pixel_satd_16x12, 4,6,14 -%else -cglobal pixel_satd_16x12, 4,6,8 -%endif - SATD_START_SSE2 m6, m7, 1 - BACKUP_POINTERS - call pixel_satd_8x8_internal2 - call %%pixel_satd_8x4_internal2 - RESTORE_AND_INC_POINTERS - call pixel_satd_8x8_internal2 - call %%pixel_satd_8x4_internal2 - HADDD m6, m0 - movd eax, m6 - RET - -%if WIN64 -cglobal pixel_satd_16x16, 4,6,14 -%else -cglobal pixel_satd_16x16, 4,6,8 -%endif - SATD_START_SSE2 m6, m7, 1 - BACKUP_POINTERS - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - RESTORE_AND_INC_POINTERS - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET - -%if WIN64 -cglobal pixel_satd_16x32, 4,6,14 -%else -cglobal pixel_satd_16x32, 4,6,8 -%endif - SATD_START_SSE2 m6, m7, 1 - BACKUP_POINTERS - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - RESTORE_AND_INC_POINTERS - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET - -%if WIN64 -cglobal pixel_satd_16x64, 4,6,14 -%else -cglobal pixel_satd_16x64, 4,6,8 -%endif - SATD_START_SSE2 m6, m7, 1 - BACKUP_POINTERS - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - RESTORE_AND_INC_POINTERS - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif - -%if HIGH_BIT_DEPTH -%if WIN64 -cglobal pixel_satd_12x16, 4,8,8 - SATD_START_MMX - mov r6, r0 - mov r7, r2 - pxor m7, m7 - SATD_4x8_SSE vertical, 0, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r6 + 4*SIZEOF_PIXEL] - lea r2, [r7 + 4*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - HADDD m7, m0 - movd eax, m7 - RET -%else -cglobal pixel_satd_12x16, 4,7,8,0-gprsize - SATD_START_MMX - mov r6, r0 - mov [rsp], r2 - pxor m7, m7 - SATD_4x8_SSE vertical, 0, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r6 + 4*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 4*SIZEOF_PIXEL - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - SATD_4x8_SSE vertical, 1, 4, 5 - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, 4, 5 - HADDD m7, m0 - movd eax, m7 - RET -%endif -%else ;HIGH_BIT_DEPTH -%if WIN64 -cglobal pixel_satd_12x16, 4,8,8 - SATD_START_MMX - mov r6, r0 - mov r7, r2 -%if vertical==0 - mova m7, [hmul_4p] -%endif - SATD_4x8_SSE vertical, 0, swap - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r6 + 4*SIZEOF_PIXEL] - lea r2, [r7 + 4*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - HADDW m7, m1 - movd eax, m7 - RET -%else -cglobal pixel_satd_12x16, 4,7,8,0-gprsize - SATD_START_MMX - mov r6, r0 - mov [rsp], r2 -%if vertical==0 - mova m7, [hmul_4p] -%endif - SATD_4x8_SSE vertical, 0, swap - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r6 + 4*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 4*SIZEOF_PIXEL - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - SATD_4x8_SSE vertical, 1, add - lea r0, [r0 + r1*2*SIZEOF_PIXEL] - lea r2, [r2 + r3*2*SIZEOF_PIXEL] - SATD_4x8_SSE vertical, 1, add - HADDW m7, m1 - movd eax, m7 - RET -%endif -%endif - -%if WIN64 -cglobal pixel_satd_24x32, 4,8,14 - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov r7, r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - lea r2, [r7 + 8*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - lea r2, [r7 + 16*SIZEOF_PIXEL] - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%else -cglobal pixel_satd_24x32, 4,7,8,0-gprsize - SATD_START_SSE2 m6, m7 - mov r6, r0 - mov [rsp], r2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 8*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 8*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - lea r0, [r6 + 16*SIZEOF_PIXEL] - mov r2, [rsp] - add r2, 16*SIZEOF_PIXEL - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET -%endif ;WIN64 - -%if WIN64 -cglobal pixel_satd_8x32, 4,6,14 -%else -cglobal pixel_satd_8x32, 4,6,8 -%endif - SATD_START_SSE2 m6, m7 -%if vertical - mova m7, [pw_00ff] -%endif - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET - -%if WIN64 -cglobal pixel_satd_8x16, 4,6,14 -%else -cglobal pixel_satd_8x16, 4,6,8 -%endif - SATD_START_SSE2 m6, m7 - call pixel_satd_8x8_internal2 - call pixel_satd_8x8_internal2 - HADDD m6, m0 - movd eax, m6 - RET - -cglobal pixel_satd_8x8, 4,6,8 - SATD_START_SSE2 m6, m7 - call pixel_satd_8x8_internal - SATD_END_SSE2 m6 - -%if WIN64 -cglobal pixel_satd_8x4, 4,6,14 -%else -cglobal pixel_satd_8x4, 4,6,8 -%endif - SATD_START_SSE2 m6, m7 - call %%pixel_satd_8x4_internal2 - SATD_END_SSE2 m6 -%endmacro ; SATDS_SSE2 - - -;============================================================================= -; SA8D -;============================================================================= - -%macro SA8D_INTER 0 -%if ARCH_X86_64 - %define lh m10 - %define rh m0 -%else - %define lh m0 - %define rh [esp+48] -%endif -%if HIGH_BIT_DEPTH - HADDUW m0, m1 - paddd lh, rh -%else - paddusw lh, rh -%endif ; HIGH_BIT_DEPTH -%endmacro - -%macro SA8D_8x8 0 - call pixel_sa8d_8x8_internal -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%else - HADDW m0, m1 -%endif ; HIGH_BIT_DEPTH - paddd m0, [pd_1] - psrld m0, 1 - paddd m12, m0 -%endmacro - -%macro SA8D_16x16 0 - call pixel_sa8d_8x8_internal ; pix[0] - add r2, 8*SIZEOF_PIXEL - add r0, 8*SIZEOF_PIXEL -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova m10, m0 - call pixel_sa8d_8x8_internal ; pix[8] - lea r2, [r2+8*r3] - lea r0, [r0+8*r1] - SA8D_INTER - call pixel_sa8d_8x8_internal ; pix[8*stride+8] - sub r2, 8*SIZEOF_PIXEL - sub r0, 8*SIZEOF_PIXEL - SA8D_INTER - call pixel_sa8d_8x8_internal ; pix[8*stride] - SA8D_INTER - SWAP 0, 10 -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - paddd m0, [pd_1] - psrld m0, 1 - paddd m12, m0 -%endmacro - -%macro AVG_16x16 0 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d -%endmacro - -%macro SA8D 0 -; sse2 doesn't seem to like the horizontal way of doing things -%define vertical ((notcpuflag(ssse3) || cpuflag(atom)) || HIGH_BIT_DEPTH) - -%if ARCH_X86_64 -;----------------------------------------------------------------------------- -; int pixel_sa8d_8x8( uint8_t *, intptr_t, uint8_t *, intptr_t ) -;----------------------------------------------------------------------------- -cglobal pixel_sa8d_8x8_internal - lea r6, [r0+4*r1] - lea r7, [r2+4*r3] - LOAD_SUMSUB_8x4P 0, 1, 2, 8, 5, 6, 7, r0, r2 - LOAD_SUMSUB_8x4P 4, 5, 3, 9, 11, 6, 7, r6, r7 -%if vertical - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax -%else ; non-sse2 - HADAMARD8_2D_HMUL 0, 1, 2, 8, 4, 5, 3, 9, 6, 11 -%endif - paddw m0, m1 - paddw m0, m2 - paddw m0, m8 - SAVE_MM_PERMUTATION - ret - -cglobal pixel_sa8d_8x8, 4,8,12 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] -%if vertical == 0 - mova m7, [hmul_8p] -%endif - call pixel_sa8d_8x8_internal -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%else - HADDW m0, m1 -%endif ; HIGH_BIT_DEPTH - movd eax, m0 - add eax, 1 - shr eax, 1 - RET - -cglobal pixel_sa8d_16x16, 4,8,12 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] -%if vertical == 0 - mova m7, [hmul_8p] -%endif - call pixel_sa8d_8x8_internal ; pix[0] - add r2, 8*SIZEOF_PIXEL - add r0, 8*SIZEOF_PIXEL -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova m10, m0 - call pixel_sa8d_8x8_internal ; pix[8] - lea r2, [r2+8*r3] - lea r0, [r0+8*r1] - SA8D_INTER - call pixel_sa8d_8x8_internal ; pix[8*stride+8] - sub r2, 8*SIZEOF_PIXEL - sub r0, 8*SIZEOF_PIXEL - SA8D_INTER - call pixel_sa8d_8x8_internal ; pix[8*stride] - SA8D_INTER - SWAP 0, 10 -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd eax, m0 - add eax, 1 - shr eax, 1 - RET - -cglobal pixel_sa8d_8x16, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_8x8 - lea r0, [r0 + 8*r1] - lea r2, [r2 + 8*r3] - SA8D_8x8 - movd eax, m12 - RET - -cglobal pixel_sa8d_8x32, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_8x8 - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - SA8D_8x8 - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - SA8D_8x8 - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - SA8D_8x8 - movd eax, m12 - RET - -cglobal pixel_sa8d_16x8, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - movd eax, m12 - RET - -cglobal pixel_sa8d_16x32, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - movd eax, m12 - RET - -cglobal pixel_sa8d_16x64, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - movd eax, m12 - RET - -cglobal pixel_sa8d_24x32, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - SA8D_8x8 - sub r0, 8*SIZEOF_PIXEL - sub r2, 8*SIZEOF_PIXEL - SA8D_8x8 - sub r0, 8*SIZEOF_PIXEL - sub r2, 8*SIZEOF_PIXEL - SA8D_8x8 - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - SA8D_8x8 - sub r0, 8*SIZEOF_PIXEL - sub r2, 8*SIZEOF_PIXEL - SA8D_8x8 - sub r0, 8*SIZEOF_PIXEL - sub r2, 8*SIZEOF_PIXEL - SA8D_8x8 - movd eax, m12 - RET - -cglobal pixel_sa8d_32x8, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - movd eax, m12 - RET - -cglobal pixel_sa8d_32x16, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - movd eax, m12 - RET - -cglobal pixel_sa8d_32x24, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - SA8D_8x8 - sub r0, 8*SIZEOF_PIXEL - sub r2, 8*SIZEOF_PIXEL - SA8D_8x8 - sub r0, 8*SIZEOF_PIXEL - sub r2, 8*SIZEOF_PIXEL - SA8D_8x8 - sub r0, 8*SIZEOF_PIXEL - sub r2, 8*SIZEOF_PIXEL - SA8D_8x8 - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_8x8 - movd eax, m12 - RET - -cglobal pixel_sa8d_32x32, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - movd eax, m12 - RET - -cglobal pixel_sa8d_32x64, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - movd eax, m12 - RET - -cglobal pixel_sa8d_48x64, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - movd eax, m12 - RET - -cglobal pixel_sa8d_64x16, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - movd eax, m12 - RET - -cglobal pixel_sa8d_64x32, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - movd eax, m12 - RET - -cglobal pixel_sa8d_64x48, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - movd eax, m12 - RET - -cglobal pixel_sa8d_64x64, 4,8,13 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - pxor m12, m12 -%if vertical == 0 - mova m7, [hmul_8p] -%endif - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - add r2, 16*SIZEOF_PIXEL - add r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - lea r4, [8*r1] - lea r5, [8*r3] - sub r0, r4 - sub r2, r5 - sub r2, 16*SIZEOF_PIXEL - sub r0, 16*SIZEOF_PIXEL - lea r4, [3*r1] - lea r5, [3*r3] - SA8D_16x16 - movd eax, m12 - RET - -%else ; ARCH_X86_32 -%if mmsize == 16 -cglobal pixel_sa8d_8x8_internal - %define spill0 [esp+4] - %define spill1 [esp+20] - %define spill2 [esp+36] -%if vertical - LOAD_DIFF_8x4P 0, 1, 2, 3, 4, 5, 6, r0, r2, 1 - HADAMARD4_2D 0, 1, 2, 3, 4 - movdqa spill0, m3 - LOAD_DIFF_8x4P 4, 5, 6, 7, 3, 3, 2, r0, r2, 1 - HADAMARD4_2D 4, 5, 6, 7, 3 - HADAMARD2_2D 0, 4, 1, 5, 3, qdq, amax - movdqa m3, spill0 - paddw m0, m1 - HADAMARD2_2D 2, 6, 3, 7, 5, qdq, amax -%else ; mmsize == 8 - mova m7, [hmul_8p] - LOAD_SUMSUB_8x4P 0, 1, 2, 3, 5, 6, 7, r0, r2, 1 - ; could do first HADAMARD4_V here to save spilling later - ; surprisingly, not a win on conroe or even p4 - mova spill0, m2 - mova spill1, m3 - mova spill2, m1 - SWAP 1, 7 - LOAD_SUMSUB_8x4P 4, 5, 6, 7, 2, 3, 1, r0, r2, 1 - HADAMARD4_V 4, 5, 6, 7, 3 - mova m1, spill2 - mova m2, spill0 - mova m3, spill1 - mova spill0, m6 - mova spill1, m7 - HADAMARD4_V 0, 1, 2, 3, 7 - SUMSUB_BADC w, 0, 4, 1, 5, 7 - HADAMARD 2, sumsub, 0, 4, 7, 6 - HADAMARD 2, sumsub, 1, 5, 7, 6 - HADAMARD 1, amax, 0, 4, 7, 6 - HADAMARD 1, amax, 1, 5, 7, 6 - mova m6, spill0 - mova m7, spill1 - paddw m0, m1 - SUMSUB_BADC w, 2, 6, 3, 7, 4 - HADAMARD 2, sumsub, 2, 6, 4, 5 - HADAMARD 2, sumsub, 3, 7, 4, 5 - HADAMARD 1, amax, 2, 6, 4, 5 - HADAMARD 1, amax, 3, 7, 4, 5 -%endif ; sse2/non-sse2 - paddw m0, m2 - paddw m0, m3 - SAVE_MM_PERMUTATION - ret -%endif ; ifndef mmx2 - -cglobal pixel_sa8d_8x8_internal2 - %define spill0 [esp+4] - LOAD_DIFF_8x4P 0, 1, 2, 3, 4, 5, 6, r0, r2, 1 - HADAMARD4_2D 0, 1, 2, 3, 4 - movdqa spill0, m3 - LOAD_DIFF_8x4P 4, 5, 6, 7, 3, 3, 2, r0, r2, 1 - HADAMARD4_2D 4, 5, 6, 7, 3 - HADAMARD2_2D 0, 4, 1, 5, 3, qdq, amax - movdqa m3, spill0 - paddw m0, m1 - HADAMARD2_2D 2, 6, 3, 7, 5, qdq, amax - paddw m0, m2 - paddw m0, m3 - SAVE_MM_PERMUTATION - ret - -cglobal pixel_sa8d_8x8, 4,7 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 48 - lea r4, [3*r1] - lea r5, [3*r3] - call pixel_sa8d_8x8_internal -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%else - HADDW m0, m1 -%endif ; HIGH_BIT_DEPTH - movd eax, m0 - add eax, 1 - shr eax, 1 - mov esp, r6 - RET - -cglobal pixel_sa8d_16x16, 4,7 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - lea r4, [3*r1] - lea r5, [3*r3] - call pixel_sa8d_8x8_internal -%if mmsize == 8 - lea r0, [r0+4*r1] - lea r2, [r2+4*r3] -%endif -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal -%if mmsize == 8 - lea r0, [r0+4*r1] - lea r2, [r2+4*r3] -%else - SA8D_INTER -%endif - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal -%if HIGH_BIT_DEPTH - SA8D_INTER -%else ; !HIGH_BIT_DEPTH - paddusw m0, [esp+64-mmsize] -%if mmsize == 16 - HADDUW m0, m1 -%else - mova m2, [esp+48] - pxor m7, m7 - mova m1, m0 - mova m3, m2 - punpcklwd m0, m7 - punpckhwd m1, m7 - punpcklwd m2, m7 - punpckhwd m3, m7 - paddd m0, m1 - paddd m2, m3 - paddd m0, m2 - HADDD m0, m1 -%endif -%endif ; HIGH_BIT_DEPTH - movd eax, m0 - add eax, 1 - shr eax, 1 - mov esp, r6 - RET - -cglobal pixel_sa8d_8x16, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_8x32, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_16x8, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_16x32, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_16x64, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_24x32, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_32x8, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_32x16, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_32x24, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 - HADDUW m0, m1 - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_32x32, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_32x64, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_48x64, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_64x16, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_64x32, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_64x48, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET - -cglobal pixel_sa8d_64x64, 4,7,8 - FIX_STRIDES r1, r3 - mov r6, esp - and esp, ~15 - sub esp, 64 - - lea r4, [r1 + 2*r1] - lea r5, [r3 + 2*r3] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [rsp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - mov dword [esp+36], r4d - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - lea r0, [r0 + r1*8] - lea r2, [r2 + r3*8] - mov [r6+20], r0 - mov [r6+28], r2 - - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 8*SIZEOF_PIXEL - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 16*SIZEOF_PIXEL - add r2, 16*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 24*SIZEOF_PIXEL - add r2, 24*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 32*SIZEOF_PIXEL - add r2, 32*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 40*SIZEOF_PIXEL - add r2, 40*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - AVG_16x16 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 48*SIZEOF_PIXEL - add r2, 48*SIZEOF_PIXEL - lea r4, [r1 + 2*r1] - call pixel_sa8d_8x8_internal2 -%if HIGH_BIT_DEPTH - HADDUW m0, m1 -%endif - mova [esp+48], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+48], m0 - - mov r0, [r6+20] - mov r2, [r6+28] - add r0, 56*SIZEOF_PIXEL - add r2, 56*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal2 - SA8D_INTER - mova [esp+64-mmsize], m0 - call pixel_sa8d_8x8_internal2 - SA8D_INTER -%if HIGH_BIT_DEPTH == 0 - HADDUW m0, m1 -%endif - movd r4d, m0 - add r4d, 1 - shr r4d, 1 - add r4d, dword [esp+36] - mov eax, r4d - mov esp, r6 - RET -%endif ; !ARCH_X86_64 -%endmacro ; SA8D - - -%if ARCH_X86_64 == 1 && BIT_DEPTH == 12 -INIT_YMM avx2 -cglobal sa8d_8x8_12bit - pmovzxwd m0, [r0] - pmovzxwd m9, [r2] - psubd m0, m9 - - pmovzxwd m1, [r0 + r1] - pmovzxwd m9, [r2 + r3] - psubd m1, m9 - - pmovzxwd m2, [r0 + r1 * 2] - pmovzxwd m9, [r2 + r3 * 2] - psubd m2, m9 - - pmovzxwd m8, [r0 + r4] - pmovzxwd m9, [r2 + r5] - psubd m8, m9 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - - pmovzxwd m4, [r0] - pmovzxwd m9, [r2] - psubd m4, m9 - - pmovzxwd m5, [r0 + r1] - pmovzxwd m9, [r2 + r3] - psubd m5, m9 - - pmovzxwd m3, [r0 + r1 * 2] - pmovzxwd m9, [r2 + r3 * 2] - psubd m3, m9 - - pmovzxwd m7, [r0 + r4] - pmovzxwd m9, [r2 + r5] - psubd m7, m9 - - mova m6, m0 - paddd m0, m1 - psubd m1, m6 - mova m6, m2 - paddd m2, m8 - psubd m8, m6 - mova m6, m0 - - punpckldq m0, m1 - punpckhdq m6, m1 - - mova m1, m0 - paddd m0, m6 - psubd m6, m1 - mova m1, m2 - - punpckldq m2, m8 - punpckhdq m1, m8 - - mova m8, m2 - paddd m2, m1 - psubd m1, m8 - mova m8, m4 - paddd m4, m5 - psubd m5, m8 - mova m8, m3 - paddd m3, m7 - psubd m7, m8 - mova m8, m4 - - punpckldq m4, m5 - punpckhdq m8, m5 - - mova m5, m4 - paddd m4, m8 - psubd m8, m5 - mova m5, m3 - punpckldq m3, m7 - punpckhdq m5, m7 - - mova m7, m3 - paddd m3, m5 - psubd m5, m7 - mova m7, m0 - paddd m0, m2 - psubd m2, m7 - mova m7, m6 - paddd m6, m1 - psubd m1, m7 - mova m7, m0 - - punpcklqdq m0, m2 - punpckhqdq m7, m2 - - mova m2, m0 - paddd m0, m7 - psubd m7, m2 - mova m2, m6 - - punpcklqdq m6, m1 - punpckhqdq m2, m1 - - mova m1, m6 - paddd m6, m2 - psubd m2, m1 - mova m1, m4 - paddd m4, m3 - psubd m3, m1 - mova m1, m8 - paddd m8, m5 - psubd m5, m1 - mova m1, m4 - - punpcklqdq m4, m3 - punpckhqdq m1, m3 - - mova m3, m4 - paddd m4, m1 - psubd m1, m3 - mova m3, m8 - - punpcklqdq m8, m5 - punpckhqdq m3, m5 - - mova m5, m8 - paddd m8, m3 - psubd m3, m5 - mova m5, m0 - paddd m0, m4 - psubd m4, m5 - mova m5, m7 - paddd m7, m1 - psubd m1, m5 - mova m5, m0 - - vinserti128 m0, m0, xm4, 1 - vperm2i128 m5, m5, m4, 00110001b - - pxor m4, m4 - psubd m4, m0 - pmaxsd m0, m4 - pxor m4, m4 - psubd m4, m5 - pmaxsd m5, m4 - pmaxsd m0, m5 - mova m4, m7 - - vinserti128 m7, m7, xm1, 1 - vperm2i128 m4, m4, m1, 00110001b - - pxor m1, m1 - psubd m1, m7 - pmaxsd m7, m1 - pxor m1, m1 - psubd m1, m4 - pmaxsd m4, m1 - pmaxsd m7, m4 - mova m1, m6 - paddd m6, m8 - psubd m8, m1 - mova m1, m2 - paddd m2, m3 - psubd m3, m1 - mova m1, m6 - - vinserti128 m6, m6, xm8, 1 - vperm2i128 m1, m1, m8, 00110001b - - pxor m8, m8 - psubd m8, m6 - pmaxsd m6, m8 - pxor m8, m8 - psubd m8, m1 - pmaxsd m1, m8 - pmaxsd m6, m1 - mova m8, m2 - - vinserti128 m2, m2, xm3, 1 - vperm2i128 m8, m8, m3, 00110001b - - pxor m3, m3 - psubd m3, m2 - pmaxsd m2, m3 - pxor m3, m3 - psubd m3, m8 - pmaxsd m8, m3 - pmaxsd m2, m8 - paddd m0, m6 - paddd m0, m7 - paddd m0, m2 - ret - -cglobal pixel_sa8d_8x8, 4,6,10 - add r1d, r1d - add r3d, r3d - lea r4, [r1 + r1 * 2] - lea r5, [r3 + r3 * 2] - - call sa8d_8x8_12bit - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - movd eax, xm0 - add eax, 1 - shr eax, 1 - RET - -cglobal pixel_sa8d_8x16, 4,7,11 - add r1d, r1d - add r3d, r3d - lea r4, [r1 + r1 * 2] - lea r5, [r3 + r3 * 2] - pxor m10, m10 - - call sa8d_8x8_12bit - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm10, xm0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm0, xm10 - movd eax, xm0 - RET - -cglobal pixel_sa8d_16x16, 4,8,11 - add r1d, r1d - add r3d, r3d - lea r4, [r1 + r1 * 2] - lea r5, [r3 + r3 * 2] - mov r6, r0 - mov r7, r2 - pxor m10, m10 - - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - movd eax, xm0 - add eax, 1 - shr eax, 1 - RET - -cglobal pixel_sa8d_16x32, 4,8,12 - add r1d, r1d - add r3d, r3d - lea r4, [r1 + r1 * 2] - lea r5, [r3 + r3 * 2] - mov r6, r0 - mov r7, r2 - pxor m10, m10 - pxor m11, m11 - - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - lea r6, [r6 + r1 * 8] - lea r6, [r6 + r1 * 8] - lea r7, [r7 + r3 * 8] - lea r7, [r7 + r3 * 8] - pxor m10, m10 - mov r0, r6 - mov r2, r7 - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - movd eax, xm11 - RET - -cglobal pixel_sa8d_32x32, 4,8,12 - add r1d, r1d - add r3d, r3d - lea r4, [r1 + r1 * 2] - lea r5, [r3 + r3 * 2] - mov r6, r0 - mov r7, r2 - pxor m10, m10 - pxor m11, m11 - - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - lea r6, [r6 + r1 * 8] - lea r6, [r6 + r1 * 8] - lea r7, [r7 + r3 * 8] - lea r7, [r7 + r3 * 8] - pxor m10, m10 - mov r0, r6 - mov r2, r7 - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - movd eax, xm11 - RET - -cglobal pixel_sa8d_32x64, 4,8,12 - add r1d, r1d - add r3d, r3d - lea r4, [r1 + r1 * 2] - lea r5, [r3 + r3 * 2] - mov r6, r0 - mov r7, r2 - pxor m10, m10 - pxor m11, m11 - - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - lea r6, [r6 + r1 * 8] - lea r6, [r6 + r1 * 8] - lea r7, [r7 + r3 * 8] - lea r7, [r7 + r3 * 8] - pxor m10, m10 - mov r0, r6 - mov r2, r7 - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - lea r6, [r6 + r1 * 8] - lea r6, [r6 + r1 * 8] - lea r7, [r7 + r3 * 8] - lea r7, [r7 + r3 * 8] - pxor m10, m10 - mov r0, r6 - mov r2, r7 - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - lea r6, [r6 + r1 * 8] - lea r6, [r6 + r1 * 8] - lea r7, [r7 + r3 * 8] - lea r7, [r7 + r3 * 8] - pxor m10, m10 - mov r0, r6 - mov r2, r7 - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - movd eax, xm11 - RET - -cglobal pixel_sa8d_64x64, 4,8,12 - add r1d, r1d - add r3d, r3d - lea r4, [r1 + r1 * 2] - lea r5, [r3 + r3 * 2] - mov r6, r0 - mov r7, r2 - pxor m10, m10 - pxor m11, m11 - - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 64] - lea r2, [r7 + 64] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 80] - lea r2, [r7 + 80] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 96] - lea r2, [r7 + 96] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 112] - lea r2, [r7 + 112] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - lea r6, [r6 + r1 * 8] - lea r6, [r6 + r1 * 8] - lea r7, [r7 + r3 * 8] - lea r7, [r7 + r3 * 8] - pxor m10, m10 - mov r0, r6 - mov r2, r7 - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 64] - lea r2, [r7 + 64] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 80] - lea r2, [r7 + 80] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 96] - lea r2, [r7 + 96] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 112] - lea r2, [r7 + 112] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - lea r6, [r6 + r1 * 8] - lea r6, [r6 + r1 * 8] - lea r7, [r7 + r3 * 8] - lea r7, [r7 + r3 * 8] - pxor m10, m10 - mov r0, r6 - mov r2, r7 - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 64] - lea r2, [r7 + 64] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 80] - lea r2, [r7 + 80] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 96] - lea r2, [r7 + 96] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 112] - lea r2, [r7 + 112] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - lea r6, [r6 + r1 * 8] - lea r6, [r6 + r1 * 8] - lea r7, [r7 + r3 * 8] - lea r7, [r7 + r3 * 8] - pxor m10, m10 - mov r0, r6 - mov r2, r7 - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 64] - lea r2, [r7 + 64] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 80] - lea r2, [r7 + 80] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - - pxor m10, m10 - lea r0, [r6 + 96] - lea r2, [r7 + 96] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r6 + 112] - lea r2, [r7 + 112] - call sa8d_8x8_12bit - paddd m10, m0 - - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - call sa8d_8x8_12bit - paddd m0, m10 - - vextracti128 xm6, m0, 1 - paddd xm0, xm6 - - movhlps xm6, xm0 - paddd xm0, xm6 - - pshuflw xm6, xm0, 0Eh - paddd xm0, xm6 - paddd xm0, [pd_1] - psrld xm0, 1 - paddd xm11, xm0 - movd eax, xm11 - RET -%endif - - -;============================================================================= -; INTRA SATD -;============================================================================= -%define TRANS TRANS_SSE2 -%define DIFFOP DIFF_UNPACK_SSE2 -%define LOAD_SUMSUB_8x4P LOAD_DIFF_8x4P -%define LOAD_SUMSUB_16P LOAD_SUMSUB_16P_SSE2 -%define movdqa movaps ; doesn't hurt pre-nehalem, might as well save size -%define movdqu movups -%define punpcklqdq movlhps -INIT_XMM sse2 -%if BIT_DEPTH <= 10 -SA8D -%endif -SATDS_SSE2 - -%if HIGH_BIT_DEPTH == 0 -INIT_XMM ssse3,atom -SATDS_SSE2 -SA8D -%endif - -%define DIFFOP DIFF_SUMSUB_SSSE3 -%define LOAD_DUP_4x8P LOAD_DUP_4x8P_CONROE -%if HIGH_BIT_DEPTH == 0 -%define LOAD_SUMSUB_8x4P LOAD_SUMSUB_8x4P_SSSE3 -%define LOAD_SUMSUB_16P LOAD_SUMSUB_16P_SSSE3 -%endif -INIT_XMM ssse3 -%if BIT_DEPTH <= 10 -SA8D -%endif -SATDS_SSE2 -%undef movdqa ; nehalem doesn't like movaps -%undef movdqu ; movups -%undef punpcklqdq ; or movlhps - -%define TRANS TRANS_SSE4 -%define LOAD_DUP_4x8P LOAD_DUP_4x8P_PENRYN -INIT_XMM sse4 -%if BIT_DEPTH <= 10 -SA8D -%endif -SATDS_SSE2 - -; Sandy/Ivy Bridge and Bulldozer do movddup in the load unit, so -; it's effectively free. -%define LOAD_DUP_4x8P LOAD_DUP_4x8P_CONROE -INIT_XMM avx -SA8D -SATDS_SSE2 - -%define TRANS TRANS_XOP -INIT_XMM xop -%if BIT_DEPTH <= 10 -SA8D -%endif -SATDS_SSE2 - -%if HIGH_BIT_DEPTH == 0 -%define LOAD_SUMSUB_8x4P LOAD_SUMSUB8_16x4P_AVX2 -%define LOAD_DUP_4x8P LOAD_DUP_4x16P_AVX2 -%define TRANS TRANS_SSE4 - -%macro LOAD_SUMSUB_8x8P_AVX2 7 ; 4*dst, 2*tmp, mul] - movddup xm%1, [r0] - movddup xm%3, [r2] - movddup xm%2, [r0+4*r1] - movddup xm%5, [r2+4*r3] - vinserti128 m%1, m%1, xm%2, 1 - vinserti128 m%3, m%3, xm%5, 1 - - movddup xm%2, [r0+r1] - movddup xm%4, [r2+r3] - movddup xm%5, [r0+r4] - movddup xm%6, [r2+r5] - vinserti128 m%2, m%2, xm%5, 1 - vinserti128 m%4, m%4, xm%6, 1 - - DIFF_SUMSUB_SSSE3 %1, %3, %2, %4, %7 - lea r0, [r0+2*r1] - lea r2, [r2+2*r3] - - movddup xm%3, [r0] - movddup xm%5, [r0+4*r1] - vinserti128 m%3, m%3, xm%5, 1 - - movddup xm%5, [r2] - movddup xm%4, [r2+4*r3] - vinserti128 m%5, m%5, xm%4, 1 - - movddup xm%4, [r0+r1] - movddup xm%6, [r0+r4] - vinserti128 m%4, m%4, xm%6, 1 - - movq xm%6, [r2+r3] - movhps xm%6, [r2+r5] - vpermq m%6, m%6, q1100 - DIFF_SUMSUB_SSSE3 %3, %5, %4, %6, %7 -%endmacro - -%macro SATD_START_AVX2 2-3 0 - FIX_STRIDES r1, r3 -%if %3 - mova %2, [hmul_8p] - lea r4, [5*r1] - lea r5, [5*r3] -%else - mova %2, [hmul_16p] - lea r4, [3*r1] - lea r5, [3*r3] -%endif - pxor %1, %1 -%endmacro - -%define TRANS TRANS_SSE4 -INIT_YMM avx2 -cglobal pixel_satd_16x8_internal - LOAD_SUMSUB_16x4P_AVX2 0, 1, 2, 3, 4, 5, 7, r0, r2, 1 - SATD_8x4_SSE 0, 0, 1, 2, 3, 4, 5, 6 - LOAD_SUMSUB_16x4P_AVX2 0, 1, 2, 3, 4, 5, 7, r0, r2, 0 - SATD_8x4_SSE 0, 0, 1, 2, 3, 4, 5, 6 - ret - -cglobal SatdCalculation_16x16, 4,6,8 - SATD_START_AVX2 m6, m7 - call pixel_satd_16x8_internal - lea r0, [r0+4*r1] - lea r2, [r2+4*r3] -pixel_satd_16x8_internal: - call pixel_satd_16x8_internal - vextracti128 xm0, m6, 1 - paddw xm0, xm6 - SATD_END_SSE2 xm0 - RET - -cglobal pixel_satd_16x8, 4,6,8 - SATD_START_AVX2 m6, m7 - jmp pixel_satd_16x8_internal - -cglobal pixel_satd_8x8_internal - LOAD_SUMSUB_8x8P_AVX2 0, 1, 2, 3, 4, 5, 7 - SATD_8x4_SSE 0, 0, 1, 2, 3, 4, 5, 6 - ret - -cglobal pixel_satd_8x16, 4,6,8 - SATD_START_AVX2 m6, m7, 1 - call pixel_satd_8x8_internal - lea r0, [r0+2*r1] - lea r2, [r2+2*r3] - lea r0, [r0+4*r1] - lea r2, [r2+4*r3] - call pixel_satd_8x8_internal - vextracti128 xm0, m6, 1 - paddw xm0, xm6 - SATD_END_SSE2 xm0 - RET - -cglobal pixel_satd_8x8, 4,6,8 - SATD_START_AVX2 m6, m7, 1 - call pixel_satd_8x8_internal - vextracti128 xm0, m6, 1 - paddw xm0, xm6 - SATD_END_SSE2 xm0 - RET - -cglobal pixel_sa8d_8x8_internal - LOAD_SUMSUB_8x8P_AVX2 0, 1, 2, 3, 4, 5, 7 - HADAMARD4_V 0, 1, 2, 3, 4 - HADAMARD 8, sumsub, 0, 1, 4, 5 - HADAMARD 8, sumsub, 2, 3, 4, 5 - HADAMARD 2, sumsub, 0, 1, 4, 5 - HADAMARD 2, sumsub, 2, 3, 4, 5 - HADAMARD 1, amax, 0, 1, 4, 5 - HADAMARD 1, amax, 2, 3, 4, 5 - paddw m6, m0 - paddw m6, m2 - ret - -cglobal pixel_sa8d_8x8, 4,6,8 - SATD_START_AVX2 m6, m7, 1 - call pixel_sa8d_8x8_internal - vextracti128 xm1, m6, 1 - paddw xm6, xm1 - HADDW xm6, xm1 - movd eax, xm6 - add eax, 1 - shr eax, 1 - RET - -cglobal pixel_sa8d_16x16, 4,6,8 - SATD_START_AVX2 m6, m7, 1 - - call pixel_sa8d_8x8_internal ; pix[0] - - sub r0, r1 - sub r0, r1 - add r0, 8*SIZEOF_PIXEL - sub r2, r3 - sub r2, r3 - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal ; pix[8] - - add r0, r4 - add r0, r1 - add r2, r5 - add r2, r3 - call pixel_sa8d_8x8_internal ; pix[8*stride+8] - - sub r0, r1 - sub r0, r1 - sub r0, 8*SIZEOF_PIXEL - sub r2, r3 - sub r2, r3 - sub r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal ; pix[8*stride] - - ; TODO: analyze Dynamic Range - vextracti128 xm0, m6, 1 - paddusw xm6, xm0 - HADDUW xm6, xm0 - movd eax, xm6 - add eax, 1 - shr eax, 1 - RET - -cglobal pixel_sa8d_16x16_internal - call pixel_sa8d_8x8_internal ; pix[0] - - sub r0, r1 - sub r0, r1 - add r0, 8*SIZEOF_PIXEL - sub r2, r3 - sub r2, r3 - add r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal ; pix[8] - - add r0, r4 - add r0, r1 - add r2, r5 - add r2, r3 - call pixel_sa8d_8x8_internal ; pix[8*stride+8] - - sub r0, r1 - sub r0, r1 - sub r0, 8*SIZEOF_PIXEL - sub r2, r3 - sub r2, r3 - sub r2, 8*SIZEOF_PIXEL - call pixel_sa8d_8x8_internal ; pix[8*stride] - - ; TODO: analyze Dynamic Range - vextracti128 xm0, m6, 1 - paddusw xm6, xm0 - HADDUW xm6, xm0 - movd eax, xm6 - add eax, 1 - shr eax, 1 - ret - -%if ARCH_X86_64 -cglobal pixel_sa8d_32x32, 4,8,8 - ; TODO: R6 is RAX on x64 platform, so we use it directly - - SATD_START_AVX2 m6, m7, 1 - xor r7d, r7d - - call pixel_sa8d_16x16_internal ; [0] - pxor m6, m6 - add r7d, eax - - add r0, r4 - add r0, r1 - add r2, r5 - add r2, r3 - call pixel_sa8d_16x16_internal ; [2] - pxor m6, m6 - add r7d, eax - - lea eax, [r4 * 5 - 16] - sub r0, rax - sub r0, r1 - lea eax, [r5 * 5 - 16] - sub r2, rax - sub r2, r3 - call pixel_sa8d_16x16_internal ; [1] - pxor m6, m6 - add r7d, eax - - add r0, r4 - add r0, r1 - add r2, r5 - add r2, r3 - call pixel_sa8d_16x16_internal ; [3] - add eax, r7d - RET -%endif ; ARCH_X86_64=1 -%endif ; HIGH_BIT_DEPTH - -%macro HMAXABSW2 4 ; a, b, tmp1, tmp2 - pabsw m%1, m%1 - pabsw m%2, m%2 - psrldq m%3, m%1, 2 - psrld m%4, m%2, 16 - pmaxsw m%1, m%3 - pmaxsw m%2, m%4 -%endmacro - -;;--------------------------------------------------------------- -;; SATD AVX2 -;; int pixel_satd(const pixel*, intptr_t, const pixel*, intptr_t) -;;--------------------------------------------------------------- -;; r0 - pix0 -;; r1 - pix0Stride -;; r2 - pix1 -;; r3 - pix1Stride - -%if ARCH_X86_64 == 1 && HIGH_BIT_DEPTH == 0 -INIT_YMM avx2 -cglobal calc_satd_16x8 ; function to compute satd cost for 16 columns, 8 rows - pxor m6, m6 - vbroadcasti128 m0, [r0] - vbroadcasti128 m4, [r2] - vbroadcasti128 m1, [r0 + r1] - vbroadcasti128 m5, [r2 + r3] - pmaddubsw m4, m7 - pmaddubsw m0, m7 - pmaddubsw m5, m7 - pmaddubsw m1, m7 - psubw m0, m4 - psubw m1, m5 - vbroadcasti128 m2, [r0 + r1 * 2] - vbroadcasti128 m4, [r2 + r3 * 2] - vbroadcasti128 m3, [r0 + r4] - vbroadcasti128 m5, [r2 + r5] - pmaddubsw m4, m7 - pmaddubsw m2, m7 - pmaddubsw m5, m7 - pmaddubsw m3, m7 - psubw m2, m4 - psubw m3, m5 - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - paddw m4, m0, m1 - psubw m1, m1, m0 - paddw m0, m2, m3 - psubw m3, m2 - paddw m2, m4, m0 - psubw m0, m4 - paddw m4, m1, m3 - psubw m3, m1 - pabsw m2, m2 - pabsw m0, m0 - pabsw m4, m4 - pabsw m3, m3 - pblendw m1, m2, m0, 10101010b - pslld m0, 16 - psrld m2, 16 - por m0, m2 - pmaxsw m1, m0 - paddw m6, m1 - pblendw m2, m4, m3, 10101010b - pslld m3, 16 - psrld m4, 16 - por m3, m4 - pmaxsw m2, m3 - paddw m6, m2 - vbroadcasti128 m1, [r0] - vbroadcasti128 m4, [r2] - vbroadcasti128 m2, [r0 + r1] - vbroadcasti128 m5, [r2 + r3] - pmaddubsw m4, m7 - pmaddubsw m1, m7 - pmaddubsw m5, m7 - pmaddubsw m2, m7 - psubw m1, m4 - psubw m2, m5 - vbroadcasti128 m0, [r0 + r1 * 2] - vbroadcasti128 m4, [r2 + r3 * 2] - vbroadcasti128 m3, [r0 + r4] - vbroadcasti128 m5, [r2 + r5] - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - pmaddubsw m4, m7 - pmaddubsw m0, m7 - pmaddubsw m5, m7 - pmaddubsw m3, m7 - psubw m0, m4 - psubw m3, m5 - paddw m4, m1, m2 - psubw m2, m1 - paddw m1, m0, m3 - psubw m3, m0 - paddw m0, m4, m1 - psubw m1, m4 - paddw m4, m2, m3 - psubw m3, m2 - pabsw m0, m0 - pabsw m1, m1 - pabsw m4, m4 - pabsw m3, m3 - pblendw m2, m0, m1, 10101010b - pslld m1, 16 - psrld m0, 16 - por m1, m0 - pmaxsw m2, m1 - paddw m6, m2 - pblendw m0, m4, m3, 10101010b - pslld m3, 16 - psrld m4, 16 - por m3, m4 - pmaxsw m0, m3 - paddw m6, m0 - vextracti128 xm0, m6, 1 - pmovzxwd m6, xm6 - pmovzxwd m0, xm0 - paddd m8, m6 - paddd m9, m0 - ret - -cglobal calc_satd_16x4 ; function to compute satd cost for 16 columns, 4 rows - pxor m6, m6 - vbroadcasti128 m0, [r0] - vbroadcasti128 m4, [r2] - vbroadcasti128 m1, [r0 + r1] - vbroadcasti128 m5, [r2 + r3] - pmaddubsw m4, m7 - pmaddubsw m0, m7 - pmaddubsw m5, m7 - pmaddubsw m1, m7 - psubw m0, m4 - psubw m1, m5 - vbroadcasti128 m2, [r0 + r1 * 2] - vbroadcasti128 m4, [r2 + r3 * 2] - vbroadcasti128 m3, [r0 + r4] - vbroadcasti128 m5, [r2 + r5] - pmaddubsw m4, m7 - pmaddubsw m2, m7 - pmaddubsw m5, m7 - pmaddubsw m3, m7 - psubw m2, m4 - psubw m3, m5 - paddw m4, m0, m1 - psubw m1, m1, m0 - paddw m0, m2, m3 - psubw m3, m2 - paddw m2, m4, m0 - psubw m0, m4 - paddw m4, m1, m3 - psubw m3, m1 - pabsw m2, m2 - pabsw m0, m0 - pabsw m4, m4 - pabsw m3, m3 - pblendw m1, m2, m0, 10101010b - pslld m0, 16 - psrld m2, 16 - por m0, m2 - pmaxsw m1, m0 - paddw m6, m1 - pblendw m2, m4, m3, 10101010b - pslld m3, 16 - psrld m4, 16 - por m3, m4 - pmaxsw m2, m3 - paddw m6, m2 - vextracti128 xm0, m6, 1 - pmovzxwd m6, xm6 - pmovzxwd m0, xm0 - paddd m8, m6 - paddd m9, m0 - ret - -cglobal pixel_satd_16x4, 4,6,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - - call calc_satd_16x4 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_16x12, 4,6,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - - call calc_satd_16x8 - call calc_satd_16x4 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_16x32, 4,6,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_16x64, 4,6,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_32x8, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_32x16, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_32x24, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_32x32, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_32x64, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 16] - lea r2, [r7 + 16] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_48x64, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_64x16, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_64x32, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_64x48, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -cglobal pixel_satd_64x64, 4,8,10 ; if WIN64 && cpuflag(avx2) - mova m7, [hmul_16p] - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m8, m8 - pxor m9, m9 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 16] - lea r2, [r7 + 16] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 32] - lea r2, [r7 + 32] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - lea r0, [r6 + 48] - lea r2, [r7 + 48] - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - paddd m8, m9 - vextracti128 xm0, m8, 1 - paddd xm0, xm8 - movhlps xm1, xm0 - paddd xm0, xm1 - pshuflw xm1, xm0, q0032 - paddd xm0, xm1 - movd eax, xm0 - RET - -%endif ; ARCH_X86_64 == 1 && HIGH_BIT_DEPTH == 0 -%if ARCH_X86_64 == 1 && HIGH_BIT_DEPTH == 1 -INIT_YMM avx2 -cglobal calc_satd_16x8 ; function to compute satd cost for 16 columns, 8 rows - ; rows 0-3 - movu m0, [r0] - movu m4, [r2] - psubw m0, m4 - movu m1, [r0 + r1] - movu m5, [r2 + r3] - psubw m1, m5 - movu m2, [r0 + r1 * 2] - movu m4, [r2 + r3 * 2] - psubw m2, m4 - movu m3, [r0 + r4] - movu m5, [r2 + r5] - psubw m3, m5 - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - paddw m4, m0, m1 - psubw m1, m0 - paddw m0, m2, m3 - psubw m3, m2 - punpckhwd m2, m4, m1 - punpcklwd m4, m1 - punpckhwd m1, m0, m3 - punpcklwd m0, m3 - paddw m3, m4, m0 - psubw m0, m4 - paddw m4, m2, m1 - psubw m1, m2 - punpckhdq m2, m3, m0 - punpckldq m3, m0 - paddw m0, m3, m2 - psubw m2, m3 - punpckhdq m3, m4, m1 - punpckldq m4, m1 - paddw m1, m4, m3 - psubw m3, m4 - punpckhqdq m4, m0, m1 - punpcklqdq m0, m1 - pabsw m0, m0 - pabsw m4, m4 - pmaxsw m0, m0, m4 - punpckhqdq m1, m2, m3 - punpcklqdq m2, m3 - pabsw m2, m2 - pabsw m1, m1 - pmaxsw m2, m1 - pxor m7, m7 - mova m1, m0 - punpcklwd m1, m7 - paddd m6, m1 - mova m1, m0 - punpckhwd m1, m7 - paddd m6, m1 - pxor m7, m7 - mova m1, m2 - punpcklwd m1, m7 - paddd m6, m1 - mova m1, m2 - punpckhwd m1, m7 - paddd m6, m1 - ; rows 4-7 - movu m0, [r0] - movu m4, [r2] - psubw m0, m4 - movu m1, [r0 + r1] - movu m5, [r2 + r3] - psubw m1, m5 - movu m2, [r0 + r1 * 2] - movu m4, [r2 + r3 * 2] - psubw m2, m4 - movu m3, [r0 + r4] - movu m5, [r2 + r5] - psubw m3, m5 - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - paddw m4, m0, m1 - psubw m1, m0 - paddw m0, m2, m3 - psubw m3, m2 - punpckhwd m2, m4, m1 - punpcklwd m4, m1 - punpckhwd m1, m0, m3 - punpcklwd m0, m3 - paddw m3, m4, m0 - psubw m0, m4 - paddw m4, m2, m1 - psubw m1, m2 - punpckhdq m2, m3, m0 - punpckldq m3, m0 - paddw m0, m3, m2 - psubw m2, m3 - punpckhdq m3, m4, m1 - punpckldq m4, m1 - paddw m1, m4, m3 - psubw m3, m4 - punpckhqdq m4, m0, m1 - punpcklqdq m0, m1 - pabsw m0, m0 - pabsw m4, m4 - pmaxsw m0, m0, m4 - punpckhqdq m1, m2, m3 - punpcklqdq m2, m3 - pabsw m2, m2 - pabsw m1, m1 - pmaxsw m2, m1 - pxor m7, m7 - mova m1, m0 - punpcklwd m1, m7 - paddd m6, m1 - mova m1, m0 - punpckhwd m1, m7 - paddd m6, m1 - pxor m7, m7 - mova m1, m2 - punpcklwd m1, m7 - paddd m6, m1 - mova m1, m2 - punpckhwd m1, m7 - paddd m6, m1 - ret - -cglobal calc_satd_16x4 ; function to compute satd cost for 16 columns, 4 rows - ; rows 0-3 - movu m0, [r0] - movu m4, [r2] - psubw m0, m4 - movu m1, [r0 + r1] - movu m5, [r2 + r3] - psubw m1, m5 - movu m2, [r0 + r1 * 2] - movu m4, [r2 + r3 * 2] - psubw m2, m4 - movu m3, [r0 + r4] - movu m5, [r2 + r5] - psubw m3, m5 - lea r0, [r0 + r1 * 4] - lea r2, [r2 + r3 * 4] - paddw m4, m0, m1 - psubw m1, m0 - paddw m0, m2, m3 - psubw m3, m2 - punpckhwd m2, m4, m1 - punpcklwd m4, m1 - punpckhwd m1, m0, m3 - punpcklwd m0, m3 - paddw m3, m4, m0 - psubw m0, m4 - paddw m4, m2, m1 - psubw m1, m2 - punpckhdq m2, m3, m0 - punpckldq m3, m0 - paddw m0, m3, m2 - psubw m2, m3 - punpckhdq m3, m4, m1 - punpckldq m4, m1 - paddw m1, m4, m3 - psubw m3, m4 - punpckhqdq m4, m0, m1 - punpcklqdq m0, m1 - pabsw m0, m0 - pabsw m4, m4 - pmaxsw m0, m0, m4 - punpckhqdq m1, m2, m3 - punpcklqdq m2, m3 - pabsw m2, m2 - pabsw m1, m1 - pmaxsw m2, m1 - pxor m7, m7 - mova m1, m0 - punpcklwd m1, m7 - paddd m6, m1 - mova m1, m0 - punpckhwd m1, m7 - paddd m6, m1 - pxor m7, m7 - mova m1, m2 - punpcklwd m1, m7 - paddd m6, m1 - mova m1, m2 - punpckhwd m1, m7 - paddd m6, m1 - ret - -cglobal pixel_satd_16x4, 4,6,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - - call calc_satd_16x4 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_16x8, 4,6,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_16x12, 4,6,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - - call calc_satd_16x8 - call calc_satd_16x4 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_16x16, 4,6,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_16x32, 4,6,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_16x64, 4,6,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_32x8, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_32x16, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_32x24, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_32x32, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_32x64, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_48x64, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 64] - lea r2, [r7 + 64] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_64x16, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 64] - lea r2, [r7 + 64] - - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 96] - lea r2, [r7 + 96] - - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_64x32, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 64] - lea r2, [r7 + 64] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 96] - lea r2, [r7 + 96] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_64x48, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 64] - lea r2, [r7 + 64] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 96] - lea r2, [r7 + 96] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -cglobal pixel_satd_64x64, 4,8,8 - add r1d, r1d - add r3d, r3d - lea r4, [3 * r1] - lea r5, [3 * r3] - pxor m6, m6 - mov r6, r0 - mov r7, r2 - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 32] - lea r2, [r7 + 32] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 64] - lea r2, [r7 + 64] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - lea r0, [r6 + 96] - lea r2, [r7 + 96] - - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - call calc_satd_16x8 - - vextracti128 xm7, m6, 1 - paddd xm6, xm7 - pxor xm7, xm7 - movhlps xm7, xm6 - paddd xm6, xm7 - pshufd xm7, xm6, 1 - paddd xm6, xm7 - movd eax, xm6 - RET - -%endif ; ARCH_X86_64 == 1 && HIGH_BIT_DEPTH == 1 - - -;------------------------------------------------------------------------------------------------------------------------------------- -; pixel planeClipAndMax(pixel *src, intptr_t stride, int width, int height, uint64_t *outsum, const pixel minPix, const pixel maxPix) -;------------------------------------------------------------------------------------------------------------------------------------- - -%if HIGH_BIT_DEPTH == 1 && BIT_DEPTH == 10 -%macro LOAD_DIFF_AVX2 4 - movu %1, %3 - movu %2, %4 - psubw %1, %2 -%endmacro - -%macro LOAD_DIFF_8x4P_AVX2 6-8 r0,r2 ; 4x dest, 2x temp, 2x pointer - LOAD_DIFF_AVX2 xm%1, xm%5, [%7], [%8] - LOAD_DIFF_AVX2 xm%2, xm%6, [%7+r1], [%8+r3] - LOAD_DIFF_AVX2 xm%3, xm%5, [%7+2*r1], [%8+2*r3] - LOAD_DIFF_AVX2 xm%4, xm%6, [%7+r4], [%8+r5] - - ;lea %7, [%7+4*r1] - ;lea %8, [%8+4*r3] -%endmacro - -%if ARCH_X86_64 -INIT_YMM avx2 -cglobal pixel_satd_8x8, 4,4,7 - - FIX_STRIDES r1, r3 - pxor xm6, xm6 - - ; load_diff 0 & 4 - movu xm0, [r0] - movu xm1, [r2] - vinserti128 m0, m0, [r0 + r1 * 4], 1 - vinserti128 m1, m1, [r2 + r3 * 4], 1 - psubw m0, m1 - add r0, r1 - add r2, r3 - - ; load_diff 1 & 5 - movu xm1, [r0] - movu xm2, [r2] - vinserti128 m1, m1, [r0 + r1 * 4], 1 - vinserti128 m2, m2, [r2 + r3 * 4], 1 - psubw m1, m2 - add r0, r1 - add r2, r3 - - ; load_diff 2 & 6 - movu xm2, [r0] - movu xm3, [r2] - vinserti128 m2, m2, [r0 + r1 * 4], 1 - vinserti128 m3, m3, [r2 + r3 * 4], 1 - psubw m2, m3 - add r0, r1 - add r2, r3 - - ; load_diff 3 & 7 - movu xm3, [r0] - movu xm4, [r2] - vinserti128 m3, m3, [r0 + r1 * 4], 1 - vinserti128 m4, m4, [r2 + r3 * 4], 1 - psubw m3, m4 - - SATD_8x4_SSE vertical, 0, 1, 2, 3, 4, 5, 6 - - vextracti128 xm0, m6, 1 - paddw xm6, xm0 - HADDUW xm6, xm0 - movd eax, xm6 - RET - -INIT_XMM avx2 -cglobal pixel_sa8d_8x8_internal - lea r6, [r0+4*r1] - lea r7, [r2+4*r3] - LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - ;HADAMARD2_2D 0, 1, 2, 8, 6, wd - ;HADAMARD2_2D 4, 5, 3, 9, 6, wd - ;HADAMARD2_2D 0, 2, 1, 8, 6, dq - ;HADAMARD2_2D 4, 3, 5, 9, 6, dq - ;HADAMARD2_2D 0, 4, 2, 3, 6, qdq, amax - ;HADAMARD2_2D 1, 5, 8, 9, 6, qdq, amax - - paddw m0, m1 - paddw m0, m2 - paddw m0, m8 - SAVE_MM_PERMUTATION - ret - - -INIT_XMM avx2 -cglobal pixel_sa8d_8x8, 4,8,12 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - call pixel_sa8d_8x8_internal - HADDUW m0, m1 - movd eax, m0 - add eax, 1 - shr eax, 1 - RET - - -INIT_YMM avx2 -cglobal pixel_sa8d_16x16, 4,8,12 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - lea r6, [r0+4*r1] - lea r7, [r2+4*r3] - vbroadcasti128 m7, [pw_1] - - ; Top 16x8 - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] ; 10 bits - movu m5, [r2] - psubw m0, m5 ; 11 bits - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax ; 16 bits - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m10, m0, m2 - - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - lea r6, [r6+8*r1] - lea r7, [r7+8*r3] - - ; Bottom 16x8 - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] - movu m5, [r2] - psubw m0, m5 - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m10, m0 - paddd m10, m2 - - HADDD m10, m0 - - movd eax, xm10 - add eax, 1 - shr eax, 1 - RET - - -; TODO: optimize me, need more 2 of YMM registers because C model get partial result every 16x16 block -INIT_YMM avx2 -cglobal pixel_sa8d_32x32, 4,8,14 - FIX_STRIDES r1, r3 - lea r4, [3*r1] - lea r5, [3*r3] - lea r6, [r0+4*r1] - lea r7, [r2+4*r3] - vbroadcasti128 m7, [pw_1] - - - ;SA8D[16x8] ; pix[0] - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] - movu m5, [r2] - psubw m0, m5 - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m10, m0, m2 - - - ; SA8D[16x8] ; pix[16] - add r0, mmsize - add r2, mmsize - add r6, mmsize - add r7, mmsize - - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] - movu m5, [r2] - psubw m0, m5 - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m12, m0, m2 - - - ; SA8D[16x8] ; pix[8*stride+16] - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - lea r6, [r6+8*r1] - lea r7, [r7+8*r3] - - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] - movu m5, [r2] - psubw m0, m5 - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m12, m0 - paddd m12, m2 - - ; sum[1] - HADDD m12, m0 - - - ; SA8D[16x8] ; pix[8*stride] - sub r0, mmsize - sub r2, mmsize - sub r6, mmsize - sub r7, mmsize - - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] - movu m5, [r2] - psubw m0, m5 - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m10, m0 - paddd m10, m2 - - ; sum[0] - HADDD m10, m0 - punpckldq xm10, xm12 - - - ;SA8D[16x8] ; pix[16*stridr] - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - lea r6, [r6+8*r1] - lea r7, [r7+8*r3] - - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] - movu m5, [r2] - psubw m0, m5 - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m12, m0, m2 - - - ; SA8D[16x8] ; pix[16*stride+16] - add r0, mmsize - add r2, mmsize - add r6, mmsize - add r7, mmsize - - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] - movu m5, [r2] - psubw m0, m5 - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m13, m0, m2 - - - ; SA8D[16x8] ; pix[24*stride+16] - lea r0, [r0+8*r1] - lea r2, [r2+8*r3] - lea r6, [r6+8*r1] - lea r7, [r7+8*r3] - - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] - movu m5, [r2] - psubw m0, m5 - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m13, m0 - paddd m13, m2 - - ; sum[3] - HADDD m13, m0 - - - ; SA8D[16x8] ; pix[24*stride] - sub r0, mmsize - sub r2, mmsize - sub r6, mmsize - sub r7, mmsize - - ;LOAD_DIFF_8x4P_AVX2 0, 1, 2, 8, 5, 6, r0, r2 - movu m0, [r0] - movu m5, [r2] - psubw m0, m5 - movu m1, [r0 + r1] - movu m6, [r2 + r3] - psubw m1, m6 - movu m2, [r0 + r1 * 2] - movu m5, [r2 + r3 * 2] - psubw m2, m5 - movu m8, [r0 + r4] - movu m6, [r2 + r5] - psubw m8, m6 - - ;LOAD_DIFF_8x4P_AVX2 4, 5, 3, 9, 11, 6, r6, r7 - movu m4, [r6] - movu m11, [r7] - psubw m4, m11 - movu m5, [r6 + r1] - movu m6, [r7 + r3] - psubw m5, m6 - movu m3, [r6 + r1 * 2] - movu m11, [r7 + r3 * 2] - psubw m3, m11 - movu m9, [r6 + r4] - movu m6, [r7 + r5] - psubw m9, m6 - - HADAMARD8_2D 0, 1, 2, 8, 4, 5, 3, 9, 6, amax - pmaddwd m0, m7 - pmaddwd m1, m7 - pmaddwd m2, m7 - pmaddwd m8, m7 - paddd m0, m1 - paddd m2, m8 - paddd m12, m0 - paddd m12, m2 - - ; sum[2] - HADDD m12, m0 - punpckldq xm12, xm13 - - ; SA8D - punpcklqdq xm0, xm10, xm12 - paddd xm0, [pd_1] - psrld xm0, 1 - HADDD xm0, xm1 - - movd eax, xm0 - RET -%endif -%endif ; HIGH_BIT_DEPTH == 1 && BIT_DEPTH == 10 diff --git a/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h b/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h deleted file mode 100644 index 6fbf6388c..000000000 --- a/Source/Lib/ASM_AVX2/EbMeSatdCalculation_AVX2.h +++ /dev/null @@ -1,23 +0,0 @@ -/* -* Copyright(c) 2018 Intel Corporation -* SPDX - License - Identifier: BSD - 2 - Clause - Patent -*/ - -#ifndef EbMeSatdCalculation_AVX2_h -#define EbMeSatdCalculation_AVX2_h - -#include "EbDefinitions.h" -#ifdef __cplusplus -extern "C" { -#endif - -EB_U32 SatdCalculation_16x16_avx2( - EB_U8 *src, - EB_U32 srcStride, - EB_U8 *ref, - EB_U32 refStride); - -#ifdef __cplusplus -} -#endif -#endif // EbMeSadCalculation_C_h \ No newline at end of file diff --git a/Source/Lib/ASM_AVX2/const-a.asm b/Source/Lib/ASM_AVX2/const-a.asm deleted file mode 100644 index 81186de4e..000000000 --- a/Source/Lib/ASM_AVX2/const-a.asm +++ /dev/null @@ -1,136 +0,0 @@ -; -; Copyright(c) 2018 Intel Corporation -; SPDX - License - Identifier: BSD - 2 - Clause - Patent -; - -%include "x86inc.asm" - -SECTION_RODATA 64 - -;; 8-bit constants - -const pb_0, times 32 db 0 -const pb_1, times 32 db 1 -const pb_2, times 32 db 2 -const pb_3, times 32 db 3 -const pb_4, times 32 db 4 -const pb_8, times 32 db 8 -const pb_15, times 32 db 15 -const pb_16, times 32 db 16 -const pb_31, times 32 db 31 -const pb_32, times 32 db 32 -const pb_64, times 32 db 64 -const pb_124, times 32 db 124 -const pb_128, times 32 db 128 -const pb_a1, times 16 db 0xa1 - -const pb_01, times 8 db 0, 1 -const pb_0123, times 4 db 0, 1 - times 4 db 2, 3 -const hsub_mul, times 16 db 1, -1 -const pw_swap, times 2 db 6, 7, 4, 5, 2, 3, 0, 1 -const pb_unpackbd1, times 2 db 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 -const pb_unpackbd2, times 2 db 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7 -const pb_unpackwq1, times 1 db 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3 -const pb_unpackwq2, times 1 db 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7 -const pb_shuf8x8c, times 1 db 0, 0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6 -const pb_movemask, times 16 db 0x00 - times 16 db 0xFF - -const pb_movemask_32, times 32 db 0x00 - times 32 db 0xFF - times 32 db 0x00 - -const pb_0000000000000F0F, times 2 db 0xff, 0x00 - times 12 db 0x00 -const pb_000000000000000F, db 0xff - times 15 db 0x00 -const pb_shuf_off4, times 2 db 0, 4, 1, 5, 2, 6, 3, 7 -const pw_shuf_off4, times 1 db 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15 - -;; 16-bit constants - -const pw_n1, times 16 dw -1 -const pw_1, times 16 dw 1 -const pw_2, times 16 dw 2 -const pw_3, times 16 dw 3 -const pw_7, times 16 dw 7 -const pw_m2, times 8 dw -2 -const pw_4, times 8 dw 4 -const pw_8, times 8 dw 8 -const pw_16, times 16 dw 16 -const pw_15, times 16 dw 15 -const pw_31, times 16 dw 31 -const pw_32, times 16 dw 32 -const pw_64, times 8 dw 64 -const pw_128, times 16 dw 128 -const pw_256, times 16 dw 256 -const pw_257, times 16 dw 257 -const pw_512, times 16 dw 512 -const pw_1023, times 16 dw 1023 -const pw_1024, times 16 dw 1024 -const pw_2048, times 16 dw 2048 -const pw_4096, times 16 dw 4096 -const pw_8192, times 8 dw 8192 -const pw_00ff, times 16 dw 0x00ff -const pw_ff00, times 8 dw 0xff00 -const pw_2000, times 16 dw 0x2000 -const pw_8000, times 8 dw 0x8000 -const pw_3fff, times 16 dw 0x3fff -const pw_32_0, times 4 dw 32, - times 4 dw 0 -const pw_pixel_max, times 16 dw ((1 << BIT_DEPTH)-1) - -const pw_0_7, times 2 dw 0, 1, 2, 3, 4, 5, 6, 7 -const pw_ppppmmmm, times 1 dw 1, 1, 1, 1, -1, -1, -1, -1 -const pw_ppmmppmm, times 1 dw 1, 1, -1, -1, 1, 1, -1, -1 -const pw_pmpmpmpm, times 16 dw 1, -1, 1, -1, 1, -1, 1, -1 -const pw_pmmpzzzz, times 1 dw 1, -1, -1, 1, 0, 0, 0, 0 -const multi_2Row, times 1 dw 1, 2, 3, 4, 1, 2, 3, 4 -const multiH, times 1 dw 9, 10, 11, 12, 13, 14, 15, 16 -const multiH3, times 1 dw 25, 26, 27, 28, 29, 30, 31, 32 -const multiL, times 1 dw 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 -const multiH2, times 1 dw 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 -const pw_planar16_mul, times 1 dw 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 -const pw_planar32_mul, times 1 dw 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16 -const pw_FFFFFFFFFFFFFFF0, dw 0x00 - times 7 dw 0xff -const hmul_16p, times 16 db 1 - times 8 db 1, -1 -const pw_exp2_0_15, dw 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 -const pw_1_ffff, times 4 dw 1 - times 4 dw 0xFFFF - - -;; 32-bit constants - -const pd_1, times 8 dd 1 -const pd_2, times 8 dd 2 -const pd_4, times 4 dd 4 -const pd_8, times 4 dd 8 -const pd_15, times 8 dd 15 -const pd_16, times 8 dd 16 -const pd_31, times 8 dd 31 -const pd_32, times 8 dd 32 -const pd_64, times 4 dd 64 -const pd_128, times 4 dd 128 -const pd_256, times 4 dd 256 -const pd_512, times 4 dd 512 -const pd_1024, times 4 dd 1024 -const pd_2048, times 4 dd 2048 -const pd_ffff, times 4 dd 0xffff -const pd_32767, times 4 dd 32767 -const pd_524416, times 4 dd 524416 -const pd_n32768, times 8 dd 0xffff8000 -const pd_n131072, times 4 dd 0xfffe0000 -const pd_0000ffff, times 8 dd 0x0000FFFF -const pd_planar16_mul0, times 1 dd 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 -const pd_planar16_mul1, times 1 dd 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 -const pd_planar32_mul1, times 1 dd 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16 -const pd_planar32_mul2, times 1 dd 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 -const pd_planar16_mul2, times 1 dd 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 -const trans8_shuf, times 1 dd 0, 4, 1, 5, 2, 6, 3, 7 - -;; 64-bit constants - -const pq_1, times 1 dq 1 diff --git a/Source/Lib/ASM_AVX2/x86inc.asm b/Source/Lib/ASM_AVX2/x86inc.asm deleted file mode 100644 index 5c2d8af0d..000000000 --- a/Source/Lib/ASM_AVX2/x86inc.asm +++ /dev/null @@ -1,1664 +0,0 @@ -;***************************************************************************** -;* x86inc.asm: x264asm abstraction layer -;***************************************************************************** -;* Copyright (C) 2003-2013 x264 project -;* Copyright (C) 2013-2017 MulticoreWarae, Inc -;* -;* Authors: Loren Merritt -;* Anton Mitrofanov -;* Fiona Glaser -;* Henrik Gramner -;* Min Chen -;* -;* Permission to use, copy, modify, and/or distribute this software for any -;* purpose with or without fee is hereby granted, provided that the above -;* copyright notice and this permission notice appear in all copies. -;* -;* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -;* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -;* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -;* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -;* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -;* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -;***************************************************************************** - -; This is a header file for the x264ASM assembly language, which uses -; NASM/YASM syntax combined with a large number of macros to provide easy -; abstraction between different calling conventions (x86_32, win64, linux64). -; It also has various other useful features to simplify writing the kind of -; DSP functions that are most often used in x264. - -; Unlike the rest of x264, this file is available under an ISC license, as it -; has significant usefulness outside of x264 and we want it to be available -; to the largest audience possible. Of course, if you modify it for your own -; purposes to add a new feature, we strongly encourage contributing a patch -; as this feature might be useful for others as well. Send patches or ideas -; to x264-devel@videolan.org . - -%define ARCH_X86_64 1 -%define HIGH_BIT_DEPTH 0 -%define BIT_DEPTH 8 - -; %ifndef private_prefix -; %define private_prefix Eb -; %endif - -; %ifndef public_prefix -; %define public_prefix private_prefix -; %endif - -%ifndef STACK_ALIGNMENT - %if ARCH_X86_64 - %define STACK_ALIGNMENT 16 - %else - %define STACK_ALIGNMENT 4 - %endif -%endif - -%define WIN64 0 -%define UNIX64 0 -%if ARCH_X86_64 - %ifidn __OUTPUT_FORMAT__,win32 - %define WIN64 1 - %elifidn __OUTPUT_FORMAT__,win64 - %define WIN64 1 - %elifidn __OUTPUT_FORMAT__,x64 - %define WIN64 1 - %else - %define UNIX64 1 - %endif -%endif - -%define FORMAT_ELF 0 -%ifidn __OUTPUT_FORMAT__,elf - %define FORMAT_ELF 1 -%elifidn __OUTPUT_FORMAT__,elf32 - %define FORMAT_ELF 1 -%elifidn __OUTPUT_FORMAT__,elf64 - %define FORMAT_ELF 1 -%endif - -%ifdef PREFIX - %define mangle(x) _ %+ x -%else - %define mangle(x) x -%endif - -%macro SECTION_RODATA 0-1 32 - %ifidn __OUTPUT_FORMAT__,win32 - SECTION .rdata align=%1 - %elif WIN64 - SECTION .rdata align=%1 - %else - SECTION .rodata align=%1 - %endif -%endmacro - -%if WIN64 - %define PIC -%elif ARCH_X86_64 == 0 -; x86_32 doesn't require PIC. -; Some distros prefer shared objects to be PIC, but nothing breaks if -; the code contains a few textrels, so we'll skip that complexity. - %undef PIC -%endif -%ifdef PIC - default rel -%endif - -%ifdef __NASM_VER__ - %use smartalign -%endif - -; Macros to eliminate most code duplication between x86_32 and x86_64: -; Currently this works only for leaf functions which load all their arguments -; into registers at the start, and make no other use of the stack. Luckily that -; covers most of x264's asm. - -; PROLOGUE: -; %1 = number of arguments. loads them from stack if needed. -; %2 = number of registers used. pushes callee-saved regs if needed. -; %3 = number of xmm registers used. pushes callee-saved xmm regs if needed. -; %4 = (optional) stack size to be allocated. The stack will be aligned before -; allocating the specified stack size. If the required stack alignment is -; larger than the known stack alignment the stack will be manually aligned -; and an extra register will be allocated to hold the original stack -; pointer (to not invalidate r0m etc.). To prevent the use of an extra -; register as stack pointer, request a negative stack size. -; %4+/%5+ = list of names to define to registers -; PROLOGUE can also be invoked by adding the same options to cglobal - -; e.g. -; cglobal foo, 2,3,7,0x40, dst, src, tmp -; declares a function (foo) that automatically loads two arguments (dst and -; src) into registers, uses one additional register (tmp) plus 7 vector -; registers (m0-m6) and allocates 0x40 bytes of stack space. - -; TODO Some functions can use some args directly from the stack. If they're the -; last args then you can just not declare them, but if they're in the middle -; we need more flexible macro. - -; RET: -; Pops anything that was pushed by PROLOGUE, and returns. - -; REP_RET: -; Use this instead of RET if it's a branch target. - -; registers: -; rN and rNq are the native-size register holding function argument N -; rNd, rNw, rNb are dword, word, and byte size -; rNh is the high 8 bits of the word size -; rNm is the original location of arg N (a register or on the stack), dword -; rNmp is native size - -%macro DECLARE_REG 2-3 - %define r%1q %2 - %define r%1d %2d - %define r%1w %2w - %define r%1b %2b - %define r%1h %2h - %define %2q %2 - %if %0 == 2 - %define r%1m %2d - %define r%1mp %2 - %elif ARCH_X86_64 ; memory - %define r%1m [rstk + stack_offset + %3] - %define r%1mp qword r %+ %1 %+ m - %else - %define r%1m [rstk + stack_offset + %3] - %define r%1mp dword r %+ %1 %+ m - %endif - %define r%1 %2 -%endmacro - -%macro DECLARE_REG_SIZE 3 - %define r%1q r%1 - %define e%1q r%1 - %define r%1d e%1 - %define e%1d e%1 - %define r%1w %1 - %define e%1w %1 - %define r%1h %3 - %define e%1h %3 - %define r%1b %2 - %define e%1b %2 - %if ARCH_X86_64 == 0 - %define r%1 e%1 - %endif -%endmacro - -DECLARE_REG_SIZE ax, al, ah -DECLARE_REG_SIZE bx, bl, bh -DECLARE_REG_SIZE cx, cl, ch -DECLARE_REG_SIZE dx, dl, dh -DECLARE_REG_SIZE si, sil, null -DECLARE_REG_SIZE di, dil, null -DECLARE_REG_SIZE bp, bpl, null - -; t# defines for when per-arch register allocation is more complex than just function arguments - -%macro DECLARE_REG_TMP 1-* - %assign %%i 0 - %rep %0 - CAT_XDEFINE t, %%i, r%1 - %assign %%i %%i+1 - %rotate 1 - %endrep -%endmacro - -%macro DECLARE_REG_TMP_SIZE 0-* - %rep %0 - %define t%1q t%1 %+ q - %define t%1d t%1 %+ d - %define t%1w t%1 %+ w - %define t%1h t%1 %+ h - %define t%1b t%1 %+ b - %rotate 1 - %endrep -%endmacro - -DECLARE_REG_TMP_SIZE 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 - -%if ARCH_X86_64 - %define gprsize 8 -%else - %define gprsize 4 -%endif - -%macro PUSH 1 - push %1 - %ifidn rstk, rsp - %assign stack_offset stack_offset+gprsize - %endif -%endmacro - -%macro POP 1 - pop %1 - %ifidn rstk, rsp - %assign stack_offset stack_offset-gprsize - %endif -%endmacro - -%macro PUSH_IF_USED 1-* - %rep %0 - %if %1 < regs_used - PUSH r%1 - %endif - %rotate 1 - %endrep -%endmacro - -%macro POP_IF_USED 1-* - %rep %0 - %if %1 < regs_used - pop r%1 - %endif - %rotate 1 - %endrep -%endmacro - -%macro LOAD_IF_USED 1-* - %rep %0 - %if %1 < num_args - mov r%1, r %+ %1 %+ mp - %endif - %rotate 1 - %endrep -%endmacro - -%macro SUB 2 - sub %1, %2 - %ifidn %1, rstk - %assign stack_offset stack_offset+(%2) - %endif -%endmacro - -%macro ADD 2 - add %1, %2 - %ifidn %1, rstk - %assign stack_offset stack_offset-(%2) - %endif -%endmacro - -%macro movifnidn 2 - %ifnidn %1, %2 - mov %1, %2 - %endif -%endmacro - -%macro movsxdifnidn 2 - %ifnidn %1, %2 - movsxd %1, %2 - %endif -%endmacro - -%macro ASSERT 1 - %if (%1) == 0 - %error assertion ``%1'' failed - %endif -%endmacro - -%macro DEFINE_ARGS 0-* - %ifdef n_arg_names - %assign %%i 0 - %rep n_arg_names - CAT_UNDEF arg_name %+ %%i, q - CAT_UNDEF arg_name %+ %%i, d - CAT_UNDEF arg_name %+ %%i, w - CAT_UNDEF arg_name %+ %%i, h - CAT_UNDEF arg_name %+ %%i, b - CAT_UNDEF arg_name %+ %%i, m - CAT_UNDEF arg_name %+ %%i, mp - CAT_UNDEF arg_name, %%i - %assign %%i %%i+1 - %endrep - %endif - - %xdefine %%stack_offset stack_offset - %undef stack_offset ; so that the current value of stack_offset doesn't get baked in by xdefine - %assign %%i 0 - %rep %0 - %xdefine %1q r %+ %%i %+ q - %xdefine %1d r %+ %%i %+ d - %xdefine %1w r %+ %%i %+ w - %xdefine %1h r %+ %%i %+ h - %xdefine %1b r %+ %%i %+ b - %xdefine %1m r %+ %%i %+ m - %xdefine %1mp r %+ %%i %+ mp - CAT_XDEFINE arg_name, %%i, %1 - %assign %%i %%i+1 - %rotate 1 - %endrep - %xdefine stack_offset %%stack_offset - %assign n_arg_names %0 -%endmacro - -%define required_stack_alignment ((mmsize + 15) & ~15) -%define vzeroupper_required (mmsize > 16 && (ARCH_X86_64 == 0 || xmm_regs_used > 16 || notcpuflag(avx512))) -%define high_mm_regs (16*cpuflag(avx512)) - -%macro ALLOC_STACK 1-2 0 ; stack_size, n_xmm_regs (for win64 only) - %ifnum %1 - %if %1 != 0 - %assign %%pad 0 - %assign stack_size %1 - %if stack_size < 0 - %assign stack_size -stack_size - %endif - %if WIN64 - %assign %%pad %%pad + 32 ; shadow space - %if mmsize != 8 - %assign xmm_regs_used %2 - %if xmm_regs_used > 8 - %assign %%pad %%pad + (xmm_regs_used-8)*16 ; callee-saved xmm registers - %endif - %endif - %endif - %if required_stack_alignment <= STACK_ALIGNMENT - ; maintain the current stack alignment - %assign stack_size_padded stack_size + %%pad + ((-%%pad-stack_offset-gprsize) & (STACK_ALIGNMENT-1)) - SUB rsp, stack_size_padded - %else - %assign %%reg_num (regs_used - 1) - %xdefine rstk r %+ %%reg_num - ; align stack, and save original stack location directly above - ; it, i.e. in [rsp+stack_size_padded], so we can restore the - ; stack in a single instruction (i.e. mov rsp, rstk or mov - ; rsp, [rsp+stack_size_padded]) - %if %1 < 0 ; need to store rsp on stack - %xdefine rstkm [rsp + stack_size + %%pad] - %assign %%pad %%pad + gprsize - %else ; can keep rsp in rstk during whole function - %xdefine rstkm rstk - %endif - %assign stack_size_padded stack_size + ((%%pad + required_stack_alignment-1) & ~(required_stack_alignment-1)) - mov rstk, rsp - and rsp, ~(required_stack_alignment-1) - sub rsp, stack_size_padded - movifnidn rstkm, rstk - %endif - WIN64_PUSH_XMM - %endif - %endif -%endmacro - -%macro SETUP_STACK_POINTER 1 - %ifnum %1 - %if %1 != 0 && required_stack_alignment > STACK_ALIGNMENT - %if %1 > 0 - ; Reserve an additional register for storing the original stack pointer, but avoid using - ; eax/rax for this purpose since it can potentially get overwritten as a return value. - %assign regs_used (regs_used + 1) - %if ARCH_X86_64 && regs_used == 7 - %assign regs_used 8 - %elif ARCH_X86_64 == 0 && regs_used == 1 - %assign regs_used 2 - %endif - %endif - %if ARCH_X86_64 && regs_used < 5 + UNIX64 * 3 - ; Ensure that we don't clobber any registers containing arguments. For UNIX64 we also preserve r6 (rax) - ; since it's used as a hidden argument in vararg functions to specify the number of vector registers used. - %assign regs_used 5 + UNIX64 * 3 - %endif - %endif - %endif -%endmacro - -%macro DEFINE_ARGS_INTERNAL 3+ - %ifnum %2 - DEFINE_ARGS %3 - %elif %1 == 4 - DEFINE_ARGS %2 - %elif %1 > 4 - DEFINE_ARGS %2, %3 - %endif -%endmacro - -%if WIN64 ; Windows x64 ;================================================= - -DECLARE_REG 0, rcx -DECLARE_REG 1, rdx -DECLARE_REG 2, R8 -DECLARE_REG 3, R9 -DECLARE_REG 4, R10, 40 -DECLARE_REG 5, R11, 48 -DECLARE_REG 6, rax, 56 -DECLARE_REG 7, rdi, 64 -DECLARE_REG 8, rsi, 72 -DECLARE_REG 9, rbx, 80 -DECLARE_REG 10, rbp, 88 -DECLARE_REG 11, R14, 96 -DECLARE_REG 12, R15, 104 -DECLARE_REG 13, R12, 112 -DECLARE_REG 14, R13, 120 - -%macro PROLOGUE 2-5+ 0 ; #args, #regs, #xmm_regs, [stack_size,] arg_names... - %assign num_args %1 - %assign regs_used %2 - ASSERT regs_used >= num_args - SETUP_STACK_POINTER %4 - ASSERT regs_used <= 15 - PUSH_IF_USED 7, 8, 9, 10, 11, 12, 13, 14 - ALLOC_STACK %4, %3 - %if mmsize != 8 && stack_size == 0 - WIN64_SPILL_XMM %3 - %endif - LOAD_IF_USED 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 - DEFINE_ARGS_INTERNAL %0, %4, %5 -%endmacro - -%macro WIN64_PUSH_XMM 0 - ; Use the shadow space to store XMM6 and XMM7, the rest needs stack space allocated. - %if xmm_regs_used > 6 + high_mm_regs - movaps [rstk + stack_offset + 8], xmm6 - %endif - %if xmm_regs_used > 7 + high_mm_regs - movaps [rstk + stack_offset + 24], xmm7 - %endif - %assign %%xmm_regs_on_stack xmm_regs_used - high_mm_regs - 8 - %if %%xmm_regs_on_stack > 0 - %assign %%i 8 - %rep %%xmm_regs_on_stack - movaps [rsp + (%%i-8)*16 + stack_size + 32], xmm %+ %%i - %assign %%i %%i+1 - %endrep - %endif -%endmacro - -%macro WIN64_SPILL_XMM 1 - %assign xmm_regs_used %1 - ASSERT xmm_regs_used <= 16 + high_mm_regs - %assign %%xmm_regs_on_stack xmm_regs_used - high_mm_regs - 8 - %if %%xmm_regs_on_stack > 0 - ; Allocate stack space for callee-saved xmm registers plus shadow space and align the stack. - %assign %%pad (xmm_regs_used-8)*16 + 32 - %assign stack_size_padded %%pad + ((-%%pad-stack_offset-gprsize) & (STACK_ALIGNMENT-1)) - SUB rsp, stack_size_padded - %endif - WIN64_PUSH_XMM -%endmacro - -%macro WIN64_RESTORE_XMM_INTERNAL 0 - %assign %%pad_size 0 - %assign %%xmm_regs_on_stack xmm_regs_used - high_mm_regs - 8 - %if %%xmm_regs_on_stack > 0 - %assign %%i xmm_regs_used - high_mm_regs - %rep %%xmm_regs_on_stack - %assign %%i %%i-1 - movaps xmm %+ %%i, [rsp + (%%i-8)*16 + stack_size + 32] - %endrep - %endif - %if stack_size_padded > 0 - %if stack_size > 0 && required_stack_alignment > STACK_ALIGNMENT - mov rsp, rstkm - %else - add rsp, stack_size_padded - %assign %%pad_size stack_size_padded - %endif - %endif - %if xmm_regs_used > 7 + high_mm_regs - movaps xmm7, [rsp + stack_offset - %%pad_size + 24] - %endif - %if xmm_regs_used > 6 + high_mm_regs - movaps xmm6, [rsp + stack_offset - %%pad_size + 8] - %endif -%endmacro - -%macro WIN64_RESTORE_XMM 0 - WIN64_RESTORE_XMM_INTERNAL - %assign stack_offset (stack_offset-stack_size_padded) - %assign stack_size_padded 0 - %assign xmm_regs_used 0 -%endmacro - -%define has_epilogue regs_used > 7 || stack_size > 0 || vzeroupper_required || xmm_regs_used > 6 + high_mm_regs - -%macro RET 0 - WIN64_RESTORE_XMM_INTERNAL - POP_IF_USED 14, 13, 12, 11, 10, 9, 8, 7 - %if vzeroupper_required - vzeroupper - %endif - AUTO_REP_RET -%endmacro - -%elif ARCH_X86_64 ; *nix x64 ;============================================= - -DECLARE_REG 0, rdi -DECLARE_REG 1, rsi -DECLARE_REG 2, rdx -DECLARE_REG 3, rcx -DECLARE_REG 4, R8 -DECLARE_REG 5, R9 -DECLARE_REG 6, rax, 8 -DECLARE_REG 7, R10, 16 -DECLARE_REG 8, R11, 24 -DECLARE_REG 9, rbx, 32 -DECLARE_REG 10, rbp, 40 -DECLARE_REG 11, R14, 48 -DECLARE_REG 12, R15, 56 -DECLARE_REG 13, R12, 64 -DECLARE_REG 14, R13, 72 - -%macro PROLOGUE 2-5+ 0; #args, #regs, #xmm_regs, [stack_size,] arg_names... - %assign num_args %1 - %assign regs_used %2 - %assign xmm_regs_used %3 - ASSERT regs_used >= num_args - SETUP_STACK_POINTER %4 - ASSERT regs_used <= 15 - PUSH_IF_USED 9, 10, 11, 12, 13, 14 - ALLOC_STACK %4 - LOAD_IF_USED 6, 7, 8, 9, 10, 11, 12, 13, 14 - DEFINE_ARGS_INTERNAL %0, %4, %5 -%endmacro - -%define has_epilogue regs_used > 9 || stack_size > 0 || vzeroupper_required - -%macro RET 0 - %if stack_size_padded > 0 - %if required_stack_alignment > STACK_ALIGNMENT - mov rsp, rstkm - %else - add rsp, stack_size_padded - %endif - %endif - POP_IF_USED 14, 13, 12, 11, 10, 9 - %if vzeroupper_required - vzeroupper - %endif - AUTO_REP_RET -%endmacro - -%else ; X86_32 ;============================================================== - -DECLARE_REG 0, eax, 4 -DECLARE_REG 1, ecx, 8 -DECLARE_REG 2, edx, 12 -DECLARE_REG 3, ebx, 16 -DECLARE_REG 4, esi, 20 -DECLARE_REG 5, edi, 24 -DECLARE_REG 6, ebp, 28 -%define rsp esp - -%macro DECLARE_ARG 1-* - %rep %0 - %define r%1m [rstk + stack_offset + 4*%1 + 4] - %define r%1mp dword r%1m - %rotate 1 - %endrep -%endmacro - -DECLARE_ARG 7, 8, 9, 10, 11, 12, 13, 14 - -%macro PROLOGUE 2-5+ ; #args, #regs, #xmm_regs, [stack_size,] arg_names... - %assign num_args %1 - %assign regs_used %2 - ASSERT regs_used >= num_args - %if num_args > 7 - %assign num_args 7 - %endif - %if regs_used > 7 - %assign regs_used 7 - %endif - SETUP_STACK_POINTER %4 - ASSERT regs_used <= 7 - PUSH_IF_USED 3, 4, 5, 6 - ALLOC_STACK %4 - LOAD_IF_USED 0, 1, 2, 3, 4, 5, 6 - DEFINE_ARGS_INTERNAL %0, %4, %5 -%endmacro - -%define has_epilogue regs_used > 3 || stack_size > 0 || vzeroupper_required - -%macro RET 0 - %if stack_size_padded > 0 - %if required_stack_alignment > STACK_ALIGNMENT - mov rsp, rstkm - %else - add rsp, stack_size_padded - %endif - %endif - POP_IF_USED 6, 5, 4, 3 - %if vzeroupper_required - vzeroupper - %endif - AUTO_REP_RET -%endmacro - -%endif ;====================================================================== - -%if WIN64 == 0 - %macro WIN64_SPILL_XMM 1 - %endmacro - %macro WIN64_RESTORE_XMM 0 - %endmacro - %macro WIN64_PUSH_XMM 0 - %endmacro -%endif - -; On AMD cpus <=K10, an ordinary ret is slow if it immediately follows either -; a branch or a branch target. So switch to a 2-byte form of ret in that case. -; We can automatically detect "follows a branch", but not a branch target. -; (SSSE3 is a sufficient condition to know that your cpu doesn't have this problem.) -%macro REP_RET 0 - %if has_epilogue - RET - %else - rep ret - %endif -%endmacro - -%define last_branch_adr $$ -%macro AUTO_REP_RET 0 - %if notcpuflag(ssse3) - times ((last_branch_adr-$)>>31)+1 rep ; times 1 iff $ == last_branch_adr. - %endif - ret -%endmacro - -%macro BRANCH_INSTR 0-* - %rep %0 - %macro %1 1-2 %1 - %2 %1 - %if notcpuflag(ssse3) - %%branch_instr equ $ - %xdefine last_branch_adr %%branch_instr - %endif - %endmacro - %rotate 1 - %endrep -%endmacro - -BRANCH_INSTR jz, je, jnz, jne, jl, jle, jnl, jnle, jg, jge, jng, jnge, ja, jae, jna, jnae, jb, jbe, jnb, jnbe, jc, jnc, js, jns, jo, jno, jp, jnp - -%macro TAIL_CALL 2 ; callee, is_nonadjacent - %if has_epilogue - call %1 - RET - %elif %2 - jmp %1 - %endif -%endmacro - -;============================================================================= -; arch-independent part -;============================================================================= - -%assign function_align 16 - -; Begin a function. -; Applies any symbol mangling needed for C linkage, and sets up a define such that -; subsequent uses of the function name automatically refer to the mangled version. -; Appends cpuflags to the function name if cpuflags has been specified. -; The "" empty default parameter is a workaround for nasm, which fails if SUFFIX -; is empty and we call cglobal_internal with just %1 %+ SUFFIX (without %2). -%macro cglobal 1-2+ "" ; name, [PROLOGUE args] - cglobal_internal 1, %1 %+ SUFFIX, %2 -%endmacro -%macro cvisible 1-2+ "" ; name, [PROLOGUE args] - cglobal_internal 0, %1 %+ SUFFIX, %2 -%endmacro -%macro cglobal_internal 2-3+ - %if %1 - ; %xdefine %%FUNCTION_PREFIX private_prefix - %xdefine %%VISIBILITY hidden - %else - ; %xdefine %%FUNCTION_PREFIX public_prefix - %xdefine %%VISIBILITY - %endif - %ifndef cglobaled_%2 - ; %xdefine %2 mangle(%%FUNCTION_PREFIX %+ _ %+ %2) - %xdefine %2.skip_prologue %2 %+ .skip_prologue - CAT_XDEFINE cglobaled_, %2, 1 - %endif - %xdefine current_function %2 - %if FORMAT_ELF - global %2:function %%VISIBILITY - %else - global %2 - %endif - align function_align - %2: - RESET_MM_PERMUTATION ; needed for x86-64, also makes disassembly somewhat nicer - %xdefine rstk rsp ; copy of the original stack pointer, used when greater alignment than the known stack alignment is required - %assign stack_offset 0 ; stack pointer offset relative to the return address - %assign stack_size 0 ; amount of stack space that can be freely used inside a function - %assign stack_size_padded 0 ; total amount of allocated stack space, including space for callee-saved xmm registers on WIN64 and alignment padding - %assign xmm_regs_used 0 ; number of XMM registers requested, used for dealing with callee-saved registers on WIN64 and vzeroupper - %ifnidn %3, "" - PROLOGUE %3 - %endif -%endmacro - -; Create a global symbol from a local label with the correct name mangling and type -%macro cglobal_label 1 - %if FORMAT_ELF - global current_function %+ %1:function hidden - %else - global current_function %+ %1 - %endif - %1: -%endmacro - -%macro cextern 1 - %xdefine %1 mangle(private_prefix %+ _ %+ %1) - CAT_XDEFINE cglobaled_, %1, 1 - extern %1 -%endmacro - -; like cextern, but without the prefix -%macro cextern_naked 1 - %ifdef PREFIX - %xdefine %1 mangle(%1) - %endif - CAT_XDEFINE cglobaled_, %1, 1 - extern %1 -%endmacro - -%macro const 1-2+ - %xdefine %1 mangle(private_prefix %+ _ %+ %1) - %if FORMAT_ELF - global %1:data hidden - %else - global %1 - %endif - ALIGN 32 - %1: %2 -%endmacro - -; This is needed for ELF, otherwise the GNU linker assumes the stack is executable by default. -%if FORMAT_ELF - [SECTION .note.GNU-stack noalloc noexec nowrite progbits] -%endif - -; cpuflags - -%assign cpuflags_mmx (1<<0) -%assign cpuflags_mmx2 (1<<1) | cpuflags_mmx -%assign cpuflags_3dnow (1<<2) | cpuflags_mmx -%assign cpuflags_3dnowext (1<<3) | cpuflags_3dnow -%assign cpuflags_sse (1<<4) | cpuflags_mmx2 -%assign cpuflags_sse2 (1<<5) | cpuflags_sse -%assign cpuflags_sse2slow (1<<6) | cpuflags_sse2 -%assign cpuflags_lzcnt (1<<7) | cpuflags_sse2 -%assign cpuflags_sse3 (1<<8) | cpuflags_sse2 -%assign cpuflags_ssse3 (1<<9) | cpuflags_sse3 -%assign cpuflags_sse4 (1<<10)| cpuflags_ssse3 -%assign cpuflags_sse42 (1<<11)| cpuflags_sse4 -%assign cpuflags_avx (1<<12)| cpuflags_sse42 -%assign cpuflags_xop (1<<13)| cpuflags_avx -%assign cpuflags_fma4 (1<<14)| cpuflags_avx -%assign cpuflags_fma3 (1<<15)| cpuflags_avx -%assign cpuflags_bmi1 (1<<16)| cpuflags_avx | cpuflags_lzcnt -%assign cpuflags_bmi2 (1<<17)| cpuflags_bmi1 -%assign cpuflags_avx2 (1<<18)| cpuflags_fma3 | cpuflags_bmi2 -%assign cpuflags_avx512 (1<<19)| cpuflags_avx2 ; F, CD, BW, DQ, VL - -%assign cpuflags_cache32 (1<<20) -%assign cpuflags_cache64 (1<<21) -%assign cpuflags_aligned (1<<22) ; not a cpu feature, but a function variant -%assign cpuflags_atom (1<<23) - -; Returns a boolean value expressing whether or not the specified cpuflag is enabled. -%define cpuflag(x) (((((cpuflags & (cpuflags_ %+ x)) ^ (cpuflags_ %+ x)) - 1) >> 31) & 1) -%define notcpuflag(x) (cpuflag(x) ^ 1) - -; Takes an arbitrary number of cpuflags from the above list. -; All subsequent functions (up to the next INIT_CPUFLAGS) is built for the specified cpu. -; You shouldn't need to invoke this macro directly, it's a subroutine for INIT_MMX &co. -%macro INIT_CPUFLAGS 0-* - %xdefine SUFFIX - %undef cpuname - %assign cpuflags 0 - - %if %0 >= 1 - %rep %0 - %ifdef cpuname - %xdefine cpuname cpuname %+ _%1 - %else - %xdefine cpuname %1 - %endif - %assign cpuflags cpuflags | cpuflags_%1 - %rotate 1 - %endrep - %xdefine SUFFIX _ %+ cpuname - - %if cpuflag(avx) - %assign avx_enabled 1 - %endif - %if (mmsize == 16 && notcpuflag(sse2)) || (mmsize == 32 && notcpuflag(avx2)) - %define mova movaps - %define movu movups - %define movnta movntps - %endif - %if cpuflag(aligned) - %define movu mova - %elif cpuflag(sse3) && notcpuflag(ssse3) - %define movu lddqu - %endif - %endif - - %if ARCH_X86_64 || cpuflag(sse2) - %ifdef __NASM_VER__ - ALIGNMODE p6 - %else - CPU amdnop - %endif - %else - %ifdef __NASM_VER__ - ALIGNMODE nop - %else - CPU basicnop - %endif - %endif -%endmacro - -; Merge mmx and sse*, and avx* -; m# is a simd register of the currently selected size -; xm# is the corresponding xmm register if mmsize >= 16, otherwise the same as m# -; ym# is the corresponding ymm register if mmsize >= 32, otherwise the same as m# -; zm# is the corresponding zmm register if mmsize >= 64, otherwise the same as m# -; (All 4 remain in sync through SWAP.) - -%macro CAT_XDEFINE 3 - %xdefine %1%2 %3 -%endmacro - -%macro CAT_UNDEF 2 - %undef %1%2 -%endmacro - -%macro DEFINE_MMREGS 1 ; mmtype - %assign %%prev_mmregs 0 - %ifdef num_mmregs - %assign %%prev_mmregs num_mmregs - %endif - - %assign num_mmregs 8 - %if ARCH_X86_64 && mmsize >= 16 - %assign num_mmregs 16 - %if cpuflag(avx512) || mmsize == 64 - %assign num_mmregs 32 - %endif - %endif - - %assign %%i 0 - %rep num_mmregs - CAT_XDEFINE m, %%i, %1 %+ %%i - CAT_XDEFINE nn%1, %%i, %%i - %assign %%i %%i+1 - %endrep - %if %%prev_mmregs > num_mmregs - %rep %%prev_mmregs - num_mmregs - CAT_UNDEF m, %%i - CAT_UNDEF nn %+ mmtype, %%i - %assign %%i %%i+1 - %endrep - %endif - %xdefine mmtype %1 -%endmacro - -; Prefer registers 16-31 over 0-15 to avoid having to use vzeroupper -%macro AVX512_MM_PERMUTATION 0-1 0 ; start_reg - %if ARCH_X86_64 && cpuflag(avx512) - %assign %%i %1 - %rep 16-%1 - %assign %%i_high %%i+16 - SWAP %%i, %%i_high - %assign %%i %%i+1 - %endrep - %endif -%endmacro - -%macro INIT_MMX 0-1+ - %assign avx_enabled 0 - %define RESET_MM_PERMUTATION INIT_MMX %1 - %define mmsize 8 - %define mova movq - %define movu movq - %define movh movd - %define movnta movntq - INIT_CPUFLAGS %1 - DEFINE_MMREGS mm -%endmacro - -%macro INIT_XMM 0-1+ - %assign avx_enabled 0 - %define RESET_MM_PERMUTATION INIT_XMM %1 - %define mmsize 16 - %define mova movdqa - %define movu movdqu - %define movh movq - %define movnta movntdq - INIT_CPUFLAGS %1 - DEFINE_MMREGS xmm - %if WIN64 - ; Swap callee-saved registers with volatile registers - AVX512_MM_PERMUTATION 6 - %endif -%endmacro - -%macro INIT_YMM 0-1+ - %assign avx_enabled 1 - %define RESET_MM_PERMUTATION INIT_YMM %1 - %define mmsize 32 - %define mova movdqa - %define movu movdqu - %undef movh - %define movnta movntdq - INIT_CPUFLAGS %1 - DEFINE_MMREGS ymm - AVX512_MM_PERMUTATION -%endmacro - -%macro INIT_ZMM 0-1+ - %assign avx_enabled 1 - %define RESET_MM_PERMUTATION INIT_ZMM %1 - %define mmsize 64 - %define mova movdqa - %define movu movdqu - %undef movh - %define movnta movntdq - INIT_CPUFLAGS %1 - DEFINE_MMREGS zmm - AVX512_MM_PERMUTATION -%endmacro - -INIT_XMM - -%macro DECLARE_MMCAST 1 - %define mmmm%1 mm%1 - %define mmxmm%1 mm%1 - %define mmymm%1 mm%1 - %define mmzmm%1 mm%1 - %define xmmmm%1 mm%1 - %define xmmxmm%1 xmm%1 - %define xmmymm%1 xmm%1 - %define xmmzmm%1 xmm%1 - %define ymmmm%1 mm%1 - %define ymmxmm%1 xmm%1 - %define ymmymm%1 ymm%1 - %define ymmzmm%1 ymm%1 - %define zmmmm%1 mm%1 - %define zmmxmm%1 xmm%1 - %define zmmymm%1 ymm%1 - %define zmmzmm%1 zmm%1 - %define xm%1 xmm %+ m%1 - %define ym%1 ymm %+ m%1 - %define zm%1 zmm %+ m%1 -%endmacro - -%assign i 0 -%rep 32 - DECLARE_MMCAST i - %assign i i+1 -%endrep - -; I often want to use macros that permute their arguments. e.g. there's no -; efficient way to implement butterfly or transpose or dct without swapping some -; arguments. -; -; I would like to not have to manually keep track of the permutations: -; If I insert a permutation in the middle of a function, it should automatically -; change everything that follows. For more complex macros I may also have multiple -; implementations, e.g. the SSE2 and SSSE3 versions may have different permutations. -; -; Hence these macros. Insert a PERMUTE or some SWAPs at the end of a macro that -; permutes its arguments. It's equivalent to exchanging the contents of the -; registers, except that this way you exchange the register names instead, so it -; doesn't cost any cycles. - -%macro PERMUTE 2-* ; takes a list of pairs to swap - %rep %0/2 - %xdefine %%tmp%2 m%2 - %rotate 2 - %endrep - %rep %0/2 - %xdefine m%1 %%tmp%2 - CAT_XDEFINE nn, m%1, %1 - %rotate 2 - %endrep -%endmacro - -%macro SWAP 2+ ; swaps a single chain (sometimes more concise than pairs) - %ifnum %1 ; SWAP 0, 1, ... - SWAP_INTERNAL_NUM %1, %2 - %else ; SWAP m0, m1, ... - SWAP_INTERNAL_NAME %1, %2 - %endif -%endmacro - -%macro SWAP_INTERNAL_NUM 2-* - %rep %0-1 - %xdefine %%tmp m%1 - %xdefine m%1 m%2 - %xdefine m%2 %%tmp - CAT_XDEFINE nn, m%1, %1 - CAT_XDEFINE nn, m%2, %2 - %rotate 1 - %endrep -%endmacro - -%macro SWAP_INTERNAL_NAME 2-* - %xdefine %%args nn %+ %1 - %rep %0-1 - %xdefine %%args %%args, nn %+ %2 - %rotate 1 - %endrep - SWAP_INTERNAL_NUM %%args -%endmacro - -; If SAVE_MM_PERMUTATION is placed at the end of a function, then any later -; calls to that function will automatically load the permutation, so values can -; be returned in mmregs. -%macro SAVE_MM_PERMUTATION 0-1 - %if %0 - %xdefine %%f %1_m - %else - %xdefine %%f current_function %+ _m - %endif - %assign %%i 0 - %rep num_mmregs - CAT_XDEFINE %%f, %%i, m %+ %%i - %assign %%i %%i+1 - %endrep -%endmacro - -%macro LOAD_MM_PERMUTATION 1 ; name to load from - %ifdef %1_m0 - %assign %%i 0 - %rep num_mmregs - CAT_XDEFINE m, %%i, %1_m %+ %%i - CAT_XDEFINE nn, m %+ %%i, %%i - %assign %%i %%i+1 - %endrep - %endif -%endmacro - -; Append cpuflags to the callee's name iff the appended name is known and the plain name isn't -%macro call 1 - %ifid %1 - call_internal %1 %+ SUFFIX, %1 - %else - call %1 - %endif -%endmacro -%macro call_internal 2 - %xdefine %%i %2 - %ifndef cglobaled_%2 - %ifdef cglobaled_%1 - %xdefine %%i %1 - %endif - %endif - call %%i - LOAD_MM_PERMUTATION %%i -%endmacro - -; Substitutions that reduce instruction size but are functionally equivalent -%macro add 2 - %ifnum %2 - %if %2==128 - sub %1, -128 - %else - add %1, %2 - %endif - %else - add %1, %2 - %endif -%endmacro - -%macro sub 2 - %ifnum %2 - %if %2==128 - add %1, -128 - %else - sub %1, %2 - %endif - %else - sub %1, %2 - %endif -%endmacro - -;============================================================================= -; AVX abstraction layer -;============================================================================= - -%assign i 0 -%rep 32 - %if i < 8 - CAT_XDEFINE sizeofmm, i, 8 - CAT_XDEFINE regnumofmm, i, i - %endif - CAT_XDEFINE sizeofxmm, i, 16 - CAT_XDEFINE sizeofymm, i, 32 - CAT_XDEFINE sizeofzmm, i, 64 - CAT_XDEFINE regnumofxmm, i, i - CAT_XDEFINE regnumofymm, i, i - CAT_XDEFINE regnumofzmm, i, i - %assign i i+1 -%endrep -%undef i - -%macro CHECK_AVX_INSTR_EMU 3-* - %xdefine %%opcode %1 - %xdefine %%dst %2 - %rep %0-2 - %ifidn %%dst, %3 - %error non-avx emulation of ``%%opcode'' is not supported - %endif - %rotate 1 - %endrep -%endmacro - -;%1 == instruction -;%2 == minimal instruction set -;%3 == 1 if float, 0 if int -;%4 == 1 if 4-operand emulation, 0 if 3-operand emulation, 255 otherwise (no emulation) -;%5 == 1 if commutative (i.e. doesn't matter which src arg is which), 0 if not -;%6+: operands -%macro RUN_AVX_INSTR 6-9+ - %ifnum sizeof%7 - %assign __sizeofreg sizeof%7 - %elifnum sizeof%6 - %assign __sizeofreg sizeof%6 - %else - %assign __sizeofreg mmsize - %endif - %assign __emulate_avx 0 - %if avx_enabled && __sizeofreg >= 16 - %xdefine __instr v%1 - %else - %xdefine __instr %1 - %if %0 >= 8+%4 - %assign __emulate_avx 1 - %endif - %endif - %ifnidn %2, fnord - %ifdef cpuname - %if notcpuflag(%2) - %error use of ``%1'' %2 instruction in cpuname function: current_function - %elif cpuflags_%2 < cpuflags_sse && notcpuflag(sse2) && __sizeofreg > 8 - %error use of ``%1'' sse2 instruction in cpuname function: current_function - %endif - %endif - %endif - - %if __emulate_avx - %xdefine __src1 %7 - %xdefine __src2 %8 - %if %5 && %4 == 0 - %ifnidn %6, %7 - %ifidn %6, %8 - %xdefine __src1 %8 - %xdefine __src2 %7 - %elifnnum sizeof%8 - ; 3-operand AVX instructions with a memory arg can only have it in src2, - ; whereas SSE emulation prefers to have it in src1 (i.e. the mov). - ; So, if the instruction is commutative with a memory arg, swap them. - %xdefine __src1 %8 - %xdefine __src2 %7 - %endif - %endif - %endif - %ifnidn %6, __src1 - %if %0 >= 9 - CHECK_AVX_INSTR_EMU {%1 %6, %7, %8, %9}, %6, __src2, %9 - %else - CHECK_AVX_INSTR_EMU {%1 %6, %7, %8}, %6, __src2 - %endif - %if __sizeofreg == 8 - MOVQ %6, __src1 - %elif %3 - MOVAPS %6, __src1 - %else - MOVDQA %6, __src1 - %endif - %endif - %if %0 >= 9 - %1 %6, __src2, %9 - %else - %1 %6, __src2 - %endif - %elif %0 >= 9 - __instr %6, %7, %8, %9 - %elif %0 == 8 - __instr %6, %7, %8 - %elif %0 == 7 - __instr %6, %7 - %else - __instr %6 - %endif -%endmacro - -;%1 == instruction -;%2 == minimal instruction set -;%3 == 1 if float, 0 if int -;%4 == 1 if 4-operand emulation, 0 if 3-operand emulation, 255 otherwise (no emulation) -;%5 == 1 if commutative (i.e. doesn't matter which src arg is which), 0 if not -%macro AVX_INSTR 1-5 fnord, 0, 255, 0 - %macro %1 1-10 fnord, fnord, fnord, fnord, %1, %2, %3, %4, %5 - %ifidn %2, fnord - RUN_AVX_INSTR %6, %7, %8, %9, %10, %1 - %elifidn %3, fnord - RUN_AVX_INSTR %6, %7, %8, %9, %10, %1, %2 - %elifidn %4, fnord - RUN_AVX_INSTR %6, %7, %8, %9, %10, %1, %2, %3 - %elifidn %5, fnord - RUN_AVX_INSTR %6, %7, %8, %9, %10, %1, %2, %3, %4 - %else - RUN_AVX_INSTR %6, %7, %8, %9, %10, %1, %2, %3, %4, %5 - %endif - %endmacro -%endmacro - -; Instructions with both VEX/EVEX and legacy encodings -; Non-destructive instructions are written without parameters -AVX_INSTR addpd, sse2, 1, 0, 1 -AVX_INSTR addps, sse, 1, 0, 1 -AVX_INSTR addsd, sse2, 1, 0, 0 -AVX_INSTR addss, sse, 1, 0, 0 -AVX_INSTR addsubpd, sse3, 1, 0, 0 -AVX_INSTR addsubps, sse3, 1, 0, 0 -AVX_INSTR aesdec, aesni, 0, 0, 0 -AVX_INSTR aesdeclast, aesni, 0, 0, 0 -AVX_INSTR aesenc, aesni, 0, 0, 0 -AVX_INSTR aesenclast, aesni, 0, 0, 0 -AVX_INSTR aesimc, aesni -AVX_INSTR aeskeygenassist, aesni -AVX_INSTR andnpd, sse2, 1, 0, 0 -AVX_INSTR andnps, sse, 1, 0, 0 -AVX_INSTR andpd, sse2, 1, 0, 1 -AVX_INSTR andps, sse, 1, 0, 1 -AVX_INSTR blendpd, sse4, 1, 1, 0 -AVX_INSTR blendps, sse4, 1, 1, 0 -AVX_INSTR blendvpd, sse4 ; can't be emulated -AVX_INSTR blendvps, sse4 ; can't be emulated -AVX_INSTR cmpeqpd, sse2, 1, 0, 1 -AVX_INSTR cmpeqps, sse, 1, 0, 1 -AVX_INSTR cmpeqsd, sse2, 1, 0, 0 -AVX_INSTR cmpeqss, sse, 1, 0, 0 -AVX_INSTR cmplepd, sse2, 1, 0, 0 -AVX_INSTR cmpleps, sse, 1, 0, 0 -AVX_INSTR cmplesd, sse2, 1, 0, 0 -AVX_INSTR cmpless, sse, 1, 0, 0 -AVX_INSTR cmpltpd, sse2, 1, 0, 0 -AVX_INSTR cmpltps, sse, 1, 0, 0 -AVX_INSTR cmpltsd, sse2, 1, 0, 0 -AVX_INSTR cmpltss, sse, 1, 0, 0 -AVX_INSTR cmpneqpd, sse2, 1, 0, 1 -AVX_INSTR cmpneqps, sse, 1, 0, 1 -AVX_INSTR cmpneqsd, sse2, 1, 0, 0 -AVX_INSTR cmpneqss, sse, 1, 0, 0 -AVX_INSTR cmpnlepd, sse2, 1, 0, 0 -AVX_INSTR cmpnleps, sse, 1, 0, 0 -AVX_INSTR cmpnlesd, sse2, 1, 0, 0 -AVX_INSTR cmpnless, sse, 1, 0, 0 -AVX_INSTR cmpnltpd, sse2, 1, 0, 0 -AVX_INSTR cmpnltps, sse, 1, 0, 0 -AVX_INSTR cmpnltsd, sse2, 1, 0, 0 -AVX_INSTR cmpnltss, sse, 1, 0, 0 -AVX_INSTR cmpordpd, sse2 1, 0, 1 -AVX_INSTR cmpordps, sse 1, 0, 1 -AVX_INSTR cmpordsd, sse2 1, 0, 0 -AVX_INSTR cmpordss, sse 1, 0, 0 -AVX_INSTR cmppd, sse2, 1, 1, 0 -AVX_INSTR cmpps, sse, 1, 1, 0 -AVX_INSTR cmpsd, sse2, 1, 1, 0 -AVX_INSTR cmpss, sse, 1, 1, 0 -AVX_INSTR cmpunordpd, sse2, 1, 0, 1 -AVX_INSTR cmpunordps, sse, 1, 0, 1 -AVX_INSTR cmpunordsd, sse2, 1, 0, 0 -AVX_INSTR cmpunordss, sse, 1, 0, 0 -AVX_INSTR comisd, sse2 -AVX_INSTR comiss, sse -AVX_INSTR cvtdq2pd, sse2 -AVX_INSTR cvtdq2ps, sse2 -AVX_INSTR cvtpd2dq, sse2 -AVX_INSTR cvtpd2ps, sse2 -AVX_INSTR cvtps2dq, sse2 -AVX_INSTR cvtps2pd, sse2 -AVX_INSTR cvtsd2si, sse2 -AVX_INSTR cvtsd2ss, sse2, 1, 0, 0 -AVX_INSTR cvtsi2sd, sse2, 1, 0, 0 -AVX_INSTR cvtsi2ss, sse, 1, 0, 0 -AVX_INSTR cvtss2sd, sse2, 1, 0, 0 -AVX_INSTR cvtss2si, sse -AVX_INSTR cvttpd2dq, sse2 -AVX_INSTR cvttps2dq, sse2 -AVX_INSTR cvttsd2si, sse2 -AVX_INSTR cvttss2si, sse -AVX_INSTR divpd, sse2, 1, 0, 0 -AVX_INSTR divps, sse, 1, 0, 0 -AVX_INSTR divsd, sse2, 1, 0, 0 -AVX_INSTR divss, sse, 1, 0, 0 -AVX_INSTR dppd, sse4, 1, 1, 0 -AVX_INSTR dpps, sse4, 1, 1, 0 -AVX_INSTR extractps, sse4 -AVX_INSTR haddpd, sse3, 1, 0, 0 -AVX_INSTR haddps, sse3, 1, 0, 0 -AVX_INSTR hsubpd, sse3, 1, 0, 0 -AVX_INSTR hsubps, sse3, 1, 0, 0 -AVX_INSTR insertps, sse4, 1, 1, 0 -AVX_INSTR lddqu, sse3 -AVX_INSTR ldmxcsr, sse -AVX_INSTR maskmovdqu, sse2 -AVX_INSTR maxpd, sse2, 1, 0, 1 -AVX_INSTR maxps, sse, 1, 0, 1 -AVX_INSTR maxsd, sse2, 1, 0, 0 -AVX_INSTR maxss, sse, 1, 0, 0 -AVX_INSTR minpd, sse2, 1, 0, 1 -AVX_INSTR minps, sse, 1, 0, 1 -AVX_INSTR minsd, sse2, 1, 0, 0 -AVX_INSTR minss, sse, 1, 0, 0 -AVX_INSTR movapd, sse2 -AVX_INSTR movaps, sse -AVX_INSTR movd, mmx -AVX_INSTR movddup, sse3 -AVX_INSTR movdqa, sse2 -AVX_INSTR movdqu, sse2 -AVX_INSTR movhlps, sse, 1, 0, 0 -AVX_INSTR movhpd, sse2, 1, 0, 0 -AVX_INSTR movhps, sse, 1, 0, 0 -AVX_INSTR movlhps, sse, 1, 0, 0 -AVX_INSTR movlpd, sse2, 1, 0, 0 -AVX_INSTR movlps, sse, 1, 0, 0 -AVX_INSTR movmskpd, sse2 -AVX_INSTR movmskps, sse -AVX_INSTR movntdq, sse2 -AVX_INSTR movntdqa, sse4 -AVX_INSTR movntpd, sse2 -AVX_INSTR movntps, sse -AVX_INSTR movq, mmx -AVX_INSTR movsd, sse2, 1, 0, 0 -AVX_INSTR movshdup, sse3 -AVX_INSTR movsldup, sse3 -AVX_INSTR movss, sse, 1, 0, 0 -AVX_INSTR movupd, sse2 -AVX_INSTR movups, sse -AVX_INSTR mpsadbw, sse4, 0, 1, 0 -AVX_INSTR mulpd, sse2, 1, 0, 1 -AVX_INSTR mulps, sse, 1, 0, 1 -AVX_INSTR mulsd, sse2, 1, 0, 0 -AVX_INSTR mulss, sse, 1, 0, 0 -AVX_INSTR orpd, sse2, 1, 0, 1 -AVX_INSTR orps, sse, 1, 0, 1 -AVX_INSTR pabsb, ssse3 -AVX_INSTR pabsd, ssse3 -AVX_INSTR pabsw, ssse3 -AVX_INSTR packsswb, mmx, 0, 0, 0 -AVX_INSTR packssdw, mmx, 0, 0, 0 -AVX_INSTR packuswb, mmx, 0, 0, 0 -AVX_INSTR packusdw, sse4, 0, 0, 0 -AVX_INSTR paddb, mmx, 0, 0, 1 -AVX_INSTR paddw, mmx, 0, 0, 1 -AVX_INSTR paddd, mmx, 0, 0, 1 -AVX_INSTR paddq, sse2, 0, 0, 1 -AVX_INSTR paddsb, mmx, 0, 0, 1 -AVX_INSTR paddsw, mmx, 0, 0, 1 -AVX_INSTR paddusb, mmx, 0, 0, 1 -AVX_INSTR paddusw, mmx, 0, 0, 1 -AVX_INSTR palignr, ssse3, 0, 1, 0 -AVX_INSTR pand, mmx, 0, 0, 1 -AVX_INSTR pandn, mmx, 0, 0, 0 -AVX_INSTR pavgb, mmx2, 0, 0, 1 -AVX_INSTR pavgw, mmx2, 0, 0, 1 -AVX_INSTR pblendvb, sse4 ; can't be emulated -AVX_INSTR pblendw, sse4, 0, 1, 0 -AVX_INSTR pclmulqdq, fnord, 0, 1, 0 -AVX_INSTR pclmulhqhqdq, fnord, 0, 0, 0 -AVX_INSTR pclmulhqlqdq, fnord, 0, 0, 0 -AVX_INSTR pclmullqhqdq, fnord, 0, 0, 0 -AVX_INSTR pclmullqlqdq, fnord, 0, 0, 0 -AVX_INSTR pcmpestri, sse42 -AVX_INSTR pcmpestrm, sse42 -AVX_INSTR pcmpistri, sse42 -AVX_INSTR pcmpistrm, sse42 -AVX_INSTR pcmpeqb, mmx, 0, 0, 1 -AVX_INSTR pcmpeqw, mmx, 0, 0, 1 -AVX_INSTR pcmpeqd, mmx, 0, 0, 1 -AVX_INSTR pcmpeqq, sse4, 0, 0, 1 -AVX_INSTR pcmpgtb, mmx, 0, 0, 0 -AVX_INSTR pcmpgtw, mmx, 0, 0, 0 -AVX_INSTR pcmpgtd, mmx, 0, 0, 0 -AVX_INSTR pcmpgtq, sse42, 0, 0, 0 -AVX_INSTR pextrb, sse4 -AVX_INSTR pextrd, sse4 -AVX_INSTR pextrq, sse4 -AVX_INSTR pextrw, mmx2 -AVX_INSTR phaddw, ssse3, 0, 0, 0 -AVX_INSTR phaddd, ssse3, 0, 0, 0 -AVX_INSTR phaddsw, ssse3, 0, 0, 0 -AVX_INSTR phminposuw, sse4 -AVX_INSTR phsubw, ssse3, 0, 0, 0 -AVX_INSTR phsubd, ssse3, 0, 0, 0 -AVX_INSTR phsubsw, ssse3, 0, 0, 0 -AVX_INSTR pinsrb, sse4, 0, 1, 0 -AVX_INSTR pinsrd, sse4, 0, 1, 0 -AVX_INSTR pinsrq, sse4, 0, 1, 0 -AVX_INSTR pinsrw, mmx2, 0, 1, 0 -AVX_INSTR pmaddwd, mmx, 0, 0, 1 -AVX_INSTR pmaddubsw, ssse3, 0, 0, 0 -AVX_INSTR pmaxsb, sse4, 0, 0, 1 -AVX_INSTR pmaxsw, mmx2, 0, 0, 1 -AVX_INSTR pmaxsd, sse4, 0, 0, 1 -AVX_INSTR pmaxub, mmx2, 0, 0, 1 -AVX_INSTR pmaxuw, sse4, 0, 0, 1 -AVX_INSTR pmaxud, sse4, 0, 0, 1 -AVX_INSTR pminsb, sse4, 0, 0, 1 -AVX_INSTR pminsw, mmx2, 0, 0, 1 -AVX_INSTR pminsd, sse4, 0, 0, 1 -AVX_INSTR pminub, mmx2, 0, 0, 1 -AVX_INSTR pminuw, sse4, 0, 0, 1 -AVX_INSTR pminud, sse4, 0, 0, 1 -AVX_INSTR pmovmskb, mmx2 -AVX_INSTR pmovsxbw, sse4 -AVX_INSTR pmovsxbd, sse4 -AVX_INSTR pmovsxbq, sse4 -AVX_INSTR pmovsxwd, sse4 -AVX_INSTR pmovsxwq, sse4 -AVX_INSTR pmovsxdq, sse4 -AVX_INSTR pmovzxbw, sse4 -AVX_INSTR pmovzxbd, sse4 -AVX_INSTR pmovzxbq, sse4 -AVX_INSTR pmovzxwd, sse4 -AVX_INSTR pmovzxwq, sse4 -AVX_INSTR pmovzxdq, sse4 -AVX_INSTR pmuldq, sse4, 0, 0, 1 -AVX_INSTR pmulhrsw, ssse3, 0, 0, 1 -AVX_INSTR pmulhuw, mmx2, 0, 0, 1 -AVX_INSTR pmulhw, mmx, 0, 0, 1 -AVX_INSTR pmullw, mmx, 0, 0, 1 -AVX_INSTR pmulld, sse4, 0, 0, 1 -AVX_INSTR pmuludq, sse2, 0, 0, 1 -AVX_INSTR por, mmx, 0, 0, 1 -AVX_INSTR psadbw, mmx2, 0, 0, 1 -AVX_INSTR pshufb, ssse3, 0, 0, 0 -AVX_INSTR pshufd, sse2 -AVX_INSTR pshufhw, sse2 -AVX_INSTR pshuflw, sse2 -AVX_INSTR psignb, ssse3, 0, 0, 0 -AVX_INSTR psignw, ssse3, 0, 0, 0 -AVX_INSTR psignd, ssse3, 0, 0, 0 -AVX_INSTR psllw, mmx, 0, 0, 0 -AVX_INSTR pslld, mmx, 0, 0, 0 -AVX_INSTR psllq, mmx, 0, 0, 0 -AVX_INSTR pslldq, sse2, 0, 0, 0 -AVX_INSTR psraw, mmx, 0, 0, 0 -AVX_INSTR psrad, mmx, 0, 0, 0 -AVX_INSTR psrlw, mmx, 0, 0, 0 -AVX_INSTR psrld, mmx, 0, 0, 0 -AVX_INSTR psrlq, mmx, 0, 0, 0 -AVX_INSTR psrldq, sse2, 0, 0, 0 -AVX_INSTR psubb, mmx, 0, 0, 0 -AVX_INSTR psubw, mmx, 0, 0, 0 -AVX_INSTR psubd, mmx, 0, 0, 0 -AVX_INSTR psubq, sse2, 0, 0, 0 -AVX_INSTR psubsb, mmx, 0, 0, 0 -AVX_INSTR psubsw, mmx, 0, 0, 0 -AVX_INSTR psubusb, mmx, 0, 0, 0 -AVX_INSTR psubusw, mmx, 0, 0, 0 -AVX_INSTR ptest, sse4 -AVX_INSTR punpckhbw, mmx, 0, 0, 0 -AVX_INSTR punpckhwd, mmx, 0, 0, 0 -AVX_INSTR punpckhdq, mmx, 0, 0, 0 -AVX_INSTR punpckhqdq, sse2, 0, 0, 0 -AVX_INSTR punpcklbw, mmx, 0, 0, 0 -AVX_INSTR punpcklwd, mmx, 0, 0, 0 -AVX_INSTR punpckldq, mmx, 0, 0, 0 -AVX_INSTR punpcklqdq, sse2, 0, 0, 0 -AVX_INSTR pxor, mmx, 0, 0, 1 -AVX_INSTR rcpps, sse -AVX_INSTR rcpss, sse, 1, 0, 0 -AVX_INSTR roundpd, sse4 -AVX_INSTR roundps, sse4 -AVX_INSTR roundsd, sse4, 1, 1, 0 -AVX_INSTR roundss, sse4, 1, 1, 0 -AVX_INSTR rsqrtps, sse -AVX_INSTR rsqrtss, sse, 1, 0, 0 -AVX_INSTR shufpd, sse2, 1, 1, 0 -AVX_INSTR shufps, sse, 1, 1, 0 -AVX_INSTR sqrtpd, sse2 -AVX_INSTR sqrtps, sse -AVX_INSTR sqrtsd, sse2, 1, 0, 0 -AVX_INSTR sqrtss, sse, 1, 0, 0 -AVX_INSTR stmxcsr, sse -AVX_INSTR subpd, sse2, 1, 0, 0 -AVX_INSTR subps, sse, 1, 0, 0 -AVX_INSTR subsd, sse2, 1, 0, 0 -AVX_INSTR subss, sse, 1, 0, 0 -AVX_INSTR ucomisd, sse2 -AVX_INSTR ucomiss, sse -AVX_INSTR unpckhpd, sse2, 1, 0, 0 -AVX_INSTR unpckhps, sse, 1, 0, 0 -AVX_INSTR unpcklpd, sse2, 1, 0, 0 -AVX_INSTR unpcklps, sse, 1, 0, 0 -AVX_INSTR xorpd, sse2, 1, 0, 1 -AVX_INSTR xorps, sse, 1, 0, 1 - -; 3DNow instructions, for sharing code between AVX, SSE and 3DN -AVX_INSTR pfadd, 3dnow, 1, 0, 1 -AVX_INSTR pfsub, 3dnow, 1, 0, 0 -AVX_INSTR pfmul, 3dnow, 1, 0, 1 - -; base-4 constants for shuffles -%assign i 0 -%rep 256 - %assign j ((i>>6)&3)*1000 + ((i>>4)&3)*100 + ((i>>2)&3)*10 + (i&3) - %if j < 10 - CAT_XDEFINE q000, j, i - %elif j < 100 - CAT_XDEFINE q00, j, i - %elif j < 1000 - CAT_XDEFINE q0, j, i - %else - CAT_XDEFINE q, j, i - %endif - %assign i i+1 -%endrep -%undef i -%undef j - -%macro FMA_INSTR 3 - %macro %1 4-7 %1, %2, %3 - %if cpuflag(xop) - v%5 %1, %2, %3, %4 - %elifnidn %1, %4 - %6 %1, %2, %3 - %7 %1, %4 - %else - %error non-xop emulation of ``%5 %1, %2, %3, %4'' is not supported - %endif - %endmacro -%endmacro - -FMA_INSTR pmacsww, pmullw, paddw -FMA_INSTR pmacsdd, pmulld, paddd ; sse4 emulation -FMA_INSTR pmacsdql, pmuldq, paddq ; sse4 emulation -FMA_INSTR pmadcswd, pmaddwd, paddd - -; Macros for consolidating FMA3 and FMA4 using 4-operand (dst, src1, src2, src3) syntax. -; FMA3 is only possible if dst is the same as one of the src registers. -; Either src2 or src3 can be a memory operand. -%macro FMA4_INSTR 2-* - %push fma4_instr - %xdefine %$prefix %1 - %rep %0 - 1 - %macro %$prefix%2 4-6 %$prefix, %2 - %if notcpuflag(fma3) && notcpuflag(fma4) - %error use of ``%5%6'' fma instruction in cpuname function: current_function - %elif cpuflag(fma4) - v%5%6 %1, %2, %3, %4 - %elifidn %1, %2 - ; If %3 or %4 is a memory operand it needs to be encoded as the last operand. - %ifnum sizeof%3 - v%{5}213%6 %2, %3, %4 - %else - v%{5}132%6 %2, %4, %3 - %endif - %elifidn %1, %3 - v%{5}213%6 %3, %2, %4 - %elifidn %1, %4 - v%{5}231%6 %4, %2, %3 - %else - %error fma3 emulation of ``%5%6 %1, %2, %3, %4'' is not supported - %endif - %endmacro - %rotate 1 - %endrep - %pop -%endmacro - -FMA4_INSTR fmadd, pd, ps, sd, ss -FMA4_INSTR fmaddsub, pd, ps -FMA4_INSTR fmsub, pd, ps, sd, ss -FMA4_INSTR fmsubadd, pd, ps -FMA4_INSTR fnmadd, pd, ps, sd, ss -FMA4_INSTR fnmsub, pd, ps, sd, ss - -; Macros for converting VEX instructions to equivalent EVEX ones. -%macro EVEX_INSTR 2-3 0 ; vex, evex, prefer_evex - %macro %1 2-7 fnord, fnord, %1, %2, %3 - %ifidn %3, fnord - %define %%args %1, %2 - %elifidn %4, fnord - %define %%args %1, %2, %3 - %else - %define %%args %1, %2, %3, %4 - %endif - %assign %%evex_required cpuflag(avx512) & %7 - %ifnum regnumof%1 - %if regnumof%1 >= 16 || sizeof%1 > 32 - %assign %%evex_required 1 - %endif - %endif - %ifnum regnumof%2 - %if regnumof%2 >= 16 || sizeof%2 > 32 - %assign %%evex_required 1 - %endif - %endif - %if %%evex_required - %6 %%args - %else - %5 %%args ; Prefer VEX over EVEX due to shorter instruction length - %endif - %endmacro -%endmacro - -EVEX_INSTR vbroadcastf128, vbroadcastf32x4 -EVEX_INSTR vbroadcasti128, vbroadcasti32x4 -EVEX_INSTR vextractf128, vextractf32x4 -EVEX_INSTR vextracti128, vextracti32x4 -EVEX_INSTR vinsertf128, vinsertf32x4 -EVEX_INSTR vinserti128, vinserti32x4 -EVEX_INSTR vmovdqa, vmovdqa32 -EVEX_INSTR vmovdqu, vmovdqu32 -EVEX_INSTR vpand, vpandd -EVEX_INSTR vpandn, vpandnd -EVEX_INSTR vpor, vpord -EVEX_INSTR vpxor, vpxord -EVEX_INSTR vrcpps, vrcp14ps, 1 ; EVEX versions have higher precision -EVEX_INSTR vrcpss, vrcp14ss, 1 -EVEX_INSTR vrsqrtps, vrsqrt14ps, 1 -EVEX_INSTR vrsqrtss, vrsqrt14ss, 1 diff --git a/Source/Lib/ASM_AVX2/x86util.asm b/Source/Lib/ASM_AVX2/x86util.asm deleted file mode 100644 index 4139fba4b..000000000 --- a/Source/Lib/ASM_AVX2/x86util.asm +++ /dev/null @@ -1,892 +0,0 @@ -;***************************************************************************** -;* x86util.asm: x86 utility macros -;***************************************************************************** -; -; Copyright(c) 2018 Intel Corporation -; SPDX - License - Identifier: BSD - 2 - Clause - Patent -; - - -%define ARCH_X86_64 1 -%define HIGH_BIT_DEPTH 0 -%define BIT_DEPTH 8 - -%assign FENC_STRIDE 64 -%assign FDEC_STRIDE 32 - -%assign SIZEOF_PIXEL 1 -%assign SIZEOF_DCTCOEF 2 -%define pixel byte -%define vpbroadcastdct vpbroadcastw -%define vpbroadcastpix vpbroadcastb -%if HIGH_BIT_DEPTH - %assign SIZEOF_PIXEL 2 - %assign SIZEOF_DCTCOEF 4 - %define pixel word - %define vpbroadcastdct vpbroadcastd - %define vpbroadcastpix vpbroadcastw -%endif - -%assign FENC_STRIDEB SIZEOF_PIXEL*FENC_STRIDE -%assign FDEC_STRIDEB SIZEOF_PIXEL*FDEC_STRIDE - -%assign PIXEL_MAX ((1 << BIT_DEPTH)-1) - -%macro FIX_STRIDES 1-* -%if HIGH_BIT_DEPTH -%rep %0 - add %1, %1 - %rotate 1 -%endrep -%endif -%endmacro - - -%macro SBUTTERFLY 4 -%ifidn %1, dqqq - vperm2i128 m%4, m%2, m%3, q0301 ; punpckh - vinserti128 m%2, m%2, xm%3, 1 ; punpckl -%elif avx_enabled && mmsize >= 16 - punpckh%1 m%4, m%2, m%3 - punpckl%1 m%2, m%3 -%else - mova m%4, m%2 - punpckl%1 m%2, m%3 - punpckh%1 m%4, m%3 -%endif - SWAP %3, %4 -%endmacro - -%macro SBUTTERFLY2 4 - punpckl%1 m%4, m%2, m%3 - punpckh%1 m%2, m%2, m%3 - SWAP %2, %4, %3 -%endmacro - -%macro TRANSPOSE4x4W 5 - SBUTTERFLY wd, %1, %2, %5 - SBUTTERFLY wd, %3, %4, %5 - SBUTTERFLY dq, %1, %3, %5 - SBUTTERFLY dq, %2, %4, %5 - SWAP %2, %3 -%endmacro - -%macro TRANSPOSE2x4x4W 5 - SBUTTERFLY wd, %1, %2, %5 - SBUTTERFLY wd, %3, %4, %5 - SBUTTERFLY dq, %1, %3, %5 - SBUTTERFLY dq, %2, %4, %5 - SBUTTERFLY qdq, %1, %2, %5 - SBUTTERFLY qdq, %3, %4, %5 -%endmacro - -%macro TRANSPOSE4x4D 5 - SBUTTERFLY dq, %1, %2, %5 - SBUTTERFLY dq, %3, %4, %5 - SBUTTERFLY qdq, %1, %3, %5 - SBUTTERFLY qdq, %2, %4, %5 - SWAP %2, %3 -%endmacro - -%macro TRANSPOSE8x8W 9-11 -%if ARCH_X86_64 - SBUTTERFLY wd, %1, %2, %9 - SBUTTERFLY wd, %3, %4, %9 - SBUTTERFLY wd, %5, %6, %9 - SBUTTERFLY wd, %7, %8, %9 - SBUTTERFLY dq, %1, %3, %9 - SBUTTERFLY dq, %2, %4, %9 - SBUTTERFLY dq, %5, %7, %9 - SBUTTERFLY dq, %6, %8, %9 - SBUTTERFLY qdq, %1, %5, %9 - SBUTTERFLY qdq, %2, %6, %9 - SBUTTERFLY qdq, %3, %7, %9 - SBUTTERFLY qdq, %4, %8, %9 - SWAP %2, %5 - SWAP %4, %7 -%else -; in: m0..m7, unless %11 in which case m6 is in %9 -; out: m0..m7, unless %11 in which case m4 is in %10 -; spills into %9 and %10 -%if %0<11 - movdqa %9, m%7 -%endif - SBUTTERFLY wd, %1, %2, %7 - movdqa %10, m%2 - movdqa m%7, %9 - SBUTTERFLY wd, %3, %4, %2 - SBUTTERFLY wd, %5, %6, %2 - SBUTTERFLY wd, %7, %8, %2 - SBUTTERFLY dq, %1, %3, %2 - movdqa %9, m%3 - movdqa m%2, %10 - SBUTTERFLY dq, %2, %4, %3 - SBUTTERFLY dq, %5, %7, %3 - SBUTTERFLY dq, %6, %8, %3 - SBUTTERFLY qdq, %1, %5, %3 - SBUTTERFLY qdq, %2, %6, %3 - movdqa %10, m%2 - movdqa m%3, %9 - SBUTTERFLY qdq, %3, %7, %2 - SBUTTERFLY qdq, %4, %8, %2 - SWAP %2, %5 - SWAP %4, %7 -%if %0<11 - movdqa m%5, %10 -%endif -%endif -%endmacro - -%macro WIDEN_SXWD 2 - punpckhwd m%2, m%1 - psrad m%2, 16 -%if cpuflag(sse4) - pmovsxwd m%1, m%1 -%else - punpcklwd m%1, m%1 - psrad m%1, 16 -%endif -%endmacro - -%macro ABSW 2-3 ; dst, src, tmp (tmp used only if dst==src) -%if cpuflag(ssse3) - pabsw %1, %2 -%elifidn %3, sign ; version for pairing with PSIGNW: modifies src - pxor %1, %1 - pcmpgtw %1, %2 - pxor %2, %1 - psubw %2, %1 - SWAP %1, %2 -%elifidn %1, %2 - pxor %3, %3 - psubw %3, %1 - pmaxsw %1, %3 -%elifid %2 - pxor %1, %1 - psubw %1, %2 - pmaxsw %1, %2 -%elif %0 == 2 - pxor %1, %1 - psubw %1, %2 - pmaxsw %1, %2 -%else - mova %1, %2 - pxor %3, %3 - psubw %3, %1 - pmaxsw %1, %3 -%endif -%endmacro - -%macro ABSW2 6 ; dst1, dst2, src1, src2, tmp, tmp -%if cpuflag(ssse3) - pabsw %1, %3 - pabsw %2, %4 -%elifidn %1, %3 - pxor %5, %5 - pxor %6, %6 - psubw %5, %1 - psubw %6, %2 - pmaxsw %1, %5 - pmaxsw %2, %6 -%else - pxor %1, %1 - pxor %2, %2 - psubw %1, %3 - psubw %2, %4 - pmaxsw %1, %3 - pmaxsw %2, %4 -%endif -%endmacro - -%macro ABSB 2 -%if cpuflag(ssse3) - pabsb %1, %1 -%else - pxor %2, %2 - psubb %2, %1 - pminub %1, %2 -%endif -%endmacro - -%macro ABSD 2-3 -%if cpuflag(ssse3) - pabsd %1, %2 -%else - %define %%s %2 -%if %0 == 3 - mova %3, %2 - %define %%s %3 -%endif - pxor %1, %1 - pcmpgtd %1, %%s - pxor %%s, %1 - psubd %%s, %1 - SWAP %1, %%s -%endif -%endmacro - -%macro PSIGN 3-4 -%if cpuflag(ssse3) && %0 == 4 - psign%1 %2, %3, %4 -%elif cpuflag(ssse3) - psign%1 %2, %3 -%elif %0 == 4 - pxor %2, %3, %4 - psub%1 %2, %4 -%else - pxor %2, %3 - psub%1 %2, %3 -%endif -%endmacro - -%define PSIGNW PSIGN w, -%define PSIGND PSIGN d, - -%macro SPLATB_LOAD 3 -%if cpuflag(ssse3) - movd %1, [%2-3] - pshufb %1, %3 -%else - movd %1, [%2-3] ;to avoid crossing a cacheline - punpcklbw %1, %1 - SPLATW %1, %1, 3 -%endif -%endmacro - -%imacro SPLATW 2-3 0 -%if cpuflag(avx2) && %3 == 0 - vpbroadcastw %1, %2 -%else - PSHUFLW %1, %2, (%3)*q1111 -%if mmsize == 16 - punpcklqdq %1, %1 -%endif -%endif -%endmacro - -%imacro SPLATD 2-3 0 -%if mmsize == 16 - pshufd %1, %2, (%3)*q1111 -%else - pshufw %1, %2, (%3)*q0101 + ((%3)+1)*q1010 -%endif -%endmacro - -%macro CLIPW 3 ;(dst, min, max) - pmaxsw %1, %2 - pminsw %1, %3 -%endmacro - -%macro CLIPW2 4 ;(dst0, dst1, min, max) - pmaxsw %1, %3 - pmaxsw %2, %3 - pminsw %1, %4 - pminsw %2, %4 -%endmacro - -%macro MOVHL 2 ; dst, src -%ifidn %1, %2 - punpckhqdq %1, %2 -%elif cpuflag(avx) - punpckhqdq %1, %2, %2 -%elif cpuflag(sse4) - pshufd %1, %2, q3232 ; pshufd is slow on some older CPUs, so only use it on more modern ones -%else - movhlps %1, %2 ; may cause an int/float domain transition and has a dependency on dst -%endif -%endmacro - -%macro HADDD 2 ; sum junk -%if sizeof%1 >= 64 - vextracti32x8 ymm%2, zmm%1, 1 - paddd ymm%1, ymm%2 -%endif -%if sizeof%1 >= 32 - vextracti128 xmm%2, ymm%1, 1 - paddd xmm%1, xmm%2 -%endif -%if sizeof%1 >= 16 - MOVHL xmm%2, xmm%1 - paddd xmm%1, xmm%2 -%endif -%if cpuflag(xop) && sizeof%1 == 16 - vphadddq xmm%1, xmm%1 -%endif -%if notcpuflag(xop) - PSHUFLW xmm%2, xmm%1, q1032 - paddd xmm%1, xmm%2 -%endif -%endmacro - -%macro HADDW 2 ; reg, tmp -%if cpuflag(xop) && sizeof%1 == 16 - vphaddwq %1, %1 - MOVHL %2, %1 - paddd %1, %2 -%else - pmaddwd %1, [pw_1] - HADDD %1, %2 -%endif -%endmacro - -%macro HADDUWD 2 -%if cpuflag(xop) && sizeof%1 == 16 - vphadduwd %1, %1 -%else - psrld %2, %1, 16 - pslld %1, 16 - psrld %1, 16 - paddd %1, %2 -%endif -%endmacro - -%macro HADDUW 2 -%if cpuflag(xop) && sizeof%1 == 16 - vphadduwq %1, %1 - MOVHL %2, %1 - paddd %1, %2 -%else - HADDUWD %1, %2 - HADDD %1, %2 -%endif -%endmacro - -%macro PALIGNR 4-5 ; [dst,] src1, src2, imm, tmp -; AVX2 version uses a precalculated extra input that -; can be re-used across calls -%if sizeof%1==32 - ; %3 = abcdefgh ijklmnop (lower address) - ; %2 = ABCDEFGH IJKLMNOP (higher address) - vperm2i128 %4, %1, %2, q0003 ; %4 = ijklmnop ABCDEFGH -%if %3 < 16 - palignr %1, %4, %2, %3 ; %1 = bcdefghi jklmnopA -%else - palignr %1, %2, %4, %3-16 ; %1 = pABCDEFG HIJKLMNO -%endif -%elif cpuflag(ssse3) - %if %0==5 - palignr %1, %2, %3, %4 - %else - palignr %1, %2, %3 - %endif -%else - %define %%dst %1 - %if %0==5 - %ifnidn %1, %2 - mova %%dst, %2 - %endif - %rotate 1 - %endif - %ifnidn %4, %2 - mova %4, %2 - %endif - %if mmsize==8 - psllq %%dst, (8-%3)*8 - psrlq %4, %3*8 - %else - pslldq %%dst, 16-%3 - psrldq %4, %3 - %endif - por %%dst, %4 -%endif -%endmacro - -%macro PSHUFLW 1+ - %if mmsize == 8 - pshufw %1 - %else - pshuflw %1 - %endif -%endmacro - -; shift a mmxreg by n bytes, or a xmmreg by 2*n bytes -; values shifted in are undefined -; faster if dst==src -%define PSLLPIX PSXLPIX l, -1, ;dst, src, shift -%define PSRLPIX PSXLPIX r, 1, ;dst, src, shift -%macro PSXLPIX 5 - %if mmsize == 8 - %if %5&1 - ps%1lq %3, %4, %5*8 - %else - pshufw %3, %4, (q3210<<8>>(8+%2*%5))&0xff - %endif - %else - ps%1ldq %3, %4, %5*2 - %endif -%endmacro - -%macro DEINTB 5 ; mask, reg1, mask, reg2, optional src to fill masks from -%ifnum %5 - pand m%3, m%5, m%4 ; src .. y6 .. y4 - pand m%1, m%5, m%2 ; dst .. y6 .. y4 -%else - mova m%1, %5 - pand m%3, m%1, m%4 ; src .. y6 .. y4 - pand m%1, m%1, m%2 ; dst .. y6 .. y4 -%endif - psrlw m%2, 8 ; dst .. y7 .. y5 - psrlw m%4, 8 ; src .. y7 .. y5 -%endmacro - -%macro SUMSUB_BA 3-4 -%if %0==3 - padd%1 m%2, m%3 - padd%1 m%3, m%3 - psub%1 m%3, m%2 -%elif avx_enabled - padd%1 m%4, m%2, m%3 - psub%1 m%3, m%2 - SWAP %2, %4 -%else - mova m%4, m%2 - padd%1 m%2, m%3 - psub%1 m%3, m%4 -%endif -%endmacro - -%macro SUMSUB_BADC 5-6 -%if %0==6 - SUMSUB_BA %1, %2, %3, %6 - SUMSUB_BA %1, %4, %5, %6 -%else - padd%1 m%2, m%3 - padd%1 m%4, m%5 - padd%1 m%3, m%3 - padd%1 m%5, m%5 - psub%1 m%3, m%2 - psub%1 m%5, m%4 -%endif -%endmacro - -%macro HADAMARD4_V 4+ - SUMSUB_BADC w, %1, %2, %3, %4 - SUMSUB_BADC w, %1, %3, %2, %4 -%endmacro - -%macro HADAMARD8_V 8+ - SUMSUB_BADC w, %1, %2, %3, %4 - SUMSUB_BADC w, %5, %6, %7, %8 - SUMSUB_BADC w, %1, %3, %2, %4 - SUMSUB_BADC w, %5, %7, %6, %8 - SUMSUB_BADC w, %1, %5, %2, %6 - SUMSUB_BADC w, %3, %7, %4, %8 -%endmacro - -%macro TRANS_SSE2 5-6 -; TRANSPOSE2x2 -; %1: transpose width (d/q) - use SBUTTERFLY qdq for dq -; %2: ord/unord (for compat with sse4, unused) -; %3/%4: source regs -; %5/%6: tmp regs -%ifidn %1, d -%define mask [mask_10] -%define shift 16 -%elifidn %1, q -%define mask [mask_1100] -%define shift 32 -%endif -%if %0==6 ; less dependency if we have two tmp - mova m%5, mask ; ff00 - mova m%6, m%4 ; x5x4 - psll%1 m%4, shift ; x4.. - pand m%6, m%5 ; x5.. - pandn m%5, m%3 ; ..x0 - psrl%1 m%3, shift ; ..x1 - por m%4, m%5 ; x4x0 - por m%3, m%6 ; x5x1 -%else ; more dependency, one insn less. sometimes faster, sometimes not - mova m%5, m%4 ; x5x4 - psll%1 m%4, shift ; x4.. - pxor m%4, m%3 ; (x4^x1)x0 - pand m%4, mask ; (x4^x1).. - pxor m%3, m%4 ; x4x0 - psrl%1 m%4, shift ; ..(x1^x4) - pxor m%5, m%4 ; x5x1 - SWAP %4, %3, %5 -%endif -%endmacro - -%macro TRANS_SSE4 5-6 ; see above -%ifidn %1, d -%ifidn %2, ord - psrl%1 m%5, m%3, 16 - pblendw m%5, m%4, q2222 - psll%1 m%4, 16 - pblendw m%4, m%3, q1111 - SWAP %3, %5 -%else -%if avx_enabled - pblendw m%5, m%3, m%4, q2222 - SWAP %3, %5 -%else - mova m%5, m%3 - pblendw m%3, m%4, q2222 -%endif - psll%1 m%4, 16 - psrl%1 m%5, 16 - por m%4, m%5 -%endif -%elifidn %1, q - shufps m%5, m%3, m%4, q3131 - shufps m%3, m%3, m%4, q2020 - SWAP %4, %5 -%endif -%endmacro - -%macro TRANS_XOP 5-6 -%ifidn %1, d - vpperm m%5, m%3, m%4, [transd_shuf1] - vpperm m%3, m%3, m%4, [transd_shuf2] -%elifidn %1, q - shufps m%5, m%3, m%4, q3131 - shufps m%3, m%4, q2020 -%endif - SWAP %4, %5 -%endmacro - -%macro HADAMARD 5-6 -; %1=distance in words (0 for vertical pass, 1/2/4 for horizontal passes) -; %2=sumsub/max/amax (sum and diff / maximum / maximum of absolutes) -; %3/%4: regs -; %5(%6): tmpregs -%if %1!=0 ; have to reorder stuff for horizontal op - %ifidn %2, sumsub - %define ORDER ord - ; sumsub needs order because a-b != b-a unless a=b - %else - %define ORDER unord - ; if we just max, order doesn't matter (allows pblendw+or in sse4) - %endif - %if %1==1 - TRANS d, ORDER, %3, %4, %5, %6 - %elif %1==2 - %if mmsize==8 - SBUTTERFLY dq, %3, %4, %5 - %else - TRANS q, ORDER, %3, %4, %5, %6 - %endif - %elif %1==4 - SBUTTERFLY qdq, %3, %4, %5 - %elif %1==8 - SBUTTERFLY dqqq, %3, %4, %5 - %endif -%endif -%ifidn %2, sumsub - SUMSUB_BA w, %3, %4, %5 -%else - %ifidn %2, amax - %if %0==6 - ABSW2 m%3, m%4, m%3, m%4, m%5, m%6 - %else - ABSW m%3, m%3, m%5 - ABSW m%4, m%4, m%5 - %endif - %endif - pmaxsw m%3, m%4 -%endif -%endmacro - - -%macro HADAMARD2_2D 6-7 sumsub - HADAMARD 0, sumsub, %1, %2, %5 - HADAMARD 0, sumsub, %3, %4, %5 - SBUTTERFLY %6, %1, %2, %5 -%ifnum %7 - HADAMARD 0, amax, %1, %2, %5, %7 -%else - HADAMARD 0, %7, %1, %2, %5 -%endif - SBUTTERFLY %6, %3, %4, %5 -%ifnum %7 - HADAMARD 0, amax, %3, %4, %5, %7 -%else - HADAMARD 0, %7, %3, %4, %5 -%endif -%endmacro - -%macro HADAMARD4_2D 5-6 sumsub - HADAMARD2_2D %1, %2, %3, %4, %5, wd - HADAMARD2_2D %1, %3, %2, %4, %5, dq, %6 - SWAP %2, %3 -%endmacro - -%macro HADAMARD4_2D_SSE 5-6 sumsub - HADAMARD 0, sumsub, %1, %2, %5 ; 1st V row 0 + 1 - HADAMARD 0, sumsub, %3, %4, %5 ; 1st V row 2 + 3 - SBUTTERFLY wd, %1, %2, %5 ; %1: m0 1+0 %2: m1 1+0 - SBUTTERFLY wd, %3, %4, %5 ; %3: m0 3+2 %4: m1 3+2 - HADAMARD2_2D %1, %3, %2, %4, %5, dq - SBUTTERFLY qdq, %1, %2, %5 - HADAMARD 0, %6, %1, %2, %5 ; 2nd H m1/m0 row 0+1 - SBUTTERFLY qdq, %3, %4, %5 - HADAMARD 0, %6, %3, %4, %5 ; 2nd H m1/m0 row 2+3 -%endmacro - -%macro HADAMARD8_2D 9-10 sumsub - HADAMARD2_2D %1, %2, %3, %4, %9, wd - HADAMARD2_2D %5, %6, %7, %8, %9, wd - HADAMARD2_2D %1, %3, %2, %4, %9, dq - HADAMARD2_2D %5, %7, %6, %8, %9, dq - HADAMARD2_2D %1, %5, %3, %7, %9, qdq, %10 - HADAMARD2_2D %2, %6, %4, %8, %9, qdq, %10 -%ifnidn %10, amax - SWAP %2, %5 - SWAP %4, %7 -%endif -%endmacro - -; doesn't include the "pmaddubsw hmul_8p" pass -%macro HADAMARD8_2D_HMUL 10 - HADAMARD4_V %1, %2, %3, %4, %9 - HADAMARD4_V %5, %6, %7, %8, %9 - SUMSUB_BADC w, %1, %5, %2, %6, %9 - HADAMARD 2, sumsub, %1, %5, %9, %10 - HADAMARD 2, sumsub, %2, %6, %9, %10 - SUMSUB_BADC w, %3, %7, %4, %8, %9 - HADAMARD 2, sumsub, %3, %7, %9, %10 - HADAMARD 2, sumsub, %4, %8, %9, %10 - HADAMARD 1, amax, %1, %5, %9, %10 - HADAMARD 1, amax, %2, %6, %9, %5 - HADAMARD 1, amax, %3, %7, %9, %5 - HADAMARD 1, amax, %4, %8, %9, %5 -%endmacro - -%macro SUMSUB2_AB 4 -%if cpuflag(xop) - pmacs%1%1 m%4, m%3, [p%1_m2], m%2 - pmacs%1%1 m%2, m%2, [p%1_2], m%3 -%elifnum %3 - psub%1 m%4, m%2, m%3 - psub%1 m%4, m%3 - padd%1 m%2, m%2 - padd%1 m%2, m%3 -%else - mova m%4, m%2 - padd%1 m%2, m%2 - padd%1 m%2, %3 - psub%1 m%4, %3 - psub%1 m%4, %3 -%endif -%endmacro - -%macro SUMSUBD2_AB 5 -%ifnum %4 - psra%1 m%5, m%2, 1 ; %3: %3>>1 - psra%1 m%4, m%3, 1 ; %2: %2>>1 - padd%1 m%4, m%2 ; %3: %3>>1+%2 - psub%1 m%5, m%3 ; %2: %2>>1-%3 - SWAP %2, %5 - SWAP %3, %4 -%else - mova %5, m%2 - mova %4, m%3 - psra%1 m%3, 1 ; %3: %3>>1 - psra%1 m%2, 1 ; %2: %2>>1 - padd%1 m%3, %5 ; %3: %3>>1+%2 - psub%1 m%2, %4 ; %2: %2>>1-%3 -%endif -%endmacro - -%macro DCT4_1D 5 -%ifnum %5 - SUMSUB_BADC w, %4, %1, %3, %2, %5 - SUMSUB_BA w, %3, %4, %5 - SUMSUB2_AB w, %1, %2, %5 - SWAP %1, %3, %4, %5, %2 -%else - SUMSUB_BADC w, %4, %1, %3, %2 - SUMSUB_BA w, %3, %4 - mova [%5], m%2 - SUMSUB2_AB w, %1, [%5], %2 - SWAP %1, %3, %4, %2 -%endif -%endmacro - -%macro IDCT4_1D 6-7 -%ifnum %6 - SUMSUBD2_AB %1, %3, %5, %7, %6 - ; %3: %3>>1-%5 %5: %3+%5>>1 - SUMSUB_BA %1, %4, %2, %7 - ; %4: %2+%4 %2: %2-%4 - SUMSUB_BADC %1, %5, %4, %3, %2, %7 - ; %5: %2+%4 + (%3+%5>>1) - ; %4: %2+%4 - (%3+%5>>1) - ; %3: %2-%4 + (%3>>1-%5) - ; %2: %2-%4 - (%3>>1-%5) -%else -%ifidn %1, w - SUMSUBD2_AB %1, %3, %5, [%6], [%6+16] -%else - SUMSUBD2_AB %1, %3, %5, [%6], [%6+32] -%endif - SUMSUB_BA %1, %4, %2 - SUMSUB_BADC %1, %5, %4, %3, %2 -%endif - SWAP %2, %5, %4 - ; %2: %2+%4 + (%3+%5>>1) row0 - ; %3: %2-%4 + (%3>>1-%5) row1 - ; %4: %2-%4 - (%3>>1-%5) row2 - ; %5: %2+%4 - (%3+%5>>1) row3 -%endmacro - - -%macro LOAD_DIFF 5-6 1 -%if HIGH_BIT_DEPTH -%if %6 ; %5 aligned? - mova %1, %4 - psubw %1, %5 -%elif cpuflag(avx) - movu %1, %4 - psubw %1, %5 -%else - movu %1, %4 - movu %2, %5 - psubw %1, %2 -%endif -%else ; !HIGH_BIT_DEPTH - movh %1, %4 - movh %2, %5 -%ifidn %3, none - punpcklbw %1, %2 - punpcklbw %2, %2 -%else - punpcklbw %1, %3 - punpcklbw %2, %3 -%endif - psubw %1, %2 -%endif ; HIGH_BIT_DEPTH -%endmacro - -%macro LOAD_DIFF8x4 8 ; 4x dst, 1x tmp, 1x mul, 2x ptr -%if BIT_DEPTH == 8 && cpuflag(ssse3) - movh m%2, [%8+%1*FDEC_STRIDE] - movh m%1, [%7+%1*FENC_STRIDE] - punpcklbw m%1, m%2 - movh m%3, [%8+%2*FDEC_STRIDE] - movh m%2, [%7+%2*FENC_STRIDE] - punpcklbw m%2, m%3 - movh m%4, [%8+%3*FDEC_STRIDE] - movh m%3, [%7+%3*FENC_STRIDE] - punpcklbw m%3, m%4 - movh m%5, [%8+%4*FDEC_STRIDE] - movh m%4, [%7+%4*FENC_STRIDE] - punpcklbw m%4, m%5 - pmaddubsw m%1, m%6 - pmaddubsw m%2, m%6 - pmaddubsw m%3, m%6 - pmaddubsw m%4, m%6 -%else - LOAD_DIFF m%1, m%5, m%6, [%7+%1*FENC_STRIDEB], [%8+%1*FDEC_STRIDEB] - LOAD_DIFF m%2, m%5, m%6, [%7+%2*FENC_STRIDEB], [%8+%2*FDEC_STRIDEB] - LOAD_DIFF m%3, m%5, m%6, [%7+%3*FENC_STRIDEB], [%8+%3*FDEC_STRIDEB] - LOAD_DIFF m%4, m%5, m%6, [%7+%4*FENC_STRIDEB], [%8+%4*FDEC_STRIDEB] -%endif -%endmacro - -%macro STORE_DCT 6 - movq [%5+%6+ 0], m%1 - movq [%5+%6+ 8], m%2 - movq [%5+%6+16], m%3 - movq [%5+%6+24], m%4 - movhps [%5+%6+32], m%1 - movhps [%5+%6+40], m%2 - movhps [%5+%6+48], m%3 - movhps [%5+%6+56], m%4 -%endmacro - -%macro STORE_IDCT 4 - movhps [r0-4*FDEC_STRIDE], %1 - movh [r0-3*FDEC_STRIDE], %1 - movhps [r0-2*FDEC_STRIDE], %2 - movh [r0-1*FDEC_STRIDE], %2 - movhps [r0+0*FDEC_STRIDE], %3 - movh [r0+1*FDEC_STRIDE], %3 - movhps [r0+2*FDEC_STRIDE], %4 - movh [r0+3*FDEC_STRIDE], %4 -%endmacro - -%macro LOAD_DIFF_8x4P 7-11 r0,r2,0,1 ; 4x dest, 2x temp, 2x pointer, increment, aligned? - LOAD_DIFF m%1, m%5, m%7, [%8], [%9], %11 - LOAD_DIFF m%2, m%6, m%7, [%8+r1], [%9+r3], %11 - LOAD_DIFF m%3, m%5, m%7, [%8+2*r1], [%9+2*r3], %11 - LOAD_DIFF m%4, m%6, m%7, [%8+r4], [%9+r5], %11 -%if %10 - lea %8, [%8+4*r1] - lea %9, [%9+4*r3] -%endif -%endmacro - -; 2xdst, 2xtmp, 2xsrcrow -%macro LOAD_DIFF16x2_AVX2 6 - pmovzxbw m%1, [r1+%5*FENC_STRIDE] - pmovzxbw m%2, [r1+%6*FENC_STRIDE] - pmovzxbw m%3, [r2+(%5-4)*FDEC_STRIDE] - pmovzxbw m%4, [r2+(%6-4)*FDEC_STRIDE] - psubw m%1, m%3 - psubw m%2, m%4 -%endmacro - -%macro DIFFx2 6-7 - movh %3, %5 - punpcklbw %3, %4 - psraw %1, 6 - paddsw %1, %3 - movh %3, %6 - punpcklbw %3, %4 - psraw %2, 6 - paddsw %2, %3 - packuswb %2, %1 -%endmacro - -; (high depth) in: %1, %2, min to clip, max to clip, mem128 -; in: %1, tmp, %3, mem64 -%macro STORE_DIFF 4-5 -%if HIGH_BIT_DEPTH - psrad %1, 6 - psrad %2, 6 - packssdw %1, %2 - paddw %1, %5 - CLIPW %1, %3, %4 - mova %5, %1 -%else - movh %2, %4 - punpcklbw %2, %3 - psraw %1, 6 - paddsw %1, %2 - packuswb %1, %1 - movh %4, %1 -%endif -%endmacro - -%macro SHUFFLE_MASK_W 8 - %rep 8 - %if %1>=0x80 - db %1, %1 - %else - db %1*2 - db %1*2+1 - %endif - %rotate 1 - %endrep -%endmacro - -; instruction, accum, input, iteration (zero to swap, nonzero to add) -%macro ACCUM 4 -%if %4 - %1 m%2, m%3 -%else - SWAP %2, %3 -%endif -%endmacro - -; IACA support -%macro IACA_START 0 - mov ebx, 111 - db 0x64, 0x67, 0x90 -%endmacro - -%macro IACA_END 0 - mov ebx, 222 - db 0x64, 0x67, 0x90 -%endmacro diff --git a/Source/Lib/Codec/CMakeLists.txt b/Source/Lib/Codec/CMakeLists.txt index bc5d7520b..bf14eacc9 100644 --- a/Source/Lib/Codec/CMakeLists.txt +++ b/Source/Lib/Codec/CMakeLists.txt @@ -53,7 +53,6 @@ add_library(SvtHevcEnc EbMcp.h EbMdRateEstimation.h EbMeSadCalculation.h - EbMeSatdCalculation.h EbModeDecision.h EbModeDecisionConfiguration.h EbModeDecisionConfigurationProcess.h diff --git a/Source/Lib/Codec/EbMeSatdCalculation.h b/Source/Lib/Codec/EbMeSatdCalculation.h deleted file mode 100644 index 4dbd33cce..000000000 --- a/Source/Lib/Codec/EbMeSatdCalculation.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -* Copyright(c) 2018 Intel Corporation -* SPDX - License - Identifier: BSD - 2 - Clause - Patent -*/ - -#ifndef EbMeSatdCalculation_h -#define EbMeSatdCalculation_h - -#include "EbMeSatdCalculation_C.h" -#include "EbMeSatdCalculation_AVX2.h" - -#include "EbDefinitions.h" -#ifdef __cplusplus -extern "C" { -#endif - -/*************************************** -* Function Types -***************************************/ -typedef EB_U32(*EB_SATDCALCULATION16X16_TYPE)( - EB_U8 *src, - EB_U32 srcStride, - EB_U8 *ref, - EB_U32 refStride); - -/*************************************** -* Function Tables -***************************************/ - -static EB_SATDCALCULATION16X16_TYPE SatdCalculation_16x16_funcPtrArray[EB_ASM_TYPE_TOTAL] = { - // C_DEFAULT - SatdCalculation_16x16, - // AVX2 - SatdCalculation_16x16_avx2 - /*SatdCalculation_16x16_SSE2_INTRIN,*/ -}; - - -#ifdef __cplusplus -} -#endif -#endif // EbMeSatdCalculation_h \ No newline at end of file diff --git a/Source/Lib/Codec/EbMotionEstimation.c b/Source/Lib/Codec/EbMotionEstimation.c index d8dbbdca1..94fb515c0 100644 --- a/Source/Lib/Codec/EbMotionEstimation.c +++ b/Source/Lib/Codec/EbMotionEstimation.c @@ -16,7 +16,7 @@ #include "EbReferenceObject.h" #include "EbAvcStyleMcp.h" #include "EbMeSadCalculation.h" -#include "EbMeSatdCalculation.h" +#include "EbMeSatdCalculation_C.h" #include "EbIntraPrediction.h" #include "EbLambdaRateTables.h" @@ -3745,6 +3745,7 @@ EB_ERRORTYPE MotionEstimateLcu( EB_U64 ref0Poc = 0; EB_U64 ref1Poc = 0; + EB_S16 hmeLevel1SearchAreaInWidth; EB_S16 hmeLevel1SearchAreaInHeight; @@ -4459,7 +4460,7 @@ EB_ERRORTYPE MotionEstimateLcu( referenceObject = (EbPaReferenceObject_t*)pictureControlSetPtr->refPaPicPtrArray[0]->objectPtr; refPicPtr = (EbPictureBufferDesc_t*)referenceObject->inputPaddedPicturePtr; searchRegionIndex = (EB_S16)refPicPtr->originX + originX + ((EB_S16)refPicPtr->originY + originY) * refPicPtr->strideY; - pictureControlSetPtr->rcMESatdDistortion[lcuIndex] += SatdCalculation_16x16_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1] + pictureControlSetPtr->rcMESatdDistortion[lcuIndex] += SatdCalculation_16x16 (contextPtr->lcuSrcPtr, contextPtr->lcuSrcStride, &(refPicPtr->bufferY[searchRegionIndex]), refPicPtr->strideY); pictureControlSetPtr->rcMEdistortion[lcuIndex] += pictureControlSetPtr->meResults[lcuIndex][puIndex].distortionDirection[0].distortion; } @@ -5132,8 +5133,7 @@ EB_ERRORTYPE OpenLoopIntraSearchLcu( (EB_U32)EB_INTRA_PLANAR); //Distortion - oisCuPtr[0].distortion = SatdCalculation_16x16_funcPtrArray[(ASM_TYPES & AVX2_MASK) && 1]( - //oisCuPtr[0].distortion = (EB_U32)NxMSadKernel_funcPtrArray[!!(ASM_TYPES & AVX2_MASK)][cuSize >> 3]( // Always SAD without weighting + oisCuPtr[0].distortion = SatdCalculation_16x16( &(inputPtr->bufferY[(inputPtr->originY + cuOriginY) * inputPtr->strideY + (inputPtr->originX + cuOriginX)]), inputPtr->strideY, &(contextPtr->meContextPtr->lcuBuffer[0]), From 40a5f97a568fcb7b030cb220336848d535febf56 Mon Sep 17 00:00:00 2001 From: kirithika Date: Mon, 12 Aug 2019 16:10:21 +0530 Subject: [PATCH 83/93] Move PredictBits() to a new file to remove redundant copy of it as PredictBitsDup() in EbEncDecProcess.c Signed-off-by: kirithika --- Source/Lib/Codec/CMakeLists.txt | 2 + Source/Lib/Codec/EbEncDecProcess.c | 51 +-------------------- Source/Lib/Codec/EbPerFramePrediction.c | 60 +++++++++++++++++++++++++ Source/Lib/Codec/EbPerFramePrediction.h | 22 +++++++++ Source/Lib/Codec/EbRateControlProcess.c | 54 +--------------------- 5 files changed, 87 insertions(+), 102 deletions(-) create mode 100644 Source/Lib/Codec/EbPerFramePrediction.c create mode 100644 Source/Lib/Codec/EbPerFramePrediction.h diff --git a/Source/Lib/Codec/CMakeLists.txt b/Source/Lib/Codec/CMakeLists.txt index bf14eacc9..9b38573ba 100644 --- a/Source/Lib/Codec/CMakeLists.txt +++ b/Source/Lib/Codec/CMakeLists.txt @@ -69,6 +69,7 @@ add_library(SvtHevcEnc EbPacketizationProcess.h EbPacketizationReorderQueue.h EbPackUnPack.h + EbPerFramePrediction.h EbPictureAnalysisProcess.h EbPictureAnalysisResults.h EbPictureBufferDesc.h @@ -121,6 +122,7 @@ add_library(SvtHevcEnc EbMotionEstimationResults.c EbPacketizationProcess.c EbPacketizationReorderQueue.c + EbPerFramePrediction.c EbPictureAnalysisProcess.c EbPictureAnalysisResults.c EbPictureBufferDesc.c diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 322304ee0..869441602 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -14,7 +14,7 @@ #include "EbSampleAdaptiveOffset.h" #include "EbErrorCodes.h" #include "EbErrorHandling.h" - +#include "EbPerFramePrediction.h" void PrecomputeCabacCost(CabacCost_t *CabacCostPtr, CabacEncodeContext_t *cabacEncodeCtxPtr); @@ -3904,53 +3904,6 @@ EB_U32 predBitsPerLcu(PictureControlSet_t* pictureControlSetPtr, EncodeContext_t return sadBits; } -EB_U64 predictBitsDup(SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp,EB_U32 qp) -{ - EB_U64 totalBits = 0; - RateControlTables_t *rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qp]; - EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[hlRateControlHistogramPtrTemp->temporalLayerIndex]; - EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; - EB_U32 predBitsRefQp = 0; - EB_U32 numOfFullLcus = 0; - EB_U32 areaInPixel = sequenceControlSetPtr->lumaWidth * sequenceControlSetPtr->lumaHeight; - - if (hlRateControlHistogramPtrTemp->sliceType == EB_I_PICTURE) { - // Loop over block in the frame and calculated the predicted bits at reg QP - EB_U32 i; - EB_U32 accum = 0; - for (i = 0; i < NUMBER_OF_INTRA_SAD_INTERVALS; ++i) - { - accum += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); - } - - predBitsRefQp = accum; - numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; - totalBits += predBitsRefQp; - } - else { - EB_U32 i; - EB_U32 accum = 0; - EB_U32 accumIntra = 0; - for (i = 0; i < NUMBER_OF_SAD_INTERVALS; ++i) - { - accum += (EB_U32)(hlRateControlHistogramPtrTemp->meDistortionHistogram[i] * sadBitsArrayPtr[i]); - accumIntra += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); - - } - if (accum > accumIntra * 3) - predBitsRefQp = accumIntra; - else - predBitsRefQp = accum; - numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; - totalBits += predBitsRefQp; - } - - // Scale for in complete LCSs - // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries - totalBits = totalBits * (EB_U64)areaInPixel / (numOfFullLcus << 12); - return totalBits; -} - EB_U64 predictRowsSizeSum(PictureControlSet_t* pictureControlSetPtr, SequenceControlSet_t* sequenceControlSetPtr, EB_U8 qpVbv, EB_U64 *encodedBitsSoFar) { EB_U64 predictedBitsForFrame = 0; @@ -3971,7 +3924,7 @@ EB_U64 predictRowsSizeSum(PictureControlSet_t* pictureControlSetPtr, SequenceCon *encodedBitsSoFar += pictureControlSetPtr->rowStats[row]->encodedBits; predictedBitsDoneSoFar += pictureControlSetPtr->rowStats[row]->predictedBits; } - predictedBitsForFrame = predictBitsDup(sequenceControlSetPtr, sequenceControlSetPtr->encodeContextPtr, hlRateControlHistogramPtrTemp, qpVbv); + predictedBitsForFrame = predictBits(sequenceControlSetPtr, sequenceControlSetPtr->encodeContextPtr, hlRateControlHistogramPtrTemp, qpVbv); framesizeEstimated = (predictedBitsForFrame - predictedBitsDoneSoFar) + (*encodedBitsSoFar); return framesizeEstimated; } diff --git a/Source/Lib/Codec/EbPerFramePrediction.c b/Source/Lib/Codec/EbPerFramePrediction.c new file mode 100644 index 000000000..a8e99ce8a --- /dev/null +++ b/Source/Lib/Codec/EbPerFramePrediction.c @@ -0,0 +1,60 @@ +/* +* Copyright(c) 2018 Intel Corporation +* SPDX - License - Identifier: BSD - 2 - Clause - Patent +*/ + +#include "EbPerFramePrediction.h" + +EB_U64 predictBits(SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp, EB_U32 qp) +{ + EB_U64 totalBits = 0; + if (hlRateControlHistogramPtrTemp->isCoded) { + // If the frame is already coded, use the actual number of bits + totalBits = hlRateControlHistogramPtrTemp->totalNumBitsCoded; + } + else { + RateControlTables_t *rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qp]; + EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[hlRateControlHistogramPtrTemp->temporalLayerIndex]; + EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; + EB_U32 predBitsRefQp = 0; + EB_U32 numOfFullLcus = 0; + EB_U32 areaInPixel = sequenceControlSetPtr->lumaWidth * sequenceControlSetPtr->lumaHeight; + + if (hlRateControlHistogramPtrTemp->sliceType == EB_I_PICTURE) { + // Loop over block in the frame and calculated the predicted bits at reg QP + EB_U32 i; + EB_U32 accum = 0; + for (i = 0; i < NUMBER_OF_INTRA_SAD_INTERVALS; ++i) + { + accum += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); + } + + predBitsRefQp = accum; + numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; + totalBits += predBitsRefQp; + } + else { + EB_U32 i; + EB_U32 accum = 0; + EB_U32 accumIntra = 0; + for (i = 0; i < NUMBER_OF_SAD_INTERVALS; ++i) + { + accum += (EB_U32)(hlRateControlHistogramPtrTemp->meDistortionHistogram[i] * sadBitsArrayPtr[i]); + accumIntra += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); + + } + if (accum > accumIntra * 3) + predBitsRefQp = accumIntra; + else + predBitsRefQp = accum; + numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; + totalBits += predBitsRefQp; + } + + // Scale for in complete LCSs + // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries + totalBits = totalBits * (EB_U64)areaInPixel / (numOfFullLcus << 12); + } + hlRateControlHistogramPtrTemp->predBitsRefQp[qp] = totalBits; + return totalBits; +} \ No newline at end of file diff --git a/Source/Lib/Codec/EbPerFramePrediction.h b/Source/Lib/Codec/EbPerFramePrediction.h new file mode 100644 index 000000000..fdb7037ea --- /dev/null +++ b/Source/Lib/Codec/EbPerFramePrediction.h @@ -0,0 +1,22 @@ +/* +* Copyright(c) 2018 Intel Corporation +* SPDX - License - Identifier: BSD - 2 - Clause - Patent +*/ + + +#ifndef EbPerFramePrediction_h +#define EbPerFramePrediction_h + +#include "EbSequenceControlSet.h" +#ifdef __cplusplus +extern "C" { +#endif +EB_U64 predictBits( + SequenceControlSet_t *sequenceControlSetPtr, + EncodeContext_t *encodeContextPtr, + HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp, + EB_U32 qp); +#ifdef __cplusplus +} +#endif +#endif // EbPerFramePrediction_h \ No newline at end of file diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index c7074cd77..513480ad3 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -17,6 +17,7 @@ #include "EbRateControlResults.h" #include "EbRateControlTasks.h" +#include "EbPerFramePrediction.h" /***************************** * Internal Typedefs @@ -2274,59 +2275,6 @@ void HighLevelRcFeedBackPicture( } } -EB_U64 predictBits(SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr, HlRateControlHistogramEntry_t *hlRateControlHistogramPtrTemp, EB_U32 qp) -{ - EB_U64 totalBits = 0; - if (hlRateControlHistogramPtrTemp->isCoded) { - // If the frame is already coded, use the actual number of bits - totalBits = hlRateControlHistogramPtrTemp->totalNumBitsCoded; - } - else { - RateControlTables_t *rateControlTablesPtr = &encodeContextPtr->rateControlTablesArray[qp]; - EB_Bit_Number *sadBitsArrayPtr = rateControlTablesPtr->sadBitsArray[hlRateControlHistogramPtrTemp->temporalLayerIndex]; - EB_Bit_Number *intraSadBitsArrayPtr = rateControlTablesPtr->intraSadBitsArray[0]; - EB_U32 predBitsRefQp = 0; - EB_U32 numOfFullLcus = 0; - EB_U32 areaInPixel = sequenceControlSetPtr->lumaWidth * sequenceControlSetPtr->lumaHeight; - - if (hlRateControlHistogramPtrTemp->sliceType == EB_I_PICTURE) { - // Loop over block in the frame and calculated the predicted bits at reg QP - EB_U32 i; - EB_U32 accum = 0; - for (i = 0; i < NUMBER_OF_INTRA_SAD_INTERVALS; ++i) - { - accum += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); - } - - predBitsRefQp = accum; - numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; - totalBits += predBitsRefQp; - } - else { - EB_U32 i; - EB_U32 accum = 0; - EB_U32 accumIntra = 0; - for (i = 0; i < NUMBER_OF_SAD_INTERVALS; ++i) - { - accum += (EB_U32)(hlRateControlHistogramPtrTemp->meDistortionHistogram[i] * sadBitsArrayPtr[i]); - accumIntra += (EB_U32)(hlRateControlHistogramPtrTemp->oisDistortionHistogram[i] * intraSadBitsArrayPtr[i]); - - } - if (accum > accumIntra * 3) - predBitsRefQp = accumIntra; - else - predBitsRefQp = accum; - numOfFullLcus = hlRateControlHistogramPtrTemp->fullLcuCount; - totalBits += predBitsRefQp; - } - - // Scale for in complete LCSs - // predBitsRefQp is normalized based on the area because of the LCUs at the picture boundries - totalBits = totalBits * (EB_U64)areaInPixel / (numOfFullLcus << 12); - } - hlRateControlHistogramPtrTemp->predBitsRefQp[qp] = totalBits; - return totalBits; -} EB_U8 Vbv_Buf_Calc(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr, EncodeContext_t *encodeContextPtr) { From ce4672ced9cb9acbedb50a264465207dd476f09b Mon Sep 17 00:00:00 2001 From: kirithika Date: Tue, 13 Aug 2019 15:05:49 +0530 Subject: [PATCH 84/93] Remove unused variable rowIndex+Move tempCoeffPtr assignment after EncodeLcu This commit also has some cleanups done Signed-off-by: kirithika --- Source/App/EbAppConfig.c | 10 +++++----- Source/Lib/Codec/EbCodingUnit.c | 4 ++-- Source/Lib/Codec/EbCodingUnit.h | 2 +- Source/Lib/Codec/EbEncDecProcess.c | 15 +++++---------- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Source/App/EbAppConfig.c b/Source/App/EbAppConfig.c index 667857016..874e08fbf 100644 --- a/Source/App/EbAppConfig.c +++ b/Source/App/EbAppConfig.c @@ -224,11 +224,11 @@ static void SetEnableConstrainedIntra (const char *value, EbConfig_t * static void SetCfgTune (const char *value, EbConfig_t *cfg) {cfg->tune = (uint8_t)strtoul(value, NULL, 0); }; static void SetBitRateReduction (const char *value, EbConfig_t *cfg) {cfg->bitRateReduction = (EB_BOOL)strtol(value, NULL, 0); }; static void SetImproveSharpness (const char *value, EbConfig_t *cfg) {cfg->improveSharpness = (EB_BOOL)strtol(value, NULL, 0);}; -static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0); }; -static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0); }; -static void SetVbvBufInit (const char *value, EbConfig_t *cfg) { cfg->vbvBufInit = strtoul(value, NULL, 0); }; -static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0); }; -static void SetLowLevelVbv (const char *value, EbConfig_t *cfg) { cfg->lowLevelVbv = (EB_BOOL)strtol(value, NULL, 0); }; +static void SetVbvMaxrate (const char *value, EbConfig_t *cfg) { cfg->vbvMaxRate = strtoul(value, NULL, 0);}; +static void SetVbvBufsize (const char *value, EbConfig_t *cfg) { cfg->vbvBufsize = strtoul(value, NULL, 0);}; +static void SetVbvBufInit (const char *value, EbConfig_t *cfg) { cfg->vbvBufInit = strtoul(value, NULL, 0);}; +static void SetHrdFlag (const char *value, EbConfig_t *cfg) { cfg->hrdFlag = strtoul(value, NULL, 0);}; +static void SetLowLevelVbv (const char *value, EbConfig_t *cfg) { cfg->lowLevelVbv = (EB_BOOL)strtol(value, NULL, 0); }; static void SetVideoUsabilityInfo (const char *value, EbConfig_t *cfg) {cfg->videoUsabilityInfo = strtol(value, NULL, 0);}; static void SetHighDynamicRangeInput (const char *value, EbConfig_t *cfg) {cfg->highDynamicRangeInput = strtol(value, NULL, 0);}; static void SetAccessUnitDelimiter (const char *value, EbConfig_t *cfg) {cfg->accessUnitDelimiter = strtol(value, NULL, 0);}; diff --git a/Source/Lib/Codec/EbCodingUnit.c b/Source/Lib/Codec/EbCodingUnit.c index e54859ff1..fc1387262 100644 --- a/Source/Lib/Codec/EbCodingUnit.c +++ b/Source/Lib/Codec/EbCodingUnit.c @@ -29,11 +29,11 @@ EB_ERRORTYPE RCStatRowCtor( EB_MALLOC(RCStatRow_t*, rcStatRowPtr, sizeof(RCStatRow_t), EB_N_PTR); *rcStatRowDblPtr = rcStatRowPtr; rcStatRowPtr->rowIndex = rowIndex; - rcStatRowPtr->numEncodedCUs = 0; rcStatRowPtr->predictedBits = 0; rcStatRowPtr->encodedBits = 0; rcStatRowPtr->rowQp = 0; rcStatRowPtr->totalCUEncoded = 0; + rcStatRowPtr->lastEncodedCU = 0; EB_CREATEMUTEX(EB_HANDLE, rcStatRowPtr->rowUpdateMutex, sizeof(EB_HANDLE), EB_MUTEX); if (return_error == EB_ErrorInsufficientResources) { return EB_ErrorInsufficientResources; @@ -90,7 +90,7 @@ EB_ERRORTYPE LargestCodingUnitCtor( largestCodingUnitPtr->proxytotalBits = 0; largestCodingUnitPtr->rowInd = 0; largestCodingUnitPtr->intraSadInterval = 0; - largestCodingUnitPtr->intraSadInterval = 0; + largestCodingUnitPtr->interSadInterval = 0; EB_MALLOC(CodingUnit_t**, largestCodingUnitPtr->codedLeafArrayPtr, sizeof(CodingUnit_t*) * CU_MAX_COUNT, EB_N_PTR); for(codedLeafIndex=0; codedLeafIndex < CU_MAX_COUNT; ++codedLeafIndex) { EB_MALLOC(CodingUnit_t*, largestCodingUnitPtr->codedLeafArrayPtr[codedLeafIndex], sizeof(CodingUnit_t) , EB_N_PTR); diff --git a/Source/Lib/Codec/EbCodingUnit.h b/Source/Lib/Codec/EbCodingUnit.h index b6272b35b..f46eb506f 100644 --- a/Source/Lib/Codec/EbCodingUnit.h +++ b/Source/Lib/Codec/EbCodingUnit.h @@ -219,11 +219,11 @@ typedef struct LargestCodingUnit_s { typedef struct RCStatRow_s { EB_U16 rowIndex; - EB_U32 numEncodedCUs; /* Addr of last encoded LCU in row */ EB_U32 encodedBits; /* sum of 'totalBits' of encoded LCUs */ EB_U32 predictedBits; EB_U32 rowQp; EB_U32 totalCUEncoded; /*Tracks number of LCUs encoded in each row*/ + EB_U32 lastEncodedCU; /*Tracks the address of last encoded CU*/ EB_HANDLE rowUpdateMutex; }RCStatRow_t; diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 869441602..003ad71fb 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -1355,7 +1355,7 @@ static void ResetRowStats( EbBlockOnMutex(pictureControlSetPtr->rowStats[row]->rowUpdateMutex); pictureControlSetPtr->rowStats[row]->totalCUEncoded = 0; pictureControlSetPtr->rowStats[row]->encodedBits = 0; - pictureControlSetPtr->rowStats[row]->numEncodedCUs = 0; + pictureControlSetPtr->rowStats[row]->lastEncodedCU = 0; EbReleaseMutex(pictureControlSetPtr->rowStats[row]->rowUpdateMutex); } } @@ -3929,7 +3929,7 @@ EB_U64 predictRowsSizeSum(PictureControlSet_t* pictureControlSetPtr, SequenceCon return framesizeEstimated; } -EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr,//mutex to be added since Buffer fill is read +EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr, RCStatRow_t *rowPtr, EncodeContext_t *rcData, @@ -4015,7 +4015,6 @@ void* EncDecKernel(void *inputPtr) // LCU Loop variables LargestCodingUnit_t *lcuPtr; EB_U16 lcuIndex; - EB_U16 rowIndex; EB_U8 lcuSize; EB_U8 lcuSizeLog2; EB_U32 xLcuIndex; @@ -4199,10 +4198,6 @@ void* EncDecKernel(void *inputPtr) // LCU per picture-wise lcuIndex = (EB_U16)((tileGroupLcuStartY + yLcuIndex) * pictureWidthInLcu + (tileGroupLcuStartX + xLcuIndex)); lcuPtr = pictureControlSetPtr->lcuPtrArray[lcuIndex]; - rowIndex = (EB_U16)(yLcuIndex / pictureHeightInLcu); - rowPtr = pictureControlSetPtr->rowStats[rowIndex]; - pictureControlSetPtr->firstRowOfPicture = (xLcuIndex <= pictureWidthInLcu) ? EB_TRUE : EB_FALSE; - lcuOriginX = (xLcuIndex+tileGroupLcuStartX) << lcuSizeLog2; lcuOriginY = (yLcuIndex+tileGroupLcuStartY) << lcuSizeLog2; //printf("Process lcu (%d, %d), lcuIndex %d, segmentIndex %d\n", lcuOriginX, lcuOriginY, lcuIndex, segmentIndex); @@ -4228,7 +4223,6 @@ void* EncDecKernel(void *inputPtr) lcuRowIndexCount = (xLcuIndex == tileRowWidthInLcu - 1) ? lcuRowIndexCount + 1 : lcuRowIndexCount; mdcPtr = &pictureControlSetPtr->mdcLcuArray[lcuIndex]; contextPtr->lcuIndex = lcuIndex; - tempCoeffPicturePtr = lcuPtr->quantizedCoeff; // Derive cuUseRefSrcFlag Flag contextPtr->mdContext->cuUseRefSrcFlag = (pictureControlSetPtr->ParentPcsPtr->useSrcRef) && (pictureControlSetPtr->ParentPcsPtr->edgeResultsPtr[lcuIndex].edgeBlockNum == EB_FALSE || pictureControlSetPtr->ParentPcsPtr->lcuFlatNoiseArray[lcuIndex]) ? EB_TRUE : EB_FALSE; @@ -4393,6 +4387,7 @@ void* EncDecKernel(void *inputPtr) contextPtr); if (sequenceControlSetPtr->staticConfig.lowLevelVbv) { /*Entropy Estimation for LCU*/ + tempCoeffPicturePtr = lcuPtr->quantizedCoeff; tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); @@ -4432,11 +4427,11 @@ void* EncDecKernel(void *inputPtr) lcuPtr->fullLcu = 1; } } - //Update CU Stats for row level vbv control + //Update LCU Stats for row level vbv control EbBlockOnMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); pictureControlSetPtr->rowStats[yLcuIndex]->encodedBits += lcuPtr->proxytotalBits; pictureControlSetPtr->rowStats[yLcuIndex]->totalCUEncoded++; - pictureControlSetPtr->rowStats[yLcuIndex]->numEncodedCUs = lcuPtr->index; + pictureControlSetPtr->rowStats[yLcuIndex]->lastEncodedCU = lcuPtr->index; EbReleaseMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); } if (pictureControlSetPtr->ParentPcsPtr->referencePictureWrapperPtr != NULL){ From 3ee527f53c7fd4cd8927d3d0e6bbb9e023b24c54 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 14 Aug 2019 17:28:58 +0530 Subject: [PATCH 85/93] Add Macro for Rc Tolerance and Step size values + Add VBV control to avoid underflow while removing a single frame Signed-off-by: kirithika --- Source/Lib/Codec/EbDefinitions.h | 4 ++++ Source/Lib/Codec/EbEncDecProcess.c | 20 ++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Source/Lib/Codec/EbDefinitions.h b/Source/Lib/Codec/EbDefinitions.h index 24072fce5..bdfd18b5c 100644 --- a/Source/Lib/Codec/EbDefinitions.h +++ b/Source/Lib/Codec/EbDefinitions.h @@ -1093,6 +1093,10 @@ typedef enum EB_SEI { #define BR_SHIFT 6 #define CPB_SHIFT 4 +#define STEP_SIZE 1 +#define RC_TOL 0 +#define RC_TOL_FACTOR 0 + // INTRA restriction for global motion #define INTRA_GLOBAL_MOTION_NON_MOVING_INDEX_TH 2 #define INTRA_GLOBAL_MOTION_DARK_LCU_TH 50 diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 003ad71fb..d7c985a4a 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3943,10 +3943,11 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, EB_U8 qpAbsoluteMin = sequenceControlSetPtr->staticConfig.minQpAllowed; EB_U8 qpMax = MIN(prevRowQp + 4, qpAbsoluteMax); EB_U8 qpMin = MAX(prevRowQp - 4, qpAbsoluteMin); + pictureControlSetPtr->bufferFillPerFrame = sequenceControlSetPtr->encodeContextPtr->bufferFill; EB_U64 bufferLeftPlanned = pictureControlSetPtr->bufferFillPerFrame - pictureControlSetPtr->frameSizePlanned; - if (rowPtr->rowIndexrowIndex < pictureHeightInLcu) { + //There is no tolerance limit allowed in low level RC as of now. + EB_U64 rcTol = RC_TOL; EB_U64 encodedBitsSoFar = 0; EB_U64 accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); @@ -3958,8 +3959,9 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, qpMax = qpAbsoluteMax = prevRowQp; if (pictureControlSetPtr->sliceType!= EB_I_PICTURE) - rcTol *= 0; + rcTol *= RC_TOL_FACTOR; + if(sequenceControlSetPtr->targetBitrate <= sequenceControlSetPtr->encodeContextPtr->vbvMaxrate) qpMin = MAX(qpMin, pictureControlSetPtr->qpNoVbv); //Increase the Qp when the current frame size exceeds the estimated frame size @@ -3968,7 +3970,7 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, (pictureControlSetPtr->bufferFillPerFrame - accFrameBits < (EB_U64)(bufferLeftPlanned * 0.5)) || (accFrameBits > pictureControlSetPtr->frameSizePlanned && qpVbv < pictureControlSetPtr->qpNoVbv) ))) { - qpVbv += 1; + qpVbv += STEP_SIZE; encodedBitsSoFar = 0; accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); } @@ -3979,11 +3981,17 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, && (((accFrameBits < (EB_U64)(pictureControlSetPtr->frameSizePlanned * 0.8f) && qpVbv <= prevRowQp) || (EB_S64)accFrameBits < (EB_S64)((EB_S64)(pictureControlSetPtr->bufferFillPerFrame - (rcData->vbvBufsize + (rcData->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16)))) * 1.1)) )) { - qpVbv -= 1; + qpVbv -= STEP_SIZE; encodedBitsSoFar = 0; accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); } + /* avoid VBV underflow */ + while ((qpVbv < qpAbsoluteMax) + && (pictureControlSetPtr->bufferFillPerFrame - accFrameBits < (rcData->vbvMaxrate / (sequenceControlSetPtr->staticConfig.frameRate >> 16)))) { + qpVbv += STEP_SIZE; + accFrameBits = predictRowsSizeSum(pictureControlSetPtr, sequenceControlSetPtr, qpVbv, &encodedBitsSoFar); + } pictureControlSetPtr->frameSizeEstimated = accFrameBits; From a5d6a00e58f06b174ec7e4304be0c998acdb0bd8 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 14 Aug 2019 13:29:49 +0530 Subject: [PATCH 86/93] Add code for planning VBV Buffer Fullness after lookahead VBV to use in low level VBV Signed-off-by: kirithika --- Source/Lib/Codec/EbEncDecProcess.c | 2 +- Source/Lib/Codec/EbRateControlProcess.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index d7c985a4a..c725d8bbe 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3943,7 +3943,7 @@ EB_U8 RowVbvRateControl(PictureControlSet_t *pictureControlSetPtr, EB_U8 qpAbsoluteMin = sequenceControlSetPtr->staticConfig.minQpAllowed; EB_U8 qpMax = MIN(prevRowQp + 4, qpAbsoluteMax); EB_U8 qpMin = MAX(prevRowQp - 4, qpAbsoluteMin); - pictureControlSetPtr->bufferFillPerFrame = sequenceControlSetPtr->encodeContextPtr->bufferFill; + EB_U64 bufferLeftPlanned = pictureControlSetPtr->bufferFillPerFrame - pictureControlSetPtr->frameSizePlanned; if (rowPtr->rowIndex < pictureHeightInLcu) { //There is no tolerance limit allowed in low level RC as of now. diff --git a/Source/Lib/Codec/EbRateControlProcess.c b/Source/Lib/Codec/EbRateControlProcess.c index 513480ad3..09d2b5dfa 100644 --- a/Source/Lib/Codec/EbRateControlProcess.c +++ b/Source/Lib/Codec/EbRateControlProcess.c @@ -2670,10 +2670,16 @@ void* RateControlKernel(void *inputPtr) if (encodeContextPtr->vbvMaxrate && encodeContextPtr->vbvBufsize && sequenceControlSetPtr->staticConfig.lookAheadDistance > 0) { EbBlockOnMutex(encodeContextPtr->bufferFillMutex); pictureControlSetPtr->qpNoVbv = pictureControlSetPtr->pictureQp; - pictureControlSetPtr->bufferFillPerFrame = encodeContextPtr->bufferFill; pictureControlSetPtr->pictureQp = (EB_U8)Vbv_Buf_Calc(pictureControlSetPtr, sequenceControlSetPtr, encodeContextPtr); hlRateControlHistogramPtrTemp = encodeContextPtr->hlRateControlHistorgramQueue[pictureControlSetPtr->ParentPcsPtr->hlHistogramQueueIndex]; pictureControlSetPtr->frameSizePlanned = predictBits(sequenceControlSetPtr, encodeContextPtr, hlRateControlHistogramPtrTemp, pictureControlSetPtr->pictureQp); + /* Update low level VBV Plan*/ + EB_S64 bufferfill_plan = (EB_S64)(encodeContextPtr->bufferFill); + bufferfill_plan -= pictureControlSetPtr->frameSizePlanned; + bufferfill_plan = MAX(bufferfill_plan, 0); + bufferfill_plan = (EB_S64)(bufferfill_plan + (encodeContextPtr->vbvMaxrate * (1.0 / (sequenceControlSetPtr->frameRate >> RC_PRECISION)))); + bufferfill_plan = MIN(bufferfill_plan, encodeContextPtr->vbvBufsize); + pictureControlSetPtr->bufferFillPerFrame = (EB_U64)(bufferfill_plan); EbReleaseMutex(encodeContextPtr->bufferFillMutex); } pictureControlSetPtr->ParentPcsPtr->pictureQp = pictureControlSetPtr->pictureQp; From a984744f6b2ee0b84623c3b6d6ce538bc6cf2221 Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 14 Aug 2019 17:35:31 +0530 Subject: [PATCH 87/93] Restrict low level vbv when lookahead predictions are not done + enable lookahead for crf Signed-off-by: kirithika --- Source/Lib/Codec/EbEncDecProcess.c | 4 ++-- Source/Lib/Codec/EbInitialRateControlProcess.c | 4 ++-- Source/Lib/Codec/EbResourceCoordinationProcess.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index c725d8bbe..565cc143b 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -4348,7 +4348,7 @@ void* EncDecKernel(void *inputPtr) } //Block level vbv tuning starts here - if (sequenceControlSetPtr->staticConfig.lowLevelVbv) { + if (sequenceControlSetPtr->staticConfig.lowLevelVbv && sequenceControlSetPtr->staticConfig.lookAheadDistance > 0) { EbBlockOnMutex(pictureControlSetPtr->rowStats[yLcuIndex]->rowUpdateMutex); rowPtr = pictureControlSetPtr->rowStats[yLcuIndex]; rowPtr->rowIndex = yLcuIndex; @@ -4393,7 +4393,7 @@ void* EncDecKernel(void *inputPtr) lcuPtr->qp, enableSaoFlag, contextPtr); - if (sequenceControlSetPtr->staticConfig.lowLevelVbv) { + if (sequenceControlSetPtr->staticConfig.lowLevelVbv && sequenceControlSetPtr->staticConfig.lookAheadDistance > 0) { /*Entropy Estimation for LCU*/ tempCoeffPicturePtr = lcuPtr->quantizedCoeff; tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + diff --git a/Source/Lib/Codec/EbInitialRateControlProcess.c b/Source/Lib/Codec/EbInitialRateControlProcess.c index 8169cae92..83d7cdc6b 100644 --- a/Source/Lib/Codec/EbInitialRateControlProcess.c +++ b/Source/Lib/Codec/EbInitialRateControlProcess.c @@ -954,7 +954,7 @@ void* InitialRateControlKernel(void *inputPtr) pictureControlSetPtr, inputResultsPtr); - if (sequenceControlSetPtr->staticConfig.rateControlMode == 1) + if (sequenceControlSetPtr->staticConfig.rateControlMode != 0) { if (sequenceControlSetPtr->staticConfig.lookAheadDistance != 0){ @@ -1041,7 +1041,7 @@ void* InitialRateControlKernel(void *inputPtr) else pictureControlSetPtr->endOfSequenceRegion = EB_FALSE; - if (sequenceControlSetPtr->staticConfig.rateControlMode == 1) + if (sequenceControlSetPtr->staticConfig.rateControlMode != 0) { // Determine offset from the Head Ptr for HLRC histogram queue and set the life count if (sequenceControlSetPtr->staticConfig.lookAheadDistance != 0){ diff --git a/Source/Lib/Codec/EbResourceCoordinationProcess.c b/Source/Lib/Codec/EbResourceCoordinationProcess.c index b5a5b37f3..1037c9d01 100644 --- a/Source/Lib/Codec/EbResourceCoordinationProcess.c +++ b/Source/Lib/Codec/EbResourceCoordinationProcess.c @@ -777,7 +777,7 @@ void* ResourceCoordinationKernel(void *inputPtr) // Rate Control // Set the ME Distortion and OIS Historgrams to zero - if (sequenceControlSetPtr->staticConfig.rateControlMode == 1){ + if (sequenceControlSetPtr->staticConfig.rateControlMode != 0){ EB_MEMSET(pictureControlSetPtr->meDistortionHistogram, 0, NUMBER_OF_SAD_INTERVALS*sizeof(EB_U16)); EB_MEMSET(pictureControlSetPtr->oisDistortionHistogram, 0, NUMBER_OF_INTRA_SAD_INTERVALS*sizeof(EB_U16)); } From 2e231e24133ec5192d3477fdb18f30ab17b62f6a Mon Sep 17 00:00:00 2001 From: kirithika Date: Wed, 14 Aug 2019 18:31:32 +0530 Subject: [PATCH 88/93] Fix build error + update BKC for frame level VBV Signed-off-by: kirithika --- Docs/svt-hevc_encoder_user_guide.md | 4 ++-- Source/Lib/Codec/EbEncDecProcess.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Docs/svt-hevc_encoder_user_guide.md b/Docs/svt-hevc_encoder_user_guide.md index c1ce3bf41..966fd6860 100644 --- a/Docs/svt-hevc_encoder_user_guide.md +++ b/Docs/svt-hevc_encoder_user_guide.md @@ -448,9 +448,9 @@ Similarly, in order to enable VBV and run a 2-stream 8kp50 simultaneous encode o #### *Running Windows\* Server 2016:* ->start /node 0 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -vbv-maxrate 10000000 -vbv-bufsize 10000000 -fps 50 -b out1.bin -n 5000 –nb 500 +>start /node 0 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -vbv-maxrate 10000000 -vbv-bufsize 20000000 -fps 50 -b out1.bin -n 5000 –nb 500 ->start /node 1 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -vbv-maxrate 10000000 -vbv-bufsize 10000000 -fps 50 -b out3.bin -n 5000 –nb 500 +>start /node 1 SvtHevcEncApp.exe -encMode 12 -tune 0 -w 3840 -h 2160 -bit-depth 10 -compressed-ten-bit-format 1 -i in.yuv -rc 1 –tbr 10000000 -vbv-maxrate 10000000 -vbv-bufsize 20000000 -fps 50 -b out3.bin -n 5000 –nb 500 #### *Running Ubuntu\* 18.04:* diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 2b04b5d95..37db2d05d 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -1331,7 +1331,7 @@ static void ResetTempEntropy( EncDecContext_t *contextPtr, PictureControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr) { - EB_U32 tileCnt = pictureControlSetPtr->tileRowCount * pictureControlSetPtr->tileColumnCount; + EB_U32 tileCnt = pictureControlSetPtr->ParentPcsPtr->tileRowCount * pictureControlSetPtr->ParentPcsPtr->tileColumnCount; EB_U32 tileIdx = 0; EB_U32 entropyCodingQp = pictureControlSetPtr->pictureQp; for (tileIdx = 0; tileIdx < tileCnt; tileIdx++) { From a218fa7f8f4f8feb074f2381718c6d85189658a4 Mon Sep 17 00:00:00 2001 From: kirithika Date: Thu, 15 Aug 2019 22:35:49 +0530 Subject: [PATCH 89/93] Remove -DPIC as it is not needed for SATD C implementation --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 02eb260a4..0cb2c4785 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) if(WIN32) set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DWIN64") else() - set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DUNIX64 -DPIC") + set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DUNIX64") endif() if(UNIX) From e93b4e975fea101f5f415d307ca96bd0ef5448f8 Mon Sep 17 00:00:00 2001 From: Austin Hu Date: Fri, 18 Oct 2019 01:23:26 +0800 Subject: [PATCH 90/93] Fixed the compiling errors/warnings for rebasing PR #70. Signed-off-by: Austin Hu --- Source/Lib/Codec/EbEncDecProcess.c | 36 +++++++++---------- Source/Lib/Codec/EbEncHandle.c | 2 +- Source/Lib/Codec/EbEntropyCodingProcess.c | 5 +-- .../Lib/Codec/EbResourceCoordinationProcess.c | 4 +-- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 71dda7341..6ab5cc735 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -1358,7 +1358,6 @@ static void EntropyCodingResetTempNeighborArrays(PictureControlSet_t *pictureCon } static void ResetTempEntropy( - EncDecContext_t *contextPtr, PictureControlSet_t *pictureControlSetPtr, SequenceControlSet_t *sequenceControlSetPtr) { EB_U32 tileCnt = pictureControlSetPtr->ParentPcsPtr->tileRowCount * pictureControlSetPtr->ParentPcsPtr->tileColumnCount; @@ -3003,8 +3002,9 @@ void* EncDecKernel(void *inputPtr) //Reset Stats required for low level vbv if (segmentIndex == 0) { - if (contextPtr->tileRowIndex == 0) { - ResetTempEntropy(contextPtr, pictureControlSetPtr, sequenceControlSetPtr); + // Reset for the tiles in the 1st row. + if ((tileGroupIdx / tileGroupWidthInLcu) == 0) { + ResetTempEntropy(pictureControlSetPtr, sequenceControlSetPtr); ResetRowStats(pictureControlSetPtr,sequenceControlSetPtr); } } @@ -3173,7 +3173,7 @@ void* EncDecKernel(void *inputPtr) //Assign the base qp for the LCU if (xLcuIndex <= yLcuIndex && yLcuIndex) - lcuPtr->qp = pictureControlSetPtr->lcuPtrArray[lcuIndex - pictureWidthInLcu]->qp; + lcuPtr->qp = pictureControlSetPtr->lcuPtrArray[lcuIndex - tileGroupWidthInLcu]->qp; else lcuPtr->qp = pictureControlSetPtr->rowStats[rowPtr->rowIndex]->rowQp; @@ -3211,31 +3211,31 @@ void* EncDecKernel(void *inputPtr) if (sequenceControlSetPtr->staticConfig.lowLevelVbv && sequenceControlSetPtr->staticConfig.lookAheadDistance > 0) { /*Entropy Estimation for LCU*/ tempCoeffPicturePtr = lcuPtr->quantizedCoeff; - tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); EstimateLcu( lcuPtr, lcuOriginX, lcuOriginY, pictureControlSetPtr, sequenceControlSetPtr->lcuSize, - pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr, + pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr, tempCoeffPicturePtr, - pictureControlSetPtr->tempModeTypeNeighborArray[contextPtr->tileIndex], - pictureControlSetPtr->tempLeafDepthNeighborArray[contextPtr->tileIndex], - pictureControlSetPtr->tempIntraLumaModeNeighborArray[contextPtr->tileIndex], - pictureControlSetPtr->tempSkipFlagNeighborArray[contextPtr->tileIndex], - contextPtr->tileIndex, + pictureControlSetPtr->tempModeTypeNeighborArray[contextPtr->mdContext->tileIndex], + pictureControlSetPtr->tempLeafDepthNeighborArray[contextPtr->mdContext->tileIndex], + pictureControlSetPtr->tempIntraLumaModeNeighborArray[contextPtr->mdContext->tileIndex], + pictureControlSetPtr->tempSkipFlagNeighborArray[contextPtr->mdContext->tileIndex], + contextPtr->mdContext->tileIndex, 0, 0); - tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); lcuPtr->proxytotalBits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; - lcuWidth = (sequenceControlSetPtr->lumaWidth - lcuOriginX) < MAX_LCU_SIZE ? sequenceControlSetPtr->lumaWidth - lcuOriginX : MAX_LCU_SIZE; - lcuHeight = (sequenceControlSetPtr->lumaHeight - lcuOriginY) < MAX_LCU_SIZE ? sequenceControlSetPtr->lumaHeight - lcuOriginY : MAX_LCU_SIZE; + lcuWidth = (sequenceControlSetPtr->lumaWidth - lcuOriginX) < (EB_U16)MAX_LCU_SIZE ? (sequenceControlSetPtr->lumaWidth - lcuOriginX) : (EB_U16)MAX_LCU_SIZE; + lcuHeight = (sequenceControlSetPtr->lumaHeight - lcuOriginY) < (EB_U16)MAX_LCU_SIZE ? (sequenceControlSetPtr->lumaHeight - lcuOriginY) : (EB_U16)MAX_LCU_SIZE; lcuPtr->fullLcu = 0; if (pictureControlSetPtr->sliceType == EB_I_PICTURE) { if (lcuWidth == MAX_LCU_SIZE && lcuHeight == MAX_LCU_SIZE) { diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index b9b9db9e4..2fdfcfd5a 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2122,7 +2122,7 @@ void LoadDefaultBufferConfigurationSettings( EB_U32 meSegH = (((sequenceControlSetPtr->maxInputLumaHeight + 32) / MAX_LCU_SIZE) < 6) ? 1 : 6; EB_U32 meSegW = (((sequenceControlSetPtr->maxInputLumaWidth + 32) / MAX_LCU_SIZE) < 10) ? 1 : 10; - EB_U16 tileColCount = sequenceControlSetPtr->staticConfig.tileColumnCount; + // EB_U16 tileColCount = sequenceControlSetPtr->staticConfig.tileColumnCount; EB_U16 tileRowCount = sequenceControlSetPtr->staticConfig.tileRowCount; EB_U32 inputPic = SetParentPcs(&sequenceControlSetPtr->staticConfig); diff --git a/Source/Lib/Codec/EbEntropyCodingProcess.c b/Source/Lib/Codec/EbEntropyCodingProcess.c index ba08f074d..f0a3cb747 100644 --- a/Source/Lib/Codec/EbEntropyCodingProcess.c +++ b/Source/Lib/Codec/EbEntropyCodingProcess.c @@ -425,8 +425,9 @@ void* EntropyCodingKernel(void *inputPtr) lastLcuFlagInSlice = lastLcuFlagInTile; } - if (sequenceControlSetPtr->staticConfig.lowLevelVbv) - contextPtr->qp = lcuPtr->qp; + if (sequenceControlSetPtr->staticConfig.lowLevelVbv) + contextPtr->qp = lcuPtr->qp; + // Configure the LCU EntropyCodingConfigureLcu( contextPtr, diff --git a/Source/Lib/Codec/EbResourceCoordinationProcess.c b/Source/Lib/Codec/EbResourceCoordinationProcess.c index c6f9d3b2e..19ea40dda 100644 --- a/Source/Lib/Codec/EbResourceCoordinationProcess.c +++ b/Source/Lib/Codec/EbResourceCoordinationProcess.c @@ -380,7 +380,7 @@ void* ResourceCoordinationKernel(void *inputPtr) EbObjectWrapper_t *prevPictureControlSetWrapperPtr = 0; EB_U32 chromaFormat = EB_YUV420; EB_U32 subWidthCMinus1 = 1; - EB_U32 subHeightCMinus1 = 1; + // EB_U32 subHeightCMinus1 = 1; for(;;) { @@ -401,7 +401,7 @@ void* ResourceCoordinationKernel(void *inputPtr) chromaFormat = sequenceControlSetPtr->chromaFormatIdc; subWidthCMinus1 = (chromaFormat == EB_YUV444 ? 1 : 2) - 1; - subHeightCMinus1 = (chromaFormat >= EB_YUV422 ? 1 : 2) - 1; + // subHeightCMinus1 = (chromaFormat >= EB_YUV422 ? 1 : 2) - 1; // If config changes occured since the last picture began encoding, then // prepare a new sequenceControlSetPtr containing the new changes and update the state // of the previous Active SequenceControlSet From b7effc8473112daf06d0d9ee7d817b591879ce59 Mon Sep 17 00:00:00 2001 From: Austin Hu Date: Fri, 18 Oct 2019 03:14:46 +0800 Subject: [PATCH 91/93] Refined the CRF usage. Signed-off-by: Austin Hu --- Docs/svt-hevc_encoder_user_guide.md | 2 +- Source/Lib/Codec/EbEncHandle.c | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Docs/svt-hevc_encoder_user_guide.md b/Docs/svt-hevc_encoder_user_guide.md index 77764cc1f..d7e520da8 100644 --- a/Docs/svt-hevc_encoder_user_guide.md +++ b/Docs/svt-hevc_encoder_user_guide.md @@ -296,7 +296,7 @@ The encoder parameters present in the Sample.cfg file are listed in this table b | **SearchAreaWidth** | -search-w | [1 - 256] | Depends on input resolution | Motion vector search area width | | **SearchAreaHeight** | -search-h | [1 - 256] | Depends on input resolution | Motion vector search area height | | **ConstrainedIntra** | -constrd-intra | [0,1] | 0 | Allow the use of Constrained Intra, when enabled, this features yields to sending two PPSs in the HEVC Elementary streams
0 = OFF, 1 = ON | -| **RateControlMode** | -rc | [0,1] | 0 | 0 : CQP , 1 : VBR , 2 : CRF | +| **RateControlMode** | -rc | [0 - 2] | 0 | 0 : CQP , 1 : VBR , 2 : CRF | | **ConstantRateFactor** | -crf | [0 - 51] | 28 | CRF value allowed for rate control use, only apllicable when RateControlMode is set to 2 | | **TargetBitRate** | -tbr | Any Number | 7000000 | Target bitrate in bits / second. Only used when RateControlMode is set to 1 | | **vbvMaxrate** | -vbv-maxrate | Any Number | 0 | VBVMaxrate in bits / second. Only used when RateControlMode is set to 1 | diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index 2fdfcfd5a..afa872de2 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -2997,15 +2997,20 @@ static EB_ERRORTYPE VerifySettings(\ SVT_LOG("SVT [Error]: Instance %u: The constrained intra must be [0 - 1] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (config->rateControlMode > 2) { + if (config->rateControlMode > 2) { SVT_LOG("SVT [Error]: Instance %u: The rate control mode must be [0 - 2] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if ((config->rateControlMode == 2) && (config->crf > 51)) - { - SVT_LOG("SVT [Error]: Instance %u:The crf value must be [0-51] \n", channelNumber + 1); - return_error = EB_ErrorBadParameter; - } + if (((config->rateControlMode == 0) || (config->rateControlMode == 1)) && (config->crf != 28)) + { + SVT_LOG("SVT [Warning]: Instance %u: The crf setting with %u wouldn't take effect with rc %u\n", + channelNumber + 1, config->crf, config->rateControlMode); + } + if ((config->rateControlMode == 2) && (config->crf > 51)) + { + SVT_LOG("SVT [Error]: Instance %u:The crf value must be [0-51] \n", channelNumber + 1); + return_error = EB_ErrorBadParameter; + } if (config->lookAheadDistance > 250 && config->lookAheadDistance != (EB_U32)~0) { SVT_LOG("SVT [Error]: Instance %u: The lookahead distance must be [0 - 250] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; From 8ba809c0172e3432922e16b4bec610cef2b3c56b Mon Sep 17 00:00:00 2001 From: Austin-Hu <51988754+Austin-Hu@users.noreply.github.com> Date: Fri, 18 Oct 2019 09:49:46 -0700 Subject: [PATCH 92/93] Update EbEncHandle.c --- Source/Lib/Codec/EbEncHandle.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Lib/Codec/EbEncHandle.c b/Source/Lib/Codec/EbEncHandle.c index afa872de2..b8b74d610 100644 --- a/Source/Lib/Codec/EbEncHandle.c +++ b/Source/Lib/Codec/EbEncHandle.c @@ -3001,13 +3001,11 @@ static EB_ERRORTYPE VerifySettings(\ SVT_LOG("SVT [Error]: Instance %u: The rate control mode must be [0 - 2] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } - if (((config->rateControlMode == 0) || (config->rateControlMode == 1)) && (config->crf != 28)) - { + if (((config->rateControlMode == 0) || (config->rateControlMode == 1)) && (config->crf != 28)) { SVT_LOG("SVT [Warning]: Instance %u: The crf setting with %u wouldn't take effect with rc %u\n", channelNumber + 1, config->crf, config->rateControlMode); } - if ((config->rateControlMode == 2) && (config->crf > 51)) - { + if ((config->rateControlMode == 2) && (config->crf > 51)) { SVT_LOG("SVT [Error]: Instance %u:The crf value must be [0-51] \n", channelNumber + 1); return_error = EB_ErrorBadParameter; } From 71352941a8e6db5b494df7a7697376b98c32347f Mon Sep 17 00:00:00 2001 From: Austin-Hu <51988754+Austin-Hu@users.noreply.github.com> Date: Thu, 24 Oct 2019 16:56:53 -0700 Subject: [PATCH 93/93] Fixed some rebasing errors in EbEncDecProcess.c Signed-off-by: Austin Hu --- Source/Lib/Codec/EbEncDecProcess.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Source/Lib/Codec/EbEncDecProcess.c b/Source/Lib/Codec/EbEncDecProcess.c index 6ab5cc735..45ade1371 100644 --- a/Source/Lib/Codec/EbEncDecProcess.c +++ b/Source/Lib/Codec/EbEncDecProcess.c @@ -3003,7 +3003,7 @@ void* EncDecKernel(void *inputPtr) //Reset Stats required for low level vbv if (segmentIndex == 0) { // Reset for the tiles in the 1st row. - if ((tileGroupIdx / tileGroupWidthInLcu) == 0) { + if (tileGroupIdx == 0) { ResetTempEntropy(pictureControlSetPtr, sequenceControlSetPtr); ResetRowStats(pictureControlSetPtr,sequenceControlSetPtr); } @@ -3173,7 +3173,7 @@ void* EncDecKernel(void *inputPtr) //Assign the base qp for the LCU if (xLcuIndex <= yLcuIndex && yLcuIndex) - lcuPtr->qp = pictureControlSetPtr->lcuPtrArray[lcuIndex - tileGroupWidthInLcu]->qp; + lcuPtr->qp = pictureControlSetPtr->lcuPtrArray[lcuIndex - ppcsPtr->pictureWidthInLcu]->qp; else lcuPtr->qp = pictureControlSetPtr->rowStats[rowPtr->rowIndex]->rowQp; @@ -3211,28 +3211,28 @@ void* EncDecKernel(void *inputPtr) if (sequenceControlSetPtr->staticConfig.lowLevelVbv && sequenceControlSetPtr->staticConfig.lookAheadDistance > 0) { /*Entropy Estimation for LCU*/ tempCoeffPicturePtr = lcuPtr->quantizedCoeff; - tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + tempWrittenBitsBeforeQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[tileGroupIdx]->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[tileGroupIdx]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[tileGroupIdx]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); EstimateLcu( lcuPtr, lcuOriginX, lcuOriginY, pictureControlSetPtr, sequenceControlSetPtr->lcuSize, - pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr, + pictureControlSetPtr->entropyCodingInfo[tileGroupIdx]->tempEntropyCoderPtr, tempCoeffPicturePtr, - pictureControlSetPtr->tempModeTypeNeighborArray[contextPtr->mdContext->tileIndex], - pictureControlSetPtr->tempLeafDepthNeighborArray[contextPtr->mdContext->tileIndex], - pictureControlSetPtr->tempIntraLumaModeNeighborArray[contextPtr->mdContext->tileIndex], - pictureControlSetPtr->tempSkipFlagNeighborArray[contextPtr->mdContext->tileIndex], - contextPtr->mdContext->tileIndex, + pictureControlSetPtr->tempModeTypeNeighborArray[tileGroupIdx], + pictureControlSetPtr->tempLeafDepthNeighborArray[tileGroupIdx], + pictureControlSetPtr->tempIntraLumaModeNeighborArray[tileGroupIdx], + pictureControlSetPtr->tempSkipFlagNeighborArray[tileGroupIdx], + tileGroupIdx, 0, 0); - tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr))->writtenBitsCount + - 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + - (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[contextPtr->mdContext->tileIndex]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); + tempWrittenBitsAfterQuantizedCoeff = ((OutputBitstreamUnit_t*)EntropyCoderGetBitstreamPtr(pictureControlSetPtr->entropyCodingInfo[tileGroupIdx]->tempEntropyCoderPtr))->writtenBitsCount + + 32 - ((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[tileGroupIdx]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.bitsRemainingNum + + (((CabacEncodeContext_t*)pictureControlSetPtr->entropyCodingInfo[tileGroupIdx]->tempEntropyCoderPtr->cabacEncodeContextPtr)->bacEncContext.tempBufferedBytesNum << 3); lcuPtr->proxytotalBits = tempWrittenBitsAfterQuantizedCoeff - tempWrittenBitsBeforeQuantizedCoeff; lcuWidth = (sequenceControlSetPtr->lumaWidth - lcuOriginX) < (EB_U16)MAX_LCU_SIZE ? (sequenceControlSetPtr->lumaWidth - lcuOriginX) : (EB_U16)MAX_LCU_SIZE; lcuHeight = (sequenceControlSetPtr->lumaHeight - lcuOriginY) < (EB_U16)MAX_LCU_SIZE ? (sequenceControlSetPtr->lumaHeight - lcuOriginY) : (EB_U16)MAX_LCU_SIZE;