1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444 |
- /* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see <http://www.gnu.org/licenses/>.
- */
- #include <stdlib.h>
- #include <string.h>
- #include <limits.h>
- #include <math.h>
- #include <compat/strl.h>
- #include <features/features_cpu.h>
- #include <string/stdstring.h>
- #include "video_driver.h"
- #include "video_thread_wrapper.h"
- #ifdef HAVE_FONT
- #include "font_driver.h"
- #endif
- #include "../retroarch.h"
- #include "../runloop.h"
- #include "../verbosity.h"
- static void *video_thread_init_never_call(const video_info_t *video,
- input_driver_t **input, void **input_data)
- {
- (void)video;
- (void)input;
- (void)input_data;
- RARCH_ERR("Sanity check fail! Threaded mustn't be reinit.\n");
- abort();
- return NULL;
- }
- /* thread -> user */
- static void video_thread_reply(thread_video_t *thr, const thread_packet_t *pkt)
- {
- slock_lock(thr->lock);
- thr->cmd_data = *pkt;
- thr->reply_cmd = pkt->type;
- thr->send_cmd = CMD_VIDEO_NONE;
- scond_signal(thr->cond_cmd);
- slock_unlock(thr->lock);
- }
- /* user -> thread */
- static void video_thread_send_packet(thread_video_t *thr,
- const thread_packet_t *pkt)
- {
- slock_lock(thr->lock);
- thr->cmd_data = *pkt;
- thr->send_cmd = pkt->type;
- thr->reply_cmd = CMD_VIDEO_NONE;
- scond_signal(thr->cond_thread);
- slock_unlock(thr->lock);
- }
- /* user -> thread */
- static void video_thread_wait_reply(thread_video_t *thr, thread_packet_t *pkt)
- {
- slock_lock(thr->lock);
- while (pkt->type != thr->reply_cmd)
- scond_wait(thr->cond_cmd, thr->lock);
- *pkt = thr->cmd_data;
- thr->cmd_data.type = CMD_VIDEO_NONE;
- slock_unlock(thr->lock);
- }
- /* user -> thread */
- static void video_thread_send_and_wait_user_to_thread(thread_video_t *thr, thread_packet_t *pkt)
- {
- video_thread_send_packet(thr, pkt);
- video_thread_wait_reply(thr, pkt);
- }
- static void thread_update_driver_state(thread_video_t *thr)
- {
- #ifdef HAVE_MENU
- if (thr->texture.frame_updated)
- {
- if (thr->driver_data && thr->poke && thr->poke->set_texture_frame)
- thr->poke->set_texture_frame(thr->driver_data,
- thr->texture.frame, thr->texture.rgb32,
- thr->texture.width, thr->texture.height,
- thr->texture.alpha);
- thr->texture.frame_updated = false;
- }
- if (thr->driver_data && thr->poke && thr->poke->set_texture_enable)
- thr->poke->set_texture_enable(thr->driver_data,
- thr->texture.enable, thr->texture.full_screen);
- #endif
- #ifdef HAVE_OVERLAY
- slock_lock(thr->alpha_lock);
- if (thr->alpha_update)
- {
- if (thr->driver_data && thr->overlay && thr->overlay->set_alpha)
- {
- int i;
- for (i = 0; i < (int)thr->alpha_mods; i++)
- thr->overlay->set_alpha(thr->driver_data, i, thr->alpha_mod[i]);
- }
- thr->alpha_update = false;
- }
- slock_unlock(thr->alpha_lock);
- #endif
- if (thr->apply_state_changes)
- {
- if (thr->driver_data && thr->poke && thr->poke->apply_state_changes)
- thr->poke->apply_state_changes(thr->driver_data);
- thr->apply_state_changes = false;
- }
- }
- /* returns true when video_thread_loop should quit */
- static bool video_thread_handle_packet(
- thread_video_t *thr,
- const thread_packet_t *incoming)
- {
- thread_packet_t pkt = *incoming;
- switch (pkt.type)
- {
- case CMD_INIT:
- if (thr->driver && thr->driver->init)
- {
- thr->driver_data = thr->driver->init(&thr->info,
- thr->input, thr->input_data);
- if (thr->driver_data && thr->driver->viewport_info)
- thr->driver->viewport_info(thr->driver_data, &thr->vp);
- }
- else
- thr->driver_data = NULL;
- pkt.data.b = (thr->driver_data != NULL);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_FREE:
- if (thr->driver_data && thr->driver && thr->driver->free)
- thr->driver->free(thr->driver_data);
- thr->driver_data = NULL;
- video_thread_reply(thr, &pkt);
- return true;
- case CMD_SET_ROTATION:
- if (thr->driver_data && thr->driver && thr->driver->set_rotation)
- thr->driver->set_rotation(thr->driver_data, pkt.data.i);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_READ_VIEWPORT:
- if (thr->driver_data && thr->driver &&
- thr->driver->viewport_info && thr->driver->read_viewport)
- {
- struct video_viewport vp;
- vp.x = 0;
- vp.y = 0;
- vp.width = 0;
- vp.height = 0;
- vp.full_width = 0;
- vp.full_height = 0;
- thr->driver->viewport_info(thr->driver_data, &vp);
- if (!memcmp(&vp, &thr->read_vp, sizeof(vp)))
- {
- /* We can read safely
- *
- * read_viewport() in GL driver calls
- * 'cached frame render' to be able to read from
- * back buffer.
- *
- * This means frame() callback in threaded wrapper will
- * be called from this thread, causing a timeout, and
- * no frame to be rendered.
- *
- * To avoid this, set a flag so wrapper can see if
- * it's called in this "special" way. */
- thr->frame.within_thread = true;
- pkt.data.b = thr->driver->read_viewport(thr->driver_data,
- (uint8_t*)pkt.data.v, thr->is_idle);
- thr->frame.within_thread = false;
- }
- else
- {
- /* Viewport dimensions changed right after main
- * thread read the async value. Cannot read safely. */
- pkt.data.b = false;
- }
- }
- else
- pkt.data.b = false;
- video_thread_reply(thr, &pkt);
- break;
- case CMD_SET_SHADER:
- if (thr->driver_data && thr->driver && thr->driver->set_shader)
- pkt.data.b = thr->driver->set_shader(thr->driver_data,
- pkt.data.set_shader.type, pkt.data.set_shader.path);
- else
- pkt.data.b = false;
- video_thread_reply(thr, &pkt);
- break;
- case CMD_ALIVE:
- if (thr->driver_data && thr->driver && thr->driver->alive)
- pkt.data.b = thr->driver->alive(thr->driver_data);
- else
- pkt.data.b = false;
- video_thread_reply(thr, &pkt);
- break;
- #ifdef HAVE_OVERLAY
- case CMD_OVERLAY_ENABLE:
- if (thr->driver_data && thr->overlay && thr->overlay->enable)
- thr->overlay->enable(thr->driver_data, pkt.data.b);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_OVERLAY_LOAD:
- {
- unsigned tmp_alpha_mods = pkt.data.image.num;
- if (thr->driver_data && thr->overlay && thr->overlay->load)
- pkt.data.b = thr->overlay->load(thr->driver_data,
- pkt.data.image.data, pkt.data.image.num);
- else
- pkt.data.b = false;
- if (tmp_alpha_mods > 0)
- {
- float *tmp_alpha_mod = (float*)realloc(thr->alpha_mod,
- tmp_alpha_mods * sizeof(float));
- if (tmp_alpha_mod)
- {
- /* Avoid temporary garbage data. */
- int i;
- for (i = 0; i < (int)tmp_alpha_mods; i++)
- tmp_alpha_mod[i] = 1.0f;
- thr->alpha_mods = tmp_alpha_mods;
- thr->alpha_mod = tmp_alpha_mod;
- }
- }
- else
- {
- free(thr->alpha_mod);
- thr->alpha_mods = 0;
- thr->alpha_mod = NULL;
- }
- }
- video_thread_reply(thr, &pkt);
- break;
- case CMD_OVERLAY_TEX_GEOM:
- if (thr->driver_data && thr->overlay && thr->overlay->tex_geom)
- thr->overlay->tex_geom(thr->driver_data,
- pkt.data.rect.index,
- pkt.data.rect.x,
- pkt.data.rect.y,
- pkt.data.rect.w,
- pkt.data.rect.h);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_OVERLAY_VERTEX_GEOM:
- if (thr->driver_data && thr->overlay && thr->overlay->vertex_geom)
- thr->overlay->vertex_geom(thr->driver_data,
- pkt.data.rect.index,
- pkt.data.rect.x,
- pkt.data.rect.y,
- pkt.data.rect.w,
- pkt.data.rect.h);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_OVERLAY_FULL_SCREEN:
- if (thr->driver_data && thr->overlay && thr->overlay->full_screen)
- thr->overlay->full_screen(thr->driver_data, pkt.data.b);
- video_thread_reply(thr, &pkt);
- break;
- #endif
- case CMD_POKE_SET_VIDEO_MODE:
- if (thr->driver_data && thr->poke && thr->poke->set_video_mode)
- thr->poke->set_video_mode(thr->driver_data,
- pkt.data.new_mode.width,
- pkt.data.new_mode.height,
- pkt.data.new_mode.fullscreen);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_POKE_SET_FILTERING:
- if (thr->driver_data && thr->poke && thr->poke->set_filtering)
- thr->poke->set_filtering(thr->driver_data,
- pkt.data.filtering.index,
- pkt.data.filtering.smooth,
- pkt.data.filtering.ctx_scaling);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_POKE_SET_ASPECT_RATIO:
- if (thr->driver_data && thr->poke && thr->poke->set_aspect_ratio)
- thr->poke->set_aspect_ratio(thr->driver_data, pkt.data.i);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_FONT_INIT:
- if (pkt.data.font_init.method)
- pkt.data.font_init.return_value = pkt.data.font_init.method(
- pkt.data.font_init.font_driver,
- pkt.data.font_init.font_handle,
- pkt.data.font_init.video_data,
- pkt.data.font_init.font_path,
- pkt.data.font_init.font_size,
- pkt.data.font_init.api,
- pkt.data.font_init.is_threaded
- );
- video_thread_reply(thr, &pkt);
- break;
- case CMD_CUSTOM_COMMAND:
- if (pkt.data.custom_command.method)
- pkt.data.custom_command.return_value =
- pkt.data.custom_command.method(pkt.data.custom_command.data);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_POKE_SHOW_MOUSE:
- if (thr->driver_data && thr->poke && thr->poke->show_mouse)
- thr->poke->show_mouse(thr->driver_data, pkt.data.b);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_POKE_GRAB_MOUSE_TOGGLE:
- if (thr->driver_data && thr->poke && thr->poke->grab_mouse_toggle)
- thr->poke->grab_mouse_toggle(thr->driver_data);
- video_thread_reply(thr, &pkt);
- break;
- case CMD_VIDEO_NONE:
- /* Never reply on no command. Possible deadlock if
- * thread sends command right after frame update. */
- break;
-
- case CMD_POKE_SET_HDR_MAX_NITS:
- if (thr->driver_data && thr->poke && thr->poke->set_hdr_max_nits)
- thr->poke->set_hdr_max_nits(
- thr->driver_data,
- pkt.data.hdr.max_nits
- );
- video_thread_reply(thr, &pkt);
- break;
-
- case CMD_POKE_SET_HDR_PAPER_WHITE_NITS:
- if (thr->driver_data &&
- thr->poke && thr->poke->set_hdr_paper_white_nits)
- thr->poke->set_hdr_paper_white_nits(
- thr->driver_data,
- pkt.data.hdr.paper_white_nits
- );
- video_thread_reply(thr, &pkt);
- break;
-
- case CMD_POKE_SET_HDR_CONTRAST:
- if (thr->driver_data && thr->poke && thr->poke->set_hdr_contrast)
- thr->poke->set_hdr_contrast(
- thr->driver_data,
- pkt.data.hdr.contrast
- );
- video_thread_reply(thr, &pkt);
- break;
-
- case CMD_POKE_SET_HDR_EXPAND_GAMUT:
- if (thr->driver_data && thr->poke && thr->poke->set_hdr_expand_gamut)
- thr->poke->set_hdr_expand_gamut(
- thr->driver_data,
- pkt.data.hdr.expand_gamut
- );
- video_thread_reply(thr, &pkt);
- break;
- default:
- video_thread_reply(thr, &pkt);
- break;
- }
- return false;
- }
- static void video_thread_loop(void *data)
- {
- thread_packet_t pkt;
- bool updated;
- thread_video_t *thr = (thread_video_t*)data;
- for (;;)
- {
- slock_lock(thr->lock);
- while (thr->send_cmd == CMD_VIDEO_NONE && !thr->frame.updated)
- scond_wait(thr->cond_thread, thr->lock);
- updated = thr->frame.updated;
- /* To avoid race condition where send_cmd is updated
- * right after the switch is checked. */
- pkt = thr->cmd_data;
- slock_unlock(thr->lock);
- if (video_thread_handle_packet(thr, &pkt))
- return;
- if (updated)
- {
- struct video_viewport vp;
- bool alive = false;
- bool focus = false;
- bool has_windowed = false;
- vp.x = 0;
- vp.y = 0;
- vp.width = 0;
- vp.height = 0;
- vp.full_width = 0;
- vp.full_height = 0;
- slock_lock(thr->frame.lock);
- thread_update_driver_state(thr);
- if (thr->driver_data && thr->driver)
- {
- if (thr->driver->frame)
- {
- video_frame_info_t video_info;
- bool ret;
- /* TODO/FIXME - not thread-safe - should get
- * rid of this */
- video_driver_build_info(&video_info);
- ret = thr->driver->frame(thr->driver_data,
- thr->frame.buffer, thr->frame.width, thr->frame.height,
- thr->frame.count, thr->frame.pitch,
- *thr->frame.msg ? thr->frame.msg : NULL,
- &video_info);
- slock_unlock(thr->frame.lock);
- if (ret)
- {
- if (thr->driver->alive)
- alive = thr->driver->alive(thr->driver_data);
- if (thr->driver->focus)
- focus = thr->driver->focus(thr->driver_data);
- if (thr->driver->has_windowed)
- has_windowed = thr->driver->has_windowed(thr->driver_data);
- }
- }
- else
- slock_unlock(thr->frame.lock);
- if (thr->driver->viewport_info)
- thr->driver->viewport_info(thr->driver_data, &vp);
- }
- else
- slock_unlock(thr->frame.lock);
- slock_lock(thr->lock);
- thr->alive = alive;
- thr->focus = focus;
- thr->has_windowed = has_windowed;
- thr->vp = vp;
- thr->frame.updated = false;
- scond_signal(thr->cond_cmd);
- slock_unlock(thr->lock);
- }
- }
- }
- static bool video_thread_alive(void *data)
- {
- bool ret;
- uint32_t runloop_flags;
- thread_video_t *thr = (thread_video_t*)data;
- if (!thr)
- return false;
-
- runloop_flags = runloop_get_flags();
- if (runloop_flags & RUNLOOP_FLAG_PAUSED)
- {
- thread_packet_t pkt;
- pkt.type = CMD_ALIVE;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- return pkt.data.b;
- }
- slock_lock(thr->lock);
- ret = thr->alive;
- slock_unlock(thr->lock);
- return ret;
- }
- static bool video_thread_focus(void *data)
- {
- bool ret;
- thread_video_t *thr = (thread_video_t*)data;
- if (!thr)
- return false;
- slock_lock(thr->lock);
- ret = thr->focus;
- slock_unlock(thr->lock);
- return ret;
- }
- static bool video_thread_suppress_screensaver(void *data, bool enable)
- {
- bool ret;
- thread_video_t *thr = (thread_video_t*)data;
- if (!thr)
- return false;
- slock_lock(thr->lock);
- ret = thr->suppress_screensaver;
- slock_unlock(thr->lock);
- return ret;
- }
- static bool video_thread_has_windowed(void *data)
- {
- bool ret;
- thread_video_t *thr = (thread_video_t*)data;
- if (!thr)
- return false;
- slock_lock(thr->lock);
- ret = thr->has_windowed;
- slock_unlock(thr->lock);
- return ret;
- }
- static bool video_thread_frame(void *data, const void *frame_,
- unsigned width, unsigned height, uint64_t frame_count,
- unsigned pitch, const char *msg, video_frame_info_t *video_info)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (!thr)
- return false;
- /* If called from within read_viewport, we're actually in the
- * driver thread, so just render directly. */
- if (thr->frame.within_thread)
- {
- thread_update_driver_state(thr);
- if (thr->driver_data && thr->driver && thr->driver->frame)
- return thr->driver->frame(thr->driver_data, frame_,
- width, height, frame_count, pitch, msg, video_info);
- return false;
- }
- slock_lock(thr->lock);
- if (!thr->nonblock)
- {
- retro_time_t target_frame_time =
- (retro_time_t)roundf(1000000 / video_info->refresh_rate);
- retro_time_t target = thr->last_time + target_frame_time;
- /* Ideally, use absolute time, but that is only a good idea on POSIX. */
- while (thr->frame.updated)
- {
- retro_time_t current = cpu_features_get_time_usec();
- retro_time_t delta = target - current;
- if (delta <= 0)
- break;
- if (!scond_wait_timeout(thr->cond_cmd, thr->lock, delta))
- break;
- }
- }
- /* Drop frame if updated flag is still set, as thread is
- * still working on last frame. */
- if (!thr->frame.updated)
- {
- const uint8_t *src = (const uint8_t*)frame_;
- uint8_t *dst = thr->frame.buffer;
- unsigned copy_stride = width *
- (thr->info.rgb32 ? sizeof(uint32_t) : sizeof(uint16_t));
- if (src)
- {
- int i; /* TODO/FIXME - increment counter never meaningfully used */
- for (i = 0; i < (int)height; i++, src += pitch, dst += copy_stride)
- memcpy(dst, src, copy_stride);
- }
- thr->frame.updated = true;
- thr->frame.width = width;
- thr->frame.height = height;
- thr->frame.count = frame_count;
- thr->frame.pitch = copy_stride;
- if (msg)
- strlcpy(thr->frame.msg, msg, sizeof(thr->frame.msg));
- else
- *thr->frame.msg = '\0';
- scond_signal(thr->cond_thread);
- #ifdef HAVE_MENU
- if (thr->texture.enable)
- {
- do
- {
- scond_wait(thr->cond_cmd, thr->lock);
- } while (thr->frame.updated);
- }
- #endif
- thr->hit_count++;
- }
- else
- thr->miss_count++;
- slock_unlock(thr->lock);
- thr->last_time = cpu_features_get_time_usec();
- return true;
- }
- static void video_thread_set_nonblock_state(void *data, bool state,
- bool adaptive_vsync_enabled,
- unsigned swap_interval)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- thr->nonblock = state;
- }
- static bool video_thread_init(thread_video_t *thr,
- const video_info_t info,
- input_driver_t **input, void **input_data)
- {
- thread_packet_t pkt;
- if (!(thr->lock = slock_new()))
- return false;
- if (!(thr->alpha_lock = slock_new()))
- return false;
- if (!(thr->frame.lock = slock_new()))
- return false;
- if (!(thr->cond_cmd = scond_new()))
- return false;
- if (!(thr->cond_thread = scond_new()))
- return false;
- {
- size_t max_size = info.input_scale * RARCH_SCALE_BASE;
- max_size *= max_size;
- max_size *= info.rgb32 ?
- sizeof(uint32_t) : sizeof(uint16_t);
- #ifdef _3DS
- thr->frame.buffer = linearMemAlign(max_size, 0x80);
- #else
- thr->frame.buffer = (uint8_t*)malloc(max_size);
- #endif
- if (!thr->frame.buffer)
- return false;
- memset(thr->frame.buffer, 0x80, max_size);
- }
- thr->input = input;
- thr->input_data = input_data;
- thr->info = info;
- thr->alive = true;
- thr->focus = true;
- thr->has_windowed = true;
- thr->suppress_screensaver = true;
- thr->last_time = cpu_features_get_time_usec();
- if (!(thr->thread = sthread_create(video_thread_loop, thr)))
- return false;
- pkt.type = CMD_INIT;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- return pkt.data.b;
- }
- static bool video_thread_set_shader(void *data,
- enum rarch_shader_type type, const char *path)
- {
- thread_packet_t pkt;
- thread_video_t *thr = (thread_video_t*)data;
- if (!thr)
- return false;
- pkt.type = CMD_SET_SHADER;
- pkt.data.set_shader.type = type;
- pkt.data.set_shader.path = path;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- return pkt.data.b;
- }
- static void video_thread_set_viewport(void *data, unsigned width,
- unsigned height, bool force_full, bool video_allow_rotate)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data && thr->driver && thr->driver->set_viewport)
- {
- slock_lock(thr->lock);
- thr->driver->set_viewport(thr->driver_data, width, height,
- force_full, video_allow_rotate);
- slock_unlock(thr->lock);
- }
- }
- static void video_thread_set_rotation(void *data, unsigned rotation)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_SET_ROTATION;
- pkt.data.i = rotation;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- /* This value is set async as stalling on the video driver for
- * every query is too slow.
- *
- * This means this value might not be correct, so viewport
- * reads are not supported for now. */
- static void video_thread_viewport_info(void *data, struct video_viewport *vp)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- slock_lock(thr->lock);
- *vp = thr->vp;
- /* Explicitly mem-copied so we can use memcmp correctly later. */
- memcpy(&thr->read_vp, &thr->vp, sizeof(thr->read_vp));
- slock_unlock(thr->lock);
- }
- }
- static bool video_thread_read_viewport(void *data,
- uint8_t *buffer, bool is_idle)
- {
- thread_packet_t pkt;
- thread_video_t *thr = (thread_video_t*)data;
- if (!thr)
- return false;
- pkt.type = CMD_READ_VIEWPORT;
- pkt.data.v = buffer;
- thr->is_idle = is_idle;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- return pkt.data.b;
- }
- static void video_thread_free(void *data)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- if (thr->thread)
- {
- thread_packet_t pkt;
- pkt.type = CMD_FREE;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- sthread_join(thr->thread);
- }
- else
- {
- /* If we don't have a thread,
- we must call the driver's free function ourselves. */
- if (thr->driver_data && thr->driver && thr->driver->free)
- thr->driver->free(thr->driver_data);
- }
- free(thr->texture.frame);
- #ifdef _3DS
- linearFree(thr->frame.buffer);
- #else
- free(thr->frame.buffer);
- #endif
- free(thr->alpha_mod);
- slock_free(thr->frame.lock);
- slock_free(thr->alpha_lock);
- slock_free(thr->lock);
- scond_free(thr->cond_cmd);
- scond_free(thr->cond_thread);
- RARCH_LOG(
- "Threaded video stats: Frames pushed: %u, Frames dropped: %u.\n",
- thr->hit_count, thr->miss_count);
- free(thr);
- }
- }
- #ifdef HAVE_OVERLAY
- static void thread_overlay_enable(void *data, bool state)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_OVERLAY_ENABLE;
- pkt.data.b = state;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static bool thread_overlay_load(void *data,
- const void *image_data, unsigned num_images)
- {
- thread_packet_t pkt;
- thread_video_t *thr = (thread_video_t*)data;
- if (!thr)
- return false;
- pkt.type = CMD_OVERLAY_LOAD;
- pkt.data.image.data = (const struct texture_image*)image_data;
- pkt.data.image.num = num_images;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- return pkt.data.b;
- }
- static void thread_overlay_tex_geom(void *data,
- unsigned idx, float x, float y, float w, float h)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_OVERLAY_TEX_GEOM;
- pkt.data.rect.index = idx;
- pkt.data.rect.x = x;
- pkt.data.rect.y = y;
- pkt.data.rect.w = w;
- pkt.data.rect.h = h;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_overlay_vertex_geom(void *data,
- unsigned idx, float x, float y, float w, float h)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_OVERLAY_VERTEX_GEOM;
- pkt.data.rect.index = idx;
- pkt.data.rect.x = x;
- pkt.data.rect.y = y;
- pkt.data.rect.w = w;
- pkt.data.rect.h = h;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_overlay_full_screen(void *data, bool enable)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_OVERLAY_FULL_SCREEN;
- pkt.data.b = enable;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- /* We cannot wait for this to complete. Totally blocks the main thread. */
- static void thread_overlay_set_alpha(void *data, unsigned idx, float mod)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- slock_lock(thr->alpha_lock);
- thr->alpha_mod[idx] = mod;
- thr->alpha_update = true;
- slock_unlock(thr->alpha_lock);
- }
- }
- static const video_overlay_interface_t thread_overlay = {
- thread_overlay_enable,
- thread_overlay_load,
- thread_overlay_tex_geom,
- thread_overlay_vertex_geom,
- thread_overlay_full_screen,
- thread_overlay_set_alpha,
- };
- static void video_thread_get_overlay_interface(void *data,
- const video_overlay_interface_t **iface)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data &&
- thr->driver && thr->driver->overlay_interface)
- {
- thr->driver->overlay_interface(thr->driver_data, &thr->overlay);
- *iface = &thread_overlay;
- }
- else
- *iface = NULL;
- }
- #endif
- static void thread_set_video_mode(void *data,
- unsigned width, unsigned height, bool video_fullscreen)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_POKE_SET_VIDEO_MODE;
- pkt.data.new_mode.width = width;
- pkt.data.new_mode.height = height;
- pkt.data.new_mode.fullscreen = video_fullscreen;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_set_filtering(void *data,
- unsigned idx, bool smooth, bool ctx_scaling)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_POKE_SET_FILTERING;
- pkt.data.filtering.index = idx;
- pkt.data.filtering.smooth = smooth;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_set_hdr_max_nits(void *data, float max_nits)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_POKE_SET_HDR_MAX_NITS;
- pkt.data.hdr.max_nits = max_nits;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_set_hdr_paper_white_nits(void *data, float paper_white_nits)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_POKE_SET_HDR_PAPER_WHITE_NITS;
- pkt.data.hdr.paper_white_nits = paper_white_nits;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_set_hdr_contrast(void *data, float contrast)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_POKE_SET_HDR_CONTRAST;
- pkt.data.hdr.contrast = contrast;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_set_hdr_expand_gamut(void *data, bool expand_gamut)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_POKE_SET_HDR_EXPAND_GAMUT;
- pkt.data.hdr.expand_gamut = expand_gamut;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_get_video_output_size(void *data,
- unsigned *width, unsigned *height, char *desc, size_t desc_len)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data &&
- thr->poke && thr->poke->get_video_output_size)
- thr->poke->get_video_output_size(thr->driver_data,
- width, height, desc, desc_len);
- }
- static void thread_get_video_output_prev(void *data)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data &&
- thr->poke && thr->poke->get_video_output_prev)
- thr->poke->get_video_output_prev(thr->driver_data);
- }
- static void thread_get_video_output_next(void *data)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data &&
- thr->poke && thr->poke->get_video_output_next)
- thr->poke->get_video_output_next(thr->driver_data);
- }
- static void thread_set_aspect_ratio(void *data, unsigned aspect_ratio_idx)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_POKE_SET_ASPECT_RATIO;
- pkt.data.i = aspect_ratio_idx;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_set_texture_frame(void *data, const void *frame,
- bool rgb32, unsigned width, unsigned height, float alpha)
- {
- thread_video_t *thr = (thread_video_t*)data;
- size_t required = width * height *
- (rgb32 ? sizeof(uint32_t) : sizeof(uint16_t));
- if (!thr)
- return;
- slock_lock(thr->frame.lock);
- if (!thr->texture.frame || required > thr->texture.frame_cap)
- {
- void *tmp_frame = realloc(thr->texture.frame, required);
- if (!tmp_frame)
- goto end;
- thr->texture.frame = tmp_frame;
- thr->texture.frame_cap = required;
- }
- memcpy(thr->texture.frame, frame, required);
- thr->texture.rgb32 = rgb32;
- thr->texture.width = width;
- thr->texture.height = height;
- thr->texture.alpha = alpha;
- thr->texture.frame_updated = true;
- end:
- slock_unlock(thr->frame.lock);
- }
- static void thread_set_texture_enable(void *data, bool state, bool full_screen)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- slock_lock(thr->frame.lock);
- thr->texture.enable = state;
- thr->texture.full_screen = full_screen;
- slock_unlock(thr->frame.lock);
- }
- }
- static void thread_set_osd_msg(void *data,
- const char *msg, const struct font_params *params, void *font)
- {
- thread_video_t *thr = (thread_video_t*)data;
- /* TODO : find a way to determine if the calling
- * thread is the driver thread or not. */
- if (thr && thr->driver_data && thr->poke && thr->poke->set_osd_msg)
- thr->poke->set_osd_msg(thr->driver_data, msg, params, font);
- }
- static void thread_show_mouse(void *data, bool state)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_POKE_SHOW_MOUSE;
- pkt.data.b = state;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static void thread_grab_mouse_toggle(void *data)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- thread_packet_t pkt;
- pkt.type = CMD_POKE_GRAB_MOUSE_TOGGLE;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- }
- }
- static uintptr_t thread_load_texture(void *video_data, void *data,
- bool threaded, enum texture_filter_type filter_type)
- {
- thread_video_t *thr = (thread_video_t*)video_data;
- if (thr && thr->driver_data && thr->poke && thr->poke->load_texture)
- return thr->poke->load_texture(thr->driver_data,
- data, threaded, filter_type);
- return 0;
- }
- static void thread_unload_texture(void *data,
- bool threaded, uintptr_t id)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data && thr->poke && thr->poke->unload_texture)
- thr->poke->unload_texture(thr->driver_data, threaded, id);
- }
- static void thread_apply_state_changes(void *data)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr)
- {
- slock_lock(thr->frame.lock);
- thr->apply_state_changes = true;
- slock_unlock(thr->frame.lock);
- }
- }
- /* This is read-only state which should not
- * have any kind of race condition. */
- static struct video_shader *thread_get_current_shader(void *data)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data && thr->poke && thr->poke->get_current_shader)
- return thr->poke->get_current_shader(thr->driver_data);
- return NULL;
- }
- static uint32_t thread_get_flags(void *data)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data && thr->poke && thr->poke->get_flags)
- return thr->poke->get_flags(thr->driver_data);
- return 0;
- }
- static const video_poke_interface_t thread_poke = {
- thread_get_flags,
- thread_load_texture,
- thread_unload_texture,
- thread_set_video_mode,
- NULL, /* get_refresh_rate */
- thread_set_filtering,
- thread_get_video_output_size,
- thread_get_video_output_prev,
- thread_get_video_output_next,
- NULL, /* get_current_framebuffer */
- NULL, /* get_proc_address */
- thread_set_aspect_ratio,
- thread_apply_state_changes,
- thread_set_texture_frame,
- thread_set_texture_enable,
- thread_set_osd_msg,
- thread_show_mouse,
- thread_grab_mouse_toggle,
- thread_get_current_shader,
- NULL, /* get_current_software_framebuffer */
- NULL, /* get_hw_render_interface */
- thread_set_hdr_max_nits,
- thread_set_hdr_paper_white_nits,
- thread_set_hdr_contrast,
- thread_set_hdr_expand_gamut
- };
- static void video_thread_get_poke_interface(void *data,
- const video_poke_interface_t **iface)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data &&
- thr->driver && thr->driver->poke_interface)
- {
- thr->driver->poke_interface(thr->driver_data, &thr->poke);
- *iface = &thread_poke;
- }
- else
- *iface = NULL;
- }
- #ifdef HAVE_GFX_WIDGETS
- static bool video_thread_wrapper_gfx_widgets_enabled(void *data)
- {
- thread_video_t *thr = (thread_video_t*)data;
- if (thr && thr->driver_data &&
- thr->driver && thr->driver->gfx_widgets_enabled)
- return thr->driver->gfx_widgets_enabled(thr->driver_data);
- return false;
- }
- #endif
- static const video_driver_t video_thread = {
- video_thread_init_never_call, /* Should never be called directly. */
- video_thread_frame,
- video_thread_set_nonblock_state,
- video_thread_alive,
- video_thread_focus,
- video_thread_suppress_screensaver,
- video_thread_has_windowed,
- video_thread_set_shader,
- video_thread_free,
- "Thread wrapper",
- video_thread_set_viewport,
- video_thread_set_rotation,
- video_thread_viewport_info,
- video_thread_read_viewport,
- NULL, /* read_frame_raw */
- #ifdef HAVE_OVERLAY
- video_thread_get_overlay_interface,
- #endif
- video_thread_get_poke_interface,
- NULL, /* wrap_type_to_enum */
- #ifdef HAVE_GFX_WIDGETS
- video_thread_wrapper_gfx_widgets_enabled
- #endif
- };
- static void video_thread_set_callbacks(thread_video_t *thr,
- const video_driver_t *drv)
- {
- thr->video_thread = video_thread;
- thr->driver = drv;
- if (drv)
- {
- /* Disable optional features if not present. */
- if (!drv->read_viewport)
- thr->video_thread.read_viewport = NULL;
- if (!drv->set_viewport)
- thr->video_thread.set_viewport = NULL;
- if (!drv->set_rotation)
- thr->video_thread.set_rotation = NULL;
- if (!drv->set_shader)
- thr->video_thread.set_shader = NULL;
- #ifdef HAVE_OVERLAY
- if (!drv->overlay_interface)
- thr->video_thread.overlay_interface = NULL;
- #endif
- if (!drv->poke_interface)
- thr->video_thread.poke_interface = NULL;
- }
- }
- /**
- * video_init_thread:
- * @out_driver : Output video driver
- * @out_data : Output video data
- * @input : Input input driver
- * @input_data : Input input data
- * @driver : Input Video driver
- * @info : Video info handle.
- *
- * Creates, initializes and starts a video driver in a new thread.
- * Access to video driver will be mediated through this driver.
- *
- * Returns: true (1) if successful, otherwise false (0).
- **/
- bool video_init_thread(const video_driver_t **out_driver, void **out_data,
- input_driver_t **input, void **input_data,
- const video_driver_t *drv, const video_info_t info)
- {
- thread_video_t *thr = (thread_video_t*)calloc(1, sizeof(*thr));
- if (!thr)
- return false;
- video_thread_set_callbacks(thr, drv);
- thr->driver = drv;
- *out_driver = &thr->video_thread;
- *out_data = thr;
- return video_thread_init(thr, info, input, input_data);
- }
- bool video_thread_font_init(const void **font_driver, void **font_handle,
- void *data, const char *font_path, float video_font_size,
- enum font_driver_render_api api, custom_font_command_method_t func,
- bool is_threaded)
- {
- thread_packet_t pkt;
- video_driver_state_t *video_st = video_state_get_ptr();
- thread_video_t *thr = (thread_video_t*)video_st->data;
- if (!thr)
- return false;
- pkt.type = CMD_FONT_INIT;
- pkt.data.font_init.method = func;
- pkt.data.font_init.font_driver = font_driver;
- pkt.data.font_init.font_handle = font_handle;
- pkt.data.font_init.video_data = data;
- pkt.data.font_init.font_path = font_path;
- pkt.data.font_init.font_size = video_font_size;
- pkt.data.font_init.is_threaded = is_threaded;
- pkt.data.font_init.api = api;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- return pkt.data.font_init.return_value;
- }
- unsigned video_thread_texture_load(void *data, custom_command_method_t func)
- {
- thread_packet_t pkt;
- video_driver_state_t *video_st = video_state_get_ptr();
- thread_video_t *thr = (thread_video_t*)video_st->data;
- if (!thr)
- return 0;
- pkt.type = CMD_CUSTOM_COMMAND;
- pkt.data.custom_command.method = func;
- pkt.data.custom_command.data = data;
- video_thread_send_and_wait_user_to_thread(thr, &pkt);
- return pkt.data.custom_command.return_value;
- }
|