bool useElapsedTime = true;
bool updateRequired = true;
+ uint64_t timeToSleepUntil = 0;
+ int extraFramesDropped = 0;
- while( UpdateRenderReady( useElapsedTime, updateRequired ) )
+ while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
{
LOG_UPDATE_RENDER_TRACE;
if( useElapsedTime )
{
// If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
- // Round up if remainder is more than half the default frame time
- noOfFramesSinceLastUpdate = ( timeSinceLastFrame + mDefaultHalfFrameNanoseconds) / mDefaultFrameDurationNanoseconds;
+ noOfFramesSinceLastUpdate += extraFramesDropped;
+
frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
}
LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
// FRAME TIME
//////////////////////////////
+ extraFramesDropped = 0;
+
+ if (timeToSleepUntil == 0)
+ {
+ // If this is the first frame after the thread is initialized or resumed, we
+ // use the actual time the current frame starts from to calculate the time to
+ // sleep until the next frame.
+ timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
+ }
+ else
+ {
+ // Otherwise, always use the sleep-until time calculated in the last frame to
+ // calculate the time to sleep until the next frame. In this way, if there is
+ // any time gap between the current frame and the next frame, or if update or
+ // rendering in the current frame takes too much time so that the specified
+ // sleep-until time has already passed, it will try to keep the frames syncing
+ // by shortening the duration of the next frame.
+ timeToSleepUntil += mDefaultFrameDurationNanoseconds;
+
+ // Check the current time at the end of the frame
+ uint64_t currentFrameEndTime = 0;
+ TimeService::GetNanoseconds( currentFrameEndTime );
+ while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
+ {
+ // We are more than one frame behind already, so just drop the next frames
+ // until the sleep-until time is later than the current time so that we can
+ // catch up.
+ timeToSleepUntil += mDefaultFrameDurationNanoseconds;
+ extraFramesDropped++;
+ }
+ }
+
// Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
- TimeService::SleepUntil( currentFrameStartTime + mDefaultFrameDurationNanoseconds );
+ TimeService::SleepUntil( timeToSleepUntil );
}
// Inform core of context destruction & shutdown EGL
mEnvironmentOptions.UnInstallLogFunction();
}
-bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired )
+bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
{
useElapsedTime = true;
LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
+ // Reset the time when the thread is waiting, so the sleep-until time for
+ // the first frame after resuming should be based on the actual start time
+ // of the first frame.
+ timeToSleepUntil = 0;
+
mUpdateRenderThreadWaitCondition.Wait( updateLock );
if( ! mUseElapsedTimeAfterWait )
/**
* Called by the Update/Render Thread which ensures a wait if required.
*
- * @param[out] useElapsedTime If true when returned, then the actual elapsed time will be used for animation.
- * If false when returned, then there should NOT be any animation progression in the next Update.
- * @param[in] updateRequired Whether another update is required.
+ * @param[out] useElapsedTime If true when returned, then the actual elapsed time will be used for animation.
+ * If false when returned, then there should NOT be any animation progression in the next Update.
+ * @param[in] updateRequired Whether another update is required.
+ * @param[out] timeToSleepUntil The time in nanoseconds to put the thread to sleep until.
* @return false, if the thread should stop.
*/
- bool UpdateRenderReady( bool& useElapsedTime, bool updateRequired );
+ bool UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil );
/**
* Checks to see if the surface needs to be replaced.
// Constants used by the ImageResampler.
const float DEFAULT_SOURCE_GAMMA = 1.75f; ///< Default source gamma value used in the Resampler() function. Partial gamma correction looks better on mips. Set to 1.0 to disable gamma correction.
const float FILTER_SCALE = 1.f; ///< Default filter scale value used in the Resampler() function. Filter scale - values < 1.0 cause aliasing, but create sharper looking mips.
-const char* const FILTER_TYPE = "lanczos4"; ///< Default filter used in the Resampler() function. Possible Lanczos filters are: lanczos3, lanczos4, lanczos6, lanczos12
+const Resampler::Filter FILTER_TYPE = Resampler::LANCZOS4; ///< Default filter used in the Resampler() function. Possible Lanczos filters are: lanczos3, lanczos4, lanczos6, lanczos12
using Integration::Bitmap;
using Integration::BitmapPtr;
// filters[] is a list of all the available filter functions.
static struct
{
- char name[32];
+ Resampler::Filter name;
Resample_Real (*func)(Resample_Real t);
Resample_Real support;
} g_filters[] =
{
- { "box", box_filter, BOX_FILTER_SUPPORT },
- { "tent", tent_filter, TENT_FILTER_SUPPORT },
- { "bell", bell_filter, BELL_SUPPORT },
- { "b-spline", B_spline_filter, B_SPLINE_SUPPORT },
- { "mitchell", mitchell_filter, MITCHELL_SUPPORT },
- { "lanczos3", lanczos3_filter, LANCZOS3_SUPPORT },
- { "blackman", blackman_filter, BLACKMAN_SUPPORT },
- { "lanczos4", lanczos4_filter, LANCZOS4_SUPPORT },
- { "lanczos6", lanczos6_filter, LANCZOS6_SUPPORT },
- { "lanczos12", lanczos12_filter, LANCZOS12_SUPPORT },
- { "kaiser", kaiser_filter, KAISER_SUPPORT },
- { "gaussian", gaussian_filter, GAUSSIAN_SUPPORT },
- { "catmullrom", catmull_rom_filter, CATMULL_ROM_SUPPORT },
- { "quadratic_interp", quadratic_interp_filter, QUADRATIC_SUPPORT },
- { "quadratic_approx", quadratic_approx_filter, QUADRATIC_SUPPORT },
- { "quadratic_mix", quadratic_mix_filter, QUADRATIC_SUPPORT },
+ { Resampler::BOX, box_filter, BOX_FILTER_SUPPORT },
+ { Resampler::TENT, tent_filter, TENT_FILTER_SUPPORT },
+ { Resampler::BELL, bell_filter, BELL_SUPPORT },
+ { Resampler::B_SPLINE, B_spline_filter, B_SPLINE_SUPPORT },
+ { Resampler::MITCHELL, mitchell_filter, MITCHELL_SUPPORT },
+ { Resampler::LANCZOS3, lanczos3_filter, LANCZOS3_SUPPORT },
+ { Resampler::BLACKMAN, blackman_filter, BLACKMAN_SUPPORT },
+ { Resampler::LANCZOS4, lanczos4_filter, LANCZOS4_SUPPORT },
+ { Resampler::LANCZOS6, lanczos6_filter, LANCZOS6_SUPPORT },
+ { Resampler::LANCZOS12, lanczos12_filter, LANCZOS12_SUPPORT },
+ { Resampler::KAISER, kaiser_filter, KAISER_SUPPORT },
+ { Resampler::GAUSSIAN, gaussian_filter, GAUSSIAN_SUPPORT },
+ { Resampler::CATMULLROM, catmull_rom_filter, CATMULL_ROM_SUPPORT },
+ { Resampler::QUADRATIC_INTERPOLATION, quadratic_interp_filter, QUADRATIC_SUPPORT },
+ { Resampler::QUADRATIC_APPROXIMATION, quadratic_approx_filter, QUADRATIC_SUPPORT },
+ { Resampler::QUADRATIC_MIX, quadratic_mix_filter, QUADRATIC_SUPPORT },
};
static const int NUM_FILTERS = sizeof(g_filters) / sizeof(g_filters[0]);
int dst_x, int dst_y,
Boundary_Op boundary_op,
Resample_Real sample_low, Resample_Real sample_high,
- const char* Pfilter_name,
+ Resampler::Filter filter,
Contrib_List* Pclist_x,
Contrib_List* Pclist_y,
Resample_Real filter_x_scale,
}
// Find the specified filter.
-
- if (Pfilter_name == NULL)
- Pfilter_name = RESAMPLER_DEFAULT_FILTER;
-
for (i = 0; i < NUM_FILTERS; i++)
- if (strcmp(Pfilter_name, g_filters[i].name) == 0)
+ if ( filter == g_filters[i].name )
break;
if (i == NUM_FILTERS)
{
- m_status = STATUS_BAD_FILTER_NAME;
+ m_status = STATUS_BAD_FILTER_TYPE;
return;
}
*ptr_clist_y = m_Pclist_y;
}
-int Resampler::get_filter_num()
-{
- return NUM_FILTERS;
-}
-
-char* Resampler::get_filter_name(int filter_num)
-{
- if ((filter_num < 0) || (filter_num >= NUM_FILTERS))
- return NULL;
- else
- return g_filters[filter_num].name;
-}
-
#pragma GCC diagnostic pop
// resampler.h, Separable filtering image rescaler v2.21, Rich Geldreich - richgel99@gmail.com
// See unlicense.org text at the bottom of this file.
+// Modified to specify filters as enum rather than char *
#ifndef __RESAMPLER_H__
#define __RESAMPLER_H__
#define RESAMPLER_DEBUG_OPS 0
-#define RESAMPLER_DEFAULT_FILTER "lanczos4"
#define RESAMPLER_MAX_DIMENSION 16384
public:
typedef Resample_Real Sample;
+ /**
+ * Supported filter types
+ */
+ enum Filter
+ {
+ BOX,
+ TENT,
+ BELL,
+ B_SPLINE,
+ MITCHELL,
+ LANCZOS3,
+ BLACKMAN,
+ LANCZOS4,
+ LANCZOS6,
+ LANCZOS12,
+ KAISER,
+ GAUSSIAN,
+ CATMULLROM,
+ QUADRATIC_INTERPOLATION,
+ QUADRATIC_APPROXIMATION,
+ QUADRATIC_MIX,
+ };
+
struct Contrib
{
Resample_Real weight;
{
STATUS_OKAY = 0,
STATUS_OUT_OF_MEMORY = 1,
- STATUS_BAD_FILTER_NAME = 2,
+ STATUS_BAD_FILTER_TYPE = 2,
STATUS_SCAN_BUFFER_FULL = 3
};
int dst_x, int dst_y,
Boundary_Op boundary_op = BOUNDARY_CLAMP,
Resample_Real sample_low = 0.0f, Resample_Real sample_high = 0.0f,
- const char* Pfilter_name = RESAMPLER_DEFAULT_FILTER,
+ Resampler::Filter = Resampler::LANCZOS3,
Contrib_List* Pclist_x = NULL,
Contrib_List* Pclist_y = NULL,
Resample_Real filter_x_scale = 1.0f,
Contrib_List* get_clist_x() const { return m_Pclist_x; }
Contrib_List* get_clist_y() const { return m_Pclist_y; }
- // Filter accessors.
- static int get_filter_num();
- static char* get_filter_name(int filter_num);
-
private:
Resampler();
Resampler(const Resampler& o);