Beispiel #1
0
Datei: video.c Projekt: Gu1/arcan
void platform_video_timing(float* o_sync, float* o_stddev, float* o_variance)
{
	static float sync, stddev, variance;
	static bool gottiming;

	if (!gottiming){
		platform_video_bufferswap();
	
		int retrycount = 0;

/* 
 * try to get a decent measurement of actual timing, this is not really used for
 * synchronization but rather as a guess of we're actually vsyncing and how 
 * processing should be scheduled in relation to vsync, or if we should yield at
 * appropriate times.
 */

		const int nsamples = 10;
		long long int samples[nsamples], sample_sum;

retry:
		sample_sum = 0;
		for (int i = 0; i < nsamples; i++){
			long long int start = arcan_timemillis();
			platform_video_bufferswap();
			long long int stop = arcan_timemillis();
			samples[i] = stop - start;
			sample_sum += samples[i];
		}

		sync = (float) sample_sum / (float) nsamples;
		variance = 0.0;
		for (int i = 0; i < nsamples; i++){
			variance += powf(sync - (float)samples[i], 2);
		}
		stddev = sqrtf(variance / (float) nsamples);
		if (stddev > 0.5){
			retrycount++;
			if (retrycount > 10)
				arcan_video_display.vsync_timing = 16.667; /* give up and just revert */
			else
				goto retry;
		}
	}

	*o_sync = sync;
	*o_stddev = stddev;
	*o_variance = variance;
}
Beispiel #2
0
int64_t arcan_frametime()
{
	int64_t now = arcan_timemillis();
	if (now < epoch)
		epoch = now - (epoch - now);

	return now - epoch;
}
Beispiel #3
0
void platform_video_synch(uint64_t tick_count, float fract,
	video_synchevent pre, video_synchevent post)
{
	long long start = arcan_timemillis();
	if (pre)
		pre();

	arcan_vobject* vobj = arcan_video_getobject(out_vid);
	if (!vobj){
		out_vid = ARCAN_VIDEO_WORLDID;
		vobj = arcan_video_getobject(ARCAN_VIDEO_WORLDID);
	}

	size_t nd;
	arcan_bench_register_cost( arcan_vint_refresh(fract, &nd) );
	agp_shader_id shid = agp_default_shader(BASIC_2D);

	agp_activate_rendertarget(NULL);
	if (blackframes){
		agp_rendertarget_clear();
		blackframes--;
	}

	if (vobj->program > 0)
		shid = vobj->program;

	agp_activate_vstore(out_vid == ARCAN_VIDEO_WORLDID ?
		arcan_vint_world() : vobj->vstore);

	agp_shader_activate(shid);

	agp_draw_vobj(0, 0, d_width, d_height, txcos, NULL);
	arcan_vint_drawcursor(false);

/*
 * NOTE: heuristic fix-point for direct- mapping dedicated source for
 * low latency here when we fix up internal syncing paths.
 */
	eglSwapBuffers(eglDpy, eglSurface);

/* With dynamic, we run an artificial vsync if the time between swaps
 * become to low. This is a workaround for a driver issue spotted on
 * nvidia and friends from time to time where multiple swaps in short
 * regression in combination with 'only redraw' adds bubbles */
	int delta = arcan_frametime() - last;
	if (delta >= 0 && delta < 8){
		arcan_timesleep(16 - delta);
	}

	last = arcan_frametime();

	if (post)
		post();
}
Beispiel #4
0
void agp_readback_synchronous(struct storage_info_t* dst)
{
	if (!(dst->txmapped == TXSTATE_TEX2D) || !dst->vinf.text.raw)
		return;
	struct agp_fenv* env = agp_env();

	env->bind_texture(GL_TEXTURE_2D, dst->vinf.text.glid);
	env->get_tex_image(GL_TEXTURE_2D, 0,
		GL_PIXEL_FORMAT, GL_UNSIGNED_BYTE, dst->vinf.text.raw);
	dst->update_ts = arcan_timemillis();
	env->bind_texture(GL_TEXTURE_2D, 0);
}
Beispiel #5
0
/* positions and offsets in meta have been verified in _frameserver */
static void pbo_stream_sub(struct storage_info_t* s,
	av_pixel* buf, struct stream_meta* meta, bool synch)
{
	struct agp_fenv* env = agp_env();
	if ( (float)(meta->w * meta->h) / (s->w * s->h) > 0.5)
		return pbo_stream(s, buf, meta, synch);

	agp_activate_vstore(s);
	size_t row_sz = meta->w * sizeof(av_pixel);
	set_pixel_store(s->w, *meta);
	env->tex_subimage_2d(GL_TEXTURE_2D, 0, meta->x1, meta->y1, meta->w, meta->h,
		s->vinf.text.s_fmt ? s->vinf.text.s_fmt : GL_PIXEL_FORMAT,
		GL_UNSIGNED_BYTE, buf
	);
	reset_pixel_store();
	agp_deactivate_vstore(s);

	if (synch){
		av_pixel* cpy = s->vinf.text.raw;
		for (size_t y = meta->y1; y < meta->y1 + meta->h; y++)
			memcpy(&cpy[y * s->w + meta->x1], &buf[y * s->w + meta->x1], row_sz);
		s->update_ts = arcan_timemillis();
	}

/*
 * Currently disabled approach to update subregion using PBO, experienced
 * data corruption / driver bugs on several drivers :'(
 */

#if 0
	av_pixel* ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);

