// main entry point int VP8EncAnalyze(VP8Encoder* const enc) { int ok = 1; const int do_segments = enc->config_->emulate_jpeg_size || // We need the complexity evaluation. (enc->segment_hdr_.num_segments_ > 1) || (enc->method_ == 0); // for method 0, we need preds_[] to be filled. if (do_segments) { const int last_row = enc->mb_h_; // We give a little more than a half work to the main thread. const int split_row = (9 * last_row + 15) >> 4; const int total_mb = last_row * enc->mb_w_; #ifdef WEBP_USE_THREAD const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow); #else const int do_mt = 0; #endif const WebPWorkerInterface* const worker_interface = WebPGetWorkerInterface(); SegmentJob main_job; if (do_mt) { SegmentJob side_job; // Note the use of '&' instead of '&&' because we must call the functions // no matter what. InitSegmentJob(enc, &main_job, 0, split_row); InitSegmentJob(enc, &side_job, split_row, last_row); // we don't need to call Reset() on main_job.worker, since we're calling // WebPWorkerExecute() on it ok &= worker_interface->Reset(&side_job.worker); // launch the two jobs in parallel if (ok) { worker_interface->Launch(&side_job.worker); worker_interface->Execute(&main_job.worker); ok &= worker_interface->Sync(&side_job.worker); ok &= worker_interface->Sync(&main_job.worker); } worker_interface->End(&side_job.worker); if (ok) MergeJobs(&side_job, &main_job); // merge results together } else { // Even for single-thread case, we use the generic Worker tools. InitSegmentJob(enc, &main_job, 0, last_row); worker_interface->Execute(&main_job.worker); ok &= worker_interface->Sync(&main_job.worker); } worker_interface->End(&main_job.worker); if (ok) { enc->alpha_ = main_job.alpha / total_mb; enc->uv_alpha_ = main_job.uv_alpha / total_mb; AssignSegments(enc, main_job.alphas); } } else { // Use only one default segment.
// initialize the job struct with some TODOs static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job, int start_row, int end_row) { WebPGetWorkerInterface()->Init(&job->worker); job->worker.data1 = job; job->worker.data2 = &job->it; job->worker.hook = (WebPWorkerHook)DoSegmentsJob; VP8IteratorInit(enc, &job->it); VP8IteratorSetRow(&job->it, start_row); VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_); memset(job->alphas, 0, sizeof(job->alphas)); job->alpha = 0; job->uv_alpha = 0; // only one of both jobs can record the progress, since we don't // expect the user's hook to be multi-thread safe job->delta_progress = (start_row == 0) ? 20 : 0; }
static THREADFN ThreadLoop(void* ptr) { WebPWorker* const worker = (WebPWorker*)ptr; WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_; int done = 0; while (!done) { pthread_mutex_lock(&impl->mutex_); while (worker->status_ == OK) { // wait in idling mode pthread_cond_wait(&impl->condition_, &impl->mutex_); } if (worker->status_ == WORK) { WebPGetWorkerInterface()->Execute(worker); worker->status_ = OK; } else if (worker->status_ == NOT_OK) { // finish the worker done = 1; } // signal to the main thread that we're done (for Sync()) pthread_cond_signal(&impl->condition_); pthread_mutex_unlock(&impl->mutex_); } return THREAD_RETURN(NULL); // Thread is finished }