[Intel-gfx] [PATCH v2] Idleness DRRS test

Chris Wilson chris at chris-wilson.co.uk
Fri Sep 23 13:18:31 UTC 2016


On Fri, Sep 23, 2016 at 06:10:29PM +0530, Nautiyal Ankit wrote:
> From: Ramalingam C <ramalingam.c at intel.com>
> 
> Idleness DRRS:
> 	By default the DRRS state will be at DRRS_HIGH_RR. When a Display
> 	content is Idle for more than 1Sec Idleness will be declared and
> 	DRRS_LOW_RR will be invoked, changing the refresh rate to the
> 	lower most refresh rate supported by the panel. As soon as there
> 	is a display content change there will be a DRRS state transition
> 	as DRRS_LOW_RR--> DRRS_HIGH_RR, changing the refresh rate to the
> 	highest refresh rate supported by the panel.
> 
> To test this, Idleness DRRS IGT will probe the DRRS state at below
> instances and compare with the expected state.
> 
> 	Instance					Expected State
> 1. Immediately after rendering the still image		DRRS_HIGH_RR
> 2. After a delay of 1.2Sec				DRRS_LOW_RR
> 3. After changing the frame buffer			DRRS_HIGH_RR
> 4. After a delay of 1.2Sec				DRRS_LOW_RR
> 5. After changing the frame buffer			DRRS_HIGH_RR
> 6. After a delay of 1.2Sec				DRRS_LOW_RR
> 
> The test checks the driver DRRS state from the debugfs entry. To check the
> actual refresh-rate, a separate thread counts the number of vblanks
> received per sec. The refresh-rate calculated is checked against the
> expected refresh-rate with a tolerance value of 2.
> 
> This patch is a continuation of the earlier work
> https://patchwork.freedesktop.org/patch/45472/ towards igt for idleness
> 
> DRRS. The code is tested on Broxton BXT_T platform.
> 
> v2: Addressed the comments and suggestions from Vlad, Marius.
> The signoff details from the earlier work are also included.
> 
> Signed-off-by: Ramalingam C <ramalingam.c at intel.com>
> Signed-off-by: Vandana Kannan <vandana.kannan at intel.com>
> Signed-off-by: aknautiy <ankit.k.nautiyal at intel.com>
> ---
>  tests/Makefile.sources |   1 +
>  tests/kms_drrs.c       | 612 +++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 613 insertions(+)
>  create mode 100644 tests/kms_drrs.c
> 
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index a837977..5f31521 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -91,6 +91,7 @@ TESTS_progs_M = \
>  	kms_cursor_crc \
>  	kms_cursor_legacy \
>  	kms_draw_crc \
> +	kms_drrs \
>  	kms_fbc_crc \
>  	kms_fbcon_fbt \
>  	kms_flip \
> diff --git a/tests/kms_drrs.c b/tests/kms_drrs.c
> new file mode 100644
> index 0000000..69f8b06
> --- /dev/null
> +++ b/tests/kms_drrs.c
> @@ -0,0 +1,612 @@
> +/*
> + * Copyright © 2016 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + *
> + */
> +
> +#include "drmtest.h"
> +#include "igt_debugfs.h"
> +#include "igt_kms.h"
> +#include "intel_chipset.h"
> +#include "intel_batchbuffer.h"
> +#include "ioctl_wrappers.h"
> +#include <time.h>
> +#include <pthread.h>
> +#include <stdlib.h>
> +#include <sys/time.h>
> +IGT_TEST_DESCRIPTION(
> +"Performs write operations and then waits for DRRS to invoke the"
> +"Low Refresh Rate and then disturbs the contents of the screen once"
> +"again hence DRRS revert back to High Refresh Rate(Default).");
> +
> +#define DRRS_STATUS_BYTES_CNT	1000
> +#define SET	1
> +#define RESET	0
> +
> +/*
> + * Structure to store data to create 2 framebuffers, fb[0] and fb[1] on a given
> + * display. To disturb the content of the screen, we replace fb[0] by fb[1] and
> + * vice versa.
> + */
> +typedef struct {
> +	int drm_fd;
> +	uint32_t devid;
> +	uint32_t handle[2];
> +	igt_display_t display;
> +	igt_output_t *output;
> +	enum pipe pipe;
> +	igt_plane_t *primary;
> +	struct igt_fb fb[2];
> +	uint32_t fb_id[2];
> +} data_t;
> +
> +/*
> + * Structure to count vblank and note the starting time of the counter
> + */
> +typedef struct {
> +	unsigned int vbl_count;
> +	struct timeval start;
> +} vbl_info;
> +
> +/*
> + * Condition variables,mutex, and shared variables for thread synchronization
> + */
> +pthread_mutex_t rr_mutex;
> +pthread_cond_t cv_rr_calc_requested;
> +pthread_cond_t cv_rr_calc_completed;
> +int rr_calc_requested = RESET;
> +int rr_calc_completed = RESET;
> +int thread_flag = SET;
> +int refresh_rate_shared = -1;
> +
> +/*
> + * Structure for refresh rate type
> + */
> +typedef struct{
> +	int rate;
> +	const char *name;
> +} refresh_rate_t;
> +
> +/*
> + * vblank_handler :
> + *	User defined vblank event handler, to count vblanks.
> + *	The control reaches this function after a vblank event is registered
> + */
> +static void vblank_handler(int fd, unsigned int frame,
> +			unsigned int sec, unsigned int usec, void *data)
> +{
> +	/* The control reaches this function after a vblank event is
> +	 * registered
> +	 */
> +	vbl_info *info = (vbl_info *) data;
> +
> +	if (info == NULL) {
> +		igt_info("ERROR: Invalid Data passed to the vblank handler\n");
> +		return;
> +	}
> +	info->vbl_count++;
> +}
> +
> +
> +/*
> + * read_drrs_status :
> + *	Func to read the DRRS status from debugfs
> + */
> +static bool read_drrs_status(char *str)
> +{
> +	FILE *status;
> +	int cnt;
> +
> +	status = igt_debugfs_fopen("i915_drrs_status", "r");
> +	igt_assert(status);
> +
> +	cnt = fread(str, DRRS_STATUS_BYTES_CNT - 1, 1, status);
> +	if (!cnt) {
> +		if (!feof(status)) {
> +			igt_info("ERROR: %d at fread\n", ferror(status));
> +			return false;
> +		}
> +		clearerr(status);
> +	}
> +	fclose(status);
> +	return true;
> +}
> +
> +/*
> + * is_drrs_supported :
> + *	Func to check for DRRS support
> + */
> +static bool is_drrs_supported(void)
> +{
> +	char str[DRRS_STATUS_BYTES_CNT] = {};
> +
> +	if (!read_drrs_status(str)) {
> +		igt_info("ERROR: Debugfs read FAILED\n");
> +		return false;
> +	}
> +	return strstr(str, "DRRS Supported: Yes") != NULL;
> +}
> +
> +/*
> + * is_drrs_enabled :
> + *	Func to check if DRRS is enabled by driver
> + */
> +static bool is_drrs_enabled(void)
> +{
> +	char str[DRRS_STATUS_BYTES_CNT] = {};
> +
> +	if (!read_drrs_status(str)) {
> +		igt_info("ERROR: Debugfs read FAILED\n");
> +		return false;
> +	}
> +	return strstr(str, "Idleness DRRS: Disabled") == NULL;
> +}
> +
> +/*
> + * prepare_crtc :
> + *	Func to prepare crtc for the given display
> + */
> +static bool prepare_crtc(data_t *data)
> +{
> +	if (data == NULL)
> +		return false;
> +	igt_display_t *display = &data->display;
> +	igt_output_t *output = data->output;
> +
> +	/* select the pipe we want to use */
> +	igt_output_set_pipe(output, data->pipe);
> +	igt_display_commit(display);
> +
> +	if (!output->valid) {
> +		igt_output_set_pipe(output, PIPE_ANY);
> +		igt_display_commit(display);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +/*
> + * calculate_refresh_rate :
> + *	Func to calculate the refresh rate by counting
> + *	vblanks in 1 sec. It returns the calculated refresh rate on success.
> + *	Returns -1 in case of error.
> + */
> +int calculate_refresh_rate(data_t *data)
> +{
> +	drmEventContext evctx;
> +	drmVBlank vbl;
> +	vbl_info handler_info;
> +	struct timeval start_time, curr_time;
> +	double time_elapsed;
> +	int refresh_rate = 0, vbl_count = 0;
> +	/*set up event context for handling vblank events*/
> +	memset(&evctx, 0, sizeof(evctx));
> +	evctx.version = DRM_EVENT_CONTEXT_VERSION;
> +	evctx.vblank_handler = vblank_handler;
> +	evctx.page_flip_handler = NULL;
> +
> +	/*initialize the vbl count to zero and set the starting time*/
> +	handler_info.vbl_count = 0;
> +	gettimeofday(&start_time, NULL);
> +	handler_info.start = start_time;
> +	curr_time = start_time;
> +
> +	time_elapsed = 0.0;
> +	/* To count vblanks in 1 sec. Loop till
> +	 * (current-time - starting time) <= 1.0 sec
> +	 */
> +	while (time_elapsed <= 1.0) {
> +		int ret;
> +
> +		vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
> +		vbl.request.sequence = 1;
> +		vbl.request.signal = (unsigned long)&handler_info;
> +		ret = drmWaitVBlank(data->drm_fd, &vbl);
> +		if (ret != 0) {
> +			igt_info("ERROR : drmWaitVBlank FAILED :%d\n", ret);
> +			return -1;
> +		}
> +		/*call drmHandleEvent() to handle vblank event */
> +		ret = drmHandleEvent(data->drm_fd, &evctx);

Looks quite complicated. What you want is just

static double vbl_rate(const union drm_wait_vblank *start,
		       const union drm_wait_vblank *end)
{
	double s, e;

	s = start->reply.tval_sec + 1e-6*start->reply.tval_usec;
	e = end->reply.tval_sec + 1e-6*end->reply.tval_usec;

	return (end->reply.sequence - start->reply.sequnce) / (e - s);
}

static int calculate_refresh_rate(int fd, int pipe, int period_ms)
{
	union drm_wait_vblank start, end;
	struct timeval tv;

	memset(&start, 0, sizeof(start));
	start.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe);
	igt_assert(drmWaitVblank(fb, &start) == 0);

	while (igt_milliseconds_elapsed(&tv) < period_ms) {
		memset(&end, 0, sizeof(end));
		end.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe);
		end.sequence = 1;
		igt_assert(drmWaitVblank(fb, &end) == 0);
	}

	return vbl_rate(&start, &end);
}

right?

Call that igt_pipe_measure_vrefresh(int fd, enum pipe pipe, int min_period_ms);

or igt_crtc_measure_vrefresh(with igt_pipe_t instead of enum pipe).

and be done.

> +		if (ret != 0) {
> +			igt_info("ERROR : drmHandleEvent FAILED %d\n", ret);
> +			return -1;
> +		}
> +
> +		gettimeofday(&curr_time, NULL);
> +		time_elapsed = curr_time.tv_sec + curr_time.tv_usec * 1e-6 -
> +			(start_time.tv_sec + start_time.tv_usec * 1e-6);
> +	}
> +	if (!time_elapsed) {
> +		igt_info("ERROR: Incorrect measurement of vblank duration\n");
> +		return -1;
> +	}
> +	vbl_count = handler_info.vbl_count;
> +	/* calculate the refresh rate; rr=vblank_count/time_taken */
> +	refresh_rate = vbl_count / time_elapsed;
> +	return refresh_rate;
> +}
> +
> +/*
> + * worker_thread_func :
> + *	Func which is run by a worker thread to calculate the refresh_rate,
> + *	when signalled by the master thread.
> + */
> +void *worker_thread_func(void *ptr)
> +{
> +	data_t *data = (data_t *) ptr;
> +
> +	while (1) {
> +		int refresh_rate = 0;
> +		/*wait for signal from master*/
> +		pthread_mutex_lock(&rr_mutex);
> +		while (!rr_calc_requested)
> +			pthread_cond_wait(&cv_rr_calc_requested, &rr_mutex);
> +
> +		/* checkpoint for thread termination */
> +		if (thread_flag == RESET) {
> +			pthread_mutex_unlock(&rr_mutex);
> +			pthread_exit(NULL);
> +		}
> +		rr_calc_requested = RESET;
> +		pthread_mutex_unlock(&rr_mutex);
> +		/*calculate refresh_rate*/
> +		refresh_rate = calculate_refresh_rate(data);
> +
> +		/* signal the master */
> +		pthread_mutex_lock(&rr_mutex);
> +		refresh_rate_shared = refresh_rate;
> +		rr_calc_completed = SET;
> +		pthread_mutex_unlock(&rr_mutex);
> +		pthread_cond_signal(&cv_rr_calc_completed);
> +	}
> +	return NULL;
> +}

You are calculating the refresh rate synchronously, so what's the point
of the thread?

You are serialising on this thread

> +static int execute_test(data_t *data)
> +{

Split up this monolith into separate subtests and groups thereof.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre


More information about the Intel-gfx mailing list