glBindBuffer(GL_PIXEL_UNPACK_BUFFER, s->vinf.text.wid);


/* warning, with the normal copy we check alignment in beforehand as we have
 * had cases where glMapBuffer intermittently returns unaligned pointers AND
 * the compiler has emitted intrinsics that assumed alignment */
	for (size_t y = meta->y1; y < meta->y1 + meta->h; y++)
		memcpy(&ptr[y * s->w + meta->x1], &buf[y * s->w + meta->x1], row_sz);

		glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
	glTexSubImage2D(GL_TEXTURE_2D, 0, meta->x1, meta->y1, meta->w, meta->h,
		GL_PIXEL_FORMAT, GL_UNSIGNED_BYTE, 0);
	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
#endif
}
Beispiel #6
0
void arcan_bench_register_frame()
{
	static long long int lastframe = -1;
	if (benchdata.bench_enabled == false)
		return;

	long long int ftime = arcan_timemillis();
	if (lastframe > 0 && ftime > lastframe){
		unsigned delta = ftime - lastframe;
		benchdata.frametime[(unsigned)benchdata.frameofs] = delta;
		benchdata.framecount++;
		benchdata.frameofs = (benchdata.frameofs + 1) %
			(sizeof(benchdata.frametime) / sizeof(benchdata.frametime[0]));
		}

	lastframe = ftime;
}
Beispiel #7
0
static void pbo_stream(struct storage_info_t* s,
	av_pixel* buf, struct stream_meta* meta, bool synch)
{
	agp_activate_vstore(s);
	struct agp_fenv* env = agp_env();

	env->bind_buffer(GL_PIXEL_UNPACK_BUFFER, s->vinf.text.wid);
	size_t ntc = s->w * s->h;

	av_pixel* ptr = env->map_buffer(GL_PIXEL_UNPACK_BUFFER,GL_WRITE_ONLY);

	if (!ptr)
		return;

	av_pixel* obuf = buf;

/* note, explicitly replace with a simd unaligned version */
	if ( ((uintptr_t)ptr % 16) == 0 && ((uintptr_t)buf % 16) == 0	)
		memcpy(ptr, buf, ntc * sizeof(av_pixel));
	else
		for (size_t i = 0; i < ntc; i++)
			*ptr++ = *buf++;

/* synch :- on-host backing store, one extra copy into local buffer */
	if (synch){
		buf = obuf;
		ptr = s->vinf.text.raw;
		s->update_ts = arcan_timemillis();

		if ( ((uintptr_t)ptr % 16) == 0 && ((uintptr_t)buf % 16) == 0	)
			memcpy(ptr, buf, ntc * sizeof(av_pixel));
		else
			for (size_t i = 0; i < ntc; i++)
				*ptr++ = *buf++;
	}

	env->unmap_buffer(GL_PIXEL_UNPACK_BUFFER);

	env->tex_subimage_2d(GL_TEXTURE_2D, 0, 0, 0, s->w, s->h,
		s->vinf.text.s_fmt ? s->vinf.text.s_fmt : GL_PIXEL_FORMAT,
		GL_UNSIGNED_BYTE, 0
	);

	env->bind_buffer(GL_PIXEL_UNPACK_BUFFER, 0);
	agp_deactivate_vstore();
}
Beispiel #8
0
/*
 * keep the time tracking separate from the other
 * timekeeping parts, discard non-monotonic values
 */
