Index: encoder.c
===================================================================
RCS file: /xvid/xvidcore/src/encoder.c,v
retrieving revision 1.118
diff -u -r1.118 encoder.c
--- encoder.c	23 May 2005 09:29:43 -0000	1.118
+++ encoder.c	30 Jun 2005 20:05:04 -0000
@@ -536,6 +536,8 @@
 	return XVID_ERR_MEMORY;
 }
 
+extern void EndThread2(void);
+
 /*****************************************************************************
  * Encoder destruction
  *
@@ -640,6 +642,8 @@
 
 	xvid_free(pEnc);
 
+	EndThread2();
+
 	return 0;  /* ok */
 }
 
Index: motion/estimation_pvop.c
===================================================================
RCS file: /xvid/xvidcore/src/motion/estimation_pvop.c,v
retrieving revision 1.17
diff -u -r1.17 estimation_pvop.c
--- motion/estimation_pvop.c	31 Mar 2005 22:14:20 -0000	1.17
+++ motion/estimation_pvop.c	21 Nov 2005 16:50:27 -0000
@@ -784,6 +784,398 @@
 	}
 }
 
+static MBParam *g_pParam;
+static FRAMEINFO *g_current;
+static FRAMEINFO *g_reference;
+static IMAGE *g_pRefH;
+static IMAGE *g_pRefV;
+static IMAGE *g_pRefHV;
+static IMAGE *g_pGMC;
+static int g_MVmax = 0, g_mvSum = 0, g_mvCount = 0;
+static int t1blocks, t2blocks;
+
+//uint32_t *completed_blocks = NULL;
+int *mb_row_progress = NULL;
+int *mb_rows_ready = NULL;
+int mb_rows_ready_idx = -1;
+int mbs_left;
+static CRITICAL_SECTION block_list_lock;
+
+static HANDLE hThread2 = INVALID_HANDLE_VALUE;
+static int ThreadTerminated = 0;
+static HANDLE hThread2Start;
+static HANDLE hThread2Done;
+static HANDLE hExtraReadyBlocks;
+static FILE *f;
+
+static __inline bool MEGetNextBlock_st(bool completed, uint32_t* pX, uint32_t* pY)
+{
+	uint32_t x = *pX + 1;
+	uint32_t y = *pY;
+
+	/* this is the first call, X and Y should be 0 */
+	if (!completed)
+		return TRUE;
+
+	if (x < g_pParam->mb_width)
+	{
+		*pX = x;
+	}
+	else
+	{
+		*pX = 0;
+		y++;
+		if (y < g_pParam->mb_height)
+			*pY = y;
+		else
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static bool MEGetNextBlock_mt(bool completed, uint32_t* pX, uint32_t* pY)
+{
+	uint32_t x = *pX;
+	uint32_t y = *pY;
+	bool block_found = FALSE;
+
+	EnterCriticalSection(&block_list_lock);
+	//fprintf(f, "-%x=%u,%u idx %d (%d)\n", GetCurrentThreadId(), *pX, *pY, mb_rows_ready_idx, completed); fflush(f);
+	//fprintf(f, "Entered %x (%d)\n", GetCurrentThreadId(), completed); fflush(f);
+	if (completed && mbs_left)
+	{
+		bool bl_ready = FALSE;
+		bool r_ready = FALSE;
+
+		mb_row_progress[y] = x;
+		if ((x+1) < g_pParam->mb_width)
+		{
+			// if we're on the first row (y=0), right is always ready
+			// if the right (x+1,y) block is the last MB in a row, it is always ready
+			// if the right (x+1,y) block's top-left (x+2,y-1) block is done, it is ready
+			if ((y == 0) || ((x+2) == g_pParam->mb_width) || (mb_row_progress[y-1] >= (int)(x+2)))
+				r_ready = TRUE;
+		}
+
+		if ((x > 0) && ((y+1) < g_pParam->mb_height))
+		{
+			// if we just did the second block on a row (x=1), the bottom left is always ready
+			// if the bottom left (x-1, y+1) block's left (x-2, y+1) is done, it is ready to process
+			if ((x == 1) || (mb_row_progress[y+1] >= (int)(x - 2)))
+				bl_ready = TRUE;
+		}
+
+		/* Favor keeping the thread moving right */
+		if (r_ready)
+		{
+			block_found = TRUE;
+			*pX = x + 1;
+			mbs_left--;
+			/* if both are ready, put the bl block on the stack */
+			if (bl_ready)
+			{
+				mb_rows_ready[++mb_rows_ready_idx] = y + 1; // (x - 1) + (y + 1) * g_pParam->mb_width;
+				/* if this is the first block on the list,  or we've used the last block
+				   wake up the other thread that might be waiting */
+				if (mb_rows_ready_idx == 0)
+				{
+					//fprintf(f, "unblocking to start: idx=%d mbs_left=%d\n", ready_block_idx, mbs_left); fflush(f);
+					SetEvent(hExtraReadyBlocks);
+				}
+			}
+			else if (mbs_left == 0)
+			{
+				//fprintf(f, "unblocking due to end: idx=%d mbs_left=%d\n", ready_block_idx, mbs_left); fflush(f);
+				SetEvent(hExtraReadyBlocks);
+			}
+		}  /* if r_ready */
+		else if (bl_ready)
+		{
+			block_found = TRUE;
+			*pX = x - 1;
+			*pY = y + 1;
+			//retVal = (x - 1) + ((y + 1) * g_pParam->mb_width);
+			mbs_left--;
+			/* if the bottom left needs to be done, then mbs_left will never be 0, since there
+			   should always be a block to the right of the bl block */
+		}
+
+		//fprintf(f, "A%x=%u,%u idx %d (%s)\n", GetCurrentThreadId(), *pX, *pY, mb_rows_ready_idx, (block_found) ? "OK" : "SEARCHING"); fflush(f);
+	}  /* if completed */
+	LeaveCriticalSection(&block_list_lock);
+
+	while (!block_found && mbs_left)
+	{
+		EnterCriticalSection(&block_list_lock);
+		if (mb_rows_ready_idx >= 0)
+		{
+			block_found = TRUE;
+			y = mb_rows_ready[mb_rows_ready_idx--];
+			*pY = y;
+			*pX = mb_row_progress[y] + 1;
+
+			mbs_left--;
+		}
+		//fprintf(f, "B%x=%u,%u idx %d (%s)\n", GetCurrentThreadId(), *pX, *pY, mb_rows_ready_idx, (block_found) ? "OK" : "SEARCHING"); fflush(f);
+		LeaveCriticalSection(&block_list_lock);
+
+		if (!block_found)
+		{
+			//EnterCriticalSection(&block_list_lock);
+			//fprintf(f, "%x going to sleep\n", GetCurrentThreadId());fflush(f);
+			//LeaveCriticalSection(&block_list_lock);
+  			WaitForSingleObject(hExtraReadyBlocks, 10000);
+			//EnterCriticalSection(&block_list_lock);
+			//fprintf(f, "%x woke up\n", GetCurrentThreadId());	fflush(f);
+			//LeaveCriticalSection(&block_list_lock);
+		}
+	}
+
+	return block_found;
+}
+
+DWORD WINAPI Thread2Func(LPVOID parm)
+{
+
+	while (!ThreadTerminated)
+	{
+		WaitForSingleObject(hThread2Start, INFINITE);
+//EnterCriticalSection(&block_list_lock);
+//fprintf(f, "%x started\n", GetCurrentThreadId());fflush(f);
+//LeaveCriticalSection(&block_list_lock);
+		if (!ThreadTerminated)
+		{
+/*  BEGIN THREAD STITCH */
+	MACROBLOCK *const pMBs = g_current->mbs;
+	const IMAGE *const pCurrent = &g_current->image;
+	const IMAGE *const pRef = &g_reference->image;
+
+	const uint32_t mb_width = g_pParam->mb_width;
+	const uint32_t mb_height = g_pParam->mb_height;
+	const uint32_t iEdgedWidth = g_pParam->edged_width;
+	const uint32_t MotionFlags = MakeGoodMotionFlags(g_current->motion_flags, g_current->vop_flags, g_current->vol_flags);
+	int stat_thresh = 0;
+	bool block_completed = FALSE;
+
+	uint32_t x, y;
+	int32_t sad00;
+	int skip_thresh = INITIAL_SKIP_THRESH * \
+		(g_current->vop_flags & XVID_VOP_MODEDECISION_RD ? 2:1);
+
+	/* some pre-initialized thingies for SearchP */
+	DECLARE_ALIGNED_MATRIX(dct_space, 3, 64, int16_t, CACHE_LINE);
+	SearchData Data;
+	memset(&Data, 0, sizeof(SearchData));
+	Data.iEdgedWidth = iEdgedWidth;
+	Data.iFcode = g_current->fcode;
+	Data.rounding = g_pParam->m_rounding_type;
+	Data.qpel = (g_current->vol_flags & XVID_VOL_QUARTERPEL ? 1:0);
+	Data.chroma = MotionFlags & XVID_ME_CHROMA_PVOP;
+	Data.dctSpace = dct_space;
+	Data.quant_type = !(g_pParam->vol_flags & XVID_VOL_MPEGQUANT);
+	Data.mpeg_quant_matrices = g_pParam->mpeg_quant_matrices;
+
+	Data.RefQ = g_pRefV->u; /* a good place, also used in MC (for similar purpose) */
+//	if (sadInit) (*sadInit) ();
+
+	g_MVmax = 0; g_mvSum = 0; g_mvCount = 0;
+	x = y = 0;
+	t2blocks = 0;
+	while (MEGetNextBlock_mt(block_completed, &x, &y))
+	{
+		t2blocks++;
+		block_completed = TRUE;
+//	for (y = 0; y < mb_height; y++)	{
+//		for (x = 0; x < mb_width; x++)	{
+		{
+			MACROBLOCK *pMB = &pMBs[x + y * g_pParam->mb_width];
+			MACROBLOCK *prevMB = &g_reference->mbs[x + y * g_pParam->mb_width];
+
+			pMB->sad16 =
+				sad16v(pCurrent->y + (x + y * iEdgedWidth) * 16,
+							pRef->y + (x + y * iEdgedWidth) * 16,
+							g_pParam->edged_width, pMB->sad8);
+			
+			sad00 = 4*MAX(MAX(pMB->sad8[0], pMB->sad8[1]), MAX(pMB->sad8[2], pMB->sad8[3]));
+
+			if (Data.chroma) {
+				Data.chromaSAD = sad8(pCurrent->u + x*8 + y*(iEdgedWidth/2)*8,
+									pRef->u + x*8 + y*(iEdgedWidth/2)*8, iEdgedWidth/2)
+								+ sad8(pCurrent->v + (x + y*(iEdgedWidth/2))*8,
+									pRef->v + (x + y*(iEdgedWidth/2))*8, iEdgedWidth/2);
+				pMB->sad16 += Data.chromaSAD;
+				sad00 += Data.chromaSAD;
+			}
+
+			/* initial skip decision */
+			if (g_current->coding_type != S_VOP)	{ /* no fast SKIP for S(GMC)-VOPs */
+				if (pMB->dquant == 0 && sad00 < pMB->quant * skip_thresh)
+					if (Data.chroma || xvid_me_SkipDecisionP(pCurrent, pRef, x, y, iEdgedWidth/2, pMB->quant)) {
+						ZeroMacroblockP(pMB, sad00);
+						pMB->mode = MODE_NOT_CODED;
+						continue;
+					}
+			}
+
+			if(MotionFlags & XVID_ME_DETECT_STATIC_MOTION) {
+				VECTOR *cmpMV;
+				VECTOR staticMV = { 0, 0 };
+				
+				if (g_current->coding_type == S_VOP) 
+					cmpMV = &pMB->amv;
+				else
+					cmpMV = &staticMV;
+
+				if(x > 0 && y > 0 && x < g_pParam->mb_width) {
+					if(MVequal((&pMBs[(x-1) + y * g_pParam->mb_width])->mvs[0], *cmpMV) &&
+					   MVequal((&pMBs[x + (y-1) * g_pParam->mb_width])->mvs[0], *cmpMV) &&
+				       MVequal((&pMBs[(x+1) + (y-1) * g_pParam->mb_width])->mvs[0], *cmpMV) &&
+				       MVequal(prevMB->mvs[0], *cmpMV)) {
+						stat_thresh = MAX((&pMBs[(x-1) + y * g_pParam->mb_width])->sad16,
+								 	  MAX((&pMBs[x + (y-1) * g_pParam->mb_width])->sad16,
+									  MAX((&pMBs[(x+1) + (y-1) * g_pParam->mb_width])->sad16,
+									  prevMB->sad16)));
+					} else {
+						stat_thresh = MIN((&pMBs[(x-1) + y * g_pParam->mb_width])->sad16,
+								 	  MIN((&pMBs[x + (y-1) * g_pParam->mb_width])->sad16,
+									  MIN((&pMBs[(x+1) + (y-1) * g_pParam->mb_width])->sad16,
+									  prevMB->sad16)));
+					}
+				}
+			}
+
+			 /* favorize (0,0) or global vector for cartoons */
+			if (g_current->vop_flags & XVID_VOP_CARTOON) {
+				if (g_current->coding_type == S_VOP) {
+					int32_t iSAD = sad16(pCurrent->y + (x + y * iEdgedWidth) * 16,
+					g_pGMC->y + 16*y*iEdgedWidth + 16*x, iEdgedWidth, 65536);
+
+					if (Data.chroma) {
+						iSAD += sad8(pCurrent->u + x*8 + y*(iEdgedWidth/2)*8, g_pGMC->u + 8*y*(iEdgedWidth/2) + 8*x, iEdgedWidth/2);
+						iSAD += sad8(pCurrent->v + (x + y*(iEdgedWidth/2))*8, g_pGMC->v + 8*y*(iEdgedWidth/2) + 8*x, iEdgedWidth/2);
+					}
+
+					if (iSAD <= stat_thresh) {		/* mode decision GMC */
+						pMB->mode = MODE_INTER;
+						pMB->sad16 = pMB->sad8[0] = pMB->sad8[1] = pMB->sad8[2] = pMB->sad8[3] = iSAD;
+						pMB->mcsel = 1;
+						if (Data.qpel) {
+							pMB->qmvs[0] = pMB->qmvs[1] = pMB->qmvs[2] = pMB->qmvs[3] = pMB->amv;
+							pMB->mvs[0].x = pMB->mvs[1].x = pMB->mvs[2].x = pMB->mvs[3].x = pMB->amv.x/2;
+							pMB->mvs[0].y = pMB->mvs[1].y = pMB->mvs[2].y = pMB->mvs[3].y = pMB->amv.y/2;
+						} else
+							pMB->mvs[0] = pMB->mvs[1] = pMB->mvs[2] = pMB->mvs[3] = pMB->amv;
+						
+						continue;
+					}
+				}
+				else if (sad00 < stat_thresh) {
+					VECTOR predMV;
+					if (g_current->vol_flags & XVID_VOL_QUARTERPEL)
+						predMV = get_qpmv2(g_current->mbs, mb_width, 0, x, y, 0);
+					else
+						predMV = get_pmv2(g_current->mbs, mb_width, 0, x, y, 0);
+
+					ZeroMacroblockP(pMB, sad00);
+					pMB->cbp = 0x3f;
+					pMB->pmvs[0].x = - predMV.x;
+					pMB->pmvs[0].y = - predMV.y;
+					continue;
+				}
+			}
+
+			SearchP(pRef, g_pRefH->y, g_pRefV->y, g_pRefHV->y, pCurrent, x,
+					y, MotionFlags, g_current->vop_flags,
+					&Data, g_pParam, pMBs, g_reference->mbs, pMB);
+
+			if (g_current->vop_flags & XVID_VOP_MODEDECISION_RD)
+				xvid_me_ModeDecision_RD(&Data, pMB, pMBs, x, y, g_pParam,
+								MotionFlags, g_current->vop_flags, g_current->vol_flags,
+								pCurrent, pRef, g_pGMC, g_current->coding_type);
+
+			else if (g_current->vop_flags & XVID_VOP_FAST_MODEDECISION_RD)
+				xvid_me_ModeDecision_Fast(&Data, pMB, pMBs, x, y, g_pParam,
+								MotionFlags, g_current->vop_flags, g_current->vol_flags,
+								pCurrent, pRef, g_pGMC, g_current->coding_type);
+			else
+				ModeDecision_SAD(&Data, pMB, pMBs, x, y, g_pParam,
+								MotionFlags, g_current->vop_flags, g_current->vol_flags,
+								pCurrent, pRef, g_pGMC, g_current->coding_type, sad00);
+
+
+			motionStatsPVOP(&g_MVmax, &g_mvCount, &g_mvSum, pMB, Data.qpel);
+		}
+	}
+
+
+
+/*  END THREAD STITCH */
+//EnterCriticalSection(&block_list_lock);
+//fprintf(f, "%x done\n", GetCurrentThreadId());fflush(f);
+//LeaveCriticalSection(&block_list_lock);
+			SetEvent(hThread2Done);
+		}
+	}  /* while !Terminated */
+
+	SetEvent(hThread2Done);
+	return 0;
+}
+
+void EndThread2(void)
+{
+	if (hThread2 != INVALID_HANDLE_VALUE)
+	{
+		ThreadTerminated = 1;
+		SetEvent(hThread2Start);
+		if (WaitForSingleObject(hThread2Done, 5000) == WAIT_TIMEOUT)
+			fprintf(f, "Thread would not fucking end!\n"); fflush(f);
+
+		CloseHandle(hExtraReadyBlocks);
+		CloseHandle(hThread2Start);
+		CloseHandle(hThread2Done);
+		CloseHandle(hThread2);
+
+		hThread2Start = INVALID_HANDLE_VALUE;
+		hThread2Done = INVALID_HANDLE_VALUE;
+		hThread2 = INVALID_HANDLE_VALUE;
+
+		fclose(f);
+	}
+}
+
+static void StartThread2(void)
+{
+	if (hThread2 == INVALID_HANDLE_VALUE)
+	{
+		int iPriority;
+
+		ThreadTerminated = 0;
+		hThread2Start = CreateEvent(NULL, FALSE, FALSE, NULL);
+		hThread2Done = CreateEvent(NULL, FALSE, FALSE, NULL);
+		hExtraReadyBlocks = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+		hThread2 = CreateThread(NULL, 0, Thread2Func, NULL, 0, NULL);
+		//SetThreadAffinityMask(hThread2, 0x2);
+		//SetThreadAffinityMask(GetCurrentThread(), 0x1);
+
+		f = fopen("\\x.log", "w");
+
+		//iPriority = GetThreadPriority(GetCurrentThread());
+		//fprintf(f, "Setting thread priority from %d to %d\n", GetThreadPriority(hThread2), iPriority);
+		//SetThreadPriority(hThread2, iPriority);
+	}
+	SetEvent(hThread2Start);
+}
+
+static void WaitForThread2(void)
+{
+	if (hThread2 != INVALID_HANDLE_VALUE)
+	{
+		WaitForSingleObject(hThread2Done, INFINITE);
+	}
+}
+
 bool
 MotionEstimation(MBParam * const pParam,
 				 FRAMEINFO * const current,
@@ -794,6 +1186,31 @@
 				const IMAGE * const pGMC,
 				 const uint32_t iLimit)
 {
+	uint32_t x, y;
+	if (sadInit) (*sadInit) ();
+
+g_pParam = pParam;
+g_current = current;
+g_reference = reference;
+g_pRefH = pRefH;
+g_pRefV = pRefV;
+g_pRefHV = pRefHV;
+g_pGMC = pGMC;
+
+	mbs_left = pParam->mb_width * pParam->mb_height;
+	if (mb_row_progress == NULL)
+	{
+		mb_row_progress = (int *)malloc(pParam->mb_height * sizeof(int));
+		mb_rows_ready = (int *)malloc(pParam->mb_height * sizeof(int));
+		InitializeCriticalSection(&block_list_lock);
+	}
+	for (y=0; y<pParam->mb_height; y++)
+		mb_row_progress[y] = -1;
+	mb_rows_ready[0] = 0;
+	mb_rows_ready_idx = 0;
+
+	StartThread2();	
+	{
 	MACROBLOCK *const pMBs = current->mbs;
 	const IMAGE *const pCurrent = &current->image;
 	const IMAGE *const pRef = &reference->image;
@@ -804,8 +1221,8 @@
 	const uint32_t MotionFlags = MakeGoodMotionFlags(current->motion_flags, current->vop_flags, current->vol_flags);
 	int stat_thresh = 0;
 	int MVmax = 0, mvSum = 0, mvCount = 0;
+	bool block_completed = FALSE;
 
-	uint32_t x, y;
 	int32_t sad00;
 	int skip_thresh = INITIAL_SKIP_THRESH * \
 		(current->vop_flags & XVID_VOP_MODEDECISION_RD ? 2:1);
@@ -813,6 +1230,7 @@
 	/* some pre-initialized thingies for SearchP */
 	DECLARE_ALIGNED_MATRIX(dct_space, 3, 64, int16_t, CACHE_LINE);
 	SearchData Data;
+		
 	memset(&Data, 0, sizeof(SearchData));
 	Data.iEdgedWidth = iEdgedWidth;
 	Data.iFcode = current->fcode;
@@ -824,14 +1242,20 @@
 	Data.mpeg_quant_matrices = pParam->mpeg_quant_matrices;
 
 	Data.RefQ = pRefV->u; /* a good place, also used in MC (for similar purpose) */
-	if (sadInit) (*sadInit) ();
 
-	for (y = 0; y < mb_height; y++)	{
-		for (x = 0; x < mb_width; x++)	{
+    x = y = 0;
+	t1blocks = 0;
+	while (MEGetNextBlock_mt(block_completed, &x, &y))
+	{
+		t1blocks++;
+		block_completed = TRUE;
+//	for (y = 0; y < mb_height; y++)	{
+//		for (x = 0; x < mb_width; x++)	{
+		{
 			MACROBLOCK *pMB = &pMBs[x + y * pParam->mb_width];
 			MACROBLOCK *prevMB = &reference->mbs[x + y * pParam->mb_width];
 
-			pMB->sad16 =
+			pMB->sad16 = 
 				sad16v(pCurrent->y + (x + y * iEdgedWidth) * 16,
 							pRef->y + (x + y * iEdgedWidth) * 16,
 							pParam->edged_width, pMB->sad8);
@@ -944,12 +1368,27 @@
 
 
 			motionStatsPVOP(&MVmax, &mvCount, &mvSum, pMB, Data.qpel);
+//		}
 		}
+//	}
 	}
 
+//EnterCriticalSection(&block_list_lock);
+//fprintf(f, "%x waiting\n", GetCurrentThreadId());fflush(f);
+//LeaveCriticalSection(&block_list_lock);
+WaitForThread2();
+//EnterCriticalSection(&block_list_lock);
+//fprintf(f, "%x continuing\n", GetCurrentThreadId());fflush(f);
+//LeaveCriticalSection(&block_list_lock);
+
+//fprintf(f, "%04d, %04d\n", t1blocks, t2blocks);
+
+	if (g_MVmax > MVmax)
+		MVmax = g_MVmax;
 	current->fcode = getMinFcode(MVmax);
-	current->sStat.iMvSum = mvSum;
-	current->sStat.iMvCount = mvCount;
+	current->sStat.iMvSum = mvSum + g_mvSum;
+	current->sStat.iMvCount = mvCount + g_mvCount;
+	}
 
 	return 0;
 }
