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

Nautiyal, Ankit K ankit.k.nautiyal at intel.com
Tue Sep 27 11:19:26 UTC 2016


Hi Chris,

Thanks for the comments.

I will try out the changes you have mentioned for counting vblanks.

For other comments please find the responses inline.


On 9/23/2016 6:48 PM, Chris Wilson wrote:
> 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.

I will try this out and get back to you.

>> +		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

The DRRS status is verified in 2 ways.

One is through debugfs entry which checks the driver state [DRRS_HIGH / DRRS_LOW].

the other is by counting vblanks/sec for approximate refresh-rate.

As soon as we do a flip, the state is at DRRS_HIGH status, and panel displays at higher refresh rate.

The state goes to DRRS_LOW after waiting for 1.2 sec.

In this window of 1.2sec the main thread checks the state through debugfs, while the worker thread

counts the vblanks and calculates the approximate refresh rate in parallel.

The threads are synchronized so that master (after determining the driver state from debugfs),

waits for the worker thread to complete the refresh rate calculation, before proceeding with the test.

>> +static int execute_test(data_t *data)
>> +{
> Split up this monolith into separate subtests and groups thereof.

There are no sub-tests here, as we are flipping and checking the transition from high to low and low to high refresh rate.
Flip [1.2 sec ](HIGH->LOW); Flip (LOW->HIGH); Flip [1.2 sec] (HIGH->LOW). This constitutes a single test.

> -Chris
>


More information about the Intel-gfx mailing list