void arcan_bench_register_tick(unsigned nticks)
{
	static long long int lasttick = -1;
	if (benchdata.bench_enabled == false)
		return;

	while (nticks--){
		long long int ftime = arcan_timemillis();
		benchdata.tickcount++;

		if (lasttick > 0 && ftime > lasttick){
			unsigned delta = ftime - lasttick;
			benchdata.ticktime[(unsigned)benchdata.tickofs] = delta;
			benchdata.tickofs = (benchdata.tickofs + 1) %
				(sizeof(benchdata.ticktime) / sizeof(benchdata.ticktime[0]));
		}

		lasttick = ftime;
	}
}
Beispiel #9
0
void agp_update_vstore(struct storage_info_t* s, bool copy)
{
	struct agp_fenv* env = agp_env();
	if (s->txmapped == TXSTATE_OFF)
		return;

	FLAG_DIRTY();

	if (!copy)
		env->bind_texture(GL_TEXTURE_2D, s->vinf.text.glid);
	else{
		if (GL_NONE == s->vinf.text.glid)
			env->gen_textures(1, &s->vinf.text.glid);

/* for the launch_resume and resize states, were we'd push a new
 * update	but have multiple references */
		if (s->refcount == 0)
			s->refcount = 1;

		env->bind_texture(GL_TEXTURE_2D, s->vinf.text.glid);
	}

	env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
		s->txu == ARCAN_VTEX_REPEAT ? GL_REPEAT : GL_CLAMP_TO_EDGE);
	env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
		s->txv == ARCAN_VTEX_REPEAT ? GL_REPEAT : GL_CLAMP_TO_EDGE);

	int filtermode = s->filtermode & (~ARCAN_VFILTER_MIPMAP);
	bool mipmap = s->filtermode & ARCAN_VFILTER_MIPMAP;

/*
 * Mipmapping still misses the option to manually define mipmap levels
 */
	if (copy){
#ifndef GL_GENERATE_MIPMAP
		if (mipmap)
			env->generate_mipmap(GL_TEXTURE_2D);
#else
			env->tex_param_i(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, mipmap);
#endif
	}

	switch (filtermode){
	case ARCAN_VFILTER_NONE:
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	break;

	case ARCAN_VFILTER_LINEAR:
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	break;

	case ARCAN_VFILTER_BILINEAR:
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	break;

	case ARCAN_VFILTER_TRILINEAR:
		if (mipmap){
			env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
				GL_LINEAR_MIPMAP_LINEAR);
			env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		}
		else {
			env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		}
	break;
	}

	if (copy){
		env->pixel_storei(GL_UNPACK_ROW_LENGTH, 0);
		s->update_ts = arcan_timemillis();
		if (s->txmapped == TXSTATE_DEPTH)
			env->tex_image_2d(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, s->w, s->h, 0,
				GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
		else
			env->tex_image_2d(GL_TEXTURE_2D, 0,
				s->vinf.text.d_fmt ? s->vinf.text.d_fmt : GL_STORE_PIXEL_FORMAT,
				s->w, s->h, 0,
				s->vinf.text.s_fmt ? s->vinf.text.s_fmt : GL_PIXEL_FORMAT,
				GL_UNSIGNED_BYTE, s->vinf.text.raw
			);
	}

#ifndef HEADLESS_NOARCAN
	if (arcan_video_display.conservative){
		arcan_mem_free(s->vinf.text.raw);
		s->vinf.text.raw = NULL;
		s->vinf.text.s_raw = 0;
	}
#endif

	env->bind_texture(GL_TEXTURE_2D, 0);
}
Beispiel #10
0
int main(int argc, char* argv[])
{
	bool windowed     = false;
	bool fullscreen   = false;
	bool conservative = false;
	bool nosound      = false;
	bool in_monitor   = getenv("ARCAN_MONITOR_FD") != NULL;
	bool waitsleep    = true;

	unsigned char debuglevel = 0;

	int scalemode = ARCAN_VIMAGE_NOPOW2;
	int width     = 640;
	int height    = 480;
	int winx      = -1;
	int winy      = -1;
	int timedump  = 0;
	float vfalign = 0.6;
	
/* only used when monitor mode is activated, where we want some 
 * of the global paths etc. accessible, but not *all* of them */
	FILE* monitor_outf    = NULL;
	int monitor           = 0;
	int monitor_infd      = -1;
	char* monitor_arg     = "LOG";

	char* dbfname = NULL;
	int ch;
	
	srand( time(0) );
/* VIDs all have a randomized base to provoke crashes in poorly written scripts,
 * only -g will make their base and sequence repeatable */

	while ((ch = getopt_long(argc, argv, "w:h:x:y:?fvVmisp:q:"
		"t:M:O:o:l:a:d:F:1:2:gr:S", longopts, NULL)) >= 0){
	switch (ch) {
	case '?' :
		usage();
		exit(1);
	break;
	case 'w' : width = strtol(optarg, NULL, 10); break;
	case 'h' : height = strtol(optarg, NULL, 10); break;
	case 'm' : conservative = true; break;
	case 'x' : winx = strtol(optarg, NULL, 10); break;
	case 'y' : winy = strtol(optarg, NULL, 10); break;
	case 'F' : vfalign = strtof(optarg, NULL); break;
	case 'f' : fullscreen = true; break;
	case 's' : windowed = true; break;
	case 'l' : arcan_libpath = strdup(optarg); break;
	case 'd' : dbfname = strdup(optarg); break;
	case 'S' : nosound = true; break;
	case 'q' : timedump = strtol(optarg, NULL, 10); break;
	case 'a' : arcan_video_display.msasamples = strtol(optarg, NULL, 10); break;
	case 'v' : arcan_video_display.vsync = false; break;
	case 'V' : waitsleep = false; break;
	case 'p' : arcan_resourcepath = strdup(optarg); break;
#ifndef _WIN32
	case 'M' : monitor = abs( strtol(optarg, NULL, 10) ); break;
	case 'O' : monitor_arg = strdup( optarg ); break; 
#endif
	case 't' : arcan_themepath = strdup(optarg); break;
	case 'o' : arcan_binpath = strdup(optarg); break;
	case 'g' :
		debuglevel++;
		srand(0xdeadbeef);
		break;
	case 'r' :
		scalemode = strtol(optarg, NULL, 10);
		printf("scalemode: %d\n", scalemode);
		if (scalemode != ARCAN_VIMAGE_NOPOW2 && scalemode != 
			ARCAN_VIMAGE_SCALEPOW2 && scalemode){
			arcan_warning("Warning: main(), -r, invalid scalemode. Ignoring.\n");
			scalemode = ARCAN_VIMAGE_SCALEPOW2;
		}
	break;
	case '1' :
		stdout_redirected = true;
		if (freopen(optarg, "a", stdout) == NULL);
		break;
	case '2' :
		stderr_redirected = true;
		if (freopen(optarg, "a", stderr) == NULL);
		break;

	default:
		break;
	}
	}

	if (arcan_setpaths() == false)
		goto error;

	if (check_theme(*(argv + optind)))
		arcan_themename = *(argv + optind);
	else if (check_theme("welcome"))
		arcan_themename = "welcome";
	else
		arcan_fatal("No theme found.\n");

	if (strcmp(arcan_themename, "arcan") == 0){
		arcan_fatal("Theme name 'arcan' is reserved\n");
	}

	if (vfalign > 1.0 || vfalign < 0.0){
		arcan_warning("Argument Error (-F, --vsync-falign): "
		"bad range specified (%f), reverting to default (0.6)\n", vfalign);
		vfalign = 0.6;
	}

#ifndef _WIN32
/* pipe to file, socket or launch script based on monitor output,
 * format will be LUA tables with the exception of each cell ending with
 * #ENDSAMPLE . The block will be sampled, parsed and should return a table
 * pushed through the sample() function in the LUA space */
	if (in_monitor){
		monitor_infd = strtol( getenv("ARCAN_MONITOR_FD"), NULL, 10);
	 	signal(SIGPIPE, SIG_IGN);	
	}
	else if (monitor > 0){
		extern arcan_benchdata benchdata;
		benchdata.bench_enabled = true;

		if (strncmp(monitor_arg, "LOG:", 4) == 0){
			monitor_outf = fopen(&monitor_arg[4], "w+");	
			if (NULL == monitor_outf)
				arcan_fatal("couldn't open log output (%s) for writing\n", monitor_arg[4]);
			fcntl(fileno(monitor_outf), F_SETFD, FD_CLOEXEC);
		}
		else {
			int pair[2];

			pid_t p1;
			if (pipe(pair) == 0)
				;

			if ( (p1 = fork()) == 0){
				close(pair[1]);

/* double-fork to get away from parent */
				if (fork() != 0) 
					exit(0); 

/* 
 * set the descriptor of the inherited pipe as an envvariable,
 * this will have the program be launched with in_monitor set to true
 * the monitor args will then be ignored and themename replaced with 
 * the monitorarg 
 */
				char monfd_buf[8] = {0};
				snprintf(monfd_buf, 8, "%d", pair[0]);
				setenv("ARCAN_MONITOR_FD", monfd_buf, 1);
				argv[optind] = strdup(monitor_arg);	

				execv(argv[0], argv);
				exit(1);
			}
			else {
/* don't terminate just because the pipe gets broken (i.e. dead monitor) */
				close(pair[0]);
				monitor_outf = fdopen(pair[1], "w");
			 	signal(SIGPIPE, SIG_IGN);	
			}
		}
		
		fullscreen = false;
	}
#endif

/* also used as restart point for switiching themes */
themeswitch:

/*
 * try to open the specified database,
 * if that fails, warn, try to create an empty 
 * database and if that fails, give up. 
 */
	if (!dbfname)
		dbfname = arcan_expand_resource("arcandb.sqlite", true);

	dbhandle = arcan_db_open(dbfname, arcan_themename);

	if (!dbhandle) {
		arcan_warning("Couldn't open database (requested: %s),"
			"trying to create a new one.\n", dbfname);
		FILE* fpek = fopen(dbfname, "a");
		if (fpek){
			fclose(fpek);
			dbhandle = arcan_db_open(dbfname, arcan_themename);
		}

		if (!dbhandle){
			arcan_warning("Couldn't create database, giving up.\n");
			goto error;
		}
	}
	
	arcan_video_default_scalemode(scalemode);

	if (winx != -1 || winy != -1){
		char windbuf[64] = {0};
		snprintf(windbuf, 63, "SDL_VIDEO_WINDOW_POS=%i,%i", winx >= 0 ?
			winx : 0, winy >= 0 ? winy : 0);
		putenv(strdup(windbuf));
	}

	if (windowed)
		fullscreen = false;

/* grab video, (necessary) */
	if (arcan_video_init(width, height, 32, fullscreen, windowed, conservative)
		!= ARCAN_OK) {
		arcan_fatal("Error; Couldn't initialize video system,"
			"try other windowing options (-f, -w, ...)\n");
	}
	
	errno = 0;
/* grab audio, (possible to live without) */
	if (ARCAN_OK != arcan_audio_setup(nosound))
		arcan_warning("Warning: No audio devices could be found.\n");

	arcan_math_init();

/* setup device polling, cleanup, ... */
	arcan_evctx* def = arcan_event_defaultctx();
	arcan_event_init( def );

#ifdef ARCAN_LED
	arcan_led_init();
#endif

#ifdef ARCAN_HMD
	arcan_hmd_setup();
#endif

/*
 * MINGW implements putenv, so use this to set
 * the system subpath path (BIOS, ..) 
 */
	if (getenv("ARCAN_SYSTEMPATH") == NULL){
		size_t len = strlen(arcan_resourcepath) + sizeof("/games/system") + 
			sizeof("ARCAN_SYSTEMPATH=") + 1;

		char* const syspath = malloc(len);
		sprintf(syspath, "ARCAN_SYSTEMPATH=%s/games/system", arcan_resourcepath);
		arcan_warning("Notice: Using default systempath (%s)\n", syspath);
		putenv(syspath);
	} else
		arcan_warning("Notice: Using systempath from environment (%s)\n", 
			getenv("ARCAN_SYSTEMPATH"));

	if (getenv("ARCAN_FRAMESERVER_LOGDIR") == NULL){
		size_t len = strlen(arcan_resourcepath) + sizeof("/logs") + 
			sizeof("ARCAN_FRAMESERVER_LOGDIR=/logs");

		char* const logpath = malloc(len);
		sprintf(logpath, "ARCAN_FRAMESERVER_LOGDIR=%s/logs", arcan_resourcepath);
		putenv(logpath);
	}

#ifndef _WIN32
	struct rlimit coresize = {0};

/* debuglevel 0, no coredumps etc.
 * debuglevel 1, maximum 1M coredump, should prioritize stack etc.
 * would want to be able to hint to mmap which pages that should be avoided
 * so that we could exclude texture data that both comprise most memory used
 * and can be recovered through other means. One option for this would be
 * to push image loading/management to a separate process, 
 * debuglevel > 1, dump everything */
	if (debuglevel == 0);
	else if (debuglevel == 1) coresize.rlim_max = 10 * 1024 * 1024;
	else coresize.rlim_max = RLIM_INFINITY;

	coresize.rlim_cur = coresize.rlim_max;
	setrlimit(RLIMIT_CORE, &coresize);
	system_page_size = sysconf(_SC_PAGE_SIZE);
#endif


/* setup VM, map arguments and possible overrides */ 
	struct arcan_luactx* luactx = arcan_lua_alloc();
	arcan_lua_mapfunctions(luactx, debuglevel);

	char* themescr = (char*) malloc(strlen(arcan_themename) + 5);
	sprintf(themescr, "%s.lua", arcan_themename);
	char* fn = arcan_find_resource(themescr, ARCAN_RESOURCE_THEME);

	char* msg = arcan_luaL_dofile(luactx, fn);
	if (msg != NULL){
		arcan_fatal("Fatal: main(), Error loading theme script"
			"(%s) : (%s)\n", themescr, msg);
		free(msg);
		goto error;
	}

	free(fn);
	free(themescr);

/* entry point follows the name of the theme,
 * hand over execution and begin event loop */
	if (argc > optind)
		arcan_lua_pushargv(luactx, argv + optind + 1);

	arcan_lua_callvoidfun(luactx, "", true);
	arcan_lua_callvoidfun(luactx, "show", false);

	bool done = false, framepulse = true, feedtrig = true;
	float lastfrag = 0.0f;
	long long int lastflip = arcan_timemillis();
	int monitor_counter = monitor;
	
	arcan_event ev;
	arcan_evctx* evctx = arcan_event_defaultctx();

	while (!done) {
/* pollfeed can actually populate event-loops, assuming we don't exceed a 
 * compile- time threshold */
#ifdef ARCAN_HMD
		arcan_hmd_update();
#endif
		if (feedtrig){
			feedtrig = false;
			arcan_video_pollfeed();
		}

/* NOTE: might be better if this terminates if we're closing in on a 
 * deadline as to not be saturated with an onslaught of I/O events. */
		while (1 == arcan_event_poll(evctx, &ev)){ 

/*
 * these events can typically be determined in video_tick(),
 * however there are so many hierarchical dependencies 
 * (linked objs, instances, ...)
 * that a full delete is not really safe there (e.g. event -> callback -> 
 */
			switch (ev.category){
			case EVENT_VIDEO:
				if (ev.kind == EVENT_VIDEO_EXPIRE)
					arcan_video_deleteobject(ev.data.video.source);
			break;

			case EVENT_SYSTEM:
/* note the LUA shutdown() call actually emits this event */
				if (ev.kind == EVENT_SYSTEM_EXIT)
					done = true;
				else if (ev.kind == EVENT_SYSTEM_SWITCHTHEME){
					arcan_luaL_shutdown(luactx);
					arcan_video_shutdown();
					arcan_audio_shutdown();
					arcan_event_deinit(arcan_event_defaultctx());
					arcan_db_close(dbhandle);

					arcan_themename = strdup(ev.data.system.data.message);
					goto themeswitch;
				}
				else 
					continue;
			break;
			}

			arcan_lua_pushevent(luactx, &ev);
		}

		unsigned nticks;
		float frag = arcan_event_process(arcan_event_defaultctx(), &nticks);

		if (debuglevel == 4)
			arcan_warning("main() event_process (%d, %f)\n", nticks, frag);

/* priority is always in maintaining logical clock and event processing */
		if (nticks > 0){
			unsigned njobs;

			arcan_video_tick(nticks, &njobs);
			arcan_bench_register_tick(nticks);

			arcan_audio_tick(nticks);
			lastfrag = 0.0;
				
			if (monitor && !in_monitor){
				if (--monitor_counter == 0){
					static int mc;
					char buf[8];
					snprintf(buf, 8, "%d", mc++);
					monitor_counter = monitor;
					arcan_lua_statesnap(monitor_outf, buf, true);
				}
			}

/* debugging functionality to generate a dump and abort after n ticks */
			if (timedump){
				timedump -= nticks; 

				if (timedump <= 0){
					arcan_state_dump("timedump", "user requested a dump", __func__);
					break;
				}
			}	
		}

/* this is internally buffering and non-blocking, hence the fd use compared
 * to arcan_lua_statesnap above */
#ifndef _WIN32
		if (in_monitor)
			arcan_lua_stategrab(luactx, "sample", monitor_infd);
#endif
	
/*
 * difficult decision, should we flip or not?
 * a full- redraw can be costly, so should only really be done if 
 * enough things have changed or if we're closing in on the next 
 * deadline for the unknown video clock, this also depends on if 
 * the user favors energy saving (waitsleep) or responsiveness. 
 */
		const int min_respthresh = 9;

/* only render if there's enough relevant changes */
		if (!waitsleep || nticks > 0 || frag - lastfrag > INTERP_MINSTEP){

/* separate between cheap (possibly vsync off or triple buffering) 
 * flip cost and expensive (vsync on) */
			if (arcan_video_display.vsync_timing < 8.0){
				unsigned cost = arcan_video_refresh(frag, true);
				feedtrig = true;

				arcan_bench_register_cost(cost);
				arcan_bench_register_frame();
					if (framepulse)
						framepulse = arcan_lua_callvoidfun(luactx, "frame_pulse", false);

				int delta = arcan_timemillis() - lastflip;
				lastflip += delta;
	
				if (waitsleep && delta < min_respthresh)
					arcan_timesleep(min_respthresh - delta);
			}
			else {
				int delta = arcan_timemillis() - lastflip;
				if (delta >= (float)arcan_video_display.vsync_timing * vfalign){
					unsigned cost = arcan_video_refresh(frag, true);
					feedtrig = true;

					arcan_bench_register_cost(cost);
					arcan_bench_register_frame();
					if (framepulse)
						framepulse = arcan_lua_callvoidfun(luactx, "frame_pulse", false);

					lastflip += delta;
				} 
			}
		}

		arcan_audio_refresh();
	}

	arcan_lua_callvoidfun(luactx, "shutdown", false);

#ifdef ARCAN_LED
	arcan_led_shutdown();
#endif

#ifdef ARCAN_HMD
	arcan_hmd_shutdown();
#endif

	arcan_video_shutdown();

error:
	exit(1);

	return 0;
}
Beispiel #11
0
void agp_update_vstore(struct storage_info_t* s, bool copy)
{
	if (s->txmapped == TXSTATE_OFF)
		return;

	FLAG_DIRTY();

	if (!copy)
		glBindTexture(GL_TEXTURE_2D, s->vinf.text.glid);
	else{
		glGenTextures(1, &s->vinf.text.glid);

/* for the launch_resume and resize states, were we'd push a new
 * update	but have multiple references */
		if (s->refcount == 0)
			s->refcount = 1;

		glBindTexture(GL_TEXTURE_2D, s->vinf.text.glid);
	}

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, s->txu == ARCAN_VTEX_REPEAT ?
		GL_REPEAT : GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, s->txv == ARCAN_VTEX_REPEAT ?
		GL_REPEAT : GL_CLAMP_TO_EDGE);

	int filtermode = s->filtermode & (~ARCAN_VFILTER_MIPMAP);
	bool mipmap = s->filtermode & ARCAN_VFILTER_MIPMAP;

/*
 * Mipmapping still misses the option to manually define mipmap levels
 */
	if (copy){
#ifndef GL_GENERATE_MIPMAP
		if (mipmap)
			glGenerateMipmap(GL_TEXTURE_2D);
#else
			glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, mipmap);
#endif
	}

	switch (filtermode){
	case ARCAN_VFILTER_NONE:
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	break;

	case ARCAN_VFILTER_LINEAR:
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	break;

	case ARCAN_VFILTER_BILINEAR:
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	break;

	case ARCAN_VFILTER_TRILINEAR:
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	break;
	}

	if (copy){
		s->update_ts = arcan_timemillis();
		if (s->txmapped == TXSTATE_DEPTH)
			glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, s->w, s->h, 0,
				GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
		else
			glTexImage2D(GL_TEXTURE_2D, 0, GL_PIXEL_FORMAT, s->w, s->h,
				0, GL_PIXEL_FORMAT, GL_UNSIGNED_BYTE, s->vinf.text.raw);
	}

#ifndef HEADLESS_NOARCAN
	if (arcan_video_display.conservative){
		arcan_mem_free(s->vinf.text.raw);
		s->vinf.text.raw = NULL;
		s->vinf.text.s_raw = 0;
	}
#endif

	glBindTexture(GL_TEXTURE_2D, 0);
}